StagefrightMetadataRetriever.cpp revision 34581f44cde67960fbac3ba1f191a2c063ea5145
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
24#include "include/StagefrightMetadataRetriever.h"
25
26#include <media/IMediaHTTPService.h>
27#include <media/stagefright/foundation/ADebug.h>
28#include <media/stagefright/ColorConverter.h>
29#include <media/stagefright/DataSource.h>
30#include <media/stagefright/FileSource.h>
31#include <media/stagefright/MediaExtractor.h>
32#include <media/stagefright/MetaData.h>
33#include <media/stagefright/OMXCodec.h>
34#include <media/stagefright/MediaDefs.h>
35#include <CharacterEncodingDetector.h>
36
37namespace android {
38
39StagefrightMetadataRetriever::StagefrightMetadataRetriever()
40    : mParsedMetaData(false),
41      mAlbumArt(NULL) {
42    ALOGV("StagefrightMetadataRetriever()");
43
44    DataSource::RegisterDefaultSniffers();
45    CHECK_EQ(mClient.connect(), (status_t)OK);
46}
47
48StagefrightMetadataRetriever::~StagefrightMetadataRetriever() {
49    ALOGV("~StagefrightMetadataRetriever()");
50
51    delete mAlbumArt;
52    mAlbumArt = NULL;
53
54    mClient.disconnect();
55}
56
57status_t StagefrightMetadataRetriever::setDataSource(
58        const sp<IMediaHTTPService> &httpService,
59        const char *uri,
60        const KeyedVector<String8, String8> *headers) {
61    ALOGV("setDataSource(%s)", uri);
62
63    mParsedMetaData = false;
64    mMetaData.clear();
65    delete mAlbumArt;
66    mAlbumArt = NULL;
67
68    mSource = DataSource::CreateFromURI(httpService, uri, headers);
69
70    if (mSource == NULL) {
71        ALOGE("Unable to create data source for '%s'.", uri);
72        return UNKNOWN_ERROR;
73    }
74
75    mExtractor = MediaExtractor::Create(mSource);
76
77    if (mExtractor == NULL) {
78        ALOGE("Unable to instantiate an extractor for '%s'.", uri);
79
80        mSource.clear();
81
82        return UNKNOWN_ERROR;
83    }
84
85    return OK;
86}
87
88// Warning caller retains ownership of the filedescriptor! Dup it if necessary.
89status_t StagefrightMetadataRetriever::setDataSource(
90        int fd, int64_t offset, int64_t length) {
91    fd = dup(fd);
92
93    ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
94
95    mParsedMetaData = false;
96    mMetaData.clear();
97    delete mAlbumArt;
98    mAlbumArt = NULL;
99
100    mSource = new FileSource(fd, offset, length);
101
102    status_t err;
103    if ((err = mSource->initCheck()) != OK) {
104        mSource.clear();
105
106        return err;
107    }
108
109    mExtractor = MediaExtractor::Create(mSource);
110
111    if (mExtractor == NULL) {
112        mSource.clear();
113
114        return UNKNOWN_ERROR;
115    }
116
117    return OK;
118}
119
120static bool isYUV420PlanarSupported(
121            OMXClient *client,
122            const sp<MetaData> &trackMeta) {
123
124    const char *mime;
125    CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
126
127    Vector<CodecCapabilities> caps;
128    if (QueryCodecs(client->interface(), mime,
129                    true, /* queryDecoders */
130                    true, /* hwCodecOnly */
131                    &caps) == OK) {
132
133        for (size_t j = 0; j < caps.size(); ++j) {
134            CodecCapabilities cap = caps[j];
135            for (size_t i = 0; i < cap.mColorFormats.size(); ++i) {
136                if (cap.mColorFormats[i] == OMX_COLOR_FormatYUV420Planar) {
137                    return true;
138                }
139            }
140        }
141    }
142    return false;
143}
144
145static VideoFrame *extractVideoFrameWithCodecFlags(
146        OMXClient *client,
147        const sp<MetaData> &trackMeta,
148        const sp<MediaSource> &source,
149        uint32_t flags,
150        int64_t frameTimeUs,
151        int seekMode) {
152
153    sp<MetaData> format = source->getFormat();
154
155    // XXX:
156    // Once all vendors support OMX_COLOR_FormatYUV420Planar, we can
157    // remove this check and always set the decoder output color format
158    if (isYUV420PlanarSupported(client, trackMeta)) {
159        format->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar);
160    }
161
162    sp<MediaSource> decoder =
163        OMXCodec::Create(
164                client->interface(), format, false, source,
165                NULL, flags | OMXCodec::kClientNeedsFramebuffer);
166
167    if (decoder.get() == NULL) {
168        ALOGV("unable to instantiate video decoder.");
169
170        return NULL;
171    }
172
173    status_t err = decoder->start();
174    if (err != OK) {
175        ALOGW("OMXCodec::start returned error %d (0x%08x)\n", err, err);
176        return NULL;
177    }
178
179    // Read one output buffer, ignore format change notifications
180    // and spurious empty buffers.
181
182    MediaSource::ReadOptions options;
183    if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC ||
184        seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) {
185
186        ALOGE("Unknown seek mode: %d", seekMode);
187        return NULL;
188    }
189
190    MediaSource::ReadOptions::SeekMode mode =
191            static_cast<MediaSource::ReadOptions::SeekMode>(seekMode);
192
193    int64_t thumbNailTime;
194    if (frameTimeUs < 0) {
195        if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime)
196                || thumbNailTime < 0) {
197            thumbNailTime = 0;
198        }
199        options.setSeekTo(thumbNailTime, mode);
200    } else {
201        thumbNailTime = -1;
202        options.setSeekTo(frameTimeUs, mode);
203    }
204
205    MediaBuffer *buffer = NULL;
206    do {
207        if (buffer != NULL) {
208            buffer->release();
209            buffer = NULL;
210        }
211        err = decoder->read(&buffer, &options);
212        options.clearSeekTo();
213    } while (err == INFO_FORMAT_CHANGED
214             || (buffer != NULL && buffer->range_length() == 0));
215
216    if (err != OK) {
217        CHECK(buffer == NULL);
218
219        ALOGV("decoding frame failed.");
220        decoder->stop();
221
222        return NULL;
223    }
224
225    ALOGV("successfully decoded video frame.");
226
227    int32_t unreadable;
228    if (buffer->meta_data()->findInt32(kKeyIsUnreadable, &unreadable)
229            && unreadable != 0) {
230        ALOGV("video frame is unreadable, decoder does not give us access "
231             "to the video data.");
232
233        buffer->release();
234        buffer = NULL;
235
236        decoder->stop();
237
238        return NULL;
239    }
240
241    int64_t timeUs;
242    CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
243    if (thumbNailTime >= 0) {
244        if (timeUs != thumbNailTime) {
245            const char *mime;
246            CHECK(trackMeta->findCString(kKeyMIMEType, &mime));
247
248            ALOGV("thumbNailTime = %" PRId64 " us, timeUs = %" PRId64 " us, mime = %s",
249                 thumbNailTime, timeUs, mime);
250        }
251    }
252
253    sp<MetaData> meta = decoder->getFormat();
254
255    int32_t width, height;
256    CHECK(meta->findInt32(kKeyWidth, &width));
257    CHECK(meta->findInt32(kKeyHeight, &height));
258
259    int32_t crop_left, crop_top, crop_right, crop_bottom;
260    if (!meta->findRect(
261                kKeyCropRect,
262                &crop_left, &crop_top, &crop_right, &crop_bottom)) {
263        crop_left = crop_top = 0;
264        crop_right = width - 1;
265        crop_bottom = height - 1;
266    }
267
268    int32_t rotationAngle;
269    if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
270        rotationAngle = 0;  // By default, no rotation
271    }
272
273    VideoFrame *frame = new VideoFrame;
274    frame->mWidth = crop_right - crop_left + 1;
275    frame->mHeight = crop_bottom - crop_top + 1;
276    frame->mDisplayWidth = frame->mWidth;
277    frame->mDisplayHeight = frame->mHeight;
278    frame->mSize = frame->mWidth * frame->mHeight * 2;
279    frame->mData = new uint8_t[frame->mSize];
280    frame->mRotationAngle = rotationAngle;
281
282    int32_t displayWidth, displayHeight;
283    if (meta->findInt32(kKeyDisplayWidth, &displayWidth)) {
284        frame->mDisplayWidth = displayWidth;
285    }
286    if (meta->findInt32(kKeyDisplayHeight, &displayHeight)) {
287        frame->mDisplayHeight = displayHeight;
288    }
289
290    int32_t srcFormat;
291    CHECK(meta->findInt32(kKeyColorFormat, &srcFormat));
292
293    ColorConverter converter(
294            (OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565);
295
296    if (converter.isValid()) {
297        err = converter.convert(
298                (const uint8_t *)buffer->data() + buffer->range_offset(),
299                width, height,
300                crop_left, crop_top, crop_right, crop_bottom,
301                frame->mData,
302                frame->mWidth,
303                frame->mHeight,
304                0, 0, frame->mWidth - 1, frame->mHeight - 1);
305    } else {
306        ALOGE("Unable to instantiate color conversion from format 0x%08x to "
307              "RGB565",
308              srcFormat);
309
310        err = ERROR_UNSUPPORTED;
311    }
312
313    buffer->release();
314    buffer = NULL;
315
316    decoder->stop();
317
318    if (err != OK) {
319        ALOGE("Colorconverter failed to convert frame.");
320
321        delete frame;
322        frame = NULL;
323    }
324
325    return frame;
326}
327
328VideoFrame *StagefrightMetadataRetriever::getFrameAtTime(
329        int64_t timeUs, int option) {
330
331    ALOGV("getFrameAtTime: %" PRId64 " us option: %d", timeUs, option);
332
333    if (mExtractor.get() == NULL) {
334        ALOGV("no extractor.");
335        return NULL;
336    }
337
338    sp<MetaData> fileMeta = mExtractor->getMetaData();
339
340    if (fileMeta == NULL) {
341        ALOGV("extractor doesn't publish metadata, failed to initialize?");
342        return NULL;
343    }
344
345    int32_t drm = 0;
346    if (fileMeta->findInt32(kKeyIsDRM, &drm) && drm != 0) {
347        ALOGE("frame grab not allowed.");
348        return NULL;
349    }
350
351    size_t n = mExtractor->countTracks();
352    size_t i;
353    for (i = 0; i < n; ++i) {
354        sp<MetaData> meta = mExtractor->getTrackMetaData(i);
355
356        const char *mime;
357        CHECK(meta->findCString(kKeyMIMEType, &mime));
358
359        if (!strncasecmp(mime, "video/", 6)) {
360            break;
361        }
362    }
363
364    if (i == n) {
365        ALOGV("no video track found.");
366        return NULL;
367    }
368
369    sp<MetaData> trackMeta = mExtractor->getTrackMetaData(
370            i, MediaExtractor::kIncludeExtensiveMetaData);
371
372    sp<MediaSource> source = mExtractor->getTrack(i);
373
374    if (source.get() == NULL) {
375        ALOGV("unable to instantiate video track.");
376        return NULL;
377    }
378
379    const void *data;
380    uint32_t type;
381    size_t dataSize;
382    if (fileMeta->findData(kKeyAlbumArt, &type, &data, &dataSize)
383            && mAlbumArt == NULL) {
384        mAlbumArt = MediaAlbumArt::fromData(dataSize, data);
385    }
386
387    VideoFrame *frame =
388        extractVideoFrameWithCodecFlags(
389                &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs,
390                timeUs, option);
391
392    if (frame == NULL) {
393        ALOGV("Software decoder failed to extract thumbnail, "
394             "trying hardware decoder.");
395
396        frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0,
397                        timeUs, option);
398    }
399
400    return frame;
401}
402
403MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() {
404    ALOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO");
405
406    if (mExtractor == NULL) {
407        return NULL;
408    }
409
410    if (!mParsedMetaData) {
411        parseMetaData();
412
413        mParsedMetaData = true;
414    }
415
416    if (mAlbumArt) {
417        return mAlbumArt->clone();
418    }
419
420    return NULL;
421}
422
423const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) {
424    if (mExtractor == NULL) {
425        return NULL;
426    }
427
428    if (!mParsedMetaData) {
429        parseMetaData();
430
431        mParsedMetaData = true;
432    }
433
434    ssize_t index = mMetaData.indexOfKey(keyCode);
435
436    if (index < 0) {
437        return NULL;
438    }
439
440    return mMetaData.valueAt(index).string();
441}
442
443void StagefrightMetadataRetriever::parseMetaData() {
444    sp<MetaData> meta = mExtractor->getMetaData();
445
446    if (meta == NULL) {
447        ALOGV("extractor doesn't publish metadata, failed to initialize?");
448        return;
449    }
450
451    struct Map {
452        int from;
453        int to;
454        const char *name;
455    };
456    static const Map kMap[] = {
457        { kKeyMIMEType, METADATA_KEY_MIMETYPE, NULL },
458        { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER, "tracknumber" },
459        { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER, "discnumber" },
460        { kKeyAlbum, METADATA_KEY_ALBUM, "album" },
461        { kKeyArtist, METADATA_KEY_ARTIST, "artist" },
462        { kKeyAlbumArtist, METADATA_KEY_ALBUMARTIST, "albumartist" },
463        { kKeyAuthor, METADATA_KEY_AUTHOR, NULL },
464        { kKeyComposer, METADATA_KEY_COMPOSER, "composer" },
465        { kKeyDate, METADATA_KEY_DATE, NULL },
466        { kKeyGenre, METADATA_KEY_GENRE, "genre" },
467        { kKeyTitle, METADATA_KEY_TITLE, "title" },
468        { kKeyYear, METADATA_KEY_YEAR, "year" },
469        { kKeyWriter, METADATA_KEY_WRITER, "writer" },
470        { kKeyCompilation, METADATA_KEY_COMPILATION, "compilation" },
471        { kKeyLocation, METADATA_KEY_LOCATION, NULL },
472    };
473
474    static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
475
476    CharacterEncodingDetector *detector = new CharacterEncodingDetector();
477
478    for (size_t i = 0; i < kNumMapEntries; ++i) {
479        const char *value;
480        if (meta->findCString(kMap[i].from, &value)) {
481            if (kMap[i].name) {
482                // add to charset detector
483                detector->addTag(kMap[i].name, value);
484            } else {
485                // directly add to output list
486                mMetaData.add(kMap[i].to, String8(value));
487            }
488        }
489    }
490
491    detector->detectAndConvert();
492    int size = detector->size();
493    if (size) {
494        for (int i = 0; i < size; i++) {
495            const char *name;
496            const char *value;
497            detector->getTag(i, &name, &value);
498            for (size_t j = 0; j < kNumMapEntries; ++j) {
499                if (kMap[j].name && !strcmp(kMap[j].name, name)) {
500                    mMetaData.add(kMap[j].to, String8(value));
501                }
502            }
503        }
504    }
505    delete detector;
506
507    const void *data;
508    uint32_t type;
509    size_t dataSize;
510    if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize)
511            && mAlbumArt == NULL) {
512        mAlbumArt = MediaAlbumArt::fromData(dataSize, data);
513    }
514
515    size_t numTracks = mExtractor->countTracks();
516
517    char tmp[32];
518    sprintf(tmp, "%zu", numTracks);
519
520    mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp));
521
522    bool hasAudio = false;
523    bool hasVideo = false;
524    int32_t videoWidth = -1;
525    int32_t videoHeight = -1;
526    int32_t audioBitrate = -1;
527    int32_t rotationAngle = -1;
528
529    // The overall duration is the duration of the longest track.
530    int64_t maxDurationUs = 0;
531    String8 timedTextLang;
532    for (size_t i = 0; i < numTracks; ++i) {
533        sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i);
534
535        int64_t durationUs;
536        if (trackMeta->findInt64(kKeyDuration, &durationUs)) {
537            if (durationUs > maxDurationUs) {
538                maxDurationUs = durationUs;
539            }
540        }
541
542        const char *mime;
543        if (trackMeta->findCString(kKeyMIMEType, &mime)) {
544            if (!hasAudio && !strncasecmp("audio/", mime, 6)) {
545                hasAudio = true;
546
547                if (!trackMeta->findInt32(kKeyBitRate, &audioBitrate)) {
548                    audioBitrate = -1;
549                }
550            } else if (!hasVideo && !strncasecmp("video/", mime, 6)) {
551                hasVideo = true;
552
553                CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth));
554                CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight));
555                if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
556                    rotationAngle = 0;
557                }
558            } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) {
559                const char *lang;
560                trackMeta->findCString(kKeyMediaLanguage, &lang);
561                timedTextLang.append(String8(lang));
562                timedTextLang.append(String8(":"));
563            }
564        }
565    }
566
567    // To save the language codes for all timed text tracks
568    // If multiple text tracks present, the format will look
569    // like "eng:chi"
570    if (!timedTextLang.isEmpty()) {
571        mMetaData.add(METADATA_KEY_TIMED_TEXT_LANGUAGES, timedTextLang);
572    }
573
574    // The duration value is a string representing the duration in ms.
575    sprintf(tmp, "%" PRId64, (maxDurationUs + 500) / 1000);
576    mMetaData.add(METADATA_KEY_DURATION, String8(tmp));
577
578    if (hasAudio) {
579        mMetaData.add(METADATA_KEY_HAS_AUDIO, String8("yes"));
580    }
581
582    if (hasVideo) {
583        mMetaData.add(METADATA_KEY_HAS_VIDEO, String8("yes"));
584
585        sprintf(tmp, "%d", videoWidth);
586        mMetaData.add(METADATA_KEY_VIDEO_WIDTH, String8(tmp));
587
588        sprintf(tmp, "%d", videoHeight);
589        mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp));
590
591        sprintf(tmp, "%d", rotationAngle);
592        mMetaData.add(METADATA_KEY_VIDEO_ROTATION, String8(tmp));
593    }
594
595    if (numTracks == 1 && hasAudio && audioBitrate >= 0) {
596        sprintf(tmp, "%d", audioBitrate);
597        mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
598    } else {
599        off64_t sourceSize;
600        if (mSource->getSize(&sourceSize) == OK) {
601            int64_t avgBitRate = (int64_t)(sourceSize * 8E6 / maxDurationUs);
602
603            sprintf(tmp, "%" PRId64, avgBitRate);
604            mMetaData.add(METADATA_KEY_BITRATE, String8(tmp));
605        }
606    }
607
608    if (numTracks == 1) {
609        const char *fileMIME;
610        CHECK(meta->findCString(kKeyMIMEType, &fileMIME));
611
612        if (!strcasecmp(fileMIME, "video/x-matroska")) {
613            sp<MetaData> trackMeta = mExtractor->getTrackMetaData(0);
614            const char *trackMIME;
615            CHECK(trackMeta->findCString(kKeyMIMEType, &trackMIME));
616
617            if (!strncasecmp("audio/", trackMIME, 6)) {
618                // The matroska file only contains a single audio track,
619                // rewrite its mime type.
620                mMetaData.add(
621                        METADATA_KEY_MIMETYPE, String8("audio/x-matroska"));
622            }
623        }
624    }
625
626    // To check whether the media file is drm-protected
627    if (mExtractor->getDrmFlag()) {
628        mMetaData.add(METADATA_KEY_IS_DRM, String8("1"));
629    }
630}
631
632}  // namespace android
633