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