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