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 40 namespace android { 41 42 NuMediaExtractor::NuMediaExtractor() 43 : mIsWidevineExtractor(false), 44 mTotalBitrate(-1ll), 45 mDurationUs(-1ll) { 46 } 47 48 NuMediaExtractor::~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 60 status_t NuMediaExtractor::setDataSource( 61 const char *path, const KeyedVector<String8, String8> *headers) { 62 Mutex::Autolock autoLock(mLock); 63 64 if (mImpl != NULL) { 65 return -EINVAL; 66 } 67 68 sp<DataSource> dataSource = 69 DataSource::CreateFromURI(path, headers); 70 71 if (dataSource == NULL) { 72 return -ENOENT; 73 } 74 75 mIsWidevineExtractor = false; 76 if (!strncasecmp("widevine://", path, 11)) { 77 String8 mimeType; 78 float confidence; 79 sp<AMessage> dummy; 80 bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy); 81 82 if (!success 83 || strcasecmp( 84 mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) { 85 return ERROR_UNSUPPORTED; 86 } 87 88 sp<WVMExtractor> extractor = new WVMExtractor(dataSource); 89 extractor->setAdaptiveStreamingMode(true); 90 91 mImpl = extractor; 92 mIsWidevineExtractor = true; 93 } else { 94 mImpl = MediaExtractor::Create(dataSource); 95 } 96 97 if (mImpl == NULL) { 98 return ERROR_UNSUPPORTED; 99 } 100 101 sp<MetaData> fileMeta = mImpl->getMetaData(); 102 const char *containerMime; 103 if (fileMeta != NULL 104 && fileMeta->findCString(kKeyMIMEType, &containerMime) 105 && !strcasecmp(containerMime, "video/wvm")) { 106 // We always want to use "cryptoPluginMode" when using the wvm 107 // extractor. We can tell that it is this extractor by looking 108 // at the container mime type. 109 // The cryptoPluginMode ensures that the extractor will actually 110 // give us data in a call to MediaSource::read(), unlike its 111 // default mode that we use from AwesomePlayer. 112 static_cast<WVMExtractor *>(mImpl.get())->setCryptoPluginMode(true); 113 } else if (mImpl->getDrmFlag()) { 114 // For all other drm content, we don't want to expose decrypted 115 // content to Java application. 116 mImpl.clear(); 117 mImpl = NULL; 118 return ERROR_UNSUPPORTED; 119 } 120 121 mDataSource = dataSource; 122 123 updateDurationAndBitrate(); 124 125 return OK; 126 } 127 128 status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { 129 130 Mutex::Autolock autoLock(mLock); 131 132 if (mImpl != NULL) { 133 return -EINVAL; 134 } 135 136 sp<FileSource> fileSource = new FileSource(dup(fd), offset, size); 137 138 status_t err = fileSource->initCheck(); 139 if (err != OK) { 140 return err; 141 } 142 143 mImpl = MediaExtractor::Create(fileSource); 144 145 if (mImpl == NULL) { 146 return ERROR_UNSUPPORTED; 147 } 148 149 mDataSource = fileSource; 150 151 updateDurationAndBitrate(); 152 153 return OK; 154 } 155 156 status_t NuMediaExtractor::setDataSource(const sp<DataSource> &source) { 157 Mutex::Autolock autoLock(mLock); 158 159 if (mImpl != NULL) { 160 return -EINVAL; 161 } 162 163 status_t err = source->initCheck(); 164 if (err != OK) { 165 return err; 166 } 167 168 mImpl = MediaExtractor::Create(source); 169 170 if (mImpl == NULL) { 171 return ERROR_UNSUPPORTED; 172 } 173 174 mDataSource = source; 175 176 updateDurationAndBitrate(); 177 178 return OK; 179 } 180 181 void NuMediaExtractor::updateDurationAndBitrate() { 182 mTotalBitrate = 0ll; 183 mDurationUs = -1ll; 184 185 for (size_t i = 0; i < mImpl->countTracks(); ++i) { 186 sp<MetaData> meta = mImpl->getTrackMetaData(i); 187 188 int32_t bitrate; 189 if (!meta->findInt32(kKeyBitRate, &bitrate)) { 190 const char *mime; 191 CHECK(meta->findCString(kKeyMIMEType, &mime)); 192 ALOGV("track of type '%s' does not publish bitrate", mime); 193 194 mTotalBitrate = -1ll; 195 } else if (mTotalBitrate >= 0ll) { 196 mTotalBitrate += bitrate; 197 } 198 199 int64_t durationUs; 200 if (meta->findInt64(kKeyDuration, &durationUs) 201 && durationUs > mDurationUs) { 202 mDurationUs = durationUs; 203 } 204 } 205 } 206 207 size_t NuMediaExtractor::countTracks() const { 208 Mutex::Autolock autoLock(mLock); 209 210 return mImpl == NULL ? 0 : mImpl->countTracks(); 211 } 212 213 status_t NuMediaExtractor::getTrackFormat( 214 size_t index, sp<AMessage> *format) const { 215 Mutex::Autolock autoLock(mLock); 216 217 *format = NULL; 218 219 if (mImpl == NULL) { 220 return -EINVAL; 221 } 222 223 if (index >= mImpl->countTracks()) { 224 return -ERANGE; 225 } 226 227 sp<MetaData> meta = mImpl->getTrackMetaData(index); 228 return convertMetaDataToMessage(meta, format); 229 } 230 231 status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const { 232 Mutex::Autolock autoLock(mLock); 233 234 *format = NULL; 235 236 if (mImpl == NULL) { 237 return -EINVAL; 238 } 239 240 sp<MetaData> meta = mImpl->getMetaData(); 241 242 const char *mime; 243 CHECK(meta->findCString(kKeyMIMEType, &mime)); 244 *format = new AMessage(); 245 (*format)->setString("mime", mime); 246 247 uint32_t type; 248 const void *pssh; 249 size_t psshsize; 250 if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) { 251 sp<ABuffer> buf = new ABuffer(psshsize); 252 memcpy(buf->data(), pssh, psshsize); 253 (*format)->setBuffer("pssh", buf); 254 } 255 256 return OK; 257 } 258 259 status_t NuMediaExtractor::selectTrack(size_t index) { 260 Mutex::Autolock autoLock(mLock); 261 262 if (mImpl == NULL) { 263 return -EINVAL; 264 } 265 266 if (index >= mImpl->countTracks()) { 267 return -ERANGE; 268 } 269 270 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 271 TrackInfo *info = &mSelectedTracks.editItemAt(i); 272 273 if (info->mTrackIndex == index) { 274 // This track has already been selected. 275 return OK; 276 } 277 } 278 279 sp<MediaSource> source = mImpl->getTrack(index); 280 281 CHECK_EQ((status_t)OK, source->start()); 282 283 mSelectedTracks.push(); 284 TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1); 285 286 info->mSource = source; 287 info->mTrackIndex = index; 288 info->mFinalResult = OK; 289 info->mSample = NULL; 290 info->mSampleTimeUs = -1ll; 291 info->mTrackFlags = 0; 292 293 const char *mime; 294 CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime)); 295 296 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { 297 info->mTrackFlags |= kIsVorbis; 298 } 299 300 return OK; 301 } 302 303 status_t NuMediaExtractor::unselectTrack(size_t index) { 304 Mutex::Autolock autoLock(mLock); 305 306 if (mImpl == NULL) { 307 return -EINVAL; 308 } 309 310 if (index >= mImpl->countTracks()) { 311 return -ERANGE; 312 } 313 314 size_t i; 315 for (i = 0; i < mSelectedTracks.size(); ++i) { 316 TrackInfo *info = &mSelectedTracks.editItemAt(i); 317 318 if (info->mTrackIndex == index) { 319 break; 320 } 321 } 322 323 if (i == mSelectedTracks.size()) { 324 // Not selected. 325 return OK; 326 } 327 328 TrackInfo *info = &mSelectedTracks.editItemAt(i); 329 330 if (info->mSample != NULL) { 331 info->mSample->release(); 332 info->mSample = NULL; 333 334 info->mSampleTimeUs = -1ll; 335 } 336 337 CHECK_EQ((status_t)OK, info->mSource->stop()); 338 339 mSelectedTracks.removeAt(i); 340 341 return OK; 342 } 343 344 void NuMediaExtractor::releaseTrackSamples() { 345 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 346 TrackInfo *info = &mSelectedTracks.editItemAt(i); 347 348 if (info->mSample != NULL) { 349 info->mSample->release(); 350 info->mSample = NULL; 351 352 info->mSampleTimeUs = -1ll; 353 } 354 } 355 } 356 357 ssize_t NuMediaExtractor::fetchTrackSamples( 358 int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) { 359 TrackInfo *minInfo = NULL; 360 ssize_t minIndex = -1; 361 362 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 363 TrackInfo *info = &mSelectedTracks.editItemAt(i); 364 365 if (seekTimeUs >= 0ll) { 366 info->mFinalResult = OK; 367 368 if (info->mSample != NULL) { 369 info->mSample->release(); 370 info->mSample = NULL; 371 info->mSampleTimeUs = -1ll; 372 } 373 } else if (info->mFinalResult != OK) { 374 continue; 375 } 376 377 if (info->mSample == NULL) { 378 MediaSource::ReadOptions options; 379 if (seekTimeUs >= 0ll) { 380 options.setSeekTo(seekTimeUs, mode); 381 } 382 status_t err = info->mSource->read(&info->mSample, &options); 383 384 if (err != OK) { 385 CHECK(info->mSample == NULL); 386 387 info->mFinalResult = err; 388 389 if (info->mFinalResult != ERROR_END_OF_STREAM) { 390 ALOGW("read on track %d failed with error %d", 391 info->mTrackIndex, err); 392 } 393 394 info->mSampleTimeUs = -1ll; 395 continue; 396 } else { 397 CHECK(info->mSample != NULL); 398 CHECK(info->mSample->meta_data()->findInt64( 399 kKeyTime, &info->mSampleTimeUs)); 400 } 401 } 402 403 if (minInfo == NULL || info->mSampleTimeUs < minInfo->mSampleTimeUs) { 404 minInfo = info; 405 minIndex = i; 406 } 407 } 408 409 return minIndex; 410 } 411 412 status_t NuMediaExtractor::seekTo( 413 int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) { 414 Mutex::Autolock autoLock(mLock); 415 416 ssize_t minIndex = fetchTrackSamples(timeUs, mode); 417 418 if (minIndex < 0) { 419 return ERROR_END_OF_STREAM; 420 } 421 422 return OK; 423 } 424 425 status_t NuMediaExtractor::advance() { 426 Mutex::Autolock autoLock(mLock); 427 428 ssize_t minIndex = fetchTrackSamples(); 429 430 if (minIndex < 0) { 431 return ERROR_END_OF_STREAM; 432 } 433 434 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 435 436 info->mSample->release(); 437 info->mSample = NULL; 438 info->mSampleTimeUs = -1ll; 439 440 return OK; 441 } 442 443 status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { 444 Mutex::Autolock autoLock(mLock); 445 446 ssize_t minIndex = fetchTrackSamples(); 447 448 if (minIndex < 0) { 449 return ERROR_END_OF_STREAM; 450 } 451 452 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 453 454 size_t sampleSize = info->mSample->range_length(); 455 456 if (info->mTrackFlags & kIsVorbis) { 457 // Each sample's data is suffixed by the number of page samples 458 // or -1 if not available. 459 sampleSize += sizeof(int32_t); 460 } 461 462 if (buffer->capacity() < sampleSize) { 463 return -ENOMEM; 464 } 465 466 const uint8_t *src = 467 (const uint8_t *)info->mSample->data() 468 + info->mSample->range_offset(); 469 470 memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length()); 471 472 if (info->mTrackFlags & kIsVorbis) { 473 int32_t numPageSamples; 474 if (!info->mSample->meta_data()->findInt32( 475 kKeyValidSamples, &numPageSamples)) { 476 numPageSamples = -1; 477 } 478 479 memcpy((uint8_t *)buffer->data() + info->mSample->range_length(), 480 &numPageSamples, 481 sizeof(numPageSamples)); 482 } 483 484 buffer->setRange(0, sampleSize); 485 486 return OK; 487 } 488 489 status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { 490 Mutex::Autolock autoLock(mLock); 491 492 ssize_t minIndex = fetchTrackSamples(); 493 494 if (minIndex < 0) { 495 return ERROR_END_OF_STREAM; 496 } 497 498 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 499 *trackIndex = info->mTrackIndex; 500 501 return OK; 502 } 503 504 status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { 505 Mutex::Autolock autoLock(mLock); 506 507 ssize_t minIndex = fetchTrackSamples(); 508 509 if (minIndex < 0) { 510 return ERROR_END_OF_STREAM; 511 } 512 513 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 514 *sampleTimeUs = info->mSampleTimeUs; 515 516 return OK; 517 } 518 519 status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) { 520 Mutex::Autolock autoLock(mLock); 521 522 *sampleMeta = NULL; 523 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 *sampleMeta = info->mSample->meta_data(); 532 533 return OK; 534 } 535 536 bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const { 537 if (mTotalBitrate >= 0) { 538 *bitrate = mTotalBitrate; 539 return true; 540 } 541 542 off64_t size; 543 if (mDurationUs >= 0 && mDataSource->getSize(&size) == OK) { 544 *bitrate = size * 8000000ll / mDurationUs; // in bits/sec 545 return true; 546 } 547 548 return false; 549 } 550 551 // Returns true iff cached duration is available/applicable. 552 bool NuMediaExtractor::getCachedDuration( 553 int64_t *durationUs, bool *eos) const { 554 Mutex::Autolock autoLock(mLock); 555 556 int64_t bitrate; 557 if (mIsWidevineExtractor) { 558 sp<WVMExtractor> wvmExtractor = 559 static_cast<WVMExtractor *>(mImpl.get()); 560 561 status_t finalStatus; 562 *durationUs = wvmExtractor->getCachedDurationUs(&finalStatus); 563 *eos = (finalStatus != OK); 564 return true; 565 } else if ((mDataSource->flags() & DataSource::kIsCachingDataSource) 566 && getTotalBitrate(&bitrate)) { 567 sp<NuCachedSource2> cachedSource = 568 static_cast<NuCachedSource2 *>(mDataSource.get()); 569 570 status_t finalStatus; 571 size_t cachedDataRemaining = 572 cachedSource->approxDataRemaining(&finalStatus); 573 574 *durationUs = cachedDataRemaining * 8000000ll / bitrate; 575 *eos = (finalStatus != OK); 576 return true; 577 } 578 579 return false; 580 } 581 582 } // namespace android 583