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