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