LiveSession.cpp revision 2aa4cc04154d82f6658fb86f394f13bb488b0468
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( 219 const char *url, sp<ABuffer> *out, 220 int64_t range_offset, int64_t range_length) { 221 *out = NULL; 222 223 sp<DataSource> source; 224 225 if (!strncasecmp(url, "file://", 7)) { 226 source = new FileSource(url + 7); 227 } else if (strncasecmp(url, "http://", 7) 228 && strncasecmp(url, "https://", 8)) { 229 return ERROR_UNSUPPORTED; 230 } else { 231 { 232 Mutex::Autolock autoLock(mLock); 233 234 if (mDisconnectPending) { 235 return ERROR_IO; 236 } 237 } 238 239 KeyedVector<String8, String8> headers = mExtraHeaders; 240 if (range_offset > 0 || range_length >= 0) { 241 headers.add( 242 String8("Range"), 243 String8( 244 StringPrintf( 245 "bytes=%lld-%s", 246 range_offset, 247 range_length < 0 248 ? "" : StringPrintf("%lld", range_offset + range_length - 1).c_str()).c_str())); 249 } 250 status_t err = mHTTPDataSource->connect(url, &headers); 251 252 if (err != OK) { 253 return err; 254 } 255 256 source = mHTTPDataSource; 257 } 258 259 off64_t size; 260 status_t err = source->getSize(&size); 261 262 if (err != OK) { 263 size = 65536; 264 } 265 266 sp<ABuffer> buffer = new ABuffer(size); 267 buffer->setRange(0, 0); 268 269 for (;;) { 270 size_t bufferRemaining = buffer->capacity() - buffer->size(); 271 272 if (bufferRemaining == 0) { 273 bufferRemaining = 32768; 274 275 ALOGV("increasing download buffer to %d bytes", 276 buffer->size() + bufferRemaining); 277 278 sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining); 279 memcpy(copy->data(), buffer->data(), buffer->size()); 280 copy->setRange(0, buffer->size()); 281 282 buffer = copy; 283 } 284 285 size_t maxBytesToRead = bufferRemaining; 286 if (range_length >= 0) { 287 int64_t bytesLeftInRange = range_length - buffer->size(); 288 if (bytesLeftInRange < maxBytesToRead) { 289 maxBytesToRead = bytesLeftInRange; 290 291 if (bytesLeftInRange == 0) { 292 break; 293 } 294 } 295 } 296 297 ssize_t n = source->readAt( 298 buffer->size(), buffer->data() + buffer->size(), 299 maxBytesToRead); 300 301 if (n < 0) { 302 return n; 303 } 304 305 if (n == 0) { 306 break; 307 } 308 309 buffer->setRange(0, buffer->size() + (size_t)n); 310 } 311 312 *out = buffer; 313 314 return OK; 315} 316 317sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) { 318 *unchanged = false; 319 320 sp<ABuffer> buffer; 321 status_t err = fetchFile(url, &buffer); 322 323 if (err != OK) { 324 return NULL; 325 } 326 327 // MD5 functionality is not available on the simulator, treat all 328 // playlists as changed. 329 330#if defined(HAVE_ANDROID_OS) 331 uint8_t hash[16]; 332 333 MD5_CTX m; 334 MD5_Init(&m); 335 MD5_Update(&m, buffer->data(), buffer->size()); 336 337 MD5_Final(hash, &m); 338 339 if (mPlaylist != NULL && !memcmp(hash, mPlaylistHash, 16)) { 340 // playlist unchanged 341 342 if (mRefreshState != THIRD_UNCHANGED_RELOAD_ATTEMPT) { 343 mRefreshState = (RefreshState)(mRefreshState + 1); 344 } 345 346 *unchanged = true; 347 348 ALOGV("Playlist unchanged, refresh state is now %d", 349 (int)mRefreshState); 350 351 return NULL; 352 } 353 354 memcpy(mPlaylistHash, hash, sizeof(hash)); 355 356 mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY; 357#endif 358 359 sp<M3UParser> playlist = 360 new M3UParser(url, buffer->data(), buffer->size()); 361 362 if (playlist->initCheck() != OK) { 363 LOGE("failed to parse .m3u8 playlist"); 364 365 return NULL; 366 } 367 368 return playlist; 369} 370 371static double uniformRand() { 372 return (double)rand() / RAND_MAX; 373} 374 375size_t LiveSession::getBandwidthIndex() { 376 if (mBandwidthItems.size() == 0) { 377 return 0; 378 } 379 380#if 1 381 int32_t bandwidthBps; 382 if (mHTTPDataSource != NULL 383 && mHTTPDataSource->estimateBandwidth(&bandwidthBps)) { 384 ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); 385 } else { 386 ALOGV("no bandwidth estimate."); 387 return 0; // Pick the lowest bandwidth stream by default. 388 } 389 390 char value[PROPERTY_VALUE_MAX]; 391 if (property_get("media.httplive.max-bw", value, NULL)) { 392 char *end; 393 long maxBw = strtoul(value, &end, 10); 394 if (end > value && *end == '\0') { 395 if (maxBw > 0 && bandwidthBps > maxBw) { 396 ALOGV("bandwidth capped to %ld bps", maxBw); 397 bandwidthBps = maxBw; 398 } 399 } 400 } 401 402 // Consider only 80% of the available bandwidth usable. 403 bandwidthBps = (bandwidthBps * 8) / 10; 404 405 // Pick the highest bandwidth stream below or equal to estimated bandwidth. 406 407 size_t index = mBandwidthItems.size() - 1; 408 while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth 409 > (size_t)bandwidthBps) { 410 --index; 411 } 412#elif 0 413 // Change bandwidth at random() 414 size_t index = uniformRand() * mBandwidthItems.size(); 415#elif 0 416 // There's a 50% chance to stay on the current bandwidth and 417 // a 50% chance to switch to the next higher bandwidth (wrapping around 418 // to lowest) 419 const size_t kMinIndex = 0; 420 421 size_t index; 422 if (mPrevBandwidthIndex < 0) { 423 index = kMinIndex; 424 } else if (uniformRand() < 0.5) { 425 index = (size_t)mPrevBandwidthIndex; 426 } else { 427 index = mPrevBandwidthIndex + 1; 428 if (index == mBandwidthItems.size()) { 429 index = kMinIndex; 430 } 431 } 432#elif 0 433 // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec 434 435 size_t index = mBandwidthItems.size() - 1; 436 while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth > 1200000) { 437 --index; 438 } 439#else 440 size_t index = mBandwidthItems.size() - 1; // Highest bandwidth stream 441#endif 442 443 return index; 444} 445 446bool LiveSession::timeToRefreshPlaylist(int64_t nowUs) const { 447 if (mPlaylist == NULL) { 448 CHECK_EQ((int)mRefreshState, (int)INITIAL_MINIMUM_RELOAD_DELAY); 449 return true; 450 } 451 452 int32_t targetDurationSecs; 453 CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs)); 454 455 int64_t targetDurationUs = targetDurationSecs * 1000000ll; 456 457 int64_t minPlaylistAgeUs; 458 459 switch (mRefreshState) { 460 case INITIAL_MINIMUM_RELOAD_DELAY: 461 { 462 size_t n = mPlaylist->size(); 463 if (n > 0) { 464 sp<AMessage> itemMeta; 465 CHECK(mPlaylist->itemAt(n - 1, NULL /* uri */, &itemMeta)); 466 467 int64_t itemDurationUs; 468 CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); 469 470 minPlaylistAgeUs = itemDurationUs; 471 break; 472 } 473 474 // fall through 475 } 476 477 case FIRST_UNCHANGED_RELOAD_ATTEMPT: 478 { 479 minPlaylistAgeUs = targetDurationUs / 2; 480 break; 481 } 482 483 case SECOND_UNCHANGED_RELOAD_ATTEMPT: 484 { 485 minPlaylistAgeUs = (targetDurationUs * 3) / 2; 486 break; 487 } 488 489 case THIRD_UNCHANGED_RELOAD_ATTEMPT: 490 { 491 minPlaylistAgeUs = targetDurationUs * 3; 492 break; 493 } 494 495 default: 496 TRESPASS(); 497 break; 498 } 499 500 return mLastPlaylistFetchTimeUs + minPlaylistAgeUs <= nowUs; 501} 502 503void LiveSession::onDownloadNext() { 504 size_t bandwidthIndex = getBandwidthIndex(); 505 506rinse_repeat: 507 int64_t nowUs = ALooper::GetNowUs(); 508 509 if (mLastPlaylistFetchTimeUs < 0 510 || (ssize_t)bandwidthIndex != mPrevBandwidthIndex 511 || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) { 512 AString url; 513 if (mBandwidthItems.size() > 0) { 514 url = mBandwidthItems.editItemAt(bandwidthIndex).mURI; 515 } else { 516 url = mMasterURL; 517 } 518 519 bool firstTime = (mPlaylist == NULL); 520 521 if ((ssize_t)bandwidthIndex != mPrevBandwidthIndex) { 522 // If we switch bandwidths, do not pay any heed to whether 523 // playlists changed since the last time... 524 mPlaylist.clear(); 525 } 526 527 bool unchanged; 528 sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &unchanged); 529 if (playlist == NULL) { 530 if (unchanged) { 531 // We succeeded in fetching the playlist, but it was 532 // unchanged from the last time we tried. 533 } else { 534 LOGE("failed to load playlist at url '%s'", url.c_str()); 535 mDataSource->queueEOS(ERROR_IO); 536 return; 537 } 538 } else { 539 mPlaylist = playlist; 540 } 541 542 if (firstTime) { 543 Mutex::Autolock autoLock(mLock); 544 545 if (!mPlaylist->isComplete()) { 546 mDurationUs = -1; 547 } else { 548 mDurationUs = 0; 549 for (size_t i = 0; i < mPlaylist->size(); ++i) { 550 sp<AMessage> itemMeta; 551 CHECK(mPlaylist->itemAt( 552 i, NULL /* uri */, &itemMeta)); 553 554 int64_t itemDurationUs; 555 CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); 556 557 mDurationUs += itemDurationUs; 558 } 559 } 560 } 561 562 mLastPlaylistFetchTimeUs = ALooper::GetNowUs(); 563 } 564 565 int32_t firstSeqNumberInPlaylist; 566 if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( 567 "media-sequence", &firstSeqNumberInPlaylist)) { 568 firstSeqNumberInPlaylist = 0; 569 } 570 571 bool seekDiscontinuity = false; 572 bool explicitDiscontinuity = false; 573 bool bandwidthChanged = false; 574 575 if (mSeekTimeUs >= 0) { 576 if (mPlaylist->isComplete()) { 577 size_t index = 0; 578 int64_t segmentStartUs = 0; 579 while (index < mPlaylist->size()) { 580 sp<AMessage> itemMeta; 581 CHECK(mPlaylist->itemAt( 582 index, NULL /* uri */, &itemMeta)); 583 584 int64_t itemDurationUs; 585 CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); 586 587 if (mSeekTimeUs < segmentStartUs + itemDurationUs) { 588 break; 589 } 590 591 segmentStartUs += itemDurationUs; 592 ++index; 593 } 594 595 if (index < mPlaylist->size()) { 596 int32_t newSeqNumber = firstSeqNumberInPlaylist + index; 597 598 if (newSeqNumber != mSeqNumber) { 599 LOGI("seeking to seq no %d", newSeqNumber); 600 601 mSeqNumber = newSeqNumber; 602 603 mDataSource->reset(); 604 605 // reseting the data source will have had the 606 // side effect of discarding any previously queued 607 // bandwidth change discontinuity. 608 // Therefore we'll need to treat these seek 609 // discontinuities as involving a bandwidth change 610 // even if they aren't directly. 611 seekDiscontinuity = true; 612 bandwidthChanged = true; 613 } 614 } 615 } 616 617 mSeekTimeUs = -1; 618 619 Mutex::Autolock autoLock(mLock); 620 mSeekDone = true; 621 mCondition.broadcast(); 622 } 623 624 if (mSeqNumber < 0) { 625 mSeqNumber = firstSeqNumberInPlaylist; 626 } 627 628 int32_t lastSeqNumberInPlaylist = 629 firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; 630 631 if (mSeqNumber < firstSeqNumberInPlaylist 632 || mSeqNumber > lastSeqNumberInPlaylist) { 633 if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) { 634 // Go back to the previous bandwidth. 635 636 LOGI("new bandwidth does not have the sequence number " 637 "we're looking for, switching back to previous bandwidth"); 638 639 mLastPlaylistFetchTimeUs = -1; 640 bandwidthIndex = mPrevBandwidthIndex; 641 goto rinse_repeat; 642 } 643 644 if (!mPlaylist->isComplete() && mNumRetries < kMaxNumRetries) { 645 ++mNumRetries; 646 647 if (mSeqNumber > lastSeqNumberInPlaylist) { 648 mLastPlaylistFetchTimeUs = -1; 649 postMonitorQueue(3000000ll); 650 return; 651 } 652 653 // we've missed the boat, let's start from the lowest sequence 654 // number available and signal a discontinuity. 655 656 LOGI("We've missed the boat, restarting playback."); 657 mSeqNumber = lastSeqNumberInPlaylist; 658 explicitDiscontinuity = true; 659 660 // fall through 661 } else { 662 LOGE("Cannot find sequence number %d in playlist " 663 "(contains %d - %d)", 664 mSeqNumber, firstSeqNumberInPlaylist, 665 firstSeqNumberInPlaylist + mPlaylist->size() - 1); 666 667 mDataSource->queueEOS(ERROR_END_OF_STREAM); 668 return; 669 } 670 } 671 672 mNumRetries = 0; 673 674 AString uri; 675 sp<AMessage> itemMeta; 676 CHECK(mPlaylist->itemAt( 677 mSeqNumber - firstSeqNumberInPlaylist, 678 &uri, 679 &itemMeta)); 680 681 int32_t val; 682 if (itemMeta->findInt32("discontinuity", &val) && val != 0) { 683 explicitDiscontinuity = true; 684 } 685 686 int64_t range_offset, range_length; 687 if (!itemMeta->findInt64("range-offset", &range_offset) 688 || !itemMeta->findInt64("range-length", &range_length)) { 689 range_offset = 0; 690 range_length = -1; 691 } 692 693 sp<ABuffer> buffer; 694 status_t err = fetchFile(uri.c_str(), &buffer, range_offset, range_length); 695 if (err != OK) { 696 LOGE("failed to fetch .ts segment at url '%s'", uri.c_str()); 697 mDataSource->queueEOS(err); 698 return; 699 } 700 701 CHECK(buffer != NULL); 702 703 err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer); 704 705 if (err != OK) { 706 LOGE("decryptBuffer failed w/ error %d", err); 707 708 mDataSource->queueEOS(err); 709 return; 710 } 711 712 if (buffer->size() == 0 || buffer->data()[0] != 0x47) { 713 // Not a transport stream??? 714 715 LOGE("This doesn't look like a transport stream..."); 716 717 mBandwidthItems.removeAt(bandwidthIndex); 718 719 if (mBandwidthItems.isEmpty()) { 720 mDataSource->queueEOS(ERROR_UNSUPPORTED); 721 return; 722 } 723 724 LOGI("Retrying with a different bandwidth stream."); 725 726 mLastPlaylistFetchTimeUs = -1; 727 bandwidthIndex = getBandwidthIndex(); 728 mPrevBandwidthIndex = bandwidthIndex; 729 mSeqNumber = -1; 730 731 goto rinse_repeat; 732 } 733 734 if ((size_t)mPrevBandwidthIndex != bandwidthIndex) { 735 bandwidthChanged = true; 736 } 737 738 if (mPrevBandwidthIndex < 0) { 739 // Don't signal a bandwidth change at the very beginning of 740 // playback. 741 bandwidthChanged = false; 742 } 743 744 if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) { 745 // Signal discontinuity. 746 747 LOGI("queueing discontinuity (seek=%d, explicit=%d, bandwidthChanged=%d)", 748 seekDiscontinuity, explicitDiscontinuity, bandwidthChanged); 749 750 sp<ABuffer> tmp = new ABuffer(188); 751 memset(tmp->data(), 0, tmp->size()); 752 753 // signal a 'hard' discontinuity for explicit or bandwidthChanged. 754 tmp->data()[1] = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0; 755 756 mDataSource->queueBuffer(tmp); 757 } 758 759 mDataSource->queueBuffer(buffer); 760 761 mPrevBandwidthIndex = bandwidthIndex; 762 ++mSeqNumber; 763 764 postMonitorQueue(); 765} 766 767void LiveSession::onMonitorQueue() { 768 if (mSeekTimeUs >= 0 769 || mDataSource->countQueuedBuffers() < kMaxNumQueuedFragments) { 770 onDownloadNext(); 771 } else { 772 postMonitorQueue(1000000ll); 773 } 774} 775 776status_t LiveSession::decryptBuffer( 777 size_t playlistIndex, const sp<ABuffer> &buffer) { 778 sp<AMessage> itemMeta; 779 bool found = false; 780 AString method; 781 782 for (ssize_t i = playlistIndex; i >= 0; --i) { 783 AString uri; 784 CHECK(mPlaylist->itemAt(i, &uri, &itemMeta)); 785 786 if (itemMeta->findString("cipher-method", &method)) { 787 found = true; 788 break; 789 } 790 } 791 792 if (!found) { 793 method = "NONE"; 794 } 795 796 if (method == "NONE") { 797 return OK; 798 } else if (!(method == "AES-128")) { 799 LOGE("Unsupported cipher method '%s'", method.c_str()); 800 return ERROR_UNSUPPORTED; 801 } 802 803 AString keyURI; 804 if (!itemMeta->findString("cipher-uri", &keyURI)) { 805 LOGE("Missing key uri"); 806 return ERROR_MALFORMED; 807 } 808 809 ssize_t index = mAESKeyForURI.indexOfKey(keyURI); 810 811 sp<ABuffer> key; 812 if (index >= 0) { 813 key = mAESKeyForURI.valueAt(index); 814 } else { 815 key = new ABuffer(16); 816 817 sp<HTTPBase> keySource = 818 HTTPBase::Create( 819 (mFlags & kFlagIncognito) 820 ? HTTPBase::kFlagIncognito 821 : 0); 822 823 if (mUIDValid) { 824 keySource->setUID(mUID); 825 } 826 827 status_t err = 828 keySource->connect( 829 keyURI.c_str(), 830 mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); 831 832 if (err == OK) { 833 size_t offset = 0; 834 while (offset < 16) { 835 ssize_t n = keySource->readAt( 836 offset, key->data() + offset, 16 - offset); 837 if (n <= 0) { 838 err = ERROR_IO; 839 break; 840 } 841 842 offset += n; 843 } 844 } 845 846 if (err != OK) { 847 LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str()); 848 return ERROR_IO; 849 } 850 851 mAESKeyForURI.add(keyURI, key); 852 } 853 854 AES_KEY aes_key; 855 if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { 856 LOGE("failed to set AES decryption key."); 857 return UNKNOWN_ERROR; 858 } 859 860 unsigned char aes_ivec[16]; 861 862 AString iv; 863 if (itemMeta->findString("cipher-iv", &iv)) { 864 if ((!iv.startsWith("0x") && !iv.startsWith("0X")) 865 || iv.size() != 16 * 2 + 2) { 866 LOGE("malformed cipher IV '%s'.", iv.c_str()); 867 return ERROR_MALFORMED; 868 } 869 870 memset(aes_ivec, 0, sizeof(aes_ivec)); 871 for (size_t i = 0; i < 16; ++i) { 872 char c1 = tolower(iv.c_str()[2 + 2 * i]); 873 char c2 = tolower(iv.c_str()[3 + 2 * i]); 874 if (!isxdigit(c1) || !isxdigit(c2)) { 875 LOGE("malformed cipher IV '%s'.", iv.c_str()); 876 return ERROR_MALFORMED; 877 } 878 uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; 879 uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; 880 881 aes_ivec[i] = nibble1 << 4 | nibble2; 882 } 883 } else { 884 memset(aes_ivec, 0, sizeof(aes_ivec)); 885 aes_ivec[15] = mSeqNumber & 0xff; 886 aes_ivec[14] = (mSeqNumber >> 8) & 0xff; 887 aes_ivec[13] = (mSeqNumber >> 16) & 0xff; 888 aes_ivec[12] = (mSeqNumber >> 24) & 0xff; 889 } 890 891 AES_cbc_encrypt( 892 buffer->data(), buffer->data(), buffer->size(), 893 &aes_key, aes_ivec, AES_DECRYPT); 894 895 // hexdump(buffer->data(), buffer->size()); 896 897 size_t n = buffer->size(); 898 CHECK_GT(n, 0u); 899 900 size_t pad = buffer->data()[n - 1]; 901 902 CHECK_GT(pad, 0u); 903 CHECK_LE(pad, 16u); 904 CHECK_GE((size_t)n, pad); 905 for (size_t i = 0; i < pad; ++i) { 906 CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad); 907 } 908 909 n -= pad; 910 911 buffer->setRange(buffer->offset(), n); 912 913 return OK; 914} 915 916void LiveSession::postMonitorQueue(int64_t delayUs) { 917 sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id()); 918 msg->setInt32("generation", ++mMonitorQueueGeneration); 919 msg->post(delayUs); 920} 921 922void LiveSession::onSeek(const sp<AMessage> &msg) { 923 int64_t timeUs; 924 CHECK(msg->findInt64("timeUs", &timeUs)); 925 926 mSeekTimeUs = timeUs; 927 postMonitorQueue(); 928} 929 930status_t LiveSession::getDuration(int64_t *durationUs) { 931 Mutex::Autolock autoLock(mLock); 932 *durationUs = mDurationUs; 933 934 return OK; 935} 936 937bool LiveSession::isSeekable() { 938 int64_t durationUs; 939 return getDuration(&durationUs) == OK && durationUs >= 0; 940} 941 942} // namespace android 943 944