| /* |
| * 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 "ESQueue" |
| #include <media/stagefright/foundation/ADebug.h> |
| |
| #include "ESQueue.h" |
| |
| #include <media/stagefright/foundation/hexdump.h> |
| #include <media/stagefright/foundation/ABitReader.h> |
| #include <media/stagefright/foundation/ABuffer.h> |
| #include <media/stagefright/foundation/AMessage.h> |
| #include <media/stagefright/foundation/ByteUtils.h> |
| #include <media/stagefright/foundation/avc_utils.h> |
| #include <media/stagefright/MediaErrors.h> |
| #include <media/stagefright/MediaDefs.h> |
| #include <media/stagefright/MetaData.h> |
| #include <media/stagefright/MetaDataUtils.h> |
| #include <media/cas/DescramblerAPI.h> |
| #include <media/hardware/CryptoAPI.h> |
| |
| #include <inttypes.h> |
| #include <netinet/in.h> |
| |
| #ifdef ENABLE_CRYPTO |
| #include "HlsSampleDecryptor.h" |
| #endif |
| |
| namespace android { |
| |
| ElementaryStreamQueue::ElementaryStreamQueue(Mode mode, uint32_t flags) |
| : mMode(mode), |
| mFlags(flags), |
| mEOSReached(false), |
| mCASystemId(0), |
| mAUIndex(0) { |
| |
| ALOGV("ElementaryStreamQueue(%p) mode %x flags %x isScrambled %d isSampleEncrypted %d", |
| this, mode, flags, isScrambled(), isSampleEncrypted()); |
| |
| // Create the decryptor anyway since we don't know the use-case unless key is provided |
| // Won't decrypt if key info not available (e.g., scanner/extractor just parsing ts files) |
| mSampleDecryptor = isSampleEncrypted() ? |
| #ifdef ENABLE_CRYPTO |
| new HlsSampleDecryptor |
| #else |
| new SampleDecryptor |
| #endif |
| : NULL; |
| } |
| |
| sp<MetaData> ElementaryStreamQueue::getFormat() { |
| return mFormat; |
| } |
| |
| void ElementaryStreamQueue::clear(bool clearFormat) { |
| if (mBuffer != NULL) { |
| mBuffer->setRange(0, 0); |
| } |
| |
| mRangeInfos.clear(); |
| |
| if (mScrambledBuffer != NULL) { |
| mScrambledBuffer->setRange(0, 0); |
| } |
| mScrambledRangeInfos.clear(); |
| |
| if (clearFormat) { |
| mFormat.clear(); |
| } |
| |
| mEOSReached = false; |
| } |
| |
| bool ElementaryStreamQueue::isScrambled() const { |
| return (mFlags & kFlag_ScrambledData) != 0; |
| } |
| |
| void ElementaryStreamQueue::setCasInfo( |
| int32_t systemId, const std::vector<uint8_t> &sessionId) { |
| mCASystemId = systemId; |
| mCasSessionId = sessionId; |
| } |
| |
| static int32_t readVariableBits(ABitReader &bits, int32_t nbits) { |
| int32_t value = 0; |
| int32_t more_bits = 1; |
| |
| while (more_bits) { |
| value += bits.getBits(nbits); |
| more_bits = bits.getBits(1); |
| if (!more_bits) |
| break; |
| value++; |
| value <<= nbits; |
| } |
| return value; |
| } |
| |
| // 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, 3, 4, 4, 5}; |
| static const unsigned samplingRateTable[] = {48000, 44100, 32000}; |
| |
| 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); |
| 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; |
| } |
| |
| bits.skipBits(3); // bsmod |
| unsigned acmod = bits.getBits(3); |
| |
| if ((acmod & 1) > 0 && acmod != 1) { |
| if (bits.numBitsLeft() < 2) { |
| return 0; |
| } |
| bits.skipBits(2); //cmixlev |
| } |
| if ((acmod & 4) > 0) { |
| if (bits.numBitsLeft() < 2) { |
| return 0; |
| } |
| bits.skipBits(2); //surmixlev |
| } |
| if (acmod == 2) { |
| if (bits.numBitsLeft() < 2) { |
| return 0; |
| } |
| bits.skipBits(2); //dsurmod |
| } |
| |
| 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; |
| } |
| |
| // Parse EAC3 header assuming the current ptr is start position of syncframe, |
| // update metadata only applicable, and return the payload size |
| // ATSC A/52:2012 E2.3.1 |
| static unsigned parseEAC3SyncFrame( |
| const uint8_t *ptr, size_t size, sp<MetaData> *metaData) { |
| static const unsigned channelCountTable[] = {2, 1, 2, 3, 3, 4, 4, 5}; |
| static const unsigned samplingRateTable[] = {48000, 44100, 32000}; |
| static const unsigned samplingRateTable2[] = {24000, 22050, 16000}; |
| |
| ABitReader bits(ptr, size); |
| if (bits.numBitsLeft() < 16) { |
| ALOGE("Not enough bits left for further parsing"); |
| return 0; |
| } |
| if (bits.getBits(16) != 0x0B77) { |
| ALOGE("No valid sync word in EAC3 header"); |
| return 0; |
| } |
| |
| // we parse up to bsid so there needs to be at least that many bits |
| if (bits.numBitsLeft() < 2 + 3 + 11 + 2 + 2 + 3 + 1 + 5) { |
| ALOGE("Not enough bits left for further parsing"); |
| return 0; |
| } |
| |
| unsigned strmtyp = bits.getBits(2); |
| if (strmtyp == 3) { |
| ALOGE("Incorrect strmtyp in EAC3 header"); |
| return 0; |
| } |
| |
| unsigned substreamid = bits.getBits(3); |
| // only the first independent stream is supported |
| if ((strmtyp == 0 || strmtyp == 2) && substreamid != 0) |
| return 0; |
| |
| unsigned frmsiz = bits.getBits(11); |
| unsigned fscod = bits.getBits(2); |
| |
| unsigned samplingRate = 0; |
| if (fscod == 0x3) { |
| unsigned fscod2 = bits.getBits(2); |
| if (fscod2 == 3) { |
| ALOGW("Incorrect fscod2 in EAC3 header"); |
| return 0; |
| } |
| samplingRate = samplingRateTable2[fscod2]; |
| } else { |
| samplingRate = samplingRateTable[fscod]; |
| bits.skipBits(2); // numblkscod |
| } |
| |
| unsigned acmod = bits.getBits(3); |
| unsigned lfeon = bits.getBits(1); |
| unsigned bsid = bits.getBits(5); |
| if (bsid < 11 || bsid > 16) { |
| ALOGW("Incorrect bsid in EAC3 header. Could be AC-3 or some unknown EAC3 format"); |
| return 0; |
| } |
| |
| // we currently only support the first independant stream |
| if (metaData != NULL && (strmtyp == 0 || strmtyp == 2)) { |
| unsigned channelCount = channelCountTable[acmod] + lfeon; |
| ALOGV("EAC3 channelCount = %d", channelCount); |
| ALOGV("EAC3 samplingRate = %d", samplingRate); |
| (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EAC3); |
| (*metaData)->setInt32(kKeyChannelCount, channelCount); |
| (*metaData)->setInt32(kKeySampleRate, samplingRate); |
| (*metaData)->setInt32(kKeyIsSyncFrame, 1); |
| } |
| |
| unsigned payloadSize = frmsiz + 1; |
| payloadSize <<= 1; // convert from 16-bit words to bytes |
| |
| return payloadSize; |
| } |
| |
| // Parse AC4 header assuming the current ptr is start position of syncframe |
| // and update frameSize and metadata. |
| static status_t parseAC4SyncFrame( |
| const uint8_t *ptr, size_t size, unsigned &frameSize, sp<MetaData> *metaData) { |
| // ETSI TS 103 190-2 V1.1.1 (2015-09), Annex C |
| // The sync_word can be either 0xAC40 or 0xAC41. |
| static const int kSyncWordAC40 = 0xAC40; |
| static const int kSyncWordAC41 = 0xAC41; |
| |
| size_t headerSize = 0; |
| ABitReader bits(ptr, size); |
| int32_t syncWord = bits.getBits(16); |
| if ((syncWord != kSyncWordAC40) && (syncWord != kSyncWordAC41)) { |
| ALOGE("Invalid syncword in AC4 header"); |
| return ERROR_MALFORMED; |
| } |
| headerSize += 2; |
| |
| frameSize = bits.getBits(16); |
| headerSize += 2; |
| if (frameSize == 0xFFFF) { |
| frameSize = bits.getBits(24); |
| headerSize += 3; |
| } |
| |
| if (frameSize == 0) { |
| ALOGE("Invalid frame size in AC4 header"); |
| return ERROR_MALFORMED; |
| } |
| frameSize += headerSize; |
| // If the sync_word is 0xAC41, a crc_word is also transmitted. |
| if (syncWord == kSyncWordAC41) { |
| frameSize += 2; // crc_word |
| } |
| ALOGV("AC4 frameSize = %u", frameSize); |
| |
| // ETSI TS 103 190-2 V1.1.1 6.2.1.1 |
| uint32_t bitstreamVersion = bits.getBits(2); |
| if (bitstreamVersion == 3) { |
| bitstreamVersion += readVariableBits(bits, 2); |
| } |
| |
| bits.skipBits(10); // Sequence Counter |
| |
| uint32_t bWaitFrames = bits.getBits(1); |
| if (bWaitFrames) { |
| uint32_t waitFrames = bits.getBits(3); |
| if (waitFrames > 0) { |
| bits.skipBits(2); // br_code; |
| } |
| } |
| |
| // ETSI TS 103 190 V1.1.1 Table 82 |
| bool fsIndex = bits.getBits(1); |
| uint32_t samplingRate = fsIndex ? 48000 : 44100; |
| |
| if (metaData != NULL) { |
| ALOGV("dequeueAccessUnitAC4 Setting mFormat"); |
| (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4); |
| (*metaData)->setInt32(kKeyIsSyncFrame, 1); |
| // [FIXME] AC4 channel count is defined per presentation. Provide a default channel count |
| // as stereo for the entire stream. |
| (*metaData)->setInt32(kKeyChannelCount, 2); |
| (*metaData)->setInt32(kKeySampleRate, samplingRate); |
| } |
| return OK; |
| } |
| |
| #define RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bitstream, size) \ |
| do { \ |
| if ((bitstream).numBitsLeft() < (size)) { \ |
| ALOGE("Not enough bits left for further parsing"); \ |
| return ERROR_MALFORMED; } \ |
| } while (0) |
| |
| // Parse DTS Digital Surround and DTS Express(LBR) stream header |
| static status_t parseDTSHDSyncFrame( |
| const uint8_t *ptr, size_t size, unsigned &frameSize, sp<MetaData> *metaData) { |
| static const unsigned channelCountTable[] = {1, 2, 2, 2, 2, 3, 3, 4, |
| 4, 5, 6, 6, 6, 7, 8, 8}; |
| static const unsigned samplingRateTableCoreSS[] = {0, 8000, 16000, 32000, 0, 0, 11025, 22050, |
| 44100, 0, 0, 12000, 24000, 48000, 0, 0}; |
| static const unsigned samplingRateTableExtSS[] = {8000, 16000, 32000, 64000, 128000, |
| 22050, 44100, 88200, 176400, 352800, |
| 12000, 24000, 48000, 96000, 192000, 384000}; |
| |
| const uint32_t DTSHD_SYNC_CORE_16BIT_BE = 0x7ffe8001; |
| const uint32_t DTSHD_SYNC_EXSS_16BIT_BE = 0x64582025; |
| |
| uint32_t numChannels = 0, samplingRate = 0; |
| bool isLBR = false; |
| |
| ABitReader bits(ptr, size); |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 32); |
| uint32_t dtshdSyncWord = bits.getBits(32); |
| |
| // Expecting DTS Digital Surround or DTS Express(LBR) streams only |
| if (dtshdSyncWord == DTSHD_SYNC_CORE_16BIT_BE) { // DTS Digital Surround Header |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (1 + 5 + 1 + 7 + 14 + 6 + 4 + 15 + 2)); |
| |
| // FTYPE, SHORT, CRC, NBLKS |
| bits.skipBits(1 + 5 + 1 + 7); |
| |
| frameSize = bits.getBits(14) + 1; |
| uint32_t amode = bits.getBits(6); |
| uint32_t freqIndex = bits.getBits(4); |
| |
| // RATE, FIXEDBIT, DYNF, TIMEF, AUXF, HDCD, EXT_AUDIO_ID, EXT_AUDIO, ASPF |
| bits.skipBits(5 + 1 + 1 + 1 + 1 + 1 + 3 + 1 + 1); |
| |
| uint32_t lfeFlag = bits.getBits(2); |
| numChannels = (amode <= 15) ? channelCountTable[amode] : 0; |
| numChannels += ((lfeFlag == 1) || (lfeFlag == 2)) ? 1 : 0; |
| samplingRate = (freqIndex <= 15) ? samplingRateTableCoreSS[freqIndex] : 0; |
| |
| isLBR = false; |
| } else if (dtshdSyncWord == DTSHD_SYNC_EXSS_16BIT_BE) { // DTS Express(LBR) Header |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (8 + 2 + 1)); |
| |
| uint32_t extHeadersize, extSSFsize; |
| uint32_t numAudioPresent = 1, numAssets = 1; |
| uint32_t nuActiveExSSMask[8]; |
| |
| // userDefinedBits |
| bits.skipBits(8); |
| |
| uint32_t extSSIndex = bits.getBits(2); |
| uint32_t headerSizeType = bits.getBits(1); |
| |
| if (headerSizeType == 0) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (8 + 16)); |
| |
| extHeadersize = bits.getBits(8) + 1; |
| extSSFsize = bits.getBits(16) + 1; |
| } else { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (12 + 20)); |
| |
| extHeadersize = bits.getBits(12) + 1; |
| extSSFsize = bits.getBits(20) + 1; |
| } |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (1)); |
| |
| uint32_t staticFieldsPresent = bits.getBits(1); |
| |
| if (staticFieldsPresent) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (2 + 3 + 1)); |
| |
| // nuRefClockCode, nuExSSFrameDurationCode |
| bits.skipBits(2 + 3); |
| |
| if (bits.getBits(1)) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (32 + 4)); |
| |
| bits.skipBits(32 + 4); |
| } |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (3 + 3)); |
| |
| // numAudioPresent, numAssets |
| bits.skipBits(3 + 3); |
| |
| for (uint32_t nAuPr = 0; nAuPr < numAudioPresent; nAuPr++) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (extSSIndex + 1)); |
| |
| nuActiveExSSMask[nAuPr] = bits.getBits(extSSIndex + 1); |
| } |
| |
| for (uint32_t nAuPr = 0; nAuPr < numAudioPresent; nAuPr++) { |
| for (uint32_t nSS = 0; nSS < extSSIndex + 1; nSS++) { |
| if (((nuActiveExSSMask[nAuPr] >> nSS) & 0x1) == 1) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 8); |
| |
| // nuActiveAssetMask |
| bits.skipBits(8); |
| } |
| } |
| } |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 1); |
| |
| // bMixMetadataEnbl |
| if (bits.getBits(1)) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (2 + 2 + 2)); |
| |
| // nuMixMetadataAdjLevel |
| bits.skipBits(2); |
| |
| uint32_t bits4MixOutMask = (bits.getBits(2) + 1) << 2; |
| uint32_t numMixOutConfigs = bits.getBits(2) + 1; |
| |
| for (int ns = 0; ns < numMixOutConfigs; ns++) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, bits4MixOutMask); |
| |
| // nuMixOutChMask |
| bits.skipBits(bits4MixOutMask); |
| } |
| } |
| } |
| |
| for (int nAst = 0; nAst < numAssets; nAst++) { |
| int bits4ExSSFsize = (headerSizeType == 0) ? 16 : 20; |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, bits4ExSSFsize); |
| |
| bits.skipBits(bits4ExSSFsize); |
| } |
| |
| /* Asset descriptor */ |
| for (int nAst = 0; nAst < numAssets; nAst++) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (9 + 3)); |
| |
| // nuAssetDescriptFsize, nuAssetIndex |
| bits.skipBits(9 + 3); |
| |
| if (staticFieldsPresent) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 1); |
| |
| // bAssetTypeDescrPresent |
| if (bits.getBits(1)) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 4); |
| |
| // nuAssetTypeDescriptor |
| bits.skipBits(4); |
| } |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 1); |
| |
| // bLanguageDescrPresent |
| if (bits.getBits(1)) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 24); |
| |
| // LanguageDescriptor |
| bits.skipBits(24); |
| } |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 1); |
| |
| // bInfoTextPresent |
| if (bits.getBits(1)) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 10); |
| |
| uint32_t nuInfoTextByteSize = bits.getBits(10) + 1; |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (nuInfoTextByteSize * 8)); |
| |
| // InfoTextString |
| bits.skipBits(nuInfoTextByteSize * 8); |
| } |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (5 + 4 + 8)); |
| |
| // nuBitResolution |
| bits.skipBits(5); |
| |
| samplingRate = samplingRateTableExtSS[bits.getBits(4)]; |
| numChannels = bits.getBits(8) + 1; |
| } |
| } |
| |
| frameSize = extHeadersize + extSSFsize; |
| isLBR = true; |
| } else { |
| ALOGE("No valid sync word in DTS/DTSHD header"); |
| return ERROR_MALFORMED; |
| } |
| |
| if (metaData != NULL) { |
| if (isLBR) { |
| (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_DTS_HD); |
| (*metaData)->setInt32(kKeyAudioProfile, 0x2); // CodecProfileLevel.DTS_HDProfileLBR |
| } else { |
| (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_DTS); |
| } |
| (*metaData)->setInt32(kKeyChannelCount, numChannels); |
| (*metaData)->setInt32(kKeySampleRate, samplingRate); |
| } |
| return OK; |
| } |
| |
| static status_t extractVarLenBitFields( |
| ABitReader *bits, size_t *bitsUsed, uint32_t *value, |
| unsigned ucTable[], bool extractAndAddFlag) { |
| |
| static const unsigned bitsUsedTbl[8] = {1, 1, 1, 1, 2, 2, 3, 3}; // prefix code lengths |
| static const unsigned indexTbl[8] = {0, 0, 0, 0, 1, 1, 2, 3}; // code to prefix code index map |
| |
| /* Clone the bitstream */ |
| ABitReader bitStream(bits->data(), bits->numBitsLeft() / 8); |
| ABitReader bitstreamClone(bits->data(), bits->numBitsLeft() / 8); |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bitstreamClone, 3); |
| |
| unsigned code = bitstreamClone.getBits(3); |
| unsigned totalBitsUsed = bitsUsedTbl[code]; |
| unsigned unIndex = indexTbl[code]; |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bitStream, totalBitsUsed); |
| |
| bitStream.skipBits(totalBitsUsed); |
| |
| uint32_t unValue = 0; |
| if (ucTable[unIndex] > 0) { |
| if (extractAndAddFlag) { |
| for (unsigned un = 0; un < unIndex; un++) { |
| unValue += (1 << ucTable[un]); |
| } |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bitStream, ucTable[unIndex]); |
| |
| unValue += bitStream.getBits(ucTable[unIndex]); |
| totalBitsUsed += ucTable[unIndex]; |
| } else { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bitStream, ucTable[unIndex]); |
| |
| unValue += bitStream.getBits(ucTable[unIndex]); |
| totalBitsUsed += ucTable[unIndex]; |
| } |
| } |
| |
| *bitsUsed = (size_t)totalBitsUsed; |
| *value = unValue; |
| return OK; |
| } |
| |
| // Parse DTS UHD Profile-2 stream header |
| static status_t parseDTSUHDSyncFrame( |
| const uint8_t *ptr, size_t size, unsigned &frameSize, sp<MetaData> *metaData) { |
| |
| static const uint32_t DTSUHD_SYNC_CORE_16BIT_BE = 0x40411BF2; |
| static const uint32_t DTSUHD_NONSYNC_CORE_16BIT_BE = 0x71C442E8; |
| |
| unsigned audioSamplRate = 0; |
| |
| ABitReader bits(ptr, size); |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 32); |
| |
| uint32_t syncWord = bits.getBits(32); |
| |
| bool isSyncFrameFlag = false; |
| switch (syncWord) { |
| case DTSUHD_SYNC_CORE_16BIT_BE: |
| isSyncFrameFlag = true; |
| break; |
| case DTSUHD_NONSYNC_CORE_16BIT_BE: |
| isSyncFrameFlag = false; |
| break; |
| default: |
| ALOGE("No valid sync word in DTSUHD header"); |
| return ERROR_MALFORMED; // invalid sync word |
| } |
| |
| unsigned uctable1[4] = { 5, 8, 10, 12 }; |
| uint32_t sizeOfFTOCPayload = 0; |
| size_t nuBitsUsed = 0; |
| status_t status = OK; |
| |
| status = extractVarLenBitFields(&bits, &nuBitsUsed, &sizeOfFTOCPayload, uctable1, true); |
| |
| if (status != OK) { |
| ALOGE("Failed to extractVarLenBitFields from DTSUHD header"); |
| return ERROR_MALFORMED; |
| } |
| |
| bits.skipBits(nuBitsUsed); |
| |
| if (isSyncFrameFlag) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (1 + 2 + 3 + 2 + 1)); |
| |
| // FullChannelBasedMixFlag, ETSI TS 103 491 V1.2.1, Section 6.4.6.1 |
| if (!(bits.getBits(1))) { |
| // This implementation only supports full channel mask-based |
| // audio presentation (i.e. 2.0, 5.1, 11.1 mix without objects) |
| ALOGE("Objects not supported, only DTSUHD full channel mask-based mix"); |
| return ERROR_MALFORMED; |
| } |
| |
| // BaseDuration, FrameDuration |
| bits.skipBits(2 + 3); |
| |
| unsigned clockRateIndex = bits.getBits(2); |
| unsigned clockRateHertz = 0; |
| |
| switch (clockRateIndex) { |
| case 0: |
| clockRateHertz = 32000; |
| break; |
| case 1: |
| clockRateHertz = 44100; |
| break; |
| case 2: |
| clockRateHertz = 48000; |
| break; |
| default: |
| ALOGE("Invalid clockRateIndex in DTSUHD header"); |
| return ERROR_MALFORMED; |
| } |
| |
| if (bits.getBits(1)) { |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, (32 + 4)); |
| |
| bits.skipBits(32 + 4); |
| } |
| |
| RETURN_ERROR_IF_NOT_ENOUGH_BYTES_LEFT(bits, 2); |
| |
| unsigned samplRateMultiplier = (1 << bits.getBits(2)); |
| audioSamplRate = clockRateHertz * samplRateMultiplier; |
| } |
| |
| uint32_t chunkPayloadBytes = 0; |
| int numOfMDChunks = isSyncFrameFlag ? 1 : 0; // Metadata chunks |
| for (int nmdc = 0; nmdc < numOfMDChunks; nmdc++) { |
| unsigned uctable2[4] = {6, 9, 12, 15}; |
| uint32_t nuMDChunkSize = 0; |
| nuBitsUsed = 0; |
| |
| status = extractVarLenBitFields(&bits, &nuBitsUsed, &nuMDChunkSize, uctable2, true); |
| if (status != OK) { |
| ALOGE("Failed to extractVarLenBitFields from DTSUHD header"); |
| return ERROR_MALFORMED; |
| } |
| |
| bits.skipBits(nuBitsUsed); |
| |
| if (nuMDChunkSize > 32767) { |
| ALOGE("Unsupported number of metadata chunks in DTSUHD header"); |
| return ERROR_MALFORMED; |
| } |
| chunkPayloadBytes += nuMDChunkSize; |
| } |
| |
| // Ony one audio chunk is supported |
| int numAudioChunks = 1; |
| for (int nac = 0; nac < numAudioChunks; nac++) { |
| uint32_t acID = 256, nuAudioChunkSize = 0; |
| |
| // isSyncFrameFlag means that ACID is present |
| if (isSyncFrameFlag) { |
| unsigned uctable3[4] = {2, 4, 6, 8}; |
| nuBitsUsed = 0; |
| |
| status = extractVarLenBitFields(&bits, &nuBitsUsed, &acID, uctable3, true); |
| |
| if (status != OK) { |
| ALOGE("Failed to extractVarLenBitFields from DTSUHD header"); |
| return ERROR_MALFORMED; |
| } |
| |
| bits.skipBits(nuBitsUsed); |
| } |
| |
| nuBitsUsed = 0; |
| if (acID == 0) { |
| nuAudioChunkSize = 0; |
| } else { |
| unsigned uctable4[4] = {9, 11, 13, 16}; |
| |
| status = extractVarLenBitFields(&bits, &nuBitsUsed, &nuAudioChunkSize, uctable4, true); |
| |
| if (status != OK) { |
| ALOGE("Failed to extractVarLenBitFields from DTSUHD header"); |
| return ERROR_MALFORMED; |
| } |
| } |
| |
| if (nuAudioChunkSize > 65535){ |
| ALOGE("Unsupported number of audio chunks in DTSUHD header"); |
| return ERROR_MALFORMED; |
| } |
| |
| chunkPayloadBytes += nuAudioChunkSize; |
| } |
| |
| frameSize = (sizeOfFTOCPayload + 1) + chunkPayloadBytes; |
| |
| if (metaData != NULL) { |
| (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_DTS_UHD); |
| (*metaData)->setInt32(kKeyAudioProfile, 0x2); // CodecProfileLevel.DTS_UHDProfileP2 |
| (*metaData)->setInt32(kKeyChannelCount, 2); // Setting default channel count as stereo |
| (*metaData)->setInt32(kKeySampleRate, audioSamplRate); |
| } |
| |
| return OK; |
| } |
| |
| static status_t isSeeminglyValidDTSHDHeader(const uint8_t *ptr, size_t size,unsigned &frameSize) |
| { |
| return parseDTSHDSyncFrame(ptr, size, frameSize, NULL); |
| } |
| |
| static status_t isSeeminglyValidDTSUHDHeader(const uint8_t *ptr, size_t size,unsigned &frameSize) |
| { |
| return parseDTSUHDSyncFrame(ptr, size, frameSize, NULL); |
| } |
| |
| static status_t IsSeeminglyValidAC4Header(const uint8_t *ptr, size_t size, unsigned &frameSize) { |
| return parseAC4SyncFrame(ptr, size, frameSize, NULL); |
| } |
| |
| static bool IsSeeminglyValidADTSHeader( |
| const uint8_t *ptr, size_t size, size_t *frameLength) { |
| if (size < 7) { |
| // Not enough data to verify header. |
| return false; |
| } |
| |
| if (ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) { |
| return false; |
| } |
| |
| unsigned layer = (ptr[1] >> 1) & 3; |
| |
| if (layer != 0) { |
| return false; |
| } |
| |
| unsigned ID = (ptr[1] >> 3) & 1; |
| unsigned profile_ObjectType = ptr[2] >> 6; |
| |
| if (ID == 1 && profile_ObjectType == 3) { |
| // MPEG-2 profile 3 is reserved. |
| return false; |
| } |
| |
| size_t frameLengthInHeader = |
| ((ptr[3] & 3) << 11) + (ptr[4] << 3) + ((ptr[5] >> 5) & 7); |
| if (frameLengthInHeader > size) { |
| return false; |
| } |
| |
| *frameLength = frameLengthInHeader; |
| return true; |
| } |
| |
| static bool IsSeeminglyValidMPEGAudioHeader(const uint8_t *ptr, size_t size) { |
| if (size < 3) { |
| // Not enough data to verify header. |
| return false; |
| } |
| |
| if (ptr[0] != 0xff || (ptr[1] >> 5) != 0x07) { |
| return false; |
| } |
| |
| unsigned ID = (ptr[1] >> 3) & 3; |
| |
| if (ID == 1) { |
| return false; // reserved |
| } |
| |
| unsigned layer = (ptr[1] >> 1) & 3; |
| |
| if (layer == 0) { |
| return false; // reserved |
| } |
| |
| unsigned bitrateIndex = (ptr[2] >> 4); |
| |
| if (bitrateIndex == 0x0f) { |
| return false; // reserved |
| } |
| |
| unsigned samplingRateIndex = (ptr[2] >> 2) & 3; |
| |
| if (samplingRateIndex == 3) { |
| return false; // reserved |
| } |
| |
| return true; |
| } |
| |
| status_t ElementaryStreamQueue::appendData( |
| const void *data, size_t size, int64_t timeUs, |
| int32_t payloadOffset, uint32_t pesScramblingControl) { |
| |
| if (mEOSReached) { |
| ALOGE("appending data after EOS"); |
| return ERROR_MALFORMED; |
| } |
| |
| if (!isScrambled() && (mBuffer == NULL || mBuffer->size() == 0)) { |
| switch (mMode) { |
| case H264: |
| case MPEG_VIDEO: |
| { |
| #if 0 |
| if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) { |
| return ERROR_MALFORMED; |
| } |
| #else |
| uint8_t *ptr = (uint8_t *)data; |
| |
| ssize_t startOffset = -1; |
| for (size_t i = 0; i + 2 < size; ++i) { |
| if (!memcmp("\x00\x00\x01", &ptr[i], 3)) { |
| startOffset = i; |
| break; |
| } |
| } |
| |
| if (startOffset < 0) { |
| return ERROR_MALFORMED; |
| } |
| |
| if (mFormat == NULL && startOffset > 0) { |
| ALOGI("found something resembling an H.264/MPEG syncword " |
| "at offset %zd", |
| startOffset); |
| } |
| |
| data = &ptr[startOffset]; |
| size -= startOffset; |
| #endif |
| break; |
| } |
| |
| case MPEG4_VIDEO: |
| { |
| #if 0 |
| if (size < 3 || memcmp("\x00\x00\x01", data, 3)) { |
| return ERROR_MALFORMED; |
| } |
| #else |
| uint8_t *ptr = (uint8_t *)data; |
| |
| ssize_t startOffset = -1; |
| for (size_t i = 0; i + 2 < size; ++i) { |
| if (!memcmp("\x00\x00\x01", &ptr[i], 3)) { |
| startOffset = i; |
| break; |
| } |
| } |
| |
| if (startOffset < 0) { |
| return ERROR_MALFORMED; |
| } |
| |
| if (startOffset > 0) { |
| ALOGI("found something resembling an H.264/MPEG syncword " |
| "at offset %zd", |
| startOffset); |
| } |
| |
| data = &ptr[startOffset]; |
| size -= startOffset; |
| #endif |
| break; |
| } |
| |
| case AAC: |
| { |
| uint8_t *ptr = (uint8_t *)data; |
| |
| #if 0 |
| if (size < 2 || ptr[0] != 0xff || (ptr[1] >> 4) != 0x0f) { |
| return ERROR_MALFORMED; |
| } |
| #else |
| ssize_t startOffset = -1; |
| size_t frameLength; |
| for (size_t i = 0; i < size; ++i) { |
| if (IsSeeminglyValidADTSHeader( |
| &ptr[i], size - i, &frameLength)) { |
| startOffset = i; |
| break; |
| } |
| } |
| |
| if (startOffset < 0) { |
| return ERROR_MALFORMED; |
| } |
| |
| if (startOffset > 0) { |
| ALOGI("found something resembling an AAC syncword at " |
| "offset %zd", |
| startOffset); |
| } |
| |
| if (frameLength != size - startOffset) { |
| ALOGV("First ADTS AAC frame length is %zd bytes, " |
| "while the buffer size is %zd bytes.", |
| frameLength, size - startOffset); |
| } |
| |
| data = &ptr[startOffset]; |
| size -= startOffset; |
| #endif |
| break; |
| } |
| |
| case AC3: |
| case EAC3: |
| { |
| uint8_t *ptr = (uint8_t *)data; |
| |
| ssize_t startOffset = -1; |
| for (size_t i = 0; i < size; ++i) { |
| unsigned payloadSize = 0; |
| if (mMode == AC3) { |
| payloadSize = parseAC3SyncFrame(&ptr[i], size - i, NULL); |
| } else if (mMode == EAC3) { |
| payloadSize = parseEAC3SyncFrame(&ptr[i], size - i, NULL); |
| } |
| if (payloadSize > 0) { |
| startOffset = i; |
| break; |
| } |
| } |
| |
| if (startOffset < 0) { |
| return ERROR_MALFORMED; |
| } |
| |
| if (startOffset > 0) { |
| ALOGI("found something resembling an (E)AC3 syncword at " |
| "offset %zd", |
| startOffset); |
| } |
| |
| data = &ptr[startOffset]; |
| size -= startOffset; |
| break; |
| } |
| |
| case AC4: |
| { |
| uint8_t *ptr = (uint8_t *)data; |
| unsigned frameSize = 0; |
| ssize_t startOffset = -1; |
| |
| // A valid AC4 stream should have minimum of 7 bytes in its buffer. |
| // (Sync header 4 bytes + AC4 toc 3 bytes) |
| if (size < 7) { |
| return ERROR_MALFORMED; |
| } |
| for (size_t i = 0; i < size; ++i) { |
| if (IsSeeminglyValidAC4Header(&ptr[i], size - i, frameSize) == OK) { |
| startOffset = i; |
| break; |
| } |
| } |
| |
| if (startOffset < 0) { |
| return ERROR_MALFORMED; |
| } |
| |
| if (startOffset > 0) { |
| ALOGI("found something resembling an AC4 syncword at " |
| "offset %zd", |
| startOffset); |
| } |
| if (frameSize != size - startOffset) { |
| ALOGV("AC4 frame size is %u bytes, while the buffer size is %zd bytes.", |
| frameSize, size - startOffset); |
| } |
| |
| data = &ptr[startOffset]; |
| size -= startOffset; |
| break; |
| } |
| |
| case MPEG_AUDIO: |
| { |
| uint8_t *ptr = (uint8_t *)data; |
| |
| ssize_t startOffset = -1; |
| for (size_t i = 0; i < size; ++i) { |
| if (IsSeeminglyValidMPEGAudioHeader(&ptr[i], size - i)) { |
| startOffset = i; |
| break; |
| } |
| } |
| |
| if (startOffset < 0) { |
| return ERROR_MALFORMED; |
| } |
| |
| if (startOffset > 0) { |
| ALOGI("found something resembling an MPEG audio " |
| "syncword at offset %zd", |
| startOffset); |
| } |
| |
| data = &ptr[startOffset]; |
| size -= startOffset; |
| break; |
| } |
| |
| case DTS: // Checking for DTS or DTS-HD syncword |
| case DTS_HD: |
| { |
| uint8_t *ptr = (uint8_t *)data; |
| unsigned frameSize = 0; |
| ssize_t startOffset = -1; |
| |
| for (size_t i = 0; i < size; ++i) { |
| if (isSeeminglyValidDTSHDHeader(&ptr[i], size - i, frameSize) == OK) { |
| startOffset = i; |
| break; |
| } |
| } |
| |
| if (startOffset < 0) { |
| return ERROR_MALFORMED; |
| } |
| if (startOffset > 0) { |
| ALOGI("found something resembling a DTS-HD syncword at " |
| "offset %zd", |
| startOffset); |
| } |
| |
| if (frameSize != size - startOffset) { |
| ALOGV("DTS-HD frame size is %u bytes, while the buffer size is %zd bytes.", |
| frameSize, size - startOffset); |
| } |
| |
| data = &ptr[startOffset]; |
| size -= startOffset; |
| break; |
| } |
| |
| case DTS_UHD: |
| { |
| uint8_t *ptr = (uint8_t *)data; |
| ssize_t startOffset = -1; |
| unsigned frameSize = 0; |
| |
| for (size_t i = 0; i < size; ++i) { |
| if (isSeeminglyValidDTSUHDHeader(&ptr[i], size - i, frameSize) == OK) { |
| startOffset = i; |
| break; |
| } |
| } |
| |
| if (startOffset < 0) { |
| return ERROR_MALFORMED; |
| } |
| if (startOffset >= 0) { |
| ALOGI("found something resembling a DTS UHD syncword" |
| "syncword at offset %zd", |
| startOffset); |
| } |
| |
| if (frameSize != size - startOffset) { |
| ALOGV("DTS-UHD frame size is %u bytes, while the buffer size is %zd bytes.", |
| frameSize, size - startOffset); |
| } |
| data = &ptr[startOffset]; |
| size -= startOffset; |
| break; |
| } |
| |
| case PCM_AUDIO: |
| case METADATA: |
| { |
| break; |
| } |
| |
| default: |
| ALOGE("Unknown mode: %d", mMode); |
| return ERROR_MALFORMED; |
| } |
| } |
| |
| size_t neededSize = (mBuffer == NULL ? 0 : mBuffer->size()) + size; |
| if (mBuffer == NULL || neededSize > mBuffer->capacity()) { |
| neededSize = (neededSize + 65535) & ~65535; |
| |
| ALOGV("resizing buffer to size %zu", neededSize); |
| |
| sp<ABuffer> buffer = new ABuffer(neededSize); |
| if (mBuffer != NULL) { |
| memcpy(buffer->data(), mBuffer->data(), mBuffer->size()); |
| buffer->setRange(0, mBuffer->size()); |
| } else { |
| buffer->setRange(0, 0); |
| } |
| |
| mBuffer = buffer; |
| } |
| |
| memcpy(mBuffer->data() + mBuffer->size(), data, size); |
| mBuffer->setRange(0, mBuffer->size() + size); |
| |
| RangeInfo info; |
| info.mLength = size; |
| info.mTimestampUs = timeUs; |
| info.mPesOffset = payloadOffset; |
| info.mPesScramblingControl = pesScramblingControl; |
| mRangeInfos.push_back(info); |
| |
| #if 0 |
| if (mMode == AAC) { |
| ALOGI("size = %zu, timeUs = %.2f secs", size, timeUs / 1E6); |
| hexdump(data, size); |
| } |
| #endif |
| |
| return OK; |
| } |
| |
| void ElementaryStreamQueue::appendScrambledData( |
| const void *data, size_t size, |
| size_t leadingClearBytes, |
| int32_t keyId, bool isSync, |
| sp<ABuffer> clearSizes, sp<ABuffer> encSizes) { |
| if (!isScrambled()) { |
| return; |
| } |
| |
| size_t neededSize = (mScrambledBuffer == NULL ? 0 : mScrambledBuffer->size()) + size; |
| if (mScrambledBuffer == NULL || neededSize > mScrambledBuffer->capacity()) { |
| neededSize = (neededSize + 65535) & ~65535; |
| |
| ALOGI("resizing scrambled buffer to size %zu", neededSize); |
| |
| sp<ABuffer> buffer = new ABuffer(neededSize); |
| if (mScrambledBuffer != NULL) { |
| memcpy(buffer->data(), mScrambledBuffer->data(), mScrambledBuffer->size()); |
| buffer->setRange(0, mScrambledBuffer->size()); |
| } else { |
| buffer->setRange(0, 0); |
| } |
| |
| mScrambledBuffer = buffer; |
| } |
| memcpy(mScrambledBuffer->data() + mScrambledBuffer->size(), data, size); |
| mScrambledBuffer->setRange(0, mScrambledBuffer->size() + size); |
| |
| ScrambledRangeInfo scrambledInfo; |
| scrambledInfo.mLength = size; |
| scrambledInfo.mLeadingClearBytes = leadingClearBytes; |
| scrambledInfo.mKeyId = keyId; |
| scrambledInfo.mIsSync = isSync; |
| scrambledInfo.mClearSizes = clearSizes; |
| scrambledInfo.mEncSizes = encSizes; |
| |
| ALOGV("[stream %d] appending scrambled range: size=%zu", mMode, size); |
| |
| mScrambledRangeInfos.push_back(scrambledInfo); |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueScrambledAccessUnit() { |
| size_t nextScan = mBuffer->size(); |
| int32_t pesOffset = 0, pesScramblingControl = 0; |
| int64_t timeUs = fetchTimestamp(nextScan, &pesOffset, &pesScramblingControl); |
| if (timeUs < 0ll) { |
| ALOGE("Negative timeUs"); |
| return NULL; |
| } |
| |
| // return scrambled unit |
| int32_t keyId = pesScramblingControl, isSync = 0, scrambledLength = 0; |
| sp<ABuffer> clearSizes, encSizes; |
| size_t leadingClearBytes; |
| while (mScrambledRangeInfos.size() > mRangeInfos.size()) { |
| auto it = mScrambledRangeInfos.begin(); |
| ALOGV("[stream %d] fetching scrambled range: size=%zu", mMode, it->mLength); |
| |
| if (scrambledLength > 0) { |
| // This shouldn't happen since we always dequeue the entire PES. |
| ALOGW("Discarding srambled length %d", scrambledLength); |
| } |
| scrambledLength = it->mLength; |
| |
| // TODO: handle key id change, use first non-zero keyId for now |
| if (keyId == 0) { |
| keyId = it->mKeyId; |
| } |
| clearSizes = it->mClearSizes; |
| encSizes = it->mEncSizes; |
| isSync = it->mIsSync; |
| leadingClearBytes = it->mLeadingClearBytes; |
| mScrambledRangeInfos.erase(it); |
| } |
| if (scrambledLength == 0) { |
| ALOGE("[stream %d] empty scrambled unit!", mMode); |
| return NULL; |
| } |
| |
| // Retrieve the leading clear bytes info, and use it to set the clear |
| // range on mBuffer. Note that the leading clear bytes includes the |
| // PES header portion, while mBuffer doesn't. |
| if ((int32_t)leadingClearBytes > pesOffset) { |
| mBuffer->setRange(0, leadingClearBytes - pesOffset); |
| } else { |
| mBuffer->setRange(0, 0); |
| } |
| |
| // Try to parse formats, and if unavailable set up a dummy format. |
| // Only support the following modes for scrambled content for now. |
| // (will be expanded later). |
| if (mFormat == NULL) { |
| mFormat = new MetaData; |
| switch (mMode) { |
| case H264: |
| { |
| if (!MakeAVCCodecSpecificData( |
| *mFormat, mBuffer->data(), mBuffer->size())) { |
| ALOGI("Creating dummy AVC format for scrambled content"); |
| |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); |
| mFormat->setInt32(kKeyWidth, 1280); |
| mFormat->setInt32(kKeyHeight, 720); |
| } |
| break; |
| } |
| case AAC: |
| { |
| if (!MakeAACCodecSpecificData( |
| *mFormat, mBuffer->data(), mBuffer->size())) { |
| ALOGI("Creating dummy AAC format for scrambled content"); |
| |
| MakeAACCodecSpecificData(*mFormat, |
| 1 /*profile*/, 7 /*sampling_freq_index*/, 1 /*channel_config*/); |
| mFormat->setInt32(kKeyIsADTS, true); |
| } |
| |
| break; |
| } |
| case MPEG_VIDEO: |
| { |
| ALOGI("Creating dummy MPEG format for scrambled content"); |
| |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2); |
| mFormat->setInt32(kKeyWidth, 1280); |
| mFormat->setInt32(kKeyHeight, 720); |
| break; |
| } |
| default: |
| { |
| ALOGE("Unknown mode for scrambled content"); |
| return NULL; |
| } |
| } |
| |
| // for MediaExtractor.CasInfo |
| mFormat->setInt32(kKeyCASystemID, mCASystemId); |
| mFormat->setData(kKeyCASessionID, |
| 0, mCasSessionId.data(), mCasSessionId.size()); |
| } |
| |
| mBuffer->setRange(0, 0); |
| |
| // copy into scrambled access unit |
| sp<ABuffer> scrambledAccessUnit = ABuffer::CreateAsCopy( |
| mScrambledBuffer->data(), scrambledLength); |
| |
| scrambledAccessUnit->meta()->setInt64("timeUs", timeUs); |
| if (isSync) { |
| scrambledAccessUnit->meta()->setInt32("isSync", 1); |
| } |
| |
| // fill in CryptoInfo fields for AnotherPacketSource::read() |
| // MediaCas doesn't use cryptoMode, but set to non-zero value here. |
| scrambledAccessUnit->meta()->setInt32( |
| "cryptoMode", CryptoPlugin::kMode_AES_CTR); |
| scrambledAccessUnit->meta()->setInt32("cryptoKey", keyId); |
| scrambledAccessUnit->meta()->setBuffer("clearBytes", clearSizes); |
| scrambledAccessUnit->meta()->setBuffer("encBytes", encSizes); |
| scrambledAccessUnit->meta()->setInt32("pesOffset", pesOffset); |
| |
| memmove(mScrambledBuffer->data(), |
| mScrambledBuffer->data() + scrambledLength, |
| mScrambledBuffer->size() - scrambledLength); |
| |
| mScrambledBuffer->setRange(0, mScrambledBuffer->size() - scrambledLength); |
| |
| ALOGV("[stream %d] dequeued scrambled AU: timeUs=%lld, size=%zu", |
| mMode, (long long)timeUs, scrambledAccessUnit->size()); |
| |
| return scrambledAccessUnit; |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() { |
| if (isScrambled()) { |
| return dequeueScrambledAccessUnit(); |
| } |
| |
| if ((mFlags & kFlag_AlignedData) && mMode == H264) { |
| if (mRangeInfos.empty()) { |
| return NULL; |
| } |
| |
| RangeInfo info = *mRangeInfos.begin(); |
| mRangeInfos.erase(mRangeInfos.begin()); |
| |
| sp<ABuffer> accessUnit = new ABuffer(info.mLength); |
| memcpy(accessUnit->data(), mBuffer->data(), info.mLength); |
| accessUnit->meta()->setInt64("timeUs", info.mTimestampUs); |
| |
| memmove(mBuffer->data(), |
| mBuffer->data() + info.mLength, |
| mBuffer->size() - info.mLength); |
| |
| mBuffer->setRange(0, mBuffer->size() - info.mLength); |
| |
| if (mFormat == NULL) { |
| mFormat = new MetaData; |
| if (!MakeAVCCodecSpecificData(*mFormat, accessUnit->data(), accessUnit->size())) { |
| mFormat.clear(); |
| } |
| } |
| |
| return accessUnit; |
| } |
| |
| switch (mMode) { |
| case H264: |
| return dequeueAccessUnitH264(); |
| case AAC: |
| return dequeueAccessUnitAAC(); |
| case AC3: |
| case EAC3: |
| return dequeueAccessUnitEAC3(); |
| case AC4: |
| return dequeueAccessUnitAC4(); |
| case MPEG_VIDEO: |
| return dequeueAccessUnitMPEGVideo(); |
| case MPEG4_VIDEO: |
| return dequeueAccessUnitMPEG4Video(); |
| case PCM_AUDIO: |
| return dequeueAccessUnitPCMAudio(); |
| case METADATA: |
| return dequeueAccessUnitMetadata(); |
| case DTS: // Using same dequeue function for both DTS and DTS-HD types. |
| case DTS_HD: |
| return dequeueAccessUnitDTSOrDTSHD(); |
| case DTS_UHD: |
| return dequeueAccessUnitDTSUHD(); |
| default: |
| if (mMode != MPEG_AUDIO) { |
| ALOGE("Unknown mode"); |
| return NULL; |
| } |
| return dequeueAccessUnitMPEGAudio(); |
| } |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitDTSOrDTSHD() { |
| unsigned syncStartPos = 0; // in bytes |
| unsigned payloadSize = 0; |
| sp<MetaData> format = new MetaData; |
| |
| ALOGV("dequeueAccessUnitDTSOrDTSHD[%d]: mBuffer %p(%zu)", mAUIndex, |
| mBuffer->data(), mBuffer->size()); |
| |
| while (true) { |
| if (syncStartPos + 4 >= mBuffer->size()) { |
| return NULL; |
| } |
| uint8_t *ptr = mBuffer->data() + syncStartPos; |
| size_t size = mBuffer->size() - syncStartPos; |
| status_t status = parseDTSHDSyncFrame(ptr, size, payloadSize, &format); |
| if (status == 0) { |
| break; |
| } |
| ++syncStartPos; |
| } |
| |
| if (mBuffer->size() < syncStartPos + payloadSize) { |
| ALOGV("Not enough buffer size for DTS/DTS-HD"); |
| return NULL; |
| } |
| |
| if (mFormat == NULL) { |
| mFormat = format; |
| } |
| |
| int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize); |
| if (timeUs < 0LL) { |
| ALOGE("negative timeUs"); |
| return NULL; |
| } |
| mAUIndex++; |
| |
| sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize); |
| memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize); |
| |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| accessUnit->meta()->setInt32("isSync", 1); |
| |
| memmove( |
| mBuffer->data(), |
| mBuffer->data() + syncStartPos + payloadSize, |
| mBuffer->size() - syncStartPos - payloadSize); |
| |
| mBuffer->setRange(0, mBuffer->size() - syncStartPos - payloadSize); |
| |
| return accessUnit; |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitDTSUHD() |
| { |
| unsigned syncStartPos = 0; // in bytes |
| unsigned payloadSize = 0; |
| sp<MetaData> format = new MetaData; |
| |
| ALOGV("dequeueAccessUnitDTSUHD[%d]: mBuffer %p(%zu)", mAUIndex, |
| mBuffer->data(), mBuffer->size()); |
| |
| while (true) { |
| if (syncStartPos + 4 >= mBuffer->size()) { |
| return NULL; |
| } |
| uint8_t *ptr = mBuffer->data() + syncStartPos; |
| size_t size = mBuffer->size() - syncStartPos; |
| status_t status = parseDTSUHDSyncFrame(ptr, size, payloadSize, &format); |
| if (status == 0) { |
| break; |
| } |
| ++syncStartPos; |
| } |
| |
| if (mBuffer->size() < syncStartPos + payloadSize) { |
| ALOGV("Not enough buffer size for DTS-UHD"); |
| return NULL; |
| } |
| |
| if (mFormat == NULL) { |
| mFormat = format; |
| } |
| |
| int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize); |
| if (timeUs < 0LL) { |
| ALOGE("negative timeUs"); |
| return NULL; |
| } |
| mAUIndex++; |
| |
| sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize); |
| memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize); |
| |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| accessUnit->meta()->setInt32("isSync", 1); |
| |
| memmove( |
| mBuffer->data(), |
| mBuffer->data() + syncStartPos + payloadSize, |
| mBuffer->size() - syncStartPos - payloadSize); |
| |
| mBuffer->setRange(0, mBuffer->size() - syncStartPos - payloadSize); |
| |
| return accessUnit; |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitEAC3() { |
| unsigned syncStartPos = 0; // in bytes |
| unsigned payloadSize = 0; |
| sp<MetaData> format = new MetaData; |
| |
| ALOGV("dequeueAccessUnitEAC3[%d]: mBuffer %p(%zu)", mAUIndex, |
| mBuffer->data(), mBuffer->size()); |
| |
| while (true) { |
| if (syncStartPos + 2 >= mBuffer->size()) { |
| return NULL; |
| } |
| |
| uint8_t *ptr = mBuffer->data() + syncStartPos; |
| size_t size = mBuffer->size() - syncStartPos; |
| if (mMode == AC3) { |
| payloadSize = parseAC3SyncFrame(ptr, size, &format); |
| } else if (mMode == EAC3) { |
| payloadSize = parseEAC3SyncFrame(ptr, size, &format); |
| } |
| if (payloadSize > 0) { |
| break; |
| } |
| |
| ALOGV("dequeueAccessUnitEAC3[%d]: syncStartPos %u payloadSize %u", |
| mAUIndex, syncStartPos, payloadSize); |
| |
| ++syncStartPos; |
| } |
| |
| if (mBuffer->size() < syncStartPos + payloadSize) { |
| ALOGV("Not enough buffer size for E/AC3"); |
| return NULL; |
| } |
| |
| if (mFormat == NULL) { |
| mFormat = format; |
| } |
| |
| int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize); |
| if (timeUs < 0ll) { |
| ALOGE("negative timeUs"); |
| return NULL; |
| } |
| |
| // Not decrypting if key info not available (e.g., scanner/extractor parsing ts files) |
| if (mSampleDecryptor != NULL) { |
| if (mMode == AC3) { |
| mSampleDecryptor->processAC3(mBuffer->data() + syncStartPos, payloadSize); |
| } else if (mMode == EAC3) { |
| ALOGE("EAC3 AU is encrypted and decryption is not supported"); |
| return NULL; |
| } |
| } |
| mAUIndex++; |
| |
| sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize); |
| memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize); |
| |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| accessUnit->meta()->setInt32("isSync", 1); |
| |
| memmove( |
| mBuffer->data(), |
| mBuffer->data() + syncStartPos + payloadSize, |
| mBuffer->size() - syncStartPos - payloadSize); |
| |
| mBuffer->setRange(0, mBuffer->size() - syncStartPos - payloadSize); |
| |
| return accessUnit; |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAC4() { |
| unsigned syncStartPos = 0; |
| unsigned payloadSize = 0; |
| sp<MetaData> format = new MetaData; |
| ALOGV("dequeueAccessUnit_AC4[%d]: mBuffer %p(%zu)", mAUIndex, mBuffer->data(), mBuffer->size()); |
| |
| // A valid AC4 stream should have minimum of 7 bytes in its buffer. |
| // (Sync header 4 bytes + AC4 toc 3 bytes) |
| if (mBuffer->size() < 7) { |
| return NULL; |
| } |
| |
| while (true) { |
| if (syncStartPos + 2 >= mBuffer->size()) { |
| return NULL; |
| } |
| |
| status_t status = parseAC4SyncFrame( |
| mBuffer->data() + syncStartPos, |
| mBuffer->size() - syncStartPos, |
| payloadSize, |
| &format); |
| if (status == OK) { |
| break; |
| } |
| |
| ALOGV("dequeueAccessUnit_AC4[%d]: syncStartPos %u payloadSize %u", |
| mAUIndex, syncStartPos, payloadSize); |
| |
| ++syncStartPos; |
| } |
| |
| if (mBuffer->size() < syncStartPos + payloadSize) { |
| ALOGV("Not enough buffer size for AC4"); |
| return NULL; |
| } |
| |
| if (mFormat == NULL) { |
| mFormat = format; |
| } |
| |
| int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize); |
| if (timeUs < 0ll) { |
| ALOGE("negative timeUs"); |
| return NULL; |
| } |
| mAUIndex++; |
| |
| sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize); |
| memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize); |
| |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| accessUnit->meta()->setInt32("isSync", 1); |
| |
| 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; |
| } |
| |
| ABitReader bits(mBuffer->data(), 4); |
| if (bits.getBits(8) != 0xa0) { |
| ALOGE("Unexpected bit values"); |
| return NULL; |
| } |
| unsigned numAUs = bits.getBits(8); |
| bits.skipBits(8); |
| bits.skipBits(2); // quantization_word_length |
| unsigned audio_sampling_frequency = bits.getBits(3); |
| unsigned num_channels = bits.getBits(3); |
| |
| if (audio_sampling_frequency != 2) { |
| ALOGE("Wrong sampling freq"); |
| return NULL; |
| } |
| if (num_channels != 1u) { |
| ALOGE("Wrong channel #"); |
| return NULL; |
| } |
| |
| if (mFormat == NULL) { |
| mFormat = new MetaData; |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); |
| mFormat->setInt32(kKeyChannelCount, 2); |
| mFormat->setInt32(kKeySampleRate, 48000); |
| mFormat->setInt32(kKeyPcmEncoding, kAudioEncodingPcm16bit); |
| } |
| |
| static const size_t kFramesPerAU = 80; |
| size_t frameSize = 2 /* numChannels */ * sizeof(int16_t); |
| |
| size_t payloadSize = numAUs * frameSize * kFramesPerAU; |
| |
| if (mBuffer->size() < 4 + payloadSize) { |
| return NULL; |
| } |
| |
| sp<ABuffer> accessUnit = new ABuffer(payloadSize); |
| memcpy(accessUnit->data(), mBuffer->data() + 4, payloadSize); |
| |
| int64_t timeUs = fetchTimestamp(payloadSize + 4); |
| if (timeUs < 0LL) { |
| ALOGE("Negative timeUs"); |
| return NULL; |
| } |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| accessUnit->meta()->setInt32("isSync", 1); |
| |
| int16_t *ptr = (int16_t *)accessUnit->data(); |
| for (size_t i = 0; i < payloadSize / sizeof(int16_t); ++i) { |
| ptr[i] = ntohs(ptr[i]); |
| } |
| |
| memmove( |
| mBuffer->data(), |
| mBuffer->data() + 4 + payloadSize, |
| mBuffer->size() - 4 - payloadSize); |
| |
| mBuffer->setRange(0, mBuffer->size() - 4 - payloadSize); |
| |
| return accessUnit; |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() { |
| if (mBuffer->size() == 0) { |
| return NULL; |
| } |
| |
| if (mRangeInfos.empty()) { |
| return NULL; |
| } |
| |
| const RangeInfo &info = *mRangeInfos.begin(); |
| if (info.mLength == 0 || mBuffer->size() < info.mLength) { |
| return NULL; |
| } |
| |
| if (info.mTimestampUs < 0LL) { |
| ALOGE("Negative info.mTimestampUs"); |
| return NULL; |
| } |
| |
| ALOGV("dequeueAccessUnit_AAC[%d]: mBuffer %zu info.mLength %zu", |
| mAUIndex, mBuffer->size(), info.mLength); |
| |
| struct ADTSPosition { |
| size_t offset; |
| size_t headerSize; |
| size_t length; |
| }; |
| |
| Vector<ADTSPosition> frames; |
| |
| // The idea here is consume all AAC frames starting at offsets before |
| // info.mLength so we can assign a meaningful timestamp without |
| // having to interpolate. |
| // The final AAC frame may well extend into the next RangeInfo but |
| // that's ok. |
| size_t offset = 0; |
| while (offset < info.mLength) { |
| if (offset + 7 > mBuffer->size()) { |
| return NULL; |
| } |
| |
| ABitReader bits(mBuffer->data() + offset, mBuffer->size() - offset); |
| |
| // adts_fixed_header |
| |
| if (bits.getBits(12) != 0xfffu) { |
| ALOGE("Wrong atds_fixed_header"); |
| return NULL; |
| } |
| bits.skipBits(3); // ID, layer |
| bool protection_absent = bits.getBits(1) != 0; |
| |
| if (mFormat == NULL) { |
| mFormat = new MetaData; |
| if (!MakeAACCodecSpecificData( |
| *mFormat, mBuffer->data() + offset, mBuffer->size() - offset)) { |
| return NULL; |
| } |
| |
| int32_t sampleRate; |
| int32_t numChannels; |
| if (!mFormat->findInt32(kKeySampleRate, &sampleRate)) { |
| ALOGE("SampleRate not found"); |
| return NULL; |
| } |
| if (!mFormat->findInt32(kKeyChannelCount, &numChannels)) { |
| ALOGE("ChannelCount not found"); |
| return NULL; |
| } |
| |
| ALOGI("found AAC codec config (%d Hz, %d channels)", |
| sampleRate, numChannels); |
| } |
| |
| // profile_ObjectType, sampling_frequency_index, private_bits, |
| // channel_configuration, original_copy, home |
| bits.skipBits(12); |
| |
| // adts_variable_header |
| |
| // copyright_identification_bit, copyright_identification_start |
| bits.skipBits(2); |
| |
| unsigned aac_frame_length = bits.getBits(13); |
| if (aac_frame_length == 0){ |
| ALOGE("b/62673179, Invalid AAC frame length!"); |
| android_errorWriteLog(0x534e4554, "62673179"); |
| return NULL; |
| } |
| |
| bits.skipBits(11); // adts_buffer_fullness |
| |
| unsigned number_of_raw_data_blocks_in_frame = bits.getBits(2); |
| |
| if (number_of_raw_data_blocks_in_frame != 0) { |
| // To be implemented. |
| ALOGE("Should not reach here."); |
| return NULL; |
| } |
| |
| if (offset + aac_frame_length > mBuffer->size()) { |
| return NULL; |
| } |
| |
| size_t headerSize = protection_absent ? 7 : 9; |
| |
| // tracking the frame positions first then decrypt only if an accessUnit to be generated |
| if (mSampleDecryptor != NULL) { |
| ADTSPosition frame = { |
| .offset = offset, |
| .headerSize = headerSize, |
| .length = aac_frame_length |
| }; |
| |
| frames.push(frame); |
| } |
| |
| offset += aac_frame_length; |
| } |
| |
| // Decrypting only if the loop didn't exit early and an accessUnit is about to be generated |
| // Not decrypting if key info not available (e.g., scanner/extractor parsing ts files) |
| if (mSampleDecryptor != NULL) { |
| for (size_t frameId = 0; frameId < frames.size(); frameId++) { |
| const ADTSPosition &frame = frames.itemAt(frameId); |
| |
| mSampleDecryptor->processAAC(frame.headerSize, |
| mBuffer->data() + frame.offset, frame.length); |
| // ALOGV("dequeueAccessUnitAAC[%zu]: while offset %zu headerSize %zu frame_len %zu", |
| // frameId, frame.offset, frame.headerSize, frame.length); |
| } |
| } |
| mAUIndex++; |
| |
| int64_t timeUs = fetchTimestamp(offset); |
| |
| sp<ABuffer> accessUnit = new ABuffer(offset); |
| memcpy(accessUnit->data(), mBuffer->data(), offset); |
| |
| memmove(mBuffer->data(), mBuffer->data() + offset, |
| mBuffer->size() - offset); |
| mBuffer->setRange(0, mBuffer->size() - offset); |
| |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| accessUnit->meta()->setInt32("isSync", 1); |
| |
| return accessUnit; |
| } |
| |
| int64_t ElementaryStreamQueue::fetchTimestamp( |
| size_t size, int32_t *pesOffset, int32_t *pesScramblingControl) { |
| int64_t timeUs = -1; |
| bool first = true; |
| |
| while (size > 0) { |
| if (mRangeInfos.empty()) { |
| return timeUs; |
| } |
| |
| RangeInfo *info = &*mRangeInfos.begin(); |
| |
| if (first) { |
| timeUs = info->mTimestampUs; |
| if (pesOffset != NULL) { |
| *pesOffset = info->mPesOffset; |
| } |
| if (pesScramblingControl != NULL) { |
| *pesScramblingControl = info->mPesScramblingControl; |
| } |
| first = false; |
| } |
| |
| if (info->mLength > size) { |
| info->mLength -= size; |
| size = 0; |
| } else { |
| size -= info->mLength; |
| |
| mRangeInfos.erase(mRangeInfos.begin()); |
| info = NULL; |
| } |
| |
| } |
| |
| if (timeUs == 0LL) { |
| ALOGV("Returning 0 timestamp"); |
| } |
| |
| return timeUs; |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() { |
| const uint8_t *data = mBuffer->data(); |
| |
| size_t size = mBuffer->size(); |
| Vector<NALPosition> nals; |
| |
| size_t totalSize = 0; |
| size_t seiCount = 0; |
| |
| status_t err; |
| const uint8_t *nalStart; |
| size_t nalSize; |
| bool foundSlice = false; |
| bool foundIDR = false; |
| |
| ALOGV("dequeueAccessUnit_H264[%d] %p/%zu", mAUIndex, data, size); |
| |
| while ((err = getNextNALUnit(&data, &size, &nalStart, &nalSize)) == OK) { |
| if (nalSize == 0) continue; |
| |
| unsigned nalType = nalStart[0] & 0x1f; |
| bool flush = false; |
| |
| if (nalType == 1 || nalType == 5) { |
| if (nalType == 5) { |
| foundIDR = true; |
| } |
| if (foundSlice) { |
| //TODO: Shouldn't this have been called with nalSize-1? |
| ABitReader br(nalStart + 1, nalSize); |
| unsigned first_mb_in_slice = parseUE(&br); |
| |
| if (first_mb_in_slice == 0) { |
| // This slice starts a new frame. |
| |
| flush = true; |
| } |
| } |
| |
| foundSlice = true; |
| } else if ((nalType == 9 || nalType == 7) && foundSlice) { |
| // Access unit delimiter and SPS will be associated with the |
| // next frame. |
| |
| flush = true; |
| } else if (nalType == 6 && nalSize > 0) { |
| // found non-zero sized SEI |
| ++seiCount; |
| } |
| |
| if (flush) { |
| // The access unit will contain all nal units up to, but excluding |
| // the current one, separated by 0x00 0x00 0x00 0x01 startcodes. |
| |
| size_t auSize = 4 * nals.size() + totalSize; |
| sp<ABuffer> accessUnit = new ABuffer(auSize); |
| sp<ABuffer> sei; |
| |
| if (seiCount > 0) { |
| sei = new ABuffer(seiCount * sizeof(NALPosition)); |
| accessUnit->meta()->setBuffer("sei", sei); |
| } |
| |
| #if !LOG_NDEBUG |
| AString out; |
| #endif |
| |
| size_t dstOffset = 0; |
| size_t seiIndex = 0; |
| size_t shrunkBytes = 0; |
| for (size_t i = 0; i < nals.size(); ++i) { |
| const NALPosition &pos = nals.itemAt(i); |
| |
| unsigned nalType = mBuffer->data()[pos.nalOffset] & 0x1f; |
| |
| if (nalType == 6 && pos.nalSize > 0) { |
| if (seiIndex >= sei->size() / sizeof(NALPosition)) { |
| ALOGE("Wrong seiIndex"); |
| return NULL; |
| } |
| NALPosition &seiPos = ((NALPosition *)sei->data())[seiIndex++]; |
| seiPos.nalOffset = dstOffset + 4; |
| seiPos.nalSize = pos.nalSize; |
| } |
| |
| #if !LOG_NDEBUG |
| char tmp[128]; |
| sprintf(tmp, "0x%02x", nalType); |
| if (i > 0) { |
| out.append(", "); |
| } |
| out.append(tmp); |
| #endif |
| |
| memcpy(accessUnit->data() + dstOffset, "\x00\x00\x00\x01", 4); |
| |
| if (mSampleDecryptor != NULL && (nalType == 1 || nalType == 5)) { |
| uint8_t *nalData = mBuffer->data() + pos.nalOffset; |
| size_t newSize = mSampleDecryptor->processNal(nalData, pos.nalSize); |
| // Note: the data can shrink due to unescaping, but it can never grow |
| if (newSize > pos.nalSize) { |
| // don't log unless verbose, since this can get called a lot if |
| // the caller is trying to resynchronize |
| ALOGV("expected sample size < %u, got %zu", pos.nalSize, newSize); |
| return NULL; |
| } |
| memcpy(accessUnit->data() + dstOffset + 4, |
| nalData, |
| newSize); |
| dstOffset += newSize + 4; |
| |
| size_t thisShrunkBytes = pos.nalSize - newSize; |
| //ALOGV("dequeueAccessUnitH264[%d]: nalType: %d -> %zu (%zu)", |
| // nalType, (int)pos.nalSize, newSize, thisShrunkBytes); |
| |
| shrunkBytes += thisShrunkBytes; |
| } |
| else { |
| memcpy(accessUnit->data() + dstOffset + 4, |
| mBuffer->data() + pos.nalOffset, |
| pos.nalSize); |
| |
| dstOffset += pos.nalSize + 4; |
| //ALOGV("dequeueAccessUnitH264 [%d] %d @%d", |
| // nalType, (int)pos.nalSize, (int)pos.nalOffset); |
| } |
| } |
| |
| #if !LOG_NDEBUG |
| ALOGV("accessUnit contains nal types %s", out.c_str()); |
| #endif |
| |
| const NALPosition &pos = nals.itemAt(nals.size() - 1); |
| size_t nextScan = pos.nalOffset + pos.nalSize; |
| |
| memmove(mBuffer->data(), |
| mBuffer->data() + nextScan, |
| mBuffer->size() - nextScan); |
| |
| mBuffer->setRange(0, mBuffer->size() - nextScan); |
| |
| int64_t timeUs = fetchTimestamp(nextScan); |
| if (timeUs < 0LL) { |
| ALOGE("Negative timeUs"); |
| return NULL; |
| } |
| |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| if (foundIDR) { |
| accessUnit->meta()->setInt32("isSync", 1); |
| } |
| |
| if (mFormat == NULL) { |
| mFormat = new MetaData; |
| if (!MakeAVCCodecSpecificData(*mFormat, |
| accessUnit->data(), |
| accessUnit->size())) { |
| mFormat.clear(); |
| } |
| } |
| |
| if (mSampleDecryptor != NULL && shrunkBytes > 0) { |
| size_t adjustedSize = accessUnit->size() - shrunkBytes; |
| ALOGV("dequeueAccessUnitH264[%d]: AU size adjusted %zu -> %zu", |
| mAUIndex, accessUnit->size(), adjustedSize); |
| accessUnit->setRange(0, adjustedSize); |
| } |
| |
| ALOGV("dequeueAccessUnitH264[%d]: AU %p(%zu) dstOffset:%zu, nals:%zu, totalSize:%zu ", |
| mAUIndex, accessUnit->data(), accessUnit->size(), |
| dstOffset, nals.size(), totalSize); |
| mAUIndex++; |
| |
| return accessUnit; |
| } |
| |
| NALPosition pos; |
| pos.nalOffset = nalStart - mBuffer->data(); |
| pos.nalSize = nalSize; |
| |
| nals.push(pos); |
| |
| totalSize += nalSize; |
| } |
| if (err != (status_t)-EAGAIN) { |
| ALOGE("Unexpeted err"); |
| return NULL; |
| } |
| |
| return NULL; |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGAudio() { |
| const uint8_t *data = mBuffer->data(); |
| size_t size = mBuffer->size(); |
| |
| if (size < 4) { |
| return NULL; |
| } |
| |
| uint32_t header = U32_AT(data); |
| |
| size_t frameSize; |
| int samplingRate, numChannels, bitrate, numSamples; |
| if (!GetMPEGAudioFrameSize( |
| header, &frameSize, &samplingRate, &numChannels, |
| &bitrate, &numSamples)) { |
| ALOGE("Failed to get audio frame size"); |
| mBuffer->setRange(0, 0); |
| return NULL; |
| } |
| |
| if (size < frameSize) { |
| return NULL; |
| } |
| |
| unsigned layer = 4 - ((header >> 17) & 3); |
| |
| sp<ABuffer> accessUnit = new ABuffer(frameSize); |
| memcpy(accessUnit->data(), data, frameSize); |
| |
| memmove(mBuffer->data(), |
| mBuffer->data() + frameSize, |
| mBuffer->size() - frameSize); |
| |
| mBuffer->setRange(0, mBuffer->size() - frameSize); |
| |
| int64_t timeUs = fetchTimestamp(frameSize); |
| if (timeUs < 0LL) { |
| ALOGE("Negative timeUs"); |
| return NULL; |
| } |
| |
| if (mFormat != NULL) { |
| const char *mime; |
| if (mFormat->findCString(kKeyMIMEType, &mime)) { |
| if ((layer == 1) && strcmp (mime, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I)) { |
| ALOGE("Audio layer is not MPEG_LAYER_I"); |
| return NULL; |
| } else if ((layer == 2) && strcmp (mime, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II)) { |
| ALOGE("Audio layer is not MPEG_LAYER_II"); |
| return NULL; |
| } else if ((layer == 3) && strcmp (mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { |
| ALOGE("Audio layer is not AUDIO_MPEG"); |
| return NULL; |
| } |
| } |
| } |
| |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| accessUnit->meta()->setInt32("isSync", 1); |
| |
| if (mFormat == NULL) { |
| mFormat = new MetaData; |
| |
| switch (layer) { |
| case 1: |
| mFormat->setCString( |
| kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I); |
| break; |
| case 2: |
| mFormat->setCString( |
| kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II); |
| break; |
| case 3: |
| mFormat->setCString( |
| kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); |
| break; |
| default: |
| return NULL; |
| } |
| |
| mFormat->setInt32(kKeySampleRate, samplingRate); |
| mFormat->setInt32(kKeyChannelCount, numChannels); |
| } |
| |
| return accessUnit; |
| } |
| |
| static void EncodeSize14(uint8_t **_ptr, size_t size) { |
| if (size > 0x3fff) { |
| ALOGE("Wrong size"); |
| return; |
| } |
| |
| uint8_t *ptr = *_ptr; |
| |
| *ptr++ = 0x80 | (size >> 7); |
| *ptr++ = size & 0x7f; |
| |
| *_ptr = ptr; |
| } |
| |
| static sp<ABuffer> MakeMPEGVideoESDS(const sp<ABuffer> &csd) { |
| sp<ABuffer> esds = new ABuffer(csd->size() + 25); |
| |
| uint8_t *ptr = esds->data(); |
| *ptr++ = 0x03; |
| EncodeSize14(&ptr, 22 + csd->size()); |
| |
| *ptr++ = 0x00; // ES_ID |
| *ptr++ = 0x00; |
| |
| *ptr++ = 0x00; // streamDependenceFlag, URL_Flag, OCRstreamFlag |
| |
| *ptr++ = 0x04; |
| EncodeSize14(&ptr, 16 + csd->size()); |
| |
| *ptr++ = 0x40; // Audio ISO/IEC 14496-3 |
| |
| for (size_t i = 0; i < 12; ++i) { |
| *ptr++ = 0x00; |
| } |
| |
| *ptr++ = 0x05; |
| EncodeSize14(&ptr, csd->size()); |
| |
| memcpy(ptr, csd->data(), csd->size()); |
| |
| return esds; |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGVideo() { |
| const uint8_t *data = mBuffer->data(); |
| size_t size = mBuffer->size(); |
| |
| Vector<size_t> userDataPositions; |
| |
| bool sawPictureStart = false; |
| int pprevStartCode = -1; |
| int prevStartCode = -1; |
| int currentStartCode = -1; |
| bool gopFound = false; |
| bool isClosedGop = false; |
| bool brokenLink = false; |
| |
| size_t offset = 0; |
| while (offset + 3 < size) { |
| if (memcmp(&data[offset], "\x00\x00\x01", 3)) { |
| ++offset; |
| continue; |
| } |
| |
| pprevStartCode = prevStartCode; |
| prevStartCode = currentStartCode; |
| currentStartCode = data[offset + 3]; |
| |
| if (currentStartCode == 0xb3 && mFormat == NULL) { |
| memmove(mBuffer->data(), mBuffer->data() + offset, size - offset); |
| size -= offset; |
| (void)fetchTimestamp(offset); |
| offset = 0; |
| mBuffer->setRange(0, size); |
| } |
| |
| if ((prevStartCode == 0xb3 && currentStartCode != 0xb5) |
| || (pprevStartCode == 0xb3 && prevStartCode == 0xb5)) { |
| // seqHeader without/with extension |
| |
| if (mFormat == NULL) { |
| if (size < 7u) { |
| ALOGE("Size too small"); |
| return NULL; |
| } |
| |
| unsigned width = |
| (data[4] << 4) | data[5] >> 4; |
| |
| unsigned height = |
| ((data[5] & 0x0f) << 8) | data[6]; |
| |
| mFormat = new MetaData; |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2); |
| mFormat->setInt32(kKeyWidth, width); |
| mFormat->setInt32(kKeyHeight, height); |
| |
| ALOGI("found MPEG2 video codec config (%d x %d)", width, height); |
| |
| sp<ABuffer> csd = new ABuffer(offset); |
| memcpy(csd->data(), data, offset); |
| |
| memmove(mBuffer->data(), |
| mBuffer->data() + offset, |
| mBuffer->size() - offset); |
| |
| mBuffer->setRange(0, mBuffer->size() - offset); |
| size -= offset; |
| (void)fetchTimestamp(offset); |
| offset = 0; |
| |
| // hexdump(csd->data(), csd->size()); |
| |
| sp<ABuffer> esds = MakeMPEGVideoESDS(csd); |
| mFormat->setData( |
| kKeyESDS, kTypeESDS, esds->data(), esds->size()); |
| |
| return NULL; |
| } |
| } |
| |
| if (mFormat != NULL && currentStartCode == 0xb8) { |
| // GOP layer |
| if (offset + 7 >= size) { |
| ALOGE("Size too small"); |
| return NULL; |
| } |
| gopFound = true; |
| isClosedGop = (data[offset + 7] & 0x40) != 0; |
| brokenLink = (data[offset + 7] & 0x20) != 0; |
| } |
| |
| if (mFormat != NULL && currentStartCode == 0xb2) { |
| userDataPositions.add(offset); |
| } |
| |
| if (mFormat != NULL && currentStartCode == 0x00) { |
| // Picture start |
| |
| if (!sawPictureStart) { |
| sawPictureStart = true; |
| } else { |
| sp<ABuffer> accessUnit = new ABuffer(offset); |
| memcpy(accessUnit->data(), data, offset); |
| |
| memmove(mBuffer->data(), |
| mBuffer->data() + offset, |
| mBuffer->size() - offset); |
| |
| mBuffer->setRange(0, mBuffer->size() - offset); |
| |
| int64_t timeUs = fetchTimestamp(offset); |
| if (timeUs < 0LL) { |
| ALOGE("Negative timeUs"); |
| return NULL; |
| } |
| |
| offset = 0; |
| |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| if (gopFound && (!brokenLink || isClosedGop)) { |
| accessUnit->meta()->setInt32("isSync", 1); |
| } |
| |
| ALOGV("returning MPEG video access unit at time %" PRId64 " us", |
| timeUs); |
| |
| // hexdump(accessUnit->data(), accessUnit->size()); |
| |
| if (userDataPositions.size() > 0) { |
| sp<ABuffer> mpegUserData = |
| new ABuffer(userDataPositions.size() * sizeof(size_t)); |
| if (mpegUserData != NULL && mpegUserData->data() != NULL) { |
| for (size_t i = 0; i < userDataPositions.size(); ++i) { |
| memcpy( |
| mpegUserData->data() + i * sizeof(size_t), |
| &userDataPositions[i], sizeof(size_t)); |
| } |
| accessUnit->meta()->setBuffer("mpeg-user-data", mpegUserData); |
| } |
| } |
| |
| return accessUnit; |
| } |
| } |
| |
| ++offset; |
| } |
| |
| return NULL; |
| } |
| |
| static ssize_t getNextChunkSize( |
| const uint8_t *data, size_t size) { |
| static const char kStartCode[] = "\x00\x00\x01"; |
| |
| // per ISO/IEC 14496-2 6.2.1, a chunk has a 3-byte prefix + 1-byte start code |
| // we need at least <prefix><start><next prefix> to successfully scan |
| if (size < 3 + 1 + 3) { |
| return -EAGAIN; |
| } |
| |
| if (memcmp(kStartCode, data, 3)) { |
| return -EAGAIN; |
| } |
| |
| size_t offset = 4; |
| while (offset + 2 < size) { |
| if (!memcmp(&data[offset], kStartCode, 3)) { |
| return offset; |
| } |
| |
| ++offset; |
| } |
| |
| return -EAGAIN; |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEG4Video() { |
| uint8_t *data = mBuffer->data(); |
| size_t size = mBuffer->size(); |
| |
| enum { |
| SKIP_TO_VISUAL_OBJECT_SEQ_START, |
| EXPECT_VISUAL_OBJECT_START, |
| EXPECT_VO_START, |
| EXPECT_VOL_START, |
| WAIT_FOR_VOP_START, |
| SKIP_TO_VOP_START, |
| |
| } state; |
| |
| if (mFormat == NULL) { |
| state = SKIP_TO_VISUAL_OBJECT_SEQ_START; |
| } else { |
| state = SKIP_TO_VOP_START; |
| } |
| |
| int32_t width = -1, height = -1; |
| |
| size_t offset = 0; |
| ssize_t chunkSize; |
| while ((chunkSize = getNextChunkSize( |
| &data[offset], size - offset)) > 0) { |
| bool discard = false; |
| |
| unsigned chunkType = data[offset + 3]; |
| |
| switch (state) { |
| case SKIP_TO_VISUAL_OBJECT_SEQ_START: |
| { |
| if (chunkType == 0xb0) { |
| // Discard anything before this marker. |
| |
| state = EXPECT_VISUAL_OBJECT_START; |
| } else { |
| discard = true; |
| offset += chunkSize; |
| ALOGW("b/74114680, advance to next chunk"); |
| android_errorWriteLog(0x534e4554, "74114680"); |
| } |
| break; |
| } |
| |
| case EXPECT_VISUAL_OBJECT_START: |
| { |
| if (chunkType != 0xb5) { |
| ALOGE("Unexpected chunkType"); |
| return NULL; |
| } |
| state = EXPECT_VO_START; |
| break; |
| } |
| |
| case EXPECT_VO_START: |
| { |
| if (chunkType > 0x1f) { |
| ALOGE("Unexpected chunkType"); |
| return NULL; |
| } |
| state = EXPECT_VOL_START; |
| break; |
| } |
| |
| case EXPECT_VOL_START: |
| { |
| if ((chunkType & 0xf0) != 0x20) { |
| ALOGE("Wrong chunkType"); |
| return NULL; |
| } |
| |
| if (!ExtractDimensionsFromVOLHeader( |
| &data[offset], chunkSize, |
| &width, &height)) { |
| ALOGE("Failed to get dimension"); |
| return NULL; |
| } |
| |
| state = WAIT_FOR_VOP_START; |
| break; |
| } |
| |
| case WAIT_FOR_VOP_START: |
| { |
| if (chunkType == 0xb3 || chunkType == 0xb6) { |
| // group of VOP or VOP start. |
| |
| mFormat = new MetaData; |
| mFormat->setCString( |
| kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); |
| |
| mFormat->setInt32(kKeyWidth, width); |
| mFormat->setInt32(kKeyHeight, height); |
| |
| ALOGI("found MPEG4 video codec config (%d x %d)", |
| width, height); |
| |
| sp<ABuffer> csd = new ABuffer(offset); |
| memcpy(csd->data(), data, offset); |
| |
| // hexdump(csd->data(), csd->size()); |
| |
| sp<ABuffer> esds = MakeMPEGVideoESDS(csd); |
| mFormat->setData( |
| kKeyESDS, kTypeESDS, |
| esds->data(), esds->size()); |
| |
| discard = true; |
| state = SKIP_TO_VOP_START; |
| } |
| |
| break; |
| } |
| |
| case SKIP_TO_VOP_START: |
| { |
| if (chunkType == 0xb6) { |
| int vopCodingType = (data[offset + 4] & 0xc0) >> 6; |
| |
| offset += chunkSize; |
| |
| sp<ABuffer> accessUnit = new ABuffer(offset); |
| memcpy(accessUnit->data(), data, offset); |
| |
| memmove(data, &data[offset], size - offset); |
| size -= offset; |
| mBuffer->setRange(0, size); |
| |
| int64_t timeUs = fetchTimestamp(offset); |
| if (timeUs < 0LL) { |
| ALOGE("Negative timeus"); |
| return NULL; |
| } |
| |
| offset = 0; |
| |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| if (vopCodingType == 0) { // intra-coded VOP |
| accessUnit->meta()->setInt32("isSync", 1); |
| } |
| |
| ALOGV("returning MPEG4 video access unit at time %" PRId64 " us", |
| timeUs); |
| |
| // hexdump(accessUnit->data(), accessUnit->size()); |
| |
| return accessUnit; |
| } else if (chunkType != 0xb3) { |
| offset += chunkSize; |
| discard = true; |
| } |
| |
| break; |
| } |
| |
| default: |
| ALOGE("Unknown state: %d", state); |
| return NULL; |
| } |
| |
| if (discard) { |
| (void)fetchTimestamp(offset); |
| memmove(data, &data[offset], size - offset); |
| size -= offset; |
| offset = 0; |
| mBuffer->setRange(0, size); |
| } else { |
| offset += chunkSize; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| void ElementaryStreamQueue::signalEOS() { |
| if (!mEOSReached) { |
| if (mMode == MPEG_VIDEO) { |
| const char *theEnd = "\x00\x00\x01\x00"; |
| appendData(theEnd, 4, 0); |
| } |
| mEOSReached = true; |
| } else { |
| ALOGW("EOS already signaled"); |
| } |
| } |
| |
| sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMetadata() { |
| size_t size = mBuffer->size(); |
| if (!size) { |
| return NULL; |
| } |
| |
| sp<ABuffer> accessUnit = new ABuffer(size); |
| int64_t timeUs = fetchTimestamp(size); |
| accessUnit->meta()->setInt64("timeUs", timeUs); |
| |
| memcpy(accessUnit->data(), mBuffer->data(), size); |
| mBuffer->setRange(0, 0); |
| |
| if (mFormat == NULL) { |
| mFormat = new MetaData; |
| mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_DATA_TIMED_ID3); |
| } |
| |
| return accessUnit; |
| } |
| |
| void ElementaryStreamQueue::signalNewSampleAesKey(const sp<AMessage> &keyItem) { |
| if (mSampleDecryptor == NULL) { |
| ALOGE("signalNewSampleAesKey: Stream %x is not encrypted; keyItem: %p", |
| mMode, keyItem.get()); |
| return; |
| } |
| |
| mSampleDecryptor->signalNewSampleAesKey(keyItem); |
| } |
| |
| |
| } // namespace android |