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

Commit b4962777 authored by Gopalakrishnan Nallasamy's avatar Gopalakrishnan Nallasamy
Browse files

MPEG4Extractor+MPEG4Writer:Bframes+EditlistSupport

Enabled BFrames encoding in ACodec. Added edit list box support
for B frames recording use case in Mpeg4Writer. Added edit list
support in MPEG4Extractor for video tracks with B frames and postive
CTTS entries. Also, fixed issues in the existing edit list support
for audio tracks.

Bug: 113022200
Bug: 113114128

Test: Recorded video in crosshatch and that played in synch on
      VLC Player-Linux, QT Player-MAC and test device/crosshatch.
Test: run cts-dev --module CtsMediaTestCases --compatibility:module-arg
      CtsMediaTestCases:include-annotation:android.platform.test.annotations.RequiresDevice
      A few test cases in MediaRecorderTest is failing, but those are
      failing without any of the code changes in this change list.
      A few of the test cases in MediaMetadataRetrieverTest are failing.
      Those cases didn't account for edit list entry in video file and
      hence those cases have to be modified.
Test: Simulated frame drops while B Frames recording, by skipping a few frames
      while muxing a video file with Bframes using MediaMuxer and manually
      checked values in CTTS table and also the elst entry in edts box.
Test: Checked playback of files in bug 113114128.
Test: Recorded video on crosshatch. Checked playback on Nexus 4 and
      Nexus 6.

Change-Id: I0cf7b26c0d6180b6bb7ad9924505823a629e55a0
parent 77ebff94
Loading
Loading
Loading
Loading
+121 −64
Original line number Original line Diff line number Diff line
@@ -80,7 +80,8 @@ public:
                Vector<SidxEntry> &sidx,
                Vector<SidxEntry> &sidx,
                const Trex *trex,
                const Trex *trex,
                off64_t firstMoofOffset,
                off64_t firstMoofOffset,
                const sp<ItemTable> &itemTable);
                const sp<ItemTable> &itemTable,
                int32_t elstShiftStartTicks);
    virtual status_t init();
    virtual status_t init();


    virtual media_status_t start();
    virtual media_status_t start();
@@ -109,7 +110,7 @@ private:
    off64_t mFirstMoofOffset;
    off64_t mFirstMoofOffset;
    off64_t mCurrentMoofOffset;
    off64_t mCurrentMoofOffset;
    off64_t mNextMoofOffset;
    off64_t mNextMoofOffset;
    uint32_t mCurrentTime;
    uint32_t mCurrentTime; // in media timescale ticks
    int32_t mLastParsedTrackId;
    int32_t mLastParsedTrackId;
    int32_t mTrackId;
    int32_t mTrackId;


@@ -143,6 +144,10 @@ private:
    bool mIsHeif;
    bool mIsHeif;
    sp<ItemTable> mItemTable;
    sp<ItemTable> mItemTable;


    // Start offset from composition time to presentation time.
    // Support shift only for video tracks through mElstShiftStartTicks for now.
    int32_t mElstShiftStartTicks;

    size_t parseNALSize(const uint8_t *data) const;
    size_t parseNALSize(const uint8_t *data) const;
    status_t parseChunk(off64_t *offset);
    status_t parseChunk(off64_t *offset);
    status_t parseTrackFragmentHeader(off64_t offset, off64_t size);
    status_t parseTrackFragmentHeader(off64_t offset, off64_t size);
