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