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

Commit 116913ac authored by Gopalakrishnan Nallasamy's avatar Gopalakrishnan Nallasamy
Browse files

MPEG4Writer:Ignore sample table of malformed track

1. Don't write sample table box for malformed tracks.
   Return error to the client about malformed track.
   MOOV atom would be finalized with other well-formed tracks.
2. Throw relevant track id and recorder-error on error while making
   codec specific data from input buffers for AVC, HEIC, HEVC and MPEG4.
3. Capture and log errors returned by system calls.
4. Bubble up other relevant errors.
5. Good to release internal buffer and stop source before notifying client.
6. Don't need to update filesize for every preallocation.
   Use fallocate in FALLOC_FL_KEEP_SIZE mode.
7. Added some important logs
8. Added and corrected some comments.
9. Added TrackId subclass to validate track ids for MediaRecorder and
   MediaMuxer.

Bug: 153923798
Bug: 156657952

Test: atest android.media.cts.MediaMuxerTest
      atest android.media.cts.MediaRecorderTest
      atest android.mediav2.cts.MuxerTest

Change-Id: I87f9b8cc9cbd23789f7fb69490e8a6f81469907a
parent fd267eb2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -2011,6 +2011,7 @@ void StagefrightRecorder::setupMPEG4orWEBMMetaData(sp<MetaData> *meta) {
    }
    if (mOutputFormat == OUTPUT_FORMAT_MPEG_4 || mOutputFormat == OUTPUT_FORMAT_THREE_GPP) {
        (*meta)->setInt32(kKeyEmptyTrackMalFormed, true);
        (*meta)->setInt32(kKey4BitTrackIds, true);
    }
}

+1 −0
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ status_t HevcParameterSets::addNalUnit(const uint8_t* data, size_t size) {
    }

    if (err != OK) {
        ALOGE("error parsing VPS or SPS or PPS");
        return err;
    }

