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

Commit 28d4d67a authored by Gopalakrishnan Nallasamy's avatar Gopalakrishnan Nallasamy
Browse files

MPEG4Writer:64bit offset + fallocate

Use 64bit offset in mpeg4writer. Allows us to compose/mux files more
than 4GB in size.  Notify MediaMuxer and MediaRecorder users about any failure during
write() and seek(), with best effort attempt to save moov box.
SDK and NDK MediaMuxer.WriteSampleData() would throw IllegalStateException and return
INVALID_OPERATION as before, but in the very next call to this function after failure.
Earlier, writeSampleData wasn't aware of any write/seek failure.
SDK MediaMuxer.Stop() would throw relevant exception with error message.
NDK stop() would return relevant errors.
fallocate() is used to pre-allocate space for MOOV atom dynamically during
run time. If required space couldn't be allocated, then recording is
stopped and moov atom would be written to the file successfully.
Added logic to stop and release looper.

Bug: 135685864
Bug: 142060459
Bug: 142915394
Bug: 145235586

Test:  1) atest CtsMediaTestCases -- \
	--module-arg CtsMediaTestCases:size:small
       2) Composed files of size around 32GB using MediaMuxer and it is playing fine on the phone
       	and linux workstation.
       3) Filled up most of the flash memory in phone and tried to compose files using MediaMuxer
       	and MediaRecorder separately. Writing was stopped  when ::write failed due to out of space,
	and all threads exited gracefully in both cases. Expected errors were reported in Java layer.
       	(pre-allocation was disabled in this step).
       4) The same experiment as in step 3 with pre-allocation enabled, recording was stopped
	when there was not enough space and moov atom was written to the output file. File played
	well on phone and linux workstation.
       5) atest android.media.cts.MediaMuxerTest

