LiveSession.cpp revision 8dae4ca229d21f68f3ee76c76b49b6f0a47aad53
1/* 2 * Copyright (C) 2010 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 "LiveSession" 19#include <utils/Log.h> 20 21#include "include/LiveSession.h" 22 23#include "LiveDataSource.h" 24 25#include "include/M3UParser.h" 26#include "include/HTTPBase.h" 27 28#include <cutils/properties.h> 29#include <media/stagefright/foundation/hexdump.h> 30#include <media/stagefright/foundation/ABuffer.h> 31#include <media/stagefright/foundation/ADebug.h> 32#include <media/stagefright/foundation/AMessage.h> 33#include <media/stagefright/DataSource.h> 34#include <media/stagefright/FileSource.h> 35#include <media/stagefright/MediaErrors.h> 36 37#include <ctype.h> 38#include <openssl/aes.h> 39#include <openssl/md5.h> 40 41namespace android { 42 43LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid) 44 : mFlags(flags), 45 mUIDValid(uidValid), 46 mUID(uid), 47 mDataSource(new LiveDataSource), 48 mHTTPDataSource( 49 HTTPBase::Create( 50 (mFlags & kFlagIncognito) 51 ? HTTPBase::kFlagIncognito 52 : 0)), 53 mPrevBandwidthIndex(-1), 54 mLastPlaylistFetchTimeUs(-1), 55 mSeqNumber(-1), 56 mSeekTimeUs(-1), 57 mNumRetries(0), 58 mDurationUs(-1), 59 mSeekDone(false), 60 mDisconnectPending(false), 61 mMonitorQueueGeneration(0), 62 mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY) { 63 if (mUIDValid) { 64 mHTTPDataSource->setUID(mUID); 65 } 66} 67 68LiveSession::~LiveSession() { 69} 70 71sp<DataSource> LiveSession::getDataSource() { 72 return mDataSource; 73} 74 75void LiveSession::connect( 76 const char *url, const KeyedVector<String8, String8> *headers) { 77 sp<AMessage> msg = new AMessage(kWhatConnect, id()); 78 msg->setString("url", url); 79 80 if (headers != NULL) { 81 msg->setPointer( 82 "headers", 83 new KeyedVector<String8, String8>(*headers)); 84 } 85 86 msg->post(); 87} 88 89void LiveSession::disconnect() { 90 Mutex::Autolock autoLock(mLock); 91 mDisconnectPending = true; 92 93 mHTTPDataSource->disconnect(); 94 95 (new AMessage(kWhatDisconnect, id()))->post(); 96} 97 98void LiveSession::seekTo(int64_t timeUs) { 99 Mutex::Autolock autoLock(mLock); 100 mSeekDone = false; 101 102 sp<AMessage> msg = new AMessage(kWhatSeek, id()); 103 msg->setInt64("timeUs", timeUs); 104 msg->post(); 105 106 while (!mSeekDone) { 107 mCondition.wait(mLock); 108 } 109} 110 111void LiveSession::onMessageReceived(const sp<AMessage> &msg) { 112 switch (msg->what()) { 113 case kWhatConnect: 114 onConnect(msg); 115 break; 116 117 case kWhatDisconnect: 118 onDisconnect(); 119 break; 120 121 case kWhatMonitorQueue: 122 { 123 int32_t generation; 124 CHECK(msg->findInt32("generation", &generation)); 125 126 if (generation != mMonitorQueueGeneration) { 127 // Stale event 128 break; 129 } 130 131 onMonitorQueue(); 132 break; 133 } 134 135 case kWhatSeek: 136 onSeek(msg); 137 break; 138 139 default: 140 TRESPASS(); 141 break; 142 } 143} 144 145// static 146int LiveSession::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) { 147 if (a->mBandwidth < b->mBandwidth) { 148 return -1; 149 } else if (a->mBandwidth == b->mBandwidth) { 150 return 0; 151 } 152 153 return 1; 154} 155 156void LiveSession::onConnect(const sp<AMessage> &msg) { 157 AString url; 158 CHECK(msg->findString("url", &url)); 159 160 KeyedVector<String8, String8> *headers = NULL; 161 if (!msg->findPointer("headers", (void **)&headers)) { 162 mExtraHeaders.clear(); 163 } else { 164 mExtraHeaders = *headers; 165 166 delete headers; 167 headers = NULL; 168 } 169 170 if (!(mFlags & kFlagIncognito)) { 171 LOGI("onConnect '%s'", url.c_str()); 172 } else { 173 LOGI("onConnect <URL suppressed>"); 174 } 175 176 mMasterURL = url; 177 178 bool dummy; 179 sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &dummy); 180 181 if (playlist == NULL) { 182 LOGE("unable to fetch master playlist '%s'.", url.c_str()); 183 184 mDataSource->queueEOS(ERROR_IO); 185 return; 186 } 187 188 if (playlist->isVariantPlaylist()) { 189 for (size_t i = 0; i < playlist->size(); ++i) { 190 BandwidthItem item; 191 192 sp<AMessage> meta; 193 playlist->itemAt(i, &item.mURI, &meta); 194 195 unsigned long bandwidth; 196 CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth)); 197 198 mBandwidthItems.push(item); 199 } 200 201 CHECK_GT(mBandwidthItems.size(), 0u); 202 203 mBandwidthItems.sort(SortByBandwidth); 204 } 205 206 postMonitorQueue(); 207} 208 209void LiveSession::onDisconnect() { 210 LOGI("onDisconnect"); 211 212 mDataSource->queueEOS(ERROR_END_OF_STREAM); 213 214 Mutex::Autolock autoLock(mLock); 215 mDisconnectPending = false; 216} 217 218status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) { 219 *out = NULL; 220 221 sp<DataSource> source; 222 223 if (!strncasecmp(url, "file://", 7)) { 224 source = new FileSource(url + 7); 225 } else if (strncasecmp(url, "http://", 7) 226 && strncasecmp(url, "https://", 8)) { 227 return ERROR_UNSUPPORTED; 228 } else { 229 { 230 Mutex::Autolock autoLock(mLock); 231 232 if (mDisconnectPending) { 233 return ERROR_IO; 234 } 235 } 236 237 status_t err = mHTTPDataSource->connect( 238 url, mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); 239 240 if (err != OK) { 241 return err; 242 } 243 244 source = mHTTPDataSource; 245 } 246 247 off64_t size; 248 status_t err = source->getSize(&size); 249 250 if (err != OK) { 251 size = 65536; 252 } 253 254 sp<ABuffer> buffer = new ABuffer(size); 255 buffer->setRange(0, 0); 256 257 for (;;) { 258 size_t bufferRemaining = buffer->capacity() - buffer->size(); 259 260 if (bufferRemaining == 0) { 261 bufferRemaining = 32768; 262 263 LOGV("increasing download buffer to %d bytes", 264 buffer->size() + bufferRemaining); 265 266 sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining); 267 memcpy(copy->data(), buffer->data(), buffer->size()); 268 copy->setRange(0, buffer->size()); 269 270 buffer = copy; 271 } 272 273 ssize_t n = source->readAt( 274 buffer->size(), buffer->data() + buffer->size(), 275 bufferRemaining); 276 277 if (n < 0) { 278 return n; 279 } 280 281 if (n == 0) { 282 break; 283 } 284 285 buffer->setRange(0, buffer->size() + (size_t)n); 286 } 287 288 *out = buffer; 289 290 return OK; 291} 292 293sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) { 294 *unchanged = false; 295 296 sp<ABuffer> buffer; 297 status_t err = fetchFile(url, &buffer); 298 299 if (err != OK) { 300 return NULL; 301 } 302 303 // MD5 functionality is not available on the simulator, treat all 304 // playlists as changed. 305 306#if defined(HAVE_ANDROID_OS) 307 uint8_t hash[16]; 308 309 MD5_CTX m; 310 MD5_Init(&m); 311 MD5_Update(&m, buffer->data(), buffer->size()); 312 313 MD5_Final(hash, &m); 314 315 if (mPlaylist != NULL && !memcmp(hash, mPlaylistHash, 16)) { 316 // playlist unchanged 317 318 if (mRefreshState != THIRD_UNCHANGED_RELOAD_ATTEMPT) { 319 mRefreshState = (RefreshState)(mRefreshState + 1); 320 } 321 322 *unchanged = true; 323 324 LOGV("Playlist unchanged, refresh state is now %d", 325 (int)mRefreshState); 326 327 return NULL; 328 } 329 330 memcpy(mPlaylistHash, hash, sizeof(hash)); 331 332 mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY; 333#endif 334 335 sp<M3UParser> playlist = 336 new M3UParser(url, buffer->data(), buffer->size()); 337 338 if (playlist->initCheck() != OK) { 339 LOGE("failed to parse .m3u8 playlist"); 340 341 return NULL; 342 } 343 344 return playlist; 345} 346 347static double uniformRand() { 348 return (double)rand() / RAND_MAX; 349} 350 351size_t LiveSession::getBandwidthIndex() { 352 if (mBandwidthItems.size() == 0) { 353 return 0; 354 } 355 356#if 1 357 int32_t bandwidthBps; 358 if (mHTTPDataSource != NULL 359 && mHTTPDataSource->estimateBandwidth(&bandwidthBps)) { 360 LOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); 361 } else { 362 LOGV("no bandwidth estimate."); 363 return 0; // Pick the lowest bandwidth stream by default. 364 } 365 366 char value[PROPERTY_VALUE_MAX]; 367 if (property_get("media.httplive.max-bw", value, NULL)) { 368 char *end; 369 long maxBw = strtoul(value, &end, 10); 370 if (end > value && *end == '\0') { 371 if (maxBw > 0 && bandwidthBps > maxBw) { 372 LOGV("bandwidth capped to %ld bps", maxBw); 373 bandwidthBps = maxBw; 374 } 375 } 376 } 377 378 // Consider only 80% of the available bandwidth usable. 379 bandwidthBps = (bandwidthBps * 8) / 10; 380 381 // Pick the highest bandwidth stream below or equal to estimated bandwidth. 382 383 size_t index = mBandwidthItems.size() - 1; 384 while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth 385 > (size_t)bandwidthBps) { 386 --index; 387 } 388#elif 0 389 // Change bandwidth at random() 390 size_t index = uniformRand() * mBandwidthItems.size(); 391#elif 0 392 // There's a 50% chance to stay on the current bandwidth and 393 // a 50% chance to switch to the next higher bandwidth (wrapping around 394 // to lowest) 395 const size_t kMinIndex = 0; 396 397 size_t index; 398 if (mPrevBandwidthIndex < 0) { 399 index = kMinIndex; 400 } else if (uniformRand() < 0.5) { 401 index = (size_t)mPrevBandwidthIndex; 402 } else { 403 index = mPrevBandwidthIndex + 1; 404 if (index == mBandwidthItems.size()) { 405 index = kMinIndex; 406 } 407 } 408#elif 0 409 // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec 410 411 size_t index = mBandwidthItems.size() - 1; 412 while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth > 1200000) { 413 --index; 414 } 415#else 416 size_t index = mBandwidthItems.size() - 1; // Highest bandwidth stream 417#endif 418 419 return index; 420} 421 422bool LiveSession::timeToRefreshPlaylist(int64_t nowUs) const { 423 if (mPlaylist == NULL) { 424 CHECK_EQ((int)mRefreshState, (int)INITIAL_MINIMUM_RELOAD_DELAY); 425 return true; 426 } 427 428 int32_t targetDurationSecs; 429 CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); 430 431 int64_t targetDurationUs = targetDurationSecs * 1000000ll; 432 433 int64_t minPlaylistAgeUs; 434 435 switch (mRefreshState) { 436 case INITIAL_MINIMUM_RELOAD_DELAY: 437 { 438 size_t n = mPlaylist->size(); 439 if (n > 0) { 440 sp<AMessage> itemMeta; 441 CHECK(mPlaylist->itemAt(n - 1, NULL /* uri */, &itemMeta)); 442 443 int64_t itemDurationUs; 444 CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); 445 446 minPlaylistAgeUs = itemDurationUs; 447 break; 448 } 449 450 // fall through 451 } 452 453 case FIRST_UNCHANGED_RELOAD_ATTEMPT: 454 { 455 minPlaylistAgeUs = targetDurationUs / 2; 456 break; 457 } 458 459 case SECOND_UNCHANGED_RELOAD_ATTEMPT: 460 { 461 minPlaylistAgeUs = (targetDurationUs * 3) / 2; 462 break; 463 } 464 465 case THIRD_UNCHANGED_RELOAD_ATTEMPT: 466 { 467 minPlaylistAgeUs = targetDurationUs * 3; 468 break; 469 } 470 471 default: 472 TRESPASS(); 473 break; 474 } 475 476 return mLastPlaylistFetchTimeUs + minPlaylistAgeUs <= nowUs; 477} 478 479void LiveSession::onDownloadNext() { 480 size_t bandwidthIndex = getBandwidthIndex(); 481 482rinse_repeat: 483 int64_t nowUs = ALooper::GetNowUs(); 484 485 if (mLastPlaylistFetchTimeUs < 0 486 || (ssize_t)bandwidthIndex != mPrevBandwidthIndex 487 || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) { 488 AString url; 489 if (mBandwidthItems.size() > 0) { 490 url = mBandwidthItems.editItemAt(bandwidthIndex).mURI; 491 } else { 492 url = mMasterURL; 493 } 494 495 bool firstTime = (mPlaylist == NULL); 496 497 if ((ssize_t)bandwidthIndex != mPrevBandwidthIndex) { 498 // If we switch bandwidths, do not pay any heed to whether 499 // playlists changed since the last time... 500 mPlaylist.clear(); 501 } 502 503 bool unchanged; 504 sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &unchanged); 505 if (playlist == NULL) { 506 if (unchanged) { 507 // We succeeded in fetching the playlist, but it was 508 // unchanged from the last time we tried. 509 } else { 510 LOGE("failed to load playlist at url '%s'", url.c_str()); 511 mDataSource->queueEOS(ERROR_IO); 512 return; 513 } 514 } else { 515 mPlaylist = playlist; 516 } 517 518 if (firstTime) { 519 Mutex::Autolock autoLock(mLock); 520 521 if (!mPlaylist->isComplete()) { 522 mDurationUs = -1; 523 } else { 524 mDurationUs = 0; 525 for (size_t i = 0; i < mPlaylist->size(); ++i) { 526 sp<AMessage> itemMeta; 527 CHECK(mPlaylist->itemAt( 528 i, NULL /* uri */, &itemMeta)); 529 530 int64_t itemDurationUs; 531 CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); 532 533 mDurationUs += itemDurationUs; 534 } 535 } 536 } 537 538 mLastPlaylistFetchTimeUs = ALooper::GetNowUs(); 539 } 540 541 int32_t firstSeqNumberInPlaylist; 542 if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( 543 "media-sequence", &firstSeqNumberInPlaylist)) { 544 firstSeqNumberInPlaylist = 0; 545 } 546 547 bool explicitDiscontinuity = false; 548 bool bandwidthChanged = false; 549 550 if (mSeekTimeUs >= 0) { 551 if (mPlaylist->isComplete()) { 552 size_t index = 0; 553 int64_t segmentStartUs = 0; 554 while (index < mPlaylist->size()) { 555 sp<AMessage> itemMeta; 556 CHECK(mPlaylist->itemAt( 557 index, NULL /* uri */, &itemMeta)); 558 559 int64_t itemDurationUs; 560 CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); 561 562 if (mSeekTimeUs < segmentStartUs + itemDurationUs) { 563 break; 564 } 565 566 segmentStartUs += itemDurationUs; 567 ++index; 568 } 569 570 if (index < mPlaylist->size()) { 571 int32_t newSeqNumber = firstSeqNumberInPlaylist + index; 572 573 if (newSeqNumber != mSeqNumber) { 574 LOGI("seeking to seq no %d", newSeqNumber); 575 576 mSeqNumber = newSeqNumber; 577 578 mDataSource->reset(); 579 580 // reseting the data source will have had the 581 // side effect of discarding any previously queued 582 // bandwidth change discontinuity. 583 // Therefore we'll need to treat these explicit 584 // discontinuities as involving a bandwidth change 585 // even if they aren't directly. 586 explicitDiscontinuity = true; 587 bandwidthChanged = true; 588 } 589 } 590 } 591 592 mSeekTimeUs = -1; 593 594 Mutex::Autolock autoLock(mLock); 595 mSeekDone = true; 596 mCondition.broadcast(); 597 } 598 599 if (mSeqNumber < 0) { 600 if (mPlaylist->isComplete()) { 601 mSeqNumber = firstSeqNumberInPlaylist; 602 } else { 603 mSeqNumber = firstSeqNumberInPlaylist + mPlaylist->size() / 2; 604 } 605 } 606 607 int32_t lastSeqNumberInPlaylist = 608 firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; 609 610 if (mSeqNumber < firstSeqNumberInPlaylist 611 || mSeqNumber > lastSeqNumberInPlaylist) { 612 if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) { 613 // Go back to the previous bandwidth. 614 615 LOGI("new bandwidth does not have the sequence number " 616 "we're looking for, switching back to previous bandwidth"); 617 618 mLastPlaylistFetchTimeUs = -1; 619 bandwidthIndex = mPrevBandwidthIndex; 620 goto rinse_repeat; 621 } 622 623 if (!mPlaylist->isComplete() 624 && mSeqNumber > lastSeqNumberInPlaylist 625 && mNumRetries < kMaxNumRetries) { 626 ++mNumRetries; 627 628 mLastPlaylistFetchTimeUs = -1; 629 postMonitorQueue(3000000ll); 630 return; 631 } 632 633 LOGE("Cannot find sequence number %d in playlist " 634 "(contains %d - %d)", 635 mSeqNumber, firstSeqNumberInPlaylist, 636 firstSeqNumberInPlaylist + mPlaylist->size() - 1); 637 638 mDataSource->queueEOS(ERROR_END_OF_STREAM); 639 return; 640 } 641 642 mNumRetries = 0; 643 644 AString uri; 645 sp<AMessage> itemMeta; 646 CHECK(mPlaylist->itemAt( 647 mSeqNumber - firstSeqNumberInPlaylist, 648 &uri, 649 &itemMeta)); 650 651 int32_t val; 652 if (itemMeta->findInt32("discontinuity", &val) && val != 0) { 653 explicitDiscontinuity = true; 654 } 655 656 sp<ABuffer> buffer; 657 status_t err = fetchFile(uri.c_str(), &buffer); 658 if (err != OK) { 659 LOGE("failed to fetch .ts segment at url '%s'", uri.c_str()); 660 mDataSource->queueEOS(err); 661 return; 662 } 663 664 CHECK(buffer != NULL); 665 666 err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer); 667 668 if (err != OK) { 669 LOGE("decryptBuffer failed w/ error %d", err); 670 671 mDataSource->queueEOS(err); 672 return; 673 } 674 675 if (buffer->size() == 0 || buffer->data()[0] != 0x47) { 676 // Not a transport stream??? 677 678 LOGE("This doesn't look like a transport stream..."); 679 680 mBandwidthItems.removeAt(bandwidthIndex); 681 682 if (mBandwidthItems.isEmpty()) { 683 mDataSource->queueEOS(ERROR_UNSUPPORTED); 684 return; 685 } 686 687 LOGI("Retrying with a different bandwidth stream."); 688 689 mLastPlaylistFetchTimeUs = -1; 690 bandwidthIndex = getBandwidthIndex(); 691 mPrevBandwidthIndex = bandwidthIndex; 692 mSeqNumber = -1; 693 694 goto rinse_repeat; 695 } 696 697 if ((size_t)mPrevBandwidthIndex != bandwidthIndex) { 698 bandwidthChanged = true; 699 } 700 701 if (mPrevBandwidthIndex < 0) { 702 // Don't signal a bandwidth change at the very beginning of 703 // playback. 704 bandwidthChanged = false; 705 } 706 707 if (explicitDiscontinuity || bandwidthChanged) { 708 // Signal discontinuity. 709 710 LOGI("queueing discontinuity (explicit=%d, bandwidthChanged=%d)", 711 explicitDiscontinuity, bandwidthChanged); 712 713 sp<ABuffer> tmp = new ABuffer(188); 714 memset(tmp->data(), 0, tmp->size()); 715 tmp->data()[1] = bandwidthChanged; 716 717 mDataSource->queueBuffer(tmp); 718 } 719 720 mDataSource->queueBuffer(buffer); 721 722 mPrevBandwidthIndex = bandwidthIndex; 723 ++mSeqNumber; 724 725 postMonitorQueue(); 726} 727 728void LiveSession::onMonitorQueue() { 729 if (mSeekTimeUs >= 0 730 || mDataSource->countQueuedBuffers() < kMaxNumQueuedFragments) { 731 onDownloadNext(); 732 } else { 733 postMonitorQueue(1000000ll); 734 } 735} 736 737status_t LiveSession::decryptBuffer( 738 size_t playlistIndex, const sp<ABuffer> &buffer) { 739 sp<AMessage> itemMeta; 740 bool found = false; 741 AString method; 742 743 for (ssize_t i = playlistIndex; i >= 0; --i) { 744 AString uri; 745 CHECK(mPlaylist->itemAt(i, &uri, &itemMeta)); 746 747 if (itemMeta->findString("cipher-method", &method)) { 748 found = true; 749 break; 750 } 751 } 752 753 if (!found) { 754 method = "NONE"; 755 } 756 757 if (method == "NONE") { 758 return OK; 759 } else if (!(method == "AES-128")) { 760 LOGE("Unsupported cipher method '%s'", method.c_str()); 761 return ERROR_UNSUPPORTED; 762 } 763 764 AString keyURI; 765 if (!itemMeta->findString("cipher-uri", &keyURI)) { 766 LOGE("Missing key uri"); 767 return ERROR_MALFORMED; 768 } 769 770 ssize_t index = mAESKeyForURI.indexOfKey(keyURI); 771 772 sp<ABuffer> key; 773 if (index >= 0) { 774 key = mAESKeyForURI.valueAt(index); 775 } else { 776 key = new ABuffer(16); 777 778 sp<HTTPBase> keySource = 779 HTTPBase::Create( 780 (mFlags & kFlagIncognito) 781 ? HTTPBase::kFlagIncognito 782 : 0); 783 784 if (mUIDValid) { 785 keySource->setUID(mUID); 786 } 787 788 status_t err = keySource->connect(keyURI.c_str()); 789 790 if (err == OK) { 791 size_t offset = 0; 792 while (offset < 16) { 793 ssize_t n = keySource->readAt( 794 offset, key->data() + offset, 16 - offset); 795 if (n <= 0) { 796 err = ERROR_IO; 797 break; 798 } 799 800 offset += n; 801 } 802 } 803 804 if (err != OK) { 805 LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str()); 806 return ERROR_IO; 807 } 808 809 mAESKeyForURI.add(keyURI, key); 810 } 811 812 AES_KEY aes_key; 813 if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { 814 LOGE("failed to set AES decryption key."); 815 return UNKNOWN_ERROR; 816 } 817 818 unsigned char aes_ivec[16]; 819 820 AString iv; 821 if (itemMeta->findString("cipher-iv", &iv)) { 822 if ((!iv.startsWith("0x") && !iv.startsWith("0X")) 823 || iv.size() != 16 * 2 + 2) { 824 LOGE("malformed cipher IV '%s'.", iv.c_str()); 825 return ERROR_MALFORMED; 826 } 827 828 memset(aes_ivec, 0, sizeof(aes_ivec)); 829 for (size_t i = 0; i < 16; ++i) { 830 char c1 = tolower(iv.c_str()[2 + 2 * i]); 831 char c2 = tolower(iv.c_str()[3 + 2 * i]); 832 if (!isxdigit(c1) || !isxdigit(c2)) { 833 LOGE("malformed cipher IV '%s'.", iv.c_str()); 834 return ERROR_MALFORMED; 835 } 836 uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; 837 uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; 838 839 aes_ivec[i] = nibble1 << 4 | nibble2; 840 } 841 } else { 842 memset(aes_ivec, 0, sizeof(aes_ivec)); 843 aes_ivec[15] = mSeqNumber & 0xff; 844 aes_ivec[14] = (mSeqNumber >> 8) & 0xff; 845 aes_ivec[13] = (mSeqNumber >> 16) & 0xff; 846 aes_ivec[12] = (mSeqNumber >> 24) & 0xff; 847 } 848 849 AES_cbc_encrypt( 850 buffer->data(), buffer->data(), buffer->size(), 851 &aes_key, aes_ivec, AES_DECRYPT); 852 853 // hexdump(buffer->data(), buffer->size()); 854 855 size_t n = buffer->size(); 856 CHECK_GT(n, 0u); 857 858 size_t pad = buffer->data()[n - 1]; 859 860 CHECK_GT(pad, 0u); 861 CHECK_LE(pad, 16u); 862 CHECK_GE((size_t)n, pad); 863 for (size_t i = 0; i < pad; ++i) { 864 CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad); 865 } 866 867 n -= pad; 868 869 buffer->setRange(buffer->offset(), n); 870 871 return OK; 872} 873 874void LiveSession::postMonitorQueue(int64_t delayUs) { 875 sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id()); 876 msg->setInt32("generation", ++mMonitorQueueGeneration); 877 msg->post(delayUs); 878} 879 880void LiveSession::onSeek(const sp<AMessage> &msg) { 881 int64_t timeUs; 882 CHECK(msg->findInt64("timeUs", &timeUs)); 883 884 mSeekTimeUs = timeUs; 885 postMonitorQueue(); 886} 887 888status_t LiveSession::getDuration(int64_t *durationUs) { 889 Mutex::Autolock autoLock(mLock); 890 *durationUs = mDurationUs; 891 892 return OK; 893} 894 895bool LiveSession::isSeekable() { 896 int64_t durationUs; 897 return getDuration(&durationUs) == OK && durationUs >= 0; 898} 899 900} // namespace android 901 902