+146 −61
Original line number Diff line number Diff line
@@ -103,8 +103,40 @@ static const uint8_t kHevcNalUnitTypes[5] = {
//#define SHOW_MODEL_BUILD 1

class MPEG4Writer::Track {
    struct TrackId {
        TrackId(uint32_t aId)
            :mId(aId),
             mTrackIdValid(false) {
        }
        bool isValid(bool akKey4BitTrackIds) {
            // trackId cannot be zero, ISO/IEC 14496-12 8.3.2.3
            if (mId == 0) {
                return false;
            }
            /* MediaRecorder uses only 4 bit to represent track ids during notifying clients.
             * MediaMuxer's track ids are restricted by container allowed size only.
             * MPEG4 Container defines unsigned int (32), ISO/IEC 14496-12 8.3.2.2
             */
            if (akKey4BitTrackIds && mId > 15) {
                return false;
            }
            mTrackIdValid = true;
            return true;
        }
        uint32_t getId() const {
            CHECK(mTrackIdValid);
            return mId;
        }
        TrackId() = delete;
        DISALLOW_EVIL_CONSTRUCTORS(TrackId);
    private:
        // unsigned int (32), ISO/IEC 14496-12 8.3.2.2
        uint32_t mId;
        bool mTrackIdValid;
    };

public:
    Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
    Track(MPEG4Writer *owner, const sp<MediaSource> &source, uint32_t aTrackId);

    ~Track();

@@ -129,7 +161,7 @@ public:
    void addChunkOffset(off64_t offset);
    void addItemOffsetAndSize(off64_t offset, size_t size, bool isExif);
    void flushItemRefs();
    int32_t getTrackId() const { return mTrackId; }
    TrackId& getTrackId() { return mTrackId; }
    status_t dump(int fd, const Vector<String16>& args) const;
    static const char *getFourCCForMime(const char *mime);
    const char *getTrackType() const;
@@ -290,7 +322,7 @@ private:
    bool mIsMPEG4;
    bool mGotStartKeyFrame;
    bool mIsMalformed;
    int32_t mTrackId;
    TrackId mTrackId;
    int64_t mTrackDurationUs;
    int64_t mMaxChunkDurationUs;
    int64_t mLastDecodingTimeUs;
@@ -413,7 +445,7 @@ private:
    void addOneElstTableEntry(uint32_t segmentDuration, int32_t mediaTime,
        int16_t mediaRate, int16_t mediaRateFraction);

    bool isTrackMalFormed() const;
    bool isTrackMalFormed();
    void sendTrackSummary(bool hasMultipleTracks);

    // Write the boxes
@@ -534,7 +566,7 @@ void MPEG4Writer::initInternal(int fd, bool isFirstSession) {
        release();
    }

    if (fallocate(mFd, 0, 0, 1) == 0) {
    if (fallocate(mFd, FALLOC_FL_KEEP_SIZE, 0, 1) == 0) {
        ALOGD("PreAllocation enabled");
        mPreAllocationEnabled = true;
    } else {
@@ -744,8 +776,7 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
    // where 1MB is the common file size limit for MMS application.
    // The default MAX _MOOV_BOX_SIZE value is based on about 3
    // minute video recording with a bit rate about 3 Mbps, because
    // statistics also show that most of the video captured are going
    // to be less than 3 minutes.
    // statistics show that most captured videos are less than 3 minutes.

    // If the estimation is wrong, we will pay the price of wasting
    // some reserved space. This should not happen so often statistically.
@@ -796,6 +827,15 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
    return size;
}

status_t MPEG4Writer::validateAllTracksId(bool akKey4BitTrackIds) {
    for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end(); ++it) {
        if (!(*it)->getTrackId().isValid(akKey4BitTrackIds)) {
            return BAD_VALUE;
        }
    }
    return OK;
}

status_t MPEG4Writer::start(MetaData *param) {
    if (mInitCheck != OK) {
        return UNKNOWN_ERROR;
@@ -810,6 +850,9 @@ status_t MPEG4Writer::start(MetaData *param) {
        mIsFileSizeLimitExplicitlyRequested = true;
    }

    /* mMaxFileSizeLimitBytes has to be set everytime fd is switched, hence the following code is
     * appropriate in start() method.
     */
    int32_t fileSizeBits = fpathconf(mFd, _PC_FILESIZEBITS);
    ALOGD("fpathconf _PC_FILESIZEBITS:%" PRId32, fileSizeBits);
    fileSizeBits = std::min(fileSizeBits, 52 /* cap it below 4 peta bytes */);
@@ -944,6 +987,17 @@ status_t MPEG4Writer::start(MetaData *param) {

    setupAndStartLooper();

    int32_t is4bitTrackId = false;
    if (param && param->findInt32(kKey4BitTrackIds, &is4bitTrackId) && is4bitTrackId) {
        err = validateAllTracksId(true);
    }
    else {
        err = validateAllTracksId(false);
    }
    if (err != OK) {
        return err;
    }

    err = startTracks(param);
    if (err != OK) {
        return err;
@@ -961,6 +1015,7 @@ status_t MPEG4Writer::pause() {
status_t MPEG4Writer::stopWriterThread() {
    ALOGV("Stopping writer thread");
    if (!mWriterThreadStarted) {
        ALOGD("Writer thread not started");
        return OK;
    }
    {
@@ -975,7 +1030,8 @@ status_t MPEG4Writer::stopWriterThread() {

    err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
    mWriterThreadStarted = false;
    WARN_UNLESS(err == 0, "stopWriterThread pthread_join retVal: %d, writer thread stopped", err);
    WARN_UNLESS(err == 0, "stopWriterThread pthread_join retVal: %d", err);
    ALOGD("Writer thread stopped");
    return err;
}

@@ -1031,26 +1087,32 @@ void MPEG4Writer::writeCompositionMatrix(int degrees) {
    writeInt32(0x40000000);  // w
}

void MPEG4Writer::release() {
status_t MPEG4Writer::release() {
    ALOGD("release()");
    if (mPreAllocationEnabled) {
        truncatePreAllocation();
    }
    int err = OK;
    int retVal = fsync(mFd);
    WARN_UNLESS(retVal == 0, "fsync retVal:%d", retVal);
    WARN_UNLESS(retVal == 0, "fsync err:%s(%d)", std::strerror(errno), errno);
    err |= retVal;
    retVal = close(mFd);
    WARN_UNLESS(retVal == 0, "close mFd retVal :%d", retVal);
    WARN_UNLESS(retVal == 0, "close err:%s(%d)", std::strerror(errno), errno);
    err |= retVal;
    mFd = -1;
    if (mNextFd != -1) {
        retVal = close(mNextFd);
        mNextFd = -1;
        WARN_UNLESS(retVal == 0, "close mNextFd retVal :%d", retVal);
        WARN_UNLESS(retVal == 0, "close mNextFd error:%s(%d)",
                    std::strerror(errno), errno);
        err |= retVal;
    }
    stopAndReleaseLooper();
    mInitCheck = NO_INIT;
    mStarted = false;
    free(mInMemoryCache);
    mInMemoryCache = NULL;
    return err;
}

void MPEG4Writer::finishCurrentSession() {
@@ -1065,7 +1127,7 @@ status_t MPEG4Writer::switchFd() {
    }

    if (mNextFd == -1) {
        ALOGW("No FileDescripter for next recording");
        ALOGW("No FileDescriptor for next recording");
        return INVALID_OPERATION;
    }

@@ -1084,12 +1146,15 @@ status_t MPEG4Writer::reset(bool stopSource) {
    } else {
        if (!mWriterThreadStarted ||
            !mStarted) {
            status_t err = OK;
            status_t writerErr = OK;
            if (mWriterThreadStarted) {
                err = stopWriterThread();
                writerErr = stopWriterThread();
            }
            release();
            return err;
            status_t retErr = release();
            if (writerErr != OK) {
                retErr = writerErr;
            }
            return retErr;
        }
    }

@@ -1100,8 +1165,9 @@ status_t MPEG4Writer::reset(bool stopSource) {
    for (List<Track *>::iterator it = mTracks.begin();
        it != mTracks.end(); ++it) {
        status_t trackErr = (*it)->stop(stopSource);
        WARN_UNLESS(trackErr == 0, "%s track stopped with an error",
                    (*it)->getTrackType());
        if (err == OK && trackErr != OK) {
            ALOGW("%s track stopped with an error", (*it)->getTrackType());
            err = trackErr;
        }

@@ -1118,7 +1184,6 @@ status_t MPEG4Writer::reset(bool stopSource) {
        }
    }


    if (nonImageTrackCount > 1) {
        ALOGD("Duration from tracks range is [%" PRId64 ", %" PRId64 "] us",
            minDurationUs, maxDurationUs);
@@ -1126,13 +1191,15 @@ status_t MPEG4Writer::reset(bool stopSource) {

    status_t writerErr = stopWriterThread();

    // TODO: which error to propagage, writerErr or trackErr?
    // Propagating writer error
    if (err == OK && writerErr != OK) {
        err = writerErr;
    }

    // Do not write out movie header on error except malformed track.
    // TODO: Remove samples of malformed tracks added in mdat.
    if (err != OK && err != ERROR_MALFORMED) {
        // Ignoring release() return value as there was an "err" already.
        release();
        return err;
    }
@@ -1174,6 +1241,7 @@ status_t MPEG4Writer::reset(bool stopSource) {
        } else {
            ALOGI("The mp4 file will not be streamable.");
        }
        ALOGI("MOOV atom was written to the file");
    }
    mWriteBoxToMemory = false;

@@ -1186,7 +1254,7 @@ status_t MPEG4Writer::reset(bool stopSource) {

    CHECK(mBoxes.empty());

    release();
    err = release();
    return err;
}

@@ -1354,7 +1422,7 @@ void MPEG4Writer::sendSessionSummary() {

    for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
         it != mChunkInfos.end(); ++it) {
        int trackNum = it->mTrack->getTrackId() << 28;
        uint32_t trackNum = (it->mTrack->getTrackId().getId() << 28);
        notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
                trackNum | MEDIA_RECORDER_TRACK_INTER_CHUNK_TIME_MS,
                it->mMaxInterChunkDurUs);
@@ -1512,7 +1580,8 @@ void MPEG4Writer::writeOrPostError(int fd, const void* buf, size_t count) {
          std::strerror(errno), errno);

    // Can't guarantee that file is usable or write would succeed anymore, hence signal to stop.
    sp<AMessage> msg = new AMessage(kWhatHandleIOError, mReflector);
    sp<AMessage> msg = new AMessage(kWhatIOError, mReflector);
    msg->setInt32("errno", errno);
    status_t err = msg->post();
    ALOGE("writeOrPostError post:%d", err);
}
@@ -1531,7 +1600,8 @@ void MPEG4Writer::seekOrPostError(int fd, off64_t offset, int whence) {
          offset, std::strerror(errno), errno);

    // Can't guarantee that file is usable or seek would succeed anymore, hence signal to stop.
    sp<AMessage> msg = new AMessage(kWhatHandleIOError, mReflector);
    sp<AMessage> msg = new AMessage(kWhatIOError, mReflector);
    msg->setInt32("errno", errno);
    status_t err = msg->post();
    ALOGE("seekOrPostError post:%d", err);
}
@@ -1768,10 +1838,11 @@ bool MPEG4Writer::preAllocate(uint64_t wantSize) {
    ALOGV("preAllocateSize :%" PRIu64 " lastFileEndOffset:%" PRIu64, preAllocateSize,
          lastFileEndOffset);

    int res = fallocate(mFd, 0, lastFileEndOffset, preAllocateSize);
    int res = fallocate(mFd, FALLOC_FL_KEEP_SIZE, lastFileEndOffset, preAllocateSize);
    if (res == -1) {
        ALOGE("fallocate err:%s, %d, fd:%d", strerror(errno), errno, mFd);
        sp<AMessage> msg = new AMessage(kWhatHandleFallocateError, mReflector);
        sp<AMessage> msg = new AMessage(kWhatFallocateError, mReflector);
        msg->setInt32("errno", errno);
        status_t err = msg->post();
        mFallocateErr = true;
        ALOGD("preAllocation post:%d", err);
@@ -1899,7 +1970,7 @@ size_t MPEG4Writer::numTracks() {
////////////////////////////////////////////////////////////////////////////////

MPEG4Writer::Track::Track(
        MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId)
        MPEG4Writer *owner, const sp<MediaSource> &source, uint32_t aTrackId)
    : mOwner(owner),
      mMeta(source->getFormat()),
      mSource(source),
@@ -1909,7 +1980,7 @@ MPEG4Writer::Track::Track(
      mStarted(false),
      mGotStartKeyFrame(false),
      mIsMalformed(false),
      mTrackId(trackId),
      mTrackId(aTrackId),
      mTrackDurationUs(0),
      mEstimatedTrackSizeBytes(0),
      mSamplesHaveSameSize(true),
@@ -2089,7 +2160,7 @@ void MPEG4Writer::Track::addOneElstTableEntry(
void MPEG4Writer::setupAndStartLooper() {
    if (mLooper == nullptr) {
        mLooper = new ALooper;
        mLooper->setName("MP4WriterLooper");
        mLooper->setName("MP4WtrCtrlHlpLooper");
        mLooper->start();
        mReflector = new AHandlerReflector<MPEG4Writer>(this);
        mLooper->registerHandler(mReflector);
@@ -2099,12 +2170,12 @@ void MPEG4Writer::setupAndStartLooper() {
void MPEG4Writer::stopAndReleaseLooper() {
    if (mLooper != nullptr) {
        if (mReflector != nullptr) {
            ALOGD("unregisterHandler");
            mLooper->unregisterHandler(mReflector->id());
            mReflector.clear();
        }
        mLooper->stop();
        mLooper.clear();
        ALOGD("MP4WtrCtrlHlpLooper stopped");
    }
}

@@ -2329,18 +2400,22 @@ void MPEG4Writer::onMessageReceived(const sp<AMessage> &msg) {
            break;
        }
        // ::write() or lseek64() wasn't a success, file could be malformed
        case kWhatHandleIOError: {
            ALOGE("kWhatHandleIOError");
        case kWhatIOError: {
            ALOGE("kWhatIOError");
            int32_t err;
            CHECK(msg->findInt32("errno", &err));
            // Stop tracks' threads and main writer thread.
            notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, ERROR_MALFORMED);
            notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
            stop();
            break;
        }
        // fallocate() failed, hence notify app about it and stop().
        case kWhatHandleFallocateError: {
            ALOGE("kWhatHandleFallocateError");
            //TODO: introduce new MEDIA_RECORDER_INFO_STOPPED instead MEDIA_RECORDER_INFO_UNKNOWN?
            notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_UNKNOWN, ERROR_IO);
        case kWhatFallocateError: {
            ALOGE("kWhatFallocateError");
            int32_t err;
            CHECK(msg->findInt32("errno", &err));
            //TODO: introduce a suitable MEDIA_RECORDER_ERROR_* instead MEDIA_RECORDER_ERROR_UNKNOWN?
            notify(MEDIA_RECORDER_EVENT_ERROR, MEDIA_RECORDER_ERROR_UNKNOWN, err);
            stop();
            break;
        }
@@ -2708,9 +2783,9 @@ status_t MPEG4Writer::Track::stop(bool stopSource) {
    void *dummy;
    status_t err = pthread_join(mThread, &dummy);
    WARN_UNLESS(err == 0, "track::stop: pthread_join status:%d", err);
    err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
    WARN_UNLESS(err == 0, "%s track stopped. Status :%d. %s source", getTrackType(), err,
                stopSource ? "Stop" : "Not Stop");
    status_t threadRetVal = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
    WARN_UNLESS(threadRetVal == 0, "%s track stopped. Status :%d. %s source",
                getTrackType(), err, stopSource ? "Stop" : "Not Stop");
    mStarted = false;
    return err;
}
@@ -2849,6 +2924,7 @@ status_t MPEG4Writer::Track::parseAVCCodecSpecificData(
        }

        if (nextStartCode == NULL) {
            ALOGE("nextStartCode is null");
            return ERROR_MALFORMED;
        }

@@ -3126,11 +3202,11 @@ status_t MPEG4Writer::Track::threadEntry() {
    int64_t lastSampleDurationTicks = -1;   // Timescale based ticks

    if (mIsAudio) {
        prctl(PR_SET_NAME, (unsigned long)"AudioTrackWriterThread", 0, 0, 0);
        prctl(PR_SET_NAME, (unsigned long)"MP4WtrAudTrkThread", 0, 0, 0);
    } else if (mIsVideo) {
        prctl(PR_SET_NAME, (unsigned long)"VideoTrackWriterThread", 0, 0, 0);
        prctl(PR_SET_NAME, (unsigned long)"MP4WtrVidTrkThread", 0, 0, 0);
    } else {
        prctl(PR_SET_NAME, (unsigned long)"MetadataTrackWriterThread", 0, 0, 0);
        prctl(PR_SET_NAME, (unsigned long)"MP4WtrMetaTrkThread", 0, 0, 0);
    }

    if (mOwner->isRealTimeRecording()) {
@@ -3181,6 +3257,7 @@ status_t MPEG4Writer::Track::threadEntry() {
        }

        ++count;

        int32_t isCodecConfig;
        if (buffer->meta_data().findInt32(kKeyIsCodecConfig, &isCodecConfig)
                && isCodecConfig) {
@@ -3204,7 +3281,7 @@ status_t MPEG4Writer::Track::threadEntry() {
                                + buffer->range_offset(),
                            buffer->range_length());
                } else if (mIsMPEG4) {
                    copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(),
                    err = copyCodecSpecificData((const uint8_t *)buffer->data() + buffer->range_offset(),
                            buffer->range_length());
                }
            }
@@ -3213,8 +3290,10 @@ status_t MPEG4Writer::Track::threadEntry() {
            buffer = NULL;
            if (OK != err) {
                mSource->stop();
                mIsMalformed = true;
                uint32_t trackNum = (mTrackId.getId() << 28);
                mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_ERROR,
                       mTrackId | MEDIA_RECORDER_TRACK_ERROR_GENERAL, err);
                       trackNum | MEDIA_RECORDER_TRACK_ERROR_GENERAL, err);
                break;
            }

@@ -3251,7 +3330,7 @@ status_t MPEG4Writer::Track::threadEntry() {
         * Reserve space in the file for the current sample + to be written MOOV box. If reservation
         * for a new sample fails, preAllocate(...) stops muxing session completely. Stop() could
         * write MOOV box successfully as space for the same was reserved in the prior call.
         * Release the current buffer/sample only here.
         * Release the current buffer/sample here.
         */
        if (!mOwner->preAllocate(buffer->range_length())) {
            buffer->release();
@@ -3291,6 +3370,7 @@ status_t MPEG4Writer::Track::threadEntry() {
        updateTrackSizeEstimate();

        if (mOwner->exceedsFileSizeLimit()) {
            copy->release();
            if (mOwner->switchFd() != OK) {
                ALOGW("Recorded file size exceeds limit %" PRId64 "bytes",
                        mOwner->mMaxFileSizeLimitBytes);
@@ -3301,16 +3381,15 @@ status_t MPEG4Writer::Track::threadEntry() {
                ALOGV("%s Current recorded file size exceeds limit %" PRId64 "bytes. Switching output",
                        getTrackType(), mOwner->mMaxFileSizeLimitBytes);
            }
            copy->release();
            break;
        }

        if (mOwner->exceedsFileDurationLimit()) {
            ALOGW("Recorded file duration exceeds limit %" PRId64 "microseconds",
                    mOwner->mMaxFileDurationLimitUs);
            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
            copy->release();
            mSource->stop();
            mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
            break;
        }

@@ -3592,13 +3671,13 @@ status_t MPEG4Writer::Track::threadEntry() {
            }
        }
    }

    if (isTrackMalFormed()) {
        mIsMalformed = true;
        dumpTimeStamps();
        err = ERROR_MALFORMED;
    }

    mOwner->trackProgressStatus(mTrackId, -1, err);
    mOwner->trackProgressStatus(mTrackId.getId(), -1, err);

    // Add final entries only for non-empty tracks.
    if (mStszTableEntries->count() > 0) {
@@ -3665,7 +3744,7 @@ status_t MPEG4Writer::Track::threadEntry() {
    return err;
}

bool MPEG4Writer::Track::isTrackMalFormed() const {
bool MPEG4Writer::Track::isTrackMalFormed() {
    if (mIsMalformed) {
        return true;
    }
@@ -3674,23 +3753,29 @@ bool MPEG4Writer::Track::isTrackMalFormed() const {
    if (mOwner->mStartMeta &&
        mOwner->mStartMeta->findInt32(kKeyEmptyTrackMalFormed, &emptyTrackMalformed) &&
        emptyTrackMalformed) {
        // MediaRecorder(sets kKeyEmptyTrackMalFormed by default) report empty tracks as malformed.
        if (!mIsHeic && mStszTableEntries->count() == 0) {  // no samples written
            ALOGE("The number of recorded samples is 0");
            mIsMalformed = true;
            return true;
        }
        if (mIsVideo && mStssTableEntries->count() == 0) {  // no sync frames for video
            ALOGE("There are no sync frames for video track");
            mIsMalformed = true;
            return true;
        }
    } else {
        // No sync frames for video.
        // Through MediaMuxer, empty tracks can be added. No sync frames for video.
        if (mIsVideo && mStszTableEntries->count() > 0 && mStssTableEntries->count() == 0) {
            ALOGE("There are no sync frames for video track");
            mIsMalformed = true;
            return true;
        }
    }

    if (OK != checkCodecSpecificData()) {         // no codec specific data
    // Don't check for CodecSpecificData when track is empty.
    if (mStszTableEntries->count() > 0 && OK != checkCodecSpecificData()) {
        // No codec specific data.
        mIsMalformed = true;
        return true;
    }

@@ -3704,7 +3789,7 @@ void MPEG4Writer::Track::sendTrackSummary(bool hasMultipleTracks) {
        return;
    }

    int trackNum = (mTrackId << 28);
    uint32_t trackNum = (mTrackId.getId() << 28);

    mOwner->notify(MEDIA_RECORDER_TRACK_EVENT_INFO,
                    trackNum | MEDIA_RECORDER_TRACK_INFO_TYPE,
@@ -3758,15 +3843,15 @@ void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
    if (mTrackEveryTimeDurationUs > 0 &&
        timeUs - mPreviousTrackTimeUs >= mTrackEveryTimeDurationUs) {
        ALOGV("Fire time tracking progress status at %" PRId64 " us", timeUs);
        mOwner->trackProgressStatus(mTrackId, timeUs - mPreviousTrackTimeUs, err);
        mOwner->trackProgressStatus(mTrackId.getId(), timeUs - mPreviousTrackTimeUs, err);
        mPreviousTrackTimeUs = timeUs;
    }
}

void MPEG4Writer::trackProgressStatus(
        size_t trackId, int64_t timeUs, status_t err) {
        uint32_t trackId, int64_t timeUs, status_t err) {
    Mutex::Autolock lock(mLock);
    int32_t trackNum = (trackId << 28);
    uint32_t trackNum = (trackId << 28);

    // Error notification
    // Do not consider ERROR_END_OF_STREAM an error
@@ -3936,8 +4021,8 @@ int64_t MPEG4Writer::Track::getMinCttsOffsetTimeUs() {

void MPEG4Writer::Track::writeStblBox() {
    mOwner->beginBox("stbl");
    // Add subboxes only for non-empty tracks.
    if (mStszTableEntries->count() > 0) {
    // Add subboxes for only non-empty and well-formed tracks.
    if (mStszTableEntries->count() > 0 && !isTrackMalFormed()) {
        mOwner->beginBox("stsd");
        mOwner->writeInt32(0);               // version=0, flags=0
        mOwner->writeInt32(1);               // entry count
@@ -4242,7 +4327,7 @@ void MPEG4Writer::Track::writeTkhdBox(uint32_t now) {
    mOwner->writeInt32(0x07);          // version=0, flags=7
    mOwner->writeInt32(now);           // creation time
    mOwner->writeInt32(now);           // modification time
    mOwner->writeInt32(mTrackId);      // track id starts with 1
    mOwner->writeInt32(mTrackId.getId()); // track id starts with 1
    mOwner->writeInt32(0);             // reserved
    int64_t trakDurationUs = getDurationUs();
    int32_t mvhdTimeScale = mOwner->getTimeScale();
+4 −1
Original line number Diff line number Diff line
@@ -171,7 +171,10 @@ status_t MediaMuxer::stop() {
        if (err != OK || mError != OK) {
            ALOGE("stop err: %d, mError:%d", err, mError);
        }
        // Prioritize mError over err.
        /* Prioritize mError over err as writer would have got stopped on any
         * internal error and notified muxer already.  Clients might issue
         * stop again later, and mWriter->stop() would return success.
         */
        if (mError != OK) {
            err = mError;
        }
+7 −6
Original line number Diff line number Diff line
@@ -86,8 +86,8 @@ private:

    enum {
        kWhatSwitch                  = 'swch',
        kWhatHandleIOError                   = 'ioer',
        kWhatHandleFallocateError            = 'faer'
        kWhatIOError                 = 'ioer',
        kWhatFallocateError          = 'faer'
    };

    int  mFd;
@@ -287,7 +287,8 @@ private:
    bool exceedsFileDurationLimit();
    bool approachingFileSizeLimit();
    bool isFileStreamable() const;
    void trackProgressStatus(size_t trackId, int64_t timeUs, status_t err = OK);
    void trackProgressStatus(uint32_t trackId, int64_t timeUs, status_t err = OK);
    status_t validateAllTracksId(bool akKey4BitTrackIds);
    void writeCompositionMatrix(int32_t degrees);
    void writeMvhdBox(int64_t durationUs);
    void writeMoovBox(int64_t durationUs);
@@ -327,7 +328,7 @@ private:
    void writeFileLevelMetaBox();

    void sendSessionSummary();
    void release();
    status_t release();
    status_t switchFd();
    status_t reset(bool stopSource = true);

Loading