Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e32a4130 authored by Chong Zhang's avatar Chong Zhang
Browse files

MediaCas: return audio samples in scrambled form

We can no longer assume audio stream can always be
descrambled into clear buffers. If audio is scrambled,
handle it the same way and extract samples in scrambled
form. Set a dummy format on the track to provide info
about stream type only, and leave it to the decoder
to handle the format change.

bug: 111271614
Test: CTS MediaDrmClearkeyTest#testClearKeyPlaybackMpeg2ts
MediaDrmClearKeyTest#testPlaybackMpeg2ts
Change-Id: I2a803a92f395f06af54f3ad65a5c436edd7ce251
parent 965737ed
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@

//#define LOG_NDEBUG 0
#define LOG_TAG "MetaDataUtils"
#include <utils/Log.h>

#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaDataUtils.h>
@@ -25,6 +27,10 @@
namespace android {

bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) {
    if (data == nullptr || size == 0) {
        return false;
    }

    int32_t width;
    int32_t height;
    int32_t sarWidth;
@@ -46,6 +52,44 @@ bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t si
    return true;
}

bool MakeAACCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) {
    if (data == nullptr || size < 7) {
        return false;
    }

    ABitReader bits(data, size);

    // adts_fixed_header

    if (bits.getBits(12) != 0xfffu) {
        ALOGE("Wrong atds_fixed_header");
        return false;
    }

    bits.skipBits(4);  // ID, layer, protection_absent

    unsigned profile = bits.getBits(2);
    if (profile == 3u) {
        ALOGE("profile should not be 3");
        return false;
    }
    unsigned sampling_freq_index = bits.getBits(4);
    bits.getBits(1);  // private_bit
    unsigned channel_configuration = bits.getBits(3);
    if (channel_configuration == 0u) {
        ALOGE("channel_config should not be 0");
        return false;
    }

    if (!MakeAACCodecSpecificData(
            meta, profile, sampling_freq_index, channel_configuration)) {
        return false;
    }

    meta.setInt32(kKeyIsADTS, true);
    return true;
}

bool MakeAACCodecSpecificData(
        MetaDataBase &meta,
        unsigned profile, unsigned sampling_freq_index,
+1 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ namespace android {

struct ABuffer;
bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size);
bool MakeAACCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size);
bool MakeAACCodecSpecificData(MetaDataBase &meta, unsigned profile, unsigned sampling_freq_index,
        unsigned channel_configuration);

+21 −10
Original line number Diff line number Diff line
@@ -775,10 +775,12 @@ ATSParser::Stream::Stream(
    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;
    uint32_t flags = 0;
    if (((isVideo() || isAudio()) && mScrambled)) {
        flags = ElementaryStreamQueue::kFlag_ScrambledData;
    } else if (mSampleEncrypted) {
        flags = ElementaryStreamQueue::kFlag_SampleEncryptedData;
    }

    ElementaryStreamQueue::Mode mode = ElementaryStreamQueue::INVALID;

@@ -1499,7 +1501,13 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) {
        descrambleBytes = bytesWritten;
    }

    sp<ABuffer> buffer;
    // |buffer| points to the buffer from which we'd parse the PES header.
    // When the output stream is scrambled, it points to mDescrambledBuffer
    // (unless all packets in this PES are actually clear, in which case,
    // it points to mBuffer since we never copied into mDescrambledBuffer).
    // When the output stream is clear, it points to mBuffer, and we'll
    // copy all descrambled data back to mBuffer.
    sp<ABuffer> buffer = mBuffer;
    if (mQueue->isScrambled()) {
        // Queue subSample info for scrambled queue
        sp<ABuffer> clearSizesBuffer = new ABuffer(mSubSamples.size() * 4);
@@ -1528,15 +1536,18 @@ status_t ATSParser::Stream::flushScrambled(SyncEvent *event) {
        }
        // Pass the original TS subsample size now. The PES header adjust
        // will be applied when the scrambled AU is dequeued.
        // Note that if descrambleBytes is 0, it means this PES contains only
        // all ts packets, leadingClearBytes is entire buffer size.
        mQueue->appendScrambledData(
                mBuffer->data(), mBuffer->size(), sctrl,
                isSync, clearSizesBuffer, encSizesBuffer);
                mBuffer->data(), mBuffer->size(),
                (descrambleBytes > 0) ? descrambleBytes : mBuffer->size(),
                sctrl, isSync, clearSizesBuffer, encSizesBuffer);

        if (descrambleBytes > 0) {
            buffer = mDescrambledBuffer;
        }
    } else {
        memcpy(mBuffer->data(), mDescrambledBuffer->data(), descrambleBytes);

        buffer = mBuffer;
    }

    ABitReader br(buffer->data(), buffer->size());
