xref: /aosp_15_r20/frameworks/av/media/libmediaplayerservice/StagefrightMetadataRetriever.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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