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