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

Commit 4449e397 authored by Gopalakrishnan Nallasamy's avatar Gopalakrishnan Nallasamy
Browse files

AppendData:MPEG4Writer&Extractor,MediaAppender

Bug: 154734325

Append media data to an MPEG4File.  Changes were made in
MPEG4Extractor to pass sample offsets of existing data in file to
the writer. MediaAppender class takes cares of parsing and preparing
exisiting MPEG4 file to be added with new data. Data that is present
in the file is not copied again, hence that pass happens fast.
New data is appended at the end of the file.

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

Merged-In: I3f2bfa6b92d85c3a87ac740b2d401dcb10d7976e
Change-Id: I55c90b3e71f2d94a497b2196a02e17e61c34ce3a
parent 56ab5d5e
Loading
Loading
Loading
Loading
+24 −1
Original line number Diff line number Diff line
@@ -6259,6 +6259,18 @@ media_status_t MPEG4Source::read(
                    AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
                }

                AMediaFormat_setInt64(
                        meta, "sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET*/,
                        offset);

                if (mSampleTable != nullptr &&
                        mCurrentSampleIndex == mSampleTable->getLastSampleIndexInChunk()) {
                    AMediaFormat_setInt64(
                    meta,
                    "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
                    mSampleTable->getLastSampleIndexInChunk());
                }

                ++mCurrentSampleIndex;
            }
        }
