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
20#include "webm/WebmWriter.h"
21
22#include <utils/Log.h>
23
24#include <media/stagefright/MediaMuxer.h>
25
26#include <media/stagefright/foundation/ABuffer.h>
27#include <media/stagefright/foundation/ADebug.h>
28#include <media/stagefright/foundation/AMessage.h>
29#include <media/stagefright/MediaAdapter.h>
30#include <media/stagefright/MediaBuffer.h>
31#include <media/stagefright/MediaCodec.h>
32#include <media/stagefright/MediaDefs.h>
33#include <media/stagefright/MediaErrors.h>
34#include <media/stagefright/MediaSource.h>
35#include <media/stagefright/MetaData.h>
36#include <media/stagefright/MPEG4Writer.h>
37#include <media/stagefright/Utils.h>
38
39namespace android {
40
41MediaMuxer::MediaMuxer(const char *path, OutputFormat format)
42    : mFormat(format),
43      mState(UNINITIALIZED) {
44    if (format == OUTPUT_FORMAT_MPEG_4) {
45        mWriter = new MPEG4Writer(path);
46    } else if (format == OUTPUT_FORMAT_WEBM) {
47        mWriter = new WebmWriter(path);
48    }
49
50    if (mWriter != NULL) {
51        mFileMeta = new MetaData;
52        mState = INITIALIZED;
53    }
54}
55
56MediaMuxer::MediaMuxer(int fd, OutputFormat format)
57    : mFormat(format),
58      mState(UNINITIALIZED) {
59    if (format == OUTPUT_FORMAT_MPEG_4) {
60        mWriter = new MPEG4Writer(fd);
61    } else if (format == OUTPUT_FORMAT_WEBM) {
62        mWriter = new WebmWriter(fd);
63    }
64
65    if (mWriter != NULL) {
66        mFileMeta = new MetaData;
67        mState = INITIALIZED;
68    }
69}
70
71MediaMuxer::~MediaMuxer() {
72    Mutex::Autolock autoLock(mMuxerLock);
73
74    // Clean up all the internal resources.
75    mFileMeta.clear();
76    mWriter.clear();
77    mTrackList.clear();
78}
79
80ssize_t MediaMuxer::addTrack(const sp<AMessage> &format) {
81    Mutex::Autolock autoLock(mMuxerLock);
82
83    if (format.get() == NULL) {
84        ALOGE("addTrack() get a null format");
85        return -EINVAL;
86    }
87
88    if (mState != INITIALIZED) {
89        ALOGE("addTrack() must be called after constructor and before start().");
90        return INVALID_OPERATION;
91    }
92
93    sp<MetaData> trackMeta = new MetaData;
94    convertMessageToMetaData(format, trackMeta);
95
96    sp<MediaAdapter> newTrack = new MediaAdapter(trackMeta);
97    status_t result = mWriter->addSource(newTrack);
98    if (result == OK) {
99        return mTrackList.add(newTrack);
100    }
101    return -1;
102}
103
104status_t MediaMuxer::setOrientationHint(int degrees) {
105    Mutex::Autolock autoLock(mMuxerLock);
106    if (mState != INITIALIZED) {
107        ALOGE("setOrientationHint() must be called before start().");
108        return INVALID_OPERATION;
109    }
110
111    if (degrees != 0 && degrees != 90 && degrees != 180 && degrees != 270) {
112        ALOGE("setOrientationHint() get invalid degrees");
113        return -EINVAL;
114    }
115
116    mFileMeta->setInt32(kKeyRotation, degrees);
117    return OK;
118}
119
120status_t MediaMuxer::setLocation(int latitude, int longitude) {
121    Mutex::Autolock autoLock(mMuxerLock);
122    if (mState != INITIALIZED) {
123        ALOGE("setLocation() must be called before start().");
124        return INVALID_OPERATION;
125    }
126    if (mFormat != OUTPUT_FORMAT_MPEG_4) {
127        ALOGE("setLocation() is only supported for .mp4 output.");
128        return INVALID_OPERATION;
129    }
130
131    ALOGV("Setting location: latitude = %d, longitude = %d", latitude, longitude);
132    return static_cast<MPEG4Writer*>(mWriter.get())->setGeoData(latitude, longitude);
133}
134
135status_t MediaMuxer::start() {
136    Mutex::Autolock autoLock(mMuxerLock);
137    if (mState == INITIALIZED) {
138        mState = STARTED;
139        mFileMeta->setInt32(kKeyRealTimeRecording, false);
140        return mWriter->start(mFileMeta.get());
141    } else {
142        ALOGE("start() is called in invalid state %d", mState);
143        return INVALID_OPERATION;
144    }
145}
146
147status_t MediaMuxer::stop() {
148    Mutex::Autolock autoLock(mMuxerLock);
149
150    if (mState == STARTED) {
151        mState = STOPPED;
152        for (size_t i = 0; i < mTrackList.size(); i++) {
153            if (mTrackList[i]->stop() != OK) {
154                return INVALID_OPERATION;
155            }
156        }
157        return mWriter->stop();
158    } else {
159        ALOGE("stop() is called in invalid state %d", mState);
160        return INVALID_OPERATION;
161    }
162}
163
164status_t MediaMuxer::writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
165                                     int64_t timeUs, uint32_t flags) {
166    Mutex::Autolock autoLock(mMuxerLock);
167
168    if (buffer.get() == NULL) {
169        ALOGE("WriteSampleData() get an NULL buffer.");
170        return -EINVAL;
171    }
172
173    if (mState != STARTED) {
174        ALOGE("WriteSampleData() is called in invalid state %d", mState);
175        return INVALID_OPERATION;
176    }
177
178    if (trackIndex >= mTrackList.size()) {
179        ALOGE("WriteSampleData() get an invalid index %zu", trackIndex);
180        return -EINVAL;
181    }
182
183    MediaBuffer* mediaBuffer = new MediaBuffer(buffer);
184
185    mediaBuffer->add_ref(); // Released in MediaAdapter::signalBufferReturned().
186    mediaBuffer->set_range(buffer->offset(), buffer->size());
187
188    sp<MetaData> sampleMetaData = mediaBuffer->meta_data();
189    sampleMetaData->setInt64(kKeyTime, timeUs);
190    // Just set the kKeyDecodingTime as the presentation time for now.
191    sampleMetaData->setInt64(kKeyDecodingTime, timeUs);
192
193    if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) {
194        sampleMetaData->setInt32(kKeyIsSyncFrame, true);
195    }
196
197    sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
198    // This pushBuffer will wait until the mediaBuffer is consumed.
199    return currentTrack->pushBuffer(mediaBuffer);
200}
201
202}  // namespace android
203