@@ -459,11 +464,12 @@ media_status_t MPEG4Extractor::getTrackMetaData(
    [=] {
    [=] {
        int64_t duration;
        int64_t duration;
        int32_t samplerate;
        int32_t samplerate;
        // Only for audio track.
        if (track->has_elst && mHeaderTimescale != 0 &&
        if (track->has_elst && mHeaderTimescale != 0 &&
                AMediaFormat_getInt64(track->meta, AMEDIAFORMAT_KEY_DURATION, &duration) &&
                AMediaFormat_getInt64(track->meta, AMEDIAFORMAT_KEY_DURATION, &duration) &&
                AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &samplerate)) {
                AMediaFormat_getInt32(track->meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, &samplerate)) {


            // elst has to be processed only the first time this function is called
            // Elst has to be processed only the first time this function is called.
            track->has_elst = false;
            track->has_elst = false;


            if (track->elst_segment_duration > INT64_MAX) {
            if (track->elst_segment_duration > INT64_MAX) {
@@ -479,10 +485,13 @@ media_status_t MPEG4Extractor::getTrackMetaData(
                  halfscale, mHeaderTimescale, track->timescale);
                  halfscale, mHeaderTimescale, track->timescale);


            if ((uint32_t)samplerate != track->timescale){
            if ((uint32_t)samplerate != track->timescale){
                ALOGV("samplerate:%" PRId32 ", track->timescale and samplerate are different!", samplerate);
                ALOGV("samplerate:%" PRId32 ", track->timescale and samplerate are different!",
                    samplerate);
            }
            }

            // Both delay and paddingsamples have to be set inorder for either to be
            int64_t delay;
            // effective in the lower layers.
            int64_t delay = 0;
            if (media_time > 0) { // Gapless playback
                // delay = ((media_time * samplerate) + halfscale) / track->timescale;
                // delay = ((media_time * samplerate) + halfscale) / track->timescale;
                if (__builtin_mul_overflow(media_time, samplerate, &delay) ||
                if (__builtin_mul_overflow(media_time, samplerate, &delay) ||
                        __builtin_add_overflow(delay, halfscale, &delay) ||
                        __builtin_add_overflow(delay, halfscale, &delay) ||
@@ -492,9 +501,12 @@ media_status_t MPEG4Extractor::getTrackMetaData(
                    ALOGW("ignoring edit list with bogus values");
                    ALOGW("ignoring edit list with bogus values");
                    return;
                    return;
                }
                }
            }
            ALOGV("delay = %" PRId64, delay);
            ALOGV("delay = %" PRId64, delay);
            AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_DELAY, delay);
            AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_DELAY, delay);


            int64_t paddingsamples = 0;
            if (segment_duration > 0) {
                int64_t scaled_duration;
                int64_t scaled_duration;
                // scaled_duration = duration * mHeaderTimescale;
                // scaled_duration = duration * mHeaderTimescale;
                if (__builtin_mul_overflow(duration, mHeaderTimescale, &scaled_duration)) {
                if (__builtin_mul_overflow(duration, mHeaderTimescale, &scaled_duration)) {
@@ -508,7 +520,7 @@ media_status_t MPEG4Extractor::getTrackMetaData(
                int64_t media_time_scaled_e6;
                int64_t media_time_scaled_e6;
                int64_t media_time_scaled;
                int64_t media_time_scaled;
                // padding = scaled_duration - ((segment_duration * 1000000) +
                // padding = scaled_duration - ((segment_duration * 1000000) +
            //                  ((media_time * mHeaderTimeScale * 1000000)/track->timescale) )
                //                  ((media_time * mHeaderTimescale * 1000000)/track->timescale) )
                // segment_duration is based on timescale in movie header box(mdhd)
                // segment_duration is based on timescale in movie header box(mdhd)
                // media_time is based on timescale track header/media timescale
                // media_time is based on timescale track header/media timescale
                if (__builtin_mul_overflow(segment_duration, 1000000, &segment_duration_e6) ||
                if (__builtin_mul_overflow(segment_duration, 1000000, &segment_duration_e6) ||
@@ -517,24 +529,22 @@ media_status_t MPEG4Extractor::getTrackMetaData(
                    return;
                    return;
                }
                }
                media_time_scaled_e6 /= track->timescale;
                media_time_scaled_e6 /= track->timescale;
            if(__builtin_add_overflow(segment_duration_e6, media_time_scaled_e6, &segment_end) ||
                if (__builtin_add_overflow(segment_duration_e6, media_time_scaled_e6, &segment_end)
                __builtin_sub_overflow(scaled_duration, segment_end, &padding)) {
                    || __builtin_sub_overflow(scaled_duration, segment_end, &padding)) {
                    return;
                    return;
                }
                }
                ALOGV("segment_end = %" PRId64 ", padding = %" PRId64, segment_end, padding);
                ALOGV("segment_end = %" PRId64 ", padding = %" PRId64, segment_end, padding);
            int64_t paddingsamples = 0;
            if (padding < 0) {
                // track duration from media header (which is what AMEDIAFORMAT_KEY_DURATION is)
                // track duration from media header (which is what AMEDIAFORMAT_KEY_DURATION is)
                // might be slightly shorter than the segment duration, which would make the
                // might be slightly shorter than the segment duration, which would make the
                // padding negative. Clamp to zero.
                // padding negative. Clamp to zero.
                padding = 0;
                if (padding > 0) {
            } else {
                    int64_t halfscale_mht = mHeaderTimescale / 2;
                    int64_t halfscale_e6;
                    int64_t halfscale_e6;
                    int64_t timescale_e6;
                    int64_t timescale_e6;
                // paddingsamples = ((padding * samplerate) + (halfscale * 1000000))
                    // paddingsamples = ((padding * samplerate) + (halfscale_mht * 1000000))
                    //                / (mHeaderTimescale * 1000000);
                    //                / (mHeaderTimescale * 1000000);
                    if (__builtin_mul_overflow(padding, samplerate, &paddingsamples) ||
                    if (__builtin_mul_overflow(padding, samplerate, &paddingsamples) ||
                        __builtin_mul_overflow(halfscale, 1000000, &halfscale_e6) ||
                            __builtin_mul_overflow(halfscale_mht, 1000000, &halfscale_e6) ||
                            __builtin_mul_overflow(mHeaderTimescale, 1000000, &timescale_e6) ||
                            __builtin_mul_overflow(mHeaderTimescale, 1000000, &timescale_e6) ||
                            __builtin_add_overflow(paddingsamples, halfscale_e6, &paddingsamples) ||
                            __builtin_add_overflow(paddingsamples, halfscale_e6, &paddingsamples) ||
                            (paddingsamples /= timescale_e6, false) ||
                            (paddingsamples /= timescale_e6, false) ||
@@ -542,6 +552,7 @@ media_status_t MPEG4Extractor::getTrackMetaData(
                        return;
                        return;
                    }
                    }
                }
                }
            }
            ALOGV("paddingsamples = %" PRId64, paddingsamples);
            ALOGV("paddingsamples = %" PRId64, paddingsamples);
            AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_PADDING, paddingsamples);
            AMediaFormat_setInt32(track->meta, AMEDIAFORMAT_KEY_ENCODER_PADDING, paddingsamples);
        }
        }
@@ -668,6 +679,7 @@ status_t MPEG4Extractor::readMetaData() {
            track->includes_expensive_metadata = false;
            track->includes_expensive_metadata = false;
            track->skipTrack = false;
            track->skipTrack = false;
            track->timescale = 1000000;
            track->timescale = 1000000;
            track->elstShiftStartTicks = 0;
        }
        }
    }
    }


@@ -965,6 +977,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
                        AMEDIAFORMAT_KEY_MIME, "application/octet-stream");
                        AMEDIAFORMAT_KEY_MIME, "application/octet-stream");
                track->has_elst = false;
                track->has_elst = false;
                track->subsample_encryption = false;
                track->subsample_encryption = false;
                track->elstShiftStartTicks = 0;
            }
            }


            off64_t stop_offset = *offset + chunk_size;
            off64_t stop_offset = *offset + chunk_size;
