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