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