@@ -6408,6 +6420,17 @@ media_status_t MPEG4Source::read(
            AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, 1);
        }

        AMediaFormat_setInt64(
                meta, "sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_FILE_OFFSET*/, offset);

        if (mSampleTable != nullptr &&
                mCurrentSampleIndex == mSampleTable->getLastSampleIndexInChunk()) {
            AMediaFormat_setInt64(
                    meta,
                    "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
                    mSampleTable->getLastSampleIndexInChunk());
        }

        ++mCurrentSampleIndex;

        *out = mBuffer;
+1 −0
Original line number Diff line number Diff line
@@ -274,6 +274,7 @@ cc_library {
        "MPEG2TSWriter.cpp",
        "MPEG4Writer.cpp",
        "MediaAdapter.cpp",
        "MediaAppender.cpp",
        "MediaClock.cpp",
        "MediaCodec.cpp",
        "MediaCodecList.cpp",
+72 −10
Original line number Diff line number Diff line
@@ -519,12 +519,12 @@ void MPEG4Writer::initInternal(int fd, bool isFirstSession) {
    mSendNotify = false;
    mWriteSeekErr = false;
    mFallocateErr = false;

    // Reset following variables for all the sessions and they will be
    // initialized in start(MetaData *param).
    mIsRealTimeRecording = true;
    mUse4ByteNalLength = true;
    mOffset = 0;
    mMaxOffsetAppend = 0;
    mPreAllocateFileEndOffset = 0;
    mMdatOffset = 0;
    mMdatEndOffset = 0;
@@ -992,6 +992,19 @@ status_t MPEG4Writer::start(MetaData *param) {
        seekOrPostError(mFd, mFreeBoxOffset, SEEK_SET);
        writeInt32(mInMemoryCacheSize);
        write("free", 4);
        if (mInMemoryCacheSize >= 8) {
            off64_t bufSize = mInMemoryCacheSize - 8;
            char* zeroBuffer = new (std::nothrow) char[bufSize];
            if (zeroBuffer) {
                std::fill_n(zeroBuffer, bufSize, '0');
                writeOrPostError(mFd, zeroBuffer, bufSize);
                delete [] zeroBuffer;
            } else {
                ALOGW("freebox in file isn't initialized to 0");
            }
        } else {
            ALOGW("freebox size is less than 8:%" PRId64, mInMemoryCacheSize);
        }
        mMdatOffset = mFreeBoxOffset + mInMemoryCacheSize;
    } else {
        mMdatOffset = mOffset;
@@ -1541,6 +1554,26 @@ off64_t MPEG4Writer::addSample_l(
        MediaBuffer *buffer, bool usePrefix,
        uint32_t tiffHdrOffset, size_t *bytesWritten) {
    off64_t old_offset = mOffset;
    int64_t offset;
    ALOGV("buffer->range_length:%lld", (long long)buffer->range_length());
    if (buffer->meta_data().findInt64(kKeySampleFileOffset, &offset)) {
        ALOGV("offset:%lld, old_offset:%lld", (long long)offset, (long long)old_offset);
        if (old_offset == offset) {
            mOffset += buffer->range_length();
        } else {
            ALOGV("offset and old_offset are not equal! diff:%lld", (long long)offset - old_offset);
            mOffset = offset + buffer->range_length();
            // mOffset += buffer->range_length() + offset - old_offset;
        }
        *bytesWritten = buffer->range_length();
        ALOGV("mOffset:%lld, mMaxOffsetAppend:%lld, bytesWritten:%lld", (long long)mOffset,
                  (long long)mMaxOffsetAppend, (long long)*bytesWritten);
        mMaxOffsetAppend = std::max(mOffset, mMaxOffsetAppend);
        seekOrPostError(mFd, mMaxOffsetAppend, SEEK_SET);
        return offset;
    }

    ALOGV("mOffset:%lld, mMaxOffsetAppend:%lld", (long long)mOffset, (long long)mMaxOffsetAppend);

    if (usePrefix) {
        addMultipleLengthPrefixedSamples_l(buffer);
@@ -1557,6 +1590,10 @@ off64_t MPEG4Writer::addSample_l(
        mOffset += buffer->range_length();
    }
    *bytesWritten = mOffset - old_offset;

    ALOGV("mOffset:%lld, old_offset:%lld, bytesWritten:%lld", (long long)mOffset,
          (long long)old_offset, (long long)*bytesWritten);

    return old_offset;
}

@@ -1569,6 +1606,7 @@ static void StripStartcode(MediaBuffer *buffer) {
        (const uint8_t *)buffer->data() + buffer->range_offset();

    if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
        ALOGV("stripping start code");
        buffer->set_range(
                buffer->range_offset() + 4, buffer->range_length() - 4);
    }
@@ -1599,8 +1637,10 @@ void MPEG4Writer::addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer) {
}

void MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
    ALOGV("alp:buffer->range_length:%lld", (long long)buffer->range_length());
    size_t length = buffer->range_length();
    if (mUse4ByteNalLength) {
        ALOGV("mUse4ByteNalLength");
        uint8_t x[4];
        x[0] = length >> 24;
        x[1] = (length >> 16) & 0xff;
@@ -1610,6 +1650,7 @@ void MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
        writeOrPostError(mFd, (const uint8_t*)buffer->data() + buffer->range_offset(), length);
        mOffset += length + 4;
    } else {
        ALOGV("mUse2ByteNalLength");
        CHECK_LT(length, 65536u);

        uint8_t x[2];
@@ -2762,6 +2803,9 @@ void MPEG4Writer::threadFunc() {
    }

    writeAllChunks();
    ALOGV("threadFunc mOffset:%lld, mMaxOffsetAppend:%lld", (long long)mOffset,
          (long long)mMaxOffsetAppend);
    mOffset = std::max(mOffset, mMaxOffsetAppend);
}

status_t MPEG4Writer::startWriterThread() {
@@ -3323,6 +3367,7 @@ status_t MPEG4Writer::Track::threadEntry() {
    uint32_t lastSamplesPerChunk = 0;
    int64_t lastSampleDurationUs = -1;      // Duration calculated from EOS buffer and its timestamp
    int64_t lastSampleDurationTicks = -1;   // Timescale based ticks
    int64_t sampleFileOffset = -1;

    if (mIsAudio) {
        prctl(PR_SET_NAME, (unsigned long)"MP4WtrAudTrkThread", 0, 0, 0);
@@ -3342,6 +3387,7 @@ status_t MPEG4Writer::Track::threadEntry() {
    MediaBufferBase *buffer;
    const char *trackName = getTrackType();
    while (!mDone && (err = mSource->read(&buffer)) == OK) {
        ALOGV("read:buffer->range_length:%lld", (long long)buffer->range_length());
        int32_t isEOS = false;
        if (buffer->range_length() == 0) {
            if (buffer->meta_data().findInt32(kKeyIsEndOfStream, &isEOS) && isEOS) {
@@ -3448,6 +3494,14 @@ status_t MPEG4Writer::Track::threadEntry() {
                continue;
            }
        }
        if (!buffer->meta_data().findInt64(kKeySampleFileOffset, &sampleFileOffset)) {
            sampleFileOffset = -1;
        }
        int64_t lastSample = -1;
        if (!buffer->meta_data().findInt64(kKeyLastSampleIndexInChunk, &lastSample)) {
            lastSample = -1;
        }
        ALOGV("sampleFileOffset:%lld", (long long)sampleFileOffset);

        /*
         * Reserve space in the file for the current sample + to be written MOOV box. If reservation
@@ -3455,7 +3509,7 @@ status_t MPEG4Writer::Track::threadEntry() {
         * write MOOV box successfully as space for the same was reserved in the prior call.
         * Release the current buffer/sample here.
         */
        if (!mOwner->preAllocate(buffer->range_length())) {
        if (sampleFileOffset == -1 && !mOwner->preAllocate(buffer->range_length())) {
            buffer->release();
            buffer = nullptr;
            break;
@@ -3466,9 +3520,14 @@ status_t MPEG4Writer::Track::threadEntry() {
        // Make a deep copy of the MediaBuffer and Metadata and release
        // the original as soon as we can
        MediaBuffer *copy = new MediaBuffer(buffer->range_length());
        if (sampleFileOffset != -1) {
            copy->meta_data().setInt64(kKeySampleFileOffset, sampleFileOffset);
        } else {
            memcpy(copy->data(), (uint8_t*)buffer->data() + buffer->range_offset(),
                   buffer->range_length());
        }
        copy->set_range(0, buffer->range_length());

        meta_data = new MetaData(buffer->meta_data());
        buffer->release();
        buffer = NULL;
@@ -3476,14 +3535,16 @@ status_t MPEG4Writer::Track::threadEntry() {
            copy->meta_data().setInt32(kKeyExifTiffOffset, tiffHdrOffset);
        }
        bool usePrefix = this->usePrefix() && !isExif;

        if (usePrefix) StripStartcode(copy);

        if (sampleFileOffset == -1 && usePrefix) {
            StripStartcode(copy);
        }
        size_t sampleSize = copy->range_length();
        if (usePrefix) {
        if (sampleFileOffset == -1 && usePrefix) {
            if (mOwner->useNalLengthFour()) {
                ALOGV("nallength4");
                sampleSize += 4;
            } else {
                ALOGV("nallength2");
                sampleSize += 2;
            }
        }
@@ -3778,7 +3839,8 @@ status_t MPEG4Writer::Track::threadEntry() {
                chunkTimestampUs = timestampUs;
            } else {
                int64_t chunkDurationUs = timestampUs - chunkTimestampUs;
                if (chunkDurationUs > interleaveDurationUs) {
                if (chunkDurationUs > interleaveDurationUs || lastSample > 1) {
                    ALOGV("lastSample:%lld", (long long)lastSample);
                    if (chunkDurationUs > mMaxChunkDurationUs) {
                        mMaxChunkDurationUs = chunkDurationUs;
                    }
+425 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "MediaAppender"

#include <media/stagefright/MediaAppender.h>
#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <utils/Log.h>
// TODO : check if this works for NDK apps without JVM
// #include <media/ndk/NdkJavaVMHelperPriv.h>

namespace android {

struct MediaAppender::sampleDataInfo {
    size_t size;
    int64_t time;
    size_t exTrackIndex;
    sp<MetaData> meta;
};

sp<MediaAppender> MediaAppender::create(int fd, AppendMode mode) {
    if (fd < 0) {
        ALOGE("invalid file descriptor");
        return nullptr;
    }
    if (!(mode >= APPEND_MODE_FIRST && mode <= APPEND_MODE_LAST)) {
        ALOGE("invalid mode %d", mode);
        return nullptr;
    }
    sp<MediaAppender> ma = new (std::nothrow) MediaAppender(fd, mode);
    if (ma->init() != OK) {
        return nullptr;
    }
    return ma;
}

// TODO: inject mediamuxer and mediaextractor objects.
// TODO: @format is not required as an input if we can sniff the file and find the format of
//       the existing content.
// TODO: Code it to the interface(MediaAppender), and have a separate MediaAppender NDK
MediaAppender::MediaAppender(int fd, AppendMode mode)
    : mFd(fd),
      mMode(mode),
      // TODO : check if this works for NDK apps without JVM
      // mExtractor(new NuMediaExtractor(NdkJavaVMHelper::getJNIEnv() != nullptr
      //           ? NuMediaExtractor::EntryPoint::NDK_WITH_JVM
      //           : NuMediaExtractor::EntryPoint::NDK_NO_JVM)),
      mExtractor(new (std::nothrow) NuMediaExtractor(NuMediaExtractor::EntryPoint::NDK_WITH_JVM)),
      mTrackCount(0),
      mState(UNINITIALIZED) {
          ALOGV("MediaAppender::MediaAppender mode:%d", mode);
      }

status_t MediaAppender::init() {
    std::scoped_lock lock(mMutex);
    ALOGV("MediaAppender::init");
    status_t status = mExtractor->setDataSource(mFd, 0, lseek(mFd, 0, SEEK_END));
    if (status != OK) {
        ALOGE("extractor_setDataSource failed, status :%d", status);
        return status;
    }

    if (strcmp("MPEG4Extractor", mExtractor->getName()) == 0) {
        mFormat = MediaMuxer::OUTPUT_FORMAT_MPEG_4;
    } else {
        ALOGE("Unsupported format, extractor name:%s", mExtractor->getName());
        return ERROR_UNSUPPORTED;
    }

    mTrackCount = mExtractor->countTracks();
    ALOGV("mTrackCount:%zu", mTrackCount);
    if (mTrackCount == 0) {
        ALOGE("no tracks are present");
        return ERROR_MALFORMED;
    }
    size_t exTrackIndex = 0;
    ssize_t audioTrackIndex = -1, videoTrackIndex = -1;
    bool audioSyncSampleTimeSet = false;

    while (exTrackIndex < mTrackCount) {
        sp<AMessage> fmt;
        status = mExtractor->getTrackFormat(exTrackIndex, &fmt, 0);
        if (status != OK) {
            ALOGE("getTrackFormat failed for trackIndex:%zu, status:%d", exTrackIndex, status);
            return status;
        }
        AString mime;
        if (fmt->findString("mime", &mime)) {
            if (!strncasecmp(mime.c_str(), "video/", 6)) {
                ALOGV("VideoTrack");
                if (videoTrackIndex != -1) {
                    ALOGE("Not more than one video track is supported");
                    return ERROR_UNSUPPORTED;
                }
                videoTrackIndex = exTrackIndex;
            } else if (!strncasecmp(mime.c_str(), "audio/", 6)) {
                ALOGV("AudioTrack");
                if (audioTrackIndex != -1) {
                    ALOGE("Not more than one audio track is supported");
                }
                audioTrackIndex = exTrackIndex;
            } else {
                ALOGV("Neither Video nor Audio track");
            }
        }
        mFmtIndexMap.emplace(exTrackIndex, fmt);
        mSampleCountVect.emplace_back(0);
        mMaxTimestampVect.emplace_back(0);
        mLastSyncSampleTimeVect.emplace_back(0);
        status = mExtractor->selectTrack(exTrackIndex);
        if (status != OK) {
            ALOGE("selectTrack failed for trackIndex:%zu, status:%d", exTrackIndex, status);
            return status;
        }
        ++exTrackIndex;
    }

    ALOGV("AudioTrackIndex:%zu, VideoTrackIndex:%zu", audioTrackIndex, videoTrackIndex);

    do {
        sampleDataInfo tmpSDI;
        // TODO: read info into members of the struct sampleDataInfo directly
        size_t sampleSize;
        status = mExtractor->getSampleSize(&sampleSize);
        if (status != OK) {
            ALOGE("getSampleSize failed, status:%d", status);
            return status;
        }
        mSampleSizeVect.emplace_back(sampleSize);
        tmpSDI.size = sampleSize;
        int64_t sampleTime = 0;
        status = mExtractor->getSampleTime(&sampleTime);
        if (status != OK) {
            ALOGE("getSampleTime failed, status:%d", status);
            return status;
        }
        mSampleTimeVect.emplace_back(sampleTime);
        tmpSDI.time = sampleTime;
        status = mExtractor->getSampleTrackIndex(&exTrackIndex);
        if (status != OK) {
            ALOGE("getSampleTrackIndex failed, status:%d", status);
            return status;
        }
        mSampleIndexVect.emplace_back(exTrackIndex);
        tmpSDI.exTrackIndex = exTrackIndex;
        ++mSampleCountVect[exTrackIndex];
        mMaxTimestampVect[exTrackIndex] = std::max(mMaxTimestampVect[exTrackIndex], sampleTime);
        sp<MetaData> sampleMeta;
        status = mExtractor->getSampleMeta(&sampleMeta);
        if (status != OK) {
            ALOGE("getSampleMeta failed, status:%d", status);
            return status;
        }
        mSampleMetaVect.emplace_back(sampleMeta);
        int32_t val = 0;
        if (sampleMeta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
            mLastSyncSampleTimeVect[exTrackIndex] = sampleTime;
        }
        tmpSDI.meta = sampleMeta;
        mSDI.emplace_back(tmpSDI);
    } while (mExtractor->advance() == OK);

    mExtractor.clear();

    std::sort(mSDI.begin(), mSDI.end(), [](sampleDataInfo& a, sampleDataInfo& b) {
        int64_t aOffset, bOffset;
        a.meta->findInt64(kKeySampleFileOffset, &aOffset);
        b.meta->findInt64(kKeySampleFileOffset, &bOffset);
        return aOffset < bOffset;
    });
    for (int64_t syncSampleTime : mLastSyncSampleTimeVect) {
        ALOGV("before ignoring frames, mLastSyncSampleTimeVect:%lld", (long long)syncSampleTime);
    }
    ALOGV("mMode:%u", mMode);
    if (mMode == APPEND_MODE_IGNORE_LAST_VIDEO_GOP && videoTrackIndex != -1 ) {
        ALOGV("Video track is present");
        bool lastVideoIframe = false;
        size_t lastVideoIframeOffset = 0;
        int64_t lastVideoSampleTime = -1;
        for (auto rItr = mSDI.rbegin(); rItr != mSDI.rend(); ++rItr) {
            if (rItr->exTrackIndex != videoTrackIndex) {
                continue;
            }
            if (lastVideoSampleTime == -1) {
                lastVideoSampleTime = rItr->time;
            }
            int64_t offset = 0;
            if (!rItr->meta->findInt64(kKeySampleFileOffset, &offset) || offset == 0) {
                ALOGE("Missing offset");
                return ERROR_MALFORMED;
            }
            ALOGV("offset:%lld", (long long)offset);
            int32_t val = 0;
            if (rItr->meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
                ALOGV("sampleTime:%lld", (long long)rItr->time);
                ALOGV("lastVideoSampleTime:%lld", (long long)lastVideoSampleTime);
                if (lastVideoIframe == false && (lastVideoSampleTime - rItr->time) >
                                1000000/* Track interleaving duration in MPEG4Writer*/) {
                    ALOGV("lastVideoIframe got chosen");
                    lastVideoIframe = true;
                    mLastSyncSampleTimeVect[videoTrackIndex] = rItr->time;
                    lastVideoIframeOffset = offset;
                    ALOGV("lastVideoIframeOffset:%lld", (long long)offset);
                    break;
                }
            }
        }
        if (lastVideoIframe == false) {
            ALOGV("Need to rewrite all samples");
            mLastSyncSampleTimeVect[videoTrackIndex] = 0;
            lastVideoIframeOffset = 0;
        }
        unsigned int framesIgnoredCount = 0;
        for (auto itr = mSDI.begin(); itr != mSDI.end();) {
            int64_t offset = 0;
            ALOGV("trackIndex:%zu, %" PRId64 "", itr->exTrackIndex, itr->time);
            if (itr->meta->findInt64(kKeySampleFileOffset, &offset) &&
                                        offset >= lastVideoIframeOffset) {
                ALOGV("offset:%lld", (long long)offset);
                if (!audioSyncSampleTimeSet && audioTrackIndex != -1 &&
                                            audioTrackIndex == itr->exTrackIndex) {
                    mLastSyncSampleTimeVect[audioTrackIndex] = itr->time;
                    audioSyncSampleTimeSet = true;
                }
                itr = mSDI.erase(itr);
                ++framesIgnoredCount;
            } else {
                ++itr;
            }
        }
        ALOGV("framesIgnoredCount:%u", framesIgnoredCount);
    }

    if (mMode == APPEND_MODE_IGNORE_LAST_VIDEO_GOP && videoTrackIndex == -1 &&
                            audioTrackIndex != -1) {
        ALOGV("Only AudioTrack is present");
        for (auto rItr = mSDI.rbegin(); rItr != mSDI.rend();  ++rItr) {
            int32_t val = 0;
            if (rItr->meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
                    mLastSyncSampleTimeVect[audioTrackIndex] = rItr->time;
                    break;
            }
        }
        unsigned int framesIgnoredCount = 0;
        for (auto itr = mSDI.begin(); itr != mSDI.end();) {
            if (itr->time >= mLastSyncSampleTimeVect[audioTrackIndex]) {
                itr = mSDI.erase(itr);
                ++framesIgnoredCount;
            } else {
                ++itr;
            }
        }
        ALOGV("framesIgnoredCount :%u", framesIgnoredCount);
    }

    for (size_t i = 0; i < mLastSyncSampleTimeVect.size(); ++i) {
        ALOGV("mLastSyncSampleTimeVect[%zu]:%lld", i, (long long)mLastSyncSampleTimeVect[i]);
        mFmtIndexMap[i]->setInt64(
                "sample-time-before-append" /*AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND*/,
                mLastSyncSampleTimeVect[i]);
    }
    for (size_t i = 0; i < mMaxTimestampVect.size(); ++i) {
        ALOGV("mMaxTimestamp[%zu]:%lld", i, (long long)mMaxTimestampVect[i]);
    }
    for (size_t i = 0; i < mSampleCountVect.size(); ++i) {
        ALOGV("SampleCountVect[%zu]:%zu", i, mSampleCountVect[i]);
    }
    mState = INITIALIZED;
    return OK;
}

MediaAppender::~MediaAppender() {
    ALOGV("MediaAppender::~MediaAppender");
    mMuxer.clear();
    mExtractor.clear();
}

status_t MediaAppender::start() {
    std::scoped_lock lock(mMutex);
    ALOGV("MediaAppender::start");
    if (mState != INITIALIZED) {
        ALOGE("MediaAppender::start() is called in invalid state %d", mState);
        return INVALID_OPERATION;
    }
    mMuxer = new (std::nothrow) MediaMuxer(mFd, mFormat);
    for (const auto& n : mFmtIndexMap) {
        ssize_t muxIndex = mMuxer->addTrack(n.second);
        if (muxIndex < 0) {
            ALOGE("addTrack failed");
            return UNKNOWN_ERROR;
        }
        mTrackIndexMap.emplace(n.first, muxIndex);
    }
    ALOGV("trackIndexmap size:%zu", mTrackIndexMap.size());

    status_t status = mMuxer->start();
    if (status != OK) {
        ALOGE("muxer start failed:%d", status);
        return status;
    }

    ALOGV("Sorting samples based on their offsets");
    for (int i = 0; i < mSDI.size(); ++i) {
        ALOGV("i:%d", i + 1);
        /* TODO : Allocate a single allocation of the max size, and reuse it across ABuffers if
         * using new ABuffer(void *, size_t).
         */
        sp<ABuffer> data = new (std::nothrow) ABuffer(mSDI[i].size);
        if (data == nullptr) {
            ALOGE("memory allocation failed");
            return NO_MEMORY;
        }
        data->setRange(0, mSDI[i].size);
        int32_t val = 0;
        int sampleFlags = 0;
        if (mSDI[i].meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
            sampleFlags |= MediaCodec::BUFFER_FLAG_SYNCFRAME;
        }

        int64_t val64;
        if (mSDI[i].meta->findInt64(kKeySampleFileOffset, &val64)) {
            ALOGV("SampleFileOffset Found :%zu:%lld:%lld", mSDI[i].exTrackIndex,
                  (long long)mSampleCountVect[mSDI[i].exTrackIndex], (long long)val64);
            sp<AMessage> bufMeta = data->meta();
            bufMeta->setInt64("sample-file-offset" /*AMEDIAFORMAT_KEY_SAMPLE_TIME_BEFORE_APPEND*/,
                              val64);
        }
        if (mSDI[i].meta->findInt64(kKeyLastSampleIndexInChunk, &val64)) {
            ALOGV("kKeyLastSampleIndexInChunk Found %lld:%lld",
                  (long long)mSampleCountVect[mSDI[i].exTrackIndex], (long long)val64);
            sp<AMessage> bufMeta = data->meta();
            bufMeta->setInt64(
                    "last-sample-index-in-chunk" /*AMEDIAFORMAT_KEY_LAST_SAMPLE_INDEX_IN_CHUNK*/,
                    val64);
        }
        status = mMuxer->writeSampleData(data, mTrackIndexMap[mSDI[i].exTrackIndex], mSDI[i].time,
                                         sampleFlags);
        if (status != OK) {
            ALOGE("muxer writeSampleData failed:%d", status);
            return status;
        }
    }
    mState = STARTED;
    return OK;
}

status_t MediaAppender::stop() {
    std::scoped_lock lock(mMutex);
    ALOGV("MediaAppender::stop");
    if (mState == STARTED) {
        status_t status = mMuxer->stop();
        if (status != OK) {
            mState = ERROR;
        } else {
            mState = STOPPED;
        }
        return status;
    } else {
        ALOGE("stop() is called in invalid state %d", mState);
        return INVALID_OPERATION;
    }
}

ssize_t MediaAppender::getTrackCount() {
    std::scoped_lock lock(mMutex);
    ALOGV("MediaAppender::getTrackCount");
    if (mState != INITIALIZED && mState != STARTED) {
        ALOGE("getTrackCount() is called in invalid state %d", mState);
        return -1;
    }
    return mTrackCount;
}

sp<AMessage> MediaAppender::getTrackFormat(size_t idx) {
    std::scoped_lock lock(mMutex);
    ALOGV("MediaAppender::getTrackFormat");
    if (mState != INITIALIZED && mState != STARTED) {
        ALOGE("getTrackFormat() is called in invalid state %d", mState);
        return nullptr;
    }
    if (idx < 0 || idx >= mTrackCount) {
        ALOGE("getTrackFormat() idx is out of range");
        return nullptr;
    }
    return mFmtIndexMap[idx];
}

status_t MediaAppender::writeSampleData(const sp<ABuffer>& buffer, size_t trackIndex,
                                        int64_t timeUs, uint32_t flags) {
    std::scoped_lock lock(mMutex);
    ALOGV("writeSampleData:trackIndex:%zu, time:%" PRId64 "", trackIndex, timeUs);
    return mMuxer->writeSampleData(buffer, trackIndex, timeUs, flags);
}

status_t MediaAppender::setOrientationHint([[maybe_unused]] int degrees) {
    ALOGE("setOrientationHint not supported. Has to be called prior to start on initial muxer");
    return ERROR_UNSUPPORTED;
};

status_t MediaAppender::setLocation([[maybe_unused]] int latit, [[maybe_unused]] int longit) {
    ALOGE("setLocation not supported. Has to be called prior to start on initial muxer");
    return ERROR_UNSUPPORTED;
}

ssize_t MediaAppender::addTrack([[maybe_unused]] const sp<AMessage> &format) {
    ALOGE("addTrack not supported");
    return ERROR_UNSUPPORTED;
}

}  // namespace android
+36 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading