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