OSDN Git Service

Add the native MediaMuxer support.
authorztenghui <ztenghui@google.com>
Mon, 4 Feb 2013 23:59:38 +0000 (15:59 -0800)
committerztenghui <ztenghui@google.com>
Tue, 12 Feb 2013 19:10:12 +0000 (11:10 -0800)
MediaAdapter: a helper class to convert the push model to pull model.
MediaMuxer: the real muxer.

bug:7991013

Change-Id: If3b79551bc6332bc81f5c2740885e579a5c4abf9

include/media/stagefright/MediaAdapter.h [new file with mode: 0644]
include/media/stagefright/MediaMuxer.h [new file with mode: 0644]
media/libstagefright/Android.mk
media/libstagefright/MediaAdapter.cpp [new file with mode: 0644]
media/libstagefright/MediaMuxer.cpp [new file with mode: 0644]

diff --git a/include/media/stagefright/MediaAdapter.h b/include/media/stagefright/MediaAdapter.h
new file mode 100644 (file)
index 0000000..369fce6
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef MEDIA_ADAPTER_H
+#define MEDIA_ADAPTER_H
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/threads.h>
+
+namespace android {
+
+// Convert the MediaMuxer's push model into MPEG4Writer's pull model.
+// Used only by the MediaMuxer for now.
+struct MediaAdapter : public MediaSource, public MediaBufferObserver {
+public:
+    // MetaData is used to set the format and returned at getFormat.
+    MediaAdapter(const sp<MetaData> &meta);
+    virtual ~MediaAdapter();
+    /////////////////////////////////////////////////
+    // Inherited functions from MediaSource
+    /////////////////////////////////////////////////
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+    virtual sp<MetaData> getFormat();
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+    /////////////////////////////////////////////////
+    // Inherited functions from MediaBufferObserver
+    /////////////////////////////////////////////////
+
+    virtual void signalBufferReturned(MediaBuffer *buffer);
+
+    /////////////////////////////////////////////////
+    // Non-inherited functions:
+    /////////////////////////////////////////////////
+
+    // pushBuffer() will wait for the read() finish, and read() will have a
+    // deep copy, such that after pushBuffer return, the buffer can be re-used.
+    status_t pushBuffer(MediaBuffer *buffer);
+
+private:
+    Mutex mAdapterLock;
+    // Make sure the read() wait for the incoming buffer.
+    Condition mBufferReadCond;
+    // Make sure the pushBuffer() wait for the current buffer consumed.
+    Condition mBufferReturnedCond;
+
+    MediaBuffer *mCurrentMediaBuffer;
+
+    bool mStarted;
+    sp<MetaData> mOutputFormat;
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaAdapter);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_ADAPTER_H
diff --git a/include/media/stagefright/MediaMuxer.h b/include/media/stagefright/MediaMuxer.h
new file mode 100644 (file)
index 0000000..27a141e
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2013, 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.
+ */
+
+#ifndef MEDIA_MUXER_H_
+#define MEDIA_MUXER_H_
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+struct MediaAdapter;
+struct MediaBuffer;
+struct MediaSource;
+struct MetaData;
+struct MPEG4Writer;
+
+// MediaMuxer is used to mux multiple tracks into a video. Currently, we only
+// support a mp4 file as the output.
+// The expected calling order of the functions is:
+// Constructor -> addTrack+ -> start -> writeSampleData+ -> stop
+// If muxing operation need to be cancelled, the app is responsible for
+// deleting the output file after stop.
+struct MediaMuxer : public RefBase {
+public:
+    // Construct the muxer with the output file path.
+    MediaMuxer(const char* pathOut);
+    // Construct the muxer with the file descriptor. Note that the MediaMuxer
+    // will close this file at stop().
+    MediaMuxer(int fd);
+
+    virtual ~MediaMuxer();
+
+    /**
+     * Add a track with its format information. This should be
+     * called before start().
+     * @param format the track's format.
+     * @return the track's index or negative number if error.
+     */
+    ssize_t addTrack(const sp<AMessage> &format);
+
+    /**
+     * Start muxing. Make sure all the tracks have been added before
+     * calling this.
+     */
+    status_t start();
+
+    /**
+     * Stop muxing.
+     * This method is a blocking call. Depending on how
+     * much data is bufferred internally, the time needed for stopping
+     * the muxer may be time consuming. UI thread is
+     * not recommended for launching this call.
+     */
+    status_t stop();
+
+    /**
+     * Send a sample buffer for muxing.
+     * The buffer can be reused once this method returns. Typically,
+     * this function won't be blocked for very long, and thus there
+     * is no need to use a separate thread calling this method to
+     * push a buffer.
+     * @param buffer the incoming sample buffer.
+     * @param trackIndex the buffer's track index number.
+     * @param timeUs the buffer's time stamp.
+     * @param flags the only supported flag for now is
+     *              MediaCodec::BUFFER_FLAG_SYNCFRAME.
+     * @return OK if no error.
+     */
+    status_t writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
+                             int64_t timeUs, uint32_t flags) ;
+
+private:
+    sp<MPEG4Writer> mWriter;
+    Vector< sp<MediaAdapter> > mTrackList;  // Each track has its MediaAdapter.
+
+    Mutex mMuxerLock;
+
+    enum State {
+        INITED,
+        STARTED,
+        STOPPED
+    };
+    State mState;
+
+    DISALLOW_EVIL_CONSTRUCTORS(MediaMuxer);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_MUXER_H_
+
index 85662db..6934e59 100644 (file)
@@ -26,12 +26,14 @@ LOCAL_SRC_FILES:=                         \
         MPEG2TSWriter.cpp                 \
         MPEG4Extractor.cpp                \
         MPEG4Writer.cpp                   \
+        MediaAdapter.cpp                  \
         MediaBuffer.cpp                   \
         MediaBufferGroup.cpp              \
         MediaCodec.cpp                    \
         MediaCodecList.cpp                \
         MediaDefs.cpp                     \
         MediaExtractor.cpp                \
+        MediaMuxer.cpp                    \
         MediaSource.cpp                   \
         MetaData.cpp                      \
         NuCachedSource2.cpp               \
diff --git a/media/libstagefright/MediaAdapter.cpp b/media/libstagefright/MediaAdapter.cpp
new file mode 100644 (file)
index 0000000..2484212
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 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 "MediaAdapter"
+#include <utils/Log.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaAdapter.h>
+#include <media/stagefright/MediaBuffer.h>
+
+namespace android {
+
+MediaAdapter::MediaAdapter(const sp<MetaData> &meta)
+    : mCurrentMediaBuffer(NULL),
+      mStarted(false),
+      mOutputFormat(meta) {
+}
+
+MediaAdapter::~MediaAdapter() {
+    Mutex::Autolock autoLock(mAdapterLock);
+    mOutputFormat.clear();
+    CHECK(mCurrentMediaBuffer == NULL);
+}
+
+status_t MediaAdapter::start(MetaData *params) {
+    Mutex::Autolock autoLock(mAdapterLock);
+    if (!mStarted) {
+        mStarted = true;
+    }
+    return OK;
+}
+
+status_t MediaAdapter::stop() {
+    Mutex::Autolock autoLock(mAdapterLock);
+    if (mStarted) {
+        mStarted = false;
+        // If stop() happens immediately after a pushBuffer(), we should
+        // clean up the mCurrentMediaBuffer
+        if (mCurrentMediaBuffer != NULL) {
+            mCurrentMediaBuffer->release();
+            mCurrentMediaBuffer = NULL;
+        }
+        // While read() is still waiting, we should signal it to finish.
+        mBufferReadCond.signal();
+    }
+    return OK;
+}
+
+sp<MetaData> MediaAdapter::getFormat() {
+    Mutex::Autolock autoLock(mAdapterLock);
+    return mOutputFormat;
+}
+
+void MediaAdapter::signalBufferReturned(MediaBuffer *buffer) {
+    Mutex::Autolock autoLock(mAdapterLock);
+    CHECK(buffer != NULL);
+    buffer->setObserver(0);
+    buffer->release();
+    ALOGV("buffer returned %p", buffer);
+    mBufferReturnedCond.signal();
+}
+
+status_t MediaAdapter::read(
+            MediaBuffer **buffer, const ReadOptions *options) {
+    Mutex::Autolock autoLock(mAdapterLock);
+    if (!mStarted) {
+        ALOGV("Read before even started!");
+        return ERROR_END_OF_STREAM;
+    }
+
+    while (mCurrentMediaBuffer == NULL && mStarted) {
+        ALOGV("waiting @ read()");
+        mBufferReadCond.wait(mAdapterLock);
+    }
+
+    if (!mStarted) {
+        ALOGV("read interrupted after stop");
+        CHECK(mCurrentMediaBuffer == NULL);
+        return ERROR_END_OF_STREAM;
+    }
+
+    CHECK(mCurrentMediaBuffer != NULL);
+
+    *buffer = mCurrentMediaBuffer;
+    mCurrentMediaBuffer = NULL;
+    (*buffer)->setObserver(this);
+
+    return OK;
+}
+
+status_t MediaAdapter::pushBuffer(MediaBuffer *buffer) {
+    if (buffer == NULL) {
+        ALOGE("pushBuffer get an NULL buffer");
+        return -EINVAL;
+    }
+
+    Mutex::Autolock autoLock(mAdapterLock);
+    if (!mStarted) {
+        ALOGE("pushBuffer called before start");
+        return INVALID_OPERATION;
+    }
+    mCurrentMediaBuffer = buffer;
+    mBufferReadCond.signal();
+
+    ALOGV("wait for the buffer returned @ pushBuffer! %p", buffer);
+    mBufferReturnedCond.wait(mAdapterLock);
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
new file mode 100644 (file)
index 0000000..30bed90
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2013, 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 "MediaMuxer"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaMuxer.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaAdapter.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+MediaMuxer::MediaMuxer(const char* pathOut)
+    : mState(INITED) {
+    mWriter = new MPEG4Writer(pathOut);
+}
+
+MediaMuxer::MediaMuxer(int fd)
+    : mState(INITED) {
+    mWriter = new MPEG4Writer(fd);
+}
+
+MediaMuxer::~MediaMuxer() {
+    Mutex::Autolock autoLock(mMuxerLock);
+
+    // Clean up all the internal resources.
+    mWriter.clear();
+    mTrackList.clear();
+}
+
+ssize_t MediaMuxer::addTrack(const sp<AMessage> &format) {
+    Mutex::Autolock autoLock(mMuxerLock);
+
+    if (format.get() == NULL) {
+        ALOGE("addTrack() get a null format");
+        return -EINVAL;
+    }
+
+    if (mState != INITED) {
+        ALOGE("addTrack() must be called after constructor and before start().");
+        return INVALID_OPERATION;
+    }
+
+    sp<MetaData> meta = new MetaData;
+    convertMessageToMetaData(format, meta);
+
+    sp<MediaAdapter> newTrack = new MediaAdapter(meta);
+    return mTrackList.add(newTrack);
+}
+
+status_t MediaMuxer::start() {
+    Mutex::Autolock autoLock(mMuxerLock);
+
+    if (mState == INITED) {
+        mState = STARTED;
+        for (size_t i = 0 ; i < mTrackList.size(); i++) {
+            mWriter->addSource(mTrackList[i]);
+        }
+        return mWriter->start();
+    } else {
+        ALOGE("start() is called in invalid state %d", mState);
+        return INVALID_OPERATION;
+    }
+}
+
+status_t MediaMuxer::stop() {
+    Mutex::Autolock autoLock(mMuxerLock);
+
+    if (mState == STARTED) {
+        mState = STOPPED;
+        for (size_t i = 0; i < mTrackList.size(); i++) {
+            mTrackList[i]->stop();
+        }
+        return mWriter->stop();
+    } else {
+        ALOGE("stop() is called in invalid state %d", mState);
+        return INVALID_OPERATION;
+    }
+}
+
+status_t MediaMuxer::writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
+                                     int64_t timeUs, uint32_t flags) {
+    Mutex::Autolock autoLock(mMuxerLock);
+
+    sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
+
+    if (buffer.get() == NULL) {
+        ALOGE("WriteSampleData() get an NULL buffer.");
+        return -EINVAL;
+    }
+
+    if (mState != STARTED) {
+        ALOGE("WriteSampleData() is called in invalid state %d", mState);
+        return INVALID_OPERATION;
+    }
+
+    if (trackIndex >= mTrackList.size()) {
+        ALOGE("WriteSampleData() get an invalid index %d", trackIndex);
+        return -EINVAL;
+    }
+
+    MediaBuffer* mediaBuffer = new MediaBuffer(buffer);
+
+    mediaBuffer->add_ref(); // Released in MediaAdapter::signalBufferReturned().
+    mediaBuffer->set_range(buffer->offset(), buffer->size());
+
+    sp<MetaData> metaData = mediaBuffer->meta_data();
+    metaData->setInt64(kKeyTime, timeUs);
+    // Just set the kKeyDecodingTime as the presentation time for now.
+    metaData->setInt64(kKeyDecodingTime, timeUs);
+
+    if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) {
+        metaData->setInt32(kKeyIsSyncFrame, true);
+    }
+
+    // This pushBuffer will wait until the mediaBuffer is consumed.
+    return currentTrack->pushBuffer(mediaBuffer);
+}
+
+}  // namespace android