From 4f1732b8068970b368a89271158ca29daf25650e Mon Sep 17 00:00:00 2001 From: ztenghui Date: Mon, 4 Feb 2013 15:59:38 -0800 Subject: Add the native MediaMuxer support. 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 | 76 ++++++++++++++++ include/media/stagefright/MediaMuxer.h | 109 +++++++++++++++++++++++ media/libstagefright/Android.mk | 2 + media/libstagefright/MediaAdapter.cpp | 126 +++++++++++++++++++++++++++ media/libstagefright/MediaMuxer.cpp | 145 +++++++++++++++++++++++++++++++ 5 files changed, 458 insertions(+) create mode 100644 include/media/stagefright/MediaAdapter.h create mode 100644 include/media/stagefright/MediaMuxer.h create mode 100644 media/libstagefright/MediaAdapter.cpp create mode 100644 media/libstagefright/MediaMuxer.cpp diff --git a/include/media/stagefright/MediaAdapter.h b/include/media/stagefright/MediaAdapter.h new file mode 100644 index 0000000..369fce6 --- /dev/null +++ b/include/media/stagefright/MediaAdapter.h @@ -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 +#include +#include +#include +#include + +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 &meta); + virtual ~MediaAdapter(); + ///////////////////////////////////////////////// + // Inherited functions from MediaSource + ///////////////////////////////////////////////// + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp 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 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 index 0000000..27a141e --- /dev/null +++ b/include/media/stagefright/MediaMuxer.h @@ -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 +#include +#include +#include + +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 &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 &buffer, size_t trackIndex, + int64_t timeUs, uint32_t flags) ; + +private: + sp mWriter; + Vector< sp > 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_ + diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 85662db..6934e59 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -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 index 0000000..2484212 --- /dev/null +++ b/media/libstagefright/MediaAdapter.cpp @@ -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 + +#include +#include +#include + +namespace android { + +MediaAdapter::MediaAdapter(const sp &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 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 index 0000000..30bed90 --- /dev/null +++ b/media/libstagefright/MediaMuxer.cpp @@ -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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 &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 meta = new MetaData; + convertMessageToMetaData(format, meta); + + sp 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 &buffer, size_t trackIndex, + int64_t timeUs, uint32_t flags) { + Mutex::Autolock autoLock(mMuxerLock); + + sp 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 = 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 -- cgit v1.1