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