@@ -1092,6 +1105,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {


            if (entry_count != 1) {
            if (entry_count != 1) {
                // we only support a single entry at the moment, for gapless playback
                // we only support a single entry at the moment, for gapless playback
                // or start offset
                ALOGW("ignoring edit list with %d entries", entry_count);
                ALOGW("ignoring edit list with %d entries", entry_count);
            } else {
            } else {
                off64_t entriesoffset = data_offset + 8;
                off64_t entriesoffset = data_offset + 8;
@@ -3929,9 +3943,15 @@ MediaTrackHelper *MPEG4Extractor::getTrack(size_t index) {
        }
        }
    }
    }


    if (track->has_elst and !strncasecmp("video/", mime, 6) and track->elst_media_time > 0) {
        track->elstShiftStartTicks = track->elst_media_time;
        ALOGV("video track->elstShiftStartTicks :%" PRId64, track->elst_media_time);
    }

    MPEG4Source *source =  new MPEG4Source(
    MPEG4Source *source =  new MPEG4Source(
            track->meta, mDataSource, track->timescale, track->sampleTable,
            track->meta, mDataSource, track->timescale, track->sampleTable,
            mSidxEntries, trex, mMoofOffset, itemTable);
            mSidxEntries, trex, mMoofOffset, itemTable,
            track->elstShiftStartTicks);
    if (source->init() != OK) {
    if (source->init() != OK) {
        delete source;
        delete source;
        return NULL;
        return NULL;
@@ -4332,7 +4352,8 @@ MPEG4Source::MPEG4Source(
        Vector<SidxEntry> &sidx,
        Vector<SidxEntry> &sidx,
        const Trex *trex,
        const Trex *trex,
        off64_t firstMoofOffset,
        off64_t firstMoofOffset,
        const sp<ItemTable> &itemTable)
        const sp<ItemTable> &itemTable,
        int32_t elstShiftStartTicks)
    : mFormat(format),
    : mFormat(format),
      mDataSource(dataSource),
      mDataSource(dataSource),
      mTimescale(timeScale),
      mTimescale(timeScale),
@@ -4360,7 +4381,8 @@ MPEG4Source::MPEG4Source(
      mBuffer(NULL),
      mBuffer(NULL),
      mSrcBuffer(NULL),
      mSrcBuffer(NULL),
      mIsHeif(itemTable != NULL),
      mIsHeif(itemTable != NULL),
      mItemTable(itemTable) {
      mItemTable(itemTable),
      mElstShiftStartTicks(elstShiftStartTicks) {


    memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));
    memset(&mTrackFragmentHeaderInfo, 0, sizeof(mTrackFragmentHeaderInfo));


@@ -4445,11 +4467,31 @@ MPEG4Source::MPEG4Source(
}
}


status_t MPEG4Source::init() {
status_t MPEG4Source::init() {
    status_t err = OK;
    const char *mime;
    CHECK(AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mime));
    if (mFirstMoofOffset != 0) {
    if (mFirstMoofOffset != 0) {
        off64_t offset = mFirstMoofOffset;
        off64_t offset = mFirstMoofOffset;
        return parseChunk(&offset);
        err = parseChunk(&offset);
        if(err == OK && !strncasecmp("video/", mime, 6)
            && !mCurrentSamples.isEmpty()) {
            // Start offset should be less or equal to composition time of first sample.
            // ISO : sample_composition_time_offset, version 0 (unsigned) for major brands.
            mElstShiftStartTicks = std::min(mElstShiftStartTicks,
                (*mCurrentSamples.begin()).compositionOffset);
        }
        }
    return OK;
        return err;
    }

    if (!strncasecmp("video/", mime, 6)) {
        uint32_t firstSampleCTS = 0;
        err = mSampleTable->getMetaDataForSample(0, NULL, NULL, &firstSampleCTS);
        // Start offset should be less or equal to composition time of first sample.
        // Composition time stamp of first sample cannot be negative.
        mElstShiftStartTicks = std::min(mElstShiftStartTicks, (int32_t)firstSampleCTS);
    }

    return err;
}
}


