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