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