blob: fac2acc56910a40bddffe7ad29f7b3ece820b50f [file] [log] [blame]
ztenghui6df48bf2013-02-07 15:12:10 -08001/*
2 * Copyright (C) 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 "muxer"
19#include <utils/Log.h>
20
21#include <binder/ProcessState.h>
22#include <media/stagefright/foundation/ABuffer.h>
23#include <media/stagefright/foundation/ADebug.h>
24#include <media/stagefright/foundation/ALooper.h>
25#include <media/stagefright/foundation/AMessage.h>
26#include <media/stagefright/foundation/AString.h>
27#include <media/stagefright/DataSource.h>
28#include <media/stagefright/MediaCodec.h>
29#include <media/stagefright/MediaDefs.h>
30#include <media/stagefright/MediaMuxer.h>
31#include <media/stagefright/MetaData.h>
32#include <media/stagefright/NuMediaExtractor.h>
33
34static void usage(const char *me) {
35 fprintf(stderr, "usage: %s [-a] [-v] [-s <trim start time>]"
36 " [-e <trim end time>] [-o <output file>]"
37 " <input video file>\n", me);
38 fprintf(stderr, " -h help\n");
39 fprintf(stderr, " -a use audio\n");
40 fprintf(stderr, " -v use video\n");
41 fprintf(stderr, " -s Time in milli-seconds when the trim should start\n");
42 fprintf(stderr, " -e Time in milli-seconds when the trim should end\n");
43 fprintf(stderr, " -o output file name. Default is /sdcard/muxeroutput.mp4\n");
44
45 exit(1);
46}
47
48using namespace android;
49
50static int muxing(
51 const android::sp<android::ALooper> &looper,
52 const char *path,
53 bool useAudio,
54 bool useVideo,
55 const char *outputFileName,
56 bool enableTrim,
57 int trimStartTimeMs,
58 int trimEndTimeMs) {
59 sp<NuMediaExtractor> extractor = new NuMediaExtractor;
60 if (extractor->setDataSource(path) != OK) {
61 fprintf(stderr, "unable to instantiate extractor. %s\n", path);
62 return 1;
63 }
64
65 if (outputFileName == NULL) {
66 outputFileName = "/sdcard/muxeroutput.mp4";
67 }
68
69 ALOGV("input file %s, output file %s", path, outputFileName);
70 ALOGV("useAudio %d, useVideo %d", useAudio, useVideo);
71
ztenghui3db62df2013-02-22 14:32:59 -080072 sp<MediaMuxer> muxer = new MediaMuxer(outputFileName,
73 MediaMuxer::OUTPUT_FORMAT_MPEG_4);
ztenghui6df48bf2013-02-07 15:12:10 -080074
75 size_t trackCount = extractor->countTracks();
76 // Map the extractor's track index to the muxer's track index.
77 KeyedVector<size_t, ssize_t> trackIndexMap;
78 size_t bufferSize = 1 * 1024 * 1024; // default buffer size is 1MB.
79
80 bool haveAudio = false;
81 bool haveVideo = false;
82
83 int64_t trimStartTimeUs = trimStartTimeMs * 1000;
84 int64_t trimEndTimeUs = trimEndTimeMs * 1000;
85 bool trimStarted = false;
86 int64_t trimOffsetTimeUs = 0;
87
88 for (size_t i = 0; i < trackCount; ++i) {
89 sp<AMessage> format;
90 status_t err = extractor->getTrackFormat(i, &format);
91 CHECK_EQ(err, (status_t)OK);
92 ALOGV("extractor getTrackFormat: %s", format->debugString().c_str());
93
94 AString mime;
95 CHECK(format->findString("mime", &mime));
96
97 bool isAudio = !strncasecmp(mime.c_str(), "audio/", 6);
98 bool isVideo = !strncasecmp(mime.c_str(), "video/", 6);
99
100 if (useAudio && !haveAudio && isAudio) {
101 haveAudio = true;
102 } else if (useVideo && !haveVideo && isVideo) {
103 haveVideo = true;
104 } else {
105 continue;
106 }
107
108 if (isVideo) {
109 int width , height;
110 CHECK(format->findInt32("width", &width));
111 CHECK(format->findInt32("height", &height));
112 bufferSize = width * height * 4; // Assuming it is maximally 4BPP
113 }
114
115 int64_t duration;
116 CHECK(format->findInt64("durationUs", &duration));
117
118 // Since we got the duration now, correct the start time.
119 if (enableTrim) {
120 if (trimStartTimeUs > duration) {
121 fprintf(stderr, "Warning: trimStartTimeUs > duration,"
122 " reset to 0\n");
123 trimStartTimeUs = 0;
124 }
125 }
126
127 ALOGV("selecting track %d", i);
128
129 err = extractor->selectTrack(i);
130 CHECK_EQ(err, (status_t)OK);
131
132 ssize_t newTrackIndex = muxer->addTrack(format);
133 CHECK_GE(newTrackIndex, 0);
134 trackIndexMap.add(i, newTrackIndex);
135 }
136
137 int64_t muxerStartTimeUs = ALooper::GetNowUs();
138
139 bool sawInputEOS = false;
140
141 size_t trackIndex = -1;
142 sp<ABuffer> newBuffer = new ABuffer(bufferSize);
143
144 muxer->start();
145
146 while (!sawInputEOS) {
147 status_t err = extractor->getSampleTrackIndex(&trackIndex);
148 if (err != OK) {
149 ALOGV("saw input eos, err %d", err);
150 sawInputEOS = true;
151 break;
152 } else {
153 err = extractor->readSampleData(newBuffer);
154 CHECK_EQ(err, (status_t)OK);
155
156 int64_t timeUs;
157 err = extractor->getSampleTime(&timeUs);
158 CHECK_EQ(err, (status_t)OK);
159
160 sp<MetaData> meta;
161 err = extractor->getSampleMeta(&meta);
162 CHECK_EQ(err, (status_t)OK);
163
164 uint32_t sampleFlags = 0;
165 int32_t val;
166 if (meta->findInt32(kKeyIsSyncFrame, &val) && val != 0) {
167 // We only support BUFFER_FLAG_SYNCFRAME in the flag for now.
168 sampleFlags |= MediaCodec::BUFFER_FLAG_SYNCFRAME;
169
170 // We turn on trimming at the sync frame.
171 if (enableTrim && timeUs > trimStartTimeUs &&
172 timeUs <= trimEndTimeUs) {
173 if (trimStarted == false) {
174 trimOffsetTimeUs = timeUs;
175 }
176 trimStarted = true;
177 }
178 }
179 // Trim can end at any non-sync frame.
180 if (enableTrim && timeUs > trimEndTimeUs) {
181 trimStarted = false;
182 }
183
184 if (!enableTrim || (enableTrim && trimStarted)) {
185 err = muxer->writeSampleData(newBuffer,
186 trackIndexMap.valueFor(trackIndex),
187 timeUs - trimOffsetTimeUs, sampleFlags);
188 }
189
190 extractor->advance();
191 }
192 }
193
194 muxer->stop();
195 newBuffer.clear();
196 trackIndexMap.clear();
197
198 int64_t elapsedTimeUs = ALooper::GetNowUs() - muxerStartTimeUs;
199 fprintf(stderr, "SUCCESS: muxer generate the video in %lld ms\n",
200 elapsedTimeUs / 1000);
201
202 return 0;
203}
204
205int main(int argc, char **argv) {
206 const char *me = argv[0];
207
208 bool useAudio = false;
209 bool useVideo = false;
210 char *outputFileName = NULL;
211 int trimStartTimeMs = -1;
212 int trimEndTimeMs = -1;
213 // When trimStartTimeMs and trimEndTimeMs seems valid, we turn this switch
214 // to true.
215 bool enableTrim = false;
216
217 int res;
218 while ((res = getopt(argc, argv, "h?avo:s:e:")) >= 0) {
219 switch (res) {
220 case 'a':
221 {
222 useAudio = true;
223 break;
224 }
225
226 case 'v':
227 {
228 useVideo = true;
229 break;
230 }
231
232 case 'o':
233 {
234 outputFileName = optarg;
235 break;
236 }
237
238 case 's':
239 {
240 trimStartTimeMs = atoi(optarg);
241 break;
242 }
243
244 case 'e':
245 {
246 trimEndTimeMs = atoi(optarg);
247 break;
248 }
249
250 case '?':
251 case 'h':
252 default:
253 {
254 usage(me);
255 }
256 }
257 }
258
259 argc -= optind;
260 argv += optind;
261
262 if (argc != 1) {
263 usage(me);
264 }
265
266 if (trimStartTimeMs < 0 || trimEndTimeMs < 0) {
267 // If no input on either 's' or 'e', or they are obviously wrong input,
268 // then turn off trimming.
269 ALOGV("Trimming is disabled, copying the whole length video.");
270 enableTrim = false;
271 } else if (trimStartTimeMs > trimEndTimeMs) {
272 fprintf(stderr, "ERROR: start time is bigger\n");
273 return 1;
274 } else {
275 enableTrim = true;
276 }
277
278 if (!useAudio && !useVideo) {
279 fprintf(stderr, "ERROR: Missing both -a and -v, no track to mux then.\n");
280 return 1;
281 }
282 ProcessState::self()->startThreadPool();
283
284 // Make sure setDataSource() works.
285 DataSource::RegisterDefaultSniffers();
286
287 sp<ALooper> looper = new ALooper;
288 looper->start();
289
290 int result = muxing(looper, argv[0], useAudio, useVideo, outputFileName,
291 enableTrim, trimStartTimeMs, trimEndTimeMs);
292
293 looper->stop();
294
295 return result;
296}