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 <gui/Surface.h>
24
25#include "include/avc_utils.h"
26#include "include/StagefrightMetadataRetriever.h"
27
28#include <media/ICrypto.h>
29#include <media/IMediaHTTPService.h>
30#include <media/MediaCodecBuffer.h>
31
32#include <media/stagefright/foundation/ADebug.h>
33#include <media/stagefright/foundation/AMessage.h>
34#include <media/stagefright/ColorConverter.h>
35#include <media/stagefright/DataSource.h>
36#include <media/stagefright/FileSource.h>
37#include <media/stagefright/MediaBuffer.h>
38#include <media/stagefright/MediaCodec.h>
39#include <media/stagefright/MediaCodecList.h>
40#include <media/stagefright/MediaDefs.h>
41#include <media/stagefright/MediaErrors.h>
42#include <media/stagefright/MediaExtractor.h>
43#include <media/stagefright/MediaSource.h>
44#include <media/stagefright/MetaData.h>
45#include <media/stagefright/Utils.h>
46
47#include <CharacterEncodingDetector.h>
48
49namespace android {
50
51static const int64_t kBufferTimeOutUs = 30000ll; // 30 msec
52static const size_t kRetryCount = 20; // must be >0
53
54StagefrightMetadataRetriever::StagefrightMetadataRetriever()
55    : mParsedMetaData(false),
56      mAlbumArt(NULL) {
57    ALOGV("StagefrightMetadataRetriever()");
58}
59
60StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
61    ALOGV("~StagefrightMetadataRetriever()");
62    clearMetadata();
63    if (mSource != NULL) {
64        mSource->close();
65    }
66}
67
68status_t StagefrightMetadataRetriever::setDataSource(
69        const sp<IMediaHTTPService> &httpService,
70        const char *uri,
71        const KeyedVector<String8, String8> *headers) {
72    ALOGV("setDataSource(%s)", uri);
73
74    clearMetadata();
75    mSource = DataSource::CreateFromURI(httpService, uri, headers);
76
77    if (mSource == NULL) {
78        ALOGE("Unable to create data source for '%s'.", uri);
79        return UNKNOWN_ERROR;
80    }
81
82    mExtractor = MediaExtractor::Create(mSource);
83
84    if (mExtractor == NULL) {
85        ALOGE("Unable to instantiate an extractor for '%s'.", uri);
86
87        mSource.clear();
88
89        return UNKNOWN_ERROR;
90    }
91
92    return OK;
93}
94
95// Warning caller retains ownership of the filedescriptor! Dup it if necessary.
96status_t StagefrightMetadataRetriever::setDataSource(
97        int fd, int64_t offset, int64_t length) {
98    fd = dup(fd);
99
100    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
101
102    clearMetadata();
103    mSource = new FileSource(fd, offset, length);
104
105    status_t err;
106    if ((err = mSource->initCheck()) != OK) {
107        mSource.clear();
108
109        return err;
110    }
111
112    mExtractor = MediaExtractor::Create(mSource);
113
114    if (mExtractor == NULL) {
115        mSource.clear();
116
117        return UNKNOWN_ERROR;
118    }
119
120    return OK;
121}
122
123status_t StagefrightMetadataRetriever::setDataSource(
124        const sp<DataSource>& source) {
125    ALOGV("setDataSource(DataSource)");
126
127    clearMetadata();
128    mSource = source;
129    mExtractor = MediaExtractor::Create(mSource);
130
131    if (mExtractor == NULL) {
132        ALOGE("Failed to instantiate a MediaExtractor.");
133        mSource.clear();
134        return UNKNOWN_ERROR;
135    }
136
137    return OK;
138}
139
140static VideoFrame *extractVideoFrame(
141        const AString &componentName,
142        const sp<MetaData> &trackMeta,
143        const sp<IMediaSource> &source,
144        int64_t frameTimeUs,
145        int seekMode) {
146
147    sp<MetaData> format = source->getFormat();
148
149    sp<AMessage> videoFormat;
150    if (convertMetaDataToMessage(trackMeta, &videoFormat) != OK) {
151        ALOGE("b/23680780");
152        ALOGW("Failed to convert meta data to message");
153        return NULL;
154    }
155
156    // TODO: Use Flexible color instead
157    videoFormat->setInt32("color-format", OMX_COLOR_FormatYUV420Planar);
158
159    // For the thumbnail extraction case, try to allocate single buffer in both
160    // input and output ports, if seeking to a sync frame. NOTE: This request may
161    // fail if component requires more than that for decoding.
162    bool isSeekingClosest = (seekMode == MediaSource::ReadOptions::SEEK_CLOSEST);
163    if (!isSeekingClosest) {
164        videoFormat->setInt32("android._num-input-buffers", 1);
165        videoFormat->setInt32("android._num-output-buffers", 1);
166    }
167
168    status_t err;
169    sp<ALooper> looper = new ALooper;
170    looper->start();
171    sp<MediaCodec> decoder = MediaCodec::CreateByComponentName(
172            looper, componentName, &err);
173
174    if (decoder.get() == NULL || err != OK) {
175        ALOGW("Failed to instantiate decoder [%s]", componentName.c_str());
176        return NULL;
177    }
178
179    err = decoder->configure(videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */);
180    if (err != OK) {
181        ALOGW("configure returned error %d (%s)", err, asString(err));
182        decoder->release();
183        return NULL;
184    }
185
186    err = decoder->start();
187    if (err != OK) {
188        ALOGW("start returned error %d (%s)", err, asString(err));
189        decoder->release();
190        return NULL;
191    }
192
193    MediaSource::ReadOptions options;
194    if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
195        seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) {
196
197        ALOGE("Unknown seek mode: %d", seekMode);
198        decoder->release();
199        return NULL;
200    }
201
202    MediaSource::ReadOptions::SeekMode mode =
203            static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
204
205    int64_t thumbNailTime;
206    if (frameTimeUs < 0) {
207        if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)
208                || thumbNailTime < 0) {
209            thumbNailTime = 0;
210        }
211        options.setSeekTo(thumbNailTime, mode);
212    } else {
213        thumbNailTime = -1;
214        options.setSeekTo(frameTimeUs, mode);
215    }
216
217    err = source->start();
218    if (err != OK) {
219        ALOGW("source failed to start: %d (%s)", err, asString(err));
220        decoder->release();
221        return NULL;
222    }
223
224    Vector<sp<MediaCodecBuffer> > inputBuffers;
225    err = decoder->getInputBuffers(&inputBuffers);
226    if (err != OK) {
227        ALOGW("failed to get input buffers: %d (%s)", err, asString(err));
228        decoder->release();
229        source->stop();
230        return NULL;
231    }
232
233    Vector<sp<MediaCodecBuffer> > outputBuffers;
234    err = decoder->getOutputBuffers(&outputBuffers);
235    if (err != OK) {
236        ALOGW("failed to get output buffers: %d (%s)", err, asString(err));
237        decoder->release();
238        source->stop();
239        return NULL;
240    }
241
242    sp<AMessage> outputFormat = NULL;
243    bool haveMoreInputs = true;
244    size_t index, offset, size;
245    int64_t timeUs;
246    size_t retriesLeft = kRetryCount;
247    bool done = false;
248    const char *mime;
249    bool success = format->findCString(kKeyMIMEType, &mime);
250    if (!success) {
251        ALOGE("Could not find mime type");
252        return NULL;
253    }
254
255    bool isAvcOrHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)
256            || !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
257
258    bool firstSample = true;
259    int64_t targetTimeUs = -1ll;
260
261    do {
262        size_t inputIndex = -1;
263        int64_t ptsUs = 0ll;
264        uint32_t flags = 0;
265        sp<MediaCodecBuffer> codecBuffer = NULL;
266
267        while (haveMoreInputs) {
268            err = decoder->dequeueInputBuffer(&inputIndex, kBufferTimeOutUs);
269            if (err != OK) {
270                ALOGW("Timed out waiting for input");
271                if (retriesLeft) {
272                    err = OK;
273                }
274                break;
275            }
276            codecBuffer = inputBuffers[inputIndex];
277
278            MediaBuffer *mediaBuffer = NULL;
279
280            err = source->read(&mediaBuffer, &options);
281            options.clearSeekTo();
282            if (err != OK) {
283                ALOGW("Input Error or EOS");
284                haveMoreInputs = false;
285                break;
286            }
287            if (firstSample && isSeekingClosest) {
288                mediaBuffer->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs);
289                ALOGV("Seeking closest: targetTimeUs=%lld", (long long)targetTimeUs);
290            }
291            firstSample = false;
292
293            if (mediaBuffer->range_length() > codecBuffer->capacity()) {
294                ALOGE("buffer size (%zu) too large for codec input size (%zu)",
295                        mediaBuffer->range_length(), codecBuffer->capacity());
296                err = BAD_VALUE;
297            } else {
298                codecBuffer->setRange(0, mediaBuffer->range_length());
299
300                CHECK(mediaBuffer->meta_data()->findInt64(kKeyTime, &ptsUs));
301                memcpy(codecBuffer->data(),
302                        (const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(),
303                        mediaBuffer->range_length());
304                if (isAvcOrHevc && IsIDR(codecBuffer) && !isSeekingClosest) {
305                    // Only need to decode one IDR frame, unless we're seeking with CLOSEST
306                    // option, in which case we need to actually decode to targetTimeUs.
307                    haveMoreInputs = false;
308                    flags |= MediaCodec::BUFFER_FLAG_EOS;
309                }
310            }
311
312            mediaBuffer->release();
313            break;
314        }
315
316        if (err == OK && inputIndex < inputBuffers.size()) {
317            ALOGV("QueueInput: size=%zu ts=%" PRId64 " us flags=%x",
318                    codecBuffer->size(), ptsUs, flags);
319            err = decoder->queueInputBuffer(
320                    inputIndex,
321                    codecBuffer->offset(),
322                    codecBuffer->size(),
323                    ptsUs,
324                    flags);
325
326            // we don't expect an output from codec config buffer
327            if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) {
328                continue;
329            }
330        }
331
332        while (err == OK) {
333            // wait for a decoded buffer
334            err = decoder->dequeueOutputBuffer(
335                    &index,
336                    &offset,
337                    &size,
338                    &timeUs,
339                    &flags,
340                    kBufferTimeOutUs);
341
342            if (err == INFO_FORMAT_CHANGED) {
343                ALOGV("Received format change");
344                err = decoder->getOutputFormat(&outputFormat);
345            } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) {
346                ALOGV("Output buffers changed");
347                err = decoder->getOutputBuffers(&outputBuffers);
348            } else {
349                if (err == -EAGAIN /* INFO_TRY_AGAIN_LATER */ && --retriesLeft > 0) {
350                    ALOGV("Timed-out waiting for output.. retries left = %zu", retriesLeft);
351                    err = OK;
352                } else if (err == OK) {
353                    // If we're seeking with CLOSEST option and obtained a valid targetTimeUs
354                    // from the extractor, decode to the specified frame. Otherwise we're done.
355                    done = (targetTimeUs < 0ll) || (timeUs >= targetTimeUs);
356                    ALOGV("Received an output buffer, timeUs=%lld", (long long)timeUs);
357                    if (!done) {
358                        err = decoder->releaseOutputBuffer(index);
359                    }
360                } else {
361                    ALOGW("Received error %d (%s) instead of output", err, asString(err));
362                    done = true;
363                }
364                break;
365            }
366        }
367    } while (err == OK && !done);
368
369    if (err != OK || size <= 0 || outputFormat == NULL) {
370        ALOGE("Failed to decode thumbnail frame");
371        source->stop();
372        decoder->release();
373        return NULL;
374    }
375
376    ALOGV("successfully decoded video frame.");
377    sp<MediaCodecBuffer> videoFrameBuffer = outputBuffers.itemAt(index);
378
379    if (thumbNailTime >= 0) {
380        if (timeUs != thumbNailTime) {
381            AString mime;
382            CHECK(outputFormat->findString("mime", &mime));
383
384            ALOGV("thumbNailTime = %lld us, timeUs = %lld us, mime = %s",
385                    (long long)thumbNailTime, (long long)timeUs, mime.c_str());
386        }
387    }
388
389    int32_t width, height;
390    CHECK(outputFormat->findInt32("width", &width));
391    CHECK(outputFormat->findInt32("height", &height));
392
393    int32_t crop_left, crop_top, crop_right, crop_bottom;
394    if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) {
395        crop_left = crop_top = 0;
396        crop_right = width - 1;
397        crop_bottom = height - 1;
398    }
399
400    int32_t rotationAngle;
401    if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
402        rotationAngle = 0;  // By default, no rotation
403    }
404
405    VideoFrame *frame = new VideoFrame;
406    frame->mWidth = crop_right - crop_left + 1;
407    frame->mHeight = crop_bottom - crop_top + 1;
408    frame->mDisplayWidth = frame->mWidth;
409    frame->mDisplayHeight = frame->mHeight;
410    frame->mSize = frame->mWidth * frame->mHeight * 2;
411    frame->mData = new uint8_t[frame->mSize];
412    frame->mRotationAngle = rotationAngle;
413
414    int32_t sarWidth, sarHeight;
415    if (trackMeta->findInt32(kKeySARWidth, &sarWidth)
416            && trackMeta->findInt32(kKeySARHeight, &sarHeight)
417            && sarHeight != 0) {
418        frame->mDisplayWidth = (frame->mDisplayWidth * sarWidth) / sarHeight;
419    } else {
420        int32_t width, height;
421        if (trackMeta->findInt32(kKeyDisplayWidth, &width)
422                && trackMeta->findInt32(kKeyDisplayHeight, &height)
423                && frame->mDisplayWidth > 0 && frame->mDisplayHeight > 0
424                && width > 0 && height > 0) {
425            if (frame->mDisplayHeight * (int64_t)width / height > (int64_t)frame->mDisplayWidth) {
426                frame->mDisplayHeight =
427                        (int32_t)(height * (int64_t)frame->mDisplayWidth / width);
428            } else {
429                frame->mDisplayWidth =
430                        (int32_t)(frame->mDisplayHeight * (int64_t)width / height);
431            }
432            ALOGV("thumbNail width and height are overridden to %d x %d",
433                    frame->mDisplayWidth, frame->mDisplayHeight);
434        }
435    }
436
437    int32_t srcFormat;
438    CHECK(outputFormat->findInt32("color-format", &srcFormat));
439
440    ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565);
441
442    if (converter.isValid()) {
443        err = converter.convert(
444                (const uint8_t *)videoFrameBuffer->data(),
445                width, height,
446                crop_left, crop_top, crop_right, crop_bottom,
447                frame->mData,
448                frame->mWidth,
449                frame->mHeight,
450                0, 0, frame->mWidth - 1, frame->mHeight - 1);
451    } else {
452        ALOGE("Unable to convert from format 0x%08x to RGB565", srcFormat);
453
454        err = ERROR_UNSUPPORTED;
455    }
456
457    videoFrameBuffer.clear();
458    source->stop();
459    decoder->releaseOutputBuffer(index);
460    decoder->release();
461
462    if (err != OK) {
463        ALOGE("Colorconverter failed to convert frame.");
464
465        delete frame;
466        frame = NULL;
467    }
468
469    return frame;
470}
471
472VideoFrame *StagefrightMetadataRetriever::getFrameAtTime(
473        int64_t timeUs, int option) {
474
475    ALOGV("getFrameAtTime: %" PRId64 " us option: %d", timeUs, option);
476
477    if (mExtractor.get() == NULL) {
478        ALOGV("no extractor.");
479        return NULL;
480    }
481
482    sp<MetaData> fileMeta = mExtractor->getMetaData();
483
484    if (fileMeta == NULL) {
485        ALOGV("extractor doesn't publish metadata, failed to initialize?");
486        return NULL;
487    }
488
489    int32_t drm = 0;
490    if (fileMeta->findInt32(kKeyIsDRM, &drm) && drm != 0) {
491        ALOGE("frame grab not allowed.");
492        return NULL;
493    }
494
495    size_t n = mExtractor->countTracks();
496    size_t i;
497    for (i = 0; i < n; ++i) {
498        sp<MetaData> meta = mExtractor->getTrackMetaData(i);
499
500        const char *mime;
501        CHECK(meta->findCString(kKeyMIMEType, &mime));
502
503        if (!strncasecmp(mime, "video/", 6)) {
504            break;
505        }
506    }
507
508    if (i == n) {
509        ALOGV("no video track found.");
510        return NULL;
511    }
512
513    sp<MetaData> trackMeta = mExtractor->getTrackMetaData(
514            i, MediaExtractor::kIncludeExtensiveMetaData);
515
516    sp<IMediaSource> source = mExtractor->getTrack(i);
517
518    if (source.get() == NULL) {
519        ALOGV("unable to instantiate video track.");
520        return NULL;
521    }
522
523    const void *data;
524    uint32_t type;
525    size_t dataSize;
526    if (fileMeta->findData(kKeyAlbumArt, &type, &data, &dataSize)
527            && mAlbumArt == NULL) {
528        mAlbumArt = MediaAlbumArt::fromData(dataSize, data);
529    }
530
531    const char *mime;
532    CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
533
534    Vector<AString> matchingCodecs;
535    MediaCodecList::findMatchingCodecs(
536            mime,
537            false, /* encoder */
538            MediaCodecList::kPreferSoftwareCodecs,
539            &matchingCodecs);
540
541    for (size_t i = 0; i < matchingCodecs.size(); ++i) {
542        const AString &componentName = matchingCodecs[i];
543        VideoFrame *frame =
544            extractVideoFrame(componentName, trackMeta, source, timeUs, option);
545
546        if (frame != NULL) {
547            return frame;
548        }
549        ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str());
550    }
551
552    return NULL;
553}
554
555MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
556    ALOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
557
558    if (mExtractor == NULL) {
559        return NULL;
560    }
561
562    if (!mParsedMetaData) {
563        parseMetaData();
564
565        mParsedMetaData = true;
566    }
567
568    if (mAlbumArt) {
569        return mAlbumArt->clone();
570    }
571
572    return NULL;
573}
574
575const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
576    if (mExtractor == NULL) {
577        return NULL;
578    }
579
580    if (!mParsedMetaData) {
581        parseMetaData();
582
583        mParsedMetaData = true;
584    }
585
586    ssize_t index = mMetaData.indexOfKey(keyCode);
587
588    if (index < 0) {
589        return NULL;
590    }
591
592    return mMetaData.valueAt(index).string();
593}
594
595void StagefrightMetadataRetriever::parseMetaData() {
596    sp<MetaData> meta = mExtractor->getMetaData();
597
598    if (meta == NULL) {
599        ALOGV("extractor doesn't publish metadata, failed to initialize?");
600        return;
601    }
602
603    struct Map {
604        int from;
605        int to;
606        const char *name;
607    };
608    static const Map kMap[] = {
609        { kKeyMIMEType, METADATA_KEY_MIMETYPE, NULL },
610        { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER, "tracknumber" },
611        { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER, "discnumber" },
612        { kKeyAlbum, METADATA_KEY_ALBUM, "album" },
613        { kKeyArtist, METADATA_KEY_ARTIST, "artist" },
614        { kKeyAlbumArtist, METADATA_KEY_ALBUMARTIST, "albumartist" },
615        { kKeyAuthor, METADATA_KEY_AUTHOR, NULL },
616        { kKeyComposer, METADATA_KEY_COMPOSER, "composer" },
617        { kKeyDate, METADATA_KEY_DATE, NULL },
618        { kKeyGenre, METADATA_KEY_GENRE, "genre" },
619        { kKeyTitle, METADATA_KEY_TITLE, "title" },
620        { kKeyYear, METADATA_KEY_YEAR, "year" },
621        { kKeyWriter, METADATA_KEY_WRITER, "writer" },
622        { kKeyCompilation, METADATA_KEY_COMPILATION, "compilation" },
623        { kKeyLocation, METADATA_KEY_LOCATION, NULL },
624    };
625
626    static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
627
628    CharacterEncodingDetector *detector = new CharacterEncodingDetector();
629
630    for (size_t i = 0; i < kNumMapEntries; ++i) {
631        const char *value;
632        if (meta->findCString(kMap[i].from, &value)) {
633            if (kMap[i].name) {
634                // add to charset detector
635                detector->addTag(kMap[i].name, value);
636            } else {
637                // directly add to output list
638                mMetaData.add(kMap[i].to, String8(value));
639            }
640        }
641    }
642
643    detector->detectAndConvert();
644    int size = detector->size();
645    if (size) {
646        for (int i = 0; i < size; i++) {
647            const char *name;
648            const char *value;
649            detector->getTag(i, &name, &value);
650            for (size_t j = 0; j < kNumMapEntries; ++j) {
651                if (kMap[j].name && !strcmp(kMap[j].name, name)) {
652                    mMetaData.add(kMap[j].to, String8(value));
653                }
654            }
655        }
656    }
657    delete detector;
658
659    const void *data;
660    uint32_t type;
661    size_t dataSize;
662    if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize)
663            && mAlbumArt == NULL) {
664        mAlbumArt = MediaAlbumArt::fromData(dataSize, data);
665    }
666
667    size_t numTracks = mExtractor->countTracks();
668
669    char tmp[32];
670    sprintf(tmp, "%zu", numTracks);
671
672    mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp));
673
674    float captureFps;
675    if (meta->findFloat(kKeyCaptureFramerate, &captureFps)) {
676        sprintf(tmp, "%f", captureFps);
677        mMetaData.add(METADATA_KEY_CAPTURE_FRAMERATE, String8(tmp));
678    }
679
680    bool hasAudio = false;
681    bool hasVideo = false;
682    int32_t videoWidth = -1;
683    int32_t videoHeight = -1;
684    int32_t audioBitrate = -1;
685    int32_t rotationAngle = -1;
686
687    // The overall duration is the duration of the longest track.
688    int64_t maxDurationUs = 0;
689    String8 timedTextLang;
690    for (size_t i = 0; i < numTracks; ++i) {
691        sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
692
693        int64_t durationUs;
694        if (trackMeta->findInt64(kKeyDuration, &durationUs)) {
695            if (durationUs > maxDurationUs) {
696                maxDurationUs = durationUs;
697            }
698        }
699
700        const char *mime;
701        if (trackMeta->findCString(kKeyMIMEType, &mime)) {
702            if (!hasAudio && !strncasecmp("audio/", mime, 6)) {
703                hasAudio = true;
704
705                if (!trackMeta->findInt32(kKeyBitRate, &audioBitrate)) {
706                    audioBitrate = -1;
707                }
708            } else if (!hasVideo && !strncasecmp("video/", mime, 6)) {
709                hasVideo = true;
710
711                CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth));
712                CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight));
713                if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
714                    rotationAngle = 0;
715                }
716            } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
717                const char *lang;
718                if (trackMeta->findCString(kKeyMediaLanguage, &lang)) {
719                    timedTextLang.append(String8(lang));
720                    timedTextLang.append(String8(":"));
721                } else {
722                    ALOGE("No language found for timed text");
723                }
724            }
725        }
726    }
727
728    // To save the language codes for all timed text tracks
729    // If multiple text tracks present, the format will look
730    // like "eng:chi"
731    if (!timedTextLang.isEmpty()) {
732        mMetaData.add(METADATA_KEY_TIMED_TEXT_LANGUAGES, timedTextLang);
733    }
734
735    // The duration value is a string representing the duration in ms.
736    sprintf(tmp, "%" PRId64, (maxDurationUs + 500) / 1000);
737    mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
738
739    if (hasAudio) {
740        mMetaData.add(METADATA_KEY_HAS_AUDIO, String8("yes"));
741    }
742
743    if (hasVideo) {
744        mMetaData.add(METADATA_KEY_HAS_VIDEO, String8("yes"));
745
746        sprintf(tmp, "%d", videoWidth);
747        mMetaData.add(METADATA_KEY_VIDEO_WIDTH, String8(tmp));
748
749        sprintf(tmp, "%d", videoHeight);
750        mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp));
751
752        sprintf(tmp, "%d", rotationAngle);
753        mMetaData.add(METADATA_KEY_VIDEO_ROTATION, String8(tmp));
754    }
755
756    if (numTracks == 1 && hasAudio && audioBitrate >= 0) {
757        sprintf(tmp, "%d", audioBitrate);
758        mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
759    } else {
760        off64_t sourceSize;
761        if (mSource != NULL && mSource->getSize(&sourceSize) == OK) {
762            int64_t avgBitRate = (int64_t)(sourceSize * 8E6 / maxDurationUs);
763
764            sprintf(tmp, "%" PRId64, avgBitRate);
765            mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
766        }
767    }
768
769    if (numTracks == 1) {
770        const char *fileMIME;
771
772        if (meta->findCString(kKeyMIMEType, &fileMIME) &&
773                !strcasecmp(fileMIME, "video/x-matroska")) {
774            sp<MetaData> trackMeta = mExtractor->getTrackMetaData(0);
775            const char *trackMIME;
776            CHECK(trackMeta->findCString(kKeyMIMEType, &trackMIME));
777
778            if (!strncasecmp("audio/", trackMIME, 6)) {
779                // The matroska file only contains a single audio track,
780                // rewrite its mime type.
781                mMetaData.add(
782                        METADATA_KEY_MIMETYPE, String8("audio/x-matroska"));
783            }
784        }
785    }
786}
787
788void StagefrightMetadataRetriever::clearMetadata() {
789    mParsedMetaData = false;
790    mMetaData.clear();
791    delete mAlbumArt;
792    mAlbumArt = NULL;
793}
794
795}  // namespace android
796