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