Loading media/libstagefright/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ LOCAL_SHARED_LIBRARIES := \ libaudioutils \ libbinder \ libcamera_client \ libcrypto \ libcutils \ libdl \ libdrmframework \ Loading media/libstagefright/httplive/PlaylistFetcher.cpp +107 −26 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ #include "include/avc_utils.h" #include "include/ID3.h" #include "mpeg2ts/AnotherPacketSource.h" #include "mpeg2ts/HlsSampleDecryptor.h" #include <media/stagefright/foundation/ABitReader.h> #include <media/stagefright/foundation/ABuffer.h> Loading @@ -36,7 +37,6 @@ #include <ctype.h> #include <inttypes.h> #include <openssl/aes.h> #define FLOGV(fmt, ...) ALOGV("[fetcher-%d] " fmt, mFetcherID, ##__VA_ARGS__) #define FSLOGV(stream, fmt, ...) ALOGV("[fetcher-%d] [%s] " fmt, mFetcherID, \ Loading Loading @@ -167,11 +167,15 @@ PlaylistFetcher::PlaylistFetcher( mFirstPTSValid(false), mFirstTimeUs(-1ll), mVideoBuffer(new AnotherPacketSource(NULL)), mSampleAesKeyItemChanged(false), mThresholdRatio(-1.0f), mDownloadState(new DownloadState()), mHasMetadata(false) { memset(mPlaylistHash, 0, sizeof(mPlaylistHash)); mHTTPDownloader = mSession->getHTTPDownloader(); memset(mKeyData, 0, sizeof(mKeyData)); memset(mAESInitVec, 0, sizeof(mAESInitVec)); } PlaylistFetcher::~PlaylistFetcher() { Loading Loading @@ -306,6 +310,15 @@ status_t PlaylistFetcher::decryptBuffer( } } // TODO: Revise this when we add support for KEYFORMAT // If method has changed (e.g., -> NONE); sufficient to check at the segment boundary if (mSampleAesKeyItem != NULL && first && found && method != "SAMPLE-AES") { ALOGI("decryptBuffer: resetting mSampleAesKeyItem(%p) with method %s", mSampleAesKeyItem.get(), method.c_str()); mSampleAesKeyItem = NULL; mSampleAesKeyItemChanged = true; } if (!found) { method = "NONE"; } Loading @@ -313,6 +326,8 @@ status_t PlaylistFetcher::decryptBuffer( if (method == "NONE") { return OK; } else if (method == "SAMPLE-AES") { ALOGV("decryptBuffer: Non-Widevine SAMPLE-AES is supported now."); } else if (!(method == "AES-128")) { ALOGE("Unsupported cipher method '%s'", method.c_str()); return ERROR_UNSUPPORTED; Loading Loading @@ -345,26 +360,11 @@ status_t PlaylistFetcher::decryptBuffer( mAESKeyForURI.add(keyURI, key); } AES_KEY aes_key; if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { ALOGE("failed to set AES decryption key."); return UNKNOWN_ERROR; } size_t n = buffer->size(); if (!n) { return OK; } if (n < 16 || n % 16) { ALOGE("not enough or trailing bytes (%zu) in encrypted buffer", n); return ERROR_MALFORMED; } if (first) { // If decrypting the first block in a file, read the iv from the manifest // or derive the iv from the file's sequence number. unsigned char AESInitVec[AES_BLOCK_SIZE]; AString iv; if (itemMeta->findString("cipher-iv", &iv)) { if ((!iv.startsWith("0x") && !iv.startsWith("0X")) Loading @@ -377,7 +377,7 @@ status_t PlaylistFetcher::decryptBuffer( iv.insert("0", 1, 2); } memset(mAESInitVec, 0, sizeof(mAESInitVec)); memset(AESInitVec, 0, sizeof(AESInitVec)); for (size_t i = 0; i < 16; ++i) { char c1 = tolower(iv.c_str()[2 + 2 * i]); char c2 = tolower(iv.c_str()[3 + 2 * i]); Loading @@ -388,15 +388,65 @@ status_t PlaylistFetcher::decryptBuffer( uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; mAESInitVec[i] = nibble1 << 4 | nibble2; AESInitVec[i] = nibble1 << 4 | nibble2; } } else { memset(mAESInitVec, 0, sizeof(mAESInitVec)); mAESInitVec[15] = mSeqNumber & 0xff; mAESInitVec[14] = (mSeqNumber >> 8) & 0xff; mAESInitVec[13] = (mSeqNumber >> 16) & 0xff; mAESInitVec[12] = (mSeqNumber >> 24) & 0xff; memset(AESInitVec, 0, sizeof(AESInitVec)); AESInitVec[15] = mSeqNumber & 0xff; AESInitVec[14] = (mSeqNumber >> 8) & 0xff; AESInitVec[13] = (mSeqNumber >> 16) & 0xff; AESInitVec[12] = (mSeqNumber >> 24) & 0xff; } bool newKey = memcmp(mKeyData, key->data(), AES_BLOCK_SIZE) != 0; bool newInitVec = memcmp(mAESInitVec, AESInitVec, AES_BLOCK_SIZE) != 0; bool newSampleAesKeyItem = newKey || newInitVec; ALOGV("decryptBuffer: SAMPLE-AES newKeyItem %d/%d (Key %d initVec %d)", mSampleAesKeyItemChanged, newSampleAesKeyItem, newKey, newInitVec); if (newSampleAesKeyItem) { memcpy(mKeyData, key->data(), AES_BLOCK_SIZE); memcpy(mAESInitVec, AESInitVec, AES_BLOCK_SIZE); if (method == "SAMPLE-AES") { mSampleAesKeyItemChanged = true; sp<ABuffer> keyDataBuffer = ABuffer::CreateAsCopy(mKeyData, sizeof(mKeyData)); sp<ABuffer> initVecBuffer = ABuffer::CreateAsCopy(mAESInitVec, sizeof(mAESInitVec)); // always allocating a new one rather than updating the old message // lower layer might still have a reference to the old message mSampleAesKeyItem = new AMessage(); mSampleAesKeyItem->setBuffer("keyData", keyDataBuffer); mSampleAesKeyItem->setBuffer("initVec", initVecBuffer); ALOGV("decryptBuffer: New SampleAesKeyItem: Key: %s IV: %s", HlsSampleDecryptor::aesBlockToStr(mKeyData).c_str(), HlsSampleDecryptor::aesBlockToStr(mAESInitVec).c_str()); } // SAMPLE-AES } // newSampleAesKeyItem } // first if (method == "SAMPLE-AES") { ALOGV("decryptBuffer: skipping full-seg decrypt for SAMPLE-AES"); return OK; } AES_KEY aes_key; if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { ALOGE("failed to set AES decryption key."); return UNKNOWN_ERROR; } size_t n = buffer->size(); if (!n) { return OK; } if (n < 16 || n % 16) { ALOGE("not enough or trailing bytes (%zu) in encrypted buffer", n); return ERROR_MALFORMED; } AES_cbc_encrypt( Loading @@ -409,7 +459,7 @@ status_t PlaylistFetcher::decryptBuffer( status_t PlaylistFetcher::checkDecryptPadding(const sp<ABuffer> &buffer) { AString method; CHECK(buffer->meta()->findString("cipher-method", &method)); if (method == "NONE") { if (method == "NONE" || method == "SAMPLE-AES") { return OK; } Loading Loading @@ -1656,6 +1706,11 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu mNextPTSTimeUs = -1ll; } if (mSampleAesKeyItemChanged) { mTSParser->signalNewSampleAesKey(mSampleAesKeyItem); mSampleAesKeyItemChanged = false; } size_t offset = 0; while (offset + 188 <= buffer->size()) { status_t err = mTSParser->feedTSPacket(buffer->data() + offset, 188); Loading Loading @@ -2038,10 +2093,24 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( } } sp<HlsSampleDecryptor> sampleDecryptor = NULL; if (mSampleAesKeyItem != NULL) { ALOGV("extractAndQueueAccessUnits[%d] SampleAesKeyItem: Key: %s IV: %s", mSeqNumber, HlsSampleDecryptor::aesBlockToStr(mKeyData).c_str(), HlsSampleDecryptor::aesBlockToStr(mAESInitVec).c_str()); sampleDecryptor = new HlsSampleDecryptor(mSampleAesKeyItem); } int frameId = 0; size_t offset = 0; while (offset < buffer->size()) { const uint8_t *adtsHeader = buffer->data() + offset; CHECK_LT(offset + 5, buffer->size()); // non-const pointer for decryption if needed uint8_t *adtsFrame = buffer->data() + offset; unsigned aac_frame_length = ((adtsHeader[3] & 3) << 11) Loading Loading @@ -2099,6 +2168,18 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( } } if (sampleDecryptor != NULL) { bool protection_absent = (adtsHeader[1] & 0x1); size_t headerSize = protection_absent ? 7 : 9; if (frameId == 0) { ALOGV("extractAndQueueAAC[%d] protection_absent %d (%02x) headerSize %zu", mSeqNumber, protection_absent, adtsHeader[1], headerSize); } sampleDecryptor->processAAC(headerSize, adtsFrame, aac_frame_length); } frameId++; sp<ABuffer> unit = new ABuffer(aac_frame_length); memcpy(unit->data(), adtsHeader, aac_frame_length); Loading media/libstagefright/httplive/PlaylistFetcher.h +5 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #define PLAYLIST_FETCHER_H_ #include <media/stagefright/foundation/AHandler.h> #include <openssl/aes.h> #include "mpeg2ts/ATSParser.h" #include "LiveSession.h" Loading Loading @@ -175,7 +176,10 @@ private: // Stores the initialization vector to decrypt the next block of cipher text, which can // either be derived from the sequence number, read from the manifest, or copied from // the last block of cipher text (cipher-block chaining). unsigned char mAESInitVec[16]; unsigned char mAESInitVec[AES_BLOCK_SIZE]; unsigned char mKeyData[AES_BLOCK_SIZE]; bool mSampleAesKeyItemChanged; sp<AMessage> mSampleAesKeyItem; Mutex mThresholdLock; float mThresholdRatio; Loading media/libstagefright/mpeg2ts/ATSParser.cpp +75 −5 Original line number Diff line number Diff line Loading @@ -105,6 +105,8 @@ struct ATSParser::Program : public RefBase { void updateCasSessions(); void signalNewSampleAesKey(const sp<AMessage> &keyItem); private: struct StreamInfo { unsigned mType; Loading @@ -119,6 +121,7 @@ private: bool mFirstPTSValid; uint64_t mFirstPTS; int64_t mLastRecoveredPTS; sp<AMessage> mSampleAesKeyItem; status_t parseProgramMap(ABitReader *br); int64_t recoverPTS(uint64_t PTS_33bit); Loading Loading @@ -168,6 +171,8 @@ struct ATSParser::Stream : public RefBase { bool isVideo() const; bool isMeta() const; void signalNewSampleAesKey(const sp<AMessage> &keyItem); protected: virtual ~Stream(); Loading @@ -194,6 +199,8 @@ private: ElementaryStreamQueue *mQueue; bool mScrambled; bool mSampleEncrypted; sp<AMessage> mSampleAesKeyItem; sp<IMemory> mMem; sp<MemoryDealer> mDealer; sp<ABuffer> mDescrambledBuffer; Loading Loading @@ -586,6 +593,10 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { sp<Stream> stream = new Stream( this, info.mPID, info.mType, PCR_PID, info.mCASystemId); if (mSampleAesKeyItem != NULL) { stream->signalNewSampleAesKey(mSampleAesKeyItem); } isAddingScrambledStream |= info.mCASystemId >= 0; mStreams.add(info.mPID, stream); } Loading Loading @@ -710,22 +721,32 @@ ATSParser::Stream::Stream( mPrevPTS(0), mQueue(NULL), mScrambled(CA_system_ID >= 0) { ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d", elementaryPID, streamType, mScrambled); uint32_t flags = (isVideo() && mScrambled) ? ElementaryStreamQueue::kFlag_ScrambledData : 0; mSampleEncrypted = mStreamType == STREAMTYPE_H264_ENCRYPTED || mStreamType == STREAMTYPE_AAC_ENCRYPTED || mStreamType == STREAMTYPE_AC3_ENCRYPTED; ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d, SampleEncrypted: %d", elementaryPID, streamType, mScrambled, mSampleEncrypted); uint32_t flags = (isVideo() && mScrambled) ? ElementaryStreamQueue::kFlag_ScrambledData : (mSampleEncrypted) ? ElementaryStreamQueue::kFlag_SampleEncryptedData : 0; ElementaryStreamQueue::Mode mode = ElementaryStreamQueue::INVALID; switch (mStreamType) { case STREAMTYPE_H264: case STREAMTYPE_H264_ENCRYPTED: mode = ElementaryStreamQueue::H264; flags |= (mProgram->parserFlags() & ALIGNED_VIDEO_DATA) ? ElementaryStreamQueue::kFlag_AlignedData : 0; break; case STREAMTYPE_MPEG2_AUDIO_ADTS: case STREAMTYPE_AAC_ENCRYPTED: mode = ElementaryStreamQueue::AAC; break; Loading @@ -745,6 +766,7 @@ ATSParser::Stream::Stream( case STREAMTYPE_LPCM_AC3: case STREAMTYPE_AC3: case STREAMTYPE_AC3_ENCRYPTED: mode = ElementaryStreamQueue::AC3; break; Loading @@ -761,6 +783,10 @@ ATSParser::Stream::Stream( mQueue = new ElementaryStreamQueue(mode, flags); if (mQueue != NULL) { if (mSampleAesKeyItem != NULL) { mQueue->signalNewSampleAesKey(mSampleAesKeyItem); } ensureBufferCapacity(kInitialStreamBufferSize); if (mScrambled && (isAudio() || isVideo())) { Loading Loading @@ -913,6 +939,7 @@ status_t ATSParser::Stream::parse( bool ATSParser::Stream::isVideo() const { switch (mStreamType) { case STREAMTYPE_H264: case STREAMTYPE_H264_ENCRYPTED: case STREAMTYPE_MPEG1_VIDEO: case STREAMTYPE_MPEG2_VIDEO: case STREAMTYPE_MPEG4_VIDEO: Loading @@ -930,6 +957,8 @@ bool ATSParser::Stream::isAudio() const { case STREAMTYPE_MPEG2_AUDIO_ADTS: case STREAMTYPE_LPCM_AC3: case STREAMTYPE_AC3: case STREAMTYPE_AAC_ENCRYPTED: case STREAMTYPE_AC3_ENCRYPTED: return true; default: Loading Loading @@ -1454,7 +1483,7 @@ void ATSParser::Stream::onPayloadData( mPrevPTS = PTS; #endif ALOGV("onPayloadData mStreamType=0x%02x", mStreamType); ALOGV("onPayloadData mStreamType=0x%02x size: %zu", mStreamType, size); int64_t timeUs = 0ll; // no presentation timestamp available. if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) { Loading Loading @@ -1492,6 +1521,8 @@ void ATSParser::Stream::onPayloadData( } mSource = new AnotherPacketSource(meta); mSource->queueAccessUnit(accessUnit); ALOGV("onPayloadData: created AnotherPacketSource PID 0x%08x of type 0x%02x", mElementaryPID, mStreamType); } } else if (mQueue->getFormat() != NULL) { // After a discontinuity we invalidate the queue's format Loading Loading @@ -1730,6 +1761,9 @@ void ATSParser::parseProgramAssociationTable(ABitReader *br) { if (!found) { mPrograms.push( new Program(this, program_number, programMapPID, mLastRecoveredPTS)); if (mSampleAesKeyItem != NULL) { mPrograms.top()->signalNewSampleAesKey(mSampleAesKeyItem); } } if (mPSISections.indexOfKey(programMapPID) < 0) { Loading Loading @@ -2228,4 +2262,40 @@ bool ATSParser::PSISection::isCRCOkay() const { ALOGV("crc: %08x\n", crc); return (crc == 0); } // SAMPLE_AES key handling // TODO: Merge these to their respective class after Widevine-HLS void ATSParser::signalNewSampleAesKey(const sp<AMessage> &keyItem) { ALOGD("signalNewSampleAesKey: %p", keyItem.get()); mSampleAesKeyItem = keyItem; // a NULL key item will propagate to existing ElementaryStreamQueues for (size_t i = 0; i < mPrograms.size(); ++i) { mPrograms[i]->signalNewSampleAesKey(keyItem); } } void ATSParser::Program::signalNewSampleAesKey(const sp<AMessage> &keyItem) { ALOGD("Program::signalNewSampleAesKey: %p", keyItem.get()); mSampleAesKeyItem = keyItem; // a NULL key item will propagate to existing ElementaryStreamQueues for (size_t i = 0; i < mStreams.size(); ++i) { mStreams[i]->signalNewSampleAesKey(keyItem); } } void ATSParser::Stream::signalNewSampleAesKey(const sp<AMessage> &keyItem) { ALOGD("Stream::signalNewSampleAesKey: 0x%04x size = %zu keyItem: %p", mElementaryPID, mBuffer->size(), keyItem.get()); // a NULL key item will propagate to existing ElementaryStreamQueues mSampleAesKeyItem = keyItem; flush(NULL); mQueue->signalNewSampleAesKey(keyItem); } } // namespace android media/libstagefright/mpeg2ts/ATSParser.h +9 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,8 @@ struct ATSParser : public RefBase { int64_t getFirstPTSTimeUs(); void signalNewSampleAesKey(const sp<AMessage> &keyItem); enum { // From ISO/IEC 13818-1: 2000 (E), Table 2-29 STREAMTYPE_RESERVED = 0x00, Loading @@ -149,6 +151,11 @@ struct ATSParser : public RefBase { // Stream type 0x83 is non-standard, // it could be LPCM or TrueHD AC3 STREAMTYPE_LPCM_AC3 = 0x83, //Sample Encrypted types STREAMTYPE_H264_ENCRYPTED = 0xDB, STREAMTYPE_AAC_ENCRYPTED = 0xCF, STREAMTYPE_AC3_ENCRYPTED = 0xC1, }; protected: Loading Loading @@ -181,6 +188,8 @@ private: size_t mNumTSPacketsParsed; sp<AMessage> mSampleAesKeyItem; void parseProgramAssociationTable(ABitReader *br); void parseProgramMap(ABitReader *br); // Parse PES packet where br is pointing to. If the PES contains a sync Loading Loading
media/libstagefright/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ LOCAL_SHARED_LIBRARIES := \ libaudioutils \ libbinder \ libcamera_client \ libcrypto \ libcutils \ libdl \ libdrmframework \ Loading
media/libstagefright/httplive/PlaylistFetcher.cpp +107 −26 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ #include "include/avc_utils.h" #include "include/ID3.h" #include "mpeg2ts/AnotherPacketSource.h" #include "mpeg2ts/HlsSampleDecryptor.h" #include <media/stagefright/foundation/ABitReader.h> #include <media/stagefright/foundation/ABuffer.h> Loading @@ -36,7 +37,6 @@ #include <ctype.h> #include <inttypes.h> #include <openssl/aes.h> #define FLOGV(fmt, ...) ALOGV("[fetcher-%d] " fmt, mFetcherID, ##__VA_ARGS__) #define FSLOGV(stream, fmt, ...) ALOGV("[fetcher-%d] [%s] " fmt, mFetcherID, \ Loading Loading @@ -167,11 +167,15 @@ PlaylistFetcher::PlaylistFetcher( mFirstPTSValid(false), mFirstTimeUs(-1ll), mVideoBuffer(new AnotherPacketSource(NULL)), mSampleAesKeyItemChanged(false), mThresholdRatio(-1.0f), mDownloadState(new DownloadState()), mHasMetadata(false) { memset(mPlaylistHash, 0, sizeof(mPlaylistHash)); mHTTPDownloader = mSession->getHTTPDownloader(); memset(mKeyData, 0, sizeof(mKeyData)); memset(mAESInitVec, 0, sizeof(mAESInitVec)); } PlaylistFetcher::~PlaylistFetcher() { Loading Loading @@ -306,6 +310,15 @@ status_t PlaylistFetcher::decryptBuffer( } } // TODO: Revise this when we add support for KEYFORMAT // If method has changed (e.g., -> NONE); sufficient to check at the segment boundary if (mSampleAesKeyItem != NULL && first && found && method != "SAMPLE-AES") { ALOGI("decryptBuffer: resetting mSampleAesKeyItem(%p) with method %s", mSampleAesKeyItem.get(), method.c_str()); mSampleAesKeyItem = NULL; mSampleAesKeyItemChanged = true; } if (!found) { method = "NONE"; } Loading @@ -313,6 +326,8 @@ status_t PlaylistFetcher::decryptBuffer( if (method == "NONE") { return OK; } else if (method == "SAMPLE-AES") { ALOGV("decryptBuffer: Non-Widevine SAMPLE-AES is supported now."); } else if (!(method == "AES-128")) { ALOGE("Unsupported cipher method '%s'", method.c_str()); return ERROR_UNSUPPORTED; Loading Loading @@ -345,26 +360,11 @@ status_t PlaylistFetcher::decryptBuffer( mAESKeyForURI.add(keyURI, key); } AES_KEY aes_key; if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { ALOGE("failed to set AES decryption key."); return UNKNOWN_ERROR; } size_t n = buffer->size(); if (!n) { return OK; } if (n < 16 || n % 16) { ALOGE("not enough or trailing bytes (%zu) in encrypted buffer", n); return ERROR_MALFORMED; } if (first) { // If decrypting the first block in a file, read the iv from the manifest // or derive the iv from the file's sequence number. unsigned char AESInitVec[AES_BLOCK_SIZE]; AString iv; if (itemMeta->findString("cipher-iv", &iv)) { if ((!iv.startsWith("0x") && !iv.startsWith("0X")) Loading @@ -377,7 +377,7 @@ status_t PlaylistFetcher::decryptBuffer( iv.insert("0", 1, 2); } memset(mAESInitVec, 0, sizeof(mAESInitVec)); memset(AESInitVec, 0, sizeof(AESInitVec)); for (size_t i = 0; i < 16; ++i) { char c1 = tolower(iv.c_str()[2 + 2 * i]); char c2 = tolower(iv.c_str()[3 + 2 * i]); Loading @@ -388,15 +388,65 @@ status_t PlaylistFetcher::decryptBuffer( uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; mAESInitVec[i] = nibble1 << 4 | nibble2; AESInitVec[i] = nibble1 << 4 | nibble2; } } else { memset(mAESInitVec, 0, sizeof(mAESInitVec)); mAESInitVec[15] = mSeqNumber & 0xff; mAESInitVec[14] = (mSeqNumber >> 8) & 0xff; mAESInitVec[13] = (mSeqNumber >> 16) & 0xff; mAESInitVec[12] = (mSeqNumber >> 24) & 0xff; memset(AESInitVec, 0, sizeof(AESInitVec)); AESInitVec[15] = mSeqNumber & 0xff; AESInitVec[14] = (mSeqNumber >> 8) & 0xff; AESInitVec[13] = (mSeqNumber >> 16) & 0xff; AESInitVec[12] = (mSeqNumber >> 24) & 0xff; } bool newKey = memcmp(mKeyData, key->data(), AES_BLOCK_SIZE) != 0; bool newInitVec = memcmp(mAESInitVec, AESInitVec, AES_BLOCK_SIZE) != 0; bool newSampleAesKeyItem = newKey || newInitVec; ALOGV("decryptBuffer: SAMPLE-AES newKeyItem %d/%d (Key %d initVec %d)", mSampleAesKeyItemChanged, newSampleAesKeyItem, newKey, newInitVec); if (newSampleAesKeyItem) { memcpy(mKeyData, key->data(), AES_BLOCK_SIZE); memcpy(mAESInitVec, AESInitVec, AES_BLOCK_SIZE); if (method == "SAMPLE-AES") { mSampleAesKeyItemChanged = true; sp<ABuffer> keyDataBuffer = ABuffer::CreateAsCopy(mKeyData, sizeof(mKeyData)); sp<ABuffer> initVecBuffer = ABuffer::CreateAsCopy(mAESInitVec, sizeof(mAESInitVec)); // always allocating a new one rather than updating the old message // lower layer might still have a reference to the old message mSampleAesKeyItem = new AMessage(); mSampleAesKeyItem->setBuffer("keyData", keyDataBuffer); mSampleAesKeyItem->setBuffer("initVec", initVecBuffer); ALOGV("decryptBuffer: New SampleAesKeyItem: Key: %s IV: %s", HlsSampleDecryptor::aesBlockToStr(mKeyData).c_str(), HlsSampleDecryptor::aesBlockToStr(mAESInitVec).c_str()); } // SAMPLE-AES } // newSampleAesKeyItem } // first if (method == "SAMPLE-AES") { ALOGV("decryptBuffer: skipping full-seg decrypt for SAMPLE-AES"); return OK; } AES_KEY aes_key; if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { ALOGE("failed to set AES decryption key."); return UNKNOWN_ERROR; } size_t n = buffer->size(); if (!n) { return OK; } if (n < 16 || n % 16) { ALOGE("not enough or trailing bytes (%zu) in encrypted buffer", n); return ERROR_MALFORMED; } AES_cbc_encrypt( Loading @@ -409,7 +459,7 @@ status_t PlaylistFetcher::decryptBuffer( status_t PlaylistFetcher::checkDecryptPadding(const sp<ABuffer> &buffer) { AString method; CHECK(buffer->meta()->findString("cipher-method", &method)); if (method == "NONE") { if (method == "NONE" || method == "SAMPLE-AES") { return OK; } Loading Loading @@ -1656,6 +1706,11 @@ status_t PlaylistFetcher::extractAndQueueAccessUnitsFromTs(const sp<ABuffer> &bu mNextPTSTimeUs = -1ll; } if (mSampleAesKeyItemChanged) { mTSParser->signalNewSampleAesKey(mSampleAesKeyItem); mSampleAesKeyItemChanged = false; } size_t offset = 0; while (offset + 188 <= buffer->size()) { status_t err = mTSParser->feedTSPacket(buffer->data() + offset, 188); Loading Loading @@ -2038,10 +2093,24 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( } } sp<HlsSampleDecryptor> sampleDecryptor = NULL; if (mSampleAesKeyItem != NULL) { ALOGV("extractAndQueueAccessUnits[%d] SampleAesKeyItem: Key: %s IV: %s", mSeqNumber, HlsSampleDecryptor::aesBlockToStr(mKeyData).c_str(), HlsSampleDecryptor::aesBlockToStr(mAESInitVec).c_str()); sampleDecryptor = new HlsSampleDecryptor(mSampleAesKeyItem); } int frameId = 0; size_t offset = 0; while (offset < buffer->size()) { const uint8_t *adtsHeader = buffer->data() + offset; CHECK_LT(offset + 5, buffer->size()); // non-const pointer for decryption if needed uint8_t *adtsFrame = buffer->data() + offset; unsigned aac_frame_length = ((adtsHeader[3] & 3) << 11) Loading Loading @@ -2099,6 +2168,18 @@ status_t PlaylistFetcher::extractAndQueueAccessUnits( } } if (sampleDecryptor != NULL) { bool protection_absent = (adtsHeader[1] & 0x1); size_t headerSize = protection_absent ? 7 : 9; if (frameId == 0) { ALOGV("extractAndQueueAAC[%d] protection_absent %d (%02x) headerSize %zu", mSeqNumber, protection_absent, adtsHeader[1], headerSize); } sampleDecryptor->processAAC(headerSize, adtsFrame, aac_frame_length); } frameId++; sp<ABuffer> unit = new ABuffer(aac_frame_length); memcpy(unit->data(), adtsHeader, aac_frame_length); Loading
media/libstagefright/httplive/PlaylistFetcher.h +5 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #define PLAYLIST_FETCHER_H_ #include <media/stagefright/foundation/AHandler.h> #include <openssl/aes.h> #include "mpeg2ts/ATSParser.h" #include "LiveSession.h" Loading Loading @@ -175,7 +176,10 @@ private: // Stores the initialization vector to decrypt the next block of cipher text, which can // either be derived from the sequence number, read from the manifest, or copied from // the last block of cipher text (cipher-block chaining). unsigned char mAESInitVec[16]; unsigned char mAESInitVec[AES_BLOCK_SIZE]; unsigned char mKeyData[AES_BLOCK_SIZE]; bool mSampleAesKeyItemChanged; sp<AMessage> mSampleAesKeyItem; Mutex mThresholdLock; float mThresholdRatio; Loading
media/libstagefright/mpeg2ts/ATSParser.cpp +75 −5 Original line number Diff line number Diff line Loading @@ -105,6 +105,8 @@ struct ATSParser::Program : public RefBase { void updateCasSessions(); void signalNewSampleAesKey(const sp<AMessage> &keyItem); private: struct StreamInfo { unsigned mType; Loading @@ -119,6 +121,7 @@ private: bool mFirstPTSValid; uint64_t mFirstPTS; int64_t mLastRecoveredPTS; sp<AMessage> mSampleAesKeyItem; status_t parseProgramMap(ABitReader *br); int64_t recoverPTS(uint64_t PTS_33bit); Loading Loading @@ -168,6 +171,8 @@ struct ATSParser::Stream : public RefBase { bool isVideo() const; bool isMeta() const; void signalNewSampleAesKey(const sp<AMessage> &keyItem); protected: virtual ~Stream(); Loading @@ -194,6 +199,8 @@ private: ElementaryStreamQueue *mQueue; bool mScrambled; bool mSampleEncrypted; sp<AMessage> mSampleAesKeyItem; sp<IMemory> mMem; sp<MemoryDealer> mDealer; sp<ABuffer> mDescrambledBuffer; Loading Loading @@ -586,6 +593,10 @@ status_t ATSParser::Program::parseProgramMap(ABitReader *br) { sp<Stream> stream = new Stream( this, info.mPID, info.mType, PCR_PID, info.mCASystemId); if (mSampleAesKeyItem != NULL) { stream->signalNewSampleAesKey(mSampleAesKeyItem); } isAddingScrambledStream |= info.mCASystemId >= 0; mStreams.add(info.mPID, stream); } Loading Loading @@ -710,22 +721,32 @@ ATSParser::Stream::Stream( mPrevPTS(0), mQueue(NULL), mScrambled(CA_system_ID >= 0) { ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d", elementaryPID, streamType, mScrambled); uint32_t flags = (isVideo() && mScrambled) ? ElementaryStreamQueue::kFlag_ScrambledData : 0; mSampleEncrypted = mStreamType == STREAMTYPE_H264_ENCRYPTED || mStreamType == STREAMTYPE_AAC_ENCRYPTED || mStreamType == STREAMTYPE_AC3_ENCRYPTED; ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d, SampleEncrypted: %d", elementaryPID, streamType, mScrambled, mSampleEncrypted); uint32_t flags = (isVideo() && mScrambled) ? ElementaryStreamQueue::kFlag_ScrambledData : (mSampleEncrypted) ? ElementaryStreamQueue::kFlag_SampleEncryptedData : 0; ElementaryStreamQueue::Mode mode = ElementaryStreamQueue::INVALID; switch (mStreamType) { case STREAMTYPE_H264: case STREAMTYPE_H264_ENCRYPTED: mode = ElementaryStreamQueue::H264; flags |= (mProgram->parserFlags() & ALIGNED_VIDEO_DATA) ? ElementaryStreamQueue::kFlag_AlignedData : 0; break; case STREAMTYPE_MPEG2_AUDIO_ADTS: case STREAMTYPE_AAC_ENCRYPTED: mode = ElementaryStreamQueue::AAC; break; Loading @@ -745,6 +766,7 @@ ATSParser::Stream::Stream( case STREAMTYPE_LPCM_AC3: case STREAMTYPE_AC3: case STREAMTYPE_AC3_ENCRYPTED: mode = ElementaryStreamQueue::AC3; break; Loading @@ -761,6 +783,10 @@ ATSParser::Stream::Stream( mQueue = new ElementaryStreamQueue(mode, flags); if (mQueue != NULL) { if (mSampleAesKeyItem != NULL) { mQueue->signalNewSampleAesKey(mSampleAesKeyItem); } ensureBufferCapacity(kInitialStreamBufferSize); if (mScrambled && (isAudio() || isVideo())) { Loading Loading @@ -913,6 +939,7 @@ status_t ATSParser::Stream::parse( bool ATSParser::Stream::isVideo() const { switch (mStreamType) { case STREAMTYPE_H264: case STREAMTYPE_H264_ENCRYPTED: case STREAMTYPE_MPEG1_VIDEO: case STREAMTYPE_MPEG2_VIDEO: case STREAMTYPE_MPEG4_VIDEO: Loading @@ -930,6 +957,8 @@ bool ATSParser::Stream::isAudio() const { case STREAMTYPE_MPEG2_AUDIO_ADTS: case STREAMTYPE_LPCM_AC3: case STREAMTYPE_AC3: case STREAMTYPE_AAC_ENCRYPTED: case STREAMTYPE_AC3_ENCRYPTED: return true; default: Loading Loading @@ -1454,7 +1483,7 @@ void ATSParser::Stream::onPayloadData( mPrevPTS = PTS; #endif ALOGV("onPayloadData mStreamType=0x%02x", mStreamType); ALOGV("onPayloadData mStreamType=0x%02x size: %zu", mStreamType, size); int64_t timeUs = 0ll; // no presentation timestamp available. if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) { Loading Loading @@ -1492,6 +1521,8 @@ void ATSParser::Stream::onPayloadData( } mSource = new AnotherPacketSource(meta); mSource->queueAccessUnit(accessUnit); ALOGV("onPayloadData: created AnotherPacketSource PID 0x%08x of type 0x%02x", mElementaryPID, mStreamType); } } else if (mQueue->getFormat() != NULL) { // After a discontinuity we invalidate the queue's format Loading Loading @@ -1730,6 +1761,9 @@ void ATSParser::parseProgramAssociationTable(ABitReader *br) { if (!found) { mPrograms.push( new Program(this, program_number, programMapPID, mLastRecoveredPTS)); if (mSampleAesKeyItem != NULL) { mPrograms.top()->signalNewSampleAesKey(mSampleAesKeyItem); } } if (mPSISections.indexOfKey(programMapPID) < 0) { Loading Loading @@ -2228,4 +2262,40 @@ bool ATSParser::PSISection::isCRCOkay() const { ALOGV("crc: %08x\n", crc); return (crc == 0); } // SAMPLE_AES key handling // TODO: Merge these to their respective class after Widevine-HLS void ATSParser::signalNewSampleAesKey(const sp<AMessage> &keyItem) { ALOGD("signalNewSampleAesKey: %p", keyItem.get()); mSampleAesKeyItem = keyItem; // a NULL key item will propagate to existing ElementaryStreamQueues for (size_t i = 0; i < mPrograms.size(); ++i) { mPrograms[i]->signalNewSampleAesKey(keyItem); } } void ATSParser::Program::signalNewSampleAesKey(const sp<AMessage> &keyItem) { ALOGD("Program::signalNewSampleAesKey: %p", keyItem.get()); mSampleAesKeyItem = keyItem; // a NULL key item will propagate to existing ElementaryStreamQueues for (size_t i = 0; i < mStreams.size(); ++i) { mStreams[i]->signalNewSampleAesKey(keyItem); } } void ATSParser::Stream::signalNewSampleAesKey(const sp<AMessage> &keyItem) { ALOGD("Stream::signalNewSampleAesKey: 0x%04x size = %zu keyItem: %p", mElementaryPID, mBuffer->size(), keyItem.get()); // a NULL key item will propagate to existing ElementaryStreamQueues mSampleAesKeyItem = keyItem; flush(NULL); mQueue->signalNewSampleAesKey(keyItem); } } // namespace android
media/libstagefright/mpeg2ts/ATSParser.h +9 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,8 @@ struct ATSParser : public RefBase { int64_t getFirstPTSTimeUs(); void signalNewSampleAesKey(const sp<AMessage> &keyItem); enum { // From ISO/IEC 13818-1: 2000 (E), Table 2-29 STREAMTYPE_RESERVED = 0x00, Loading @@ -149,6 +151,11 @@ struct ATSParser : public RefBase { // Stream type 0x83 is non-standard, // it could be LPCM or TrueHD AC3 STREAMTYPE_LPCM_AC3 = 0x83, //Sample Encrypted types STREAMTYPE_H264_ENCRYPTED = 0xDB, STREAMTYPE_AAC_ENCRYPTED = 0xCF, STREAMTYPE_AC3_ENCRYPTED = 0xC1, }; protected: Loading Loading @@ -181,6 +188,8 @@ private: size_t mNumTSPacketsParsed; sp<AMessage> mSampleAesKeyItem; void parseProgramAssociationTable(ABitReader *br); void parseProgramMap(ABitReader *br); // Parse PES packet where br is pointing to. If the PES contains a sync Loading