Merge "MediaCas: add CAS support to MPEG2TSExtractor and MediaCodec"
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index 296394b..259e134 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -20,6 +20,7 @@
#include <numeric>
+#include <android/media/IDescrambler.h>
#include <binder/MemoryDealer.h>
#include <media/openmax/OMX_Core.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -33,7 +34,8 @@
#include "include/SharedMemoryBuffer.h"
namespace android {
-
+using binder::Status;
+using MediaDescrambler::DescrambleInfo;
using BufferInfo = ACodecBufferChannel::BufferInfo;
using BufferInfoIterator = std::vector<const BufferInfo>::const_iterator;
@@ -92,7 +94,7 @@
const uint8_t *iv, CryptoPlugin::Mode mode, CryptoPlugin::Pattern pattern,
const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
AString *errorDetailMsg) {
- if (mCrypto == nullptr) {
+ if (!hasCryptoOrDescrambler()) {
return -ENOSYS;
}
std::shared_ptr<const std::vector<const BufferInfo>> array(
@@ -116,9 +118,47 @@
destination.mType = ICrypto::kDestinationTypeSharedMemory;
destination.mSharedMemory = mDecryptDestination;
}
- ssize_t result = mCrypto->decrypt(key, iv, mode, pattern,
- it->mSharedEncryptedBuffer, it->mClientBuffer->offset(),
- subSamples, numSubSamples, destination, errorDetailMsg);
+
+ ssize_t result = -1;
+ if (mCrypto != NULL) {
+ result = mCrypto->decrypt(key, iv, mode, pattern,
+ it->mSharedEncryptedBuffer, it->mClientBuffer->offset(),
+ subSamples, numSubSamples, destination, errorDetailMsg);
+ } else {
+ DescrambleInfo descrambleInfo;
+ descrambleInfo.dstType = destination.mType ==
+ ICrypto::kDestinationTypeSharedMemory ?
+ DescrambleInfo::kDestinationTypeVmPointer :
+ DescrambleInfo::kDestinationTypeNativeHandle;
+ descrambleInfo.scramblingControl = key != NULL ?
+ (DescramblerPlugin::ScramblingControl)key[0] :
+ DescramblerPlugin::kScrambling_Unscrambled;
+ descrambleInfo.numSubSamples = numSubSamples;
+ descrambleInfo.subSamples = (DescramblerPlugin::SubSample *)subSamples;
+ descrambleInfo.srcMem = it->mSharedEncryptedBuffer;
+ descrambleInfo.srcOffset = 0;
+ descrambleInfo.dstPtr = NULL;
+ descrambleInfo.dstOffset = 0;
+
+ int32_t descrambleResult = -1;
+ Status status = mDescrambler->descramble(descrambleInfo, &descrambleResult);
+
+ if (status.isOk()) {
+ result = descrambleResult;
+ }
+
+ if (result < 0) {
+ ALOGE("descramble failed, exceptionCode=%d, err=%d, result=%zd",
+ status.exceptionCode(), status.transactionError(), result);
+ } else {
+ ALOGV("descramble succeeded, result=%zd", result);
+ }
+
+ if (result > 0 && destination.mType == ICrypto::kDestinationTypeSharedMemory) {
+ memcpy(destination.mSharedMemory->pointer(),
+ (uint8_t*)it->mSharedEncryptedBuffer->pointer(), result);
+ }
+ }
if (result < 0) {
return result;
@@ -212,8 +252,7 @@
}
void ACodecBufferChannel::setInputBufferArray(const std::vector<BufferAndId> &array) {
- bool secure = (mCrypto != nullptr);
- if (secure) {
+ if (hasCryptoOrDescrambler()) {
size_t totalSize = std::accumulate(
array.begin(), array.end(), 0u,
[alignment = MemoryDealer::getAllocationAlignment()]
@@ -232,7 +271,7 @@
std::vector<const BufferInfo> inputBuffers;
for (const BufferAndId &elem : array) {
sp<IMemory> sharedEncryptedBuffer;
- if (secure) {
+ if (hasCryptoOrDescrambler()) {
sharedEncryptedBuffer = mDealer->allocate(elem.mBuffer->capacity());
}
inputBuffers.emplace_back(elem.mBuffer, elem.mBufferId, sharedEncryptedBuffer);
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 6674e2c..8728b6f 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -2012,6 +2012,7 @@
}
mDescrambler = static_cast<IDescrambler *>(descrambler);
+ mBufferChannel->setDescrambler(mDescrambler);
uint32_t flags;
CHECK(msg->findInt32("flags", (int32_t *)&flags));
@@ -2033,7 +2034,6 @@
CHECK(msg->senderAwaitsResponse(&replyID));
status_t err = OK;
- sp<Surface> surface;
switch (mState) {
case CONFIGURED:
@@ -2718,7 +2718,7 @@
CryptoPlugin::Pattern pattern;
if (msg->findSize("size", &size)) {
- if (mCrypto != NULL) {
+ if (hasCryptoOrDescrambler()) {
ss.mNumBytesOfClearData = size;
ss.mNumBytesOfEncryptedData = 0;
@@ -2730,7 +2730,9 @@
pattern.mSkipBlocks = 0;
}
} else {
- if (mCrypto == NULL) {
+ if (!hasCryptoOrDescrambler()) {
+ ALOGE("[%s] queuing secure buffer without mCrypto or mDescrambler!",
+ mComponentName.c_str());
return -EINVAL;
}
@@ -2779,7 +2781,7 @@
sp<MediaCodecBuffer> buffer = info->mData;
status_t err = OK;
- if (mCrypto != NULL) {
+ if (hasCryptoOrDescrambler()) {
AString *errorDetailMsg;
CHECK(msg->findPointer("errorDetailMsg", (void **)&errorDetailMsg));
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index ec02fb9..e8c46e3 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -610,6 +610,22 @@
sp<AMessage> msg = new AMessage;
msg->setString("mime", mime);
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyCas, &type, &data, &size)) {
+ sp<ABuffer> buffer = new (std::nothrow) ABuffer(size);
+ msg->setBuffer("cas", buffer);
+ memcpy(buffer->data(), data, size);
+ }
+
+ if (!strncasecmp("video/scrambled", mime, 15)
+ || !strncasecmp("audio/scrambled", mime, 15)) {
+
+ *format = msg;
+ return OK;
+ }
+
int64_t durationUs;
if (meta->findInt64(kKeyDuration, &durationUs)) {
msg->setInt64("durationUs", durationUs);
@@ -759,9 +775,6 @@
msg->setInt32("frame-rate", fps);
}
- uint32_t type;
- const void *data;
- size_t size;
if (meta->findData(kKeyAVCC, &type, &data, &size)) {
// Parse the AVCDecoderConfigurationRecord
diff --git a/media/libstagefright/include/ACodecBufferChannel.h b/media/libstagefright/include/ACodecBufferChannel.h
index ce9bd3c..02468c1 100644
--- a/media/libstagefright/include/ACodecBufferChannel.h
+++ b/media/libstagefright/include/ACodecBufferChannel.h
@@ -126,6 +126,10 @@
// the caller has given up the reference, so that access is also safe.
std::shared_ptr<const std::vector<const BufferInfo>> mInputBuffers;
std::shared_ptr<const std::vector<const BufferInfo>> mOutputBuffers;
+
+ bool hasCryptoOrDescrambler() {
+ return mCrypto != NULL || mDescrambler != NULL;
+ }
};
} // namespace android
diff --git a/media/libstagefright/include/CodecBase.h b/media/libstagefright/include/CodecBase.h
index cfbaea4..845146d 100644
--- a/media/libstagefright/include/CodecBase.h
+++ b/media/libstagefright/include/CodecBase.h
@@ -35,9 +35,10 @@
#include <utils/NativeHandle.h>
#include <system/graphics.h>
+#include <android/media/IDescrambler.h>
namespace android {
-
+using namespace media;
class BufferChannelBase;
class BufferProducerWrapper;
class MediaCodecBuffer;
@@ -259,6 +260,10 @@
mCrypto = crypto;
}
+ inline void setDescrambler(const sp<IDescrambler> &descrambler) {
+ mDescrambler = descrambler;
+ }
+
/**
* Queue an input buffer into the buffer channel.
*
@@ -317,6 +322,7 @@
protected:
std::unique_ptr<CodecBase::BufferCallback> mCallback;
sp<ICrypto> mCrypto;
+ sp<IDescrambler> mDescrambler;
};
} // namespace android
diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h
index ef55620..2a75298f 100644
--- a/media/libstagefright/include/MPEG2TSExtractor.h
+++ b/media/libstagefright/include/MPEG2TSExtractor.h
@@ -45,6 +45,8 @@
virtual sp<MetaData> getMetaData();
+ virtual status_t setMediaCas(const sp<ICas> &cas) override;
+
virtual uint32_t flags() const;
virtual const char * name() { return "MPEG2TSExtractor"; }
@@ -70,7 +72,10 @@
off64_t mOffset;
+ static bool isScrambledFormat(const sp<MetaData> &format);
+
void init();
+ void addSource(const sp<AnotherPacketSource> &impl);
// Try to feed more data from source to parser.
// |isInit| means this function is called inside init(). This is a signal to
// save SyncEvent so that init() can add SyncPoint after it updates |mSourceImpls|.
diff --git a/media/libstagefright/include/MediaCodec.h b/media/libstagefright/include/MediaCodec.h
index fd3ff26..30454dc 100644
--- a/media/libstagefright/include/MediaCodec.h
+++ b/media/libstagefright/include/MediaCodec.h
@@ -401,6 +401,10 @@
status_t connectToSurface(const sp<Surface> &surface);
status_t disconnectFromSurface();
+ bool hasCryptoOrDescrambler() {
+ return mCrypto != NULL || mDescrambler != NULL;
+ }
+
void postActivityNotificationIfPossible();
void onInputBufferAvailable();
diff --git a/media/libstagefright/include/MetaData.h b/media/libstagefright/include/MetaData.h
index 6ba7b32..214f4ff 100644
--- a/media/libstagefright/include/MetaData.h
+++ b/media/libstagefright/include/MetaData.h
@@ -176,6 +176,7 @@
kKeyCryptoDefaultIVSize = 'cryS', // int32_t
kKeyPssh = 'pssh', // raw data
+ kKeyCas = ' cas',
// Please see MediaFormat.KEY_IS_AUTOSELECT.
kKeyTrackIsAutoselect = 'auto', // bool (int32_t)
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 4975d9a..47caf61 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -17,13 +17,14 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ATSParser"
#include <utils/Log.h>
-
#include "ATSParser.h"
-
#include "AnotherPacketSource.h"
+#include "CasManager.h"
#include "ESQueue.h"
#include "include/avc_utils.h"
+#include <android/media/IDescrambler.h>
+#include <binder/MemoryDealer.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
@@ -40,6 +41,8 @@
#include <inttypes.h>
namespace android {
+using binder::Status;
+using MediaDescrambler::DescrambleInfo;
// I want the expression "y" evaluated even if verbose logging is off.
#define MY_LOGV(x, y) \
@@ -60,6 +63,8 @@
bool parsePID(
unsigned pid, unsigned continuity_counter,
unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
ABitReader *br, status_t *err, SyncEvent *event);
void signalDiscontinuity(
@@ -90,14 +95,21 @@
return mParser->mFlags;
}
+ sp<CasManager> casManager() const {
+ return mParser->mCasManager;
+ }
+
uint64_t firstPTS() const {
return mFirstPTS;
}
+ void updateCasSessions();
+
private:
struct StreamInfo {
unsigned mType;
unsigned mPID;
+ int32_t mCASystemId;
};
ATSParser *mParser;
@@ -110,6 +122,8 @@
status_t parseProgramMap(ABitReader *br);
int64_t recoverPTS(uint64_t PTS_33bit);
+ bool findCADescriptor(
+ ABitReader *br, unsigned infoLength, CADescriptor *caDescriptor);
bool switchPIDs(const Vector<StreamInfo> &infos);
DISALLOW_EVIL_CONSTRUCTORS(Program);
@@ -119,18 +133,25 @@
Stream(Program *program,
unsigned elementaryPID,
unsigned streamType,
- unsigned PCR_PID);
+ unsigned PCR_PID,
+ int32_t CA_system_ID);
unsigned type() const { return mStreamType; }
unsigned pid() const { return mElementaryPID; }
void setPID(unsigned pid) { mElementaryPID = pid; }
+ void setCasSession(
+ const sp<IDescrambler> &descrambler,
+ const std::vector<uint8_t> &sessionId);
+
// Parse the payload and set event when PES with a sync frame is detected.
// This method knows when a PES starts; so record mPesStartOffsets in that
// case.
status_t parse(
unsigned continuity_counter,
unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
ABitReader *br,
SyncEvent *event);
@@ -150,6 +171,11 @@
virtual ~Stream();
private:
+ struct SubSampleInfo {
+ size_t subSampleSize;
+ unsigned transport_scrambling_mode;
+ unsigned random_access_indicator;
+ };
Program *mProgram;
unsigned mElementaryPID;
unsigned mStreamType;
@@ -166,10 +192,26 @@
ElementaryStreamQueue *mQueue;
+ bool mScrambled;
+ sp<IMemory> mMem;
+ sp<MemoryDealer> mDealer;
+ sp<ABuffer> mDescrambledBuffer;
+ List<SubSampleInfo> mSubSamples;
+ sp<IDescrambler> mDescrambler;
+
// Flush accumulated payload if necessary --- i.e. at EOS or at the start of
// another payload. event is set if the flushed payload is PES with a sync
// frame.
status_t flush(SyncEvent *event);
+
+ // Flush accumulated payload for scrambled streams if necessary --- i.e. at
+ // EOS or at the start of another payload. event is set if the flushed
+ // payload is PES with a sync frame.
+ status_t flushScrambled(SyncEvent *event);
+
+ // Check if a PES packet is scrambled at PES level.
+ uint32_t getPesScramblingControl(ABitReader *br, int32_t *pesOffset);
+
// Strip and parse PES headers and pass remaining payload into onPayload
// with parsed metadata. event is set if the PES contains a sync frame.
status_t parsePES(ABitReader *br, SyncEvent *event);
@@ -179,7 +221,13 @@
// and timestamp of the packet.
void onPayloadData(
unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
- const uint8_t *data, size_t size, SyncEvent *event);
+ unsigned PES_scrambling_control,
+ const uint8_t *data, size_t size,
+ int32_t payloadOffset, SyncEvent *event);
+
+ // Ensure internal buffers can hold specified size, and will re-allocate
+ // as needed.
+ void ensureBufferCapacity(size_t size);
DISALLOW_EVIL_CONSTRUCTORS(Stream);
};
@@ -254,6 +302,8 @@
bool ATSParser::Program::parsePID(
unsigned pid, unsigned continuity_counter,
unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
ABitReader *br, status_t *err, SyncEvent *event) {
*err = OK;
@@ -263,7 +313,11 @@
}
*err = mStreams.editValueAt(index)->parse(
- continuity_counter, payload_unit_start_indicator, br, event);
+ continuity_counter,
+ payload_unit_start_indicator,
+ transport_scrambling_control,
+ random_access_indicator,
+ br, event);
return true;
}
@@ -359,6 +413,38 @@
return success;
}
+bool ATSParser::Program::findCADescriptor(
+ ABitReader *br, unsigned infoLength,
+ ATSParser::CADescriptor *caDescriptor) {
+ bool found = false;
+ while (infoLength > 2) {
+ unsigned descriptor_tag = br->getBits(8);
+ ALOGV(" tag = 0x%02x", descriptor_tag);
+
+ unsigned descriptor_length = br->getBits(8);
+ ALOGV(" len = %u", descriptor_length);
+
+ infoLength -= 2;
+ if (descriptor_length > infoLength) {
+ break;
+ }
+ if (descriptor_tag == 9 && descriptor_length >= 4) {
+ found = true;
+ caDescriptor->mSystemID = br->getBits(16);
+ caDescriptor->mPID = br->getBits(16) & 0x1fff;
+ infoLength -= 4;
+ caDescriptor->mPrivateData.assign(
+ br->data(), br->data() + descriptor_length - 4);
+ break;
+ } else {
+ infoLength -= descriptor_length;
+ br->skipBits(descriptor_length * 8);
+ }
+ }
+ br->skipBits(infoLength * 8);
+ return found;
+}
+
status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
unsigned table_id = br->getBits(8);
ALOGV(" table_id = %u", table_id);
@@ -395,7 +481,13 @@
unsigned program_info_length = br->getBits(12);
ALOGV(" program_info_length = %u", program_info_length);
- br->skipBits(program_info_length * 8); // skip descriptors
+ // descriptors
+ CADescriptor programCA;
+ bool hasProgramCA = findCADescriptor(br, program_info_length, &programCA);
+ if (hasProgramCA && !mParser->mCasManager->addProgram(
+ mProgramNumber, programCA)) {
+ return ERROR_MALFORMED;
+ }
Vector<StreamInfo> infos;
@@ -419,28 +511,17 @@
unsigned ES_info_length = br->getBits(12);
ALOGV(" ES_info_length = %u", ES_info_length);
-#if 0
- br->skipBits(ES_info_length * 8); // skip descriptors
-#else
- unsigned info_bytes_remaining = ES_info_length;
- while (info_bytes_remaining >= 2) {
- MY_LOGV(" tag = 0x%02x", br->getBits(8));
-
- unsigned descLength = br->getBits(8);
- ALOGV(" len = %u", descLength);
-
- if (info_bytes_remaining < descLength) {
- return ERROR_MALFORMED;
- }
- br->skipBits(descLength * 8);
-
- info_bytes_remaining -= descLength + 2;
+ CADescriptor streamCA;
+ bool hasStreamCA = findCADescriptor(br, ES_info_length, &streamCA);
+ if (hasStreamCA && !mParser->mCasManager->addStream(
+ mProgramNumber, elementaryPID, streamCA)) {
+ return ERROR_MALFORMED;
}
-#endif
-
StreamInfo info;
info.mType = streamType;
info.mPID = elementaryPID;
+ info.mCASystemId = hasProgramCA ? programCA.mSystemID :
+ hasStreamCA ? streamCA.mSystemID : -1;
infos.push(info);
infoBytesRemaining -= 5 + ES_info_length;
@@ -490,19 +571,29 @@
}
}
+ bool isAddingScrambledStream = false;
for (size_t i = 0; i < infos.size(); ++i) {
StreamInfo &info = infos.editItemAt(i);
+ if (mParser->mCasManager->isCAPid(info.mPID)) {
+ // skip CA streams (EMM/ECM)
+ continue;
+ }
ssize_t index = mStreams.indexOfKey(info.mPID);
if (index < 0) {
sp<Stream> stream = new Stream(
- this, info.mPID, info.mType, PCR_PID);
+ this, info.mPID, info.mType, PCR_PID, info.mCASystemId);
+ isAddingScrambledStream |= info.mCASystemId >= 0;
mStreams.add(info.mPID, stream);
}
}
+ if (isAddingScrambledStream) {
+ ALOGI("Receiving scrambled streams without descrambler!");
+ return ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED;
+ }
return OK;
}
@@ -586,13 +677,27 @@
return timeUs;
}
+void ATSParser::Program::updateCasSessions() {
+ for (size_t i = 0; i < mStreams.size(); ++i) {
+ sp<Stream> &stream = mStreams.editValueAt(i);
+ sp<IDescrambler> descrambler;
+ std::vector<uint8_t> sessionId;
+ if (mParser->mCasManager->getCasSession(
+ mProgramNumber, stream->pid(), &descrambler, &sessionId)) {
+ stream->setCasSession(descrambler, sessionId);
+ }
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
+static const size_t kInitialStreamBufferSize = 192 * 1024;
ATSParser::Stream::Stream(
Program *program,
unsigned elementaryPID,
unsigned streamType,
- unsigned PCR_PID)
+ unsigned PCR_PID,
+ int32_t CA_system_ID)
: mProgram(program),
mElementaryPID(elementaryPID),
mStreamType(streamType),
@@ -601,54 +706,71 @@
mPayloadStarted(false),
mEOSReached(false),
mPrevPTS(0),
- mQueue(NULL) {
+ 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;
+
+ ElementaryStreamQueue::Mode mode = ElementaryStreamQueue::INVALID;
+
switch (mStreamType) {
case STREAMTYPE_H264:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::H264,
- (mProgram->parserFlags() & ALIGNED_VIDEO_DATA)
- ? ElementaryStreamQueue::kFlag_AlignedData : 0);
+ mode = ElementaryStreamQueue::H264;
+ flags |= (mProgram->parserFlags() & ALIGNED_VIDEO_DATA) ?
+ ElementaryStreamQueue::kFlag_AlignedData : 0;
break;
+
case STREAMTYPE_MPEG2_AUDIO_ADTS:
- mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::AAC);
+ mode = ElementaryStreamQueue::AAC;
break;
+
case STREAMTYPE_MPEG1_AUDIO:
case STREAMTYPE_MPEG2_AUDIO:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::MPEG_AUDIO);
+ mode = ElementaryStreamQueue::MPEG_AUDIO;
break;
case STREAMTYPE_MPEG1_VIDEO:
case STREAMTYPE_MPEG2_VIDEO:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::MPEG_VIDEO);
+ mode = ElementaryStreamQueue::MPEG_VIDEO;
break;
case STREAMTYPE_MPEG4_VIDEO:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::MPEG4_VIDEO);
+ mode = ElementaryStreamQueue::MPEG4_VIDEO;
break;
case STREAMTYPE_LPCM_AC3:
case STREAMTYPE_AC3:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::AC3);
+ mode = ElementaryStreamQueue::AC3;
break;
case STREAMTYPE_METADATA:
- mQueue = new ElementaryStreamQueue(
- ElementaryStreamQueue::METADATA);
+ mode = ElementaryStreamQueue::METADATA;
break;
default:
- break;
+ ALOGE("stream PID 0x%02x has invalid stream type 0x%02x",
+ elementaryPID, streamType);
+ return;
}
- ALOGV("new stream PID 0x%02x, type 0x%02x", elementaryPID, streamType);
+ mQueue = new ElementaryStreamQueue(mode, flags);
if (mQueue != NULL) {
- mBuffer = new ABuffer(192 * 1024);
- mBuffer->setRange(0, 0);
+ ensureBufferCapacity(kInitialStreamBufferSize);
+
+ if (mScrambled && (isAudio() || isVideo())) {
+ // Set initial format to scrambled
+ sp<MetaData> meta = new MetaData();
+ meta->setCString(kKeyMIMEType,
+ isAudio() ? MEDIA_MIMETYPE_AUDIO_SCRAMBLED
+ : MEDIA_MIMETYPE_VIDEO_SCRAMBLED);
+ // for DrmInitData
+ meta->setData(kKeyCas, 0, &CA_system_ID, sizeof(CA_system_ID));
+ mSource = new AnotherPacketSource(meta);
+ }
}
}
@@ -657,10 +779,57 @@
mQueue = NULL;
}
+void ATSParser::Stream::ensureBufferCapacity(size_t neededSize) {
+ if (mBuffer != NULL && mBuffer->capacity() >= neededSize) {
+ return;
+ }
+
+ ALOGV("ensureBufferCapacity: current size %zu, new size %zu, scrambled %d",
+ mBuffer == NULL ? 0 : mBuffer->capacity(), neededSize, mScrambled);
+
+ sp<ABuffer> newBuffer, newScrambledBuffer;
+ sp<IMemory> newMem;
+ sp<MemoryDealer> newDealer;
+ if (mScrambled) {
+ size_t alignment = MemoryDealer::getAllocationAlignment();
+ neededSize = (neededSize + (alignment - 1)) & ~(alignment - 1);
+ // Align to multiples of 64K.
+ neededSize = (neededSize + 65535) & ~65535;
+ newDealer = new MemoryDealer(neededSize, "ATSParser");
+ newMem = newDealer->allocate(neededSize);
+ newScrambledBuffer = new ABuffer(newMem->pointer(), newMem->size());
+
+ if (mDescrambledBuffer != NULL) {
+ memcpy(newScrambledBuffer->data(),
+ mDescrambledBuffer->data(), mDescrambledBuffer->size());
+ newScrambledBuffer->setRange(0, mDescrambledBuffer->size());
+ } else {
+ newScrambledBuffer->setRange(0, 0);
+ }
+ mMem = newMem;
+ mDealer = newDealer;
+ mDescrambledBuffer = newScrambledBuffer;
+ } else {
+ // Align to multiples of 64K.
+ neededSize = (neededSize + 65535) & ~65535;
+ }
+
+ newBuffer = new ABuffer(neededSize);
+ if (mBuffer != NULL) {
+ memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+ newBuffer->setRange(0, mBuffer->size());
+ } else {
+ newBuffer->setRange(0, 0);
+ }
+ mBuffer = newBuffer;
+}
+
status_t ATSParser::Stream::parse(
unsigned continuity_counter,
- unsigned payload_unit_start_indicator, ABitReader *br,
- SyncEvent *event) {
+ unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
+ ABitReader *br, SyncEvent *event) {
if (mQueue == NULL) {
return OK;
}
@@ -672,6 +841,7 @@
mPayloadStarted = false;
mPesStartOffsets.clear();
mBuffer->setRange(0, 0);
+ mSubSamples.clear();
mExpectedContinuityCounter = -1;
#if 0
@@ -725,21 +895,16 @@
}
size_t neededSize = mBuffer->size() + payloadSizeBits / 8;
- if (mBuffer->capacity() < neededSize) {
- // Increment in multiples of 64K.
- neededSize = (neededSize + 65535) & ~65535;
-
- ALOGI("resizing buffer to %zu bytes", neededSize);
-
- sp<ABuffer> newBuffer = new ABuffer(neededSize);
- memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
- newBuffer->setRange(0, mBuffer->size());
- mBuffer = newBuffer;
- }
+ ensureBufferCapacity(neededSize);
memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8);
mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
+ if (mScrambled) {
+ mSubSamples.push_back({payloadSizeBits / 8,
+ transport_scrambling_control, random_access_indicator});
+ }
+
return OK;
}
@@ -789,6 +954,7 @@
mPesStartOffsets.clear();
mEOSReached = false;
mBuffer->setRange(0, 0);
+ mSubSamples.clear();
bool clearFormat = false;
if (isAudio()) {
@@ -817,7 +983,15 @@
}
if (mSource != NULL) {
- mSource->queueDiscontinuity(type, extra, true);
+ sp<MetaData> meta = mSource->getFormat();
+ const char* mime;
+ if (clearFormat && meta != NULL && meta->findCString(kKeyMIMEType, &mime)
+ && (!strncasecmp(mime, MEDIA_MIMETYPE_AUDIO_SCRAMBLED, 15)
+ || !strncasecmp(mime, MEDIA_MIMETYPE_VIDEO_SCRAMBLED, 15))){
+ mSource->clear();
+ } else {
+ mSource->queueDiscontinuity(type, extra, true);
+ }
}
}
@@ -830,6 +1004,8 @@
}
status_t ATSParser::Stream::parsePES(ABitReader *br, SyncEvent *event) {
+ const uint8_t *basePtr = br->data();
+
unsigned packet_startcode_prefix = br->getBits(24);
ALOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
@@ -859,7 +1035,9 @@
return ERROR_MALFORMED;
}
- MY_LOGV("PES_scrambling_control = %u", br->getBits(2));
+ unsigned PES_scrambling_control = br->getBits(2);
+ ALOGV("PES_scrambling_control = %u", PES_scrambling_control);
+
MY_LOGV("PES_priority = %u", br->getBits(1));
MY_LOGV("data_alignment_indicator = %u", br->getBits(1));
MY_LOGV("copyright = %u", br->getBits(1));
@@ -992,6 +1170,7 @@
br->skipBits(optional_bytes_remaining * 8);
// ES data follows.
+ int32_t pesOffset = br->data() - basePtr;
if (PES_packet_length != 0) {
if (PES_packet_length < PES_header_data_length + 3) {
@@ -1009,21 +1188,26 @@
return ERROR_MALFORMED;
}
+ ALOGV("There's %u bytes of payload, PES_packet_length=%u, offset=%d",
+ dataLength, PES_packet_length, pesOffset);
+
onPayloadData(
- PTS_DTS_flags, PTS, DTS, br->data(), dataLength, event);
+ PTS_DTS_flags, PTS, DTS, PES_scrambling_control,
+ br->data(), dataLength, pesOffset, event);
br->skipBits(dataLength * 8);
} else {
onPayloadData(
- PTS_DTS_flags, PTS, DTS,
- br->data(), br->numBitsLeft() / 8, event);
+ PTS_DTS_flags, PTS, DTS, PES_scrambling_control,
+ br->data(), br->numBitsLeft() / 8, pesOffset, event);
size_t payloadSizeBits = br->numBitsLeft();
if (payloadSizeBits % 8 != 0u) {
return ERROR_MALFORMED;
}
- ALOGV("There's %zu bytes of payload.", payloadSizeBits / 8);
+ ALOGV("There's %zu bytes of payload, offset=%d",
+ payloadSizeBits / 8, pesOffset);
}
} else if (stream_id == 0xbe) { // padding_stream
if (PES_packet_length == 0u) {
@@ -1040,6 +1224,200 @@
return OK;
}
+uint32_t ATSParser::Stream::getPesScramblingControl(
+ ABitReader *br, int32_t *pesOffset) {
+ unsigned packet_startcode_prefix = br->getBits(24);
+
+ ALOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);
+
+ if (packet_startcode_prefix != 1) {
+ ALOGV("unit does not start with startcode.");
+ return 0;
+ }
+
+ if (br->numBitsLeft() < 48) {
+ return 0;
+ }
+
+ unsigned stream_id = br->getBits(8);
+ ALOGV("stream_id = 0x%02x", stream_id);
+
+ br->skipBits(16); // PES_packet_length
+
+ if (stream_id != 0xbc // program_stream_map
+ && stream_id != 0xbe // padding_stream
+ && stream_id != 0xbf // private_stream_2
+ && stream_id != 0xf0 // ECM
+ && stream_id != 0xf1 // EMM
+ && stream_id != 0xff // program_stream_directory
+ && stream_id != 0xf2 // DSMCC
+ && stream_id != 0xf8) { // H.222.1 type E
+ if (br->getBits(2) != 2u) {
+ return 0;
+ }
+
+ unsigned PES_scrambling_control = br->getBits(2);
+ ALOGV("PES_scrambling_control = %u", PES_scrambling_control);
+
+ if (PES_scrambling_control == 0) {
+ return 0;
+ }
+
+ br->skipBits(12); // don't care
+
+ unsigned PES_header_data_length = br->getBits(8);
+ ALOGV("PES_header_data_length = %u", PES_header_data_length);
+
+ if (PES_header_data_length * 8 > br->numBitsLeft()) {
+ return 0;
+ }
+
+ *pesOffset = 9 + PES_header_data_length;
+ ALOGD("found PES_scrambling_control=%d, PES offset=%d",
+ PES_scrambling_control, *pesOffset);
+ return PES_scrambling_control;
+ }
+
+ return 0;
+}
+
+status_t ATSParser::Stream::flushScrambled(SyncEvent *event) {
+ if (mDescrambler == NULL) {
+ ALOGE("received scrambled packets without descrambler!");
+ return UNKNOWN_ERROR;
+ }
+
+ if (mDescrambledBuffer == NULL || mMem == NULL) {
+ ALOGE("received scrambled packets without shared memory!");
+
+ return UNKNOWN_ERROR;
+ }
+
+ int32_t pesOffset = 0;
+ int32_t descrambleSubSamples = 0, descrambleBytes = 0;
+ uint32_t tsScramblingControl = 0, pesScramblingControl = 0;
+
+ // First, go over subsamples to find TS-level scrambling key id, and
+ // calculate how many subsample we need to descramble (assuming we don't
+ // have PES-level scrambling).
+ for (auto it = mSubSamples.begin(); it != mSubSamples.end(); it++) {
+ if (it->transport_scrambling_mode != 0) {
+ // TODO: handle keyId change, use the first non-zero keyId for now.
+ if (tsScramblingControl == 0) {
+ tsScramblingControl = it->transport_scrambling_mode;
+ }
+ }
+ if (tsScramblingControl == 0 || descrambleSubSamples == 0
+ || !mQueue->isScrambled()) {
+ descrambleSubSamples++;
+ descrambleBytes += it->subSampleSize;
+ }
+ }
+ // If not scrambled at TS-level, check PES-level scrambling
+ if (tsScramblingControl == 0) {
+ ABitReader br(mBuffer->data(), mBuffer->size());
+ pesScramblingControl = getPesScramblingControl(&br, &pesOffset);
+ // If not scrambled at PES-level either, or scrambled at PES-level but
+ // requires output to remain scrambled, we don't need to descramble
+ // anything.
+ if (pesScramblingControl == 0 || mQueue->isScrambled()) {
+ descrambleSubSamples = 0;
+ descrambleBytes = 0;
+ }
+ }
+
+ uint32_t sctrl = tsScramblingControl != 0 ?
+ tsScramblingControl : pesScramblingControl;
+
+ // Perform the 1st pass descrambling if needed
+ if (descrambleBytes > 0) {
+ memcpy(mDescrambledBuffer->data(), mBuffer->data(), descrambleBytes);
+ mDescrambledBuffer->setRange(0, descrambleBytes);
+
+ sp<ABuffer> subSamples = new ABuffer(
+ sizeof(DescramblerPlugin::SubSample) * descrambleSubSamples);
+
+ DescrambleInfo info;
+ info.dstType = DescrambleInfo::kDestinationTypeVmPointer;
+ info.scramblingControl = (DescramblerPlugin::ScramblingControl)sctrl;
+ info.numSubSamples = descrambleSubSamples;
+ info.subSamples = (DescramblerPlugin::SubSample *)subSamples->data();
+ info.srcMem = mMem;
+ info.srcOffset = 0;
+ info.dstPtr = NULL; // in-place descrambling into srcMem
+ info.dstOffset = 0;
+
+ int32_t i = 0;
+ for (auto it = mSubSamples.begin();
+ it != mSubSamples.end() && i < descrambleSubSamples; it++, i++) {
+ if (it->transport_scrambling_mode != 0 || pesScramblingControl != 0) {
+ info.subSamples[i].mNumBytesOfClearData = 0;
+ info.subSamples[i].mNumBytesOfEncryptedData = it->subSampleSize;
+ } else {
+ info.subSamples[i].mNumBytesOfClearData = it->subSampleSize;
+ info.subSamples[i].mNumBytesOfEncryptedData = 0;
+ }
+ }
+ // If scrambled at PES-level, PES header should be skipped
+ if (pesScramblingControl != 0) {
+ info.srcOffset = info.dstOffset = pesOffset;
+ info.subSamples[0].mNumBytesOfEncryptedData -= pesOffset;
+ }
+
+ int32_t result;
+ Status status = mDescrambler->descramble(info, &result);
+
+ if (!status.isOk()) {
+ ALOGE("[stream %d] descramble failed, exceptionCode=%d",
+ mElementaryPID, status.exceptionCode());
+ return UNKNOWN_ERROR;
+ }
+
+ ALOGV("[stream %d] descramble succeeded, %d bytes",
+ mElementaryPID, result);
+ memcpy(mBuffer->data(), mDescrambledBuffer->data(), descrambleBytes);
+ }
+
+ if (mQueue->isScrambled()) {
+ // Queue subSample info for scrambled queue
+ sp<ABuffer> clearSizesBuffer = new ABuffer(mSubSamples.size() * 4);
+ sp<ABuffer> encSizesBuffer = new ABuffer(mSubSamples.size() * 4);
+ int32_t *clearSizePtr = (int32_t*)clearSizesBuffer->data();
+ int32_t *encSizePtr = (int32_t*)encSizesBuffer->data();
+ int32_t isSync = 0;
+ int32_t i = 0;
+ for (auto it = mSubSamples.begin();
+ it != mSubSamples.end(); it++, i++) {
+ if ((it->transport_scrambling_mode == 0
+ && pesScramblingControl == 0)
+ || i < descrambleSubSamples) {
+ clearSizePtr[i] = it->subSampleSize;
+ encSizePtr[i] = 0;
+ } else {
+ clearSizePtr[i] = 0;
+ encSizePtr[i] = it->subSampleSize;
+ }
+ isSync |= it->random_access_indicator;
+ }
+ // Pass the original TS subsample size now. The PES header adjust
+ // will be applied when the scrambled AU is dequeued.
+ mQueue->appendScrambledData(
+ mBuffer->data(), mBuffer->size(), sctrl,
+ isSync, clearSizesBuffer, encSizesBuffer);
+ }
+
+ ABitReader br(mBuffer->data(), mBuffer->size());
+ status_t err = parsePES(&br, event);
+
+ if (err != OK) {
+ ALOGE("[stream %d] failed to parse descrambled PES, err=%d",
+ mElementaryPID, err);
+ }
+
+ return err;
+}
+
+
status_t ATSParser::Stream::flush(SyncEvent *event) {
if (mBuffer == NULL || mBuffer->size() == 0) {
return OK;
@@ -1047,9 +1425,14 @@
ALOGV("flushing stream 0x%04x size = %zu", mElementaryPID, mBuffer->size());
- ABitReader br(mBuffer->data(), mBuffer->size());
-
- status_t err = parsePES(&br, event);
+ status_t err = OK;
+ if (mScrambled) {
+ err = flushScrambled(event);
+ mSubSamples.clear();
+ } else {
+ ABitReader br(mBuffer->data(), mBuffer->size());
+ err = parsePES(&br, event);
+ }
mBuffer->setRange(0, 0);
@@ -1058,7 +1441,9 @@
void ATSParser::Stream::onPayloadData(
unsigned PTS_DTS_flags, uint64_t PTS, uint64_t /* DTS */,
- const uint8_t *data, size_t size, SyncEvent *event) {
+ unsigned PES_scrambling_control,
+ const uint8_t *data, size_t size,
+ int32_t payloadOffset, SyncEvent *event) {
#if 0
ALOGI("payload streamType 0x%02x, PTS = 0x%016llx, dPTS = %lld",
mStreamType,
@@ -1074,7 +1459,8 @@
timeUs = mProgram->convertPTSToTimestamp(PTS);
}
- status_t err = mQueue->appendData(data, size, timeUs);
+ status_t err = mQueue->appendData(
+ data, size, timeUs, payloadOffset, PES_scrambling_control);
if (mEOSReached) {
mQueue->signalEOS();
@@ -1096,9 +1482,11 @@
const char *mime;
if (meta->findCString(kKeyMIMEType, &mime)
- && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
- && !IsIDR(accessUnit)) {
- continue;
+ && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+ int32_t sync = 0;
+ if (!accessUnit->meta()->findInt32("isSync", &sync) || !sync) {
+ continue;
+ }
}
mSource = new AnotherPacketSource(meta);
mSource->queueAccessUnit(accessUnit);
@@ -1178,6 +1566,18 @@
return NULL;
}
+void ATSParser::Stream::setCasSession(
+ const sp<IDescrambler> &descrambler,
+ const std::vector<uint8_t> &sessionId) {
+ if (mSource != NULL && mDescrambler == NULL && descrambler != NULL) {
+ signalDiscontinuity(DISCONTINUITY_FORMAT_ONLY, NULL);
+ mDescrambler = descrambler;
+ if (mQueue->isScrambled()) {
+ mQueue->setCasSession(sessionId);
+ }
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
ATSParser::ATSParser(uint32_t flags)
@@ -1189,6 +1589,7 @@
mNumTSPacketsParsed(0),
mNumPCRs(0) {
mPSISections.add(0 /* PID */, new PSISection);
+ mCasManager = new CasManager();
}
ATSParser::~ATSParser() {
@@ -1205,6 +1606,17 @@
return parseTS(&br, event);
}
+status_t ATSParser::setMediaCas(const sp<ICas> &cas) {
+ status_t err = mCasManager->setMediaCas(cas);
+ if (err != OK) {
+ return err;
+ }
+ for (size_t i = 0; i < mPrograms.size(); ++i) {
+ mPrograms.editItemAt(i)->updateCasSessions();
+ }
+ return OK;
+}
+
void ATSParser::signalDiscontinuity(
DiscontinuityType type, const sp<AMessage> &extra) {
int64_t mediaTimeUs;
@@ -1331,6 +1743,8 @@
ABitReader *br, unsigned PID,
unsigned continuity_counter,
unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
SyncEvent *event) {
ssize_t sectionIndex = mPSISections.indexOfKey(PID);
@@ -1402,7 +1816,10 @@
for (size_t i = 0; i < mPrograms.size(); ++i) {
status_t err;
if (mPrograms.editItemAt(i)->parsePID(
- PID, continuity_counter, payload_unit_start_indicator,
+ PID, continuity_counter,
+ payload_unit_start_indicator,
+ transport_scrambling_control,
+ random_access_indicator,
br, &err, event)) {
if (err != OK) {
return err;
@@ -1414,13 +1831,19 @@
}
if (!handled) {
+ handled = mCasManager->parsePID(br, PID);
+ }
+
+ if (!handled) {
ALOGV("PID 0x%04x not handled.", PID);
}
return OK;
}
-status_t ATSParser::parseAdaptationField(ABitReader *br, unsigned PID) {
+status_t ATSParser::parseAdaptationField(
+ ABitReader *br, unsigned PID, unsigned *random_access_indicator) {
+ *random_access_indicator = 0;
unsigned adaptation_field_length = br->getBits(8);
if (adaptation_field_length > 0) {
@@ -1435,7 +1858,16 @@
ALOGV("PID 0x%04x: discontinuity_indicator = 1 (!!!)", PID);
}
- br->skipBits(2);
+ *random_access_indicator = br->getBits(1);
+ if (*random_access_indicator) {
+ ALOGV("PID 0x%04x: random_access_indicator = 1", PID);
+ }
+
+ unsigned elementary_stream_priority_indicator = br->getBits(1);
+ if (elementary_stream_priority_indicator) {
+ ALOGV("PID 0x%04x: elementary_stream_priority_indicator = 1", PID);
+ }
+
unsigned PCR_flag = br->getBits(1);
size_t numBitsRead = 4;
@@ -1501,7 +1933,8 @@
unsigned PID = br->getBits(13);
ALOGV("PID = 0x%04x", PID);
- MY_LOGV("transport_scrambling_control = %u", br->getBits(2));
+ unsigned transport_scrambling_control = br->getBits(2);
+ ALOGV("transport_scrambling_control = %u", transport_scrambling_control);
unsigned adaptation_field_control = br->getBits(2);
ALOGV("adaptation_field_control = %u", adaptation_field_control);
@@ -1513,13 +1946,17 @@
status_t err = OK;
+ unsigned random_access_indicator = 0;
if (adaptation_field_control == 2 || adaptation_field_control == 3) {
- err = parseAdaptationField(br, PID);
+ err = parseAdaptationField(br, PID, &random_access_indicator);
}
if (err == OK) {
if (adaptation_field_control == 1 || adaptation_field_control == 3) {
err = parsePID(br, PID, continuity_counter,
- payload_unit_start_indicator, event);
+ payload_unit_start_indicator,
+ transport_scrambling_control,
+ random_access_indicator,
+ event);
}
}
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index faae6c9..4a88713 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -26,11 +26,17 @@
#include <utils/KeyedVector.h>
#include <utils/Vector.h>
#include <utils/RefBase.h>
+#include <vector>
namespace android {
-
+namespace media {
+class ICas;
+class IDescrambler;
+};
+using namespace media;
class ABitReader;
struct ABuffer;
+struct AnotherPacketSource;
struct ATSParser : public RefBase {
enum DiscontinuityType {
@@ -100,6 +106,8 @@
explicit ATSParser(uint32_t flags = 0);
+ status_t setMediaCas(const sp<ICas> &cas);
+
// Feed a TS packet into the parser. uninitialized event with the start
// offset of this TS packet goes in, and if the parser detects PES with
// a sync frame, the event will be initiailzed with the start offset of the
@@ -150,6 +158,14 @@
struct Program;
struct Stream;
struct PSISection;
+ struct CasManager;
+ struct CADescriptor {
+ int32_t mSystemID;
+ unsigned mPID;
+ std::vector<uint8_t> mPrivateData;
+ };
+
+ sp<CasManager> mCasManager;
uint32_t mFlags;
Vector<sp<Program> > mPrograms;
@@ -181,9 +197,13 @@
ABitReader *br, unsigned PID,
unsigned continuity_counter,
unsigned payload_unit_start_indicator,
+ unsigned transport_scrambling_control,
+ unsigned random_access_indicator,
SyncEvent *event);
- status_t parseAdaptationField(ABitReader *br, unsigned PID);
+ status_t parseAdaptationField(
+ ABitReader *br, unsigned PID, unsigned *random_access_indicator);
+
// see feedTSPacket().
status_t parseTS(ABitReader *br, SyncEvent *event);
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index 92c386c..de3d4f4 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -5,6 +5,7 @@
LOCAL_SRC_FILES:= \
AnotherPacketSource.cpp \
ATSParser.cpp \
+ CasManager.cpp \
ESQueue.cpp \
MPEG2PSExtractor.cpp \
MPEG2TSExtractor.cpp \
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 548f44e..433b1fc 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -204,25 +204,53 @@
}
MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
+ sp<MetaData> bufmeta = mediaBuffer->meta_data();
- mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
+ bufmeta->setInt64(kKeyTime, timeUs);
int32_t isSync;
if (buffer->meta()->findInt32("isSync", &isSync)) {
- mediaBuffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
+ bufmeta->setInt32(kKeyIsSyncFrame, isSync);
}
sp<ABuffer> sei;
if (buffer->meta()->findBuffer("sei", &sei) && sei != NULL) {
- mediaBuffer->meta_data()->setData(kKeySEI, 0, sei->data(), sei->size());
+ bufmeta->setData(kKeySEI, 0, sei->data(), sei->size());
}
sp<ABuffer> mpegUserData;
if (buffer->meta()->findBuffer("mpegUserData", &mpegUserData) && mpegUserData != NULL) {
- mediaBuffer->meta_data()->setData(
+ bufmeta->setData(
kKeyMpegUserData, 0, mpegUserData->data(), mpegUserData->size());
}
+ int32_t cryptoMode;
+ if (buffer->meta()->findInt32("cryptoMode", &cryptoMode)) {
+ int32_t cryptoKey;
+ sp<ABuffer> clearBytesBuffer, encBytesBuffer;
+
+ CHECK(buffer->meta()->findInt32("cryptoKey", &cryptoKey));
+ CHECK(buffer->meta()->findBuffer("clearBytes", &clearBytesBuffer)
+ && clearBytesBuffer != NULL);
+ CHECK(buffer->meta()->findBuffer("encBytes", &encBytesBuffer)
+ && encBytesBuffer != NULL);
+
+ bufmeta->setInt32(kKeyCryptoMode, cryptoMode);
+
+ uint8_t array[16] = {0};
+ bufmeta->setData(kKeyCryptoIV, 0, array, 16);
+
+ array[0] = (uint8_t) (cryptoKey & 0xff);
+ bufmeta->setData(kKeyCryptoKey, 0, array, 16);
+
+ bufmeta->setData(kKeyPlainSizes, 0,
+ clearBytesBuffer->data(), clearBytesBuffer->size());
+
+ bufmeta->setData(kKeyEncryptedSizes, 0,
+ encBytesBuffer->data(), encBytesBuffer->size());
+ }
+
+
*out = mediaBuffer;
return OK;
}
diff --git a/media/libstagefright/mpeg2ts/CasManager.cpp b/media/libstagefright/mpeg2ts/CasManager.cpp
new file mode 100644
index 0000000..4e34a30
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/CasManager.cpp
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2017 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 "CasManager"
+#include "CasManager.h"
+
+#include <android/media/ICas.h>
+#include <android/media/IDescrambler.h>
+#include <android/media/IMediaCasService.h>
+#include <binder/IServiceManager.h>
+#include <media/stagefright/foundation/ABitReader.h>
+#include <utils/Log.h>
+
+namespace android {
+using binder::Status;
+
+struct ATSParser::CasManager::ProgramCasManager : public RefBase {
+ ProgramCasManager(unsigned programNumber, const CADescriptor &descriptor);
+ ProgramCasManager(unsigned programNumber);
+
+ bool addStream(unsigned elementaryPID, const CADescriptor &descriptor);
+
+ status_t setMediaCas(const sp<ICas> &cas, PidToSessionMap &sessionMap);
+
+ bool getCasSession(unsigned elementaryPID,
+ sp<IDescrambler> *descrambler, std::vector<uint8_t> *sessionId) const;
+
+ void closeAllSessions(const sp<ICas>& cas);
+
+private:
+ struct CasSession {
+ CasSession() {}
+ CasSession(const CADescriptor &descriptor) :
+ mCADescriptor(descriptor) {}
+
+ CADescriptor mCADescriptor;
+ std::vector<uint8_t> mSessionId;
+ sp<IDescrambler> mDescrambler;
+ };
+ status_t initSession(
+ const sp<ICas>& cas, PidToSessionMap &sessionMap,
+ CasSession *session, unsigned programNumber, unsigned elementaryPID);
+ void closeSession(const sp<ICas>& cas, const CasSession &casSession);
+
+ unsigned mProgramNumber;
+ bool mHasProgramCas;
+ CasSession mProgramCas;
+ KeyedVector<unsigned, CasSession> mStreamPidToCasMap;
+};
+
+ATSParser::CasManager::ProgramCasManager::ProgramCasManager(
+ unsigned programNumber, const CADescriptor &descriptor) :
+ mProgramNumber(programNumber),
+ mHasProgramCas(true),
+ mProgramCas(descriptor) {}
+
+ATSParser::CasManager::ProgramCasManager::ProgramCasManager(
+ unsigned programNumber) :
+ mProgramNumber(programNumber),
+ mHasProgramCas(false) {}
+
+bool ATSParser::CasManager::ProgramCasManager::addStream(
+ unsigned elementaryPID, const CADescriptor &descriptor) {
+ ssize_t index = mStreamPidToCasMap.indexOfKey(elementaryPID);
+ if (index >= 0) {
+ return false;
+ }
+ ALOGV("addStream: program=%d, elementaryPID=%d, CA_system_ID=0x%x",
+ mProgramNumber, elementaryPID, descriptor.mSystemID);
+ mStreamPidToCasMap.add(elementaryPID, CasSession(descriptor));
+ return true;
+}
+
+status_t ATSParser::CasManager::ProgramCasManager::setMediaCas(
+ const sp<ICas> &cas, PidToSessionMap &sessionMap) {
+ if (mHasProgramCas) {
+ return initSession(cas, sessionMap, &mProgramCas, mProgramNumber, 0);
+ }
+ for (size_t index = 0; index < mStreamPidToCasMap.size(); index++) {
+ unsigned elementaryPID = mStreamPidToCasMap.keyAt(index);
+ status_t err;
+ if ((err = initSession(cas, sessionMap,
+ &mStreamPidToCasMap.editValueAt(index),
+ mProgramNumber, elementaryPID)) != OK) {
+ return err;
+ }
+ }
+ return OK;
+}
+
+bool ATSParser::CasManager::ProgramCasManager::getCasSession(
+ unsigned elementaryPID, sp<IDescrambler> *descrambler,
+ std::vector<uint8_t> *sessionId) const {
+ if (mHasProgramCas) {
+ *descrambler = mProgramCas.mDescrambler;
+ *sessionId = mProgramCas.mSessionId;
+ return true;
+ }
+ ssize_t index = mStreamPidToCasMap.indexOfKey(elementaryPID);
+ if (index < 0) {
+ return false;
+ }
+
+ *descrambler = mStreamPidToCasMap[index].mDescrambler;
+ *sessionId = mStreamPidToCasMap[index].mSessionId;
+ return true;
+}
+
+status_t ATSParser::CasManager::ProgramCasManager::initSession(
+ const sp<ICas>& cas, PidToSessionMap &sessionMap,
+ CasSession *session, unsigned programNumber, unsigned elementaryPID) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> casServiceBinder = sm->getService(String16("media.cas"));
+ sp<IMediaCasService> casService =
+ interface_cast<IMediaCasService>(casServiceBinder);
+
+ if (casService == NULL) {
+ ALOGE("Cannot obtain IMediaCasService");
+ return NO_INIT;
+ }
+
+ sp<IDescrambler> descrambler;
+ std::vector<uint8_t> sessionId;
+ const CADescriptor &descriptor = session->mCADescriptor;
+
+ Status status;
+ if (elementaryPID == 0) {
+ status = cas->openSession(programNumber, &sessionId);
+ } else {
+ status = cas->openSessionForStream(
+ programNumber, elementaryPID, &sessionId);
+ }
+ if (!status.isOk()) {
+ ALOGE("Failed to open session: exception=%d, error=%d",
+ status.exceptionCode(), status.serviceSpecificErrorCode());
+ goto l_fail;
+ }
+
+ cas->setSessionPrivateData(sessionId, descriptor.mPrivateData);
+ if (!status.isOk()) {
+ ALOGE("Failed to set private data: exception=%d, error=%d",
+ status.exceptionCode(), status.serviceSpecificErrorCode());
+ goto l_fail;
+ }
+
+ status = casService->createDescrambler(descriptor.mSystemID, &descrambler);
+ if (!status.isOk() || descrambler == NULL) {
+ ALOGE("Failed to create descrambler: : exception=%d, error=%d",
+ status.exceptionCode(), status.serviceSpecificErrorCode());
+ goto l_fail;
+ }
+
+ status = descrambler->setMediaCasSession(sessionId);
+ if (!status.isOk()) {
+ ALOGE("Failed to init descrambler: : exception=%d, error=%d",
+ status.exceptionCode(), status.serviceSpecificErrorCode());
+ goto l_fail;
+ }
+
+ session->mSessionId = sessionId;
+ session->mDescrambler = descrambler;
+ sessionMap.add(descriptor.mPID, sessionId);
+
+ return OK;
+
+l_fail:
+ if (!sessionId.empty()) {
+ cas->closeSession(sessionId);
+ }
+ if (descrambler != NULL) {
+ descrambler->release();
+ }
+ return NO_INIT;
+}
+
+void ATSParser::CasManager::ProgramCasManager::closeSession(
+ const sp<ICas>& cas, const CasSession &casSession) {
+ if (casSession.mDescrambler != NULL) {
+ casSession.mDescrambler->release();
+ }
+ if (!casSession.mSessionId.empty()) {
+ cas->closeSession(casSession.mSessionId);
+ }
+}
+
+void ATSParser::CasManager::ProgramCasManager::closeAllSessions(
+ const sp<ICas>& cas) {
+ if (mHasProgramCas) {
+ closeSession(cas, mProgramCas);
+ }
+ for (size_t index = 0; index < mStreamPidToCasMap.size(); index++) {
+ closeSession(cas, mStreamPidToCasMap.editValueAt(index));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ATSParser::CasManager::CasManager() : mSystemId(-1) {}
+
+ATSParser::CasManager::~CasManager() {
+ // Explictly close the sessions opened by us, since the CAS object is owned
+ // by the app and may not go away after the parser is destroyed, and the app
+ // may not have information about the sessions.
+ if (mICas != NULL) {
+ for (size_t index = 0; index < mProgramCasMap.size(); index++) {
+ mProgramCasMap.editValueAt(index)->closeAllSessions(mICas);
+ }
+ }
+}
+
+bool ATSParser::CasManager::setSystemId(int32_t CA_system_ID) {
+ if (mSystemId == -1) {
+ // Verify the CA_system_ID is within range on the first program
+ if (CA_system_ID < 0 || CA_system_ID > 0xffff) {
+ ALOGE("Invalid CA_system_id: %d", CA_system_ID);
+ return false;
+ }
+ mSystemId = CA_system_ID;
+ } else if (mSystemId != CA_system_ID) {
+ // All sessions need to be under the same CA system
+ ALOGE("Multiple CA systems not allowed: %d vs %d",
+ mSystemId, CA_system_ID);
+ return false;
+ }
+ return true;
+}
+
+status_t ATSParser::CasManager::setMediaCas(const sp<ICas> &cas) {
+ if (cas == NULL) {
+ ALOGE("setMediaCas: received NULL object");
+ return BAD_VALUE;
+ }
+ if (mICas != NULL) {
+ ALOGW("setMediaCas: already set");
+ return ALREADY_EXISTS;
+ }
+ for (size_t index = 0; index < mProgramCasMap.size(); index++) {
+ status_t err;
+ if ((err = mProgramCasMap.editValueAt(
+ index)->setMediaCas(cas, mCAPidToSessionIdMap)) != OK) {
+ return err;
+ }
+ }
+ mICas = cas;
+ return OK;
+}
+
+bool ATSParser::CasManager::addProgram(
+ unsigned programNumber, const CADescriptor &descriptor) {
+ if (!setSystemId(descriptor.mSystemID)) {
+ return false;
+ }
+
+ ssize_t index = mProgramCasMap.indexOfKey(programNumber);
+ if (index < 0) {
+ ALOGV("addProgram: programNumber=%d, CA_system_ID=0x%x",
+ programNumber, descriptor.mSystemID);
+ mProgramCasMap.add(programNumber,
+ new ProgramCasManager(programNumber, descriptor));
+ mCAPidSet.insert(descriptor.mPID);
+ }
+ return true;
+}
+
+bool ATSParser::CasManager::addStream(
+ unsigned programNumber, unsigned elementaryPID,
+ const CADescriptor &descriptor) {
+ if (!setSystemId(descriptor.mSystemID)) {
+ return false;
+ }
+
+ ssize_t index = mProgramCasMap.indexOfKey(programNumber);
+ sp<ProgramCasManager> programCasManager;
+ if (index < 0) {
+ ALOGV("addProgram (no CADescriptor): programNumber=%d", programNumber);
+ programCasManager = new ProgramCasManager(programNumber);
+ mProgramCasMap.add(programNumber, programCasManager);
+ } else {
+ programCasManager = mProgramCasMap.editValueAt(index);
+ }
+ if (programCasManager->addStream(elementaryPID, descriptor)) {
+ mCAPidSet.insert(descriptor.mPID);
+ }
+ return true;
+}
+
+bool ATSParser::CasManager::getCasSession(
+ unsigned programNumber, unsigned elementaryPID,
+ sp<IDescrambler> *descrambler, std::vector<uint8_t> *sessionId) const {
+ ssize_t index = mProgramCasMap.indexOfKey(programNumber);
+ if (index < 0) {
+ return false;
+ }
+ return mProgramCasMap[index]->getCasSession(
+ elementaryPID, descrambler, sessionId);
+}
+
+bool ATSParser::CasManager::isCAPid(unsigned pid) {
+ return mCAPidSet.find(pid) != mCAPidSet.end();
+}
+
+bool ATSParser::CasManager::parsePID(ABitReader *br, unsigned pid) {
+ ssize_t index = mCAPidToSessionIdMap.indexOfKey(pid);
+ if (index < 0) {
+ return false;
+ }
+ MediaCas::ParcelableCasData ecm(br->data(), br->numBitsLeft() / 8);
+ Status status = mICas->processEcm(mCAPidToSessionIdMap[index], ecm);
+ if (!status.isOk()) {
+ ALOGE("Failed to process ECM: exception=%d, error=%d",
+ status.exceptionCode(), status.serviceSpecificErrorCode());
+ }
+ return true; // handled
+}
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/CasManager.h b/media/libstagefright/mpeg2ts/CasManager.h
new file mode 100644
index 0000000..a7a3de9
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/CasManager.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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
+
+#include "ATSParser.h"
+#include <utils/KeyedVector.h>
+#include <set>
+
+namespace android {
+namespace media {
+class ICas;
+class IDescrambler;
+}
+
+struct ATSParser::CasManager : public RefBase {
+ CasManager();
+ virtual ~CasManager();
+
+ status_t setMediaCas(const sp<ICas> &cas);
+
+ bool addProgram(
+ unsigned programNumber, const CADescriptor &descriptor);
+
+ bool addStream(
+ unsigned programNumber, unsigned elementaryPID,
+ const CADescriptor &descriptor);
+
+ bool getCasSession(
+ unsigned programNumber, unsigned elementaryPID,
+ sp<IDescrambler> *descrambler,
+ std::vector<uint8_t> *sessionId) const;
+
+ bool isCAPid(unsigned pid);
+
+ bool parsePID(ABitReader *br, unsigned pid);
+
+private:
+ typedef KeyedVector<unsigned, std::vector<uint8_t> > PidToSessionMap;
+ struct ProgramCasManager;
+
+ bool setSystemId(int32_t CA_system_ID);
+
+ int32_t mSystemId;
+ sp<ICas> mICas;
+ KeyedVector<unsigned, sp<ProgramCasManager> > mProgramCasMap;
+ PidToSessionMap mCAPidToSessionIdMap;
+ std::set<uint32_t> mCAPidSet;
+};
+
+} // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 96ca405..b933002 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -28,6 +28,8 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
+#include <media/cas/DescramblerAPI.h>
+#include <media/hardware/CryptoAPI.h>
#include "include/avc_utils.h"
@@ -53,6 +55,11 @@
mRangeInfos.clear();
+ if (mScrambledBuffer != NULL) {
+ mScrambledBuffer->setRange(0, 0);
+ }
+ mScrambledRangeInfos.clear();
+
if (clearFormat) {
mFormat.clear();
}
@@ -246,7 +253,8 @@
}
status_t ElementaryStreamQueue::appendData(
- const void *data, size_t size, int64_t timeUs) {
+ const void *data, size_t size, int64_t timeUs,
+ int32_t payloadOffset, uint32_t pesScramblingControl) {
if (mEOSReached) {
ALOGE("appending data after EOS");
@@ -276,7 +284,7 @@
return ERROR_MALFORMED;
}
- if (startOffset > 0) {
+ if (mFormat == NULL && startOffset > 0) {
ALOGI("found something resembling an H.264/MPEG syncword "
"at offset %zd",
startOffset);
@@ -451,6 +459,8 @@
RangeInfo info;
info.mLength = size;
info.mTimestampUs = timeUs;
+ info.mPesOffset = payloadOffset;
+ info.mPesScramblingControl = pesScramblingControl;
mRangeInfos.push_back(info);
#if 0
@@ -463,8 +473,129 @@
return OK;
}
+void ElementaryStreamQueue::appendScrambledData(
+ const void *data, size_t size,
+ 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.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();
+ mBuffer->setRange(0, 0);
+ 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;
+ 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;
+ mScrambledRangeInfos.erase(it);
+ }
+ if (scrambledLength == 0) {
+ ALOGE("[stream %d] empty scrambled unit!", mMode);
+ return NULL;
+ }
+
+ // skip the PES header, and copy the rest into scrambled access unit
+ sp<ABuffer> scrambledAccessUnit = ABuffer::CreateAsCopy(
+ mScrambledBuffer->data() + pesOffset,
+ scrambledLength - pesOffset);
+
+ // fix up first sample size after skipping the PES header
+ if (pesOffset > 0) {
+ int32_t &firstClearSize = *(int32_t*)clearSizes->data();
+ int32_t &firstEncSize = *(int32_t*)encSizes->data();
+ // Cut away the PES header
+ if (firstClearSize >= pesOffset) {
+ // This is for TS-level scrambling, we descrambled the first
+ // (or it was clear to begin with)
+ firstClearSize -= pesOffset;
+ } else if (firstEncSize >= pesOffset) {
+ // This can only be PES-level scrambling
+ firstEncSize -= pesOffset;
+ }
+ }
+
+ 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_CBC);
+ scrambledAccessUnit->meta()->setInt32("cryptoKey", keyId);
+ scrambledAccessUnit->meta()->setBuffer("clearBytes", clearSizes);
+ scrambledAccessUnit->meta()->setBuffer("encBytes", encSizes);
+
+ 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 ((mFlags & kFlag_AlignedData) && mMode == H264) {
+ if ((mFlags & kFlag_AlignedData) && mMode == H264 && !isScrambled()) {
if (mRangeInfos.empty()) {
return NULL;
}
@@ -751,7 +882,8 @@
return accessUnit;
}
-int64_t ElementaryStreamQueue::fetchTimestamp(size_t size) {
+int64_t ElementaryStreamQueue::fetchTimestamp(
+ size_t size, int32_t *pesOffset, int32_t *pesScramblingControl) {
int64_t timeUs = -1;
bool first = true;
@@ -764,6 +896,12 @@
if (first) {
timeUs = info->mTimestampUs;
+ if (pesOffset != NULL) {
+ *pesOffset = info->mPesOffset;
+ }
+ if (pesScramblingControl != NULL) {
+ *pesScramblingControl = info->mPesScramblingControl;
+ }
first = false;
}
@@ -787,6 +925,25 @@
}
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() {
+ if (isScrambled()) {
+ if (mBuffer == NULL || mBuffer->size() == 0) {
+ return NULL;
+ }
+ if (mFormat == NULL) {
+ mFormat = MakeAVCCodecSpecificData(mBuffer);
+ if (mFormat == NULL) {
+ ALOGI("Creating dummy AVC format for scrambled content");
+ mFormat = new MetaData;
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ mFormat->setInt32(kKeyWidth, 1280);
+ mFormat->setInt32(kKeyHeight, 720);
+ }
+ // for DrmInitData
+ mFormat->setData(kKeyCas, 0, mCasSessionId.data(), mCasSessionId.size());
+ }
+ return dequeueScrambledAccessUnit();
+ }
+
const uint8_t *data = mBuffer->data();
size_t size = mBuffer->size();
@@ -1045,6 +1202,23 @@
}
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitMPEGVideo() {
+ if (isScrambled()) {
+ if (mBuffer == NULL || mBuffer->size() == 0) {
+ return NULL;
+ }
+ if (mFormat == NULL) {
+ ALOGI("Creating dummy MPEG format for scrambled content");
+ mFormat = new MetaData;
+ mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
+ mFormat->setInt32(kKeyWidth, 1280);
+ mFormat->setInt32(kKeyHeight, 720);
+
+ // for DrmInitData
+ mFormat->setData(kKeyCas, 0, mCasSessionId.data(), mCasSessionId.size());
+ }
+ return dequeueScrambledAccessUnit();
+ }
+
const uint8_t *data = mBuffer->data();
size_t size = mBuffer->size();
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 56f0706..6941e3f 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -22,6 +22,7 @@
#include <utils/Errors.h>
#include <utils/List.h>
#include <utils/RefBase.h>
+#include <vector>
namespace android {
@@ -30,6 +31,7 @@
struct ElementaryStreamQueue {
enum Mode {
+ INVALID = 0,
H264,
AAC,
AC3,
@@ -43,10 +45,19 @@
enum Flags {
// Data appended to the queue is always at access unit boundaries.
kFlag_AlignedData = 1,
+ kFlag_ScrambledData = 2,
};
explicit ElementaryStreamQueue(Mode mode, uint32_t flags = 0);
- status_t appendData(const void *data, size_t size, int64_t timeUs);
+ status_t appendData(const void *data, size_t size,
+ int64_t timeUs, int32_t payloadOffset = 0,
+ uint32_t pesScramblingControl = 0);
+
+ void appendScrambledData(
+ const void *data, size_t size,
+ int32_t keyId, bool isSync,
+ sp<ABuffer> clearSizes, sp<ABuffer> encSizes);
+
void signalEOS();
void clear(bool clearFormat);
@@ -54,10 +65,29 @@
sp<MetaData> getFormat();
+ bool isScrambled() {
+ return (mFlags & kFlag_ScrambledData) != 0;
+ }
+
+ void setCasSession(const std::vector<uint8_t> &sessionId) {
+ mCasSessionId = sessionId;
+ }
+
private:
struct RangeInfo {
int64_t mTimestampUs;
size_t mLength;
+ int32_t mPesOffset;
+ uint32_t mPesScramblingControl;
+ };
+
+ struct ScrambledRangeInfo {
+ //int64_t mTimestampUs;
+ size_t mLength;
+ int32_t mKeyId;
+ int32_t mIsSync;
+ sp<ABuffer> mClearSizes;
+ sp<ABuffer> mEncSizes;
};
Mode mMode;
@@ -67,6 +97,10 @@
sp<ABuffer> mBuffer;
List<RangeInfo> mRangeInfos;
+ sp<ABuffer> mScrambledBuffer;
+ List<ScrambledRangeInfo> mScrambledRangeInfos;
+ std::vector<uint8_t> mCasSessionId;
+
sp<MetaData> mFormat;
sp<ABuffer> dequeueAccessUnitH264();
@@ -80,7 +114,11 @@
// consume a logical (compressed) access unit of size "size",
// returns its timestamp in us (or -1 if no time information).
- int64_t fetchTimestamp(size_t size);
+ int64_t fetchTimestamp(size_t size,
+ int32_t *pesOffset = NULL,
+ int32_t *pesScramblingControl = NULL);
+
+ sp<ABuffer> dequeueScrambledAccessUnit();
DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue);
};
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index bde33dc..c3f1274 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -148,12 +148,46 @@
return meta;
}
+//static
+bool MPEG2TSExtractor::isScrambledFormat(const sp<MetaData> &format) {
+ const char *mime;
+ return format->findCString(kKeyMIMEType, &mime)
+ && (!strcasecmp(MEDIA_MIMETYPE_VIDEO_SCRAMBLED, mime)
+ || !strcasecmp(MEDIA_MIMETYPE_AUDIO_SCRAMBLED, mime));
+}
+
+status_t MPEG2TSExtractor::setMediaCas(const sp<ICas> &cas) {
+ ALOGD("setMediaCas: %p", cas.get());
+
+ status_t err = mParser->setMediaCas(cas);
+ if (err == OK) {
+ ALOGI("All tracks now have descramblers");
+ init();
+ }
+ return err;
+}
+
+void MPEG2TSExtractor::addSource(const sp<AnotherPacketSource> &impl) {
+ bool found = false;
+ for (size_t i = 0; i < mSourceImpls.size(); i++) {
+ if (mSourceImpls[i] == impl) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ mSourceImpls.push(impl);
+ }
+}
+
void MPEG2TSExtractor::init() {
bool haveAudio = false;
bool haveVideo = false;
int64_t startTime = ALooper::GetNowUs();
- while (feedMore(true /* isInit */) == OK) {
+ status_t err;
+ while ((err = feedMore(true /* isInit */)) == OK
+ || err == ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED) {
if (haveAudio && haveVideo) {
addSyncPoint_l(mLastSyncEvent);
mLastSyncEvent.reset();
@@ -165,10 +199,15 @@
ATSParser::VIDEO).get();
if (impl != NULL) {
- haveVideo = true;
- mSourceImpls.push(impl);
- mSyncPoints.push();
- mSeekSyncPoints = &mSyncPoints.editTop();
+ sp<MetaData> format = impl->getFormat();
+ if (format != NULL) {
+ haveVideo = true;
+ addSource(impl);
+ if (!isScrambledFormat(format)) {
+ mSyncPoints.push();
+ mSeekSyncPoints = &mSyncPoints.editTop();
+ }
+ }
}
}
@@ -178,11 +217,16 @@
ATSParser::AUDIO).get();
if (impl != NULL) {
- haveAudio = true;
- mSourceImpls.push(impl);
- mSyncPoints.push();
- if (!haveVideo) {
- mSeekSyncPoints = &mSyncPoints.editTop();
+ sp<MetaData> format = impl->getFormat();
+ if (format != NULL) {
+ haveAudio = true;
+ addSource(impl);
+ if (!isScrambledFormat(format)) {
+ mSyncPoints.push();
+ if (!haveVideo) {
+ mSeekSyncPoints = &mSyncPoints.editTop();
+ }
+ }
}
}
}
@@ -190,6 +234,16 @@
addSyncPoint_l(mLastSyncEvent);
mLastSyncEvent.reset();
+ // ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED is returned when the mpeg2ts
+ // is scrambled but we don't have a MediaCas object set. The extraction
+ // will only continue when setMediaCas() is called successfully.
+ if (err == ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED) {
+ ALOGI("stopped parsing scrambled content, "
+ "haveAudio=%d, haveVideo=%d, elaspedTime=%" PRId64,
+ haveAudio, haveVideo, ALooper::GetNowUs() - startTime);
+ return;
+ }
+
// Wait only for 2 seconds to detect audio/video streams.
if (ALooper::GetNowUs() - startTime > 2000000ll) {
break;