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 #include <gui/Surface.h> 24 25 #include "include/avc_utils.h" 26 #include "include/StagefrightMetadataRetriever.h" 27 28 #include <media/ICrypto.h> 29 #include <media/IMediaHTTPService.h> 30 31 #include <media/stagefright/foundation/ABuffer.h> 32 #include <media/stagefright/foundation/ADebug.h> 33 #include <media/stagefright/foundation/AMessage.h> 34 #include <media/stagefright/ColorConverter.h> 35 #include <media/stagefright/DataSource.h> 36 #include <media/stagefright/FileSource.h> 37 #include <media/stagefright/MediaBuffer.h> 38 #include <media/stagefright/MediaCodec.h> 39 #include <media/stagefright/MediaCodecList.h> 40 #include <media/stagefright/MediaDefs.h> 41 #include <media/stagefright/MediaErrors.h> 42 #include <media/stagefright/MediaExtractor.h> 43 #include <media/stagefright/MediaSource.h> 44 #include <media/stagefright/MetaData.h> 45 #include <media/stagefright/Utils.h> 46 47 #include <CharacterEncodingDetector.h> 48 49 namespace android { 50 51 static const int64_t kBufferTimeOutUs = 30000ll; // 30 msec 52 static const size_t kRetryCount = 20; // must be >0 53 54 StagefrightMetadataRetriever::StagefrightMetadataRetriever() 55 : mParsedMetaData(false), 56 mAlbumArt(NULL) { 57 ALOGV("StagefrightMetadataRetriever()"); 58 59 DataSource::RegisterDefaultSniffers(); 60 } 61 62 StagefrightMetadataRetriever::~StagefrightMetadataRetriever() { 63 ALOGV("~StagefrightMetadataRetriever()"); 64 clearMetadata(); 65 if (mSource != NULL) { 66 mSource->close(); 67 } 68 } 69 70 status_t StagefrightMetadataRetriever::setDataSource( 71 const sp<IMediaHTTPService> &httpService, 72 const char *uri, 73 const KeyedVector<String8, String8> *headers) { 74 ALOGV("setDataSource(%s)", uri); 75 76 clearMetadata(); 77 mSource = DataSource::CreateFromURI(httpService, uri, headers); 78 79 if (mSource == NULL) { 80 ALOGE("Unable to create data source for '%s'.", uri); 81 return UNKNOWN_ERROR; 82 } 83 84 mExtractor = MediaExtractor::Create(mSource); 85 86 if (mExtractor == NULL) { 87 ALOGE("Unable to instantiate an extractor for '%s'.", uri); 88 89 mSource.clear(); 90 91 return UNKNOWN_ERROR; 92 } 93 94 return OK; 95 } 96 97 // Warning caller retains ownership of the filedescriptor! Dup it if necessary. 98 status_t StagefrightMetadataRetriever::setDataSource( 99 int fd, int64_t offset, int64_t length) { 100 fd = dup(fd); 101 102 ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length); 103 104 clearMetadata(); 105 mSource = new FileSource(fd, offset, length); 106 107 status_t err; 108 if ((err = mSource->initCheck()) != OK) { 109 mSource.clear(); 110 111 return err; 112 } 113 114 mExtractor = MediaExtractor::Create(mSource); 115 116 if (mExtractor == NULL) { 117 mSource.clear(); 118 119 return UNKNOWN_ERROR; 120 } 121 122 return OK; 123 } 124 125 status_t StagefrightMetadataRetriever::setDataSource( 126 const sp<DataSource>& source) { 127 ALOGV("setDataSource(DataSource)"); 128 129 clearMetadata(); 130 mSource = source; 131 mExtractor = MediaExtractor::Create(mSource); 132 133 if (mExtractor == NULL) { 134 ALOGE("Failed to instantiate a MediaExtractor."); 135 mSource.clear(); 136 return UNKNOWN_ERROR; 137 } 138 139 return OK; 140 } 141 142 static VideoFrame *extractVideoFrame( 143 const AString &componentName, 144 const sp<MetaData> &trackMeta, 145 const sp<IMediaSource> &source, 146 int64_t frameTimeUs, 147 int seekMode) { 148 149 sp<MetaData> format = source->getFormat(); 150 151 sp<AMessage> videoFormat; 152 if (convertMetaDataToMessage(trackMeta, &videoFormat) != OK) { 153 ALOGE("b/23680780"); 154 ALOGW("Failed to convert meta data to message"); 155 return NULL; 156 } 157 158 // TODO: Use Flexible color instead 159 videoFormat->setInt32("color-format", OMX_COLOR_FormatYUV420Planar); 160 161 // For the thumbnail extraction case, try to allocate single buffer in both 162 // input and output ports, if seeking to a sync frame. NOTE: This request may 163 // fail if component requires more than that for decoding. 164 bool isSeekingClosest = (seekMode == MediaSource::ReadOptions::SEEK_CLOSEST); 165 if (!isSeekingClosest) { 166 videoFormat->setInt32("android._num-input-buffers", 1); 167 videoFormat->setInt32("android._num-output-buffers", 1); 168 } 169 170 status_t err; 171 sp<ALooper> looper = new ALooper; 172 looper->start(); 173 sp<MediaCodec> decoder = MediaCodec::CreateByComponentName( 174 looper, componentName, &err); 175 176 if (decoder.get() == NULL || err != OK) { 177 ALOGW("Failed to instantiate decoder [%s]", componentName.c_str()); 178 return NULL; 179 } 180 181 err = decoder->configure(videoFormat, NULL /* surface */, NULL /* crypto */, 0 /* flags */); 182 if (err != OK) { 183 ALOGW("configure returned error %d (%s)", err, asString(err)); 184 decoder->release(); 185 return NULL; 186 } 187 188 err = decoder->start(); 189 if (err != OK) { 190 ALOGW("start returned error %d (%s)", err, asString(err)); 191 decoder->release(); 192 return NULL; 193 } 194 195 MediaSource::ReadOptions options; 196 if (seekMode < MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC || 197 seekMode > MediaSource::ReadOptions::SEEK_CLOSEST) { 198 199 ALOGE("Unknown seek mode: %d", seekMode); 200 decoder->release(); 201 return NULL; 202 } 203 204 MediaSource::ReadOptions::SeekMode mode = 205 static_cast<MediaSource::ReadOptions::SeekMode>(seekMode); 206 207 int64_t thumbNailTime; 208 if (frameTimeUs < 0) { 209 if (!trackMeta->findInt64(kKeyThumbnailTime, &thumbNailTime) 210 || thumbNailTime < 0) { 211 thumbNailTime = 0; 212 } 213 options.setSeekTo(thumbNailTime, mode); 214 } else { 215 thumbNailTime = -1; 216 options.setSeekTo(frameTimeUs, mode); 217 } 218 219 err = source->start(); 220 if (err != OK) { 221 ALOGW("source failed to start: %d (%s)", err, asString(err)); 222 decoder->release(); 223 return NULL; 224 } 225 226 Vector<sp<ABuffer> > inputBuffers; 227 err = decoder->getInputBuffers(&inputBuffers); 228 if (err != OK) { 229 ALOGW("failed to get input buffers: %d (%s)", err, asString(err)); 230 decoder->release(); 231 source->stop(); 232 return NULL; 233 } 234 235 Vector<sp<ABuffer> > outputBuffers; 236 err = decoder->getOutputBuffers(&outputBuffers); 237 if (err != OK) { 238 ALOGW("failed to get output buffers: %d (%s)", err, asString(err)); 239 decoder->release(); 240 source->stop(); 241 return NULL; 242 } 243 244 sp<AMessage> outputFormat = NULL; 245 bool haveMoreInputs = true; 246 size_t index, offset, size; 247 int64_t timeUs; 248 size_t retriesLeft = kRetryCount; 249 bool done = false; 250 const char *mime; 251 bool success = format->findCString(kKeyMIMEType, &mime); 252 if (!success) { 253 ALOGE("Could not find mime type"); 254 return NULL; 255 } 256 257 bool isAvcOrHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) 258 || !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC); 259 260 bool firstSample = true; 261 int64_t targetTimeUs = -1ll; 262 263 do { 264 size_t inputIndex = -1; 265 int64_t ptsUs = 0ll; 266 uint32_t flags = 0; 267 sp<ABuffer> codecBuffer = NULL; 268 269 while (haveMoreInputs) { 270 err = decoder->dequeueInputBuffer(&inputIndex, kBufferTimeOutUs); 271 if (err != OK) { 272 ALOGW("Timed out waiting for input"); 273 if (retriesLeft) { 274 err = OK; 275 } 276 break; 277 } 278 codecBuffer = inputBuffers[inputIndex]; 279 280 MediaBuffer *mediaBuffer = NULL; 281 282 err = source->read(&mediaBuffer, &options); 283 options.clearSeekTo(); 284 if (err != OK) { 285 ALOGW("Input Error or EOS"); 286 haveMoreInputs = false; 287 break; 288 } 289 if (firstSample && isSeekingClosest) { 290 mediaBuffer->meta_data()->findInt64(kKeyTargetTime, &targetTimeUs); 291 ALOGV("Seeking closest: targetTimeUs=%lld", (long long)targetTimeUs); 292 } 293 firstSample = false; 294 295 if (mediaBuffer->range_length() > codecBuffer->capacity()) { 296 ALOGE("buffer size (%zu) too large for codec input size (%zu)", 297 mediaBuffer->range_length(), codecBuffer->capacity()); 298 err = BAD_VALUE; 299 } else { 300 codecBuffer->setRange(0, mediaBuffer->range_length()); 301 302 CHECK(mediaBuffer->meta_data()->findInt64(kKeyTime, &ptsUs)); 303 memcpy(codecBuffer->data(), 304 (const uint8_t*)mediaBuffer->data() + mediaBuffer->range_offset(), 305 mediaBuffer->range_length()); 306 if (isAvcOrHevc && IsIDR(codecBuffer) && !isSeekingClosest) { 307 // Only need to decode one IDR frame, unless we're seeking with CLOSEST 308 // option, in which case we need to actually decode to targetTimeUs. 309 haveMoreInputs = false; 310 flags |= MediaCodec::BUFFER_FLAG_EOS; 311 } 312 } 313 314 mediaBuffer->release(); 315 break; 316 } 317 318 if (err == OK && inputIndex < inputBuffers.size()) { 319 ALOGV("QueueInput: size=%zu ts=%" PRId64 " us flags=%x", 320 codecBuffer->size(), ptsUs, flags); 321 err = decoder->queueInputBuffer( 322 inputIndex, 323 codecBuffer->offset(), 324 codecBuffer->size(), 325 ptsUs, 326 flags); 327 328 // we don't expect an output from codec config buffer 329 if (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) { 330 continue; 331 } 332 } 333 334 while (err == OK) { 335 // wait for a decoded buffer 336 err = decoder->dequeueOutputBuffer( 337 &index, 338 &offset, 339 &size, 340 &timeUs, 341 &flags, 342 kBufferTimeOutUs); 343 344 if (err == INFO_FORMAT_CHANGED) { 345 ALOGV("Received format change"); 346 err = decoder->getOutputFormat(&outputFormat); 347 } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { 348 ALOGV("Output buffers changed"); 349 err = decoder->getOutputBuffers(&outputBuffers); 350 } else { 351 if (err == -EAGAIN /* INFO_TRY_AGAIN_LATER */ && --retriesLeft > 0) { 352 ALOGV("Timed-out waiting for output.. retries left = %zu", retriesLeft); 353 err = OK; 354 } else if (err == OK) { 355 // If we're seeking with CLOSEST option and obtained a valid targetTimeUs 356 // from the extractor, decode to the specified frame. Otherwise we're done. 357 done = (targetTimeUs < 0ll) || (timeUs >= targetTimeUs); 358 ALOGV("Received an output buffer, timeUs=%lld", (long long)timeUs); 359 if (!done) { 360 err = decoder->releaseOutputBuffer(index); 361 } 362 } else { 363 ALOGW("Received error %d (%s) instead of output", err, asString(err)); 364 done = true; 365 } 366 break; 367 } 368 } 369 } while (err == OK && !done); 370 371 if (err != OK || size <= 0 || outputFormat == NULL) { 372 ALOGE("Failed to decode thumbnail frame"); 373 source->stop(); 374 decoder->release(); 375 return NULL; 376 } 377 378 ALOGV("successfully decoded video frame."); 379 sp<ABuffer> videoFrameBuffer = outputBuffers.itemAt(index); 380 381 if (thumbNailTime >= 0) { 382 if (timeUs != thumbNailTime) { 383 AString mime; 384 CHECK(outputFormat->findString("mime", &mime)); 385 386 ALOGV("thumbNailTime = %lld us, timeUs = %lld us, mime = %s", 387 (long long)thumbNailTime, (long long)timeUs, mime.c_str()); 388 } 389 } 390 391 int32_t width, height; 392 CHECK(outputFormat->findInt32("width", &width)); 393 CHECK(outputFormat->findInt32("height", &height)); 394 395 int32_t crop_left, crop_top, crop_right, crop_bottom; 396 if (!outputFormat->findRect("crop", &crop_left, &crop_top, &crop_right, &crop_bottom)) { 397 crop_left = crop_top = 0; 398 crop_right = width - 1; 399 crop_bottom = height - 1; 400 } 401 402 int32_t rotationAngle; 403 if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) { 404 rotationAngle = 0; // By default, no rotation 405 } 406 407 VideoFrame *frame = new VideoFrame; 408 frame->mWidth = crop_right - crop_left + 1; 409 frame->mHeight = crop_bottom - crop_top + 1; 410 frame->mDisplayWidth = frame->mWidth; 411 frame->mDisplayHeight = frame->mHeight; 412 frame->mSize = frame->mWidth * frame->mHeight * 2; 413 frame->mData = new uint8_t[frame->mSize]; 414 frame->mRotationAngle = rotationAngle; 415 416 int32_t sarWidth, sarHeight; 417 if (trackMeta->findInt32(kKeySARWidth, &sarWidth) 418 && trackMeta->findInt32(kKeySARHeight, &sarHeight) 419 && sarHeight != 0) { 420 frame->mDisplayWidth = (frame->mDisplayWidth * sarWidth) / sarHeight; 421 } 422 423 int32_t srcFormat; 424 CHECK(outputFormat->findInt32("color-format", &srcFormat)); 425 426 ColorConverter converter((OMX_COLOR_FORMATTYPE)srcFormat, OMX_COLOR_Format16bitRGB565); 427 428 if (converter.isValid()) { 429 err = converter.convert( 430 (const uint8_t *)videoFrameBuffer->data(), 431 width, height, 432 crop_left, crop_top, crop_right, crop_bottom, 433 frame->mData, 434 frame->mWidth, 435 frame->mHeight, 436 0, 0, frame->mWidth - 1, frame->mHeight - 1); 437 } else { 438 ALOGE("Unable to convert from format 0x%08x to RGB565", srcFormat); 439 440 err = ERROR_UNSUPPORTED; 441 } 442 443 videoFrameBuffer.clear(); 444 source->stop(); 445 decoder->releaseOutputBuffer(index); 446 decoder->release(); 447 448 if (err != OK) { 449 ALOGE("Colorconverter failed to convert frame."); 450 451 delete frame; 452 frame = NULL; 453 } 454 455 return frame; 456 } 457 458 VideoFrame *StagefrightMetadataRetriever::getFrameAtTime( 459 int64_t timeUs, int option) { 460 461 ALOGV("getFrameAtTime: %" PRId64 " us option: %d", timeUs, option); 462 463 if (mExtractor.get() == NULL) { 464 ALOGV("no extractor."); 465 return NULL; 466 } 467 468 sp<MetaData> fileMeta = mExtractor->getMetaData(); 469 470 if (fileMeta == NULL) { 471 ALOGV("extractor doesn't publish metadata, failed to initialize?"); 472 return NULL; 473 } 474 475 int32_t drm = 0; 476 if (fileMeta->findInt32(kKeyIsDRM, &drm) && drm != 0) { 477 ALOGE("frame grab not allowed."); 478 return NULL; 479 } 480 481 size_t n = mExtractor->countTracks(); 482 size_t i; 483 for (i = 0; i < n; ++i) { 484 sp<MetaData> meta = mExtractor->getTrackMetaData(i); 485 486 const char *mime; 487 CHECK(meta->findCString(kKeyMIMEType, &mime)); 488 489 if (!strncasecmp(mime, "video/", 6)) { 490 break; 491 } 492 } 493 494 if (i == n) { 495 ALOGV("no video track found."); 496 return NULL; 497 } 498 499 sp<MetaData> trackMeta = mExtractor->getTrackMetaData( 500 i, MediaExtractor::kIncludeExtensiveMetaData); 501 502 sp<IMediaSource> source = mExtractor->getTrack(i); 503 504 if (source.get() == NULL) { 505 ALOGV("unable to instantiate video track."); 506 return NULL; 507 } 508 509 const void *data; 510 uint32_t type; 511 size_t dataSize; 512 if (fileMeta->findData(kKeyAlbumArt, &type, &data, &dataSize) 513 && mAlbumArt == NULL) { 514 mAlbumArt = MediaAlbumArt::fromData(dataSize, data); 515 } 516 517 const char *mime; 518 CHECK(trackMeta->findCString(kKeyMIMEType, &mime)); 519 520 Vector<AString> matchingCodecs; 521 MediaCodecList::findMatchingCodecs( 522 mime, 523 false, /* encoder */ 524 MediaCodecList::kPreferSoftwareCodecs, 525 &matchingCodecs); 526 527 for (size_t i = 0; i < matchingCodecs.size(); ++i) { 528 const AString &componentName = matchingCodecs[i]; 529 VideoFrame *frame = 530 extractVideoFrame(componentName, trackMeta, source, timeUs, option); 531 532 if (frame != NULL) { 533 return frame; 534 } 535 ALOGV("%s failed to extract thumbnail, trying next decoder.", componentName.c_str()); 536 } 537 538 return NULL; 539 } 540 541 MediaAlbumArt *StagefrightMetadataRetriever::extractAlbumArt() { 542 ALOGV("extractAlbumArt (extractor: %s)", mExtractor.get() != NULL ? "YES" : "NO"); 543 544 if (mExtractor == NULL) { 545 return NULL; 546 } 547 548 if (!mParsedMetaData) { 549 parseMetaData(); 550 551 mParsedMetaData = true; 552 } 553 554 if (mAlbumArt) { 555 return mAlbumArt->clone(); 556 } 557 558 return NULL; 559 } 560 561 const char *StagefrightMetadataRetriever::extractMetadata(int keyCode) { 562 if (mExtractor == NULL) { 563 return NULL; 564 } 565 566 if (!mParsedMetaData) { 567 parseMetaData(); 568 569 mParsedMetaData = true; 570 } 571 572 ssize_t index = mMetaData.indexOfKey(keyCode); 573 574 if (index < 0) { 575 return NULL; 576 } 577 578 return mMetaData.valueAt(index).string(); 579 } 580 581 void StagefrightMetadataRetriever::parseMetaData() { 582 sp<MetaData> meta = mExtractor->getMetaData(); 583 584 if (meta == NULL) { 585 ALOGV("extractor doesn't publish metadata, failed to initialize?"); 586 return; 587 } 588 589 struct Map { 590 int from; 591 int to; 592 const char *name; 593 }; 594 static const Map kMap[] = { 595 { kKeyMIMEType, METADATA_KEY_MIMETYPE, NULL }, 596 { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER, "tracknumber" }, 597 { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER, "discnumber" }, 598 { kKeyAlbum, METADATA_KEY_ALBUM, "album" }, 599 { kKeyArtist, METADATA_KEY_ARTIST, "artist" }, 600 { kKeyAlbumArtist, METADATA_KEY_ALBUMARTIST, "albumartist" }, 601 { kKeyAuthor, METADATA_KEY_AUTHOR, NULL }, 602 { kKeyComposer, METADATA_KEY_COMPOSER, "composer" }, 603 { kKeyDate, METADATA_KEY_DATE, NULL }, 604 { kKeyGenre, METADATA_KEY_GENRE, "genre" }, 605 { kKeyTitle, METADATA_KEY_TITLE, "title" }, 606 { kKeyYear, METADATA_KEY_YEAR, "year" }, 607 { kKeyWriter, METADATA_KEY_WRITER, "writer" }, 608 { kKeyCompilation, METADATA_KEY_COMPILATION, "compilation" }, 609 { kKeyLocation, METADATA_KEY_LOCATION, NULL }, 610 }; 611 612 static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]); 613 614 CharacterEncodingDetector *detector = new CharacterEncodingDetector(); 615 616 for (size_t i = 0; i < kNumMapEntries; ++i) { 617 const char *value; 618 if (meta->findCString(kMap[i].from, &value)) { 619 if (kMap[i].name) { 620 // add to charset detector 621 detector->addTag(kMap[i].name, value); 622 } else { 623 // directly add to output list 624 mMetaData.add(kMap[i].to, String8(value)); 625 } 626 } 627 } 628 629 detector->detectAndConvert(); 630 int size = detector->size(); 631 if (size) { 632 for (int i = 0; i < size; i++) { 633 const char *name; 634 const char *value; 635 detector->getTag(i, &name, &value); 636 for (size_t j = 0; j < kNumMapEntries; ++j) { 637 if (kMap[j].name && !strcmp(kMap[j].name, name)) { 638 mMetaData.add(kMap[j].to, String8(value)); 639 } 640 } 641 } 642 } 643 delete detector; 644 645 const void *data; 646 uint32_t type; 647 size_t dataSize; 648 if (meta->findData(kKeyAlbumArt, &type, &data, &dataSize) 649 && mAlbumArt == NULL) { 650 mAlbumArt = MediaAlbumArt::fromData(dataSize, data); 651 } 652 653 size_t numTracks = mExtractor->countTracks(); 654 655 char tmp[32]; 656 sprintf(tmp, "%zu", numTracks); 657 658 mMetaData.add(METADATA_KEY_NUM_TRACKS, String8(tmp)); 659 660 float captureFps; 661 if (meta->findFloat(kKeyCaptureFramerate, &captureFps)) { 662 sprintf(tmp, "%f", captureFps); 663 mMetaData.add(METADATA_KEY_CAPTURE_FRAMERATE, String8(tmp)); 664 } 665 666 bool hasAudio = false; 667 bool hasVideo = false; 668 int32_t videoWidth = -1; 669 int32_t videoHeight = -1; 670 int32_t audioBitrate = -1; 671 int32_t rotationAngle = -1; 672 673 // The overall duration is the duration of the longest track. 674 int64_t maxDurationUs = 0; 675 String8 timedTextLang; 676 for (size_t i = 0; i < numTracks; ++i) { 677 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i); 678 679 int64_t durationUs; 680 if (trackMeta->findInt64(kKeyDuration, &durationUs)) { 681 if (durationUs > maxDurationUs) { 682 maxDurationUs = durationUs; 683 } 684 } 685 686 const char *mime; 687 if (trackMeta->findCString(kKeyMIMEType, &mime)) { 688 if (!hasAudio && !strncasecmp("audio/", mime, 6)) { 689 hasAudio = true; 690 691 if (!trackMeta->findInt32(kKeyBitRate, &audioBitrate)) { 692 audioBitrate = -1; 693 } 694 } else if (!hasVideo && !strncasecmp("video/", mime, 6)) { 695 hasVideo = true; 696 697 CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth)); 698 CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight)); 699 if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) { 700 rotationAngle = 0; 701 } 702 } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { 703 const char *lang; 704 if (trackMeta->findCString(kKeyMediaLanguage, &lang)) { 705 timedTextLang.append(String8(lang)); 706 timedTextLang.append(String8(":")); 707 } else { 708 ALOGE("No language found for timed text"); 709 } 710 } 711 } 712 } 713 714 // To save the language codes for all timed text tracks 715 // If multiple text tracks present, the format will look 716 // like "eng:chi" 717 if (!timedTextLang.isEmpty()) { 718 mMetaData.add(METADATA_KEY_TIMED_TEXT_LANGUAGES, timedTextLang); 719 } 720 721 // The duration value is a string representing the duration in ms. 722 sprintf(tmp, "%" PRId64, (maxDurationUs + 500) / 1000); 723 mMetaData.add(METADATA_KEY_DURATION, String8(tmp)); 724 725 if (hasAudio) { 726 mMetaData.add(METADATA_KEY_HAS_AUDIO, String8("yes")); 727 } 728 729 if (hasVideo) { 730 mMetaData.add(METADATA_KEY_HAS_VIDEO, String8("yes")); 731 732 sprintf(tmp, "%d", videoWidth); 733 mMetaData.add(METADATA_KEY_VIDEO_WIDTH, String8(tmp)); 734 735 sprintf(tmp, "%d", videoHeight); 736 mMetaData.add(METADATA_KEY_VIDEO_HEIGHT, String8(tmp)); 737 738 sprintf(tmp, "%d", rotationAngle); 739 mMetaData.add(METADATA_KEY_VIDEO_ROTATION, String8(tmp)); 740 } 741 742 if (numTracks == 1 && hasAudio && audioBitrate >= 0) { 743 sprintf(tmp, "%d", audioBitrate); 744 mMetaData.add(METADATA_KEY_BITRATE, String8(tmp)); 745 } else { 746 off64_t sourceSize; 747 if (mSource != NULL && mSource->getSize(&sourceSize) == OK) { 748 int64_t avgBitRate = (int64_t)(sourceSize * 8E6 / maxDurationUs); 749 750 sprintf(tmp, "%" PRId64, avgBitRate); 751 mMetaData.add(METADATA_KEY_BITRATE, String8(tmp)); 752 } 753 } 754 755 if (numTracks == 1) { 756 const char *fileMIME; 757 CHECK(meta->findCString(kKeyMIMEType, &fileMIME)); 758 759 if (!strcasecmp(fileMIME, "video/x-matroska")) { 760 sp<MetaData> trackMeta = mExtractor->getTrackMetaData(0); 761 const char *trackMIME; 762 CHECK(trackMeta->findCString(kKeyMIMEType, &trackMIME)); 763 764 if (!strncasecmp("audio/", trackMIME, 6)) { 765 // The matroska file only contains a single audio track, 766 // rewrite its mime type. 767 mMetaData.add( 768 METADATA_KEY_MIMETYPE, String8("audio/x-matroska")); 769 } 770 } 771 } 772 773 // To check whether the media file is drm-protected 774 if (mExtractor->getDrmFlag()) { 775 mMetaData.add(METADATA_KEY_IS_DRM, String8("1")); 776 } 777 } 778 779 void StagefrightMetadataRetriever::clearMetadata() { 780 mParsedMetaData = false; 781 mMetaData.clear(); 782 delete mAlbumArt; 783 mAlbumArt = NULL; 784 } 785 786 } // namespace android 787