1 /*
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
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "StagefrightMetadataRetriever"
19
20 #include <inttypes.h>
21
22 #include <utils/Log.h>
23 #include <cutils/properties.h>
24
25 #include "StagefrightMetadataRetriever.h"
26 #include "FrameDecoder.h"
27
28 #include <datasource/PlayerServiceDataSourceFactory.h>
29 #include <datasource/PlayerServiceFileSource.h>
30 #include <media/IMediaHTTPService.h>
31 #include <media/stagefright/MediaCodecConstants.h>
32 #include <media/stagefright/foundation/ADebug.h>
33 #include <media/stagefright/foundation/AMessage.h>
34 #include <media/stagefright/MediaCodecList.h>
35 #include <media/stagefright/MediaDefs.h>
36 #include <media/stagefright/MediaErrors.h>
37 #include <media/stagefright/MediaExtractor.h>
38 #include <media/stagefright/MediaExtractorFactory.h>
39 #include <media/stagefright/MetaData.h>
40 #include <media/stagefright/Utils.h>
41 #include <media/CharacterEncodingDetector.h>
42
43 namespace android {
44
StagefrightMetadataRetriever()45 StagefrightMetadataRetriever::StagefrightMetadataRetriever()
46 : mParsedMetaData(false),
47 mAlbumArt(NULL),
48 mLastDecodedIndex(-1) {
49 ALOGV("StagefrightMetadataRetriever()");
50 }
51
~StagefrightMetadataRetriever()52 StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
53 ALOGV("~StagefrightMetadataRetriever()");
54 clearMetadata();
55 if (mSource != NULL) {
56 mSource->close();
57 }
58 }
59
setDataSource(const sp<IMediaHTTPService> & httpService,const char * uri,const KeyedVector<String8,String8> * headers)60 status_t StagefrightMetadataRetriever::setDataSource(
61 const sp<IMediaHTTPService> &httpService,
62 const char *uri,
63 const KeyedVector<String8, String8> *headers) {
64 ALOGV("setDataSource(%s)", uri);
65
66 clearMetadata();
67 mSource = PlayerServiceDataSourceFactory::getInstance()->CreateFromURI(
68 httpService, uri, headers);
69
70 if (mSource == NULL) {
71 ALOGE("Unable to create data source for '%s'.", uri);
72 return UNKNOWN_ERROR;
73 }
74
75 mExtractor = MediaExtractorFactory::Create(mSource);
76
77 if (mExtractor == NULL) {
78 ALOGE("Unable to instantiate an extractor for '%s'.", uri);
79
80 mSource.clear();
81
82 return UNKNOWN_ERROR;
83 }
84
85 return OK;
86 }
87
88 // Warning caller retains ownership of the filedescriptor! Dup it if necessary.
setDataSource(int fd,int64_t offset,int64_t length)89 status_t StagefrightMetadataRetriever::setDataSource(
90 int fd, int64_t offset, int64_t length) {
91 fd = dup(fd);
92
93 ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
94
95 clearMetadata();
96 mSource = new PlayerServiceFileSource(fd, offset, length);
97
98 status_t err;
99 if ((err = mSource->initCheck()) != OK) {
100 mSource.clear();
101
102 return err;
103 }
104
105 mExtractor = MediaExtractorFactory::Create(mSource);
106
107 if (mExtractor == NULL) {
108 mSource.clear();
109
110 return UNKNOWN_ERROR;
111 }
112
113 return OK;
114 }
115
setDataSource(const sp<DataSource> & source,const char * mime)116 status_t StagefrightMetadataRetriever::setDataSource(
117 const sp<DataSource>& source, const char *mime) {
118 ALOGV("setDataSource(DataSource)");
119
120 clearMetadata();
121 mSource = source;
122 mExtractor = MediaExtractorFactory::Create(mSource, mime);
123
124 if (mExtractor == NULL) {
125 ALOGE("Failed to instantiate a MediaExtractor.");
126 mSource.clear();
127 return UNKNOWN_ERROR;
128 }
129
130 return OK;
131 }
132
getImageAtIndex(int index,int colorFormat,bool metaOnly,bool thumbnail)133 sp<IMemory> StagefrightMetadataRetriever::getImageAtIndex(
134 int index, int colorFormat, bool metaOnly, bool thumbnail) {
135 ALOGV("getImageAtIndex: index(%d) colorFormat(%d) metaOnly(%d) thumbnail(%d)",
136 index, colorFormat, metaOnly, thumbnail);
137
138 return getImageInternal(index, colorFormat, metaOnly, thumbnail, NULL);
139 }
140
getImageRectAtIndex(int index,int colorFormat,int left,int top,int right,int bottom)141 sp<IMemory> StagefrightMetadataRetriever::getImageRectAtIndex(
142 int index, int colorFormat, int left, int top, int right, int bottom) {
143 ALOGV("getImageRectAtIndex: index(%d) colorFormat(%d) rect {%d, %d, %d, %d}",
144 index, colorFormat, left, top, right, bottom);
145
146 FrameRect rect = {left, top, right, bottom};
147
148 if (mDecoder != NULL && index == mLastDecodedIndex) {
149 return mDecoder->extractFrame(&rect);
150 }
151
152 return getImageInternal(
153 index, colorFormat, false /*metaOnly*/, false /*thumbnail*/, &rect);
154 }
155
getImageInternal(int index,int colorFormat,bool metaOnly,bool thumbnail,FrameRect * rect)156 sp<IMemory> StagefrightMetadataRetriever::getImageInternal(
157 int index, int colorFormat, bool metaOnly, bool thumbnail, FrameRect* rect) {
158 mDecoder.clear();
159 mLastDecodedIndex = -1;
160
161 if (mExtractor.get() == NULL) {
162 ALOGE("no extractor.");
163 return NULL;
164 }
165
166 size_t n = mExtractor->countTracks();
167 size_t i;
168 int imageCount = 0;
169
170 for (i = 0; i < n; ++i) {
171 sp<MetaData> meta = mExtractor->getTrackMetaData(i);
172 if (!meta) {
173 continue;
174 }
175 ALOGV("getting track %zu of %zu, meta=%s", i, n, meta->toString().c_str());
176
177 const char *mime;
178 if (meta->findCString(kKeyMIMEType, &mime) && !strncasecmp(mime, "image/", 6)) {
179 int32_t isPrimary;
180 if ((index < 0 && meta->findInt32(
181 kKeyTrackIsDefault, &isPrimary) && isPrimary)
182 || (index == imageCount++)) {
183 break;
184 }
185 }
186 }
187
188 if (i == n) {
189 ALOGE("image track not found.");
190 return NULL;
191 }
192
193 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
194 if (!trackMeta) {
195 return NULL;
196 }
197
198 const char *mime;
199 bool isHeif = false;
200 if (!trackMeta->findCString(kKeyMIMEType, &mime)) {
201 ALOGE("image track has no mime type");
202 return NULL;
203 }
204 ALOGV("extracting from %s track", mime);
205 if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
206 mime = MEDIA_MIMETYPE_VIDEO_HEVC;
207 trackMeta = new MetaData(*trackMeta);
208 trackMeta->setCString(kKeyMIMEType, mime);
209 isHeif = true;
210 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_AVIF)) {
211 mime = MEDIA_MIMETYPE_VIDEO_AV1;
212 trackMeta = new MetaData(*trackMeta);
213 trackMeta->setCString(kKeyMIMEType, mime);
214 isHeif = true;
215 }
216
217 sp<AMessage> format = new AMessage;
218 status_t err = convertMetaDataToMessage(trackMeta, &format);
219 if (err != OK) {
220 ALOGE("getImageInternal: convertMetaDataToMessage() failed, unable to extract image");
221 return NULL;
222 }
223
224 uint32_t bitDepth = 8;
225 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
226 int32_t profile;
227 if (format->findInt32("profile", &profile)) {
228 if (HEVCProfileMain10 == profile || HEVCProfileMain10HDR10 == profile ||
229 HEVCProfileMain10HDR10Plus == profile) {
230 bitDepth = 10;
231 }
232 }
233 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
234 int32_t profile;
235 if (format->findInt32("profile", &profile)) {
236 if (AV1ProfileMain10 == profile || AV1ProfileMain10HDR10 == profile ||
237 AV1ProfileMain10HDR10Plus == profile) {
238 bitDepth = 10;
239 }
240 }
241 }
242
243 if (metaOnly) {
244 return FrameDecoder::getMetadataOnly(trackMeta, colorFormat, thumbnail, bitDepth);
245 }
246
247 sp<IMediaSource> source = mExtractor->getTrack(i);
248
249 if (source.get() == NULL) {
250 ALOGE("unable to instantiate image track.");
251 return NULL;
252 }
253
254 bool preferhw = property_get_bool(
255 "media.stagefright.thumbnail.prefer_hw_codecs", false);
256 uint32_t flags = preferhw ? 0 : MediaCodecList::kPreferSoftwareCodecs;
257 Vector<AString> matchingCodecs;
258
259 // If decoding thumbnail check decoder supports thumbnail dimensions instead
260 int32_t thumbHeight, thumbWidth;
261 if (thumbnail && format != NULL
262 && trackMeta->findInt32(kKeyThumbnailHeight, &thumbHeight)
263 && trackMeta->findInt32(kKeyThumbnailWidth, &thumbWidth)) {
264 format->setInt32("height", thumbHeight);
265 format->setInt32("width", thumbWidth);
266 }
267
268 // If decoding tiled HEIF check decoder supports tile dimensions instead
269 if (!thumbnail && isHeif && format != NULL) {
270 int32_t tileWidth, tileHeight;
271 if (trackMeta->findInt32(kKeyTileWidth, &tileWidth) && tileWidth > 0
272 && trackMeta->findInt32(kKeyTileHeight, &tileHeight) && tileHeight > 0) {
273 format->setInt32("height", tileHeight);
274 format->setInt32("width", tileWidth);
275 }
276 }
277
278 MediaCodecList::findMatchingCodecs(
279 mime,
280 false, /* encoder */
281 flags,
282 format,
283 &matchingCodecs);
284
285 for (size_t i = 0; i < matchingCodecs.size(); ++i) {
286 const AString &componentName = matchingCodecs[i];
287 sp<MediaImageDecoder> decoder = new MediaImageDecoder(componentName, trackMeta, source);
288 int64_t frameTimeUs = thumbnail ? -1 : 0;
289 if (decoder->init(frameTimeUs, 0 /*option*/, colorFormat) == OK) {
290 sp<IMemory> frame = decoder->extractFrame(rect);
291
292 if (frame != NULL) {
293 if (rect != NULL) {
294 // keep the decoder if slice decoding
295 mDecoder = decoder;
296 mLastDecodedIndex = index;
297 }
298 return frame;
299 }
300 }
301 ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str());
302 }
303
304 ALOGE("all codecs failed to extract frame.");
305 return NULL;
306 }
307
getFrameAtTime(int64_t timeUs,int option,int colorFormat,bool metaOnly)308 sp<IMemory> StagefrightMetadataRetriever::getFrameAtTime(
309 int64_t timeUs, int option, int colorFormat, bool metaOnly) {
310 ALOGV("getFrameAtTime: %" PRId64 " us option: %d colorFormat: %d, metaOnly: %d",
311 timeUs, option, colorFormat, metaOnly);
312
313 return getFrameInternal(timeUs, option, colorFormat, metaOnly);
314 }
315
getFrameAtIndex(int frameIndex,int colorFormat,bool metaOnly)316 sp<IMemory> StagefrightMetadataRetriever::getFrameAtIndex(
317 int frameIndex, int colorFormat, bool metaOnly) {
318 ALOGV("getFrameAtIndex: frameIndex %d, colorFormat: %d, metaOnly: %d",
319 frameIndex, colorFormat, metaOnly);
320 if (mDecoder != NULL && frameIndex == mLastDecodedIndex + 1) {
321 sp<IMemory> frame = mDecoder->extractFrame();
322 if (frame != nullptr) {
323 mLastDecodedIndex = frameIndex;
324 }
325 return frame;
326 }
327
328 return getFrameInternal(frameIndex,
329 MediaSource::ReadOptions::SEEK_FRAME_INDEX, colorFormat, metaOnly);
330 }
331
getFrameInternal(int64_t timeUs,int option,int colorFormat,bool metaOnly)332 sp<IMemory> StagefrightMetadataRetriever::getFrameInternal(
333 int64_t timeUs, int option, int colorFormat, bool metaOnly) {
334 mDecoder.clear();
335 mLastDecodedIndex = -1;
336
337 if (mExtractor.get() == NULL) {
338 ALOGE("no extractor.");
339 return NULL;
340 }
341
342 sp<MetaData> fileMeta = mExtractor->getMetaData();
343
344 if (fileMeta == NULL) {
345 ALOGE("extractor doesn't publish metadata, failed to initialize?");
346 return NULL;
347 }
348
349 size_t n = mExtractor->countTracks();
350 size_t i;
351 for (i = 0; i < n; ++i) {
352 sp<MetaData> meta = mExtractor->getTrackMetaData(i);
353 if (!meta) {
354 continue;
355 }
356
357 const char *mime;
358 if (meta->findCString(kKeyMIMEType, &mime) && !strncasecmp(mime, "video/", 6)) {
359 break;
360 }
361 }
362
363 if (i == n) {
364 ALOGE("no video track found.");
365 return NULL;
366 }
367
368 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(
369 i, MediaExtractor::kIncludeExtensiveMetaData);
370 if (!trackMeta) {
371 return NULL;
372 }
373
374 if (metaOnly) {
375 return FrameDecoder::getMetadataOnly(trackMeta, colorFormat);
376 }
377
378 sp<IMediaSource> source = mExtractor->getTrack(i);
379
380 if (source.get() == NULL) {
381 ALOGV("unable to instantiate video track.");
382 return NULL;
383 }
384
385 const void *data;
386 uint32_t type;
387 size_t dataSize;
388 if (fileMeta->findData(kKeyAlbumArt, &type, &data, &dataSize)
389 && mAlbumArt == NULL) {
390 mAlbumArt = MediaAlbumArt::fromData(dataSize, data);
391 }
392
393 const char *mime;
394 if (!trackMeta->findCString(kKeyMIMEType, &mime)) {
395 ALOGE("video track has no mime information.");
396 return NULL;
397 }
398
399 bool preferhw = property_get_bool(
400 "media.stagefright.thumbnail.prefer_hw_codecs", false);
401 uint32_t flags = preferhw ? 0 : MediaCodecList::kPreferSoftwareCodecs;
402 sp<AMessage> format = new AMessage;
403 status_t err = convertMetaDataToMessage(trackMeta, &format);
404 if (err != OK) {
405 ALOGE("getFrameInternal: convertMetaDataToMessage() failed, unable to extract frame");
406 return NULL;
407 }
408
409 Vector<AString> matchingCodecs;
410 MediaCodecList::findMatchingCodecs(
411 mime,
412 false, /* encoder */
413 flags,
414 format,
415 &matchingCodecs);
416
417 for (size_t i = 0; i < matchingCodecs.size(); ++i) {
418 const AString &componentName = matchingCodecs[i];
419 sp<VideoFrameDecoder> decoder = new VideoFrameDecoder(componentName, trackMeta, source);
420 if (decoder->init(timeUs, option, colorFormat) == OK) {
421 sp<IMemory> frame = decoder->extractFrame();
422 if (frame != nullptr) {
423 // keep the decoder if seeking by frame index
424 if (option == MediaSource::ReadOptions::SEEK_FRAME_INDEX) {
425 mDecoder = decoder;
426 mLastDecodedIndex = timeUs;
427 }
428 return frame;
429 }
430 }
431 ALOGV("%s failed to extract frame, trying next decoder.", componentName.c_str());
432 }
433
434 ALOGE("all codecs failed to extract frame.");
435 return NULL;
436 }
437
extractAlbumArt()438 MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
439 ALOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
440
441 if (mExtractor == NULL) {
442 return NULL;
443 }
444
445 if (!mParsedMetaData) {
446 parseMetaData();
447
448 mParsedMetaData = true;
449 }
450
451 if (mAlbumArt) {
452 return mAlbumArt->clone();
453 }
454
455 return NULL;
456 }
457
extractMetadata(int keyCode)458 const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
459 if (mExtractor == NULL) {
460 return NULL;
461 }
462
463 if (!mParsedMetaData) {
464 parseMetaData();
465
466 mParsedMetaData = true;
467 }
468
469 ssize_t index = mMetaData.indexOfKey(keyCode);
470
471 if (index < 0) {
472 return NULL;
473 }
474
475 return mMetaData.valueAt(index).c_str();
476 }
477
parseColorAspects(const sp<MetaData> & meta)478 void StagefrightMetadataRetriever::parseColorAspects(const sp<MetaData>& meta) {
479 sp<AMessage> format = new AMessage();
480 if (convertMetaDataToMessage(meta, &format) != OK) {
481 return;
482 }
483
484 int32_t standard, transfer, range;
485 if (format->findInt32("color-standard", &standard)
486 && format->findInt32("color-transfer", &transfer)
487 && format->findInt32("color-range", &range)) {
488 ALOGV("found color aspects : standard=%d, transfer=%d, range=%d",
489 standard, transfer, range);
490
491 mMetaData.add(METADATA_KEY_COLOR_STANDARD, String8::format("%d", standard));
492 mMetaData.add(METADATA_KEY_COLOR_TRANSFER, String8::format("%d", transfer));
493 mMetaData.add(METADATA_KEY_COLOR_RANGE, String8::format("%d", range));
494 }
495 }
496
parseMetaData()497 void StagefrightMetadataRetriever::parseMetaData() {
498 sp<MetaData> meta = mExtractor->getMetaData();
499
500 if (meta == NULL) {
501 ALOGV("extractor doesn't publish metadata, failed to initialize?");
502 return;
503 }
504
505 struct Map {
506 int from;
507 int to;
508 const char *name;
509 };
510 static const Map kMap[] = {
511 { kKeyMIMEType, METADATA_KEY_MIMETYPE, NULL },
512 { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER, "tracknumber" },
513 { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER, "discnumber" },
514 { kKeyAlbum, METADATA_KEY_ALBUM, "album" },
515 { kKeyArtist, METADATA_KEY_ARTIST, "artist" },
516 { kKeyAlbumArtist, METADATA_KEY_ALBUMARTIST, "albumartist" },
517 { kKeyAuthor, METADATA_KEY_AUTHOR, NULL },
518 { kKeyComposer, METADATA_KEY_COMPOSER, "composer" },
519 { kKeyDate, METADATA_KEY_DATE, NULL },
520 { kKeyGenre, METADATA_KEY_GENRE, "genre" },
521 { kKeyTitle, METADATA_KEY_TITLE, "title" },
522 { kKeyYear, METADATA_KEY_YEAR, "year" },
523 { kKeyWriter, METADATA_KEY_WRITER, "writer" },
524 { kKeyCompilation, METADATA_KEY_COMPILATION, "compilation" },
525 { kKeyLocation, METADATA_KEY_LOCATION, NULL },
526 };
527
528 static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
529
530 CharacterEncodingDetector *detector = new CharacterEncodingDetector();
531
532 for (size_t i = 0; i < kNumMapEntries; ++i) {
533 const char *value;
534 if (meta->findCString(kMap[i].from, &value)) {
535 if (kMap[i].name) {
536 // add to charset detector
537 detector->addTag(kMap[i].name, value);
538 } else {
539 // directly add to output list
540 mMetaData.add(kMap[i].to, String8(value));
541 }
542 }
543 }
544
545 detector->detectAndConvert();
546 int size = detector->size();
547 if (size) {
548 for (int i = 0; i < size; i++) {
549 const char *name;
550 const char *value;
551 detector->getTag(i, &name, &value);
552 for (size_t j = 0; j < kNumMapEntries; ++j) {
553 if (kMap[j].name && !strcmp(kMap[j].name, name)) {
554 mMetaData.add(kMap[j].to, String8(value));
555 }
556 }
557 }
558 }
559 delete detector;
560
561 const void *data;
562 uint32_t type;
563 size_t dataSize;
564 if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize)
565 && mAlbumArt == NULL) {
566 mAlbumArt = MediaAlbumArt::fromData(dataSize, data);
567 }
568
569 size_t numTracks = mExtractor->countTracks();
570
571 char tmp[32];
572 sprintf(tmp, "%zu", numTracks);
573
574 mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp));
575
576 float captureFps;
577 if (meta->findFloat(kKeyCaptureFramerate, &captureFps)) {
578 sprintf(tmp, "%f", captureFps);
579 mMetaData.add(METADATA_KEY_CAPTURE_FRAMERATE, String8(tmp));
580 }
581
582 int64_t exifOffset, exifSize;
583 if (meta->findInt64(kKeyExifOffset, &exifOffset)
584 && meta->findInt64(kKeyExifSize, &exifSize)) {
585 sprintf(tmp, "%lld", (long long)exifOffset);
586 mMetaData.add(METADATA_KEY_EXIF_OFFSET, String8(tmp));
587 sprintf(tmp, "%lld", (long long)exifSize);
588 mMetaData.add(METADATA_KEY_EXIF_LENGTH, String8(tmp));
589 }
590
591 int64_t xmpOffset, xmpSize;
592 if (meta->findInt64(kKeyXmpOffset, &xmpOffset)
593 && meta->findInt64(kKeyXmpSize, &xmpSize)) {
594 sprintf(tmp, "%lld", (long long)xmpOffset);
595 mMetaData.add(METADATA_KEY_XMP_OFFSET, String8(tmp));
596 sprintf(tmp, "%lld", (long long)xmpSize);
597 mMetaData.add(METADATA_KEY_XMP_LENGTH, String8(tmp));
598 }
599
600 bool hasAudio = false;
601 bool hasVideo = false;
602 int32_t videoWidth = -1;
603 int32_t videoHeight = -1;
604 int32_t videoFrameCount = 0;
605 int32_t audioBitrate = -1;
606 int32_t rotationAngle = -1;
607 int32_t imageCount = 0;
608 int32_t imagePrimary = -1;
609 int32_t imageWidth = -1;
610 int32_t imageHeight = -1;
611 int32_t imageRotation = -1;
612
613 // The overall duration is the duration of the longest track.
614 int64_t maxDurationUs = 0;
615 String8 timedTextLang, videoMime;
616 for (size_t i = 0; i < numTracks; ++i) {
617 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
618 if (!trackMeta) {
619 continue;
620 }
621
622 int64_t durationUs;
623 if (trackMeta->findInt64(kKeyDuration, &durationUs)) {
624 if (durationUs > maxDurationUs) {
625 maxDurationUs = durationUs;
626 }
627 }
628
629 const char *mime;
630 if (trackMeta->findCString(kKeyMIMEType, &mime)) {
631 if (!hasAudio && !strncasecmp("audio/", mime, 6)) {
632 hasAudio = true;
633
634 if (!trackMeta->findInt32(kKeyBitRate, &audioBitrate)) {
635 audioBitrate = -1;
636 }
637
638 int32_t bitsPerSample = -1;
639 int32_t sampleRate = -1;
640 trackMeta->findInt32(kKeyBitsPerSample, &bitsPerSample);
641 trackMeta->findInt32(kKeySampleRate, &sampleRate);
642 if (bitsPerSample >= 0) {
643 sprintf(tmp, "%d", bitsPerSample);
644 mMetaData.add(METADATA_KEY_BITS_PER_SAMPLE, String8(tmp));
645 }
646 if (sampleRate >= 0) {
647 sprintf(tmp, "%d", sampleRate);
648 mMetaData.add(METADATA_KEY_SAMPLERATE, String8(tmp));
649 }
650 } else if (!hasVideo && !strncasecmp("video/", mime, 6)) {
651 if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
652 rotationAngle = 0;
653 }
654 if (!trackMeta->findInt32(kKeyFrameCount, &videoFrameCount)) {
655 videoFrameCount = 0;
656 }
657 if (trackMeta->findInt32(kKeyWidth, &videoWidth)
658 && trackMeta->findInt32(kKeyHeight, &videoHeight)) {
659 hasVideo = true;
660 videoMime = String8(mime);
661 parseColorAspects(trackMeta);
662 } else {
663 ALOGE("video track ignored for missing dimensions");
664 }
665 } else if (!strncasecmp("image/", mime, 6)) {
666 int32_t isPrimary;
667 if (trackMeta->findInt32(
668 kKeyTrackIsDefault, &isPrimary) && isPrimary) {
669 if (!trackMeta->findInt32(kKeyRotation, &imageRotation)) {
670 imageRotation = 0;
671 }
672 if (trackMeta->findInt32(kKeyWidth, &imageWidth)
673 && trackMeta->findInt32(kKeyHeight, &imageHeight)) {
674 imagePrimary = imageCount;
675 int32_t displayLeft;
676 int32_t displayTop;
677 int32_t displayRight;
678 int32_t displayBottom;
679 if (trackMeta->findRect(kKeyCropRect, &displayLeft, &displayTop,
680 &displayRight, &displayBottom)
681 && displayLeft >= 0 && displayTop >= 0 && displayRight < imageWidth
682 && displayBottom < imageHeight && displayLeft <= displayRight
683 && displayTop <= displayBottom) {
684 imageWidth = displayRight - displayLeft + 1;
685 imageHeight = displayBottom - displayTop + 1;
686 }
687 } else {
688 ALOGE("primary image track ignored for missing dimensions");
689 }
690 }
691 imageCount++;
692 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
693 const char *lang;
694 if (trackMeta->findCString(kKeyMediaLanguage, &lang)) {
695 timedTextLang.append(String8(lang));
696 timedTextLang.append(String8(":"));
697 } else {
698 ALOGE("No language found for timed text");
699 }
700 }
701 }
702 }
703
704 // To save the language codes for all timed text tracks
705 // If multiple text tracks present, the format will look
706 // like "eng:chi"
707 if (!timedTextLang.empty()) {
708 mMetaData.add(METADATA_KEY_TIMED_TEXT_LANGUAGES, timedTextLang);
709 }
710
711 // The duration value is a string representing the duration in ms.
712 sprintf(tmp, "%" PRId64,
713 (maxDurationUs > (INT64_MAX - 500) ? INT64_MAX : (maxDurationUs + 500)) / 1000);
714 mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
715
716 if (hasAudio) {
717 mMetaData.add(METADATA_KEY_HAS_AUDIO, String8("yes"));
718 }
719
720 if (hasVideo) {
721 mMetaData.add(METADATA_KEY_HAS_VIDEO, String8("yes"));
722
723 CHECK(videoWidth >= 0);
724 sprintf(tmp, "%d", videoWidth);
725 mMetaData.add(METADATA_KEY_VIDEO_WIDTH, String8(tmp));
726
727 CHECK(videoHeight >= 0);
728 sprintf(tmp, "%d", videoHeight);
729 mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp));
730
731 sprintf(tmp, "%d", rotationAngle);
732 mMetaData.add(METADATA_KEY_VIDEO_ROTATION, String8(tmp));
733
734 mMetaData.add(METADATA_KEY_VIDEO_CODEC_MIME_TYPE, videoMime);
735
736 if (videoFrameCount > 0) {
737 sprintf(tmp, "%d", videoFrameCount);
738 mMetaData.add(METADATA_KEY_VIDEO_FRAME_COUNT, String8(tmp));
739 }
740 }
741
742 // only if we have a primary image
743 if (imageCount > 0 && imagePrimary >= 0) {
744 mMetaData.add(METADATA_KEY_HAS_IMAGE, String8("yes"));
745
746 sprintf(tmp, "%d", imageCount);
747 mMetaData.add(METADATA_KEY_IMAGE_COUNT, String8(tmp));
748
749 sprintf(tmp, "%d", imagePrimary);
750 mMetaData.add(METADATA_KEY_IMAGE_PRIMARY, String8(tmp));
751
752 CHECK(imageWidth >= 0);
753 sprintf(tmp, "%d", imageWidth);
754 mMetaData.add(METADATA_KEY_IMAGE_WIDTH, String8(tmp));
755
756 CHECK(imageHeight >= 0);
757 sprintf(tmp, "%d", imageHeight);
758 mMetaData.add(METADATA_KEY_IMAGE_HEIGHT, String8(tmp));
759
760 sprintf(tmp, "%d", imageRotation);
761 mMetaData.add(METADATA_KEY_IMAGE_ROTATION, String8(tmp));
762 }
763
764 if (numTracks == 1 && hasAudio && audioBitrate >= 0) {
765 sprintf(tmp, "%d", audioBitrate);
766 mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
767 } else {
768 off64_t sourceSize;
769 if (mSource != NULL && mSource->getSize(&sourceSize) == OK) {
770 int64_t avgBitRate = (int64_t)(sourceSize * 8E6 / maxDurationUs);
771
772 sprintf(tmp, "%" PRId64, avgBitRate);
773 mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
774 }
775 }
776
777 if (numTracks == 1) {
778 const char *fileMIME;
779
780 if (meta->findCString(kKeyMIMEType, &fileMIME) &&
781 !strcasecmp(fileMIME, "video/x-matroska")) {
782 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(0);
783 const char *trackMIME;
784 if (trackMeta != nullptr
785 && trackMeta->findCString(kKeyMIMEType, &trackMIME)
786 && !strncasecmp("audio/", trackMIME, 6)) {
787 // The matroska file only contains a single audio track,
788 // rewrite its mime type.
789 mMetaData.add(
790 METADATA_KEY_MIMETYPE, String8("audio/x-matroska"));
791 }
792 }
793 }
794 }
795
clearMetadata()796 void StagefrightMetadataRetriever::clearMetadata() {
797 mParsedMetaData = false;
798 mMetaData.clear();
799 delete mAlbumArt;
800 mAlbumArt = NULL;
801 }
802
803 } // namespace android
804