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

Commit 4108b1ed authored by James Dong's avatar James Dong
Browse files

Add B frame support for MPEG4Writer

o requires the support of negative ctts duration values (ctts version 1)

Change-Id: Ib14130c9359c3bff3c76f20a7380d468a065dcaf
parent 622d5441
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ enum {
    kKeyIsSyncFrame       = 'sync',  // int32_t (bool)
    kKeyIsCodecConfig     = 'conf',  // int32_t (bool)
    kKeyTime              = 'time',  // int64_t (usecs)
    kKeyDecodingTime      = 'decT',  // int64_t (decoding timestamp in usecs)
    kKeyNTPTime           = 'ntpT',  // uint64_t (ntp-timestamp)
    kKeyTargetTime        = 'tarT',  // int64_t (usecs)
    kKeyDriftTime         = 'dftT',  // int64_t (usecs)
+6 −0
Original line number Diff line number Diff line
@@ -202,6 +202,10 @@ private:
    bool mOnlySubmitOneBufferAtOneTime;
    bool mEnableGrallocUsageProtected;

    // Used to record the decoding time for an output picture from
    // a video encoder.
    List<int64_t> mDecodingTimeList;

    OMXCodec(const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
             bool isEncoder, const char *mime, const char *componentName,
             const sp<MediaSource> &source,
@@ -317,6 +321,8 @@ private:

    status_t applyRotation();

    int64_t retrieveDecodingTimeUs(bool isCodecSpecific);

    OMXCodec(const OMXCodec &);
    OMXCodec &operator=(const OMXCodec &);
};
+118 −9
Original line number Diff line number Diff line
@@ -128,7 +128,6 @@ private:
    size_t        mNumStssTableEntries;
    List<int32_t> mStssTableEntries;

    size_t        mNumSttsTableEntries;
    struct SttsTableEntry {

        SttsTableEntry(uint32_t count, uint32_t duration)
@@ -137,8 +136,20 @@ private:
        uint32_t sampleCount;
        uint32_t sampleDuration;  // time scale based
    };
    size_t        mNumSttsTableEntries;
    List<SttsTableEntry> mSttsTableEntries;

    struct CttsTableEntry {
        CttsTableEntry(uint32_t count, int32_t timescaledDur)
            : sampleCount(count), sampleDuration(timescaledDur) {}

        uint32_t sampleCount;
        int32_t sampleDuration;  // time scale based
    };
    bool          mHasNegativeCttsDeltaDuration;
    size_t        mNumCttsTableEntries;
    List<CttsTableEntry> mCttsTableEntries;

    // Sequence parameter set or picture parameter set
    struct AVCParamSet {
        AVCParamSet(uint16_t length, const uint8_t *data)
@@ -219,6 +230,7 @@ private:

    // Duration is time scale based
    void addOneSttsTableEntry(size_t sampleCount, int32_t timescaledDur);
    void addOneCttsTableEntry(size_t sampleCount, int32_t timescaledDur);
    void sendTrackSummary(bool hasMultipleTracks);

    // Write the boxes
@@ -227,6 +239,7 @@ private:
    void writeStszBox();
    void writeStssBox();
    void writeSttsBox();
    void writeCttsBox();
    void writeD263Box();
    void writePaspBox();
    void writeAvccBox();
@@ -1147,6 +1160,7 @@ void MPEG4Writer::Track::updateTrackSizeEstimate() {
        mEstimatedTrackSizeBytes += mNumStscTableEntries * 12 +  // stsc box size
                                    mNumStssTableEntries * 4 +   // stss box size
                                    mNumSttsTableEntries * 8 +   // stts box size
                                    mNumCttsTableEntries * 8 +   // ctts box size
                                    stcoBoxSizeBytes +           // stco box size
                                    stszBoxSizeBytes;            // stsz box size
    }
@@ -1173,6 +1187,20 @@ void MPEG4Writer::Track::addOneSttsTableEntry(
    ++mNumSttsTableEntries;
}

void MPEG4Writer::Track::addOneCttsTableEntry(
        size_t sampleCount, int32_t duration) {

    if (mIsAudio) {
        return;
    }
    if (duration < 0 && !mHasNegativeCttsDeltaDuration) {
        mHasNegativeCttsDeltaDuration = true;
    }
    CttsTableEntry cttsEntry(sampleCount, duration);
    mCttsTableEntries.push_back(cttsEntry);
    ++mNumCttsTableEntries;
}

void MPEG4Writer::Track::addChunkOffset(off64_t offset) {
    ++mNumStcoTableEntries;
    mChunkOffsets.push_back(offset);
@@ -1483,6 +1511,7 @@ status_t MPEG4Writer::Track::start(MetaData *params) {
    mNumStssTableEntries = 0;
    mNumStscTableEntries = 0;
    mNumSttsTableEntries = 0;
    mNumCttsTableEntries = 0;
    mMdatSizeBytes = 0;
    mIsMediaTimeAdjustmentOn = false;
    mPrevMediaTimeAdjustTimestampUs = 0;
@@ -1491,6 +1520,7 @@ status_t MPEG4Writer::Track::start(MetaData *params) {
    mTotalDriftTimeToAdjustUs = 0;
    mPrevTotalAccumDriftTimeUs = 0;
    mMaxChunkDurationUs = 0;
    mHasNegativeCttsDeltaDuration = false;

    pthread_create(&mThread, &attr, ThreadWrapper, this);
    pthread_attr_destroy(&attr);
@@ -1932,14 +1962,19 @@ status_t MPEG4Writer::Track::threadEntry() {
    int64_t chunkTimestampUs = 0;
    int32_t nChunks = 0;
    int32_t nZeroLengthFrames = 0;
    int64_t lastTimestampUs = 0;  // Previous sample time stamp in ms
    int64_t lastDurationUs = 0;   // Between the previous two samples in ms
    int64_t lastTimestampUs = 0;      // Previous sample time stamp
    int64_t lastCttsTimeUs = 0;       // Previous sample time stamp
    int64_t lastDurationUs = 0;       // Between the previous two samples
    int64_t currDurationTicks = 0;    // Timescale based ticks
    int64_t lastDurationTicks = 0;    // Timescale based ticks
    int32_t sampleCount = 1;          // Sample count in the current stts table entry
    int64_t currCttsDurTicks = 0;     // Timescale based ticks
    int64_t lastCttsDurTicks = 0;     // Timescale based ticks
    int32_t cttsSampleCount = 1;      // Sample count in the current ctts table entry
    uint32_t previousSampleSize = 0;      // Size of the previous sample
    int64_t previousPausedDurationUs = 0;
    int64_t timestampUs;
    int64_t timestampUs = 0;
    int64_t cttsDeltaTimeUs = 0;

    if (mIsAudio) {
        prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0);
@@ -2063,7 +2098,6 @@ status_t MPEG4Writer::Track::threadEntry() {
         *
         */
        CHECK(meta_data->findInt64(kKeyTime, &timestampUs));
        LOGV("%s timestampUs: %lld", mIsAudio? "Audio": "Video", timestampUs);

////////////////////////////////////////////////////////////////////////////////
        if (mNumSamples == 0) {
@@ -2084,6 +2118,24 @@ status_t MPEG4Writer::Track::threadEntry() {

        timestampUs -= previousPausedDurationUs;
        CHECK(timestampUs >= 0);
        if (!mIsAudio) {
            /*
             * Composition time: timestampUs
             * Decoding time: decodingTimeUs
             * Composition time delta = composition time - decoding time
             *
             * We save picture decoding time stamp delta in stts table entries,
             * and composition time delta duration in ctts table entries.
             */
            int64_t decodingTimeUs;
            CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs));
            decodingTimeUs -= previousPausedDurationUs;
            int64_t timeUs = decodingTimeUs;
            cttsDeltaTimeUs = timestampUs - decodingTimeUs;
            timestampUs = decodingTimeUs;
            LOGV("decoding time: %lld and ctts delta time: %lld",
                timestampUs, cttsDeltaTimeUs);
        }

        // Media time adjustment for real-time applications
        if (mIsRealTimeRecording) {
@@ -2139,6 +2191,18 @@ status_t MPEG4Writer::Track::threadEntry() {
            } else {
                ++sampleCount;
            }

            if (!mIsAudio) {
                currCttsDurTicks =
                     ((cttsDeltaTimeUs * mTimeScale + 500000LL) / 1000000LL -
                     (lastCttsTimeUs * mTimeScale + 500000LL) / 1000000LL);
                if (currCttsDurTicks != lastCttsDurTicks) {
                    addOneCttsTableEntry(cttsSampleCount, lastCttsDurTicks);
                    cttsSampleCount = 1;
                } else {
                    ++cttsSampleCount;
                }
            }
        }
        if (mSamplesHaveSameSize) {
            if (mNumSamples >= 2 && previousSampleSize != sampleSize) {
@@ -2152,6 +2216,11 @@ status_t MPEG4Writer::Track::threadEntry() {
        lastDurationTicks = currDurationTicks;
        lastTimestampUs = timestampUs;

        if (!mIsAudio) {
            lastCttsDurTicks = currCttsDurTicks;
            lastCttsTimeUs = cttsDeltaTimeUs;
        }

        if (isSync != 0) {
            addOneStssTableEntry(mNumSamples);
        }
@@ -2221,8 +2290,10 @@ status_t MPEG4Writer::Track::threadEntry() {
    if (mNumSamples == 1) {
        lastDurationUs = 0;  // A single sample's duration
        lastDurationTicks = 0;
        lastCttsDurTicks = 0;
    } else {
        ++sampleCount;  // Count for the last sample
        ++cttsSampleCount;
    }

    if (mNumSamples <= 2) {
@@ -2234,6 +2305,7 @@ status_t MPEG4Writer::Track::threadEntry() {
        addOneSttsTableEntry(sampleCount, lastDurationTicks);
    }

    addOneCttsTableEntry(cttsSampleCount, lastCttsDurTicks);
    mTrackDurationUs += lastDurationUs;
    mReachedEOS = true;

@@ -2432,6 +2504,7 @@ void MPEG4Writer::Track::writeStblBox(bool use32BitOffset) {
    }
    mOwner->endBox();  // stsd
    writeSttsBox();
    writeCttsBox();
    if (!mIsAudio) {
        writeStssBox();
    }
@@ -2782,13 +2855,49 @@ void MPEG4Writer::Track::writeSttsBox() {
    int32_t dur = (trackStartTimeOffsetUs * mTimeScale + 500000LL) / 1000000LL;
    mOwner->writeInt32(dur + it->sampleDuration);

    int64_t totalCount = 1;
    while (++it != mSttsTableEntries.end()) {
        mOwner->writeInt32(it->sampleCount);
        mOwner->writeInt32(it->sampleDuration);
        totalCount += it->sampleCount;
    }
    CHECK(totalCount == mNumSamples);
    mOwner->endBox();  // stts
}

void MPEG4Writer::Track::writeCttsBox() {
    if (mIsAudio) {  // ctts is not for audio
        return;
    }

    // Do not write ctts box when there is no need to have it.
    if ((mNumCttsTableEntries == 1 &&
        mCttsTableEntries.begin()->sampleDuration == 0) ||
        mNumCttsTableEntries == 0) {
        return;
    }

    LOGV("ctts box has %d entries", mNumCttsTableEntries);

    mOwner->beginBox("ctts");
    if (mHasNegativeCttsDeltaDuration) {
        mOwner->writeInt32(0x00010000);  // version=1, flags=0
    } else {
        mOwner->writeInt32(0);  // version=0, flags=0
    }
    mOwner->writeInt32(mNumCttsTableEntries);

    int64_t totalCount = 0;
    for (List<CttsTableEntry>::iterator it = mCttsTableEntries.begin();
         it != mCttsTableEntries.end(); ++it) {
        mOwner->writeInt32(it->sampleCount);
        mOwner->writeInt32(it->sampleDuration);
        totalCount += it->sampleCount;
    }
    CHECK(totalCount == mNumSamples);
    mOwner->endBox();  // ctts
}

void MPEG4Writer::Track::writeStssBox() {
    mOwner->beginBox("stss");
    mOwner->writeInt32(0);  // version=0, flags=0
+24 −0
Original line number Diff line number Diff line
@@ -1981,6 +1981,20 @@ OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() {
    return bufInfo;
}

int64_t OMXCodec::retrieveDecodingTimeUs(bool isCodecSpecific) {
    CHECK(mIsEncoder);
    CHECK(!mDecodingTimeList.empty());
    List<int64_t>::iterator it = mDecodingTimeList.begin();
    int64_t timeUs = *it;

    // If the output buffer is codec specific configuration,
    // do not remove the decoding time from the list.
    if (!isCodecSpecific) {
        mDecodingTimeList.erase(it);
    }
    return timeUs;
}

void OMXCodec::on_message(const omx_message &msg) {
    if (mState == ERROR) {
        LOGW("Dropping OMX message - we're in ERROR state.");
@@ -2128,14 +2142,21 @@ void OMXCodec::on_message(const omx_message &msg) {
                if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) {
                    buffer->meta_data()->setInt32(kKeyIsSyncFrame, true);
                }
                bool isCodecSpecific = false;
                if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_CODECCONFIG) {
                    buffer->meta_data()->setInt32(kKeyIsCodecConfig, true);
                    isCodecSpecific = true;
                }

                if (isGraphicBuffer || mQuirks & kOutputBuffersAreUnreadable) {
                    buffer->meta_data()->setInt32(kKeyIsUnreadable, true);
                }

                if (mIsEncoder) {
                    int64_t decodingTimeUs = retrieveDecodingTimeUs(isCodecSpecific);
                    buffer->meta_data()->setInt64(kKeyDecodingTime, decodingTimeUs);
                }

                buffer->meta_data()->setPointer(
                        kKeyPlatformPrivate,
                        msg.u.extended_buffer_data.platform_private);
@@ -2938,6 +2959,9 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) {
        int64_t lastBufferTimeUs;
        CHECK(srcBuffer->meta_data()->findInt64(kKeyTime, &lastBufferTimeUs));
        CHECK(lastBufferTimeUs >= 0);
        if (mIsEncoder) {
            mDecodingTimeList.push_back(lastBufferTimeUs);
        }

        if (offset == 0) {
            timestampUs = lastBufferTimeUs;