muxer.cpp revision 3db62dfc5102247d415df4667bd9609e669fc022
1/* 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 72 sp<MediaMuxer> muxer = new MediaMuxer(outputFileName, 73 MediaMuxer::OUTPUT_FORMAT_MPEG_4); 74 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} 297