LiveSession.cpp revision 1156dc913a5ba7b2bc86489468d4914430f03d14
1dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project/* 2dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Copyright (C) 2010 The Android Open Source Project 3dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 4dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * you may not use this file except in compliance with the License. 6dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * You may obtain a copy of the License at 7dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 8dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * 10dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * See the License for the specific language governing permissions and 14dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project * limitations under the License. 15dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project */ 16dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 17dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project//#define LOG_NDEBUG 0 18dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#define LOG_TAG "LiveSession" 19dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <utils/Log.h> 20dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 21dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "include/LiveSession.h" 22dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 23dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "LiveDataSource.h" 24dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 25dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "include/M3UParser.h" 26dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include "include/HTTPBase.h" 27dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 28dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <cutils/properties.h> 29dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <media/stagefright/foundation/hexdump.h> 30dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <media/stagefright/foundation/ABuffer.h> 31dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <media/stagefright/foundation/ADebug.h> 32dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <media/stagefright/foundation/AMessage.h> 33dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <media/stagefright/DataSource.h> 34dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <media/stagefright/FileSource.h> 35dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <media/stagefright/MediaErrors.h> 36dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 37dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <ctype.h> 38dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project#include <openssl/aes.h> 39dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 40dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectnamespace android { 41dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 42dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Projectconst int64_t LiveSession::kMaxPlaylistAgeUs = 15000000ll; 43dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project 44dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source ProjectLiveSession::LiveSession(uint32_t flags) 45dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project : mFlags(flags), 46dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mDataSource(new LiveDataSource), 47dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project mHTTPDataSource( 48dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0The Android Open Source Project HTTPBase::Create( 49 (mFlags & kFlagIncognito) 50 ? HTTPBase::kFlagIncognito 51 : 0)), 52 mPrevBandwidthIndex(-1), 53 mLastPlaylistFetchTimeUs(-1), 54 mSeqNumber(-1), 55 mSeekTimeUs(-1), 56 mNumRetries(0), 57 mDurationUs(-1), 58 mSeekDone(false), 59 mDisconnectPending(false), 60 mMonitorQueueGeneration(0) { 61} 62 63LiveSession::~LiveSession() { 64} 65 66sp<DataSource> LiveSession::getDataSource() { 67 return mDataSource; 68} 69 70void LiveSession::connect(const char *url) { 71 sp<AMessage> msg = new AMessage(kWhatConnect, id()); 72 msg->setString("url", url); 73 msg->post(); 74} 75 76void LiveSession::disconnect() { 77 Mutex::Autolock autoLock(mLock); 78 mDisconnectPending = true; 79 80 mHTTPDataSource->disconnect(); 81 82 (new AMessage(kWhatDisconnect, id()))->post(); 83} 84 85void LiveSession::seekTo(int64_t timeUs) { 86 Mutex::Autolock autoLock(mLock); 87 mSeekDone = false; 88 89 sp<AMessage> msg = new AMessage(kWhatSeek, id()); 90 msg->setInt64("timeUs", timeUs); 91 msg->post(); 92 93 while (!mSeekDone) { 94 mCondition.wait(mLock); 95 } 96} 97 98void LiveSession::onMessageReceived(const sp<AMessage> &msg) { 99 switch (msg->what()) { 100 case kWhatConnect: 101 onConnect(msg); 102 break; 103 104 case kWhatDisconnect: 105 onDisconnect(); 106 break; 107 108 case kWhatMonitorQueue: 109 { 110 int32_t generation; 111 CHECK(msg->findInt32("generation", &generation)); 112 113 if (generation != mMonitorQueueGeneration) { 114 // Stale event 115 break; 116 } 117 118 onMonitorQueue(); 119 break; 120 } 121 122 case kWhatSeek: 123 onSeek(msg); 124 break; 125 126 default: 127 TRESPASS(); 128 break; 129 } 130} 131 132// static 133int LiveSession::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) { 134 if (a->mBandwidth < b->mBandwidth) { 135 return -1; 136 } else if (a->mBandwidth == b->mBandwidth) { 137 return 0; 138 } 139 140 return 1; 141} 142 143void LiveSession::onConnect(const sp<AMessage> &msg) { 144 AString url; 145 CHECK(msg->findString("url", &url)); 146 147 if (!(mFlags & kFlagIncognito)) { 148 LOGI("onConnect '%s'", url.c_str()); 149 } else { 150 LOGI("onConnect <URL suppressed>"); 151 } 152 153 mMasterURL = url; 154 155 sp<M3UParser> playlist = fetchPlaylist(url.c_str()); 156 157 if (playlist == NULL) { 158 LOGE("unable to fetch master playlist '%s'.", url.c_str()); 159 160 mDataSource->queueEOS(ERROR_IO); 161 return; 162 } 163 164 if (playlist->isVariantPlaylist()) { 165 for (size_t i = 0; i < playlist->size(); ++i) { 166 BandwidthItem item; 167 168 sp<AMessage> meta; 169 playlist->itemAt(i, &item.mURI, &meta); 170 171 unsigned long bandwidth; 172 CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth)); 173 174 mBandwidthItems.push(item); 175 } 176 177 CHECK_GT(mBandwidthItems.size(), 0u); 178 179 mBandwidthItems.sort(SortByBandwidth); 180 } 181 182 postMonitorQueue(); 183} 184 185void LiveSession::onDisconnect() { 186 LOGI("onDisconnect"); 187 188 mDataSource->queueEOS(ERROR_END_OF_STREAM); 189 190 Mutex::Autolock autoLock(mLock); 191 mDisconnectPending = false; 192} 193 194status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) { 195 *out = NULL; 196 197 sp<DataSource> source; 198 199 if (!strncasecmp(url, "file://", 7)) { 200 source = new FileSource(url + 7); 201 } else if (strncasecmp(url, "http://", 7) 202 && strncasecmp(url, "https://", 8)) { 203 return ERROR_UNSUPPORTED; 204 } else { 205 { 206 Mutex::Autolock autoLock(mLock); 207 208 if (mDisconnectPending) { 209 return ERROR_IO; 210 } 211 } 212 213 status_t err = mHTTPDataSource->connect(url); 214 215 if (err != OK) { 216 return err; 217 } 218 219 source = mHTTPDataSource; 220 } 221 222 off64_t size; 223 status_t err = source->getSize(&size); 224 225 if (err != OK) { 226 size = 65536; 227 } 228 229 sp<ABuffer> buffer = new ABuffer(size); 230 buffer->setRange(0, 0); 231 232 for (;;) { 233 size_t bufferRemaining = buffer->capacity() - buffer->size(); 234 235 if (bufferRemaining == 0) { 236 bufferRemaining = 32768; 237 238 LOGV("increasing download buffer to %d bytes", 239 buffer->size() + bufferRemaining); 240 241 sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining); 242 memcpy(copy->data(), buffer->data(), buffer->size()); 243 copy->setRange(0, buffer->size()); 244 245 buffer = copy; 246 } 247 248 ssize_t n = source->readAt( 249 buffer->size(), buffer->data() + buffer->size(), 250 bufferRemaining); 251 252 if (n < 0) { 253 return n; 254 } 255 256 if (n == 0) { 257 break; 258 } 259 260 buffer->setRange(0, buffer->size() + (size_t)n); 261 } 262 263 *out = buffer; 264 265 return OK; 266} 267 268sp<M3UParser> LiveSession::fetchPlaylist(const char *url) { 269 sp<ABuffer> buffer; 270 status_t err = fetchFile(url, &buffer); 271 272 if (err != OK) { 273 return NULL; 274 } 275 276 sp<M3UParser> playlist = 277 new M3UParser(url, buffer->data(), buffer->size()); 278 279 if (playlist->initCheck() != OK) { 280 return NULL; 281 } 282 283 return playlist; 284} 285 286static double uniformRand() { 287 return (double)rand() / RAND_MAX; 288} 289 290size_t LiveSession::getBandwidthIndex() { 291 if (mBandwidthItems.size() == 0) { 292 return 0; 293 } 294 295#if 1 296 int32_t bandwidthBps; 297 if (mHTTPDataSource != NULL 298 && mHTTPDataSource->estimateBandwidth(&bandwidthBps)) { 299 LOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f); 300 } else { 301 LOGV("no bandwidth estimate."); 302 return 0; // Pick the lowest bandwidth stream by default. 303 } 304 305 char value[PROPERTY_VALUE_MAX]; 306 if (property_get("media.httplive.max-bw", value, NULL)) { 307 char *end; 308 long maxBw = strtoul(value, &end, 10); 309 if (end > value && *end == '\0') { 310 if (maxBw > 0 && bandwidthBps > maxBw) { 311 LOGV("bandwidth capped to %ld bps", maxBw); 312 bandwidthBps = maxBw; 313 } 314 } 315 } 316 317 // Consider only 80% of the available bandwidth usable. 318 bandwidthBps = (bandwidthBps * 8) / 10; 319 320 // Pick the highest bandwidth stream below or equal to estimated bandwidth. 321 322 size_t index = mBandwidthItems.size() - 1; 323 while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth 324 > (size_t)bandwidthBps) { 325 --index; 326 } 327#elif 0 328 // Change bandwidth at random() 329 size_t index = uniformRand() * mBandwidthItems.size(); 330#elif 0 331 // There's a 50% chance to stay on the current bandwidth and 332 // a 50% chance to switch to the next higher bandwidth (wrapping around 333 // to lowest) 334 const size_t kMinIndex = 0; 335 336 size_t index; 337 if (mPrevBandwidthIndex < 0) { 338 index = kMinIndex; 339 } else if (uniformRand() < 0.5) { 340 index = (size_t)mPrevBandwidthIndex; 341 } else { 342 index = mPrevBandwidthIndex + 1; 343 if (index == mBandwidthItems.size()) { 344 index = kMinIndex; 345 } 346 } 347#elif 0 348 // Pick the highest bandwidth stream below or equal to 1.2 Mbit/sec 349 350 size_t index = mBandwidthItems.size() - 1; 351 while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth > 1200000) { 352 --index; 353 } 354#else 355 size_t index = mBandwidthItems.size() - 1; // Highest bandwidth stream 356#endif 357 358 return index; 359} 360 361void LiveSession::onDownloadNext() { 362 size_t bandwidthIndex = getBandwidthIndex(); 363 364rinse_repeat: 365 int64_t nowUs = ALooper::GetNowUs(); 366 367 if (mLastPlaylistFetchTimeUs < 0 368 || (ssize_t)bandwidthIndex != mPrevBandwidthIndex 369 || (!mPlaylist->isComplete() 370 && mLastPlaylistFetchTimeUs + kMaxPlaylistAgeUs <= nowUs)) { 371 AString url; 372 if (mBandwidthItems.size() > 0) { 373 url = mBandwidthItems.editItemAt(bandwidthIndex).mURI; 374 } else { 375 url = mMasterURL; 376 } 377 378 bool firstTime = (mPlaylist == NULL); 379 380 mPlaylist = fetchPlaylist(url.c_str()); 381 if (mPlaylist == NULL) { 382 LOGE("failed to load playlist at url '%s'", url.c_str()); 383 mDataSource->queueEOS(ERROR_IO); 384 return; 385 } 386 387 if (firstTime) { 388 Mutex::Autolock autoLock(mLock); 389 390 int32_t targetDuration; 391 if (!mPlaylist->isComplete() 392 || !mPlaylist->meta()->findInt32( 393 "target-duration", &targetDuration)) { 394 mDurationUs = -1; 395 } else { 396 mDurationUs = 1000000ll * targetDuration * mPlaylist->size(); 397 } 398 } 399 400 mLastPlaylistFetchTimeUs = ALooper::GetNowUs(); 401 } 402 403 int32_t firstSeqNumberInPlaylist; 404 if (mPlaylist->meta() == NULL || !mPlaylist->meta()->findInt32( 405 "media-sequence", &firstSeqNumberInPlaylist)) { 406 firstSeqNumberInPlaylist = 0; 407 } 408 409 bool explicitDiscontinuity = false; 410 bool bandwidthChanged = false; 411 412 if (mSeekTimeUs >= 0) { 413 int32_t targetDuration; 414 if (mPlaylist->isComplete() && 415 mPlaylist->meta()->findInt32( 416 "target-duration", &targetDuration)) { 417 int64_t seekTimeSecs = (mSeekTimeUs + 500000ll) / 1000000ll; 418 int64_t index = seekTimeSecs / targetDuration; 419 420 if (index >= 0 && index < mPlaylist->size()) { 421 int32_t newSeqNumber = firstSeqNumberInPlaylist + index; 422 423 if (newSeqNumber != mSeqNumber) { 424 LOGI("seeking to seq no %d", newSeqNumber); 425 426 mSeqNumber = newSeqNumber; 427 428 mDataSource->reset(); 429 430 // reseting the data source will have had the 431 // side effect of discarding any previously queued 432 // bandwidth change discontinuity. 433 // Therefore we'll need to treat these explicit 434 // discontinuities as involving a bandwidth change 435 // even if they aren't directly. 436 explicitDiscontinuity = true; 437 bandwidthChanged = true; 438 } 439 } 440 } 441 442 mSeekTimeUs = -1; 443 444 Mutex::Autolock autoLock(mLock); 445 mSeekDone = true; 446 mCondition.broadcast(); 447 } 448 449 if (mSeqNumber < 0) { 450 if (mPlaylist->isComplete()) { 451 mSeqNumber = firstSeqNumberInPlaylist; 452 } else { 453 mSeqNumber = firstSeqNumberInPlaylist + mPlaylist->size() / 2; 454 } 455 } 456 457 int32_t lastSeqNumberInPlaylist = 458 firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1; 459 460 if (mSeqNumber < firstSeqNumberInPlaylist 461 || mSeqNumber > lastSeqNumberInPlaylist) { 462 if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) { 463 // Go back to the previous bandwidth. 464 465 LOGI("new bandwidth does not have the sequence number " 466 "we're looking for, switching back to previous bandwidth"); 467 468 mLastPlaylistFetchTimeUs = -1; 469 bandwidthIndex = mPrevBandwidthIndex; 470 goto rinse_repeat; 471 } 472 473 if (!mPlaylist->isComplete() 474 && mSeqNumber > lastSeqNumberInPlaylist 475 && mNumRetries < kMaxNumRetries) { 476 ++mNumRetries; 477 478 mLastPlaylistFetchTimeUs = -1; 479 postMonitorQueue(3000000ll); 480 return; 481 } 482 483 LOGE("Cannot find sequence number %d in playlist " 484 "(contains %d - %d)", 485 mSeqNumber, firstSeqNumberInPlaylist, 486 firstSeqNumberInPlaylist + mPlaylist->size() - 1); 487 488 mDataSource->queueEOS(ERROR_END_OF_STREAM); 489 return; 490 } 491 492 mNumRetries = 0; 493 494 AString uri; 495 sp<AMessage> itemMeta; 496 CHECK(mPlaylist->itemAt( 497 mSeqNumber - firstSeqNumberInPlaylist, 498 &uri, 499 &itemMeta)); 500 501 int32_t val; 502 if (itemMeta->findInt32("discontinuity", &val) && val != 0) { 503 explicitDiscontinuity = true; 504 } 505 506 sp<ABuffer> buffer; 507 status_t err = fetchFile(uri.c_str(), &buffer); 508 if (err != OK) { 509 LOGE("failed to fetch .ts segment at url '%s'", uri.c_str()); 510 mDataSource->queueEOS(err); 511 return; 512 } 513 514 CHECK(buffer != NULL); 515 516 err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer); 517 518 if (err != OK) { 519 LOGE("decryptBuffer failed w/ error %d", err); 520 521 mDataSource->queueEOS(err); 522 return; 523 } 524 525 if (buffer->size() == 0 || buffer->data()[0] != 0x47) { 526 // Not a transport stream??? 527 528 LOGE("This doesn't look like a transport stream..."); 529 530 mBandwidthItems.removeAt(bandwidthIndex); 531 532 if (mBandwidthItems.isEmpty()) { 533 mDataSource->queueEOS(ERROR_UNSUPPORTED); 534 return; 535 } 536 537 LOGI("Retrying with a different bandwidth stream."); 538 539 mLastPlaylistFetchTimeUs = -1; 540 bandwidthIndex = getBandwidthIndex(); 541 mPrevBandwidthIndex = bandwidthIndex; 542 mSeqNumber = -1; 543 544 goto rinse_repeat; 545 } 546 547 if ((size_t)mPrevBandwidthIndex != bandwidthIndex) { 548 bandwidthChanged = true; 549 } 550 551 if (mPrevBandwidthIndex < 0) { 552 // Don't signal a bandwidth change at the very beginning of 553 // playback. 554 bandwidthChanged = false; 555 } 556 557 if (explicitDiscontinuity || bandwidthChanged) { 558 // Signal discontinuity. 559 560 LOGI("queueing discontinuity (explicit=%d, bandwidthChanged=%d)", 561 explicitDiscontinuity, bandwidthChanged); 562 563 sp<ABuffer> tmp = new ABuffer(188); 564 memset(tmp->data(), 0, tmp->size()); 565 tmp->data()[1] = bandwidthChanged; 566 567 mDataSource->queueBuffer(tmp); 568 } 569 570 mDataSource->queueBuffer(buffer); 571 572 mPrevBandwidthIndex = bandwidthIndex; 573 ++mSeqNumber; 574 575 postMonitorQueue(); 576} 577 578void LiveSession::onMonitorQueue() { 579 if (mSeekTimeUs >= 0 580 || mDataSource->countQueuedBuffers() < kMaxNumQueuedFragments) { 581 onDownloadNext(); 582 } else { 583 postMonitorQueue(1000000ll); 584 } 585} 586 587status_t LiveSession::decryptBuffer( 588 size_t playlistIndex, const sp<ABuffer> &buffer) { 589 sp<AMessage> itemMeta; 590 bool found = false; 591 AString method; 592 593 for (ssize_t i = playlistIndex; i >= 0; --i) { 594 AString uri; 595 CHECK(mPlaylist->itemAt(i, &uri, &itemMeta)); 596 597 if (itemMeta->findString("cipher-method", &method)) { 598 found = true; 599 break; 600 } 601 } 602 603 if (!found) { 604 method = "NONE"; 605 } 606 607 if (method == "NONE") { 608 return OK; 609 } else if (!(method == "AES-128")) { 610 LOGE("Unsupported cipher method '%s'", method.c_str()); 611 return ERROR_UNSUPPORTED; 612 } 613 614 AString keyURI; 615 if (!itemMeta->findString("cipher-uri", &keyURI)) { 616 LOGE("Missing key uri"); 617 return ERROR_MALFORMED; 618 } 619 620 ssize_t index = mAESKeyForURI.indexOfKey(keyURI); 621 622 sp<ABuffer> key; 623 if (index >= 0) { 624 key = mAESKeyForURI.valueAt(index); 625 } else { 626 key = new ABuffer(16); 627 628 sp<HTTPBase> keySource = 629 HTTPBase::Create( 630 (mFlags & kFlagIncognito) 631 ? HTTPBase::kFlagIncognito 632 : 0); 633 634 status_t err = keySource->connect(keyURI.c_str()); 635 636 if (err == OK) { 637 size_t offset = 0; 638 while (offset < 16) { 639 ssize_t n = keySource->readAt( 640 offset, key->data() + offset, 16 - offset); 641 if (n <= 0) { 642 err = ERROR_IO; 643 break; 644 } 645 646 offset += n; 647 } 648 } 649 650 if (err != OK) { 651 LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str()); 652 return ERROR_IO; 653 } 654 655 mAESKeyForURI.add(keyURI, key); 656 } 657 658 AES_KEY aes_key; 659 if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) { 660 LOGE("failed to set AES decryption key."); 661 return UNKNOWN_ERROR; 662 } 663 664 unsigned char aes_ivec[16]; 665 666 AString iv; 667 if (itemMeta->findString("cipher-iv", &iv)) { 668 if ((!iv.startsWith("0x") && !iv.startsWith("0X")) 669 || iv.size() != 16 * 2 + 2) { 670 LOGE("malformed cipher IV '%s'.", iv.c_str()); 671 return ERROR_MALFORMED; 672 } 673 674 memset(aes_ivec, 0, sizeof(aes_ivec)); 675 for (size_t i = 0; i < 16; ++i) { 676 char c1 = tolower(iv.c_str()[2 + 2 * i]); 677 char c2 = tolower(iv.c_str()[3 + 2 * i]); 678 if (!isxdigit(c1) || !isxdigit(c2)) { 679 LOGE("malformed cipher IV '%s'.", iv.c_str()); 680 return ERROR_MALFORMED; 681 } 682 uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10; 683 uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10; 684 685 aes_ivec[i] = nibble1 << 4 | nibble2; 686 } 687 } else { 688 memset(aes_ivec, 0, sizeof(aes_ivec)); 689 aes_ivec[15] = mSeqNumber & 0xff; 690 aes_ivec[14] = (mSeqNumber >> 8) & 0xff; 691 aes_ivec[13] = (mSeqNumber >> 16) & 0xff; 692 aes_ivec[12] = (mSeqNumber >> 24) & 0xff; 693 } 694 695 AES_cbc_encrypt( 696 buffer->data(), buffer->data(), buffer->size(), 697 &aes_key, aes_ivec, AES_DECRYPT); 698 699 // hexdump(buffer->data(), buffer->size()); 700 701 size_t n = buffer->size(); 702 CHECK_GT(n, 0u); 703 704 size_t pad = buffer->data()[n - 1]; 705 706 CHECK_GT(pad, 0u); 707 CHECK_LE(pad, 16u); 708 CHECK_GE((size_t)n, pad); 709 for (size_t i = 0; i < pad; ++i) { 710 CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad); 711 } 712 713 n -= pad; 714 715 buffer->setRange(buffer->offset(), n); 716 717 return OK; 718} 719 720void LiveSession::postMonitorQueue(int64_t delayUs) { 721 sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id()); 722 msg->setInt32("generation", ++mMonitorQueueGeneration); 723 msg->post(delayUs); 724} 725 726void LiveSession::onSeek(const sp<AMessage> &msg) { 727 int64_t timeUs; 728 CHECK(msg->findInt64("timeUs", &timeUs)); 729 730 mSeekTimeUs = timeUs; 731 postMonitorQueue(); 732} 733 734status_t LiveSession::getDuration(int64_t *durationUs) { 735 Mutex::Autolock autoLock(mLock); 736 *durationUs = mDurationUs; 737 738 return OK; 739} 740 741bool LiveSession::isSeekable() { 742 int64_t durationUs; 743 return getDuration(&durationUs) == OK && durationUs >= 0; 744} 745 746} // namespace android 747 748