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