diff options
-rw-r--r-- | include/media/stagefright/ACodec.h | 4 | ||||
-rw-r--r-- | include/media/stagefright/MediaDefs.h | 1 | ||||
-rw-r--r-- | include/media/stagefright/OMXCodec.h | 2 | ||||
-rw-r--r-- | media/libstagefright/ACodec.cpp | 101 | ||||
-rw-r--r-- | media/libstagefright/MediaDefs.cpp | 1 | ||||
-rw-r--r-- | media/libstagefright/OMXCodec.cpp | 53 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ATSParser.cpp | 6 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ATSParser.h | 4 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ESQueue.cpp | 190 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ESQueue.h | 2 |
10 files changed, 363 insertions, 1 deletions
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index e796ab3..f1636e6 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -116,6 +116,7 @@ private: kWhatStart = 'star', kWhatRequestIDRFrame = 'ridr', kWhatSetParameters = 'setP', + kWhatSubmitOutputMetaDataBufferIfEOS = 'subm', }; enum { @@ -212,6 +213,7 @@ private: OMX_U32 *nMinUndequeuedBuffers); status_t allocateOutputMetaDataBuffers(); status_t submitOutputMetaDataBuffer(); + void signalSubmitOutputMetaDataBufferIfEOS_workaround(); status_t allocateOutputBuffersFromNativeWindow(); status_t cancelBufferToNativeWindow(BufferInfo *info); status_t freeOutputBuffersNotOwnedByComponent(); @@ -247,6 +249,8 @@ private: int32_t numChannels, int32_t sampleRate, int32_t bitRate, int32_t aacProfile, bool isADTS); + status_t setupAC3Codec(bool encoder, int32_t numChannels, int32_t sampleRate); + status_t selectAudioPortFormat( OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat); diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h index 85693d4..cf5beda 100644 --- a/include/media/stagefright/MediaDefs.h +++ b/include/media/stagefright/MediaDefs.h @@ -44,6 +44,7 @@ extern const char *MEDIA_MIMETYPE_AUDIO_RAW; extern const char *MEDIA_MIMETYPE_AUDIO_FLAC; extern const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS; extern const char *MEDIA_MIMETYPE_AUDIO_MSGSM; +extern const char *MEDIA_MIMETYPE_AUDIO_AC3; extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4; extern const char *MEDIA_MIMETYPE_CONTAINER_WAV; diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index daaf20f..5121c17 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -248,6 +248,8 @@ private: int32_t numChannels, int32_t sampleRate, int32_t bitRate, int32_t aacProfile, bool isADTS); + status_t setAC3Format(int32_t numChannels, int32_t sampleRate); + void setG711Format(int32_t numChannels); status_t setVideoPortFormatType( diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index 76a3358..c68dfc1 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -35,7 +35,9 @@ #include <media/hardware/HardwareAPI.h> +#include <OMX_AudioExt.h> #include <OMX_Component.h> +#include <OMX_IndexExt.h> #include "include/avc_utils.h" @@ -452,6 +454,18 @@ void ACodec::signalRequestIDRFrame() { (new AMessage(kWhatRequestIDRFrame, id()))->post(); } +// *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED *** +// Some codecs may return input buffers before having them processed. +// This causes a halt if we already signaled an EOS on the input +// port. For now keep submitting an output buffer if there was an +// EOS on the input port, but not yet on the output port. +void ACodec::signalSubmitOutputMetaDataBufferIfEOS_workaround() { + if (mPortEOS[kPortIndexInput] && !mPortEOS[kPortIndexOutput] && + mMetaDataBuffersToSubmit > 0) { + (new AMessage(kWhatSubmitOutputMetaDataBufferIfEOS, id()))->post(); + } +} + status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput); @@ -965,6 +979,10 @@ status_t ACodec::setComponentRole( "audio_decoder.flac", "audio_encoder.flac" }, { MEDIA_MIMETYPE_AUDIO_MSGSM, "audio_decoder.gsm", "audio_encoder.gsm" }, + { MEDIA_MIMETYPE_VIDEO_MPEG2, + "video_decoder.mpeg2", "video_encoder.mpeg2" }, + { MEDIA_MIMETYPE_AUDIO_AC3, + "audio_decoder.ac3", "audio_encoder.ac3" }, }; static const size_t kNumMimeToRole = @@ -1256,6 +1274,15 @@ status_t ACodec::configureCodec( } else { err = setupRawAudioFormat(kPortIndexInput, sampleRate, numChannels); } + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC3)) { + int32_t numChannels; + int32_t sampleRate; + if (!msg->findInt32("channel-count", &numChannels) + || !msg->findInt32("sample-rate", &sampleRate)) { + err = INVALID_OPERATION; + } else { + err = setupAC3Codec(encoder, numChannels, sampleRate); + } } if (err != OK) { @@ -1452,6 +1479,44 @@ status_t ACodec::setupAACCodec( mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); } +status_t ACodec::setupAC3Codec( + bool encoder, int32_t numChannels, int32_t sampleRate) { + status_t err = setupRawAudioFormat( + encoder ? kPortIndexInput : kPortIndexOutput, sampleRate, numChannels); + + if (err != OK) { + return err; + } + + if (encoder) { + ALOGW("AC3 encoding is not supported."); + return INVALID_OPERATION; + } + + OMX_AUDIO_PARAM_ANDROID_AC3TYPE def; + InitOMXParams(&def); + def.nPortIndex = kPortIndexInput; + + err = mOMX->getParameter( + mNode, + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3, + &def, + sizeof(def)); + + if (err != OK) { + return err; + } + + def.nChannels = numChannels; + def.nSampleRate = sampleRate; + + return mOMX->setParameter( + mNode, + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3, + &def, + sizeof(def)); +} + static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate( bool isAMRWB, int32_t bps) { if (isAMRWB) { @@ -2546,7 +2611,7 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { { OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio; - switch (audioDef->eEncoding) { + switch ((int)audioDef->eEncoding) { case OMX_AUDIO_CodingPCM: { OMX_AUDIO_PARAM_PCMMODETYPE params; @@ -2652,6 +2717,24 @@ void ACodec::sendFormatChange(const sp<AMessage> &reply) { break; } + case OMX_AUDIO_CodingAndroidAC3: + { + OMX_AUDIO_PARAM_ANDROID_AC3TYPE params; + InitOMXParams(¶ms); + params.nPortIndex = kPortIndexOutput; + + CHECK_EQ((status_t)OK, mOMX->getParameter( + mNode, + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3, + ¶ms, + sizeof(params))); + + notify->setString("mime", MEDIA_MIMETYPE_AUDIO_AC3); + notify->setInt32("channel-count", params.nChannels); + notify->setInt32("sample-rate", params.nSampleRate); + break; + } + default: TRESPASS(); } @@ -4036,6 +4119,9 @@ void ACodec::ExecutingState::submitOutputMetaBuffers() { break; } } + + // *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED *** + mCodec->signalSubmitOutputMetaDataBufferIfEOS_workaround(); } void ACodec::ExecutingState::submitRegularOutputBuffers() { @@ -4184,6 +4270,19 @@ bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) { break; } + // *** NOTE: THE FOLLOWING WORKAROUND WILL BE REMOVED *** + case kWhatSubmitOutputMetaDataBufferIfEOS: + { + if (mCodec->mPortEOS[kPortIndexInput] && + !mCodec->mPortEOS[kPortIndexOutput]) { + status_t err = mCodec->submitOutputMetaDataBuffer(); + if (err == OK) { + mCodec->signalSubmitOutputMetaDataBufferIfEOS_workaround(); + } + } + return true; + } + default: handled = BaseState::onMessageReceived(msg); break; diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index b5d4e44..340cba7 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -42,6 +42,7 @@ const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw"; const char *MEDIA_MIMETYPE_AUDIO_FLAC = "audio/flac"; const char *MEDIA_MIMETYPE_AUDIO_AAC_ADTS = "audio/aac-adts"; const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm"; +const char *MEDIA_MIMETYPE_AUDIO_AC3 = "audio/ac3"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4"; const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/x-wav"; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 43736ad..625922f 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -40,7 +40,9 @@ #include <utils/Vector.h> #include <OMX_Audio.h> +#include <OMX_AudioExt.h> #include <OMX_Component.h> +#include <OMX_IndexExt.h> #include "include/avc_utils.h" @@ -528,6 +530,17 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { sampleRate, numChannels); } + } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AC3, mMIME)) { + int32_t numChannels; + int32_t sampleRate; + CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); + CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); + + status_t err = setAC3Format(numChannels, sampleRate); + if (err != OK) { + CODEC_LOGE("setAC3Format() failed (err = %d)", err); + return err; + } } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_ALAW, mMIME) || !strcasecmp(MEDIA_MIMETYPE_AUDIO_G711_MLAW, mMIME)) { // These are PCM-like formats with a fixed sample rate but @@ -1394,6 +1407,10 @@ void OMXCodec::setComponentRole( "audio_decoder.flac", "audio_encoder.flac" }, { MEDIA_MIMETYPE_AUDIO_MSGSM, "audio_decoder.gsm", "audio_encoder.gsm" }, + { MEDIA_MIMETYPE_VIDEO_MPEG2, + "video_decoder.mpeg2", "video_encoder.mpeg2" }, + { MEDIA_MIMETYPE_AUDIO_AC3, + "audio_decoder.ac3", "audio_encoder.ac3" }, }; static const size_t kNumMimeToRole = @@ -3489,6 +3506,31 @@ status_t OMXCodec::setAACFormat( return OK; } +status_t OMXCodec::setAC3Format(int32_t numChannels, int32_t sampleRate) { + OMX_AUDIO_PARAM_ANDROID_AC3TYPE def; + InitOMXParams(&def); + def.nPortIndex = kPortIndexInput; + + status_t err = mOMX->getParameter( + mNode, + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3, + &def, + sizeof(def)); + + if (err != OK) { + return err; + } + + def.nChannels = numChannels; + def.nSampleRate = sampleRate; + + return mOMX->setParameter( + mNode, + (OMX_INDEXTYPE)OMX_IndexParamAudioAndroidAc3, + &def, + sizeof(def)); +} + void OMXCodec::setG711Format(int32_t numChannels) { CHECK(!mIsEncoder); setRawAudioFormat(kPortIndexInput, 8000, numChannels); @@ -4422,6 +4464,17 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { mOutputFormat->setInt32(kKeyChannelCount, numChannels); mOutputFormat->setInt32(kKeySampleRate, sampleRate); mOutputFormat->setInt32(kKeyBitRate, bitRate); + } else if (audio_def->eEncoding == + (OMX_AUDIO_CODINGTYPE)OMX_AUDIO_CodingAndroidAC3) { + mOutputFormat->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3); + int32_t numChannels, sampleRate, bitRate; + inputFormat->findInt32(kKeyChannelCount, &numChannels); + inputFormat->findInt32(kKeySampleRate, &sampleRate); + inputFormat->findInt32(kKeyBitRate, &bitRate); + mOutputFormat->setInt32(kKeyChannelCount, numChannels); + mOutputFormat->setInt32(kKeySampleRate, sampleRate); + mOutputFormat->setInt32(kKeyBitRate, bitRate); } else { CHECK(!"Should not be here. Unknown audio encoding."); } diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 175a263..cb57a2f 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -506,6 +506,11 @@ ATSParser::Stream::Stream( ElementaryStreamQueue::PCM_AUDIO); break; + case STREAMTYPE_AC3: + mQueue = new ElementaryStreamQueue( + ElementaryStreamQueue::AC3); + break; + default: break; } @@ -614,6 +619,7 @@ bool ATSParser::Stream::isAudio() const { case STREAMTYPE_MPEG2_AUDIO: case STREAMTYPE_MPEG2_AUDIO_ADTS: case STREAMTYPE_PCM_AUDIO: + case STREAMTYPE_AC3: return true; default: diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index a10edc9..d4e30b4 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -88,6 +88,10 @@ struct ATSParser : public RefBase { STREAMTYPE_MPEG2_AUDIO_ADTS = 0x0f, STREAMTYPE_MPEG4_VIDEO = 0x10, STREAMTYPE_H264 = 0x1b, + + // From ATSC A/53 Part 3:2009, 6.7.1 + STREAMTYPE_AC3 = 0x81, + STREAMTYPE_PCM_AUDIO = 0x83, }; diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp index e0ff0d1..5c7e741 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.cpp +++ b/media/libstagefright/mpeg2ts/ESQueue.cpp @@ -56,6 +56,122 @@ void ElementaryStreamQueue::clear(bool clearFormat) { } } +// Parse AC3 header assuming the current ptr is start position of syncframe, +// update metadata only applicable, and return the payload size +static unsigned parseAC3SyncFrame( + const uint8_t *ptr, size_t size, sp<MetaData> *metaData) { + static const unsigned channelCountTable[] = {2, 1, 2, 3, 4, 4, 5, 6}; + static const unsigned samplingRateTable[] = {48000, 44100, 32000}; + static const unsigned rates[] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, + 320, 384, 448, 512, 576, 640}; + + static const unsigned frameSizeTable[19][3] = { + { 64, 69, 96 }, + { 80, 87, 120 }, + { 96, 104, 144 }, + { 112, 121, 168 }, + { 128, 139, 192 }, + { 160, 174, 240 }, + { 192, 208, 288 }, + { 224, 243, 336 }, + { 256, 278, 384 }, + { 320, 348, 480 }, + { 384, 417, 576 }, + { 448, 487, 672 }, + { 512, 557, 768 }, + { 640, 696, 960 }, + { 768, 835, 1152 }, + { 896, 975, 1344 }, + { 1024, 1114, 1536 }, + { 1152, 1253, 1728 }, + { 1280, 1393, 1920 }, + }; + + ABitReader bits(ptr, size); + unsigned syncStartPos = 0; // in bytes + if (bits.numBitsLeft() < 16) { + return 0; + } + if (bits.getBits(16) != 0x0B77) { + return 0; + } + + if (bits.numBitsLeft() < 16 + 2 + 6 + 5 + 3 + 3) { + ALOGV("Not enough bits left for further parsing"); + return 0; + } + bits.skipBits(16); // crc1 + + unsigned fscod = bits.getBits(2); + if (fscod == 3) { + ALOGW("Incorrect fscod in AC3 header"); + return 0; + } + + unsigned frmsizecod = bits.getBits(6); + if (frmsizecod > 37) { + ALOGW("Incorrect frmsizecod in AC3 header"); + return 0; + } + + unsigned bsid = bits.getBits(5); + if (bsid > 8) { + ALOGW("Incorrect bsid in AC3 header. Possibly E-AC-3?"); + return 0; + } + + unsigned bsmod = bits.getBits(3); + unsigned acmod = bits.getBits(3); + unsigned cmixlev = 0; + unsigned surmixlev = 0; + unsigned dsurmod = 0; + + if ((acmod & 1) > 0 && acmod != 1) { + if (bits.numBitsLeft() < 2) { + return 0; + } + cmixlev = bits.getBits(2); + } + if ((acmod & 4) > 0) { + if (bits.numBitsLeft() < 2) { + return 0; + } + surmixlev = bits.getBits(2); + } + if (acmod == 2) { + if (bits.numBitsLeft() < 2) { + return 0; + } + dsurmod = bits.getBits(2); + } + + if (bits.numBitsLeft() < 1) { + return 0; + } + unsigned lfeon = bits.getBits(1); + + unsigned samplingRate = samplingRateTable[fscod]; + unsigned payloadSize = frameSizeTable[frmsizecod >> 1][fscod]; + if (fscod == 1) { + payloadSize += frmsizecod & 1; + } + payloadSize <<= 1; // convert from 16-bit words to bytes + + unsigned channelCount = channelCountTable[acmod] + lfeon; + + if (metaData != NULL) { + (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC3); + (*metaData)->setInt32(kKeyChannelCount, channelCount); + (*metaData)->setInt32(kKeySampleRate, samplingRate); + } + + return payloadSize; +} + +static bool IsSeeminglyValidAC3Header(const uint8_t *ptr, size_t size) { + return parseAC3SyncFrame(ptr, size, NULL) > 0; +} + static bool IsSeeminglyValidADTSHeader(const uint8_t *ptr, size_t size) { if (size < 3) { // Not enough data to verify header. @@ -224,6 +340,33 @@ status_t ElementaryStreamQueue::appendData( break; } + case AC3: + { + uint8_t *ptr = (uint8_t *)data; + + ssize_t startOffset = -1; + for (size_t i = 0; i < size; ++i) { + if (IsSeeminglyValidAC3Header(&ptr[i], size - i)) { + startOffset = i; + break; + } + } + + if (startOffset < 0) { + return ERROR_MALFORMED; + } + + if (startOffset > 0) { + ALOGI("found something resembling an AC3 syncword at " + "offset %d", + startOffset); + } + + data = &ptr[startOffset]; + size -= startOffset; + break; + } + case MPEG_AUDIO: { uint8_t *ptr = (uint8_t *)data; @@ -328,6 +471,8 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() { return dequeueAccessUnitH264(); case AAC: return dequeueAccessUnitAAC(); + case AC3: + return dequeueAccessUnitAC3(); case MPEG_VIDEO: return dequeueAccessUnitMPEGVideo(); case MPEG4_VIDEO: @@ -340,6 +485,51 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() { } } +sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAC3() { + unsigned syncStartPos = 0; // in bytes + unsigned payloadSize = 0; + sp<MetaData> format = new MetaData; + while (true) { + if (syncStartPos + 2 >= mBuffer->size()) { + return NULL; + } + + payloadSize = parseAC3SyncFrame( + mBuffer->data() + syncStartPos, + mBuffer->size() - syncStartPos, + &format); + if (payloadSize > 0) { + break; + } + ++syncStartPos; + } + + if (mBuffer->size() < syncStartPos + payloadSize) { + ALOGV("Not enough buffer size for AC3"); + return NULL; + } + + if (mFormat == NULL) { + mFormat = format; + } + + sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize); + memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize); + + int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize); + CHECK_GE(timeUs, 0ll); + accessUnit->meta()->setInt64("timeUs", timeUs); + + memmove( + mBuffer->data(), + mBuffer->data() + syncStartPos + payloadSize, + mBuffer->size() - syncStartPos - payloadSize); + + mBuffer->setRange(0, mBuffer->size() - syncStartPos - payloadSize); + + return accessUnit; +} + sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitPCMAudio() { if (mBuffer->size() < 4) { return NULL; diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h index 66a8087..a2cca77 100644 --- a/media/libstagefright/mpeg2ts/ESQueue.h +++ b/media/libstagefright/mpeg2ts/ESQueue.h @@ -32,6 +32,7 @@ struct ElementaryStreamQueue { enum Mode { H264, AAC, + AC3, MPEG_AUDIO, MPEG_VIDEO, MPEG4_VIDEO, @@ -67,6 +68,7 @@ private: sp<ABuffer> dequeueAccessUnitH264(); sp<ABuffer> dequeueAccessUnitAAC(); + sp<ABuffer> dequeueAccessUnitAC3(); sp<ABuffer> dequeueAccessUnitMPEGAudio(); sp<ABuffer> dequeueAccessUnitMPEGVideo(); sp<ABuffer> dequeueAccessUnitMPEG4Video(); |