StagefrightMetadataRetriever.cpp revision a5750e0dad9e90f2195ce36f2c4457fa04b2b83e
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 36namespace android { 37 38StagefrightMetadataRetriever::StagefrightMetadataRetriever() 39 : mParsedMetaData(false), 40 mAlbumArt(NULL) { 41 ALOGV("StagefrightMetadataRetriever()"); 42 43 DataSource::RegisterDefaultSniffers(); 44 CHECK_EQ(mClient.connect(), (status_t)OK); 45} 46 47StagefrightMetadataRetriever::~StagefrightMetadataRetriever() { 48 ALOGV("~StagefrightMetadataRetriever()"); 49 50 delete mAlbumArt; 51 mAlbumArt = NULL; 52 53 mClient.disconnect(); 54} 55 56status_t StagefrightMetadataRetriever::setDataSource( 57 const sp<IMediaHTTPService> &httpService, 58 const char *uri, 59 const KeyedVector<String8, String8> *headers) { 60 ALOGV("setDataSource(%s)", uri); 61 62 mParsedMetaData = false; 63 mMetaData.clear(); 64 delete mAlbumArt; 65 mAlbumArt = NULL; 66 67 mSource = DataSource::CreateFromURI(httpService, uri, headers); 68 69 if (mSource == NULL) { 70 ALOGE("Unable to create data source for '%s'.", uri); 71 return UNKNOWN_ERROR; 72 } 73 74 mExtractor = MediaExtractor::Create(mSource); 75 76 if (mExtractor == NULL) { 77 ALOGE("Unable to instantiate an extractor for '%s'.", uri); 78 79 mSource.clear(); 80 81 return UNKNOWN_ERROR; 82 } 83 84 return OK; 85} 86 87// Warning caller retains ownership of the filedescriptor! Dup it if necessary. 88status_t StagefrightMetadataRetriever::setDataSource( 89 int fd, int64_t offset, int64_t length) { 90 fd = dup(fd); 91 92 ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); 93 94 mParsedMetaData = false; 95 mMetaData.clear(); 96 delete mAlbumArt; 97 mAlbumArt = NULL; 98 99 mSource = new FileSource(fd, offset, length); 100 101 status_t err; 102 if ((err = mSource->initCheck()) != OK) { 103 mSource.clear(); 104 105 return err; 106 } 107 108 mExtractor = MediaExtractor::Create(mSource); 109 110 if (mExtractor == NULL) { 111 mSource.clear(); 112 113 return UNKNOWN_ERROR; 114 } 115 116 return OK; 117} 118 119static bool isYUV420PlanarSupported( 120 OMXClient *client, 121 const sp<MetaData> &trackMeta) { 122 123 const char *mime; 124 CHECK(trackMeta->findCString(kKeyMIMEType, &mime)); 125 126 Vector<CodecCapabilities> caps; 127 if (QueryCodecs(client->interface(), mime, 128 true, /* queryDecoders */ 129 true, /* hwCodecOnly */ 130 &caps) == OK) { 131 132 for (size_t j = 0; j < caps.size(); ++j) { 133 CodecCapabilities cap = caps[j]; 134 for (size_t i = 0; i < cap.mColorFormats.size(); ++i) { 135 if (cap.mColorFormats[i] == OMX_COLOR_FormatYUV420Planar) { 136 return true; 137 } 138 } 139 } 140 } 141 return false; 142} 143 144static VideoFrame *extractVideoFrameWithCodecFlags( 145 OMXClient *client, 146 const sp<MetaData> &trackMeta, 147 const sp<MediaSource> &source, 148 uint32_t flags, 149 int64_t frameTimeUs, 150 int seekMode) { 151 152 sp<MetaData> format = source->getFormat(); 153 154 // XXX: 155 // Once all vendors support OMX_COLOR_FormatYUV420Planar, we can 156 // remove this check and always set the decoder output color format 157 if (isYUV420PlanarSupported(client, trackMeta)) { 158 format->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar); 159 } 160 161 sp<MediaSource> decoder = 162 OMXCodec::Create( 163 client->interface(), format, false, source, 164 NULL, flags | OMXCodec::kClientNeedsFramebuffer); 165 166 if (decoder.get() == NULL) { 167 ALOGV("unable to instantiate video decoder."); 168 169 return NULL; 170 } 171 172 status_t err = decoder->start(); 173 if (err != OK) { 174 ALOGW("OMXCodec::start returned error %d (0x%08x)\n", err, err); 175 return NULL; 176 } 177 178 // Read one output buffer, ignore format change notifications 179 // and spurious empty buffers. 180 181 MediaSource::ReadOptions options; 182 if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC || 183 seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) { 184 185 ALOGE("Unknown seek mode: %d", seekMode); 186 return NULL; 187 } 188 189 MediaSource::ReadOptions::SeekMode mode = 190 static_cast<MediaSource::ReadOptions::SeekMode>(seekMode); 191 192 int64_t thumbNailTime; 193 if (frameTimeUs < 0) { 194 if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime) 195 || thumbNailTime < 0) { 196 thumbNailTime = 0; 197 } 198 options.setSeekTo(thumbNailTime, mode); 199 } else { 200 thumbNailTime = -1; 201 options.setSeekTo(frameTimeUs, mode); 202 } 203 204 MediaBuffer *buffer = NULL; 205 do { 206 if (buffer != NULL) { 207 buffer->release(); 208 buffer = NULL; 209 } 210 err = decoder->read(&buffer, &options); 211 options.clearSeekTo(); 212 } while (err == INFO_FORMAT_CHANGED 213 || (buffer != NULL && buffer->range_length() == 0)); 214 215 if (err != OK) { 216 CHECK(buffer == NULL); 217 218 ALOGV("decoding frame failed."); 219 decoder->stop(); 220 221 return NULL; 222 } 223 224 ALOGV("successfully decoded video frame."); 225 226 int32_t unreadable; 227 if (buffer->meta_data()->findInt32(kKeyIsUnreadable, &unreadable) 228 && unreadable != 0) { 229 ALOGV("video frame is unreadable, decoder does not give us access " 230 "to the video data."); 231 232 buffer->release(); 233 buffer = NULL; 234 235 decoder->stop(); 236 237 return NULL; 238 } 239 240 int64_t timeUs; 241 CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); 242 if (thumbNailTime >= 0) { 243 if (timeUs != thumbNailTime) { 244 const char *mime; 245 CHECK(trackMeta->findCString(kKeyMIMEType, &mime)); 246 247 ALOGV("thumbNailTime = %" PRId64 " us, timeUs = %" PRId64 " us, mime = %s", 248 thumbNailTime, timeUs, mime); 249 } 250 } 251 252 sp<MetaData> meta = decoder->getFormat(); 253 254 int32_t width, height; 255 CHECK(meta->findInt32(kKeyWidth, &width)); 256 CHECK(meta->findInt32(kKeyHeight, &height)); 257 258 int32_t crop_left, crop_top, crop_right, crop_bottom; 259 if (!meta->findRect( 260 kKeyCropRect, 261 &crop_left, &crop_top, &crop_right, &crop_bottom)) { 262 crop_left = crop_top = 0; 263 crop_right = width - 1; 264 crop_bottom = height - 1; 265 } 266 267 int32_t rotationAngle; 268 if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) { 269 rotationAngle = 0; // By default, no rotation 270 } 271 272 VideoFrame *frame = new VideoFrame; 273 frame->mWidth = crop_right - crop_left + 1; 274 frame->mHeight = crop_bottom - crop_top + 1; 275 frame->mDisplayWidth = frame->mWidth; 276 frame->mDisplayHeight = frame->mHeight; 277 frame->mSize = frame->mWidth * frame->mHeight * 2; 278 frame->mData = new uint8_t[frame->mSize]; 279 frame->mRotationAngle = rotationAngle; 280 281 int32_t displayWidth, displayHeight; 282 if (meta->findInt32(kKeyDisplayWidth, &displayWidth)) { 283 frame->mDisplayWidth = displayWidth; 284 } 285 if (meta->findInt32(kKeyDisplayHeight, &displayHeight)) { 286 frame->mDisplayHeight = displayHeight; 287 } 288 289 int32_t srcFormat; 290 CHECK(meta->findInt32(kKeyColorFormat, &srcFormat)); 291 292 ColorConverter converter( 293 (OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565); 294 295 if (converter.isValid()) { 296 err = converter.convert( 297 (const uint8_t *)buffer->data() + buffer->range_offset(), 298 width, height, 299 crop_left, crop_top, crop_right, crop_bottom, 300 frame->mData, 301 frame->mWidth, 302 frame->mHeight, 303 0, 0, frame->mWidth - 1, frame->mHeight - 1); 304 } else { 305 ALOGE("Unable to instantiate color conversion from format 0x%08x to " 306 "RGB565", 307 srcFormat); 308 309 err = ERROR_UNSUPPORTED; 310 } 311 312 buffer->release(); 313 buffer = NULL; 314 315 decoder->stop(); 316 317 if (err != OK) { 318 ALOGE("Colorconverter failed to convert frame."); 319 320 delete frame; 321 frame = NULL; 322 } 323 324 return frame; 325} 326 327VideoFrame *StagefrightMetadataRetriever::getFrameAtTime( 328 int64_t timeUs, int option) { 329 330 ALOGV("getFrameAtTime: %" PRId64 " us option: %d", timeUs, option); 331 332 if (mExtractor.get() == NULL) { 333 ALOGV("no extractor."); 334 return NULL; 335 } 336 337 sp<MetaData> fileMeta = mExtractor->getMetaData(); 338 339 if (fileMeta == NULL) { 340 ALOGV("extractor doesn't publish metadata, failed to initialize?"); 341 return NULL; 342 } 343 344 int32_t drm = 0; 345 if (fileMeta->findInt32(kKeyIsDRM, &drm) && drm != 0) { 346 ALOGE("frame grab not allowed."); 347 return NULL; 348 } 349 350 size_t n = mExtractor->countTracks(); 351 size_t i; 352 for (i = 0; i < n; ++i) { 353 sp<MetaData> meta = mExtractor->getTrackMetaData(i); 354 355 const char *mime; 356 CHECK(meta->findCString(kKeyMIMEType, &mime)); 357 358 if (!strncasecmp(mime, "video/", 6)) { 359 break; 360 } 361 } 362 363 if (i == n) { 364 ALOGV("no video track found."); 365 return NULL; 366 } 367 368 sp<MetaData> trackMeta = mExtractor->getTrackMetaData( 369 i, MediaExtractor::kIncludeExtensiveMetaData); 370 371 sp<MediaSource> source = mExtractor->getTrack(i); 372 373 if (source.get() == NULL) { 374 ALOGV("unable to instantiate video track."); 375 return NULL; 376 } 377 378 const void *data; 379 uint32_t type; 380 size_t dataSize; 381 if (fileMeta->findData(kKeyAlbumArt, &type, &data, &dataSize) 382 && mAlbumArt == NULL) { 383 mAlbumArt = MediaAlbumArt::fromData(dataSize, data); 384 } 385 386 VideoFrame *frame = 387 extractVideoFrameWithCodecFlags( 388 &mClient, trackMeta, source, OMXCodec::kPreferSoftwareCodecs, 389 timeUs, option); 390 391 if (frame == NULL) { 392 ALOGV("Software decoder failed to extract thumbnail, " 393 "trying hardware decoder."); 394 395 frame = extractVideoFrameWithCodecFlags(&mClient, trackMeta, source, 0, 396 timeUs, option); 397 } 398 399 return frame; 400} 401 402MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() { 403 ALOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO"); 404 405 if (mExtractor == NULL) { 406 return NULL; 407 } 408 409 if (!mParsedMetaData) { 410 parseMetaData(); 411 412 mParsedMetaData = true; 413 } 414 415 if (mAlbumArt) { 416 return mAlbumArt->clone(); 417 } 418 419 return NULL; 420} 421 422const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) { 423 if (mExtractor == NULL) { 424 return NULL; 425 } 426 427 if (!mParsedMetaData) { 428 parseMetaData(); 429 430 mParsedMetaData = true; 431 } 432 433 ssize_t index = mMetaData.indexOfKey(keyCode); 434 435 if (index < 0) { 436 return NULL; 437 } 438 439 return mMetaData.valueAt(index).string(); 440} 441 442void StagefrightMetadataRetriever::parseMetaData() { 443 sp<MetaData> meta = mExtractor->getMetaData(); 444 445 if (meta == NULL) { 446 ALOGV("extractor doesn't publish metadata, failed to initialize?"); 447 return; 448 } 449 450 struct Map { 451 int from; 452 int to; 453 }; 454 static const Map kMap[] = { 455 { kKeyMIMEType, METADATA_KEY_MIMETYPE }, 456 { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER }, 457 { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER }, 458 { kKeyAlbum, METADATA_KEY_ALBUM }, 459 { kKeyArtist, METADATA_KEY_ARTIST }, 460 { kKeyAlbumArtist, METADATA_KEY_ALBUMARTIST }, 461 { kKeyAuthor, METADATA_KEY_AUTHOR }, 462 { kKeyComposer, METADATA_KEY_COMPOSER }, 463 { kKeyDate, METADATA_KEY_DATE }, 464 { kKeyGenre, METADATA_KEY_GENRE }, 465 { kKeyTitle, METADATA_KEY_TITLE }, 466 { kKeyYear, METADATA_KEY_YEAR }, 467 { kKeyWriter, METADATA_KEY_WRITER }, 468 { kKeyCompilation, METADATA_KEY_COMPILATION }, 469 { kKeyLocation, METADATA_KEY_LOCATION }, 470 }; 471 static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]); 472 473 for (size_t i = 0; i < kNumMapEntries; ++i) { 474 const char *value; 475 if (meta->findCString(kMap[i].from, &value)) { 476 mMetaData.add(kMap[i].to, String8(value)); 477 } 478 } 479 480 const void *data; 481 uint32_t type; 482 size_t dataSize; 483 if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize) 484 && mAlbumArt == NULL) { 485 mAlbumArt = MediaAlbumArt::fromData(dataSize, data); 486 } 487 488 size_t numTracks = mExtractor->countTracks(); 489 490 char tmp[32]; 491 sprintf(tmp, "%zu", 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, "%" PRId64, (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, "%" PRId64, 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