LiveSession.cpp revision 3856b090cd04ba5dd4a59a12430ed724d5995909
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 ALOGV("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 ALOGV("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 ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); 361 } else { 362 ALOGV("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 ALOGV("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() && mNumRetries < kMaxNumRetries) { 621 ++mNumRetries; 622 623 if (mSeqNumber > lastSeqNumberInPlaylist) { 624 mLastPlaylistFetchTimeUs = -1; 625 postMonitorQueue(3000000ll); 626 return; 627 } 628 629 // we've missed the boat, let's start from the lowest sequence 630 // number available and signal a discontinuity. 631 632 LOGI("We've missed the boat, restarting playback."); 633 mSeqNumber = lastSeqNumberInPlaylist; 634 explicitDiscontinuity = true; 635 636 // fall through 637 } else { 638 LOGE("Cannot find sequence number %d in playlist " 639 "(contains %d - %d)", 640 mSeqNumber, firstSeqNumberInPlaylist, 641 firstSeqNumberInPlaylist + mPlaylist->size() - 1); 642 643 mDataSource->queueEOS(ERROR_END_OF_STREAM); 644 return; 645 } 646 } 647 648 mNumRetries = 0; 649 650 AString uri; 651 sp<AMessage> itemMeta; 652 CHECK(mPlaylist->itemAt( 653 mSeqNumber - firstSeqNumberInPlaylist, 654 &uri, 655 &itemMeta)); 656 657 int32_t val; 658 if (itemMeta->findInt32("discontinuity", &val) && val != 0) { 659 explicitDiscontinuity = true; 660 } 661 662 sp<ABuffer> buffer; 663 status_t err = fetchFile(uri.c_str(), &buffer); 664 if (err != OK) { 665 LOGE("failed to fetch .ts segment at url '%s'", uri.c_str()); 666 mDataSource->queueEOS(err); 667 return; 668 } 669 670 CHECK(buffer != NULL); 671 672 err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer); 673 674 if (err != OK) { 675 LOGE("decryptBuffer failed w/ error %d", err); 676 677 mDataSource->queueEOS(err); 678 return; 679 } 680 681 if (buffer->size() == 0 || buffer->data()[0] != 0x47) { 682 // Not a transport stream??? 683 684 LOGE("This doesn't look like a transport stream..."); 685 686 mBandwidthItems.removeAt(bandwidthIndex); 687 688 if (mBandwidthItems.isEmpty()) { 689 mDataSource->queueEOS(ERROR_UNSUPPORTED); 690 return; 691 } 692 693 LOGI("Retrying with a different bandwidth stream."); 694 695 mLastPlaylistFetchTimeUs = -1; 696 bandwidthIndex = getBandwidthIndex(); 697 mPrevBandwidthIndex = bandwidthIndex; 698 mSeqNumber = -1; 699 700 goto rinse_repeat; 701 } 702 703 if ((size_t)mPrevBandwidthIndex != bandwidthIndex) { 704 bandwidthChanged = true; 705 } 706 707 if (mPrevBandwidthIndex < 0) { 708 // Don't signal a bandwidth change at the very beginning of 709 // playback. 710 bandwidthChanged = false; 711 } 712 713 if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) { 714 // Signal discontinuity. 715 716 LOGI("queueing discontinuity (seek=%d, explicit=%d, bandwidthChanged=%d)", 717 seekDiscontinuity, explicitDiscontinuity, bandwidthChanged); 718 719 sp<ABuffer> tmp = new ABuffer(188); 720 memset(tmp->data(), 0, tmp->size()); 721 722 // signal a 'hard' discontinuity for explicit or bandwidthChanged. 723 tmp->data()[1] = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0; 724 725 mDataSource->queueBuffer(tmp); 726 } 727 728 mDataSource->queueBuffer(buffer); 729 730 mPrevBandwidthIndex = bandwidthIndex; 731 ++mSeqNumber; 732 733 postMonitorQueue(); 734} 735 736void LiveSession::onMonitorQueue() { 737 if (mSeekTimeUs >= 0 738 || mDataSource->countQueuedBuffers() < kMaxNumQueuedFragments) { 739 onDownloadNext(); 740 } else { 741 postMonitorQueue(1000000ll); 742 } 743} 744 745status_t LiveSession::decryptBuffer( 746 size_t playlistIndex, const sp<ABuffer> &buffer) { 747 sp<AMessage> itemMeta; 748 bool found = false; 749 AString method; 750 751 for (ssize_t i = playlistIndex; i >= 0; --i) { 752 AString uri; 753 CHECK(mPlaylist->itemAt(i, &uri, &itemMeta)); 754 755 if (itemMeta->findString("cipher-method", &method)) { 756 found = true; 757 break; 758 } 759 } 760 761 if (!found) { 762 method = "NONE"; 763 } 764 765 if (method == "NONE") { 766 return OK; 767 } else if (!(method == "AES-128")) { 768 LOGE("Unsupported cipher method '%s'", method.c_str()); 769 return ERROR_UNSUPPORTED; 770 } 771 772 AString keyURI; 773 if (!itemMeta->findString("cipher-uri", &keyURI)) { 774 LOGE("Missing key uri"); 775 return ERROR_MALFORMED; 776 } 777 778 ssize_t index = mAESKeyForURI.indexOfKey(keyURI); 779 780 sp<ABuffer> key; 781 if (index >= 0) { 782 key = mAESKeyForURI.valueAt(index); 783 } else { 784 key = new ABuffer(16); 785 786 sp<HTTPBase> keySource = 787 HTTPBase::Create( 788 (mFlags & kFlagIncognito) 789 ? HTTPBase::kFlagIncognito 790 : 0); 791 792 if (mUIDValid) { 793 keySource->setUID(mUID); 794 } 795 796 status_t err = 797 keySource->connect( 798 keyURI.c_str(), 799 mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); 800 801 if (err == OK) { 802 size_t offset = 0; 803 while (offset < 16) { 804 ssize_t n = keySource->readAt( 805 offset, key->data() + offset, 16 - offset); 806 if (n <= 0) { 807 err = ERROR_IO; 808 break; 809 } 810 811 offset += n; 812 } 813 } 814 815 if (err != OK) { 816 LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str()); 817 return ERROR_IO; 818 } 819 820 mAESKeyForURI.add(keyURI, key); 821 } 822 823 AES_KEY aes_key; 824 if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { 825 LOGE("failed to set AES decryption key."); 826 return UNKNOWN_ERROR; 827 } 828 829 unsigned char aes_ivec[16]; 830 831 AString iv; 832 if (itemMeta->findString("cipher-iv", &iv)) { 833 if ((!iv.startsWith("0x") && !iv.startsWith("0X")) 834 || iv.size() != 16 * 2 + 2) { 835 LOGE("malformed cipher IV '%s'.", iv.c_str()); 836 return ERROR_MALFORMED; 837 } 838 839 memset(aes_ivec, 0, sizeof(aes_ivec)); 840 for (size_t i = 0; i < 16; ++i) { 841 char c1 = tolower(iv.c_str()[2 + 2 * i]); 842 char c2 = tolower(iv.c_str()[3 + 2 * i]); 843 if (!isxdigit(c1) || !isxdigit(c2)) { 844 LOGE("malformed cipher IV '%s'.", iv.c_str()); 845 return ERROR_MALFORMED; 846 } 847 uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; 848 uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; 849 850 aes_ivec[i] = nibble1 << 4 | nibble2; 851 } 852 } else { 853 memset(aes_ivec, 0, sizeof(aes_ivec)); 854 aes_ivec[15] = mSeqNumber & 0xff; 855 aes_ivec[14] = (mSeqNumber >> 8) & 0xff; 856 aes_ivec[13] = (mSeqNumber >> 16) & 0xff; 857 aes_ivec[12] = (mSeqNumber >> 24) & 0xff; 858 } 859 860 AES_cbc_encrypt( 861 buffer->data(), buffer->data(), buffer->size(), 862 &aes_key, aes_ivec, AES_DECRYPT); 863 864 // hexdump(buffer->data(), buffer->size()); 865 866 size_t n = buffer->size(); 867 CHECK_GT(n, 0u); 868 869 size_t pad = buffer->data()[n - 1]; 870 871 CHECK_GT(pad, 0u); 872 CHECK_LE(pad, 16u); 873 CHECK_GE((size_t)n, pad); 874 for (size_t i = 0; i < pad; ++i) { 875 CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad); 876 } 877 878 n -= pad; 879 880 buffer->setRange(buffer->offset(), n); 881 882 return OK; 883} 884 885void LiveSession::postMonitorQueue(int64_t delayUs) { 886 sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id()); 887 msg->setInt32("generation", ++mMonitorQueueGeneration); 888 msg->post(delayUs); 889} 890 891void LiveSession::onSeek(const sp<AMessage> &msg) { 892 int64_t timeUs; 893 CHECK(msg->findInt64("timeUs", &timeUs)); 894 895 mSeekTimeUs = timeUs; 896 postMonitorQueue(); 897} 898 899status_t LiveSession::getDuration(int64_t *durationUs) { 900 Mutex::Autolock autoLock(mLock); 901 *durationUs = mDurationUs; 902 903 return OK; 904} 905 906bool LiveSession::isSeekable() { 907 int64_t durationUs; 908 return getDuration(&durationUs) == OK && durationUs >= 0; 909} 910 911} // namespace android 912 913