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

Commit 46e63b34 authored by James Dong's avatar James Dong Committed by Android (Google) Code Review
Browse files

Merge "Better file size estimate" into gingerbread

parents 305bcbf0 cb7e65c6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -144,6 +144,7 @@ private:

    inline size_t write(const void *ptr, size_t size, size_t nmemb, FILE* stream);
    bool exceedsFileSizeLimit();
    bool use32BitFileOffset() const;
    bool exceedsFileDurationLimit();
    void trackProgressStatus(const Track* track, int64_t timeUs, status_t err = OK);

+103 −34
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@

namespace android {

static const int64_t kMax32BitFileSize = 0x007fffffffLL;
static const uint8_t kNalUnitTypeSeqParamSet = 0x07;
static const uint8_t kNalUnitTypePicParamSet = 0x08;

@@ -59,7 +60,7 @@ public:
    bool isAvc() const { return mIsAvc; }
    bool isAudio() const { return mIsAudio; }
    bool isMPEG4() const { return mIsMPEG4; }
    void addChunkOffset(off_t offset) { mChunkOffsets.push_back(offset); }
    void addChunkOffset(off_t offset);
    status_t dump(int fd, const Vector<String16>& args) const;

private:
@@ -79,7 +80,7 @@ private:
    bool mIsRealTimeRecording;
    int64_t mMaxTimeStampUs;
    int64_t mEstimatedTrackSizeBytes;
    int64_t mMaxWriteTimeUs;
    int64_t mMdatSizeBytes;
    int32_t mTimeScale;

    pthread_t mThread;
@@ -92,8 +93,11 @@ private:
    bool                mSamplesHaveSameSize;

    List<MediaBuffer *> mChunkSamples;

    size_t              mNumStcoTableEntries;
    List<off_t>         mChunkOffsets;

    size_t              mNumStscTableEntries;
    struct StscTableEntry {

        StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id)
@@ -107,9 +111,11 @@ private:
    };
    List<StscTableEntry> mStscTableEntries;

    size_t        mNumStssTableEntries;
    List<int32_t> mStssTableEntries;
    List<int64_t> mChunkDurations;

    size_t        mNumSttsTableEntries;
    struct SttsTableEntry {

        SttsTableEntry(uint32_t count, uint32_t durationUs)
@@ -178,6 +184,11 @@ private:
    // Simple validation on the codec specific data
    status_t checkCodecSpecificData() const;

    void updateTrackSizeEstimate();
    void addOneStscTableEntry(size_t chunkId, size_t sampleId);
    void addOneStssTableEntry(size_t sampleId);
    void addOneSttsTableEntry(size_t sampleCount, int64_t durationUs);

    Track(const Track &);
    Track &operator=(const Track &);
};
@@ -211,9 +222,11 @@ MPEG4Writer::MPEG4Writer(int fd)
MPEG4Writer::~MPEG4Writer() {
    stop();

    for (List<Track *>::iterator it = mTracks.begin();
         it != mTracks.end(); ++it) {
    while (!mTracks.empty()) {
        List<Track *>::iterator it = mTracks.begin();
        delete *it;
        (*it) = NULL;
        mTracks.erase(it);
    }
    mTracks.clear();
}
@@ -332,6 +345,21 @@ status_t MPEG4Writer::start(MetaData *param) {
        mUse32BitOffset = false;
    }

    if (mUse32BitOffset) {
        // Implicit 32 bit file size limit
        if (mMaxFileSizeLimitBytes == 0) {
            mMaxFileSizeLimitBytes = kMax32BitFileSize;
        }

        // If file size is set to be larger than the 32 bit file
        // size limit, treat it as an error.
        if (mMaxFileSizeLimitBytes > kMax32BitFileSize) {
            LOGE("32-bit file size limit too big: %lld bytes",
                mMaxFileSizeLimitBytes);
            return UNKNOWN_ERROR;
        }
    }

    // System property can overwrite the file offset bits parameter
    char value[PROPERTY_VALUE_MAX];
    if (property_get("media.stagefright.record-64bits", value, NULL)
@@ -413,6 +441,10 @@ status_t MPEG4Writer::start(MetaData *param) {
    return OK;
}

bool MPEG4Writer::use32BitFileOffset() const {
    return mUse32BitOffset;
}

status_t MPEG4Writer::pause() {
    if (mFile == NULL) {
        return OK;
@@ -739,7 +771,8 @@ bool MPEG4Writer::exceedsFileSizeLimit() {
         it != mTracks.end(); ++it) {
        nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes();
    }
    return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes);

    return (nTotalBytesEstimate  + 1024 >= mMaxFileSizeLimitBytes);
}

bool MPEG4Writer::exceedsFileDurationLimit() {
@@ -819,6 +852,48 @@ MPEG4Writer::Track::Track(
    setTimeScale();
}

void MPEG4Writer::Track::updateTrackSizeEstimate() {

    int64_t stcoBoxSizeBytes = mOwner->use32BitFileOffset()
                                ? mNumStcoTableEntries * 4
                                : mNumStcoTableEntries * 8;

    int64_t stszBoxSizeBytes = mSamplesHaveSameSize? 4: (mNumSamples * 4);

    mEstimatedTrackSizeBytes = mMdatSizeBytes +             // media data size
                               mNumStscTableEntries * 12 +  // stsc box size
                               mNumStssTableEntries * 4 +   // stss box size
                               mNumSttsTableEntries * 8 +   // stts box size
                               stcoBoxSizeBytes +           // stco box size
                               stszBoxSizeBytes;            // stsz box size
}

void MPEG4Writer::Track::addOneStscTableEntry(
        size_t chunkId, size_t sampleId) {

        StscTableEntry stscEntry(chunkId, sampleId, 1);
        mStscTableEntries.push_back(stscEntry);
        ++mNumStscTableEntries;
}

void MPEG4Writer::Track::addOneStssTableEntry(size_t sampleId) {
    mStssTableEntries.push_back(sampleId);
    ++mNumStssTableEntries;
}

void MPEG4Writer::Track::addOneSttsTableEntry(
        size_t sampleCount, int64_t durationUs) {

    SttsTableEntry sttsEntry(sampleCount, durationUs);
    mSttsTableEntries.push_back(sttsEntry);
    ++mNumSttsTableEntries;
}

void MPEG4Writer::Track::addChunkOffset(off_t offset) {
    ++mNumStcoTableEntries;
    mChunkOffsets.push_back(offset);
}

void MPEG4Writer::Track::setTimeScale() {
    LOGV("setTimeScale");
    // Default time scale
@@ -1039,6 +1114,7 @@ status_t MPEG4Writer::startWriterThread() {
    return OK;
}


status_t MPEG4Writer::Track::start(MetaData *params) {
    if (!mDone && mPaused) {
        mPaused = false;
@@ -1077,6 +1153,11 @@ status_t MPEG4Writer::Track::start(MetaData *params) {
    mTrackDurationUs = 0;
    mReachedEOS = false;
    mEstimatedTrackSizeBytes = 0;
    mNumStcoTableEntries = 0;
    mNumStssTableEntries = 0;
    mNumStscTableEntries = 0;
    mNumSttsTableEntries = 0;
    mMdatSizeBytes = 0;

    pthread_create(&mThread, &attr, ThreadWrapper, this);
    pthread_attr_destroy(&attr);
@@ -1434,7 +1515,6 @@ status_t MPEG4Writer::Track::threadEntry() {
    bool collectStats = collectStatisticalData();

    mNumSamples = 0;
    mMaxWriteTimeUs = 0;
    status_t err = OK;
    MediaBuffer *buffer;
    while (!mDone && (err = mSource->read(&buffer)) == OK) {
@@ -1505,7 +1585,9 @@ status_t MPEG4Writer::Track::threadEntry() {
                : copy->range_length();

        // Max file size or duration handling
        mEstimatedTrackSizeBytes += sampleSize;
        mMdatSizeBytes += sampleSize;
        updateTrackSizeEstimate();

        if (mOwner->exceedsFileSizeLimit()) {
            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
            break;
@@ -1587,8 +1669,7 @@ status_t MPEG4Writer::Track::threadEntry() {
                     (lastTimestampUs * mTimeScale + 500000LL) / 1000000LL);

            if (currDurationTicks != lastDurationTicks) {
                SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
                mSttsTableEntries.push_back(sttsEntry);
                addOneSttsTableEntry(sampleCount, lastDurationUs);
                sampleCount = 1;
            } else {
                ++sampleCount;
@@ -1611,7 +1692,7 @@ status_t MPEG4Writer::Track::threadEntry() {
        }

        if (isSync != 0) {
            mStssTableEntries.push_back(mNumSamples);
            addOneStssTableEntry(mNumSamples);
        }

        if (mTrackingProgressStatus) {
@@ -1624,7 +1705,7 @@ status_t MPEG4Writer::Track::threadEntry() {
            off_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
                                 : mOwner->addSample_l(copy);
            if (mChunkOffsets.empty()) {
                mChunkOffsets.push_back(offset);
                addChunkOffset(offset);
            }
            copy->release();
            copy = NULL;
@@ -1633,8 +1714,7 @@ status_t MPEG4Writer::Track::threadEntry() {

        mChunkSamples.push_back(copy);
        if (interleaveDurationUs == 0) {
            StscTableEntry stscEntry(++nChunks, 1, 1);
            mStscTableEntries.push_back(stscEntry);
            addOneStscTableEntry(++nChunks, 1);
            bufferChunk(timestampUs);
        } else {
            if (chunkTimestampUs == 0) {
@@ -1648,9 +1728,7 @@ status_t MPEG4Writer::Track::threadEntry() {
                    if (nChunks == 1 ||  // First chunk
                        (--(mStscTableEntries.end()))->samplesPerChunk !=
                         mChunkSamples.size()) {
                        StscTableEntry stscEntry(nChunks,
                                mChunkSamples.size(), 1);
                        mStscTableEntries.push_back(stscEntry);
                        addOneStscTableEntry(nChunks, mChunkSamples.size());
                    }
                    bufferChunk(timestampUs);
                    chunkTimestampUs = timestampUs;
@@ -1669,12 +1747,9 @@ status_t MPEG4Writer::Track::threadEntry() {

    // Last chunk
    if (mOwner->numTracks() == 1) {
        StscTableEntry stscEntry(1, mNumSamples, 1);
        mStscTableEntries.push_back(stscEntry);
        addOneStscTableEntry(1, mNumSamples);
    } else if (!mChunkSamples.empty()) {
        ++nChunks;
        StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1);
        mStscTableEntries.push_back(stscEntry);
        addOneStscTableEntry(++nChunks, mChunkSamples.size());
        bufferChunk(timestampUs);
    }

@@ -1686,12 +1761,11 @@ status_t MPEG4Writer::Track::threadEntry() {
    } else {
        ++sampleCount;  // Count for the last sample
    }
    SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
    mSttsTableEntries.push_back(sttsEntry);
    addOneSttsTableEntry(sampleCount, lastDurationUs);
    mTrackDurationUs += lastDurationUs;
    mReachedEOS = true;
    LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. Max write time: %lld us - %s",
            count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
    LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. - %s",
            count, nZeroLengthFrames, mNumSamples, mIsAudio? "audio": "video");

    logStatisticalData(mIsAudio);
    if (err == ERROR_END_OF_STREAM) {
@@ -1855,14 +1929,9 @@ int64_t MPEG4Writer::getDriftTimeUs() {
void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
    LOGV("bufferChunk");

    int64_t startTimeUs = systemTime() / 1000;
    Chunk chunk(this, timestampUs, mChunkSamples);
    mOwner->bufferChunk(chunk);
    mChunkSamples.clear();
    int64_t endTimeUs = systemTime() / 1000;
    if (mMaxWriteTimeUs < endTimeUs - startTimeUs) {
        mMaxWriteTimeUs = endTimeUs - startTimeUs;
    }
}

int64_t MPEG4Writer::Track::getDurationUs() const {
@@ -2215,7 +2284,7 @@ void MPEG4Writer::Track::writeTrackHeader(

          mOwner->beginBox("stts");
            mOwner->writeInt32(0);  // version=0, flags=0
            mOwner->writeInt32(mSttsTableEntries.size());
            mOwner->writeInt32(mNumSttsTableEntries);
            int64_t prevTimestampUs = 0;
            for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin();
                 it != mSttsTableEntries.end(); ++it) {
@@ -2235,7 +2304,7 @@ void MPEG4Writer::Track::writeTrackHeader(
          if (!mIsAudio) {
            mOwner->beginBox("stss");
              mOwner->writeInt32(0);  // version=0, flags=0
              mOwner->writeInt32(mStssTableEntries.size());  // number of sync frames
              mOwner->writeInt32(mNumStssTableEntries);  // number of sync frames
              for (List<int32_t>::iterator it = mStssTableEntries.begin();
                   it != mStssTableEntries.end(); ++it) {
                  mOwner->writeInt32(*it);
@@ -2262,7 +2331,7 @@ void MPEG4Writer::Track::writeTrackHeader(

          mOwner->beginBox("stsc");
            mOwner->writeInt32(0);  // version=0, flags=0
            mOwner->writeInt32(mStscTableEntries.size());
            mOwner->writeInt32(mNumStscTableEntries);
            for (List<StscTableEntry>::iterator it = mStscTableEntries.begin();
                 it != mStscTableEntries.end(); ++it) {
                mOwner->writeInt32(it->firstChunk);
@@ -2272,7 +2341,7 @@ void MPEG4Writer::Track::writeTrackHeader(
          mOwner->endBox();  // stsc
          mOwner->beginBox(use32BitOffset? "stco": "co64");
            mOwner->writeInt32(0);  // version=0, flags=0
            mOwner->writeInt32(mChunkOffsets.size());
            mOwner->writeInt32(mNumStcoTableEntries);
            for (List<off_t>::iterator it = mChunkOffsets.begin();
                 it != mChunkOffsets.end(); ++it) {
                if (use32BitOffset) {