MPEG4Source::~MPEG4Source() {
MPEG4Source::~MPEG4Source() {
@@ -4990,7 +5032,7 @@ status_t MPEG4Source::parseTrackFragmentHeader(off64_t offset, off64_t size) {


status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) {
status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) {


    ALOGV("MPEG4Extractor::parseTrackFragmentRun");
    ALOGV("MPEG4Source::parseTrackFragmentRun");
    if (size < 8) {
    if (size < 8) {
        return -EINVAL;
        return -EINVAL;
    }
    }
@@ -5132,10 +5174,10 @@ status_t MPEG4Source::parseTrackFragmentRun(off64_t offset, off64_t size) {
        }
        }


        ALOGV("adding sample %d at offset 0x%08" PRIx64 ", size %u, duration %u, "
        ALOGV("adding sample %d at offset 0x%08" PRIx64 ", size %u, duration %u, "
              " flags 0x%08x", i + 1,
              " flags 0x%08x ctsOffset %" PRIu32, i + 1,
                dataOffset, sampleSize, sampleDuration,
                dataOffset, sampleSize, sampleDuration,
                (flags & kFirstSampleFlagsPresent) && i == 0
                (flags & kFirstSampleFlagsPresent) && i == 0
                    ? firstSampleFlags : sampleFlags);
                    ? firstSampleFlags : sampleFlags, sampleCtsOffset);
        tmp.offset = dataOffset;
        tmp.offset = dataOffset;
        tmp.size = sampleSize;
        tmp.size = sampleSize;
        tmp.duration = sampleDuration;
        tmp.duration = sampleDuration;
@@ -5227,6 +5269,7 @@ media_status_t MPEG4Source::read(
    int64_t seekTimeUs;
    int64_t seekTimeUs;
    ReadOptions::SeekMode mode;
    ReadOptions::SeekMode mode;
    if (options && options->getSeekTo(&seekTimeUs, &mode)) {
    if (options && options->getSeekTo(&seekTimeUs, &mode)) {

        if (mIsHeif) {
        if (mIsHeif) {
            CHECK(mSampleTable == NULL);
            CHECK(mSampleTable == NULL);
            CHECK(mItemTable != NULL);
            CHECK(mItemTable != NULL);
@@ -5264,6 +5307,9 @@ media_status_t MPEG4Source::read(
                    CHECK(!"Should not be here.");
                    CHECK(!"Should not be here.");
                    break;
                    break;
            }
            }
            if( mode != ReadOptions::SEEK_FRAME_INDEX) {
                seekTimeUs += ((int64_t)mElstShiftStartTicks * 1000000) / mTimescale;
            }


            uint32_t sampleIndex;
            uint32_t sampleIndex;
            status_t err = mSampleTable->findSampleAtTime(
            status_t err = mSampleTable->findSampleAtTime(
@@ -5305,6 +5351,7 @@ media_status_t MPEG4Source::read(


            if (mode == ReadOptions::SEEK_CLOSEST
            if (mode == ReadOptions::SEEK_CLOSEST
                || mode == ReadOptions::SEEK_FRAME_INDEX) {
                || mode == ReadOptions::SEEK_FRAME_INDEX) {
                sampleTime -= mElstShiftStartTicks;
                targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
                targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale;
            }
            }


@@ -5343,6 +5390,10 @@ media_status_t MPEG4Source::read(
        if (!mIsHeif) {
        if (!mIsHeif) {
            err = mSampleTable->getMetaDataForSample(
            err = mSampleTable->getMetaDataForSample(
                    mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample, &stts);
                    mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample, &stts);
            if(err == OK) {
                cts -= mElstShiftStartTicks;
            }

        } else {
        } else {
            err = mItemTable->getImageOffsetAndSize(
            err = mItemTable->getImageOffsetAndSize(
                    options && options->getSeekTo(&seekTimeUs, &mode) ?
                    options && options->getSeekTo(&seekTimeUs, &mode) ?
@@ -5623,6 +5674,10 @@ media_status_t MPEG4Source::fragmentedRead(
    ReadOptions::SeekMode mode;
    ReadOptions::SeekMode mode;
    if (options && options->getSeekTo(&seekTimeUs, &mode)) {
    if (options && options->getSeekTo(&seekTimeUs, &mode)) {


        seekTimeUs += ((int64_t)mElstShiftStartTicks * 1000000) / mTimescale;
        ALOGV("shifted seekTimeUs :%" PRId64 ", mElstShiftStartTicks:%" PRId32, seekTimeUs,
                mElstShiftStartTicks);

        int numSidxEntries = mSegments.size();
        int numSidxEntries = mSegments.size();
        if (numSidxEntries != 0) {
        if (numSidxEntries != 0) {
            int64_t totalTime = 0;
            int64_t totalTime = 0;
@@ -5709,6 +5764,8 @@ media_status_t MPEG4Source::fragmentedRead(
        offset = smpl->offset;
        offset = smpl->offset;
        size = smpl->size;
        size = smpl->size;
        cts = mCurrentTime + smpl->compositionOffset;
        cts = mCurrentTime + smpl->compositionOffset;
        cts -= mElstShiftStartTicks;

        mCurrentTime += smpl->duration;
        mCurrentTime += smpl->duration;
        isSyncSample = (mCurrentSampleIndex == 0);
        isSyncSample = (mCurrentSampleIndex == 0);


+1 −0
Original line number Original line Diff line number Diff line
@@ -85,6 +85,7 @@ private:
        bool has_elst;
        bool has_elst;
        int64_t elst_media_time;
        int64_t elst_media_time;
        uint64_t elst_segment_duration;
        uint64_t elst_segment_duration;
        int32_t elstShiftStartTicks;
        bool subsample_encryption;
        bool subsample_encryption;
    };
    };


+2 −2
Original line number Original line Diff line number Diff line
@@ -4421,8 +4421,8 @@ status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) {
        h264type.nBFrames = mLatency == 0 ? 1 : std::min(1U, mLatency - 1);
        h264type.nBFrames = mLatency == 0 ? 1 : std::min(1U, mLatency - 1);


        // disable B-frames until MPEG4Writer can guarantee finalizing files with B-frames
        // disable B-frames until MPEG4Writer can guarantee finalizing files with B-frames
        h264type.nRefFrames = 1;
        // h264type.nRefFrames = 1;
        h264type.nBFrames = 0;
        // h264type.nBFrames = 0;


        h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
        h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
        h264type.nAllowedPictureTypes =
        h264type.nAllowedPictureTypes =
+103 −41
Original line number Original line Diff line number Diff line
@@ -137,6 +137,8 @@ public:


private:
private:
    enum {
    enum {
        // TODO: need to increase this considering the bug
        // about camera app not sending video frames continuously?
        kMaxCttsOffsetTimeUs = 1000000LL,  // 1 second
        kMaxCttsOffsetTimeUs = 1000000LL,  // 1 second
        kSampleArraySize = 1000,
        kSampleArraySize = 1000,
    };
    };
@@ -317,6 +319,7 @@ private:
    ListTableEntries<uint32_t, 1> *mStssTableEntries;
    ListTableEntries<uint32_t, 1> *mStssTableEntries;
    ListTableEntries<uint32_t, 2> *mSttsTableEntries;
    ListTableEntries<uint32_t, 2> *mSttsTableEntries;
    ListTableEntries<uint32_t, 2> *mCttsTableEntries;
    ListTableEntries<uint32_t, 2> *mCttsTableEntries;
    ListTableEntries<uint32_t, 3> *mElstTableEntries; // 3columns: segDuration, mediaTime, mediaRate


    int64_t mMinCttsOffsetTimeUs;
    int64_t mMinCttsOffsetTimeUs;
    int64_t mMinCttsOffsetTicks;
    int64_t mMinCttsOffsetTicks;
@@ -416,6 +419,8 @@ private:
    // Duration is time scale based
    // Duration is time scale based
    void addOneSttsTableEntry(size_t sampleCount, int32_t timescaledDur);
    void addOneSttsTableEntry(size_t sampleCount, int32_t timescaledDur);
    void addOneCttsTableEntry(size_t sampleCount, int32_t timescaledDur);
    void addOneCttsTableEntry(size_t sampleCount, int32_t timescaledDur);
    void addOneElstTableEntry(uint32_t segmentDuration, int32_t mediaTime,
        int16_t mediaRate, int16_t mediaRateFraction);


    bool isTrackMalFormed() const;
    bool isTrackMalFormed() const;
    void sendTrackSummary(bool hasMultipleTracks);
    void sendTrackSummary(bool hasMultipleTracks);
@@ -448,6 +453,7 @@ private:
    void writeVideoFourCCBox();
    void writeVideoFourCCBox();
    void writeMetadataFourCCBox();
    void writeMetadataFourCCBox();
    void writeStblBox(bool use32BitOffset);
    void writeStblBox(bool use32BitOffset);
    void writeEdtsBox();


    Track(const Track &);
    Track(const Track &);
    Track &operator=(const Track &);
    Track &operator=(const Track &);
@@ -483,6 +489,7 @@ void MPEG4Writer::initInternal(int fd, bool isFirstSession) {


    mStartTimestampUs = -1ll;
    mStartTimestampUs = -1ll;
    mStartTimeOffsetMs = -1;
    mStartTimeOffsetMs = -1;
    mStartTimeOffsetBFramesUs = 0;
    mPaused = false;
    mPaused = false;
    mStarted = false;
    mStarted = false;
    mWriterThreadStarted = false;
    mWriterThreadStarted = false;
@@ -1272,6 +1279,10 @@ void MPEG4Writer::writeMoovBox(int64_t durationUs) {
    // Adjust the global start time.
    // Adjust the global start time.
    mStartTimestampUs += minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs;
    mStartTimestampUs += minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs;


    // Add mStartTimeOffsetBFramesUs(-ve or zero) to the duration of first entry in STTS.
    mStartTimeOffsetBFramesUs = minCttsOffsetTimeUs - kMaxCttsOffsetTimeUs;
    ALOGV("mStartTimeOffsetBFramesUs :%" PRId32, mStartTimeOffsetBFramesUs);

    for (List<Track *>::iterator it = mTracks.begin();
    for (List<Track *>::iterator it = mTracks.begin();
        it != mTracks.end(); ++it) {
        it != mTracks.end(); ++it) {
        if (!(*it)->isHeic()) {
        if (!(*it)->isHeic()) {
@@ -1747,6 +1758,11 @@ int64_t MPEG4Writer::getStartTimestampUs() {
    return mStartTimestampUs;
    return mStartTimestampUs;
}
}


int32_t MPEG4Writer::getStartTimeOffsetBFramesUs() {
    Mutex::Autolock autoLock(mLock);
    return mStartTimeOffsetBFramesUs;
}

size_t MPEG4Writer::numTracks() {
size_t MPEG4Writer::numTracks() {
    Mutex::Autolock autolock(mLock);
    Mutex::Autolock autolock(mLock);
    return mTracks.size();
    return mTracks.size();
@@ -1776,6 +1792,7 @@ MPEG4Writer::Track::Track(
      mStssTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
      mStssTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
      mSttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
      mSttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
      mCttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
      mCttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
      mElstTableEntries(new ListTableEntries<uint32_t, 3>(3)), // Reserve 3 rows, a row has 3 items
      mMinCttsOffsetTimeUs(0),
      mMinCttsOffsetTimeUs(0),
      mMinCttsOffsetTicks(0),
      mMinCttsOffsetTicks(0),
      mMaxCttsOffsetTicks(0),
      mMaxCttsOffsetTicks(0),
@@ -1855,7 +1872,6 @@ void MPEG4Writer::Track::resetInternal() {
        delete mStszTableEntries;
        delete mStszTableEntries;
        mStszTableEntries = new ListTableEntries<uint32_t, 1>(1000);
        mStszTableEntries = new ListTableEntries<uint32_t, 1>(1000);
    }
    }

    if (mStcoTableEntries != NULL) {
    if (mStcoTableEntries != NULL) {
        delete mStcoTableEntries;
        delete mStcoTableEntries;
        mStcoTableEntries = new ListTableEntries<uint32_t, 1>(1000);
        mStcoTableEntries = new ListTableEntries<uint32_t, 1>(1000);
@@ -1864,7 +1880,6 @@ void MPEG4Writer::Track::resetInternal() {
        delete mCo64TableEntries;
        delete mCo64TableEntries;
        mCo64TableEntries = new ListTableEntries<off64_t, 1>(1000);
        mCo64TableEntries = new ListTableEntries<off64_t, 1>(1000);
    }
    }

    if (mStscTableEntries != NULL) {
    if (mStscTableEntries != NULL) {
        delete mStscTableEntries;
        delete mStscTableEntries;
        mStscTableEntries = new ListTableEntries<uint32_t, 3>(1000);
        mStscTableEntries = new ListTableEntries<uint32_t, 3>(1000);
@@ -1881,6 +1896,10 @@ void MPEG4Writer::Track::resetInternal() {
        delete mCttsTableEntries;
        delete mCttsTableEntries;
        mCttsTableEntries = new ListTableEntries<uint32_t, 2>(1000);
        mCttsTableEntries = new ListTableEntries<uint32_t, 2>(1000);
    }
    }
    if (mElstTableEntries != NULL) {
        delete mElstTableEntries;
        mElstTableEntries = new ListTableEntries<uint32_t, 3>(3);
    }
    mReachedEOS = false;
    mReachedEOS = false;
}
}


@@ -1900,6 +1919,7 @@ void MPEG4Writer::Track::updateTrackSizeEstimate() {
                                    mStssTableEntries->count() * 4 +   // stss box size
                                    mStssTableEntries->count() * 4 +   // stss box size
                                    mSttsTableEntries->count() * 8 +   // stts box size
                                    mSttsTableEntries->count() * 8 +   // stts box size
                                    mCttsTableEntries->count() * 8 +   // ctts box size
                                    mCttsTableEntries->count() * 8 +   // ctts box size
                                    mElstTableEntries->count() * 12 +   // elst box size
                                    stcoBoxSizeBytes +           // stco box size
                                    stcoBoxSizeBytes +           // stco box size
                                    stszBoxSizeBytes;            // stsz box size
                                    stszBoxSizeBytes;            // stsz box size
    }
    }
@@ -1936,6 +1956,16 @@ void MPEG4Writer::Track::addOneCttsTableEntry(
    mCttsTableEntries->add(htonl(duration));
    mCttsTableEntries->add(htonl(duration));
}
}


void MPEG4Writer::Track::addOneElstTableEntry(
    uint32_t segmentDuration, int32_t mediaTime, int16_t mediaRate, int16_t mediaRateFraction) {
    ALOGV("segmentDuration:%u, mediaTime:%d", segmentDuration, mediaTime);
    ALOGV("mediaRate :%" PRId16 ", mediaRateFraction :%" PRId16 ", Ored %u", mediaRate,
        mediaRateFraction, ((((uint32_t)mediaRate) << 16) | ((uint32_t)mediaRateFraction)));
    mElstTableEntries->add(htonl(segmentDuration));
    mElstTableEntries->add(htonl(mediaTime));
    mElstTableEntries->add(htonl((((uint32_t)mediaRate) << 16) | (uint32_t)mediaRateFraction));
}

status_t MPEG4Writer::setNextFd(int fd) {
status_t MPEG4Writer::setNextFd(int fd) {
    ALOGV("addNextFd");
    ALOGV("addNextFd");
    Mutex::Autolock l(mLock);
    Mutex::Autolock l(mLock);
@@ -2173,6 +2203,7 @@ MPEG4Writer::Track::~Track() {
    delete mSttsTableEntries;
    delete mSttsTableEntries;
    delete mStssTableEntries;
    delete mStssTableEntries;
    delete mCttsTableEntries;
    delete mCttsTableEntries;
    delete mElstTableEntries;


    mStszTableEntries = NULL;
    mStszTableEntries = NULL;
    mStcoTableEntries = NULL;
    mStcoTableEntries = NULL;
@@ -2181,6 +2212,7 @@ MPEG4Writer::Track::~Track() {
    mSttsTableEntries = NULL;
    mSttsTableEntries = NULL;
    mStssTableEntries = NULL;
    mStssTableEntries = NULL;
    mCttsTableEntries = NULL;
    mCttsTableEntries = NULL;
    mElstTableEntries = NULL;


    if (mCodecSpecificData != NULL) {
    if (mCodecSpecificData != NULL) {
        free(mCodecSpecificData);
        free(mCodecSpecificData);
@@ -3612,6 +3644,7 @@ void MPEG4Writer::Track::writeTrackHeader(bool use32BitOffset) {
    uint32_t now = getMpeg4Time();
    uint32_t now = getMpeg4Time();
    mOwner->beginBox("trak");
    mOwner->beginBox("trak");
        writeTkhdBox(now);
        writeTkhdBox(now);
        writeEdtsBox();
        mOwner->beginBox("mdia");
        mOwner->beginBox("mdia");
            writeMdhdBox(now);
            writeMdhdBox(now);
            writeHdlrBox();
            writeHdlrBox();
@@ -3982,6 +4015,33 @@ void MPEG4Writer::Track::writeHdlrBox() {
    mOwner->endBox();
    mOwner->endBox();
}
}


void MPEG4Writer::Track::writeEdtsBox(){
    ALOGV("%s : getStartTimeOffsetTimeUs of track:%" PRId64 " us", getTrackType(),
        getStartTimeOffsetTimeUs());

    // Prepone video playback.
    if (mMinCttsOffsetTicks != mMaxCttsOffsetTicks) {
        int32_t mvhdTimeScale = mOwner->getTimeScale();
        uint32_t tkhdDuration = (mTrackDurationUs * mvhdTimeScale + 5E5) / 1E6;
        int64_t mediaTime = ((kMaxCttsOffsetTimeUs - getMinCttsOffsetTimeUs())
            * mTimeScale + 5E5) / 1E6;
        if (tkhdDuration > 0 && mediaTime > 0) {
            addOneElstTableEntry(tkhdDuration, mediaTime, 1, 0);
        }
    }

    if (mElstTableEntries->count() == 0) {
        return;
    }

    mOwner->beginBox("edts");
        mOwner->beginBox("elst");
            mOwner->writeInt32(0); // version=0, flags=0
            mElstTableEntries->write(mOwner);
        mOwner->endBox(); // elst;
    mOwner->endBox(); // edts
}

void MPEG4Writer::Track::writeMdhdBox(uint32_t now) {
void MPEG4Writer::Track::writeMdhdBox(uint32_t now) {
    int64_t trakDurationUs = getDurationUs();
    int64_t trakDurationUs = getDurationUs();
    int64_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
    int64_t mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6;
@@ -4118,7 +4178,9 @@ void MPEG4Writer::Track::writeSttsBox() {
        uint32_t duration;
        uint32_t duration;
        CHECK(mSttsTableEntries->get(duration, 1));
        CHECK(mSttsTableEntries->get(duration, 1));
        duration = htonl(duration);  // Back to host byte order
        duration = htonl(duration);  // Back to host byte order
        mSttsTableEntries->set(htonl(duration + getStartTimeOffsetScaledTime()), 1);
        int32_t startTimeOffsetScaled = (((getStartTimeOffsetTimeUs() +
            mOwner->getStartTimeOffsetBFramesUs()) * mTimeScale) + 500000LL) / 1000000LL;
        mSttsTableEntries->set(htonl((int32_t)duration + startTimeOffsetScaled), 1);
    }
    }
    mSttsTableEntries->write(mOwner);
    mSttsTableEntries->write(mOwner);
    mOwner->endBox();  // stts
    mOwner->endBox();  // stts
+2 −0
Original line number Original line Diff line number Diff line
@@ -110,6 +110,7 @@ private:
    uint32_t mInterleaveDurationUs;
    uint32_t mInterleaveDurationUs;
    int32_t mTimeScale;
    int32_t mTimeScale;
    int64_t mStartTimestampUs;
    int64_t mStartTimestampUs;
    int32_t mStartTimeOffsetBFramesUs; // Start time offset when B Frames are present
    int mLatitudex10000;
    int mLatitudex10000;
    int mLongitudex10000;
    int mLongitudex10000;
    bool mAreGeoTagsAvailable;
    bool mAreGeoTagsAvailable;
@@ -129,6 +130,7 @@ private:


    void setStartTimestampUs(int64_t timeUs);
    void setStartTimestampUs(int64_t timeUs);
    int64_t getStartTimestampUs();  // Not const
    int64_t getStartTimestampUs();  // Not const
    int32_t getStartTimeOffsetBFramesUs();
    status_t startTracks(MetaData *params);
    status_t startTracks(MetaData *params);
    size_t numTracks();
    size_t numTracks();
    int64_t estimateMoovBoxSize(int32_t bitRate);
    int64_t estimateMoovBoxSize(int32_t bitRate);