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

Commit fa43e73d authored by Gopalakrishnan Nallasamy's avatar Gopalakrishnan Nallasamy Committed by Automerger Merge Worker
Browse files

Merge "MPEG4Writer:Ignore sample table of malformed track" into rvc-dev am: db0b3444

Change-Id: Ib56f90a2938455b97054d20e686d4f0aed66ed0a
parents 223c5289 db0b3444
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line 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) {
    if (mOutputFormat == OUTPUT_FORMAT_MPEG_4 || mOutputFormat == OUTPUT_FORMAT_THREE_GPP) {
        (*meta)->setInt32(kKeyEmptyTrackMalFormed, true);
        (*meta)->setInt32(kKeyEmptyTrackMalFormed, true);
        (*meta)->setInt32(kKey4BitTrackIds, true);
    }
    }
}
}


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


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


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


class MPEG4Writer::Track {
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:
public:
    Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
    Track(MPEG4Writer *owner, const sp<MediaSource> &source, uint32_t aTrackId);


    ~Track();
    ~Track();


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


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


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


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


    // If the estimation is wrong, we will pay the price of wasting
    // If the estimation is wrong, we will pay the price of wasting
    // some reserved space. This should not happen so often statistically.
    // some reserved space. This should not happen so often statistically.
@@ -796,6 +827,15 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) {
    return size;
    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) {
status_t MPEG4Writer::start(MetaData *param) {
    if (mInitCheck != OK) {
    if (mInitCheck != OK) {
        return UNKNOWN_ERROR;
        return UNKNOWN_ERROR;
@@ -810,6 +850,9 @@ status_t MPEG4Writer::start(MetaData *param) {
        mIsFileSizeLimitExplicitlyRequested = true;
        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);
    int32_t fileSizeBits = fpathconf(mFd, _PC_FILESIZEBITS);
    ALOGD("fpathconf _PC_FILESIZEBITS:%" PRId32, fileSizeBits);
    ALOGD("fpathconf _PC_FILESIZEBITS:%" PRId32, fileSizeBits);
    fileSizeBits = std::min(fileSizeBits, 52 /* cap it below 4 peta bytes */);
    fileSizeBits = std::min(fileSizeBits, 52 /* cap it below 4 peta bytes */);
@@ -944,6 +987,17 @@ status_t MPEG4Writer::start(MetaData *param) {


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


    err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
    err = static_cast<status_t>(reinterpret_cast<uintptr_t>(dummy));
    mWriterThreadStarted = false;
    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;
    return err;
}
}


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


void MPEG4Writer::release() {
status_t MPEG4Writer::release() {
    ALOGD("release()");
    ALOGD("release()");
    if (mPreAllocationEnabled) {
    if (mPreAllocationEnabled) {
        truncatePreAllocation();
        truncatePreAllocation();
    }
    }
    int err = OK;
    int retVal = fsync(mFd);
    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);
    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;
    mFd = -1;
    if (mNextFd != -1) {
    if (mNextFd != -1) {
        retVal = close(mNextFd);
        retVal = close(mNextFd);
        mNextFd = -1;
        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();
    stopAndReleaseLooper();
    mInitCheck = NO_INIT;
    mInitCheck = NO_INIT;
    mStarted = false;
    mStarted = false;
    free(mInMemoryCache);
    free(mInMemoryCache);
    mInMemoryCache = NULL;
    mInMemoryCache = NULL;
    return err;
}
}


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


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


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


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


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



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


    status_t writerErr = stopWriterThread();
    status_t writerErr = stopWriterThread();


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


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


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


    CHECK(mBoxes.empty());
    CHECK(mBoxes.empty());


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


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


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


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


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


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


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


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


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


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


    if (mIsAudio) {
    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) {
    } else if (mIsVideo) {
        prctl(PR_SET_NAME, (unsigned long)"VideoTrackWriterThread", 0, 0, 0);
        prctl(PR_SET_NAME, (unsigned long)"MP4WtrVidTrkThread", 0, 0, 0);
    } else {
    } else {
        prctl(PR_SET_NAME, (unsigned long)"MetadataTrackWriterThread", 0, 0, 0);
        prctl(PR_SET_NAME, (unsigned long)"MP4WtrMetaTrkThread", 0, 0, 0);
    }
    }


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


        ++count;
        ++count;

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


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


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


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

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


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


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


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

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


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


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


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


void MPEG4Writer::trackProgressStatus(
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);
    Mutex::Autolock lock(mLock);
    int32_t trackNum = (trackId << 28);
    uint32_t trackNum = (trackId << 28);


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


void MPEG4Writer::Track::writeStblBox() {
void MPEG4Writer::Track::writeStblBox() {
    mOwner->beginBox("stbl");
    mOwner->beginBox("stbl");
    // Add subboxes only for non-empty tracks.
    // Add subboxes for only non-empty and well-formed tracks.
    if (mStszTableEntries->count() > 0) {
    if (mStszTableEntries->count() > 0 && !isTrackMalFormed()) {
        mOwner->beginBox("stsd");
        mOwner->beginBox("stsd");
        mOwner->writeInt32(0);               // version=0, flags=0
        mOwner->writeInt32(0);               // version=0, flags=0
        mOwner->writeInt32(1);               // entry count
        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(0x07);          // version=0, flags=7
    mOwner->writeInt32(now);           // creation time
    mOwner->writeInt32(now);           // creation time
    mOwner->writeInt32(now);           // modification 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
    mOwner->writeInt32(0);             // reserved
    int64_t trakDurationUs = getDurationUs();
    int64_t trakDurationUs = getDurationUs();
    int32_t mvhdTimeScale = mOwner->getTimeScale();
    int32_t mvhdTimeScale = mOwner->getTimeScale();
+4 −1
Original line number Original line Diff line number Diff line
@@ -171,7 +171,10 @@ status_t MediaMuxer::stop() {
        if (err != OK || mError != OK) {
        if (err != OK || mError != OK) {
            ALOGE("stop err: %d, mError:%d", err, mError);
            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) {
        if (mError != OK) {
            err = mError;
            err = mError;
        }
        }
+7 −6
Original line number Original line Diff line number Diff line
@@ -86,8 +86,8 @@ private:


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


    int  mFd;
    int  mFd;
@@ -287,7 +287,8 @@ private:
    bool exceedsFileDurationLimit();
    bool exceedsFileDurationLimit();
    bool approachingFileSizeLimit();
    bool approachingFileSizeLimit();
    bool isFileStreamable() const;
    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 writeCompositionMatrix(int32_t degrees);
    void writeMvhdBox(int64_t durationUs);
    void writeMvhdBox(int64_t durationUs);
    void writeMoovBox(int64_t durationUs);
    void writeMoovBox(int64_t durationUs);
@@ -327,7 +328,7 @@ private:
    void writeFileLevelMetaBox();
    void writeFileLevelMetaBox();


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


Loading