blob: 579d8c31672635081eeb8bd236212073e05e7c42 [file] [log] [blame]
Andreas Hubere46b7be2009-07-14 16:56:47 -07001/*
2 * Copyright (C) 2009 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
Andreas Huberb5185272010-09-23 15:01:30 -070017//#define LOG_NDEBUG 0
18#define LOG_TAG "stagefright"
19#include <media/stagefright/foundation/ADebug.h>
20
Andreas Hubere46b7be2009-07-14 16:56:47 -070021#include <sys/time.h>
22
Andreas Hubere46b7be2009-07-14 16:56:47 -070023#include <stdlib.h>
Andreas Huber09daada2009-08-18 11:27:32 -070024#include <string.h>
25#include <unistd.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070026
Andreas Huber662292a2010-04-13 09:58:39 -070027#include "SineSource.h"
28
Andreas Hubere46b7be2009-07-14 16:56:47 -070029#include <binder/IServiceManager.h>
30#include <binder/ProcessState.h>
31#include <media/IMediaPlayerService.h>
Andreas Huber4e4173b2010-07-22 09:20:13 -070032#include <media/stagefright/foundation/ALooper.h>
33#include "include/ARTSPController.h"
Andreas Huber3ac01192010-09-24 09:35:49 -070034#include "include/LiveSource.h"
35#include "include/NuCachedSource2.h"
Andreas Huber662292a2010-04-13 09:58:39 -070036#include <media/stagefright/AudioPlayer.h>
Andreas Huberc0668842010-06-10 11:16:10 -070037#include <media/stagefright/DataSource.h>
Andreas Huber6026a512009-09-11 07:47:55 -070038#include <media/stagefright/JPEGSource.h>
Andreas Hubere6c40962009-09-10 14:13:30 -070039#include <media/stagefright/MediaDefs.h>
Andreas Huberc0668842010-06-10 11:16:10 -070040#include <media/stagefright/MediaErrors.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070041#include <media/stagefright/MediaExtractor.h>
42#include <media/stagefright/MediaSource.h>
43#include <media/stagefright/MetaData.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070044#include <media/stagefright/OMXClient.h>
Andreas Huberbe06d262009-08-14 14:37:10 -070045#include <media/stagefright/OMXCodec.h>
Andreas Hubere42fdb52010-01-14 11:31:22 -080046#include <media/mediametadataretriever.h>
Andreas Hubere46b7be2009-07-14 16:56:47 -070047
Andreas Huber45bac572010-07-01 08:19:52 -070048#include <media/stagefright/foundation/hexdump.h>
Andreas Huberc751ecc2010-09-27 12:04:43 -070049#include <media/stagefright/MPEG2TSWriter.h>
Andreas Huber45bac572010-07-01 08:19:52 -070050#include <media/stagefright/MPEG4Writer.h>
51
Andreas Huberb5185272010-09-23 15:01:30 -070052#include <fcntl.h>
53
Andreas Hubere46b7be2009-07-14 16:56:47 -070054using namespace android;
55
Andreas Huber09daada2009-08-18 11:27:32 -070056static long gNumRepetitions;
Andreas Huber557c6ce2009-08-26 10:11:50 -070057static long gMaxNumFrames; // 0 means decode all available.
Andreas Huber0fb64772009-08-27 12:10:02 -070058static long gReproduceBug; // if not -1.
Andreas Hubere42fdb52010-01-14 11:31:22 -080059static bool gPreferSoftwareCodec;
Andreas Huber662292a2010-04-13 09:58:39 -070060static bool gPlaybackAudio;
Andreas Huber45bac572010-07-01 08:19:52 -070061static bool gWriteMP4;
Andreas Huber93e496f2010-11-09 11:16:03 -080062static bool gDisplayHistogram;
Andreas Huber45bac572010-07-01 08:19:52 -070063static String8 gWriteMP4Filename;
Andreas Hubere46b7be2009-07-14 16:56:47 -070064
65static int64_t getNowUs() {
66 struct timeval tv;
67 gettimeofday(&tv, NULL);
68
Andreas Huberd73a81c2010-03-16 14:34:17 -070069 return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
Andreas Hubere46b7be2009-07-14 16:56:47 -070070}
71
Andreas Huber93e496f2010-11-09 11:16:03 -080072static int CompareIncreasing(const int64_t *a, const int64_t *b) {
73 return (*a) < (*b) ? -1 : (*a) > (*b) ? 1 : 0;
74}
75
76static void displayDecodeHistogram(Vector<int64_t> *decodeTimesUs) {
77 printf("decode times:\n");
78
79 decodeTimesUs->sort(CompareIncreasing);
80
81 size_t n = decodeTimesUs->size();
82 int64_t minUs = decodeTimesUs->itemAt(0);
83 int64_t maxUs = decodeTimesUs->itemAt(n - 1);
84
85 printf("min decode time %lld us (%.2f secs)\n", minUs, minUs / 1E6);
86 printf("max decode time %lld us (%.2f secs)\n", maxUs, maxUs / 1E6);
87
88 size_t counts[100];
89 for (size_t i = 0; i < 100; ++i) {
90 counts[i] = 0;
91 }
92
93 for (size_t i = 0; i < n; ++i) {
94 int64_t x = decodeTimesUs->itemAt(i);
95
96 size_t slot = ((x - minUs) * 100) / (maxUs - minUs);
97 if (slot == 100) { slot = 99; }
98
99 ++counts[slot];
100 }
101
102 for (size_t i = 0; i < 100; ++i) {
103 int64_t slotUs = minUs + (i * (maxUs - minUs) / 100);
104
105 double fps = 1E6 / slotUs;
106 printf("[%.2f fps]: %d\n", fps, counts[i]);
107 }
108}
109
James Dong482a1b12010-11-19 11:45:16 -0800110static void displayAVCProfileLevelIfPossible(const sp<MetaData>& meta) {
111 uint32_t type;
112 const void *data;
113 size_t size;
114 if (meta->findData(kKeyAVCC, &type, &data, &size)) {
115 const uint8_t *ptr = (const uint8_t *)data;
116 CHECK(size >= 7);
117 CHECK(ptr[0] == 1); // configurationVersion == 1
118 uint8_t profile = ptr[1];
119 uint8_t level = ptr[3];
120 fprintf(stderr, "AVC video profile %d and level %d\n", profile, level);
121 }
122}
123
Andreas Huber6bfb3972010-09-16 14:55:48 -0700124static void playSource(OMXClient *client, sp<MediaSource> &source) {
Andreas Huberbe06d262009-08-14 14:37:10 -0700125 sp<MetaData> meta = source->getFormat();
126
Andreas Huber6f6bf3d2009-11-03 15:59:13 -0800127 const char *mime;
128 CHECK(meta->findCString(kKeyMIMEType, &mime));
129
130 sp<MediaSource> rawSource;
131 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_RAW, mime)) {
132 rawSource = source;
133 } else {
134 rawSource = OMXCodec::Create(
Andreas Hubere42fdb52010-01-14 11:31:22 -0800135 client->interface(), meta, false /* createEncoder */, source,
136 NULL /* matchComponentName */,
137 gPreferSoftwareCodec ? OMXCodec::kPreferSoftwareCodecs : 0);
Andreas Huberbe06d262009-08-14 14:37:10 -0700138
Andreas Huber6f6bf3d2009-11-03 15:59:13 -0800139 if (rawSource == NULL) {
140 fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime);
141 return;
142 }
James Dong482a1b12010-11-19 11:45:16 -0800143 displayAVCProfileLevelIfPossible(meta);
Andreas Huberbe06d262009-08-14 14:37:10 -0700144 }
145
Andreas Huber6bfb3972010-09-16 14:55:48 -0700146 source.clear();
147
Andreas Huber1919e5a2010-05-20 10:37:06 -0700148 status_t err = rawSource->start();
149
150 if (err != OK) {
151 fprintf(stderr, "rawSource returned error %d (0x%08x)\n", err, err);
152 return;
153 }
Andreas Huberbe06d262009-08-14 14:37:10 -0700154
Andreas Huber662292a2010-04-13 09:58:39 -0700155 if (gPlaybackAudio) {
156 AudioPlayer *player = new AudioPlayer(NULL);
157 player->setSource(rawSource);
Andreas Huber49796012010-04-21 15:47:15 -0700158 rawSource.clear();
Andreas Huber662292a2010-04-13 09:58:39 -0700159
160 player->start(true /* sourceAlreadyStarted */);
161
162 status_t finalStatus;
163 while (!player->reachedEOS(&finalStatus)) {
164 usleep(100000ll);
165 }
166
167 delete player;
168 player = NULL;
Andreas Huber49796012010-04-21 15:47:15 -0700169
170 return;
Andreas Huber662292a2010-04-13 09:58:39 -0700171 } else if (gReproduceBug >= 3 && gReproduceBug <= 5) {
Andreas Huber27366fc2009-11-20 09:32:46 -0800172 int64_t durationUs;
173 CHECK(meta->findInt64(kKeyDuration, &durationUs));
174
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700175 status_t err;
176 MediaBuffer *buffer;
177 MediaSource::ReadOptions options;
178 int64_t seekTimeUs = -1;
179 for (;;) {
Andreas Huber6f6bf3d2009-11-03 15:59:13 -0800180 err = rawSource->read(&buffer, &options);
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700181 options.clearSeekTo();
182
183 bool shouldSeek = false;
Andreas Hubercfd55572009-10-09 14:11:28 -0700184 if (err == INFO_FORMAT_CHANGED) {
Andreas Huberb5185272010-09-23 15:01:30 -0700185 CHECK(buffer == NULL);
Andreas Hubercfd55572009-10-09 14:11:28 -0700186
187 printf("format changed.\n");
188 continue;
189 } else if (err != OK) {
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700190 printf("reached EOF.\n");
191
192 shouldSeek = true;
193 } else {
Andreas Huberfa8de752009-10-08 10:07:49 -0700194 int64_t timestampUs;
195 CHECK(buffer->meta_data()->findInt64(kKeyTime, &timestampUs));
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700196
197 bool failed = false;
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700198
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700199 if (seekTimeUs >= 0) {
Andreas Huberfa8de752009-10-08 10:07:49 -0700200 int64_t diff = timestampUs - seekTimeUs;
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700201
Andreas Huber11ebbfd2009-10-16 15:05:45 -0700202 if (diff < 0) {
203 diff = -diff;
204 }
205
206 if ((gReproduceBug == 4 && diff > 500000)
207 || (gReproduceBug == 5 && timestampUs < 0)) {
208 printf("wanted: %.2f secs, got: %.2f secs\n",
209 seekTimeUs / 1E6, timestampUs / 1E6);
210
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700211 printf("ERROR: ");
212 failed = true;
213 }
214 }
215
216 printf("buffer has timestamp %lld us (%.2f secs)\n",
Andreas Huberfa8de752009-10-08 10:07:49 -0700217 timestampUs, timestampUs / 1E6);
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700218
219 buffer->release();
220 buffer = NULL;
221
222 if (failed) {
223 break;
224 }
225
226 shouldSeek = ((double)rand() / RAND_MAX) < 0.1;
Andreas Huber11ebbfd2009-10-16 15:05:45 -0700227
228 if (gReproduceBug == 3) {
229 shouldSeek = false;
230 }
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700231 }
232
233 seekTimeUs = -1;
234
235 if (shouldSeek) {
Andreas Huber11ebbfd2009-10-16 15:05:45 -0700236 seekTimeUs = (rand() * (float)durationUs) / RAND_MAX;
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700237 options.setSeekTo(seekTimeUs);
238
239 printf("seeking to %lld us (%.2f secs)\n",
240 seekTimeUs, seekTimeUs / 1E6);
241 }
242 }
243
Andreas Huber6f6bf3d2009-11-03 15:59:13 -0800244 rawSource->stop();
Andreas Huber1a77b68e2009-09-17 11:16:52 -0700245
246 return;
247 }
248
Andreas Huber09daada2009-08-18 11:27:32 -0700249 int n = 0;
Andreas Huberbe06d262009-08-14 14:37:10 -0700250 int64_t startTime = getNowUs();
251
Andreas Huber09daada2009-08-18 11:27:32 -0700252 long numIterationsLeft = gNumRepetitions;
253 MediaSource::ReadOptions options;
Andreas Huber4f5e6022009-08-19 09:29:34 -0700254
Andreas Huber6c9bf1c2010-02-17 14:57:28 -0800255 int64_t sumDecodeUs = 0;
Andreas Hubera4357ad2010-04-02 12:49:54 -0700256 int64_t totalBytes = 0;
Andreas Huber6c9bf1c2010-02-17 14:57:28 -0800257
Andreas Huber93e496f2010-11-09 11:16:03 -0800258 Vector<int64_t> decodeTimesUs;
259
Andreas Huber09daada2009-08-18 11:27:32 -0700260 while (numIterationsLeft-- > 0) {
Andreas Huber557c6ce2009-08-26 10:11:50 -0700261 long numFrames = 0;
262
Andreas Huber09daada2009-08-18 11:27:32 -0700263 MediaBuffer *buffer;
264
265 for (;;) {
Andreas Huber6c9bf1c2010-02-17 14:57:28 -0800266 int64_t startDecodeUs = getNowUs();
Andreas Huber6f6bf3d2009-11-03 15:59:13 -0800267 status_t err = rawSource->read(&buffer, &options);
Andreas Huber6c9bf1c2010-02-17 14:57:28 -0800268 int64_t delayDecodeUs = getNowUs() - startDecodeUs;
269
Andreas Huber09daada2009-08-18 11:27:32 -0700270 options.clearSeekTo();
271
272 if (err != OK) {
Andreas Huberb5185272010-09-23 15:01:30 -0700273 CHECK(buffer == NULL);
Andreas Hubercfd55572009-10-09 14:11:28 -0700274
275 if (err == INFO_FORMAT_CHANGED) {
276 printf("format changed.\n");
277 continue;
278 }
279
Andreas Huber09daada2009-08-18 11:27:32 -0700280 break;
281 }
282
Andreas Huber93e496f2010-11-09 11:16:03 -0800283 if (buffer->range_length() > 0) {
284 if (gDisplayHistogram && n > 0) {
285 // Ignore the first time since it includes some setup
286 // cost.
287 decodeTimesUs.push(delayDecodeUs);
288 }
289
290 if ((n++ % 16) == 0) {
291 printf(".");
292 fflush(stdout);
293 }
Andreas Huber09daada2009-08-18 11:27:32 -0700294 }
295
Andreas Huber6c9bf1c2010-02-17 14:57:28 -0800296 sumDecodeUs += delayDecodeUs;
Andreas Hubera4357ad2010-04-02 12:49:54 -0700297 totalBytes += buffer->range_length();
Andreas Huber6c9bf1c2010-02-17 14:57:28 -0800298
Andreas Huber09daada2009-08-18 11:27:32 -0700299 buffer->release();
300 buffer = NULL;
Andreas Huber557c6ce2009-08-26 10:11:50 -0700301
302 ++numFrames;
303 if (gMaxNumFrames > 0 && numFrames == gMaxNumFrames) {
304 break;
305 }
Andreas Huber0fb64772009-08-27 12:10:02 -0700306
307 if (gReproduceBug == 1 && numFrames == 40) {
308 printf("seeking past the end now.");
Andreas Hubera84b0e02009-08-28 08:40:00 -0700309 options.setSeekTo(0x7fffffffL);
Andreas Huber54ee7842009-09-03 16:09:33 -0700310 } else if (gReproduceBug == 2 && numFrames == 40) {
311 printf("seeking to 5 secs.");
312 options.setSeekTo(5000000);
Andreas Huber0fb64772009-08-27 12:10:02 -0700313 }
Andreas Huberbe06d262009-08-14 14:37:10 -0700314 }
315
Andreas Huber09daada2009-08-18 11:27:32 -0700316 printf("$");
317 fflush(stdout);
318
319 options.setSeekTo(0);
Andreas Huberbe06d262009-08-14 14:37:10 -0700320 }
Andreas Huber09daada2009-08-18 11:27:32 -0700321
Andreas Huber6f6bf3d2009-11-03 15:59:13 -0800322 rawSource->stop();
Andreas Huberbe06d262009-08-14 14:37:10 -0700323 printf("\n");
324
325 int64_t delay = getNowUs() - startTime;
Andreas Hubera4357ad2010-04-02 12:49:54 -0700326 if (!strncasecmp("video/", mime, 6)) {
327 printf("avg. %.2f fps\n", n * 1E6 / delay);
Andreas Huberbe06d262009-08-14 14:37:10 -0700328
Andreas Hubera4357ad2010-04-02 12:49:54 -0700329 printf("avg. time to decode one buffer %.2f usecs\n",
330 (double)sumDecodeUs / n);
331
332 printf("decoded a total of %d frame(s).\n", n);
Andreas Huber93e496f2010-11-09 11:16:03 -0800333
334 if (gDisplayHistogram) {
335 displayDecodeHistogram(&decodeTimesUs);
336 }
Andreas Hubera4357ad2010-04-02 12:49:54 -0700337 } else if (!strncasecmp("audio/", mime, 6)) {
338 // Frame count makes less sense for audio, as the output buffer
339 // sizes may be different across decoders.
340 printf("avg. %.2f KB/sec\n", totalBytes / 1024 * 1E6 / delay);
341
342 printf("decoded a total of %lld bytes\n", totalBytes);
343 }
Andreas Huberbe06d262009-08-14 14:37:10 -0700344}
345
Andreas Huberb5185272010-09-23 15:01:30 -0700346////////////////////////////////////////////////////////////////////////////////
347
348struct DetectSyncSource : public MediaSource {
349 DetectSyncSource(const sp<MediaSource> &source);
350
351 virtual status_t start(MetaData *params = NULL);
352 virtual status_t stop();
353 virtual sp<MetaData> getFormat();
354
355 virtual status_t read(
356 MediaBuffer **buffer, const ReadOptions *options);
357
358private:
Andreas Huber3ac01192010-09-24 09:35:49 -0700359 enum StreamType {
360 AVC,
361 MPEG4,
362 H263,
363 OTHER,
364 };
365
Andreas Huberb5185272010-09-23 15:01:30 -0700366 sp<MediaSource> mSource;
Andreas Huber3ac01192010-09-24 09:35:49 -0700367 StreamType mStreamType;
Andreas Huberb5185272010-09-23 15:01:30 -0700368
369 DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource);
370};
371
372DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source)
373 : mSource(source),
Andreas Huber3ac01192010-09-24 09:35:49 -0700374 mStreamType(OTHER) {
Andreas Huberb5185272010-09-23 15:01:30 -0700375 const char *mime;
376 CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
377
Andreas Huber3ac01192010-09-24 09:35:49 -0700378 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
379 mStreamType = AVC;
380 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) {
381 mStreamType = MPEG4;
382 CHECK(!"sync frame detection not implemented yet for MPEG4");
383 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) {
384 mStreamType = H263;
385 CHECK(!"sync frame detection not implemented yet for H.263");
386 }
Andreas Huberb5185272010-09-23 15:01:30 -0700387}
388
389status_t DetectSyncSource::start(MetaData *params) {
390 return mSource->start(params);
391}
392
393status_t DetectSyncSource::stop() {
394 return mSource->stop();
395}
396
397sp<MetaData> DetectSyncSource::getFormat() {
398 return mSource->getFormat();
399}
400
401static bool isIDRFrame(MediaBuffer *buffer) {
402 const uint8_t *data =
403 (const uint8_t *)buffer->data() + buffer->range_offset();
404 size_t size = buffer->range_length();
405 for (size_t i = 0; i + 3 < size; ++i) {
406 if (!memcmp("\x00\x00\x01", &data[i], 3)) {
407 uint8_t nalType = data[i + 3] & 0x1f;
408 if (nalType == 5) {
409 return true;
410 }
411 }
412 }
413
414 return false;
415}
416
417status_t DetectSyncSource::read(
418 MediaBuffer **buffer, const ReadOptions *options) {
419 status_t err = mSource->read(buffer, options);
420
421 if (err != OK) {
422 return err;
423 }
424
Andreas Huber3ac01192010-09-24 09:35:49 -0700425 if (mStreamType == AVC && isIDRFrame(*buffer)) {
Andreas Huberb5185272010-09-23 15:01:30 -0700426 (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
427 } else {
Andreas Huber3ac01192010-09-24 09:35:49 -0700428 (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true);
Andreas Huberb5185272010-09-23 15:01:30 -0700429 }
430
431 return OK;
432}
433
434////////////////////////////////////////////////////////////////////////////////
435
Andreas Huber3ac01192010-09-24 09:35:49 -0700436static void writeSourcesToMP4(
437 Vector<sp<MediaSource> > &sources, bool syncInfoPresent) {
Andreas Huberc751ecc2010-09-27 12:04:43 -0700438#if 0
Andreas Huber45bac572010-07-01 08:19:52 -0700439 sp<MPEG4Writer> writer =
440 new MPEG4Writer(gWriteMP4Filename.string());
Andreas Huberc751ecc2010-09-27 12:04:43 -0700441#else
442 sp<MPEG2TSWriter> writer =
443 new MPEG2TSWriter(gWriteMP4Filename.string());
444#endif
Andreas Huber45bac572010-07-01 08:19:52 -0700445
Andreas Huberb5185272010-09-23 15:01:30 -0700446 // at most one minute.
447 writer->setMaxFileDuration(60000000ll);
448
Andreas Huber3ac01192010-09-24 09:35:49 -0700449 for (size_t i = 0; i < sources.size(); ++i) {
450 sp<MediaSource> source = sources.editItemAt(i);
451
452 CHECK_EQ(writer->addSource(
453 syncInfoPresent ? source : new DetectSyncSource(source)),
454 (status_t)OK);
455 }
Andreas Huber45bac572010-07-01 08:19:52 -0700456
457 sp<MetaData> params = new MetaData;
Andreas Huberb5185272010-09-23 15:01:30 -0700458 params->setInt32(kKeyNotRealTime, true);
459 CHECK_EQ(writer->start(params.get()), (status_t)OK);
Andreas Huber45bac572010-07-01 08:19:52 -0700460
461 while (!writer->reachedEOS()) {
462 usleep(100000);
463 }
464 writer->stop();
465}
466
Andreas Huber5850a482010-07-20 16:30:35 -0700467static void performSeekTest(const sp<MediaSource> &source) {
Andreas Huberb5185272010-09-23 15:01:30 -0700468 CHECK_EQ((status_t)OK, source->start());
Andreas Huber5850a482010-07-20 16:30:35 -0700469
470 int64_t durationUs;
471 CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs));
472
473 for (int64_t seekTimeUs = 0; seekTimeUs <= durationUs;
474 seekTimeUs += 60000ll) {
475 MediaSource::ReadOptions options;
476 options.setSeekTo(
477 seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
478
479 MediaBuffer *buffer;
480 status_t err;
481 for (;;) {
482 err = source->read(&buffer, &options);
483
484 options.clearSeekTo();
485
486 if (err == INFO_FORMAT_CHANGED) {
487 CHECK(buffer == NULL);
488 continue;
489 }
490
491 if (err != OK) {
492 CHECK(buffer == NULL);
493 break;
494 }
495
496 if (buffer->range_length() > 0) {
497 break;
498 }
499
500 CHECK(buffer != NULL);
501
502 buffer->release();
503 buffer = NULL;
504 }
505
506 if (err == OK) {
507 int64_t timeUs;
508 CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
509
510 printf("%lld\t%lld\t%lld\n", seekTimeUs, timeUs, seekTimeUs - timeUs);
511
512 buffer->release();
513 buffer = NULL;
514 } else {
515 printf("ERROR\n");
516 break;
517 }
518 }
519
Andreas Huberb5185272010-09-23 15:01:30 -0700520 CHECK_EQ((status_t)OK, source->stop());
Andreas Huber5850a482010-07-20 16:30:35 -0700521}
522
Andreas Huber09daada2009-08-18 11:27:32 -0700523static void usage(const char *me) {
524 fprintf(stderr, "usage: %s\n", me);
525 fprintf(stderr, " -h(elp)\n");
526 fprintf(stderr, " -a(udio)\n");
527 fprintf(stderr, " -n repetitions\n");
528 fprintf(stderr, " -l(ist) components\n");
Andreas Huber557c6ce2009-08-26 10:11:50 -0700529 fprintf(stderr, " -m max-number-of-frames-to-decode in each pass\n");
Andreas Huber0fb64772009-08-27 12:10:02 -0700530 fprintf(stderr, " -b bug to reproduce\n");
Andreas Hubere6c40962009-09-10 14:13:30 -0700531 fprintf(stderr, " -p(rofiles) dump decoder profiles supported\n");
Andreas Huberedfeb1a2010-03-10 11:07:12 -0800532 fprintf(stderr, " -t(humbnail) extract video thumbnail or album art\n");
Andreas Hubere42fdb52010-01-14 11:31:22 -0800533 fprintf(stderr, " -s(oftware) prefer software codec\n");
Andreas Huber662292a2010-04-13 09:58:39 -0700534 fprintf(stderr, " -o playback audio\n");
Andreas Huber45bac572010-07-01 08:19:52 -0700535 fprintf(stderr, " -w(rite) filename (write to .mp4 file)\n");
Andreas Huber5850a482010-07-20 16:30:35 -0700536 fprintf(stderr, " -k seek test\n");
Andreas Huber93e496f2010-11-09 11:16:03 -0800537 fprintf(stderr, " -x display a histogram of decoding times/fps "
538 "(video only)\n");
Andreas Huber09daada2009-08-18 11:27:32 -0700539}
540
Andreas Hubere46b7be2009-07-14 16:56:47 -0700541int main(int argc, char **argv) {
542 android::ProcessState::self()->startThreadPool();
543
Andreas Huber8a432772009-07-28 10:03:13 -0700544 bool audioOnly = false;
Andreas Huber09daada2009-08-18 11:27:32 -0700545 bool listComponents = false;
Andreas Hubere6c40962009-09-10 14:13:30 -0700546 bool dumpProfiles = false;
Andreas Hubere42fdb52010-01-14 11:31:22 -0800547 bool extractThumbnail = false;
Andreas Huber5850a482010-07-20 16:30:35 -0700548 bool seekTest = false;
Andreas Huber09daada2009-08-18 11:27:32 -0700549 gNumRepetitions = 1;
Andreas Huber557c6ce2009-08-26 10:11:50 -0700550 gMaxNumFrames = 0;
Andreas Huber0fb64772009-08-27 12:10:02 -0700551 gReproduceBug = -1;
Andreas Hubere42fdb52010-01-14 11:31:22 -0800552 gPreferSoftwareCodec = false;
Andreas Huber662292a2010-04-13 09:58:39 -0700553 gPlaybackAudio = false;
Andreas Huber45bac572010-07-01 08:19:52 -0700554 gWriteMP4 = false;
Andreas Huber93e496f2010-11-09 11:16:03 -0800555 gDisplayHistogram = false;
Andreas Huber09daada2009-08-18 11:27:32 -0700556
Andreas Huber4e4173b2010-07-22 09:20:13 -0700557 sp<ALooper> looper;
558 sp<ARTSPController> rtspController;
559
Andreas Huber09daada2009-08-18 11:27:32 -0700560 int res;
Andreas Huber93e496f2010-11-09 11:16:03 -0800561 while ((res = getopt(argc, argv, "han:lm:b:ptsow:kx")) >= 0) {
Andreas Huber09daada2009-08-18 11:27:32 -0700562 switch (res) {
563 case 'a':
564 {
565 audioOnly = true;
566 break;
567 }
568
569 case 'l':
570 {
571 listComponents = true;
572 break;
573 }
574
Andreas Huber557c6ce2009-08-26 10:11:50 -0700575 case 'm':
Andreas Huber09daada2009-08-18 11:27:32 -0700576 case 'n':
Andreas Huber0fb64772009-08-27 12:10:02 -0700577 case 'b':
Andreas Huber09daada2009-08-18 11:27:32 -0700578 {
579 char *end;
580 long x = strtol(optarg, &end, 10);
581
582 if (*end != '\0' || end == optarg || x <= 0) {
583 x = 1;
584 }
585
Andreas Huber557c6ce2009-08-26 10:11:50 -0700586 if (res == 'n') {
587 gNumRepetitions = x;
Andreas Huber0fb64772009-08-27 12:10:02 -0700588 } else if (res == 'm') {
Andreas Huber557c6ce2009-08-26 10:11:50 -0700589 gMaxNumFrames = x;
Andreas Huber0fb64772009-08-27 12:10:02 -0700590 } else {
591 CHECK_EQ(res, 'b');
592 gReproduceBug = x;
Andreas Huber557c6ce2009-08-26 10:11:50 -0700593 }
Andreas Huber09daada2009-08-18 11:27:32 -0700594 break;
595 }
596
Andreas Huber45bac572010-07-01 08:19:52 -0700597 case 'w':
598 {
599 gWriteMP4 = true;
600 gWriteMP4Filename.setTo(optarg);
601 break;
602 }
603
Andreas Hubere6c40962009-09-10 14:13:30 -0700604 case 'p':
605 {
606 dumpProfiles = true;
607 break;
608 }
609
Andreas Hubere42fdb52010-01-14 11:31:22 -0800610 case 't':
611 {
612 extractThumbnail = true;
613 break;
614 }
615
616 case 's':
617 {
618 gPreferSoftwareCodec = true;
619 break;
620 }
621
Andreas Huber662292a2010-04-13 09:58:39 -0700622 case 'o':
623 {
624 gPlaybackAudio = true;
625 break;
626 }
627
Andreas Huber5850a482010-07-20 16:30:35 -0700628 case 'k':
629 {
630 seekTest = true;
631 break;
632 }
633
Andreas Huber93e496f2010-11-09 11:16:03 -0800634 case 'x':
635 {
636 gDisplayHistogram = true;
637 break;
638 }
639
Andreas Huber09daada2009-08-18 11:27:32 -0700640 case '?':
641 case 'h':
642 default:
643 {
644 usage(argv[0]);
645 exit(1);
646 break;
647 }
648 }
649 }
650
Andreas Huber662292a2010-04-13 09:58:39 -0700651 if (gPlaybackAudio && !audioOnly) {
652 // This doesn't make any sense if we're decoding the video track.
653 gPlaybackAudio = false;
654 }
655
Andreas Huber09daada2009-08-18 11:27:32 -0700656 argc -= optind;
657 argv += optind;
658
Andreas Hubere42fdb52010-01-14 11:31:22 -0800659 if (extractThumbnail) {
660 sp<IServiceManager> sm = defaultServiceManager();
661 sp<IBinder> binder = sm->getService(String16("media.player"));
662 sp<IMediaPlayerService> service =
663 interface_cast<IMediaPlayerService>(binder);
664
665 CHECK(service.get() != NULL);
666
667 sp<IMediaMetadataRetriever> retriever =
668 service->createMetadataRetriever(getpid());
669
670 CHECK(retriever != NULL);
671
672 for (int k = 0; k < argc; ++k) {
673 const char *filename = argv[k];
674
Andreas Huberb5185272010-09-23 15:01:30 -0700675 CHECK_EQ(retriever->setDataSource(filename), (status_t)OK);
Andreas Huberedfeb1a2010-03-10 11:07:12 -0800676 CHECK_EQ(retriever->setMode(
677 METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL),
Andreas Huberb5185272010-09-23 15:01:30 -0700678 (status_t)OK);
Andreas Hubere42fdb52010-01-14 11:31:22 -0800679
680 sp<IMemory> mem = retriever->captureFrame();
681
Andreas Huberedfeb1a2010-03-10 11:07:12 -0800682 if (mem != NULL) {
683 printf("captureFrame(%s) => OK\n", filename);
684 } else {
685 mem = retriever->extractAlbumArt();
686
687 if (mem != NULL) {
688 printf("extractAlbumArt(%s) => OK\n", filename);
689 } else {
690 printf("both captureFrame and extractAlbumArt "
691 "failed on file '%s'.\n", filename);
692 }
693 }
Andreas Hubere42fdb52010-01-14 11:31:22 -0800694 }
695
696 return 0;
697 }
698
Andreas Hubere6c40962009-09-10 14:13:30 -0700699 if (dumpProfiles) {
700 sp<IServiceManager> sm = defaultServiceManager();
701 sp<IBinder> binder = sm->getService(String16("media.player"));
702 sp<IMediaPlayerService> service =
703 interface_cast<IMediaPlayerService>(binder);
704
705 CHECK(service.get() != NULL);
706
Andreas Huber784202e2009-10-15 13:46:54 -0700707 sp<IOMX> omx = service->getOMX();
Andreas Hubere6c40962009-09-10 14:13:30 -0700708 CHECK(omx.get() != NULL);
709
710 const char *kMimeTypes[] = {
711 MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4,
Andreas Huber1a189a82010-03-24 13:49:20 -0700712 MEDIA_MIMETYPE_VIDEO_H263, MEDIA_MIMETYPE_AUDIO_AAC,
713 MEDIA_MIMETYPE_AUDIO_AMR_NB, MEDIA_MIMETYPE_AUDIO_AMR_WB,
714 MEDIA_MIMETYPE_AUDIO_MPEG
Andreas Hubere6c40962009-09-10 14:13:30 -0700715 };
716
717 for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]);
718 ++k) {
719 printf("type '%s':\n", kMimeTypes[k]);
720
721 Vector<CodecCapabilities> results;
722 CHECK_EQ(QueryCodecs(omx, kMimeTypes[k],
723 true, // queryDecoders
Andreas Huberb5185272010-09-23 15:01:30 -0700724 &results), (status_t)OK);
Andreas Hubere6c40962009-09-10 14:13:30 -0700725
726 for (size_t i = 0; i < results.size(); ++i) {
727 printf(" decoder '%s' supports ",
728 results[i].mComponentName.string());
729
730 if (results[i].mProfileLevels.size() == 0) {
731 printf("NOTHING.\n");
732 continue;
733 }
734
735 for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) {
736 const CodecProfileLevel &profileLevel =
737 results[i].mProfileLevels[j];
738
739 printf("%s%ld/%ld", j > 0 ? ", " : "",
740 profileLevel.mProfile, profileLevel.mLevel);
741 }
742
743 printf("\n");
744 }
745 }
746 }
747
Andreas Huber09daada2009-08-18 11:27:32 -0700748 if (listComponents) {
Andreas Hubere46b7be2009-07-14 16:56:47 -0700749 sp<IServiceManager> sm = defaultServiceManager();
750 sp<IBinder> binder = sm->getService(String16("media.player"));
751 sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
752
Andreas Huberbe06d262009-08-14 14:37:10 -0700753 CHECK(service.get() != NULL);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700754
Andreas Huber784202e2009-10-15 13:46:54 -0700755 sp<IOMX> omx = service->getOMX();
Andreas Huberbe06d262009-08-14 14:37:10 -0700756 CHECK(omx.get() != NULL);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700757
Andreas Huber2ea14e22009-12-16 09:30:55 -0800758 List<IOMX::ComponentInfo> list;
Andreas Huber784202e2009-10-15 13:46:54 -0700759 omx->listNodes(&list);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700760
Andreas Huber2ea14e22009-12-16 09:30:55 -0800761 for (List<IOMX::ComponentInfo>::iterator it = list.begin();
Andreas Hubere46b7be2009-07-14 16:56:47 -0700762 it != list.end(); ++it) {
Andreas Huber2ea14e22009-12-16 09:30:55 -0800763 printf("%s\n", (*it).mName.string());
Andreas Hubere46b7be2009-07-14 16:56:47 -0700764 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700765 }
766
Andreas Hubere46b7be2009-07-14 16:56:47 -0700767 DataSource::RegisterDefaultSniffers();
768
769 OMXClient client;
770 status_t err = client.connect();
771
Andreas Huber09daada2009-08-18 11:27:32 -0700772 for (int k = 0; k < argc; ++k) {
Andreas Huberb5185272010-09-23 15:01:30 -0700773 bool syncInfoPresent = true;
774
Andreas Huber09daada2009-08-18 11:27:32 -0700775 const char *filename = argv[k];
Andreas Hubere46b7be2009-07-14 16:56:47 -0700776
Andreas Huberc0668842010-06-10 11:16:10 -0700777 sp<DataSource> dataSource = DataSource::CreateFromURI(filename);
Andreas Hubere46b7be2009-07-14 16:56:47 -0700778
Andreas Huber3ac01192010-09-24 09:35:49 -0700779 if (strncasecmp(filename, "sine:", 5)
780 && strncasecmp(filename, "rtsp://", 7)
781 && strncasecmp(filename, "httplive://", 11)
782 && dataSource == NULL) {
Andreas Huber662292a2010-04-13 09:58:39 -0700783 fprintf(stderr, "Unable to create data source.\n");
784 return 1;
785 }
786
Andreas Huber09daada2009-08-18 11:27:32 -0700787 bool isJPEG = false;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700788
Andreas Huber09daada2009-08-18 11:27:32 -0700789 size_t len = strlen(filename);
790 if (len >= 4 && !strcasecmp(filename + len - 4, ".jpg")) {
791 isJPEG = true;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700792 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700793
Andreas Huber3ac01192010-09-24 09:35:49 -0700794 Vector<sp<MediaSource> > mediaSources;
Andreas Huber09daada2009-08-18 11:27:32 -0700795 sp<MediaSource> mediaSource;
Andreas Hubere46b7be2009-07-14 16:56:47 -0700796
Andreas Huber09daada2009-08-18 11:27:32 -0700797 if (isJPEG) {
798 mediaSource = new JPEGSource(dataSource);
Andreas Huber3ac01192010-09-24 09:35:49 -0700799 if (gWriteMP4) {
800 mediaSources.push(mediaSource);
801 }
Andreas Huber662292a2010-04-13 09:58:39 -0700802 } else if (!strncasecmp("sine:", filename, 5)) {
803 char *end;
804 long sampleRate = strtol(filename + 5, &end, 10);
805
806 if (end == filename + 5) {
807 sampleRate = 44100;
808 }
809 mediaSource = new SineSource(sampleRate, 1);
Andreas Huber3ac01192010-09-24 09:35:49 -0700810 if (gWriteMP4) {
811 mediaSources.push(mediaSource);
812 }
Andreas Huber09daada2009-08-18 11:27:32 -0700813 } else {
Andreas Huber4e4173b2010-07-22 09:20:13 -0700814 sp<MediaExtractor> extractor;
815
816 if (!strncasecmp("rtsp://", filename, 7)) {
817 if (looper == NULL) {
818 looper = new ALooper;
819 looper->start();
820 }
821
822 rtspController = new ARTSPController(looper);
823 status_t err = rtspController->connect(filename);
824 if (err != OK) {
825 fprintf(stderr, "could not connect to rtsp server.\n");
826 return -1;
827 }
828
829 extractor = rtspController.get();
Andreas Huberb5185272010-09-23 15:01:30 -0700830
831 syncInfoPresent = false;
Andreas Huber3ac01192010-09-24 09:35:49 -0700832 } else if (!strncasecmp("httplive://", filename, 11)) {
833 String8 uri("http://");
834 uri.append(filename + 11);
835
836 dataSource = new LiveSource(uri.string());
837 dataSource = new NuCachedSource2(dataSource);
838
839 extractor =
840 MediaExtractor::Create(
841 dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
842
843 syncInfoPresent = false;
Andreas Huber4e4173b2010-07-22 09:20:13 -0700844 } else {
845 extractor = MediaExtractor::Create(dataSource);
846 if (extractor == NULL) {
847 fprintf(stderr, "could not create extractor.\n");
848 return -1;
849 }
James Donge4cfcb82010-02-04 18:08:06 -0800850 }
Andreas Huber09daada2009-08-18 11:27:32 -0700851
852 size_t numTracks = extractor->countTracks();
853
Andreas Huber3ac01192010-09-24 09:35:49 -0700854 if (gWriteMP4) {
855 bool haveAudio = false;
856 bool haveVideo = false;
857 for (size_t i = 0; i < numTracks; ++i) {
858 sp<MediaSource> source = extractor->getTrack(i);
Andreas Huber09daada2009-08-18 11:27:32 -0700859
Andreas Huber3ac01192010-09-24 09:35:49 -0700860 const char *mime;
861 CHECK(source->getFormat()->findCString(
862 kKeyMIMEType, &mime));
Andreas Huber09daada2009-08-18 11:27:32 -0700863
Andreas Huber3ac01192010-09-24 09:35:49 -0700864 bool useTrack = false;
865 if (!haveAudio && !strncasecmp("audio/", mime, 6)) {
866 haveAudio = true;
867 useTrack = true;
868 } else if (!haveVideo && !strncasecmp("video/", mime, 6)) {
869 haveVideo = true;
870 useTrack = true;
871 }
872
873 if (useTrack) {
874 mediaSources.push(source);
875
876 if (haveAudio && haveVideo) {
877 break;
878 }
879 }
880 }
881 } else {
882 sp<MetaData> meta;
883 size_t i;
884 for (i = 0; i < numTracks; ++i) {
885 meta = extractor->getTrackMetaData(
886 i, MediaExtractor::kIncludeExtensiveMetaData);
887
888 const char *mime;
889 meta->findCString(kKeyMIMEType, &mime);
890
891 if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
892 break;
893 }
894
895 if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
896 break;
897 }
898
899 meta = NULL;
Andreas Huber09daada2009-08-18 11:27:32 -0700900 }
901
Andreas Huber3ac01192010-09-24 09:35:49 -0700902 if (meta == NULL) {
903 fprintf(stderr,
904 "No suitable %s track found. The '-a' option will "
905 "target audio tracks only, the default is to target "
906 "video tracks only.\n",
907 audioOnly ? "audio" : "video");
908 return -1;
Andreas Huber09daada2009-08-18 11:27:32 -0700909 }
Andreas Huber662292a2010-04-13 09:58:39 -0700910
Andreas Huber3ac01192010-09-24 09:35:49 -0700911 int64_t thumbTimeUs;
912 if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) {
913 printf("thumbnailTime: %lld us (%.2f secs)\n",
914 thumbTimeUs, thumbTimeUs / 1E6);
915 }
Andreas Huber662292a2010-04-13 09:58:39 -0700916
Andreas Huber3ac01192010-09-24 09:35:49 -0700917 mediaSource = extractor->getTrack(i);
Andreas Huber09daada2009-08-18 11:27:32 -0700918 }
Andreas Huber09daada2009-08-18 11:27:32 -0700919 }
920
Andreas Huber45bac572010-07-01 08:19:52 -0700921 if (gWriteMP4) {
Andreas Huber3ac01192010-09-24 09:35:49 -0700922 writeSourcesToMP4(mediaSources, syncInfoPresent);
Andreas Huber5850a482010-07-20 16:30:35 -0700923 } else if (seekTest) {
924 performSeekTest(mediaSource);
Andreas Huber45bac572010-07-01 08:19:52 -0700925 } else {
926 playSource(&client, mediaSource);
927 }
Andreas Huber4e4173b2010-07-22 09:20:13 -0700928
929 if (rtspController != NULL) {
930 rtspController->disconnect();
931 rtspController.clear();
932
933 sleep(3);
934 }
Andreas Huber09daada2009-08-18 11:27:32 -0700935 }
Andreas Hubere46b7be2009-07-14 16:56:47 -0700936
937 client.disconnect();
Andreas Hubere46b7be2009-07-14 16:56:47 -0700938
939 return 0;
940}