1 /* 2 * Copyright 2013, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //#define LOG_NDEBUG 0 18 #define LOG_TAG "MediaMuxer" 19 #include <utils/Log.h> 20 21 #include <media/stagefright/MediaMuxer.h> 22 23 #include <media/stagefright/foundation/ABuffer.h> 24 #include <media/stagefright/foundation/ADebug.h> 25 #include <media/stagefright/foundation/AMessage.h> 26 #include <media/stagefright/MediaAdapter.h> 27 #include <media/stagefright/MediaBuffer.h> 28 #include <media/stagefright/MediaCodec.h> 29 #include <media/stagefright/MediaDefs.h> 30 #include <media/stagefright/MediaErrors.h> 31 #include <media/stagefright/MediaSource.h> 32 #include <media/stagefright/MetaData.h> 33 #include <media/stagefright/MPEG4Writer.h> 34 #include <media/stagefright/Utils.h> 35 36 namespace android { 37 38 MediaMuxer::MediaMuxer(const char *path, OutputFormat format) 39 : mState(UNINITIALIZED) { 40 if (format == OUTPUT_FORMAT_MPEG_4) { 41 mWriter = new MPEG4Writer(path); 42 mFileMeta = new MetaData; 43 mState = INITIALIZED; 44 } 45 46 } 47 48 MediaMuxer::MediaMuxer(int fd, OutputFormat format) 49 : mState(UNINITIALIZED) { 50 if (format == OUTPUT_FORMAT_MPEG_4) { 51 mWriter = new MPEG4Writer(fd); 52 mFileMeta = new MetaData; 53 mState = INITIALIZED; 54 } 55 } 56 57 MediaMuxer::~MediaMuxer() { 58 Mutex::Autolock autoLock(mMuxerLock); 59 60 // Clean up all the internal resources. 61 mFileMeta.clear(); 62 mWriter.clear(); 63 mTrackList.clear(); 64 } 65 66 ssize_t MediaMuxer::addTrack(const sp<AMessage> &format) { 67 Mutex::Autolock autoLock(mMuxerLock); 68 69 if (format.get() == NULL) { 70 ALOGE("addTrack() get a null format"); 71 return -EINVAL; 72 } 73 74 if (mState != INITIALIZED) { 75 ALOGE("addTrack() must be called after constructor and before start()."); 76 return INVALID_OPERATION; 77 } 78 79 sp<MetaData> trackMeta = new MetaData; 80 convertMessageToMetaData(format, trackMeta); 81 82 sp<MediaAdapter> newTrack = new MediaAdapter(trackMeta); 83 status_t result = mWriter->addSource(newTrack); 84 if (result == OK) { 85 return mTrackList.add(newTrack); 86 } 87 return -1; 88 } 89 90 status_t MediaMuxer::setOrientationHint(int degrees) { 91 Mutex::Autolock autoLock(mMuxerLock); 92 if (mState != INITIALIZED) { 93 ALOGE("setOrientationHint() must be called before start()."); 94 return INVALID_OPERATION; 95 } 96 97 if (degrees != 0 && degrees != 90 && degrees != 180 && degrees != 270) { 98 ALOGE("setOrientationHint() get invalid degrees"); 99 return -EINVAL; 100 } 101 102 mFileMeta->setInt32(kKeyRotation, degrees); 103 return OK; 104 } 105 106 status_t MediaMuxer::setLocation(int latitude, int longitude) { 107 Mutex::Autolock autoLock(mMuxerLock); 108 if (mState != INITIALIZED) { 109 ALOGE("setLocation() must be called before start()."); 110 return INVALID_OPERATION; 111 } 112 ALOGV("Setting location: latitude = %d, longitude = %d", latitude, longitude); 113 return mWriter->setGeoData(latitude, longitude); 114 } 115 116 status_t MediaMuxer::start() { 117 Mutex::Autolock autoLock(mMuxerLock); 118 if (mState == INITIALIZED) { 119 mState = STARTED; 120 mFileMeta->setInt32(kKeyRealTimeRecording, false); 121 return mWriter->start(mFileMeta.get()); 122 } else { 123 ALOGE("start() is called in invalid state %d", mState); 124 return INVALID_OPERATION; 125 } 126 } 127 128 status_t MediaMuxer::stop() { 129 Mutex::Autolock autoLock(mMuxerLock); 130 131 if (mState == STARTED) { 132 mState = STOPPED; 133 for (size_t i = 0; i < mTrackList.size(); i++) { 134 if (mTrackList[i]->stop() != OK) { 135 return INVALID_OPERATION; 136 } 137 } 138 return mWriter->stop(); 139 } else { 140 ALOGE("stop() is called in invalid state %d", mState); 141 return INVALID_OPERATION; 142 } 143 } 144 145 status_t MediaMuxer::writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex, 146 int64_t timeUs, uint32_t flags) { 147 Mutex::Autolock autoLock(mMuxerLock); 148 149 if (buffer.get() == NULL) { 150 ALOGE("WriteSampleData() get an NULL buffer."); 151 return -EINVAL; 152 } 153 154 if (mState != STARTED) { 155 ALOGE("WriteSampleData() is called in invalid state %d", mState); 156 return INVALID_OPERATION; 157 } 158 159 if (trackIndex >= mTrackList.size()) { 160 ALOGE("WriteSampleData() get an invalid index %d", trackIndex); 161 return -EINVAL; 162 } 163 164 MediaBuffer* mediaBuffer = new MediaBuffer(buffer); 165 166 mediaBuffer->add_ref(); // Released in MediaAdapter::signalBufferReturned(). 167 mediaBuffer->set_range(buffer->offset(), buffer->size()); 168 169 sp<MetaData> sampleMetaData = mediaBuffer->meta_data(); 170 sampleMetaData->setInt64(kKeyTime, timeUs); 171 // Just set the kKeyDecodingTime as the presentation time for now. 172 sampleMetaData->setInt64(kKeyDecodingTime, timeUs); 173 174 if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) { 175 sampleMetaData->setInt32(kKeyIsSyncFrame, true); 176 } 177 178 sp<MediaAdapter> currentTrack = mTrackList[trackIndex]; 179 // This pushBuffer will wait until the mediaBuffer is consumed. 180 return currentTrack->pushBuffer(mediaBuffer); 181 } 182 183 } // namespace android 184