+82 −64
Original line number Diff line number Diff line
@@ -366,7 +366,8 @@ status_t ElementaryStreamQueue::appendData(
        ALOGE("appending data after EOS");
        return ERROR_MALFORMED;
    }
    if (mBuffer == NULL || mBuffer->size() == 0) {

    if (!isScrambled() && (mBuffer == NULL || mBuffer->size() == 0)) {
        switch (mMode) {
            case H264:
            case MPEG_VIDEO:
@@ -617,6 +618,7 @@ status_t ElementaryStreamQueue::appendData(

void ElementaryStreamQueue::appendScrambledData(
        const void *data, size_t size,
        size_t leadingClearBytes,
        int32_t keyId, bool isSync,
        sp<ABuffer> clearSizes, sp<ABuffer> encSizes) {
    if (!isScrambled()) {
@@ -644,6 +646,7 @@ void ElementaryStreamQueue::appendScrambledData(

    ScrambledRangeInfo scrambledInfo;
    scrambledInfo.mLength = size;
    scrambledInfo.mLeadingClearBytes = leadingClearBytes;
    scrambledInfo.mKeyId = keyId;
    scrambledInfo.mIsSync = isSync;
    scrambledInfo.mClearSizes = clearSizes;
@@ -656,7 +659,6 @@ void ElementaryStreamQueue::appendScrambledData(

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) {
@@ -667,6 +669,7 @@ sp<ABuffer> ElementaryStreamQueue::dequeueScrambledAccessUnit() {
    // return scrambled unit
    int32_t keyId = pesScramblingControl, isSync = 0, scrambledLength = 0;
    sp<ABuffer> clearSizes, encSizes;
    size_t leadingClearBytes;
    while (mScrambledRangeInfos.size() > mRangeInfos.size()) {
        auto it = mScrambledRangeInfos.begin();
        ALOGV("[stream %d] fetching scrambled range: size=%zu", mMode, it->mLength);
@@ -684,6 +687,7 @@ sp<ABuffer> ElementaryStreamQueue::dequeueScrambledAccessUnit() {
        clearSizes = it->mClearSizes;
        encSizes = it->mEncSizes;
        isSync = it->mIsSync;
        leadingClearBytes = it->mLeadingClearBytes;
        mScrambledRangeInfos.erase(it);
    }
    if (scrambledLength == 0) {
@@ -691,6 +695,70 @@ sp<ABuffer> ElementaryStreamQueue::dequeueScrambledAccessUnit() {
        return NULL;
    }

    // Retrieve the leading clear bytes info, and use it to set the clear
    // range on mBuffer. Note that the leading clear bytes includes the
    // PES header portion, while mBuffer doesn't.
    if ((int32_t)leadingClearBytes > pesOffset) {
        mBuffer->setRange(0, leadingClearBytes - pesOffset);
    } else {
        mBuffer->setRange(0, 0);
    }

    // Try to parse formats, and if unavailable set up a dummy format.
    // Only support the following modes for scrambled content for now.
    // (will be expanded later).
    if (mFormat == NULL) {
        mFormat = new MetaData;
        switch (mMode) {
            case H264:
            {
                if (!MakeAVCCodecSpecificData(
                        *mFormat, mBuffer->data(), mBuffer->size())) {
                    ALOGI("Creating dummy AVC format for scrambled content");

                    mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
                    mFormat->setInt32(kKeyWidth, 1280);
                    mFormat->setInt32(kKeyHeight, 720);
                }
                break;
            }
            case AAC:
            {
                if (!MakeAACCodecSpecificData(
                        *mFormat, mBuffer->data(), mBuffer->size())) {
                    ALOGI("Creating dummy AAC format for scrambled content");

                    MakeAACCodecSpecificData(*mFormat,
                            1 /*profile*/, 7 /*sampling_freq_index*/, 1 /*channel_config*/);
                    mFormat->setInt32(kKeyIsADTS, true);
                }

                break;
            }
            case MPEG_VIDEO:
            {
                ALOGI("Creating dummy MPEG format for scrambled content");

                mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG2);
                mFormat->setInt32(kKeyWidth, 1280);
                mFormat->setInt32(kKeyHeight, 720);
                break;
            }
            default:
            {
                ALOGE("Unknown mode for scrambled content");
                return NULL;
            }
        }

        // for MediaExtractor.CasInfo
        mFormat->setInt32(kKeyCASystemID, mCASystemId);
        mFormat->setData(kKeyCASessionID,
                0, mCasSessionId.data(), mCasSessionId.size());
    }

    mBuffer->setRange(0, 0);

    // copy into scrambled access unit
    sp<ABuffer> scrambledAccessUnit = ABuffer::CreateAsCopy(
            mScrambledBuffer->data(), scrambledLength);
@@ -722,7 +790,11 @@ sp<ABuffer> ElementaryStreamQueue::dequeueScrambledAccessUnit() {
}

sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnit() {
    if ((mFlags & kFlag_AlignedData) && mMode == H264 && !isScrambled()) {
    if (isScrambled()) {
        return dequeueScrambledAccessUnit();
    }

    if ((mFlags & kFlag_AlignedData) && mMode == H264) {
        if (mRangeInfos.empty()) {
            return NULL;
        }
@@ -1024,25 +1096,11 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {
        bool protection_absent = bits.getBits(1) != 0;

        if (mFormat == NULL) {
            unsigned profile = bits.getBits(2);
            if (profile == 3u) {
                ALOGE("profile should not be 3");
                return NULL;
            }
            unsigned sampling_freq_index = bits.getBits(4);
            bits.getBits(1);  // private_bit
            unsigned channel_configuration = bits.getBits(3);
            if (channel_configuration == 0u) {
                ALOGE("channel_config should not be 0");
            mFormat = new MetaData;
            if (!MakeAACCodecSpecificData(
                    *mFormat, mBuffer->data() + offset, mBuffer->size() - offset)) {
                return NULL;
            }
            bits.skipBits(2);  // original_copy, home

            mFormat = new MetaData;
            MakeAACCodecSpecificData(*mFormat,
                    profile, sampling_freq_index, channel_configuration);

            mFormat->setInt32(kKeyIsADTS, true);

            int32_t sampleRate;
            int32_t numChannels;
@@ -1057,11 +1115,11 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAAC() {

            ALOGI("found AAC codec config (%d Hz, %d channels)",
                 sampleRate, numChannels);
        } else {
        }

        // profile_ObjectType, sampling_frequency_index, private_bits,
        // channel_configuration, original_copy, home
        bits.skipBits(12);
        }

        // adts_variable_header

@@ -1177,27 +1235,6 @@ int64_t ElementaryStreamQueue::fetchTimestamp(
}

sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() {
    if (isScrambled()) {
        if (mBuffer == NULL || mBuffer->size() == 0) {
            return NULL;
        }
        if (mFormat == NULL) {
            mFormat = new MetaData;
            if (!MakeAVCCodecSpecificData(*mFormat, mBuffer->data(), mBuffer->size())) {
                ALOGW("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 MediaExtractor.CasInfo
            mFormat->setInt32(kKeyCASystemID, mCASystemId);
            mFormat->setData(kKeyCASessionID, 0,
                    mCasSessionId.data(), mCasSessionId.size());
        }
        return dequeueScrambledAccessUnit();
    }

    const uint8_t *data = mBuffer->data();

    size_t size = mBuffer->size();
@@ -1497,25 +1534,6 @@ static sp<ABuffer> MakeMPEGVideoESDS(const sp<ABuffer> &csd) {
}

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 MediaExtractor.CasInfo
            mFormat->setInt32(kKeyCASystemID, mCASystemId);
            mFormat->setData(kKeyCASessionID, 0,
                    mCasSessionId.data(), mCasSessionId.size());
        }
        return dequeueScrambledAccessUnit();
    }

    const uint8_t *data = mBuffer->data();
    size_t size = mBuffer->size();

+2 −1
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ struct ElementaryStreamQueue {

    void appendScrambledData(
            const void *data, size_t size,
            size_t leadingClearBytes,
            int32_t keyId, bool isSync,
            sp<ABuffer> clearSizes, sp<ABuffer> encSizes);

@@ -85,8 +86,8 @@ private:
    };

    struct ScrambledRangeInfo {
        //int64_t mTimestampUs;
        size_t mLength;
        size_t mLeadingClearBytes;
        int32_t mKeyId;
        int32_t mIsSync;
        sp<ABuffer> mClearSizes;