|
|
|
@ -207,19 +207,19 @@ static bool Resync(
|
|
|
|
|
return valid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MP3Source : public MediaTrackHelper {
|
|
|
|
|
class MP3Source : public MediaTrackHelperV2 {
|
|
|
|
|
public:
|
|
|
|
|
MP3Source(
|
|
|
|
|
MetaDataBase &meta, DataSourceHelper *source,
|
|
|
|
|
AMediaFormat *meta, DataSourceHelper *source,
|
|
|
|
|
off64_t first_frame_pos, uint32_t fixed_header,
|
|
|
|
|
MP3Seeker *seeker);
|
|
|
|
|
|
|
|
|
|
virtual status_t start();
|
|
|
|
|
virtual status_t stop();
|
|
|
|
|
virtual media_status_t start();
|
|
|
|
|
virtual media_status_t stop();
|
|
|
|
|
|
|
|
|
|
virtual status_t getFormat(MetaDataBase &meta);
|
|
|
|
|
virtual media_status_t getFormat(AMediaFormat *meta);
|
|
|
|
|
|
|
|
|
|
virtual status_t read(
|
|
|
|
|
virtual media_status_t read(
|
|
|
|
|
MediaBufferBase **buffer, const ReadOptions *options = NULL);
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
@ -227,7 +227,7 @@ protected:
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
static const size_t kMaxFrameSize;
|
|
|
|
|
MetaDataBase &mMeta;
|
|
|
|
|
AMediaFormat *mMeta;
|
|
|
|
|
DataSourceHelper *mDataSource;
|
|
|
|
|
off64_t mFirstFramePos;
|
|
|
|
|
uint32_t mFixedHeader;
|
|
|
|
@ -283,6 +283,7 @@ MP3Extractor::MP3Extractor(
|
|
|
|
|
mFixedHeader = header;
|
|
|
|
|
XINGSeeker *seeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos);
|
|
|
|
|
|
|
|
|
|
mMeta = AMediaFormat_new();
|
|
|
|
|
if (seeker == NULL) {
|
|
|
|
|
mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos);
|
|
|
|
|
} else {
|
|
|
|
@ -290,8 +291,8 @@ MP3Extractor::MP3Extractor(
|
|
|
|
|
int encd = seeker->getEncoderDelay();
|
|
|
|
|
int encp = seeker->getEncoderPadding();
|
|
|
|
|
if (encd != 0 || encp != 0) {
|
|
|
|
|
mMeta.setInt32(kKeyEncoderDelay, encd);
|
|
|
|
|
mMeta.setInt32(kKeyEncoderPadding, encp);
|
|
|
|
|
AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_ENCODER_DELAY, encd);
|
|
|
|
|
AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_ENCODER_PADDING, encp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -327,21 +328,23 @@ MP3Extractor::MP3Extractor(
|
|
|
|
|
|
|
|
|
|
switch (layer) {
|
|
|
|
|
case 1:
|
|
|
|
|
mMeta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I);
|
|
|
|
|
AMediaFormat_setString(mMeta,
|
|
|
|
|
AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
mMeta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II);
|
|
|
|
|
AMediaFormat_setString(mMeta,
|
|
|
|
|
AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II);
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
mMeta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
|
|
|
|
|
AMediaFormat_setString(mMeta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_MPEG);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
TRESPASS();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mMeta.setInt32(kKeySampleRate, sample_rate);
|
|
|
|
|
mMeta.setInt32(kKeyBitRate, bitrate * 1000);
|
|
|
|
|
mMeta.setInt32(kKeyChannelCount, num_channels);
|
|
|
|
|
AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sample_rate);
|
|
|
|
|
AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_BIT_RATE, bitrate * 1000);
|
|
|
|
|
AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, num_channels);
|
|
|
|
|
|
|
|
|
|
int64_t durationUs;
|
|
|
|
|
|
|
|
|
@ -361,7 +364,7 @@ MP3Extractor::MP3Extractor(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (durationUs >= 0) {
|
|
|
|
|
mMeta.setInt64(kKeyDuration, durationUs);
|
|
|
|
|
AMediaFormat_setInt64(mMeta, AMEDIAFORMAT_KEY_DURATION, durationUs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mInitCheck = OK;
|
|
|
|
@ -389,8 +392,8 @@ MP3Extractor::MP3Extractor(
|
|
|
|
|
|
|
|
|
|
int32_t delay, padding;
|
|
|
|
|
if (sscanf(value, " %*x %x %x %*x", &delay, &padding) == 2) {
|
|
|
|
|
mMeta.setInt32(kKeyEncoderDelay, delay);
|
|
|
|
|
mMeta.setInt32(kKeyEncoderPadding, padding);
|
|
|
|
|
AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_ENCODER_DELAY, delay);
|
|
|
|
|
AMediaFormat_setInt32(mMeta, AMEDIAFORMAT_KEY_ENCODER_PADDING, padding);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -404,13 +407,14 @@ MP3Extractor::MP3Extractor(
|
|
|
|
|
MP3Extractor::~MP3Extractor() {
|
|
|
|
|
delete mSeeker;
|
|
|
|
|
delete mDataSource;
|
|
|
|
|
AMediaFormat_delete(mMeta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t MP3Extractor::countTracks() {
|
|
|
|
|
return mInitCheck != OK ? 0 : 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MediaTrackHelper *MP3Extractor::getTrack(size_t index) {
|
|
|
|
|
MediaTrackHelperV2 *MP3Extractor::getTrack(size_t index) {
|
|
|
|
|
if (mInitCheck != OK || index != 0) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
@ -420,14 +424,14 @@ MediaTrackHelper *MP3Extractor::getTrack(size_t index) {
|
|
|
|
|
mSeeker);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t MP3Extractor::getTrackMetaData(
|
|
|
|
|
MetaDataBase &meta,
|
|
|
|
|
media_status_t MP3Extractor::getTrackMetaData(
|
|
|
|
|
AMediaFormat *meta,
|
|
|
|
|
size_t index, uint32_t /* flags */) {
|
|
|
|
|
if (mInitCheck != OK || index != 0) {
|
|
|
|
|
return UNKNOWN_ERROR;
|
|
|
|
|
return AMEDIA_ERROR_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
meta = mMeta;
|
|
|
|
|
return OK;
|
|
|
|
|
AMediaFormat_copy(meta, mMeta);
|
|
|
|
|
return AMEDIA_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
@ -440,7 +444,7 @@ status_t MP3Extractor::getTrackMetaData(
|
|
|
|
|
// Set our max frame size to the nearest power of 2 above this size (aka, 4kB)
|
|
|
|
|
const size_t MP3Source::kMaxFrameSize = (1 << 12); /* 4096 bytes */
|
|
|
|
|
MP3Source::MP3Source(
|
|
|
|
|
MetaDataBase &meta, DataSourceHelper *source,
|
|
|
|
|
AMediaFormat *meta, DataSourceHelper *source,
|
|
|
|
|
off64_t first_frame_pos, uint32_t fixed_header,
|
|
|
|
|
MP3Seeker *seeker)
|
|
|
|
|
: mMeta(meta),
|
|
|
|
@ -462,7 +466,7 @@ MP3Source::~MP3Source() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t MP3Source::start() {
|
|
|
|
|
media_status_t MP3Source::start() {
|
|
|
|
|
CHECK(!mStarted);
|
|
|
|
|
|
|
|
|
|
mGroup = new MediaBufferGroup;
|
|
|
|
@ -477,10 +481,10 @@ status_t MP3Source::start() {
|
|
|
|
|
|
|
|
|
|
mStarted = true;
|
|
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
return AMEDIA_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t MP3Source::stop() {
|
|
|
|
|
media_status_t MP3Source::stop() {
|
|
|
|
|
CHECK(mStarted);
|
|
|
|
|
|
|
|
|
|
delete mGroup;
|
|
|
|
@ -488,15 +492,14 @@ status_t MP3Source::stop() {
|
|
|
|
|
|
|
|
|
|
mStarted = false;
|
|
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
return AMEDIA_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t MP3Source::getFormat(MetaDataBase &meta) {
|
|
|
|
|
meta = mMeta;
|
|
|
|
|
return OK;
|
|
|
|
|
media_status_t MP3Source::getFormat(AMediaFormat *meta) {
|
|
|
|
|
return AMediaFormat_copy(meta, mMeta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t MP3Source::read(
|
|
|
|
|
media_status_t MP3Source::read(
|
|
|
|
|
MediaBufferBase **out, const ReadOptions *options) {
|
|
|
|
|
*out = NULL;
|
|
|
|
|
|
|
|
|
@ -509,11 +512,11 @@ status_t MP3Source::read(
|
|
|
|
|
if (mSeeker == NULL
|
|
|
|
|
|| !mSeeker->getOffsetForTime(&actualSeekTimeUs, &mCurrentPos)) {
|
|
|
|
|
int32_t bitrate;
|
|
|
|
|
if (!mMeta.findInt32(kKeyBitRate, &bitrate)) {
|
|
|
|
|
if (!AMediaFormat_getInt32(mMeta, AMEDIAFORMAT_KEY_BIT_RATE, &bitrate)) {
|
|
|
|
|
// bitrate is in bits/sec.
|
|
|
|
|
ALOGI("no bitrate");
|
|
|
|
|
|
|
|
|
|
return ERROR_UNSUPPORTED;
|
|
|
|
|
return AMEDIA_ERROR_UNSUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mCurrentTimeUs = seekTimeUs;
|
|
|
|
@ -530,7 +533,7 @@ status_t MP3Source::read(
|
|
|
|
|
MediaBufferBase *buffer;
|
|
|
|
|
status_t err = mGroup->acquire_buffer(&buffer);
|
|
|
|
|
if (err != OK) {
|
|
|
|
|
return err;
|
|
|
|
|
return AMEDIA_ERROR_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t frame_size;
|
|
|
|
@ -543,7 +546,7 @@ status_t MP3Source::read(
|
|
|
|
|
buffer->release();
|
|
|
|
|
buffer = NULL;
|
|
|
|
|
|
|
|
|
|
return (n < 0 ? n : ERROR_END_OF_STREAM);
|
|
|
|
|
return (n < 0 ? AMEDIA_ERROR_UNKNOWN : AMEDIA_ERROR_END_OF_STREAM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t header = U32_AT((const uint8_t *)buffer->data());
|
|
|
|
@ -572,7 +575,7 @@ status_t MP3Source::read(
|
|
|
|
|
buffer->release();
|
|
|
|
|
buffer = NULL;
|
|
|
|
|
|
|
|
|
|
return ERROR_END_OF_STREAM;
|
|
|
|
|
return AMEDIA_ERROR_END_OF_STREAM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mCurrentPos = pos;
|
|
|
|
@ -587,7 +590,7 @@ status_t MP3Source::read(
|
|
|
|
|
buffer->release();
|
|
|
|
|
buffer = NULL;
|
|
|
|
|
|
|
|
|
|
return (n < 0 ? n : ERROR_END_OF_STREAM);
|
|
|
|
|
return (n < 0 ? AMEDIA_ERROR_UNKNOWN : AMEDIA_ERROR_END_OF_STREAM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer->set_range(0, frame_size);
|
|
|
|
@ -602,40 +605,40 @@ status_t MP3Source::read(
|
|
|
|
|
|
|
|
|
|
*out = buffer;
|
|
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
return AMEDIA_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status_t MP3Extractor::getMetaData(MetaDataBase &meta) {
|
|
|
|
|
meta.clear();
|
|
|
|
|
media_status_t MP3Extractor::getMetaData(AMediaFormat *meta) {
|
|
|
|
|
AMediaFormat_clear(meta);
|
|
|
|
|
if (mInitCheck != OK) {
|
|
|
|
|
return UNKNOWN_ERROR;
|
|
|
|
|
return AMEDIA_ERROR_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
meta.setCString(kKeyMIMEType, "audio/mpeg");
|
|
|
|
|
AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, "audio/mpeg");
|
|
|
|
|
|
|
|
|
|
DataSourceHelper helper(mDataSource);
|
|
|
|
|
ID3 id3(&helper);
|
|
|
|
|
|
|
|
|
|
if (!id3.isValid()) {
|
|
|
|
|
return OK;
|
|
|
|
|
return AMEDIA_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Map {
|
|
|
|
|
int key;
|
|
|
|
|
const char *key;
|
|
|
|
|
const char *tag1;
|
|
|
|
|
const char *tag2;
|
|
|
|
|
};
|
|
|
|
|
static const Map kMap[] = {
|
|
|
|
|
{ kKeyAlbum, "TALB", "TAL" },
|
|
|
|
|
{ kKeyArtist, "TPE1", "TP1" },
|
|
|
|
|
{ kKeyAlbumArtist, "TPE2", "TP2" },
|
|
|
|
|
{ kKeyComposer, "TCOM", "TCM" },
|
|
|
|
|
{ kKeyGenre, "TCON", "TCO" },
|
|
|
|
|
{ kKeyTitle, "TIT2", "TT2" },
|
|
|
|
|
{ kKeyYear, "TYE", "TYER" },
|
|
|
|
|
{ kKeyAuthor, "TXT", "TEXT" },
|
|
|
|
|
{ kKeyCDTrackNumber, "TRK", "TRCK" },
|
|
|
|
|
{ kKeyDiscNumber, "TPA", "TPOS" },
|
|
|
|
|
{ kKeyCompilation, "TCP", "TCMP" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_ALBUM, "TALB", "TAL" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_ARTIST, "TPE1", "TP1" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_ALBUMARTIST, "TPE2", "TP2" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_COMPOSER, "TCOM", "TCM" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_GENRE, "TCON", "TCO" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_TITLE, "TIT2", "TT2" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_YEAR, "TYE", "TYER" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_AUTHOR, "TXT", "TEXT" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_CDTRACKNUMBER, "TRK", "TRCK" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_DISCNUMBER, "TPA", "TPOS" },
|
|
|
|
|
{ AMEDIAFORMAT_KEY_COMPILATION, "TCP", "TCMP" },
|
|
|
|
|
};
|
|
|
|
|
static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
|
|
|
|
|
|
|
|
|
@ -655,7 +658,7 @@ status_t MP3Extractor::getMetaData(MetaDataBase &meta) {
|
|
|
|
|
it->getString(&s);
|
|
|
|
|
delete it;
|
|
|
|
|
|
|
|
|
|
meta.setCString(kMap[i].key, s);
|
|
|
|
|
AMediaFormat_setString(meta, kMap[i].key, s.string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t dataSize;
|
|
|
|
@ -663,21 +666,20 @@ status_t MP3Extractor::getMetaData(MetaDataBase &meta) {
|
|
|
|
|
const void *data = id3.getAlbumArt(&dataSize, &mime);
|
|
|
|
|
|
|
|
|
|
if (data) {
|
|
|
|
|
meta.setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
|
|
|
|
|
meta.setCString(kKeyAlbumArtMIME, mime.string());
|
|
|
|
|
AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_ALBUMART, data, dataSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
|
return AMEDIA_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CMediaExtractor* CreateExtractor(
|
|
|
|
|
static CMediaExtractorV2* CreateExtractor(
|
|
|
|
|
CDataSource *source,
|
|
|
|
|
void *meta) {
|
|
|
|
|
Mp3Meta *metaData = static_cast<Mp3Meta *>(meta);
|
|
|
|
|
return wrap(new MP3Extractor(new DataSourceHelper(source), metaData));
|
|
|
|
|
return wrapV2(new MP3Extractor(new DataSourceHelper(source), metaData));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CreatorFunc Sniff(
|
|
|
|
|
static CreatorFuncV2 Sniff(
|
|
|
|
|
CDataSource *source, float *confidence, void **meta,
|
|
|
|
|
FreeMetaFunc *freeMeta) {
|
|
|
|
|
off64_t pos = 0;
|
|
|
|
@ -714,11 +716,11 @@ extern "C" {
|
|
|
|
|
__attribute__ ((visibility ("default")))
|
|
|
|
|
ExtractorDef GETEXTRACTORDEF() {
|
|
|
|
|
return {
|
|
|
|
|
EXTRACTORDEF_VERSION,
|
|
|
|
|
EXTRACTORDEF_VERSION_CURRENT,
|
|
|
|
|
UUID("812a3f6c-c8cf-46de-b529-3774b14103d4"),
|
|
|
|
|
1, // version
|
|
|
|
|
"MP3 Extractor",
|
|
|
|
|
{ Sniff }
|
|
|
|
|
{ .v2 = Sniff }
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|