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
36namespace android {
37
38MediaMuxer::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
48MediaMuxer::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
57MediaMuxer::~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
66ssize_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
90status_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
106status_t MediaMuxer::start() {
107    Mutex::Autolock autoLock(mMuxerLock);
108    if (mState == INITIALIZED) {
109        mState = STARTED;
110        mFileMeta->setInt32(kKeyRealTimeRecording, false);
111        return mWriter->start(mFileMeta.get());
112    } else {
113        ALOGE("start() is called in invalid state %d", mState);
114        return INVALID_OPERATION;
115    }
116}
117
118status_t MediaMuxer::stop() {
119    Mutex::Autolock autoLock(mMuxerLock);
120
121    if (mState == STARTED) {
122        mState = STOPPED;
123        for (size_t i = 0; i < mTrackList.size(); i++) {
124            if (mTrackList[i]->stop() != OK) {
125                return INVALID_OPERATION;
126            }
127        }
128        return mWriter->stop();
129    } else {
130        ALOGE("stop() is called in invalid state %d", mState);
131        return INVALID_OPERATION;
132    }
133}
134
135status_t MediaMuxer::writeSampleData(const sp<ABuffer> &buffer, size_t trackIndex,
136                                     int64_t timeUs, uint32_t flags) {
137    Mutex::Autolock autoLock(mMuxerLock);
138
139    if (buffer.get() == NULL) {
140        ALOGE("WriteSampleData() get an NULL buffer.");
141        return -EINVAL;
142    }
143
144    if (mState != STARTED) {
145        ALOGE("WriteSampleData() is called in invalid state %d", mState);
146        return INVALID_OPERATION;
147    }
148
149    if (trackIndex >= mTrackList.size()) {
150        ALOGE("WriteSampleData() get an invalid index %d", trackIndex);
151        return -EINVAL;
152    }
153
154    MediaBuffer* mediaBuffer = new MediaBuffer(buffer);
155
156    mediaBuffer->add_ref(); // Released in MediaAdapter::signalBufferReturned().
157    mediaBuffer->set_range(buffer->offset(), buffer->size());
158
159    sp<MetaData> sampleMetaData = mediaBuffer->meta_data();
160    sampleMetaData->setInt64(kKeyTime, timeUs);
161    // Just set the kKeyDecodingTime as the presentation time for now.
162    sampleMetaData->setInt64(kKeyDecodingTime, timeUs);
163
164    if (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME) {
165        sampleMetaData->setInt32(kKeyIsSyncFrame, true);
166    }
167
168    sp<MediaAdapter> currentTrack = mTrackList[trackIndex];
169    // This pushBuffer will wait until the mediaBuffer is consumed.
170    return currentTrack->pushBuffer(mediaBuffer);
171}
172
173}  // namespace android
174