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 26#include <media/stagefright/foundation/ABuffer.h> 27#include <media/stagefright/foundation/ADebug.h> 28#include <media/stagefright/foundation/AMessage.h> 29#include <media/stagefright/DataSource.h> 30#include <media/stagefright/FileSource.h> 31#include <media/stagefright/MediaBuffer.h> 32#include <media/stagefright/MediaDefs.h> 33#include <media/stagefright/MediaErrors.h> 34#include <media/stagefright/MediaExtractor.h> 35#include <media/stagefright/MediaSource.h> 36#include <media/stagefright/MetaData.h> 37#include <media/stagefright/Utils.h> 38#include <android/media/ICas.h> 39 40namespace android { 41 42NuMediaExtractor::NuMediaExtractor() 43 : mTotalBitrate(-1ll), 44 mDurationUs(-1ll) { 45} 46 47NuMediaExtractor::~NuMediaExtractor() { 48 releaseTrackSamples(); 49 50 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 51 TrackInfo *info = &mSelectedTracks.editItemAt(i); 52 53 status_t err = info->mSource->stop(); 54 ALOGE_IF(err != OK, "error %d stopping track %zu", err, i); 55 } 56 57 mSelectedTracks.clear(); 58 if (mDataSource != NULL) { 59 mDataSource->close(); 60 } 61} 62 63status_t NuMediaExtractor::setDataSource( 64 const sp<IMediaHTTPService> &httpService, 65 const char *path, 66 const KeyedVector<String8, String8> *headers) { 67 Mutex::Autolock autoLock(mLock); 68 69 if (mImpl != NULL || path == NULL) { 70 return -EINVAL; 71 } 72 73 sp<DataSource> dataSource = 74 DataSource::CreateFromURI(httpService, path, headers); 75 76 if (dataSource == NULL) { 77 return -ENOENT; 78 } 79 80 mImpl = MediaExtractor::Create(dataSource); 81 82 if (mImpl == NULL) { 83 return ERROR_UNSUPPORTED; 84 } 85 86 if (mCas != NULL) { 87 mImpl->setMediaCas(mCas); 88 } 89 90 status_t err = updateDurationAndBitrate(); 91 if (err == OK) { 92 mDataSource = dataSource; 93 } 94 95 return OK; 96} 97 98status_t NuMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) { 99 100 ALOGV("setDataSource fd=%d (%s), offset=%lld, length=%lld", 101 fd, nameForFd(fd).c_str(), (long long) offset, (long long) size); 102 103 Mutex::Autolock autoLock(mLock); 104 105 if (mImpl != NULL) { 106 return -EINVAL; 107 } 108 109 sp<FileSource> fileSource = new FileSource(dup(fd), offset, size); 110 111 status_t err = fileSource->initCheck(); 112 if (err != OK) { 113 return err; 114 } 115 116 mImpl = MediaExtractor::Create(fileSource); 117 118 if (mImpl == NULL) { 119 return ERROR_UNSUPPORTED; 120 } 121 122 if (mCas != NULL) { 123 mImpl->setMediaCas(mCas); 124 } 125 126 err = updateDurationAndBitrate(); 127 if (err == OK) { 128 mDataSource = fileSource; 129 } 130 131 return OK; 132} 133 134status_t NuMediaExtractor::setDataSource(const sp<DataSource> &source) { 135 Mutex::Autolock autoLock(mLock); 136 137 if (mImpl != NULL) { 138 return -EINVAL; 139 } 140 141 status_t err = source->initCheck(); 142 if (err != OK) { 143 return err; 144 } 145 146 mImpl = MediaExtractor::Create(source); 147 148 if (mImpl == NULL) { 149 return ERROR_UNSUPPORTED; 150 } 151 152 if (mCas != NULL) { 153 mImpl->setMediaCas(mCas); 154 } 155 156 err = updateDurationAndBitrate(); 157 if (err == OK) { 158 mDataSource = source; 159 } 160 161 return err; 162} 163 164status_t NuMediaExtractor::setMediaCas(const sp<ICas> &cas) { 165 ALOGV("setMediaCas: cas=%p", cas.get()); 166 167 Mutex::Autolock autoLock(mLock); 168 169 if (cas == NULL) { 170 return BAD_VALUE; 171 } 172 173 if (mImpl != NULL) { 174 mImpl->setMediaCas(cas); 175 status_t err = updateDurationAndBitrate(); 176 if (err != OK) { 177 return err; 178 } 179 } 180 181 mCas = cas; 182 return OK; 183} 184 185status_t NuMediaExtractor::updateDurationAndBitrate() { 186 if (mImpl->countTracks() > kMaxTrackCount) { 187 return ERROR_UNSUPPORTED; 188 } 189 190 mTotalBitrate = 0ll; 191 mDurationUs = -1ll; 192 193 for (size_t i = 0; i < mImpl->countTracks(); ++i) { 194 sp<MetaData> meta = mImpl->getTrackMetaData(i); 195 if (meta == NULL) { 196 ALOGW("no metadata for track %zu", i); 197 continue; 198 } 199 200 int32_t bitrate; 201 if (!meta->findInt32(kKeyBitRate, &bitrate)) { 202 const char *mime; 203 CHECK(meta->findCString(kKeyMIMEType, &mime)); 204 ALOGV("track of type '%s' does not publish bitrate", mime); 205 206 mTotalBitrate = -1ll; 207 } else if (mTotalBitrate >= 0ll) { 208 mTotalBitrate += bitrate; 209 } 210 211 int64_t durationUs; 212 if (meta->findInt64(kKeyDuration, &durationUs) 213 && durationUs > mDurationUs) { 214 mDurationUs = durationUs; 215 } 216 } 217 return OK; 218} 219 220size_t NuMediaExtractor::countTracks() const { 221 Mutex::Autolock autoLock(mLock); 222 223 return mImpl == NULL ? 0 : mImpl->countTracks(); 224} 225 226status_t NuMediaExtractor::getTrackFormat( 227 size_t index, sp<AMessage> *format, uint32_t flags) const { 228 Mutex::Autolock autoLock(mLock); 229 230 *format = NULL; 231 232 if (mImpl == NULL) { 233 return -EINVAL; 234 } 235 236 if (index >= mImpl->countTracks()) { 237 return -ERANGE; 238 } 239 240 sp<MetaData> meta = mImpl->getTrackMetaData(index, flags); 241 // Extractors either support trackID-s or not, so either all tracks have trackIDs or none. 242 // Generate trackID if missing. 243 int32_t trackID; 244 if (meta != NULL && !meta->findInt32(kKeyTrackID, &trackID)) { 245 meta->setInt32(kKeyTrackID, (int32_t)index + 1); 246 } 247 return convertMetaDataToMessage(meta, format); 248} 249 250status_t NuMediaExtractor::getFileFormat(sp<AMessage> *format) const { 251 Mutex::Autolock autoLock(mLock); 252 253 *format = NULL; 254 255 if (mImpl == NULL) { 256 return -EINVAL; 257 } 258 259 sp<MetaData> meta = mImpl->getMetaData(); 260 261 const char *mime; 262 CHECK(meta->findCString(kKeyMIMEType, &mime)); 263 *format = new AMessage(); 264 (*format)->setString("mime", mime); 265 266 uint32_t type; 267 const void *pssh; 268 size_t psshsize; 269 if (meta->findData(kKeyPssh, &type, &pssh, &psshsize)) { 270 sp<ABuffer> buf = new ABuffer(psshsize); 271 memcpy(buf->data(), pssh, psshsize); 272 (*format)->setBuffer("pssh", buf); 273 } 274 275 return OK; 276} 277 278status_t NuMediaExtractor::selectTrack(size_t index) { 279 Mutex::Autolock autoLock(mLock); 280 281 if (mImpl == NULL) { 282 return -EINVAL; 283 } 284 285 if (index >= mImpl->countTracks()) { 286 return -ERANGE; 287 } 288 289 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 290 TrackInfo *info = &mSelectedTracks.editItemAt(i); 291 292 if (info->mTrackIndex == index) { 293 // This track has already been selected. 294 return OK; 295 } 296 } 297 298 sp<IMediaSource> source = mImpl->getTrack(index); 299 300 if (source == nullptr) { 301 return ERROR_MALFORMED; 302 } 303 304 status_t ret = source->start(); 305 if (ret != OK) { 306 return ret; 307 } 308 309 mSelectedTracks.push(); 310 TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1); 311 312 info->mSource = source; 313 info->mTrackIndex = index; 314 info->mFinalResult = OK; 315 info->mSample = NULL; 316 info->mSampleTimeUs = -1ll; 317 info->mTrackFlags = 0; 318 319 const char *mime; 320 CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime)); 321 322 if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { 323 info->mTrackFlags |= kIsVorbis; 324 } 325 326 return OK; 327} 328 329status_t NuMediaExtractor::unselectTrack(size_t index) { 330 Mutex::Autolock autoLock(mLock); 331 332 if (mImpl == NULL) { 333 return -EINVAL; 334 } 335 336 if (index >= mImpl->countTracks()) { 337 return -ERANGE; 338 } 339 340 size_t i; 341 for (i = 0; i < mSelectedTracks.size(); ++i) { 342 TrackInfo *info = &mSelectedTracks.editItemAt(i); 343 344 if (info->mTrackIndex == index) { 345 break; 346 } 347 } 348 349 if (i == mSelectedTracks.size()) { 350 // Not selected. 351 return OK; 352 } 353 354 TrackInfo *info = &mSelectedTracks.editItemAt(i); 355 356 if (info->mSample != NULL) { 357 info->mSample->release(); 358 info->mSample = NULL; 359 360 info->mSampleTimeUs = -1ll; 361 } 362 363 CHECK_EQ((status_t)OK, info->mSource->stop()); 364 365 mSelectedTracks.removeAt(i); 366 367 return OK; 368} 369 370void NuMediaExtractor::releaseTrackSamples() { 371 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 372 TrackInfo *info = &mSelectedTracks.editItemAt(i); 373 374 if (info->mSample != NULL) { 375 info->mSample->release(); 376 info->mSample = NULL; 377 378 info->mSampleTimeUs = -1ll; 379 } 380 } 381} 382 383ssize_t NuMediaExtractor::fetchTrackSamples( 384 int64_t seekTimeUs, MediaSource::ReadOptions::SeekMode mode) { 385 TrackInfo *minInfo = NULL; 386 ssize_t minIndex = -1; 387 388 for (size_t i = 0; i < mSelectedTracks.size(); ++i) { 389 TrackInfo *info = &mSelectedTracks.editItemAt(i); 390 391 if (seekTimeUs >= 0ll) { 392 info->mFinalResult = OK; 393 394 if (info->mSample != NULL) { 395 info->mSample->release(); 396 info->mSample = NULL; 397 info->mSampleTimeUs = -1ll; 398 } 399 } else if (info->mFinalResult != OK) { 400 continue; 401 } 402 403 if (info->mSample == NULL) { 404 MediaSource::ReadOptions options; 405 if (seekTimeUs >= 0ll) { 406 options.setSeekTo(seekTimeUs, mode); 407 } 408 status_t err = info->mSource->read(&info->mSample, &options); 409 410 if (err != OK) { 411 CHECK(info->mSample == NULL); 412 413 info->mFinalResult = err; 414 415 if (info->mFinalResult != ERROR_END_OF_STREAM) { 416 ALOGW("read on track %zu failed with error %d", 417 info->mTrackIndex, err); 418 } 419 420 info->mSampleTimeUs = -1ll; 421 continue; 422 } else { 423 CHECK(info->mSample != NULL); 424 CHECK(info->mSample->meta_data()->findInt64( 425 kKeyTime, &info->mSampleTimeUs)); 426 } 427 } 428 429 if (minInfo == NULL || info->mSampleTimeUs < minInfo->mSampleTimeUs) { 430 minInfo = info; 431 minIndex = i; 432 } 433 } 434 435 return minIndex; 436} 437 438status_t NuMediaExtractor::seekTo( 439 int64_t timeUs, MediaSource::ReadOptions::SeekMode mode) { 440 Mutex::Autolock autoLock(mLock); 441 442 ssize_t minIndex = fetchTrackSamples(timeUs, mode); 443 444 if (minIndex < 0) { 445 return ERROR_END_OF_STREAM; 446 } 447 448 return OK; 449} 450 451status_t NuMediaExtractor::advance() { 452 Mutex::Autolock autoLock(mLock); 453 454 ssize_t minIndex = fetchTrackSamples(); 455 456 if (minIndex < 0) { 457 return ERROR_END_OF_STREAM; 458 } 459 460 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 461 462 info->mSample->release(); 463 info->mSample = NULL; 464 info->mSampleTimeUs = -1ll; 465 466 return OK; 467} 468 469status_t NuMediaExtractor::appendVorbisNumPageSamples(TrackInfo *info, const sp<ABuffer> &buffer) { 470 int32_t numPageSamples; 471 if (!info->mSample->meta_data()->findInt32( 472 kKeyValidSamples, &numPageSamples)) { 473 numPageSamples = -1; 474 } 475 476 memcpy((uint8_t *)buffer->data() + info->mSample->range_length(), 477 &numPageSamples, 478 sizeof(numPageSamples)); 479 480 uint32_t type; 481 const void *data; 482 size_t size, size2; 483 if (info->mSample->meta_data()->findData(kKeyEncryptedSizes, &type, &data, &size)) { 484 // Signal numPageSamples (a plain int32_t) is appended at the end, 485 // i.e. sizeof(numPageSamples) plain bytes + 0 encrypted bytes 486 if (SIZE_MAX - size < sizeof(int32_t)) { 487 return -ENOMEM; 488 } 489 490 size_t newSize = size + sizeof(int32_t); 491 sp<ABuffer> abuf = new ABuffer(newSize); 492 uint8_t *adata = static_cast<uint8_t *>(abuf->data()); 493 if (adata == NULL) { 494 return -ENOMEM; 495 } 496 497 // append 0 to encrypted sizes 498 int32_t zero = 0; 499 memcpy(adata, data, size); 500 memcpy(adata + size, &zero, sizeof(zero)); 501 info->mSample->meta_data()->setData(kKeyEncryptedSizes, type, adata, newSize); 502 503 if (info->mSample->meta_data()->findData(kKeyPlainSizes, &type, &data, &size2)) { 504 if (size2 != size) { 505 return ERROR_MALFORMED; 506 } 507 memcpy(adata, data, size); 508 } else { 509 // if sample meta data does not include plain size array, assume filled with zeros, 510 // i.e. entire buffer is encrypted 511 memset(adata, 0, size); 512 } 513 // append sizeof(numPageSamples) to plain sizes. 514 int32_t int32Size = sizeof(numPageSamples); 515 memcpy(adata + size, &int32Size, sizeof(int32Size)); 516 info->mSample->meta_data()->setData(kKeyPlainSizes, type, adata, newSize); 517 } 518 519 return OK; 520} 521 522status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { 523 Mutex::Autolock autoLock(mLock); 524 525 ssize_t minIndex = fetchTrackSamples(); 526 527 if (minIndex < 0) { 528 return ERROR_END_OF_STREAM; 529 } 530 531 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 532 533 size_t sampleSize = info->mSample->range_length(); 534 535 if (info->mTrackFlags & kIsVorbis) { 536 // Each sample's data is suffixed by the number of page samples 537 // or -1 if not available. 538 sampleSize += sizeof(int32_t); 539 } 540 541 if (buffer->capacity() < sampleSize) { 542 return -ENOMEM; 543 } 544 545 const uint8_t *src = 546 (const uint8_t *)info->mSample->data() 547 + info->mSample->range_offset(); 548 549 memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length()); 550 551 status_t err = OK; 552 if (info->mTrackFlags & kIsVorbis) { 553 err = appendVorbisNumPageSamples(info, buffer); 554 } 555 556 if (err == OK) { 557 buffer->setRange(0, sampleSize); 558 } 559 560 return err; 561} 562 563status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { 564 Mutex::Autolock autoLock(mLock); 565 566 ssize_t minIndex = fetchTrackSamples(); 567 568 if (minIndex < 0) { 569 return ERROR_END_OF_STREAM; 570 } 571 572 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 573 *trackIndex = info->mTrackIndex; 574 575 return OK; 576} 577 578status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { 579 Mutex::Autolock autoLock(mLock); 580 581 ssize_t minIndex = fetchTrackSamples(); 582 583 if (minIndex < 0) { 584 return ERROR_END_OF_STREAM; 585 } 586 587 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 588 *sampleTimeUs = info->mSampleTimeUs; 589 590 return OK; 591} 592 593status_t NuMediaExtractor::getSampleMeta(sp<MetaData> *sampleMeta) { 594 Mutex::Autolock autoLock(mLock); 595 596 *sampleMeta = NULL; 597 598 ssize_t minIndex = fetchTrackSamples(); 599 600 if (minIndex < 0) { 601 return ERROR_END_OF_STREAM; 602 } 603 604 TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); 605 *sampleMeta = info->mSample->meta_data(); 606 607 return OK; 608} 609 610status_t NuMediaExtractor::getMetrics(Parcel *reply) { 611 status_t status = mImpl->getMetrics(reply); 612 return status; 613} 614 615bool NuMediaExtractor::getTotalBitrate(int64_t *bitrate) const { 616 if (mTotalBitrate >= 0) { 617 *bitrate = mTotalBitrate; 618 return true; 619 } 620 621 off64_t size; 622 if (mDurationUs > 0 && mDataSource->getSize(&size) == OK) { 623 *bitrate = size * 8000000ll / mDurationUs; // in bits/sec 624 return true; 625 } 626 627 return false; 628} 629 630// Returns true iff cached duration is available/applicable. 631bool NuMediaExtractor::getCachedDuration( 632 int64_t *durationUs, bool *eos) const { 633 Mutex::Autolock autoLock(mLock); 634 635 int64_t bitrate; 636 if ((mDataSource->flags() & DataSource::kIsCachingDataSource) 637 && getTotalBitrate(&bitrate)) { 638 sp<NuCachedSource2> cachedSource = 639 static_cast<NuCachedSource2 *>(mDataSource.get()); 640 641 status_t finalStatus; 642 size_t cachedDataRemaining = 643 cachedSource->approxDataRemaining(&finalStatus); 644 645 *durationUs = cachedDataRemaining * 8000000ll / bitrate; 646 *eos = (finalStatus != OK); 647 return true; 648 } 649 650 return false; 651} 652 653} // namespace android 654