LiveSession.cpp revision 20f725ebcef13ded1b4b85c61c8a4b37cd030656
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 seekDiscontinuity = false; 548 bool explicitDiscontinuity = false; 549 bool bandwidthChanged = false; 550 551 if (mSeekTimeUs >= 0) { 552 if (mPlaylist->isComplete()) { 553 size_t index = 0; 554 int64_t segmentStartUs = 0; 555 while (index < mPlaylist->size()) { 556 sp<AMessage> itemMeta; 557 CHECK(mPlaylist->itemAt( 558 index, NULL /* uri */, &itemMeta)); 559 560 int64_t itemDurationUs; 561 CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); 562 563 if (mSeekTimeUs < segmentStartUs + itemDurationUs) { 564 break; 565 } 566 567 segmentStartUs += itemDurationUs; 568 ++index; 569 } 570 571 if (index < mPlaylist->size()) { 572 int32_t newSeqNumber = firstSeqNumberInPlaylist + index; 573 574 if (newSeqNumber != mSeqNumber) { 575 LOGI("seeking to seq no %d", newSeqNumber); 576 577 mSeqNumber = newSeqNumber; 578 579 mDataSource->reset(); 580 581 // reseting the data source will have had the 582 // side effect of discarding any previously queued 583 // bandwidth change discontinuity. 584 // Therefore we'll need to treat these seek 585 // discontinuities as involving a bandwidth change 586 // even if they aren't directly. 587 seekDiscontinuity = true; 588 bandwidthChanged = true; 589 } 590 } 591 } 592 593 mSeekTimeUs = -1; 594 595 Mutex::Autolock autoLock(mLock); 596 mSeekDone = true; 597 mCondition.broadcast(); 598 } 599 600 if (mSeqNumber < 0) { 601 mSeqNumber = firstSeqNumberInPlaylist; 602 } 603 604 int32_t lastSeqNumberInPlaylist = 605 firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; 606 607 if (mSeqNumber < firstSeqNumberInPlaylist 608 || mSeqNumber > lastSeqNumberInPlaylist) { 609 if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) { 610 // Go back to the previous bandwidth. 611 612 LOGI("new bandwidth does not have the sequence number " 613 "we're looking for, switching back to previous bandwidth"); 614 615 mLastPlaylistFetchTimeUs = -1; 616 bandwidthIndex = mPrevBandwidthIndex; 617 goto rinse_repeat; 618 } 619 620 if (!mPlaylist->isComplete() 621 && mSeqNumber > lastSeqNumberInPlaylist 622 && mNumRetries < kMaxNumRetries) { 623 ++mNumRetries; 624 625 mLastPlaylistFetchTimeUs = -1; 626 postMonitorQueue(3000000ll); 627 return; 628 } 629 630 LOGE("Cannot find sequence number %d in playlist " 631 "(contains %d - %d)", 632 mSeqNumber, firstSeqNumberInPlaylist, 633 firstSeqNumberInPlaylist + mPlaylist->size() - 1); 634 635 mDataSource->queueEOS(ERROR_END_OF_STREAM); 636 return; 637 } 638 639 mNumRetries = 0; 640 641 AString uri; 642 sp<AMessage> itemMeta; 643 CHECK(mPlaylist->itemAt( 644 mSeqNumber - firstSeqNumberInPlaylist, 645 &uri, 646 &itemMeta)); 647 648 int32_t val; 649 if (itemMeta->findInt32("discontinuity", &val) && val != 0) { 650 explicitDiscontinuity = true; 651 } 652 653 sp<ABuffer> buffer; 654 status_t err = fetchFile(uri.c_str(), &buffer); 655 if (err != OK) { 656 LOGE("failed to fetch .ts segment at url '%s'", uri.c_str()); 657 mDataSource->queueEOS(err); 658 return; 659 } 660 661 CHECK(buffer != NULL); 662 663 err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer); 664 665 if (err != OK) { 666 LOGE("decryptBuffer failed w/ error %d", err); 667 668 mDataSource->queueEOS(err); 669 return; 670 } 671 672 if (buffer->size() == 0 || buffer->data()[0] != 0x47) { 673 // Not a transport stream??? 674 675 LOGE("This doesn't look like a transport stream..."); 676 677 mBandwidthItems.removeAt(bandwidthIndex); 678 679 if (mBandwidthItems.isEmpty()) { 680 mDataSource->queueEOS(ERROR_UNSUPPORTED); 681 return; 682 } 683 684 LOGI("Retrying with a different bandwidth stream."); 685 686 mLastPlaylistFetchTimeUs = -1; 687 bandwidthIndex = getBandwidthIndex(); 688 mPrevBandwidthIndex = bandwidthIndex; 689 mSeqNumber = -1; 690 691 goto rinse_repeat; 692 } 693 694 if ((size_t)mPrevBandwidthIndex != bandwidthIndex) { 695 bandwidthChanged = true; 696 } 697 698 if (mPrevBandwidthIndex < 0) { 699 // Don't signal a bandwidth change at the very beginning of 700 // playback. 701 bandwidthChanged = false; 702 } 703 704 if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) { 705 // Signal discontinuity. 706 707 LOGI("queueing discontinuity (seek=%d, explicit=%d, bandwidthChanged=%d)", 708 seekDiscontinuity, explicitDiscontinuity, bandwidthChanged); 709 710 sp<ABuffer> tmp = new ABuffer(188); 711 memset(tmp->data(), 0, tmp->size()); 712 713 // signal a 'hard' discontinuity for explicit or bandwidthChanged. 714 tmp->data()[1] = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0; 715 716 mDataSource->queueBuffer(tmp); 717 } 718 719 mDataSource->queueBuffer(buffer); 720 721 mPrevBandwidthIndex = bandwidthIndex; 722 ++mSeqNumber; 723 724 postMonitorQueue(); 725} 726 727void LiveSession::onMonitorQueue() { 728 if (mSeekTimeUs >= 0 729 || mDataSource->countQueuedBuffers() < kMaxNumQueuedFragments) { 730 onDownloadNext(); 731 } else { 732 postMonitorQueue(1000000ll); 733 } 734} 735 736status_t LiveSession::decryptBuffer( 737 size_t playlistIndex, const sp<ABuffer> &buffer) { 738 sp<AMessage> itemMeta; 739 bool found = false; 740 AString method; 741 742 for (ssize_t i = playlistIndex; i >= 0; --i) { 743 AString uri; 744 CHECK(mPlaylist->itemAt(i, &uri, &itemMeta)); 745 746 if (itemMeta->findString("cipher-method", &method)) { 747 found = true; 748 break; 749 } 750 } 751 752 if (!found) { 753 method = "NONE"; 754 } 755 756 if (method == "NONE") { 757 return OK; 758 } else if (!(method == "AES-128")) { 759 LOGE("Unsupported cipher method '%s'", method.c_str()); 760 return ERROR_UNSUPPORTED; 761 } 762 763 AString keyURI; 764 if (!itemMeta->findString("cipher-uri", &keyURI)) { 765 LOGE("Missing key uri"); 766 return ERROR_MALFORMED; 767 } 768 769 ssize_t index = mAESKeyForURI.indexOfKey(keyURI); 770 771 sp<ABuffer> key; 772 if (index >= 0) { 773 key = mAESKeyForURI.valueAt(index); 774 } else { 775 key = new ABuffer(16); 776 777 sp<HTTPBase> keySource = 778 HTTPBase::Create( 779 (mFlags & kFlagIncognito) 780 ? HTTPBase::kFlagIncognito 781 : 0); 782 783 if (mUIDValid) { 784 keySource->setUID(mUID); 785 } 786 787 status_t err = 788 keySource->connect( 789 keyURI.c_str(), 790 mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); 791 792 if (err == OK) { 793 size_t offset = 0; 794 while (offset < 16) { 795 ssize_t n = keySource->readAt( 796 offset, key->data() + offset, 16 - offset); 797 if (n <= 0) { 798 err = ERROR_IO; 799 break; 800 } 801 802 offset += n; 803 } 804 } 805 806 if (err != OK) { 807 LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str()); 808 return ERROR_IO; 809 } 810 811 mAESKeyForURI.add(keyURI, key); 812 } 813 814 AES_KEY aes_key; 815 if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { 816 LOGE("failed to set AES decryption key."); 817 return UNKNOWN_ERROR; 818 } 819 820 unsigned char aes_ivec[16]; 821 822 AString iv; 823 if (itemMeta->findString("cipher-iv", &iv)) { 824 if ((!iv.startsWith("0x") && !iv.startsWith("0X")) 825 || iv.size() != 16 * 2 + 2) { 826 LOGE("malformed cipher IV '%s'.", iv.c_str()); 827 return ERROR_MALFORMED; 828 } 829 830 memset(aes_ivec, 0, sizeof(aes_ivec)); 831 for (size_t i = 0; i < 16; ++i) { 832 char c1 = tolower(iv.c_str()[2 + 2 * i]); 833 char c2 = tolower(iv.c_str()[3 + 2 * i]); 834 if (!isxdigit(c1) || !isxdigit(c2)) { 835 LOGE("malformed cipher IV '%s'.", iv.c_str()); 836 return ERROR_MALFORMED; 837 } 838 uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; 839 uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; 840 841 aes_ivec[i] = nibble1 << 4 | nibble2; 842 } 843 } else { 844 memset(aes_ivec, 0, sizeof(aes_ivec)); 845 aes_ivec[15] = mSeqNumber & 0xff; 846 aes_ivec[14] = (mSeqNumber >> 8) & 0xff; 847 aes_ivec[13] = (mSeqNumber >> 16) & 0xff; 848 aes_ivec[12] = (mSeqNumber >> 24) & 0xff; 849 } 850 851 AES_cbc_encrypt( 852 buffer->data(), buffer->data(), buffer->size(), 853 &aes_key, aes_ivec, AES_DECRYPT); 854 855 // hexdump(buffer->data(), buffer->size()); 856 857 size_t n = buffer->size(); 858 CHECK_GT(n, 0u); 859 860 size_t pad = buffer->data()[n - 1]; 861 862 CHECK_GT(pad, 0u); 863 CHECK_LE(pad, 16u); 864 CHECK_GE((size_t)n, pad); 865 for (size_t i = 0; i < pad; ++i) { 866 CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad); 867 } 868 869 n -= pad; 870 871 buffer->setRange(buffer->offset(), n); 872 873 return OK; 874} 875 876void LiveSession::postMonitorQueue(int64_t delayUs) { 877 sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id()); 878 msg->setInt32("generation", ++mMonitorQueueGeneration); 879 msg->post(delayUs); 880} 881 882void LiveSession::onSeek(const sp<AMessage> &msg) { 883 int64_t timeUs; 884 CHECK(msg->findInt64("timeUs", &timeUs)); 885 886 mSeekTimeUs = timeUs; 887 postMonitorQueue(); 888} 889 890status_t LiveSession::getDuration(int64_t *durationUs) { 891 Mutex::Autolock autoLock(mLock); 892 *durationUs = mDurationUs; 893 894 return OK; 895} 896 897bool LiveSession::isSeekable() { 898 int64_t durationUs; 899 return getDuration(&durationUs) == OK && durationUs >= 0; 900} 901 902} // namespace android 903 904