blob: c371cd094e143dc770ab885baf2a618b1819c47e [file] [log] [blame]
Andreas Huber53a76bd2009-10-06 16:20:44 -07001/*
Andreas Huberaee3c632010-01-11 15:35:19 -08002 * 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 */
Andreas Huber53a76bd2009-10-06 16:20:44 -070016
17//#define LOG_NDEBUG 0
18#define LOG_TAG "StagefrightMetadataRetriever"
19#include <utils/Log.h>
20
Andreas Hubere4a83802010-01-08 10:57:34 -080021#include "include/StagefrightMetadataRetriever.h"
Andreas Huber53a76bd2009-10-06 16:20:44 -070022
Andreas Huber53a76bd2009-10-06 16:20:44 -070023#include <media/stagefright/ColorConverter.h>
24#include <media/stagefright/DataSource.h>
Andreas Huber27366fc2009-11-20 09:32:46 -080025#include <media/stagefright/FileSource.h>
Andreas Huber53a76bd2009-10-06 16:20:44 -070026#include <media/stagefright/MediaDebug.h>
27#include <media/stagefright/MediaExtractor.h>
28#include <media/stagefright/MetaData.h>
Andreas Huber53a76bd2009-10-06 16:20:44 -070029#include <media/stagefright/OMXCodec.h>
30
31namespace android {
32
Andreas Huberaee3c632010-01-11 15:35:19 -080033StagefrightMetadataRetriever::StagefrightMetadataRetriever()
34 : mParsedMetaData(false),
35 mAlbumArt(NULL) {
Andreas Huber53a76bd2009-10-06 16:20:44 -070036 LOGV("StagefrightMetadataRetriever()");
37
38 DataSource::RegisterDefaultSniffers();
39 CHECK_EQ(mClient.connect(), OK);
40}
41
42StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
43 LOGV("~StagefrightMetadataRetriever()");
Andreas Huberaee3c632010-01-11 15:35:19 -080044
45 delete mAlbumArt;
46 mAlbumArt = NULL;
47
Andreas Huber53a76bd2009-10-06 16:20:44 -070048 mClient.disconnect();
49}
50
51status_t StagefrightMetadataRetriever::setDataSource(const char *uri) {
52 LOGV("setDataSource(%s)", uri);
53
Andreas Huberaee3c632010-01-11 15:35:19 -080054 mParsedMetaData = false;
55 mMetaData.clear();
56 delete mAlbumArt;
57 mAlbumArt = NULL;
Andreas Huber53a76bd2009-10-06 16:20:44 -070058
Andreas Huberaee3c632010-01-11 15:35:19 -080059 mSource = DataSource::CreateFromURI(uri);
60
61 if (mSource == NULL) {
62 return UNKNOWN_ERROR;
63 }
64
65 mExtractor = MediaExtractor::Create(mSource);
66
67 if (mExtractor == NULL) {
68 mSource.clear();
69
70 return UNKNOWN_ERROR;
71 }
72
73 return OK;
Andreas Huber53a76bd2009-10-06 16:20:44 -070074}
75
Andreas Huberaee3c632010-01-11 15:35:19 -080076// Warning caller retains ownership of the filedescriptor! Dup it if necessary.
Andreas Huber53a76bd2009-10-06 16:20:44 -070077status_t StagefrightMetadataRetriever::setDataSource(
78 int fd, int64_t offset, int64_t length) {
Andreas Huberaee3c632010-01-11 15:35:19 -080079 fd = dup(fd);
80
Andreas Huber53a76bd2009-10-06 16:20:44 -070081 LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
82
Andreas Huberaee3c632010-01-11 15:35:19 -080083 mParsedMetaData = false;
84 mMetaData.clear();
85 delete mAlbumArt;
86 mAlbumArt = NULL;
87
88 mSource = new FileSource(fd, offset, length);
89
90 status_t err;
91 if ((err = mSource->initCheck()) != OK) {
92 mSource.clear();
93
94 return err;
95 }
96
97 mExtractor = MediaExtractor::Create(mSource);
98
99 if (mExtractor == NULL) {
100 mSource.clear();
101
102 return UNKNOWN_ERROR;
103 }
Andreas Huber53a76bd2009-10-06 16:20:44 -0700104
105 return OK;
106}
107
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800108static VideoFrame *extractVideoFrameWithCodecFlags(
109 OMXClient *client,
110 const sp<MetaData> &trackMeta,
James Dongfaf09ba2010-12-02 17:42:08 -0800111 const sp<MediaSource> &source,
112 uint32_t flags,
113 int64_t frameTimeUs,
114 int seekMode) {
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800115 sp<MediaSource> decoder =
116 OMXCodec::Create(
117 client->interface(), source->getFormat(), false, source,
Andreas Huber5a40e392010-10-18 09:57:42 -0700118 NULL, flags | OMXCodec::kClientNeedsFramebuffer);
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800119
120 if (decoder.get() == NULL) {
121 LOGV("unable to instantiate video decoder.");
122
123 return NULL;
124 }
125
Andreas Huber1919e5a2010-05-20 10:37:06 -0700126 status_t err = decoder->start();
127 if (err != OK) {
128 LOGW("OMXCodec::start returned error %d (0x%08x)\n", err, err);
129 return NULL;
130 }
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800131
132 // Read one output buffer, ignore format change notifications
133 // and spurious empty buffers.
134
135 MediaSource::ReadOptions options;
James Dongfaf09ba2010-12-02 17:42:08 -0800136 if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
137 seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) {
138
139 LOGE("Unknown seek mode: %d", seekMode);
140 return NULL;
141 }
142
143 MediaSource::ReadOptions::SeekMode mode =
144 static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
145
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800146 int64_t thumbNailTime;
Andreas Huberb1d49de2010-12-08 14:50:10 -0800147 if (frameTimeUs < 0) {
148 if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)) {
149 thumbNailTime = 0;
150 }
James Dongfaf09ba2010-12-02 17:42:08 -0800151 options.setSeekTo(thumbNailTime, mode);
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800152 } else {
153 thumbNailTime = -1;
James Dongfaf09ba2010-12-02 17:42:08 -0800154 options.setSeekTo(frameTimeUs, mode);
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800155 }
156
157 MediaBuffer *buffer = NULL;
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800158 do {
159 if (buffer != NULL) {
160 buffer->release();
161 buffer = NULL;
162 }
163 err = decoder->read(&buffer, &options);
164 options.clearSeekTo();
165 } while (err == INFO_FORMAT_CHANGED
166 || (buffer != NULL && buffer->range_length() == 0));
167
168 if (err != OK) {
169 CHECK_EQ(buffer, NULL);
170
171 LOGV("decoding frame failed.");
172 decoder->stop();
173
174 return NULL;
175 }
176
177 LOGV("successfully decoded video frame.");
178
Andreas Huber1e194162010-10-06 16:43:57 -0700179 int32_t unreadable;
180 if (buffer->meta_data()->findInt32(kKeyIsUnreadable, &unreadable)
181 && unreadable != 0) {
182 LOGV("video frame is unreadable, decoder does not give us access "
183 "to the video data.");
184
185 buffer->release();
186 buffer = NULL;
187
188 decoder->stop();
189
190 return NULL;
191 }
192
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800193 int64_t timeUs;
194 CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
195 if (thumbNailTime >= 0) {
196 if (timeUs != thumbNailTime) {
197 const char *mime;
198 CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
199
200 LOGV("thumbNailTime = %lld us, timeUs = %lld us, mime = %s",
201 thumbNailTime, timeUs, mime);
202 }
203 }
204
205 sp<MetaData> meta = decoder->getFormat();
206
207 int32_t width, height;
208 CHECK(meta->findInt32(kKeyWidth, &width));
209 CHECK(meta->findInt32(kKeyHeight, &height));
210
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800211 int32_t crop_left, crop_top, crop_right, crop_bottom;
212 if (!meta->findRect(
213 kKeyCropRect,
214 &crop_left, &crop_top, &crop_right, &crop_bottom)) {
215 crop_left = crop_top = 0;
216 crop_right = width - 1;
217 crop_bottom = height - 1;
218 }
219
James Dong53ebc722010-11-08 16:04:27 -0800220 int32_t rotationAngle;
221 if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
222 rotationAngle = 0; // By default, no rotation
223 }
224
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800225 VideoFrame *frame = new VideoFrame;
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800226 frame->mWidth = crop_right - crop_left + 1;
227 frame->mHeight = crop_bottom - crop_top + 1;
228 frame->mDisplayWidth = frame->mWidth;
229 frame->mDisplayHeight = frame->mHeight;
230 frame->mSize = frame->mWidth * frame->mHeight * 2;
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800231 frame->mData = new uint8_t[frame->mSize];
James Dong53ebc722010-11-08 16:04:27 -0800232 frame->mRotationAngle = rotationAngle;
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800233
234 int32_t srcFormat;
235 CHECK(meta->findInt32(kKeyColorFormat, &srcFormat));
236
237 ColorConverter converter(
238 (OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565);
239 CHECK(converter.isValid());
240
Andreas Huberc0dbe3a2011-01-06 11:26:54 -0800241 err = converter.convert(
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800242 (const uint8_t *)buffer->data() + buffer->range_offset(),
Andreas Huber1bb0ffd2010-11-22 13:06:35 -0800243 width, height,
244 crop_left, crop_top, crop_right, crop_bottom,
245 frame->mData,
246 frame->mWidth,
247 frame->mHeight,
248 0, 0, frame->mWidth - 1, frame->mHeight - 1);
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800249
250 buffer->release();
251 buffer = NULL;
252
253 decoder->stop();
254
Andreas Huberc0dbe3a2011-01-06 11:26:54 -0800255 if (err != OK) {
256 LOGE("Colorconverter failed to convert frame.");
257
258 delete frame;
259 frame = NULL;
260 }
261
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800262 return frame;
263}
264
James Dongfaf09ba2010-12-02 17:42:08 -0800265VideoFrame *StagefrightMetadataRetriever::getFrameAtTime(
266 int64_t timeUs, int option) {
Andreas Huber53a76bd2009-10-06 16:20:44 -0700267
James Dongfaf09ba2010-12-02 17:42:08 -0800268 LOGV("getFrameAtTime: %lld us option: %d", timeUs, option);
Andreas Hubere3452d32010-03-12 15:08:52 -0800269
Andreas Huber53a76bd2009-10-06 16:20:44 -0700270 if (mExtractor.get() == NULL) {
Andreas Huberad285432009-10-22 09:44:00 -0700271 LOGV("no extractor.");
Andreas Huber53a76bd2009-10-06 16:20:44 -0700272 return NULL;
273 }
274
Glenn Kasten5f630692011-02-21 09:37:13 -0800275 int32_t drm = 0;
276 if (mExtractor->getMetaData()->findInt32(kKeyIsDRM, &drm) && drm != 0) {
277 LOGE("frame grab not allowed.");
278 return NULL;
279 }
280
Andreas Huber53a76bd2009-10-06 16:20:44 -0700281 size_t n = mExtractor->countTracks();
282 size_t i;
283 for (i = 0; i < n; ++i) {
284 sp<MetaData> meta = mExtractor->getTrackMetaData(i);
285
286 const char *mime;
287 CHECK(meta->findCString(kKeyMIMEType, &mime));
288
289 if (!strncasecmp(mime, "video/", 6)) {
290 break;
291 }
292 }
293
294 if (i == n) {
Andreas Huberad285432009-10-22 09:44:00 -0700295 LOGV("no video track found.");
Andreas Huber53a76bd2009-10-06 16:20:44 -0700296 return NULL;
297 }
298
Andreas Hubere981c332009-10-22 13:49:30 -0700299 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(
300 i, MediaExtractor::kIncludeExtensiveMetaData);
301
Andreas Huber53a76bd2009-10-06 16:20:44 -0700302 sp<MediaSource> source = mExtractor->getTrack(i);
303
304 if (source.get() == NULL) {
Andreas Huberad285432009-10-22 09:44:00 -0700305 LOGV("unable to instantiate video track.");
Andreas Huber53a76bd2009-10-06 16:20:44 -0700306 return NULL;
307 }
308
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800309 VideoFrame *frame =
310 extractVideoFrameWithCodecFlags(
James Dongfaf09ba2010-12-02 17:42:08 -0800311 &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs,
312 timeUs, option);
Andreas Huber53a76bd2009-10-06 16:20:44 -0700313
Andreas Huber0ffc90f2010-01-14 14:15:33 -0800314 if (frame == NULL) {
315 LOGV("Software decoder failed to extract thumbnail, "
316 "trying hardware decoder.");
Andreas Huber53a76bd2009-10-06 16:20:44 -0700317
James Dongfaf09ba2010-12-02 17:42:08 -0800318 frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0,
319 timeUs, option);
Andreas Huber53a76bd2009-10-06 16:20:44 -0700320 }
321
Andreas Huber53a76bd2009-10-06 16:20:44 -0700322 return frame;
323}
324
325MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
326 LOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
327
Andreas Huberaee3c632010-01-11 15:35:19 -0800328 if (mExtractor == NULL) {
329 return NULL;
330 }
331
332 if (!mParsedMetaData) {
333 parseMetaData();
334
335 mParsedMetaData = true;
336 }
337
338 if (mAlbumArt) {
339 return new MediaAlbumArt(*mAlbumArt);
340 }
341
Andreas Huber53a76bd2009-10-06 16:20:44 -0700342 return NULL;
343}
344
345const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
Andreas Huberaee3c632010-01-11 15:35:19 -0800346 if (mExtractor == NULL) {
347 return NULL;
348 }
349
350 if (!mParsedMetaData) {
351 parseMetaData();
352
353 mParsedMetaData = true;
354 }
355
356 ssize_t index = mMetaData.indexOfKey(keyCode);
357
358 if (index < 0) {
359 return NULL;
360 }
361
362 return strdup(mMetaData.valueAt(index).string());
Andreas Huber53a76bd2009-10-06 16:20:44 -0700363}
364
Andreas Huberaee3c632010-01-11 15:35:19 -0800365void StagefrightMetadataRetriever::parseMetaData() {
366 sp<MetaData> meta = mExtractor->getMetaData();
367
368 struct Map {
369 int from;
370 int to;
371 };
372 static const Map kMap[] = {
Andreas Huber1cb02bf2010-01-13 11:25:10 -0800373 { kKeyMIMEType, METADATA_KEY_MIMETYPE },
Andreas Huber3a3656c2010-01-13 10:45:49 -0800374 { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER },
Marco Nelissen655306f2010-02-08 14:50:19 -0800375 { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER },
Andreas Huberaee3c632010-01-11 15:35:19 -0800376 { kKeyAlbum, METADATA_KEY_ALBUM },
377 { kKeyArtist, METADATA_KEY_ARTIST },
Marco Nelissenc5d5ee32010-02-11 13:31:44 -0800378 { kKeyAlbumArtist, METADATA_KEY_ALBUMARTIST },
Andreas Huber3a3656c2010-01-13 10:45:49 -0800379 { kKeyAuthor, METADATA_KEY_AUTHOR },
Andreas Huberaee3c632010-01-11 15:35:19 -0800380 { kKeyComposer, METADATA_KEY_COMPOSER },
Andreas Huber3a3656c2010-01-13 10:45:49 -0800381 { kKeyDate, METADATA_KEY_DATE },
Andreas Huberaee3c632010-01-11 15:35:19 -0800382 { kKeyGenre, METADATA_KEY_GENRE },
383 { kKeyTitle, METADATA_KEY_TITLE },
384 { kKeyYear, METADATA_KEY_YEAR },
Andreas Huberc2c9dd32010-01-19 16:43:53 -0800385 { kKeyWriter, METADATA_KEY_WRITER },
Marco Nelissenee35aff2011-01-06 11:12:17 -0800386 { kKeyCompilation, METADATA_KEY_COMPILATION },
Andreas Huberaee3c632010-01-11 15:35:19 -0800387 };
388 static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
389
390 for (size_t i = 0; i < kNumMapEntries; ++i) {
391 const char *value;
392 if (meta->findCString(kMap[i].from, &value)) {
393 mMetaData.add(kMap[i].to, String8(value));
394 }
395 }
396
397 const void *data;
398 uint32_t type;
399 size_t dataSize;
400 if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize)) {
401 mAlbumArt = new MediaAlbumArt;
402 mAlbumArt->mSize = dataSize;
403 mAlbumArt->mData = new uint8_t[dataSize];
404 memcpy(mAlbumArt->mData, data, dataSize);
405 }
406
Andreas Huber3a3656c2010-01-13 10:45:49 -0800407 size_t numTracks = mExtractor->countTracks();
408
409 char tmp[32];
410 sprintf(tmp, "%d", numTracks);
411
412 mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp));
413
Andreas Huberc4c38fc2011-03-04 10:24:54 -0800414 bool hasAudio = false;
415 bool hasVideo = false;
416 int32_t videoWidth = -1;
417 int32_t videoHeight = -1;
418 int32_t audioBitrate = -1;
419
Andreas Huberaee3c632010-01-11 15:35:19 -0800420 // The overall duration is the duration of the longest track.
421 int64_t maxDurationUs = 0;
Andreas Huber3a3656c2010-01-13 10:45:49 -0800422 for (size_t i = 0; i < numTracks; ++i) {
Andreas Huberaee3c632010-01-11 15:35:19 -0800423 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
424
425 int64_t durationUs;
426 if (trackMeta->findInt64(kKeyDuration, &durationUs)) {
427 if (durationUs > maxDurationUs) {
428 maxDurationUs = durationUs;
429 }
430 }
Andreas Huberc4c38fc2011-03-04 10:24:54 -0800431
432 const char *mime;
433 if (trackMeta->findCString(kKeyMIMEType, &mime)) {
434 if (!hasAudio && !strncasecmp("audio/", mime, 6)) {
435 hasAudio = true;
436
437 if (!trackMeta->findInt32(kKeyBitRate, &audioBitrate)) {
438 audioBitrate = -1;
439 }
440 } else if (!hasVideo && !strncasecmp("video/", mime, 6)) {
441 hasVideo = true;
442
443 CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth));
444 CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight));
445 }
446 }
Andreas Huberaee3c632010-01-11 15:35:19 -0800447 }
448
449 // The duration value is a string representing the duration in ms.
Andreas Huberaee3c632010-01-11 15:35:19 -0800450 sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000);
Andreas Huberaee3c632010-01-11 15:35:19 -0800451 mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
Andreas Huber072f5242010-05-20 14:56:53 -0700452
Andreas Huberc4c38fc2011-03-04 10:24:54 -0800453 if (hasAudio) {
454 mMetaData.add(METADATA_KEY_HAS_AUDIO, String8("yes"));
455 }
456
457 if (hasVideo) {
458 mMetaData.add(METADATA_KEY_HAS_VIDEO, String8("yes"));
459
460 sprintf(tmp, "%d", videoWidth);
461 mMetaData.add(METADATA_KEY_VIDEO_WIDTH, String8(tmp));
462
463 sprintf(tmp, "%d", videoHeight);
464 mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp));
465 }
466
467 if (numTracks == 1 && hasAudio && audioBitrate >= 0) {
468 sprintf(tmp, "%ld", audioBitrate);
469 mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
470 } else {
471 off64_t sourceSize;
472 if (mSource->getSize(&sourceSize) == OK) {
473 int64_t avgBitRate = (int64_t)(sourceSize * 8E6 / maxDurationUs);
474
475 sprintf(tmp, "%lld", avgBitRate);
476 mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
477 }
478 }
479
Andreas Huber072f5242010-05-20 14:56:53 -0700480 if (numTracks == 1) {
481 const char *fileMIME;
482 CHECK(meta->findCString(kKeyMIMEType, &fileMIME));
483
484 if (!strcasecmp(fileMIME, "video/x-matroska")) {
485 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(0);
486 const char *trackMIME;
487 CHECK(trackMeta->findCString(kKeyMIMEType, &trackMIME));
488
489 if (!strncasecmp("audio/", trackMIME, 6)) {
490 // The matroska file only contains a single audio track,
491 // rewrite its mime type.
492 mMetaData.add(
493 METADATA_KEY_MIMETYPE, String8("audio/x-matroska"));
494 }
495 }
496 }
Andreas Huberaee3c632010-01-11 15:35:19 -0800497}
498
Andreas Huber53a76bd2009-10-06 16:20:44 -0700499} // namespace android