Merge "Some refactoring and added support for another form of vbr headers."

This commit is contained in:
Andreas Huber
2010-11-09 14:59:13 -08:00
committed by Android (Google) Code Review
8 changed files with 608 additions and 195 deletions

View File

@@ -44,8 +44,10 @@ LOCAL_SRC_FILES:= \
TimeSource.cpp \
TimedEventQueue.cpp \
Utils.cpp \
VBRISeeker.cpp \
WAVExtractor.cpp \
WVMExtractor.cpp \
XINGSeeker.cpp \
avc_utils.cpp \
string.cpp

View File

@@ -21,6 +21,8 @@
#include "include/MP3Extractor.h"
#include "include/ID3.h"
#include "include/VBRISeeker.h"
#include "include/XINGSeeker.h"
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
@@ -42,10 +44,11 @@ namespace android {
// Yes ... there are things that must indeed match...
static const uint32_t kMask = 0xfffe0cc0;
static bool get_mp3_frame_size(
// static
bool MP3Extractor::get_mp3_frame_size(
uint32_t header, size_t *frame_size,
int *out_sampling_rate = NULL, int *out_channels = NULL,
int *out_bitrate = NULL) {
int *out_sampling_rate, int *out_channels,
int *out_bitrate) {
*frame_size = 0;
if (out_sampling_rate) {
@@ -178,136 +181,13 @@ static bool get_mp3_frame_size(
return true;
}
static bool parse_xing_header(
const sp<DataSource> &source, off_t first_frame_pos,
int32_t *frame_number = NULL, int32_t *byte_number = NULL,
char *table_of_contents = NULL, int32_t *quality_indicator = NULL,
int64_t *duration = NULL) {
if (frame_number) {
*frame_number = 0;
}
if (byte_number) {
*byte_number = 0;
}
if (table_of_contents) {
table_of_contents[0] = 0;
}
if (quality_indicator) {
*quality_indicator = 0;
}
if (duration) {
*duration = 0;
}
uint8_t buffer[4];
int offset = first_frame_pos;
if (source->readAt(offset, &buffer, 4) < 4) { // get header
return false;
}
offset += 4;
uint8_t id, layer, sr_index, mode;
layer = (buffer[1] >> 1) & 3;
id = (buffer[1] >> 3) & 3;
sr_index = (buffer[2] >> 2) & 3;
mode = (buffer[3] >> 6) & 3;
if (layer == 0) {
return false;
}
if (id == 1) {
return false;
}
if (sr_index == 3) {
return false;
}
// determine offset of XING header
if(id&1) { // mpeg1
if (mode != 3) offset += 32;
else offset += 17;
} else { // mpeg2
if (mode != 3) offset += 17;
else offset += 9;
}
if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID
return false;
}
offset += 4;
// Check XING ID
if ((buffer[0] != 'X') || (buffer[1] != 'i')
|| (buffer[2] != 'n') || (buffer[3] != 'g')) {
if ((buffer[0] != 'I') || (buffer[1] != 'n')
|| (buffer[2] != 'f') || (buffer[3] != 'o')) {
return false;
}
}
if (source->readAt(offset, &buffer, 4) < 4) { // flags
return false;
}
offset += 4;
uint32_t flags = U32_AT(buffer);
if (flags & 0x0001) { // Frames field is present
if (source->readAt(offset, buffer, 4) < 4) {
return false;
}
if (frame_number) {
*frame_number = U32_AT(buffer);
}
int32_t frame = U32_AT(buffer);
// Samples per Frame: 1. index = MPEG Version ID, 2. index = Layer
const int samplesPerFrames[2][3] =
{
{ 384, 1152, 576 }, // MPEG 2, 2.5: layer1, layer2, layer3
{ 384, 1152, 1152 }, // MPEG 1: layer1, layer2, layer3
};
// sampling rates in hertz: 1. index = MPEG Version ID, 2. index = sampling rate index
const int samplingRates[4][3] =
{
{ 11025, 12000, 8000, }, // MPEG 2.5
{ 0, 0, 0, }, // reserved
{ 22050, 24000, 16000, }, // MPEG 2
{ 44100, 48000, 32000, } // MPEG 1
};
if (duration) {
*duration = (int64_t)frame * samplesPerFrames[id&1][3-layer] * 1000000LL
/ samplingRates[id][sr_index];
}
offset += 4;
}
if (flags & 0x0002) { // Bytes field is present
if (byte_number) {
if (source->readAt(offset, buffer, 4) < 4) {
return false;
}
*byte_number = U32_AT(buffer);
}
offset += 4;
}
if (flags & 0x0004) { // TOC field is present
if (table_of_contents) {
if (source->readAt(offset + 1, table_of_contents, 99) < 99) {
return false;
}
}
offset += 100;
}
if (flags & 0x0008) { // Quality indicator field is present
if (quality_indicator) {
if (source->readAt(offset, buffer, 4) < 4) {
return false;
}
*quality_indicator = U32_AT(buffer);
}
}
return true;
}
static bool Resync(
const sp<DataSource> &source, uint32_t match_header,
off_t *inout_pos, uint32_t *out_header) {
off_t *inout_pos, off_t *post_id3_pos, uint32_t *out_header) {
if (post_id3_pos != NULL) {
*post_id3_pos = 0;
}
if (*inout_pos == 0) {
// Skip an optional ID3 header if syncing at the very beginning
// of the datasource.
@@ -340,6 +220,10 @@ static bool Resync(
LOGV("skipped ID3 tag, new starting offset is %ld (0x%08lx)",
*inout_pos, *inout_pos);
}
if (post_id3_pos != NULL) {
*post_id3_pos = *inout_pos;
}
}
off_t pos = *inout_pos;
@@ -365,8 +249,9 @@ static bool Resync(
size_t frame_size;
int sample_rate, num_channels, bitrate;
if (!get_mp3_frame_size(header, &frame_size,
&sample_rate, &num_channels, &bitrate)) {
if (!MP3Extractor::get_mp3_frame_size(
header, &frame_size,
&sample_rate, &num_channels, &bitrate)) {
++pos;
continue;
}
@@ -396,7 +281,8 @@ static bool Resync(
}
size_t test_frame_size;
if (!get_mp3_frame_size(test_header, &test_frame_size)) {
if (!MP3Extractor::get_mp3_frame_size(
test_header, &test_frame_size)) {
valid = false;
break;
}
@@ -427,7 +313,7 @@ public:
MP3Source(
const sp<MetaData> &meta, const sp<DataSource> &source,
off_t first_frame_pos, uint32_t fixed_header,
int32_t byte_number, const char *table_of_contents);
const sp<MP3Seeker> &seeker);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
@@ -448,9 +334,7 @@ private:
off_t mCurrentPos;
int64_t mCurrentTimeUs;
bool mStarted;
int32_t mByteNumber; // total number of bytes in this MP3
// TOC entries in XING header. Skip the first one since it's always 0.
char mTableOfContents[99];
sp<MP3Seeker> mSeeker;
MediaBufferGroup *mGroup;
MP3Source(const MP3Source &);
@@ -462,25 +346,28 @@ MP3Extractor::MP3Extractor(
: mInitCheck(NO_INIT),
mDataSource(source),
mFirstFramePos(-1),
mFixedHeader(0),
mByteNumber(0) {
mFixedHeader(0) {
off_t pos = 0;
off_t post_id3_pos;
uint32_t header;
bool success;
int64_t meta_offset;
uint32_t meta_header;
int64_t meta_post_id3_offset;
if (meta != NULL
&& meta->findInt64("offset", &meta_offset)
&& meta->findInt32("header", (int32_t *)&meta_header)) {
&& meta->findInt32("header", (int32_t *)&meta_header)
&& meta->findInt64("post-id3-offset", &meta_post_id3_offset)) {
// The sniffer has already done all the hard work for us, simply
// accept its judgement.
pos = (off_t)meta_offset;
header = meta_header;
post_id3_pos = (off_t)meta_post_id3_offset;
success = true;
} else {
success = Resync(mDataSource, 0, &pos, &header);
success = Resync(mDataSource, 0, &pos, &post_id3_pos, &header);
}
if (!success) {
@@ -505,21 +392,27 @@ MP3Extractor::MP3Extractor(
mMeta->setInt32(kKeyBitRate, bitrate * 1000);
mMeta->setInt32(kKeyChannelCount, num_channels);
int64_t duration;
parse_xing_header(
mDataSource, mFirstFramePos, NULL, &mByteNumber,
mTableOfContents, NULL, &duration);
if (duration > 0) {
mMeta->setInt64(kKeyDuration, duration);
} else {
mSeeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos);
if (mSeeker == NULL) {
mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos);
}
int64_t durationUs;
if (mSeeker == NULL || !mSeeker->getDuration(&durationUs)) {
off_t fileSize;
if (mDataSource->getSize(&fileSize) == OK) {
mMeta->setInt64(
kKeyDuration,
8000LL * (fileSize - mFirstFramePos) / bitrate);
durationUs = 8000LL * (fileSize - mFirstFramePos) / bitrate;
} else {
durationUs = -1;
}
}
if (durationUs >= 0) {
mMeta->setInt64(kKeyDuration, durationUs);
}
mInitCheck = OK;
}
@@ -534,7 +427,7 @@ sp<MediaSource> MP3Extractor::getTrack(size_t index) {
return new MP3Source(
mMeta, mDataSource, mFirstFramePos, mFixedHeader,
mByteNumber, mTableOfContents);
mSeeker);
}
sp<MetaData> MP3Extractor::getTrackMetaData(size_t index, uint32_t flags) {
@@ -550,7 +443,7 @@ sp<MetaData> MP3Extractor::getTrackMetaData(size_t index, uint32_t flags) {
MP3Source::MP3Source(
const sp<MetaData> &meta, const sp<DataSource> &source,
off_t first_frame_pos, uint32_t fixed_header,
int32_t byte_number, const char *table_of_contents)
const sp<MP3Seeker> &seeker)
: mMeta(meta),
mDataSource(source),
mFirstFramePos(first_frame_pos),
@@ -558,9 +451,8 @@ MP3Source::MP3Source(
mCurrentPos(0),
mCurrentTimeUs(0),
mStarted(false),
mByteNumber(byte_number),
mSeeker(seeker),
mGroup(NULL) {
memcpy (mTableOfContents, table_of_contents, sizeof(mTableOfContents));
}
MP3Source::~MP3Source() {
@@ -607,43 +499,21 @@ status_t MP3Source::read(
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
int32_t bitrate;
if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
// bitrate is in bits/sec.
LOGI("no bitrate");
int64_t actualSeekTimeUs = seekTimeUs;
if (mSeeker == NULL
|| !mSeeker->getOffsetForTime(&actualSeekTimeUs, &mCurrentPos)) {
int32_t bitrate;
if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
// bitrate is in bits/sec.
LOGI("no bitrate");
return ERROR_UNSUPPORTED;
}
mCurrentTimeUs = seekTimeUs;
// interpolate in TOC to get file seek point in bytes
int64_t duration;
if ((mByteNumber > 0) && (mTableOfContents[0] > 0)
&& mMeta->findInt64(kKeyDuration, &duration)) {
float percent = (float)seekTimeUs * 100 / duration;
float fx;
if( percent <= 0.0f ) {
fx = 0.0f;
} else if( percent >= 100.0f ) {
fx = 256.0f;
} else {
int a = (int)percent;
float fa, fb;
if ( a == 0 ) {
fa = 0.0f;
} else {
fa = (float)mTableOfContents[a-1];
}
if ( a < 99 ) {
fb = (float)mTableOfContents[a];
} else {
fb = 256.0f;
}
fx = fa + (fb-fa)*(percent-a);
return ERROR_UNSUPPORTED;
}
mCurrentPos = mFirstFramePos + (int)((1.0f/256.0f)*fx*mByteNumber);
} else {
mCurrentTimeUs = seekTimeUs;
mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 8000000;
} else {
mCurrentTimeUs = actualSeekTimeUs;
}
}
@@ -667,7 +537,8 @@ status_t MP3Source::read(
uint32_t header = U32_AT((const uint8_t *)buffer->data());
if ((header & kMask) == (mFixedHeader & kMask)
&& get_mp3_frame_size(header, &frame_size, NULL, NULL, &bitrate)) {
&& MP3Extractor::get_mp3_frame_size(
header, &frame_size, NULL, NULL, &bitrate)) {
break;
}
@@ -675,7 +546,7 @@ status_t MP3Source::read(
LOGV("lost sync! header = 0x%08x, old header = 0x%08x\n", header, mFixedHeader);
off_t pos = mCurrentPos;
if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
if (!Resync(mDataSource, mFixedHeader, &pos, NULL, NULL)) {
LOGE("Unable to resync. Signalling end of stream.");
buffer->release();
@@ -781,14 +652,16 @@ bool SniffMP3(
const sp<DataSource> &source, String8 *mimeType,
float *confidence, sp<AMessage> *meta) {
off_t pos = 0;
off_t post_id3_pos;
uint32_t header;
if (!Resync(source, 0, &pos, &header)) {
if (!Resync(source, 0, &pos, &post_id3_pos, &header)) {
return false;
}
*meta = new AMessage;
(*meta)->setInt64("offset", pos);
(*meta)->setInt32("header", header);
(*meta)->setInt64("post-id3-offset", post_id3_pos);
*mimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
*confidence = 0.2f;

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "VBRISeeker"
#include <utils/Log.h>
#include "include/VBRISeeker.h"
#include "include/MP3Extractor.h"
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/Utils.h>
namespace android {
static uint32_t U24_AT(const uint8_t *ptr) {
return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
}
// static
sp<VBRISeeker> VBRISeeker::CreateFromSource(
const sp<DataSource> &source, off_t post_id3_pos) {
off_t pos = post_id3_pos;
uint8_t header[4];
ssize_t n = source->readAt(pos, header, sizeof(header));
if (n < (ssize_t)sizeof(header)) {
return NULL;
}
uint32_t tmp = U32_AT(&header[0]);
size_t frameSize;
int sampleRate;
if (!MP3Extractor::get_mp3_frame_size(tmp, &frameSize, &sampleRate)) {
return NULL;
}
// VBRI header follows 32 bytes after the header _ends_.
pos += sizeof(header) + 32;
uint8_t vbriHeader[26];
n = source->readAt(pos, vbriHeader, sizeof(vbriHeader));
if (n < (ssize_t)sizeof(vbriHeader)) {
return NULL;
}
if (memcmp(vbriHeader, "VBRI", 4)) {
return NULL;
}
size_t numFrames = U32_AT(&vbriHeader[14]);
int64_t durationUs =
numFrames * 1000000ll * (sampleRate >= 32000 ? 1152 : 576) / sampleRate;
LOGV("duration = %.2f secs", durationUs / 1E6);
size_t numEntries = U16_AT(&vbriHeader[18]);
size_t entrySize = U16_AT(&vbriHeader[22]);
size_t scale = U16_AT(&vbriHeader[20]);
LOGV("%d entries, scale=%d, size_per_entry=%d",
numEntries,
scale,
entrySize);
size_t totalEntrySize = numEntries * entrySize;
uint8_t *buffer = new uint8_t[totalEntrySize];
n = source->readAt(pos + sizeof(vbriHeader), buffer, totalEntrySize);
if (n < (ssize_t)totalEntrySize) {
delete[] buffer;
buffer = NULL;
return NULL;
}
sp<VBRISeeker> seeker = new VBRISeeker;
seeker->mBasePos = post_id3_pos;
seeker->mDurationUs = durationUs;
off_t offset = post_id3_pos;
for (size_t i = 0; i < numEntries; ++i) {
uint32_t numBytes;
switch (entrySize) {
case 1: numBytes = buffer[i]; break;
case 2: numBytes = U16_AT(buffer + 2 * i); break;
case 3: numBytes = U24_AT(buffer + 3 * i); break;
default:
{
CHECK_EQ(entrySize, 4u);
numBytes = U32_AT(buffer + 4 * i); break;
}
}
numBytes *= scale;
seeker->mSegments.push(numBytes);
LOGV("entry #%d: %d offset 0x%08lx", i, numBytes, offset);
offset += numBytes;
}
delete[] buffer;
buffer = NULL;
LOGI("Found VBRI header.");
return seeker;
}
VBRISeeker::VBRISeeker()
: mDurationUs(-1) {
}
bool VBRISeeker::getDuration(int64_t *durationUs) {
if (mDurationUs < 0) {
return false;
}
*durationUs = mDurationUs;
return true;
}
bool VBRISeeker::getOffsetForTime(int64_t *timeUs, off_t *pos) {
if (mDurationUs < 0) {
return false;
}
int64_t segmentDurationUs = mDurationUs / mSegments.size();
int64_t nowUs = 0;
*pos = mBasePos;
size_t segmentIndex = 0;
while (segmentIndex < mSegments.size() && nowUs < *timeUs) {
nowUs += segmentDurationUs;
*pos += mSegments.itemAt(segmentIndex++);
}
LOGV("getOffsetForTime %lld us => 0x%08lx", *timeUs, *pos);
*timeUs = nowUs;
return true;
}
} // namespace android

View File

@@ -0,0 +1,223 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "include/XINGSeeker.h"
#include <media/stagefright/DataSource.h>
#include <media/stagefright/Utils.h>
namespace android {
static bool parse_xing_header(
const sp<DataSource> &source, off_t first_frame_pos,
int32_t *frame_number = NULL, int32_t *byte_number = NULL,
char *table_of_contents = NULL, int32_t *quality_indicator = NULL,
int64_t *duration = NULL);
// static
sp<XINGSeeker> XINGSeeker::CreateFromSource(
const sp<DataSource> &source, off_t first_frame_pos) {
sp<XINGSeeker> seeker = new XINGSeeker;
seeker->mFirstFramePos = first_frame_pos;
if (!parse_xing_header(
source, first_frame_pos,
NULL, &seeker->mSizeBytes, seeker->mTableOfContents,
NULL, &seeker->mDurationUs)) {
return NULL;
}
LOGI("Found XING header.");
return seeker;
}
XINGSeeker::XINGSeeker()
: mDurationUs(-1),
mSizeBytes(0) {
}
bool XINGSeeker::getDuration(int64_t *durationUs) {
if (mDurationUs < 0) {
return false;
}
*durationUs = mDurationUs;
return true;
}
bool XINGSeeker::getOffsetForTime(int64_t *timeUs, off_t *pos) {
if (mSizeBytes == 0 || mTableOfContents[0] <= 0 || mDurationUs < 0) {
return false;
}
float percent = (float)(*timeUs) * 100 / mDurationUs;
float fx;
if( percent <= 0.0f ) {
fx = 0.0f;
} else if( percent >= 100.0f ) {
fx = 256.0f;
} else {
int a = (int)percent;
float fa, fb;
if ( a == 0 ) {
fa = 0.0f;
} else {
fa = (float)mTableOfContents[a-1];
}
if ( a < 99 ) {
fb = (float)mTableOfContents[a];
} else {
fb = 256.0f;
}
fx = fa + (fb-fa)*(percent-a);
}
*pos = (int)((1.0f/256.0f)*fx*mSizeBytes) + mFirstFramePos;
return true;
}
static bool parse_xing_header(
const sp<DataSource> &source, off_t first_frame_pos,
int32_t *frame_number, int32_t *byte_number,
char *table_of_contents, int32_t *quality_indicator,
int64_t *duration) {
if (frame_number) {
*frame_number = 0;
}
if (byte_number) {
*byte_number = 0;
}
if (table_of_contents) {
table_of_contents[0] = 0;
}
if (quality_indicator) {
*quality_indicator = 0;
}
if (duration) {
*duration = 0;
}
uint8_t buffer[4];
int offset = first_frame_pos;
if (source->readAt(offset, &buffer, 4) < 4) { // get header
return false;
}
offset += 4;
uint8_t id, layer, sr_index, mode;
layer = (buffer[1] >> 1) & 3;
id = (buffer[1] >> 3) & 3;
sr_index = (buffer[2] >> 2) & 3;
mode = (buffer[3] >> 6) & 3;
if (layer == 0) {
return false;
}
if (id == 1) {
return false;
}
if (sr_index == 3) {
return false;
}
// determine offset of XING header
if(id&1) { // mpeg1
if (mode != 3) offset += 32;
else offset += 17;
} else { // mpeg2
if (mode != 3) offset += 17;
else offset += 9;
}
if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID
return false;
}
offset += 4;
// Check XING ID
if ((buffer[0] != 'X') || (buffer[1] != 'i')
|| (buffer[2] != 'n') || (buffer[3] != 'g')) {
if ((buffer[0] != 'I') || (buffer[1] != 'n')
|| (buffer[2] != 'f') || (buffer[3] != 'o')) {
return false;
}
}
if (source->readAt(offset, &buffer, 4) < 4) { // flags
return false;
}
offset += 4;
uint32_t flags = U32_AT(buffer);
if (flags & 0x0001) { // Frames field is present
if (source->readAt(offset, buffer, 4) < 4) {
return false;
}
if (frame_number) {
*frame_number = U32_AT(buffer);
}
int32_t frame = U32_AT(buffer);
// Samples per Frame: 1. index = MPEG Version ID, 2. index = Layer
const int samplesPerFrames[2][3] =
{
{ 384, 1152, 576 }, // MPEG 2, 2.5: layer1, layer2, layer3
{ 384, 1152, 1152 }, // MPEG 1: layer1, layer2, layer3
};
// sampling rates in hertz: 1. index = MPEG Version ID, 2. index = sampling rate index
const int samplingRates[4][3] =
{
{ 11025, 12000, 8000, }, // MPEG 2.5
{ 0, 0, 0, }, // reserved
{ 22050, 24000, 16000, }, // MPEG 2
{ 44100, 48000, 32000, } // MPEG 1
};
if (duration) {
*duration = (int64_t)frame * samplesPerFrames[id&1][3-layer] * 1000000LL
/ samplingRates[id][sr_index];
}
offset += 4;
}
if (flags & 0x0002) { // Bytes field is present
if (byte_number) {
if (source->readAt(offset, buffer, 4) < 4) {
return false;
}
*byte_number = U32_AT(buffer);
}
offset += 4;
}
if (flags & 0x0004) { // TOC field is present
if (table_of_contents) {
if (source->readAt(offset + 1, table_of_contents, 99) < 99) {
return false;
}
}
offset += 100;
}
if (flags & 0x0008) { // Quality indicator field is present
if (quality_indicator) {
if (source->readAt(offset, buffer, 4) < 4) {
return false;
}
*quality_indicator = U32_AT(buffer);
}
}
return true;
}
} // namespace android

View File

@@ -24,6 +24,7 @@ namespace android {
struct AMessage;
class DataSource;
struct MP3Seeker;
class String8;
class MP3Extractor : public MediaExtractor {
@@ -37,6 +38,11 @@ public:
virtual sp<MetaData> getMetaData();
static bool get_mp3_frame_size(
uint32_t header, size_t *frame_size,
int *out_sampling_rate = NULL, int *out_channels = NULL,
int *out_bitrate = NULL);
private:
status_t mInitCheck;
@@ -44,8 +50,7 @@ private:
off_t mFirstFramePos;
sp<MetaData> mMeta;
uint32_t mFixedHeader;
int32_t mByteNumber; // total number of bytes in this MP3
char mTableOfContents[99]; // TOC entries in XING header
sp<MP3Seeker> mSeeker;
MP3Extractor(const MP3Extractor &);
MP3Extractor &operator=(const MP3Extractor &);

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MP3_SEEKER_H_
#define MP3_SEEKER_H_
#include <media/stagefright/foundation/ABase.h>
#include <utils/RefBase.h>
namespace android {
struct MP3Seeker : public RefBase {
MP3Seeker() {}
virtual bool getDuration(int64_t *durationUs) = 0;
// Given a request seek time in "*timeUs", find the byte offset closest
// to that position and return it in "*pos". Update "*timeUs" to reflect
// the actual time that seekpoint represents.
virtual bool getOffsetForTime(int64_t *timeUs, off_t *pos) = 0;
protected:
virtual ~MP3Seeker() {}
private:
DISALLOW_EVIL_CONSTRUCTORS(MP3Seeker);
};
} // namespace android
#endif // MP3_SEEKER_H_

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef VBRI_SEEKER_H_
#define VBRI_SEEKER_H_
#include "include/MP3Seeker.h"
#include <utils/Vector.h>
namespace android {
struct DataSource;
struct VBRISeeker : public MP3Seeker {
static sp<VBRISeeker> CreateFromSource(
const sp<DataSource> &source, off_t post_id3_pos);
virtual bool getDuration(int64_t *durationUs);
virtual bool getOffsetForTime(int64_t *timeUs, off_t *pos);
private:
off_t mBasePos;
int64_t mDurationUs;
Vector<uint32_t> mSegments;
VBRISeeker();
DISALLOW_EVIL_CONSTRUCTORS(VBRISeeker);
};
} // namespace android
#endif // VBRI_SEEKER_H_

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef XING_SEEKER_H_
#define XING_SEEKER_H_
#include "include/MP3Seeker.h"
namespace android {
struct DataSource;
struct XINGSeeker : public MP3Seeker {
static sp<XINGSeeker> CreateFromSource(
const sp<DataSource> &source, off_t first_frame_pos);
virtual bool getDuration(int64_t *durationUs);
virtual bool getOffsetForTime(int64_t *timeUs, off_t *pos);
private:
int64_t mFirstFramePos;
int64_t mDurationUs;
int32_t mSizeBytes;
// TOC entries in XING header. Skip the first one since it's always 0.
char mTableOfContents[99];
XINGSeeker();
DISALLOW_EVIL_CONSTRUCTORS(XINGSeeker);
};
} // namespace android
#endif // XING_SEEKER_H_