• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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