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