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