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 }
215
216 sp<AMessage> format = new AMessage;
217 status_t err = convertMetaDataToMessage(trackMeta, &format);
218 if (err != OK) {
219 ALOGE("getImageInternal: convertMetaDataToMessage() failed, unable to extract image");
220 return NULL;
221 }
222
223 uint32_t bitDepth = 8;
224 if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC)) {
225 int32_t profile;
226 if (format->findInt32("profile", &profile)) {
227 if (HEVCProfileMain10 == profile || HEVCProfileMain10HDR10 == profile ||
228 HEVCProfileMain10HDR10Plus == profile) {
229 bitDepth = 10;
230 }
231 }
232 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
233 int32_t profile;
234 if (format->findInt32("profile", &profile)) {
235 if (AV1ProfileMain10 == profile || AV1ProfileMain10HDR10 == profile ||
236 AV1ProfileMain10HDR10Plus == profile) {
237 bitDepth = 10;
238 }
239 }
240 }
241
242 if (metaOnly) {
243 return FrameDecoder::getMetadataOnly(trackMeta, colorFormat, thumbnail, bitDepth);
244 }
245
246 sp<IMediaSource> source = mExtractor->getTrack(i);
247
248 if (source.get() == NULL) {
249 ALOGE("unable to instantiate image track.");
250 return NULL;
251 }
252
253 bool preferhw = property_get_bool(
254 "media.stagefright.thumbnail.prefer_hw_codecs", false);
255 uint32_t flags = preferhw ? 0 : MediaCodecList::kPreferSoftwareCodecs;
256 Vector<AString> matchingCodecs;
257
258 // If decoding thumbnail check decoder supports thumbnail dimensions instead
259 int32_t thumbHeight, thumbWidth;
260 if (thumbnail && format != NULL
261 && trackMeta->findInt32(kKeyThumbnailHeight, &thumbHeight)
262 && trackMeta->findInt32(kKeyThumbnailWidth, &thumbWidth)) {
263 format->setInt32("height", thumbHeight);
264 format->setInt32("width", thumbWidth);
265 }
266
267 // If decoding tiled HEIF check decoder supports tile dimensions instead
268 if (!thumbnail && isHeif && format != NULL) {
269 int32_t tileWidth, tileHeight;
270 if (trackMeta->findInt32(kKeyTileWidth, &tileWidth) && tileWidth > 0
271 && trackMeta->findInt32(kKeyTileHeight, &tileHeight) && tileHeight > 0) {
272 format->setInt32("height", tileHeight);
273 format->setInt32("width", tileWidth);
274 }
275 }
276
277 MediaCodecList::findMatchingCodecs(
278 mime,
279 false, /* encoder */
280 flags,
281 format,
282 &matchingCodecs);
283
284 for (size_t i = 0; i < matchingCodecs.size(); ++i) {
285 const AString &componentName = matchingCodecs[i];
286 sp<MediaImageDecoder> decoder = new MediaImageDecoder(componentName, trackMeta, source);
287 int64_t frameTimeUs = thumbnail ? -1 : 0;
288 if (decoder->init(frameTimeUs, 0 /*option*/, colorFormat) == OK) {
289 sp<IMemory> frame = decoder->extractFrame(rect);
290
291 if (frame != NULL) {
292 if (rect != NULL) {
293 // keep the decoder if slice decoding
294 mDecoder = decoder;
295 mLastDecodedIndex = index;
296 }
297 return frame;
298 }
299 }
300 ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str());
301 }
302
303 ALOGE("all codecs failed to extract frame.");
304 return NULL;
305 }
306
getFrameAtTime(int64_t timeUs,int option,int colorFormat,bool metaOnly)307 sp<IMemory> StagefrightMetadataRetriever::getFrameAtTime(
308 int64_t timeUs, int option, int colorFormat, bool metaOnly) {
309 ALOGV("getFrameAtTime: %" PRId64 " us option: %d colorFormat: %d, metaOnly: %d",
310 timeUs, option, colorFormat, metaOnly);
311
312 return getFrameInternal(timeUs, option, colorFormat, metaOnly);
313 }
314
getFrameAtIndex(int frameIndex,int colorFormat,bool metaOnly)315 sp<IMemory> StagefrightMetadataRetriever::getFrameAtIndex(
316 int frameIndex, int colorFormat, bool metaOnly) {
317 ALOGV("getFrameAtIndex: frameIndex %d, colorFormat: %d, metaOnly: %d",
318 frameIndex, colorFormat, metaOnly);
319 if (mDecoder != NULL && frameIndex == mLastDecodedIndex + 1) {
320 sp<IMemory> frame = mDecoder->extractFrame();
321 if (frame != nullptr) {
322 mLastDecodedIndex = frameIndex;
323 }
324 return frame;
325 }
326
327 return getFrameInternal(frameIndex,
328 MediaSource::ReadOptions::SEEK_FRAME_INDEX, colorFormat, metaOnly);
329 }
330
getFrameInternal(int64_t timeUs,int option,int colorFormat,bool metaOnly)331 sp<IMemory> StagefrightMetadataRetriever::getFrameInternal(
332 int64_t timeUs, int option, int colorFormat, bool metaOnly) {
333 mDecoder.clear();
334 mLastDecodedIndex = -1;
335
336 if (mExtractor.get() == NULL) {
337 ALOGE("no extractor.");
338 return NULL;
339 }
340
341 sp<MetaData> fileMeta = mExtractor->getMetaData();
342
343 if (fileMeta == NULL) {
344 ALOGE("extractor doesn't publish metadata, failed to initialize?");
345 return NULL;
346 }
347
348 size_t n = mExtractor->countTracks();
349 size_t i;
350 for (i = 0; i < n; ++i) {
351 sp<MetaData> meta = mExtractor->getTrackMetaData(i);
352 if (!meta) {
353 continue;
354 }
355
356 const char *mime;
357 if (meta->findCString(kKeyMIMEType, &mime) && !strncasecmp(mime, "video/", 6)) {
358 break;
359 }
360 }
361
362 if (i == n) {
363 ALOGE("no video track found.");
364 return NULL;
365 }
366
367 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(
368 i, MediaExtractor::kIncludeExtensiveMetaData);
369 if (!trackMeta) {
370 return NULL;
371 }
372
373 if (metaOnly) {
374 return FrameDecoder::getMetadataOnly(trackMeta, colorFormat);
375 }
376
377 sp<IMediaSource> source = mExtractor->getTrack(i);
378
379 if (source.get() == NULL) {
380 ALOGV("unable to instantiate video track.");
381 return NULL;
382 }
383
384 const void *data;
385 uint32_t type;
386 size_t dataSize;
387 if (fileMeta->findData(kKeyAlbumArt, &type, &data, &dataSize)
388 && mAlbumArt == NULL) {
389 mAlbumArt = MediaAlbumArt::fromData(dataSize, data);
390 }
391
392 const char *mime;
393 if (!trackMeta->findCString(kKeyMIMEType, &mime)) {
394 ALOGE("video track has no mime information.");
395 return NULL;
396 }
397
398 bool preferhw = property_get_bool(
399 "media.stagefright.thumbnail.prefer_hw_codecs", false);
400 uint32_t flags = preferhw ? 0 : MediaCodecList::kPreferSoftwareCodecs;
401 sp<AMessage> format = new AMessage;
402 status_t err = convertMetaDataToMessage(trackMeta, &format);
403 if (err != OK) {
404 ALOGE("getFrameInternal: convertMetaDataToMessage() failed, unable to extract frame");
405 return NULL;
406 }
407
408 Vector<AString> matchingCodecs;
409 MediaCodecList::findMatchingCodecs(
410 mime,
411 false, /* encoder */
412 flags,
413 format,
414 &matchingCodecs);
415
416 for (size_t i = 0; i < matchingCodecs.size(); ++i) {
417 const AString &componentName = matchingCodecs[i];
418 sp<VideoFrameDecoder> decoder = new VideoFrameDecoder(componentName, trackMeta, source);
419 if (decoder->init(timeUs, option, colorFormat) == OK) {
420 sp<IMemory> frame = decoder->extractFrame();
421 if (frame != nullptr) {
422 // keep the decoder if seeking by frame index
423 if (option == MediaSource::ReadOptions::SEEK_FRAME_INDEX) {
424 mDecoder = decoder;
425 mLastDecodedIndex = timeUs;
426 }
427 return frame;
428 }
429 }
430 ALOGV("%s failed to extract frame, trying next decoder.", componentName.c_str());
431 }
432
433 ALOGE("all codecs failed to extract frame.");
434 return NULL;
435 }
436
extractAlbumArt()437 MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
438 ALOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
439
440 if (mExtractor == NULL) {
441 return NULL;
442 }
443
444 if (!mParsedMetaData) {
445 parseMetaData();
446
447 mParsedMetaData = true;
448 }
449
450 if (mAlbumArt) {
451 return mAlbumArt->clone();
452 }
453
454 return NULL;
455 }
456
extractMetadata(int keyCode)457 const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
458 if (mExtractor == NULL) {
459 return NULL;
460 }
461
462 if (!mParsedMetaData) {
463 parseMetaData();
464
465 mParsedMetaData = true;
466 }
467
468 ssize_t index = mMetaData.indexOfKey(keyCode);
469
470 if (index < 0) {
471 return NULL;
472 }
473
474 return mMetaData.valueAt(index).string();
475 }
476
parseColorAspects(const sp<MetaData> & meta)477 void StagefrightMetadataRetriever::parseColorAspects(const sp<MetaData>& meta) {
478 sp<AMessage> format = new AMessage();
479 if (convertMetaDataToMessage(meta, &format) != OK) {
480 return;
481 }
482
483 int32_t standard, transfer, range;
484 if (format->findInt32("color-standard", &standard)
485 && format->findInt32("color-transfer", &transfer)
486 && format->findInt32("color-range", &range)) {
487 ALOGV("found color aspects : standard=%d, transfer=%d, range=%d",
488 standard, transfer, range);
489
490 mMetaData.add(METADATA_KEY_COLOR_STANDARD, String8::format("%d", standard));
491 mMetaData.add(METADATA_KEY_COLOR_TRANSFER, String8::format("%d", transfer));
492 mMetaData.add(METADATA_KEY_COLOR_RANGE, String8::format("%d", range));
493 }
494 }
495
parseMetaData()496 void StagefrightMetadataRetriever::parseMetaData() {
497 sp<MetaData> meta = mExtractor->getMetaData();
498
499 if (meta == NULL) {
500 ALOGV("extractor doesn't publish metadata, failed to initialize?");
501 return;
502 }
503
504 struct Map {
505 int from;
506 int to;
507 const char *name;
508 };
509 static const Map kMap[] = {
510 { kKeyMIMEType, METADATA_KEY_MIMETYPE, NULL },
511 { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER, "tracknumber" },
512 { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER, "discnumber" },
513 { kKeyAlbum, METADATA_KEY_ALBUM, "album" },
514 { kKeyArtist, METADATA_KEY_ARTIST, "artist" },
515 { kKeyAlbumArtist, METADATA_KEY_ALBUMARTIST, "albumartist" },
516 { kKeyAuthor, METADATA_KEY_AUTHOR, NULL },
517 { kKeyComposer, METADATA_KEY_COMPOSER, "composer" },
518 { kKeyDate, METADATA_KEY_DATE, NULL },
519 { kKeyGenre, METADATA_KEY_GENRE, "genre" },
520 { kKeyTitle, METADATA_KEY_TITLE, "title" },
521 { kKeyYear, METADATA_KEY_YEAR, "year" },
522 { kKeyWriter, METADATA_KEY_WRITER, "writer" },
523 { kKeyCompilation, METADATA_KEY_COMPILATION, "compilation" },
524 { kKeyLocation, METADATA_KEY_LOCATION, NULL },
525 };
526
527 static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
528
529 CharacterEncodingDetector *detector = new CharacterEncodingDetector();
530
531 for (size_t i = 0; i < kNumMapEntries; ++i) {
532 const char *value;
533 if (meta->findCString(kMap[i].from, &value)) {
534 if (kMap[i].name) {
535 // add to charset detector
536 detector->addTag(kMap[i].name, value);
537 } else {
538 // directly add to output list
539 mMetaData.add(kMap[i].to, String8(value));
540 }
541 }
542 }
543
544 detector->detectAndConvert();
545 int size = detector->size();
546 if (size) {
547 for (int i = 0; i < size; i++) {
548 const char *name;
549 const char *value;
550 detector->getTag(i, &name, &value);
551 for (size_t j = 0; j < kNumMapEntries; ++j) {
552 if (kMap[j].name && !strcmp(kMap[j].name, name)) {
553 mMetaData.add(kMap[j].to, String8(value));
554 }
555 }
556 }
557 }
558 delete detector;
559
560 const void *data;
561 uint32_t type;
562 size_t dataSize;
563 if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize)
564 && mAlbumArt == NULL) {
565 mAlbumArt = MediaAlbumArt::fromData(dataSize, data);
566 }
567
568 size_t numTracks = mExtractor->countTracks();
569
570 char tmp[32];
571 sprintf(tmp, "%zu", numTracks);
572
573 mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp));
574
575 float captureFps;
576 if (meta->findFloat(kKeyCaptureFramerate, &captureFps)) {
577 sprintf(tmp, "%f", captureFps);
578 mMetaData.add(METADATA_KEY_CAPTURE_FRAMERATE, String8(tmp));
579 }
580
581 int64_t exifOffset, exifSize;
582 if (meta->findInt64(kKeyExifOffset, &exifOffset)
583 && meta->findInt64(kKeyExifSize, &exifSize)) {
584 sprintf(tmp, "%lld", (long long)exifOffset);
585 mMetaData.add(METADATA_KEY_EXIF_OFFSET, String8(tmp));
586 sprintf(tmp, "%lld", (long long)exifSize);
587 mMetaData.add(METADATA_KEY_EXIF_LENGTH, String8(tmp));
588 }
589
590 int64_t xmpOffset, xmpSize;
591 if (meta->findInt64(kKeyXmpOffset, &xmpOffset)
592 && meta->findInt64(kKeyXmpSize, &xmpSize)) {
593 sprintf(tmp, "%lld", (long long)xmpOffset);
594 mMetaData.add(METADATA_KEY_XMP_OFFSET, String8(tmp));
595 sprintf(tmp, "%lld", (long long)xmpSize);
596 mMetaData.add(METADATA_KEY_XMP_LENGTH, String8(tmp));
597 }
598
599 bool hasAudio = false;
600 bool hasVideo = false;
601 int32_t videoWidth = -1;
602 int32_t videoHeight = -1;
603 int32_t videoFrameCount = 0;
604 int32_t audioBitrate = -1;
605 int32_t rotationAngle = -1;
606 int32_t imageCount = 0;
607 int32_t imagePrimary = -1;
608 int32_t imageWidth = -1;
609 int32_t imageHeight = -1;
610 int32_t imageRotation = -1;
611
612 // The overall duration is the duration of the longest track.
613 int64_t maxDurationUs = 0;
614 String8 timedTextLang, videoMime;
615 for (size_t i = 0; i < numTracks; ++i) {
616 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
617 if (!trackMeta) {
618 continue;
619 }
620
621 int64_t durationUs;
622 if (trackMeta->findInt64(kKeyDuration, &durationUs)) {
623 if (durationUs > maxDurationUs) {
624 maxDurationUs = durationUs;
625 }
626 }
627
628 const char *mime;
629 if (trackMeta->findCString(kKeyMIMEType, &mime)) {
630 if (!hasAudio && !strncasecmp("audio/", mime, 6)) {
631 hasAudio = true;
632
633 if (!trackMeta->findInt32(kKeyBitRate, &audioBitrate)) {
634 audioBitrate = -1;
635 }
636
637 int32_t bitsPerSample = -1;
638 int32_t sampleRate = -1;
639 trackMeta->findInt32(kKeyBitsPerSample, &bitsPerSample);
640 trackMeta->findInt32(kKeySampleRate, &sampleRate);
641 if (bitsPerSample >= 0) {
642 sprintf(tmp, "%d", bitsPerSample);
643 mMetaData.add(METADATA_KEY_BITS_PER_SAMPLE, String8(tmp));
644 }
645 if (sampleRate >= 0) {
646 sprintf(tmp, "%d", sampleRate);
647 mMetaData.add(METADATA_KEY_SAMPLERATE, String8(tmp));
648 }
649 } else if (!hasVideo && !strncasecmp("video/", mime, 6)) {
650 if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
651 rotationAngle = 0;
652 }
653 if (!trackMeta->findInt32(kKeyFrameCount, &videoFrameCount)) {
654 videoFrameCount = 0;
655 }
656 if (trackMeta->findInt32(kKeyWidth, &videoWidth)
657 && trackMeta->findInt32(kKeyHeight, &videoHeight)) {
658 hasVideo = true;
659 videoMime = String8(mime);
660 parseColorAspects(trackMeta);
661 } else {
662 ALOGE("video track ignored for missing dimensions");
663 }
664 } else if (!strncasecmp("image/", mime, 6)) {
665 int32_t isPrimary;
666 if (trackMeta->findInt32(
667 kKeyTrackIsDefault, &isPrimary) && isPrimary) {
668 if (!trackMeta->findInt32(kKeyRotation, &imageRotation)) {
669 imageRotation = 0;
670 }
671 if (trackMeta->findInt32(kKeyWidth, &imageWidth)
672 && trackMeta->findInt32(kKeyHeight, &imageHeight)) {
673 imagePrimary = imageCount;
674 } else {
675 ALOGE("primary image track ignored for missing dimensions");
676 }
677 }
678 imageCount++;
679 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
680 const char *lang;
681 if (trackMeta->findCString(kKeyMediaLanguage, &lang)) {
682 timedTextLang.append(String8(lang));
683 timedTextLang.append(String8(":"));
684 } else {
685 ALOGE("No language found for timed text");
686 }
687 }
688 }
689 }
690
691 // To save the language codes for all timed text tracks
692 // If multiple text tracks present, the format will look
693 // like "eng:chi"
694 if (!timedTextLang.isEmpty()) {
695 mMetaData.add(METADATA_KEY_TIMED_TEXT_LANGUAGES, timedTextLang);
696 }
697
698 // The duration value is a string representing the duration in ms.
699 sprintf(tmp, "%" PRId64,
700 (maxDurationUs > (INT64_MAX - 500) ? INT64_MAX : (maxDurationUs + 500)) / 1000);
701 mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
702
703 if (hasAudio) {
704 mMetaData.add(METADATA_KEY_HAS_AUDIO, String8("yes"));
705 }
706
707 if (hasVideo) {
708 mMetaData.add(METADATA_KEY_HAS_VIDEO, String8("yes"));
709
710 CHECK(videoWidth >= 0);
711 sprintf(tmp, "%d", videoWidth);
712 mMetaData.add(METADATA_KEY_VIDEO_WIDTH, String8(tmp));
713
714 CHECK(videoHeight >= 0);
715 sprintf(tmp, "%d", videoHeight);
716 mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp));
717
718 sprintf(tmp, "%d", rotationAngle);
719 mMetaData.add(METADATA_KEY_VIDEO_ROTATION, String8(tmp));
720
721 mMetaData.add(METADATA_KEY_VIDEO_CODEC_MIME_TYPE, videoMime);
722
723 if (videoFrameCount > 0) {
724 sprintf(tmp, "%d", videoFrameCount);
725 mMetaData.add(METADATA_KEY_VIDEO_FRAME_COUNT, String8(tmp));
726 }
727 }
728
729 // only if we have a primary image
730 if (imageCount > 0 && imagePrimary >= 0) {
731 mMetaData.add(METADATA_KEY_HAS_IMAGE, String8("yes"));
732
733 sprintf(tmp, "%d", imageCount);
734 mMetaData.add(METADATA_KEY_IMAGE_COUNT, String8(tmp));
735
736 sprintf(tmp, "%d", imagePrimary);
737 mMetaData.add(METADATA_KEY_IMAGE_PRIMARY, String8(tmp));
738
739 CHECK(imageWidth >= 0);
740 sprintf(tmp, "%d", imageWidth);
741 mMetaData.add(METADATA_KEY_IMAGE_WIDTH, String8(tmp));
742
743 CHECK(imageHeight >= 0);
744 sprintf(tmp, "%d", imageHeight);
745 mMetaData.add(METADATA_KEY_IMAGE_HEIGHT, String8(tmp));
746
747 sprintf(tmp, "%d", imageRotation);
748 mMetaData.add(METADATA_KEY_IMAGE_ROTATION, String8(tmp));
749 }
750
751 if (numTracks == 1 && hasAudio && audioBitrate >= 0) {
752 sprintf(tmp, "%d", audioBitrate);
753 mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
754 } else {
755 off64_t sourceSize;
756 if (mSource != NULL && mSource->getSize(&sourceSize) == OK) {
757 int64_t avgBitRate = (int64_t)(sourceSize * 8E6 / maxDurationUs);
758
759 sprintf(tmp, "%" PRId64, avgBitRate);
760 mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
761 }
762 }
763
764 if (numTracks == 1) {
765 const char *fileMIME;
766
767 if (meta->findCString(kKeyMIMEType, &fileMIME) &&
768 !strcasecmp(fileMIME, "video/x-matroska")) {
769 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(0);
770 const char *trackMIME;
771 if (trackMeta != nullptr
772 && trackMeta->findCString(kKeyMIMEType, &trackMIME)
773 && !strncasecmp("audio/", trackMIME, 6)) {
774 // The matroska file only contains a single audio track,
775 // rewrite its mime type.
776 mMetaData.add(
777 METADATA_KEY_MIMETYPE, String8("audio/x-matroska"));
778 }
779 }
780 }
781 }
782
clearMetadata()783 void StagefrightMetadataRetriever::clearMetadata() {
784 mParsedMetaData = false;
785 mMetaData.clear();
786 delete mAlbumArt;
787 mAlbumArt = NULL;
788 }
789
790 } // namespace android
791