NuMediaExtractor.cpp revision a53d87c7b1428fe02f535c31dafd64cb1362fde9
1/* 2 * Copyright 2012, 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 "NuMediaExtractor" 19#include <utils/Log.h> 20 21#include <media/stagefright/NuMediaExtractor.h> 22 23#include "include/ESDS.h" 24#include "include/NuCachedSource2.h" 25#include "include/WVMExtractor.h" 26 27#include <media/stagefright/foundation/ABuffer.h> 28#include <media/stagefright/foundation/ADebug.h> 29#include <media/stagefright/foundation/AMessage.h> 30#include <media/stagefright/DataSource.h> 31#include <media/stagefright/FileSource.h> 32#include <media/stagefright/MediaBuffer.h> 33#include <media/stagefright/MediaDefs.h> 34#include <media/stagefright/MediaErrors.h> 35#include <media/stagefright/MediaExtractor.h> 36#include <media/stagefright/MediaSource.h> 37#include <media/stagefright/MetaData.h> 38#include <media/stagefright/Utils.h> 39 40namespace android { 41 42NuMediaExtractor::NuMediaExtractor() 43 : mIsWidevineExtractor(false), 44 mTotalBitrate(-1ll), 45 mDurationUs(-1ll) { 46} 47 48NuMediaExtractor::~NuMediaExtractor() { 49 releaseTrackSamples(); 50 51 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 52 TrackInfo *info = &mSelectedTracks.editItemAt(i); 53 54 CHECK_EQ((status_t)OK, info->mSource->stop()); 55 } 56 57 mSelectedTracks.clear(); 58} 59 60status_t NuMediaExtractor::setDataSource( 61 const char *path, const KeyedVector<String8, String8> *headers) { 62 if (mImpl != NULL) { 63 return -EINVAL; 64 } 65 66 sp<DataSource> dataSource = 67 DataSource::CreateFromURI(path, headers); 68 69 if (dataSource == NULL) { 70 return -ENOENT; 71 } 72 73 mIsWidevineExtractor = false; 74 if (!strncasecmp("widevine://", path, 11)) { 75 String8 mimeType; 76 float confidence; 77 sp<AMessage> dummy; 78 bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy); 79 80 if (!success 81 || strcasecmp( 82 mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) { 83 return ERROR_UNSUPPORTED; 84 } 85 86 sp<WVMExtractor> extractor = new WVMExtractor(dataSource); 87 extractor->setAdaptiveStreamingMode(true); 88 89 mImpl = extractor; 90 mIsWidevineExtractor = true; 91 } else { 92 mImpl = MediaExtractor::Create(dataSource); 93 } 94 95 if (mImpl == NULL) { 96 return ERROR_UNSUPPORTED; 97 } 98 99 mDataSource = dataSource; 100 101 updateDurationAndBitrate(); 102 103 return OK; 104} 105 106status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { 107 if (mImpl != NULL) { 108 return -EINVAL; 109 } 110 111 sp<FileSource> fileSource = new FileSource(dup(fd), offset, size); 112 113 status_t err = fileSource->initCheck(); 114 if (err != OK) { 115 return err; 116 } 117 118 mImpl = MediaExtractor::Create(fileSource); 119 120 if (mImpl == NULL) { 121 return ERROR_UNSUPPORTED; 122 } 123 124 mDataSource = fileSource; 125 126 updateDurationAndBitrate(); 127 128 return OK; 129} 130 131void NuMediaExtractor::updateDurationAndBitrate() { 132 mTotalBitrate = 0ll; 133 mDurationUs = -1ll; 134 135 for (size_t i = 0; i < mImpl->countTracks(); ++i) { 136 sp<MetaData> meta = mImpl->getTrackMetaData(i); 137 138 int32_t bitrate; 139 if (!meta->findInt32(kKeyBitRate, &bitrate)) { 140 const char *mime; 141 CHECK(meta->findCString(kKeyMIMEType, &mime)); 142 ALOGV("track of type '%s' does not publish bitrate", mime); 143 144 mTotalBitrate = -1ll; 145 } else if (mTotalBitrate >= 0ll) { 146 mTotalBitrate += bitrate; 147 } 148 149 int64_t durationUs; 150 if (meta->findInt64(kKeyDuration, &durationUs) 151 && durationUs > mDurationUs) { 152 mDurationUs = durationUs; 153 } 154 } 155} 156 157size_t NuMediaExtractor::countTracks() const { 158 return mImpl == NULL ? 0 : mImpl->countTracks(); 159} 160 161status_t NuMediaExtractor::getTrackFormat( 162 size_t index, sp<AMessage> *format) const { 163 *format = NULL; 164 165 if (mImpl == NULL) { 166 return -EINVAL; 167 } 168 169 if (index >= mImpl->countTracks()) { 170 return -ERANGE; 171 } 172 173 sp<MetaData> meta = mImpl->getTrackMetaData(index); 174 175 const char *mime; 176 CHECK(meta->findCString(kKeyMIMEType, &mime)); 177 178 sp<AMessage> msg = new AMessage; 179 msg->setString("mime", mime); 180 181 int64_t durationUs; 182 if (meta->findInt64(kKeyDuration, &durationUs)) { 183 msg->setInt64("durationUs", durationUs); 184 } 185 186 if (!strncasecmp("video/", mime, 6)) { 187 int32_t width, height; 188 CHECK(meta->findInt32(kKeyWidth, &width)); 189 CHECK(meta->findInt32(kKeyHeight, &height)); 190 191 msg->setInt32("width", width); 192 msg->setInt32("height", height); 193 } else { 194 CHECK(!strncasecmp("audio/", mime, 6)); 195 196 int32_t numChannels, sampleRate; 197 CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); 198 CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); 199 200 msg->setInt32("channel-count", numChannels); 201 msg->setInt32("sample-rate", sampleRate); 202 203 int32_t isADTS; 204 if (meta->findInt32(kKeyIsADTS, &isADTS)) { 205 msg->setInt32("is-adts", true); 206 } 207 } 208 209 int32_t maxInputSize; 210 if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) { 211 msg->setInt32("max-input-size", maxInputSize); 212 } 213 214 uint32_t type; 215 const void *data; 216 size_t size; 217 if (meta->findData(kKeyAVCC, &type, &data, &size)) { 218 // Parse the AVCDecoderConfigurationRecord 219 220 const uint8_t *ptr = (const uint8_t *)data; 221 222 CHECK(size >= 7); 223 CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1 224 uint8_t profile = ptr[1]; 225 uint8_t level = ptr[3]; 226 227 // There is decodable content out there that fails the following 228 // assertion, let's be lenient for now... 229 // CHECK((ptr[4] >> 2) == 0x3f); // reserved 230 231 size_t lengthSize = 1 + (ptr[4] & 3); 232 233 // commented out check below as H264_QVGA_500_NO_AUDIO.3gp 234 // violates it... 235 // CHECK((ptr[5] >> 5) == 7); // reserved 236 237 size_t numSeqParameterSets = ptr[5] & 31; 238 239 ptr += 6; 240 size -= 6; 241 242 sp<ABuffer> buffer = new ABuffer(1024); 243 buffer->setRange(0, 0); 244 245 for (size_t i = 0; i < numSeqParameterSets; ++i) { 246 CHECK(size >= 2); 247 size_t length = U16_AT(ptr); 248 249 ptr += 2; 250 size -= 2; 251 252 CHECK(size >= length); 253 254 memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); 255 memcpy(buffer->data() + buffer->size() + 4, ptr, length); 256 buffer->setRange(0, buffer->size() + 4 + length); 257 258 ptr += length; 259 size -= length; 260 } 261 262 buffer->meta()->setInt32("csd", true); 263 buffer->meta()->setInt64("timeUs", 0); 264 265 msg->setBuffer("csd-0", buffer); 266 267 buffer = new ABuffer(1024); 268 buffer->setRange(0, 0); 269 270 CHECK(size >= 1); 271 size_t numPictureParameterSets = *ptr; 272 ++ptr; 273 --size; 274 275 for (size_t i = 0; i < numPictureParameterSets; ++i) { 276 CHECK(size >= 2); 277 size_t length = U16_AT(ptr); 278 279 ptr += 2; 280 size -= 2; 281 282 CHECK(size >= length); 283 284 memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); 285 memcpy(buffer->data() + buffer->size() + 4, ptr, length); 286 buffer->setRange(0, buffer->size() + 4 + length); 287 288 ptr += length; 289 size -= length; 290 } 291 292 buffer->meta()->setInt32("csd", true); 293 buffer->meta()->setInt64("timeUs", 0); 294 msg->setBuffer("csd-1", buffer); 295 } else if (meta->findData(kKeyESDS, &type, &data, &size)) { 296 ESDS esds((const char *)data, size); 297 CHECK_EQ(esds.InitCheck(), (status_t)OK); 298 299 const void *codec_specific_data; 300 size_t codec_specific_data_size; 301 esds.getCodecSpecificInfo( 302 &codec_specific_data, &codec_specific_data_size); 303 304 sp<ABuffer> buffer = new ABuffer(codec_specific_data_size); 305 306 memcpy(buffer->data(), codec_specific_data, 307 codec_specific_data_size); 308 309 buffer->meta()->setInt32("csd", true); 310 buffer->meta()->setInt64("timeUs", 0); 311 msg->setBuffer("csd-0", buffer); 312 } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { 313 sp<ABuffer> buffer = new ABuffer(size); 314 memcpy(buffer->data(), data, size); 315 316 buffer->meta()->setInt32("csd", true); 317 buffer->meta()->setInt64("timeUs", 0); 318 msg->setBuffer("csd-0", buffer); 319 320 if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) { 321 return -EINVAL; 322 } 323 324 buffer = new ABuffer(size); 325 memcpy(buffer->data(), data, size); 326 327 buffer->meta()->setInt32("csd", true); 328 buffer->meta()->setInt64("timeUs", 0); 329 msg->setBuffer("csd-1", buffer); 330 } 331 332 *format = msg; 333 334 return OK; 335} 336 337status_t NuMediaExtractor::selectTrack(size_t index) { 338 if (mImpl == NULL) { 339 return -EINVAL; 340 } 341 342 if (index >= mImpl->countTracks()) { 343 return -ERANGE; 344 } 345 346 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 347 TrackInfo *info = &mSelectedTracks.editItemAt(i); 348 349 if (info->mTrackIndex == index) { 350 // This track has already been selected. 351 return OK; 352 } 353 } 354 355 sp<MediaSource> source = mImpl->getTrack(index); 356 357 CHECK_EQ((status_t)OK, source->start()); 358 359 mSelectedTracks.push(); 360 TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1); 361 362 info->mSource = source; 363 info->mTrackIndex = index; 364 info->mFinalResult = OK; 365 info->mSample = NULL; 366 info->mSampleTimeUs = -1ll; 367 info->mTrackFlags = 0; 368 369 const char *mime; 370 CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime)); 371 372 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { 373 info->mTrackFlags |= kIsVorbis; 374 } 375 376 return OK; 377} 378 379void NuMediaExtractor::releaseTrackSamples() { 380 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 381 TrackInfo *info = &mSelectedTracks.editItemAt(i); 382 383 if (info->mSample != NULL) { 384 info->mSample->release(); 385 info->mSample = NULL; 386 387 info->mSampleTimeUs = -1ll; 388 } 389 } 390} 391 392ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) { 393 TrackInfo *minInfo = NULL; 394 ssize_t minIndex = -1; 395 396 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 397 TrackInfo *info = &mSelectedTracks.editItemAt(i); 398 399 if (seekTimeUs >= 0ll) { 400 info->mFinalResult = OK; 401 402 if (info->mSample != NULL) { 403 info->mSample->release(); 404 info->mSample = NULL; 405 info->mSampleTimeUs = -1ll; 406 } 407 } else if (info->mFinalResult != OK) { 408 continue; 409 } 410 411 if (info->mSample == NULL) { 412 MediaSource::ReadOptions options; 413 if (seekTimeUs >= 0ll) { 414 options.setSeekTo(seekTimeUs); 415 } 416 status_t err = info->mSource->read(&info->mSample, &options); 417 418 if (err != OK) { 419 CHECK(info->mSample == NULL); 420 421 info->mFinalResult = err; 422 info->mSampleTimeUs = -1ll; 423 continue; 424 } else { 425 CHECK(info->mSample != NULL); 426 CHECK(info->mSample->meta_data()->findInt64( 427 kKeyTime, &info->mSampleTimeUs)); 428 } 429 } 430 431 if (minInfo == NULL || info->mSampleTimeUs < minInfo->mSampleTimeUs) { 432 minInfo = info; 433 minIndex = i; 434 } 435 } 436 437 return minIndex; 438} 439 440status_t NuMediaExtractor::seekTo(int64_t timeUs) { 441 ssize_t minIndex = fetchTrackSamples(timeUs); 442 443 if (minIndex < 0) { 444 return ERROR_END_OF_STREAM; 445 } 446 447 return OK; 448} 449 450status_t NuMediaExtractor::advance() { 451 ssize_t minIndex = fetchTrackSamples(); 452 453 if (minIndex < 0) { 454 return ERROR_END_OF_STREAM; 455 } 456 457 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 458 459 info->mSample->release(); 460 info->mSample = NULL; 461 info->mSampleTimeUs = -1ll; 462 463 return OK; 464} 465 466status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { 467 ssize_t minIndex = fetchTrackSamples(); 468 469 if (minIndex < 0) { 470 return ERROR_END_OF_STREAM; 471 } 472 473 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 474 475 size_t sampleSize = info->mSample->range_length(); 476 477 if (info->mTrackFlags & kIsVorbis) { 478 // Each sample's data is suffixed by the number of page samples 479 // or -1 if not available. 480 sampleSize += sizeof(int32_t); 481 } 482 483 if (buffer->capacity() < sampleSize) { 484 return -ENOMEM; 485 } 486 487 const uint8_t *src = 488 (const uint8_t *)info->mSample->data() 489 + info->mSample->range_offset(); 490 491 memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length()); 492 493 if (info->mTrackFlags & kIsVorbis) { 494 int32_t numPageSamples; 495 if (!info->mSample->meta_data()->findInt32( 496 kKeyValidSamples, &numPageSamples)) { 497 numPageSamples = -1; 498 } 499 500 memcpy((uint8_t *)buffer->data() + info->mSample->range_length(), 501 &numPageSamples, 502 sizeof(numPageSamples)); 503 } 504 505 buffer->setRange(0, sampleSize); 506 507 return OK; 508} 509 510status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { 511 ssize_t minIndex = fetchTrackSamples(); 512 513 if (minIndex < 0) { 514 return ERROR_END_OF_STREAM; 515 } 516 517 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 518 *trackIndex = info->mTrackIndex; 519 520 return OK; 521} 522 523status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { 524 ssize_t minIndex = fetchTrackSamples(); 525 526 if (minIndex < 0) { 527 return ERROR_END_OF_STREAM; 528 } 529 530 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 531 *sampleTimeUs = info->mSampleTimeUs; 532 533 return OK; 534} 535 536status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) { 537 *sampleMeta = NULL; 538 539 ssize_t minIndex = fetchTrackSamples(); 540 541 if (minIndex < 0) { 542 return ERROR_END_OF_STREAM; 543 } 544 545 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 546 *sampleMeta = info->mSample->meta_data(); 547 548 return OK; 549} 550 551bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const { 552 if (mTotalBitrate >= 0) { 553 *bitrate = mTotalBitrate; 554 return true; 555 } 556 557 off64_t size; 558 if (mDurationUs >= 0 && mDataSource->getSize(&size) == OK) { 559 *bitrate = size * 8000000ll / mDurationUs; // in bits/sec 560 return true; 561 } 562 563 return false; 564} 565 566// Returns true iff cached duration is available/applicable. 567bool NuMediaExtractor::getCachedDuration( 568 int64_t *durationUs, bool *eos) const { 569 int64_t bitrate; 570 if (mIsWidevineExtractor) { 571 sp<WVMExtractor> wvmExtractor = 572 static_cast<WVMExtractor *>(mImpl.get()); 573 574 status_t finalStatus; 575 *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus); 576 *eos = (finalStatus != OK); 577 return true; 578 } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource) 579 && getTotalBitrate(&bitrate)) { 580 sp<NuCachedSource2> cachedSource = 581 static_cast<NuCachedSource2 *>(mDataSource.get()); 582 583 status_t finalStatus; 584 size_t cachedDataRemaining = 585 cachedSource->approxDataRemaining(&finalStatus); 586 587 *durationUs = cachedDataRemaining * 8000000ll / bitrate; 588 *eos = (finalStatus != OK); 589 return true; 590 } 591 592 return false; 593} 594 595} // namespace android 596