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