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 32 namespace android { 33 34 StagefrightMetadataRetriever::StagefrightMetadataRetriever() 35 : mParsedMetaData(false), 36 mAlbumArt(NULL) { 37 ALOGV("StagefrightMetadataRetriever()"); 38 39 DataSource::RegisterDefaultSniffers(); 40 CHECK_EQ(mClient.connect(), (status_t)OK); 41 } 42 43 StagefrightMetadataRetriever::~StagefrightMetadataRetriever() { 44 ALOGV("~StagefrightMetadataRetriever()"); 45 46 delete mAlbumArt; 47 mAlbumArt = NULL; 48 49 mClient.disconnect(); 50 } 51 52 status_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. 82 status_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 113 static 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 138 static 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 321 VideoFrame *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 399 MediaAlbumArt *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 419 const 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 mMetaData.valueAt(index).string(); 437 } 438 439 void 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