From b636abde14f2612ea236257846b9ab15d87d4623 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 19 Mar 2012 13:49:43 -0700 Subject: Parse mp3 encoder padding/delay Get the mp3 encoder padding and delay from a XING frame or iTunSMPB tag. Change-Id: Icde598c8857d7e7c187a718f478ee9799d6a1b8a --- include/media/stagefright/MetaData.h | 2 ++ media/libstagefright/MP3Extractor.cpp | 39 +++++++++++++++++++++++-- media/libstagefright/XINGSeeker.cpp | 32 +++++++++++++++++++-- media/libstagefright/id3/ID3.cpp | 47 +++++++++++++++++++++++-------- media/libstagefright/include/ID3.h | 3 +- media/libstagefright/include/XINGSeeker.h | 5 ++++ 6 files changed, 111 insertions(+), 17 deletions(-) diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h index 8b4b8ed..00b8679 100644 --- a/include/media/stagefright/MetaData.h +++ b/include/media/stagefright/MetaData.h @@ -70,6 +70,8 @@ enum { kKeyThumbnailTime = 'thbT', // int64_t (usecs) kKeyTrackID = 'trID', kKeyIsDRM = 'idrm', // int32_t (bool) + kKeyEncoderDelay = 'encd', // int32_t (frames) + kKeyEncoderPadding = 'encp', // int32_t (frames) kKeyAlbum = 'albu', // cstring kKeyArtist = 'arti', // cstring diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index 69209b5..1886050 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -311,10 +311,18 @@ MP3Extractor::MP3Extractor( mMeta->setInt32(kKeyBitRate, bitrate * 1000); mMeta->setInt32(kKeyChannelCount, num_channels); - mSeeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos); + sp seeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos); - if (mSeeker == NULL) { + if (seeker == NULL) { mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos); + } else { + mSeeker = seeker; + int encd = seeker->getEncoderDelay(); + int encp = seeker->getEncoderPadding(); + if (encd != 0 || encp != 0) { + mMeta->setInt32(kKeyEncoderDelay, encd); + mMeta->setInt32(kKeyEncoderPadding, encp); + } } if (mSeeker != NULL) { @@ -547,6 +555,33 @@ sp MP3Extractor::getMetaData() { return meta; } + ID3::Iterator *com = new ID3::Iterator(id3, "COM"); + if (com->done()) { + delete com; + com = new ID3::Iterator(id3, "COMM"); + } + while(!com->done()) { + String8 commentdesc; + String8 commentvalue; + com->getString(&commentdesc, &commentvalue); + const char * desc = commentdesc.string(); + const char * value = commentvalue.string(); + + // first 3 characters are the language, which we don't care about + if(strlen(desc) > 3 && strcmp(desc + 3, "iTunSMPB") == 0) { + + int32_t delay, padding; + if (sscanf(value, " %*x %x %x %*x", &delay, &padding) == 2) { + mMeta->setInt32(kKeyEncoderDelay, delay); + mMeta->setInt32(kKeyEncoderPadding, padding); + } + break; + } + com->next(); + } + delete com; + com = NULL; + struct Map { int key; const char *tag1; diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp index 8c99c76..9c91134 100644 --- a/media/libstagefright/XINGSeeker.cpp +++ b/media/libstagefright/XINGSeeker.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#define LOG_TAG "XINGSEEKER" +#include + #include "include/XINGSeeker.h" #include "include/avc_utils.h" @@ -24,7 +27,9 @@ namespace android { XINGSeeker::XINGSeeker() : mDurationUs(-1), - mSizeBytes(0) { + mSizeBytes(0), + mEncoderDelay(0), + mEncoderPadding(0) { } bool XINGSeeker::getDuration(int64_t *durationUs) { @@ -76,8 +81,6 @@ sp XINGSeeker::CreateFromSource( seeker->mFirstFramePos = first_frame_pos; - ALOGI("xingseeker first frame pos: %lld", first_frame_pos); - seeker->mSizeBytes = 0; seeker->mTOCValid = false; seeker->mDurationUs = 0; @@ -111,6 +114,8 @@ sp XINGSeeker::CreateFromSource( else offset += 9; } + int xingbase = offset; + if (source->readAt(offset, &buffer, 4) < 4) { // XING header ID return NULL; } @@ -161,10 +166,31 @@ sp XINGSeeker::CreateFromSource( // do something with the quality indicator offset += 4; } + + if (source->readAt(xingbase + 0xaf - 0x24, &buffer, 1) < 1) { // encoding flags + return false; + } + + ALOGV("nogap preceding: %s, nogap continued in next: %s", + (buffer[0] & 0x80) ? "true" : "false", + (buffer[0] & 0x40) ? "true" : "false"); #endif + if (source->readAt(xingbase + 0xb1 - 0x24, &buffer, 3) == 3) { + seeker->mEncoderDelay = (buffer[0] << 4) + (buffer[1] >> 4); + seeker->mEncoderPadding = ((buffer[1] & 0xf) << 8) + buffer[2]; + } + return seeker; } +int32_t XINGSeeker::getEncoderDelay() { + return mEncoderDelay; +} + +int32_t XINGSeeker::getEncoderPadding() { + return mEncoderPadding; +} + } // namespace android diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index 2e92926..ca14054 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -463,40 +463,65 @@ static void convertISO8859ToString8( tmp = NULL; } -void ID3::Iterator::getString(String8 *id) const { +// the 2nd argument is used to get the data following the \0 in a comment field +void ID3::Iterator::getString(String8 *id, String8 *comment) const { + getstring(id, false); + if (comment != NULL) { + getstring(comment, true); + } +} + +// comment fields (COM/COMM) contain an initial short descriptor, followed by \0, +// followed by more data. The data following the \0 can be retrieved by setting +// "otherdata" to true. +void ID3::Iterator::getstring(String8 *id, bool otherdata) const { id->setTo(""); - if (mFrameData == NULL) { + const uint8_t *frameData = mFrameData; + if (frameData == NULL) { return; } + uint8_t encoding = *frameData; + if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) { if (mOffset == 126 || mOffset == 127) { // Special treatment for the track number and genre. char tmp[16]; - sprintf(tmp, "%d", (int)*mFrameData); + sprintf(tmp, "%d", (int)*frameData); id->setTo(tmp); return; } - convertISO8859ToString8(mFrameData, mFrameSize, id); + convertISO8859ToString8(frameData, mFrameSize, id); return; } size_t n = mFrameSize - getHeaderLength() - 1; + if (otherdata) { + // skip past the encoding, language, and the 0 separator + frameData += 4; + int32_t i = n - 4; + while(--i >= 0 && *++frameData != 0) ; + int skipped = (frameData - mFrameData); + if (skipped >= n) { + return; + } + n -= skipped; + } - if (*mFrameData == 0x00) { + if (encoding == 0x00) { // ISO 8859-1 - convertISO8859ToString8(mFrameData + 1, n, id); - } else if (*mFrameData == 0x03) { + convertISO8859ToString8(frameData + 1, n, id); + } else if (encoding == 0x03) { // UTF-8 - id->setTo((const char *)(mFrameData + 1), n); - } else if (*mFrameData == 0x02) { + id->setTo((const char *)(frameData + 1), n); + } else if (encoding == 0x02) { // UTF-16 BE, no byte order mark. // API wants number of characters, not number of bytes... int len = n / 2; - const char16_t *framedata = (const char16_t *) (mFrameData + 1); + const char16_t *framedata = (const char16_t *) (frameData + 1); char16_t *framedatacopy = NULL; #if BYTE_ORDER == LITTLE_ENDIAN framedatacopy = new char16_t[len]; @@ -513,7 +538,7 @@ void ID3::Iterator::getString(String8 *id) const { // UCS-2 // API wants number of characters, not number of bytes... int len = n / 2; - const char16_t *framedata = (const char16_t *) (mFrameData + 1); + const char16_t *framedata = (const char16_t *) (frameData + 1); char16_t *framedatacopy = NULL; if (*framedata == 0xfffe) { // endianness marker doesn't match host endianness, convert diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h index 98c82a4..8714008 100644 --- a/media/libstagefright/include/ID3.h +++ b/media/libstagefright/include/ID3.h @@ -50,7 +50,7 @@ struct ID3 { bool done() const; void getID(String8 *id) const; - void getString(String8 *s) const; + void getString(String8 *s, String8 *ss = NULL) const; const uint8_t *getData(size_t *length) const; void next(); @@ -65,6 +65,7 @@ struct ID3 { void findFrame(); size_t getHeaderLength() const; + void getstring(String8 *s, bool secondhalf) const; Iterator(const Iterator &); Iterator &operator=(const Iterator &); diff --git a/media/libstagefright/include/XINGSeeker.h b/media/libstagefright/include/XINGSeeker.h index 8510979..c408576 100644 --- a/media/libstagefright/include/XINGSeeker.h +++ b/media/libstagefright/include/XINGSeeker.h @@ -31,10 +31,15 @@ struct XINGSeeker : public MP3Seeker { virtual bool getDuration(int64_t *durationUs); virtual bool getOffsetForTime(int64_t *timeUs, off64_t *pos); + virtual int32_t getEncoderDelay(); + virtual int32_t getEncoderPadding(); + private: int64_t mFirstFramePos; int64_t mDurationUs; int32_t mSizeBytes; + int32_t mEncoderDelay; + int32_t mEncoderPadding; // TOC entries in XING header. Skip the first one since it's always 0. unsigned char mTOC[99]; -- cgit v1.1