Change-Id: I33de67d9dce3550ce3730a99c4aa92bc71c9f7b8
parent 990cc725
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -2002,7 +2002,6 @@ void StagefrightRecorder::setupMPEG4orWEBMMetaData(sp<MetaData> *meta) {
        (*meta)->setInt32(kKeyTimeScale, mMovieTimeScale);
    }
    if (mOutputFormat != OUTPUT_FORMAT_WEBM) {
        (*meta)->setInt32(kKey64BitFileOffset, mUse64BitFileOffset);
        if (mTrackEveryTimeDurationUs > 0) {
            (*meta)->setInt64(kKeyTrackTimeStatus, mTrackEveryTimeDurationUs);
        }
+305 −173

File changed.

Preview size limit exceeded, changes collapsed.

+38 −3
Original line number Diff line number Diff line
@@ -48,7 +48,8 @@ static bool isMp4Format(MediaMuxer::OutputFormat format) {

MediaMuxer::MediaMuxer(int fd, OutputFormat format)
    : mFormat(format),
      mState(UNINITIALIZED) {
      mState(UNINITIALIZED),
      mError(OK) {
    if (isMp4Format(format)) {
        mWriter = new MPEG4Writer(fd);
    } else if (format == OUTPUT_FORMAT_WEBM) {
@@ -58,6 +59,7 @@ MediaMuxer::MediaMuxer(int fd, OutputFormat format)
    }

    if (mWriter != NULL) {
        mWriter->setMuxerListener(this);
        mFileMeta = new MetaData;
        if (format == OUTPUT_FORMAT_HEIF) {
            // Note that the key uses recorder file types.
@@ -156,14 +158,22 @@ status_t MediaMuxer::start() {
status_t MediaMuxer::stop() {
    Mutex::Autolock autoLock(mMuxerLock);

    if (mState == STARTED) {
    if (mState == STARTED || mState == ERROR) {
        mState = STOPPED;
        for (size_t i = 0; i < mTrackList.size(); i++) {
            if (mTrackList[i]->stop() != OK) {
                return INVALID_OPERATION;
            }
        }
        return mWriter->stop();
        status_t err = mWriter->stop();
        if (err != OK || mError != OK) {
            ALOGE("stop err: %d, mError:%d", err, mError);
        }
        // Prioritize mError over err.
        if (mError != OK) {
            err = mError;
        }
        return err;
    } else {
        ALOGE("stop() is called in invalid state %d", mState);
        return INVALID_OPERATION;
@@ -212,4 +222,29 @@ status_t MediaMuxer::writeSampleData(const sp<ABuffer> &buffer, size_t trackInde
    return currentTrack->pushBuffer(mediaBuffer);
}

void MediaMuxer::notify(int msg, int ext1, int ext2) {
    switch (msg) {
        case MEDIA_RECORDER_EVENT_ERROR:
        case MEDIA_RECORDER_TRACK_EVENT_ERROR: {
            Mutex::Autolock autoLock(mMuxerLock);
            mState = ERROR;
            mError = ext2;
            ALOGW("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
            break;
        }
        case MEDIA_RECORDER_EVENT_INFO: {
            if (ext1 == MEDIA_RECORDER_INFO_UNKNOWN) {
                Mutex::Autolock autoLock(mMuxerLock);
                mState = ERROR;
                mError = ext2;
                ALOGW("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
            }
            break;
        }
        default:
            // Ignore INFO and other notifications for now.
            break;
    }
}

}  // namespace android
+31 −4
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <utils/threads.h>
#include <media/stagefright/foundation/AHandlerReflector.h>
#include <media/stagefright/foundation/ALooper.h>
#include <mutex>

namespace android {

@@ -58,6 +59,10 @@ public:
    void writeFourcc(const char *fourcc);
    void write(const void *data, size_t size);
    inline size_t write(const void *ptr, size_t size, size_t nmemb);
    // Write to file system by calling ::write() or post error message to looper on failure.
    void writeOrPostError(int fd, const void *buf, size_t count);
    // Seek in the file by calling ::lseek64() or post error message to looper on failure.
    void seekOrPostError(int fd, off64_t offset, int whence);
    void endBox();
    uint32_t interleaveDuration() const { return mInterleaveDurationUs; }
    status_t setInterleaveDuration(uint32_t duration);
@@ -80,6 +85,8 @@ private:

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

    int  mFd;
@@ -88,14 +95,15 @@ private:
    status_t mInitCheck;
    bool mIsRealTimeRecording;
    bool mUse4ByteNalLength;
    bool mUse32BitOffset;
    bool mIsFileSizeLimitExplicitlyRequested;
    bool mPaused;
    bool mStarted;  // Writer thread + track threads started successfully
    bool mWriterThreadStarted;  // Only writer thread started successfully
    bool mSendNotify;
    off64_t mOffset;
    off_t mMdatOffset;
    off64_t mPreAllocateFileEndOffset;  //End of file offset during preallocation.
    off64_t mMdatOffset;
    off64_t mMdatEndOffset;  // End offset of mdat atom.
    uint8_t *mInMemoryCache;
    off64_t mInMemoryCacheOffset;
    off64_t mInMemoryCacheSize;
@@ -112,11 +120,18 @@ private:
    bool mAreGeoTagsAvailable;
    int32_t mStartTimeOffsetMs;
    bool mSwitchPending;
    bool mWriteSeekErr;
    bool mFallocateErr;
    bool mPreAllocationEnabled;

    sp<ALooper> mLooper;
    sp<AHandlerReflector<MPEG4Writer> > mReflector;

    Mutex mLock;
    std::mutex mResetMutex;
    std::mutex mFallocMutex;
    bool mPreAllocFirstTime; // Pre-allocate space for file and track headers only once per file.
    uint64_t mPrevAllTracksTotalMetaDataSizeEstimate;

    List<Track *> mTracks;

@@ -200,6 +215,7 @@ private:
    } ItemProperty;

    bool mHasFileLevelMeta;
    uint64_t mFileLevelMetaDataSize;
    bool mHasMoovBox;
    uint32_t mPrimaryItemId;
    uint32_t mAssociationEntryCount;
@@ -210,9 +226,11 @@ private:

    // Writer thread handling
    status_t startWriterThread();
    void stopWriterThread();
    status_t stopWriterThread();
    static void *ThreadWrapper(void *me);
    void threadFunc();
    void setupAndStartLooper();
    void stopAndReleaseLooper();

    // Buffer a single chunk to be written out later.
    void bufferChunk(const Chunk& chunk);
@@ -263,7 +281,6 @@ private:
    void addRefs_l(uint16_t itemId, const ItemRefs &);

    bool exceedsFileSizeLimit();
    bool use32BitFileOffset() const;
    bool exceedsFileDurationLimit();
    bool approachingFileSizeLimit();
    bool isFileStreamable() const;
@@ -284,6 +301,16 @@ private:
    void writeIlst();
    void writeMoovLevelMetaBox();

    /*
     * Allocate space needed for MOOV atom in advance and maintain just enough before write
     * of any data.  Stop writing and save MOOV atom if there was any error.
     */
    bool preAllocate(uint64_t wantSize);
    /*
     * Truncate file as per the size used for meta data and actual data in a session.
     */
    bool truncatePreAllocation();

    // HEIF writing
    void writeIlocBox();
    void writeInfeBox(uint16_t itemId, const char *type, uint32_t flags);
+5 −2
Original line number Diff line number Diff line
@@ -117,21 +117,24 @@ public:
    status_t writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
                             int64_t timeUs, uint32_t flags) ;

    void notify(int msg, int ext1, int ext2);

private:
    const OutputFormat mFormat;
    sp<MediaWriter> mWriter;
    Vector< sp<MediaAdapter> > mTrackList;  // Each track has its MediaAdapter.
    sp<MetaData> mFileMeta;  // Metadata for the whole file.

    Mutex mMuxerLock;

    enum State {
        UNINITIALIZED,
        INITIALIZED,
        STARTED,
        STOPPED
        STOPPED,
        ERROR
    };
    State mState;
    status_t mError;

    DISALLOW_EVIL_CONSTRUCTORS(MediaMuxer);
};
Loading