blob: 73b3d5b9a9052bb4ed5c1c44810b62d7b65ddc77 [file] [log] [blame]
Andreas Hubera44153c2010-12-03 16:12:25 -08001/*
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"
Andreas Huber1156dc92011-03-08 15:59:28 -080026#include "include/HTTPBase.h"
Andreas Hubera44153c2010-12-03 16:12:25 -080027
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>
Andreas Huber7e43a5a2011-07-15 11:30:16 -070039#include <openssl/md5.h>
Andreas Hubera44153c2010-12-03 16:12:25 -080040
41namespace android {
42
Andreas Huber9b80c2b2011-06-30 15:47:02 -070043LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid)
Andreas Huber7314fa12011-02-24 14:42:48 -080044 : mFlags(flags),
Andreas Huber9b80c2b2011-06-30 15:47:02 -070045 mUIDValid(uidValid),
46 mUID(uid),
Andreas Huber7314fa12011-02-24 14:42:48 -080047 mDataSource(new LiveDataSource),
48 mHTTPDataSource(
Andreas Huber1156dc92011-03-08 15:59:28 -080049 HTTPBase::Create(
Andreas Huber7314fa12011-02-24 14:42:48 -080050 (mFlags & kFlagIncognito)
Andreas Huber1156dc92011-03-08 15:59:28 -080051 ? HTTPBase::kFlagIncognito
Andreas Huber7314fa12011-02-24 14:42:48 -080052 : 0)),
Andreas Hubera44153c2010-12-03 16:12:25 -080053 mPrevBandwidthIndex(-1),
54 mLastPlaylistFetchTimeUs(-1),
55 mSeqNumber(-1),
56 mSeekTimeUs(-1),
57 mNumRetries(0),
58 mDurationUs(-1),
59 mSeekDone(false),
Andreas Huberab8a0ba2011-01-31 16:18:49 -080060 mDisconnectPending(false),
Andreas Huber7e43a5a2011-07-15 11:30:16 -070061 mMonitorQueueGeneration(0),
62 mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY) {
Andreas Huber9b80c2b2011-06-30 15:47:02 -070063 if (mUIDValid) {
64 mHTTPDataSource->setUID(mUID);
65 }
Andreas Hubera44153c2010-12-03 16:12:25 -080066}
67
68LiveSession::~LiveSession() {
69}
70
71sp<DataSource> LiveSession::getDataSource() {
72 return mDataSource;
73}
74
Andreas Huberad0d9c92011-04-19 11:50:27 -070075void LiveSession::connect(
76 const char *url, const KeyedVector<String8, String8> *headers) {
Andreas Hubera44153c2010-12-03 16:12:25 -080077 sp<AMessage> msg = new AMessage(kWhatConnect, id());
78 msg->setString("url", url);
Andreas Huberad0d9c92011-04-19 11:50:27 -070079
80 if (headers != NULL) {
81 msg->setPointer(
82 "headers",
83 new KeyedVector<String8, String8>(*headers));
84 }
85
Andreas Hubera44153c2010-12-03 16:12:25 -080086 msg->post();
87}
88
89void LiveSession::disconnect() {
Andreas Huberab8a0ba2011-01-31 16:18:49 -080090 Mutex::Autolock autoLock(mLock);
91 mDisconnectPending = true;
92
93 mHTTPDataSource->disconnect();
94
Andreas Hubera44153c2010-12-03 16:12:25 -080095 (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
Andreas Huberad0d9c92011-04-19 11:50:27 -0700160 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
Andreas Huber7314fa12011-02-24 14:42:48 -0800170 if (!(mFlags & kFlagIncognito)) {
171 LOGI("onConnect '%s'", url.c_str());
172 } else {
173 LOGI("onConnect <URL suppressed>");
174 }
Andreas Hubera44153c2010-12-03 16:12:25 -0800175
176 mMasterURL = url;
177
Andreas Huber7e43a5a2011-07-15 11:30:16 -0700178 bool dummy;
179 sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &dummy);
Andreas Huberab8a0ba2011-01-31 16:18:49 -0800180
181 if (playlist == NULL) {
182 LOGE("unable to fetch master playlist '%s'.", url.c_str());
183
184 mDataSource->queueEOS(ERROR_IO);
185 return;
186 }
Andreas Hubera44153c2010-12-03 16:12:25 -0800187
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);
Andreas Hubera44153c2010-12-03 16:12:25 -0800204 }
205
206 postMonitorQueue();
207}
208
209void LiveSession::onDisconnect() {
210 LOGI("onDisconnect");
211
212 mDataSource->queueEOS(ERROR_END_OF_STREAM);
Andreas Huberab8a0ba2011-01-31 16:18:49 -0800213
214 Mutex::Autolock autoLock(mLock);
215 mDisconnectPending = false;
Andreas Hubera44153c2010-12-03 16:12:25 -0800216}
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);
Andreas Huber8cb0c412011-02-17 13:35:08 -0800225 } else if (strncasecmp(url, "http://", 7)
226 && strncasecmp(url, "https://", 8)) {
Andreas Huberdf42f942010-12-21 14:36:19 -0800227 return ERROR_UNSUPPORTED;
Andreas Hubera44153c2010-12-03 16:12:25 -0800228 } else {
Andreas Huberab8a0ba2011-01-31 16:18:49 -0800229 {
230 Mutex::Autolock autoLock(mLock);
231
232 if (mDisconnectPending) {
233 return ERROR_IO;
234 }
235 }
236
Andreas Huberad0d9c92011-04-19 11:50:27 -0700237 status_t err = mHTTPDataSource->connect(
238 url, mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
Andreas Hubera44153c2010-12-03 16:12:25 -0800239
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) {
Andreas Huber20ad3a32011-01-06 17:32:00 -0800278 return n;
Andreas Hubera44153c2010-12-03 16:12:25 -0800279 }
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
Andreas Huber7e43a5a2011-07-15 11:30:16 -0700293sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) {
294 *unchanged = false;
295
Andreas Hubera44153c2010-12-03 16:12:25 -0800296 sp<ABuffer> buffer;
297 status_t err = fetchFile(url, &buffer);
298
299 if (err != OK) {
300 return NULL;
301 }
302
Andreas Huber7e43a5a2011-07-15 11:30:16 -0700303 // 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
Andreas Hubera44153c2010-12-03 16:12:25 -0800335 sp<M3UParser> playlist =
336 new M3UParser(url, buffer->data(), buffer->size());
337
338 if (playlist->initCheck() != OK) {
Andreas Huber9067e302011-06-21 11:55:34 -0700339 LOGE("failed to parse .m3u8 playlist");
340
Andreas Hubera44153c2010-12-03 16:12:25 -0800341 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
Andreas Huber7e43a5a2011-07-15 11:30:16 -0700422bool 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
Andreas Hubera44153c2010-12-03 16:12:25 -0800479void LiveSession::onDownloadNext() {
480 size_t bandwidthIndex = getBandwidthIndex();
481
Andreas Huberaea5aff2011-01-06 16:32:05 -0800482rinse_repeat:
Andreas Hubera44153c2010-12-03 16:12:25 -0800483 int64_t nowUs = ALooper::GetNowUs();
484
485 if (mLastPlaylistFetchTimeUs < 0
486 || (ssize_t)bandwidthIndex != mPrevBandwidthIndex
Andreas Huber7e43a5a2011-07-15 11:30:16 -0700487 || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) {
Andreas Hubera44153c2010-12-03 16:12:25 -0800488 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
Andreas Huber8dae4ca2011-07-15 16:53:31 -0700497 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
Andreas Huber7e43a5a2011-07-15 11:30:16 -0700503 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;
Andreas Huber6e6b1ca2010-12-16 11:31:13 -0800516 }
Andreas Hubera44153c2010-12-03 16:12:25 -0800517
518 if (firstTime) {
519 Mutex::Autolock autoLock(mLock);
520
Andreas Huber0f30bd92011-06-30 16:52:33 -0700521 if (!mPlaylist->isComplete()) {
Andreas Hubera44153c2010-12-03 16:12:25 -0800522 mDurationUs = -1;
523 } else {
Andreas Huber0f30bd92011-06-30 16:52:33 -0700524 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 }
Andreas Hubera44153c2010-12-03 16:12:25 -0800535 }
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;
Andreas Huber22fc52f2011-01-05 16:24:27 -0800548 bool bandwidthChanged = false;
Andreas Hubera44153c2010-12-03 16:12:25 -0800549
550 if (mSeekTimeUs >= 0) {
Andreas Huber0f30bd92011-06-30 16:52:33 -0700551 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));
Andreas Hubera44153c2010-12-03 16:12:25 -0800558
Andreas Huber0f30bd92011-06-30 16:52:33 -0700559 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()) {
Andreas Huber43c3e6c2011-01-05 12:17:08 -0800571 int32_t newSeqNumber = firstSeqNumberInPlaylist + index;
Andreas Hubera44153c2010-12-03 16:12:25 -0800572
Andreas Huber43c3e6c2011-01-05 12:17:08 -0800573 if (newSeqNumber != mSeqNumber) {
574 LOGI("seeking to seq no %d", newSeqNumber);
575
576 mSeqNumber = newSeqNumber;
577
578 mDataSource->reset();
579
Andreas Huber22fc52f2011-01-05 16:24:27 -0800580 // 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.
Andreas Huber43c3e6c2011-01-05 12:17:08 -0800586 explicitDiscontinuity = true;
Andreas Huber22fc52f2011-01-05 16:24:27 -0800587 bandwidthChanged = true;
Andreas Huber43c3e6c2011-01-05 12:17:08 -0800588 }
Andreas Hubera44153c2010-12-03 16:12:25 -0800589 }
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) {
Andreas Huber88b7ad42011-01-19 11:30:43 -0800612 if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) {
Andreas Huberaea5aff2011-01-06 16:32:05 -0800613 // 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
Andreas Hubera44153c2010-12-03 16:12:25 -0800623 if (!mPlaylist->isComplete()
624 && mSeqNumber > lastSeqNumberInPlaylist
625 && mNumRetries < kMaxNumRetries) {
626 ++mNumRetries;
627
628 mLastPlaylistFetchTimeUs = -1;
Andreas Huber5bc087c2010-12-23 10:27:40 -0800629 postMonitorQueue(3000000ll);
Andreas Hubera44153c2010-12-03 16:12:25 -0800630 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);
Andreas Huber6e6b1ca2010-12-16 11:31:13 -0800658 if (err != OK) {
659 LOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
660 mDataSource->queueEOS(err);
661 return;
662 }
Andreas Hubera44153c2010-12-03 16:12:25 -0800663
Andreas Huber43c3e6c2011-01-05 12:17:08 -0800664 CHECK(buffer != NULL);
665
Andreas Huber88b34892011-01-19 11:24:39 -0800666 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 }
Andreas Hubera44153c2010-12-03 16:12:25 -0800674
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
Andreas Huberbc7f5b22011-01-21 10:15:23 -0800680 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;
Andreas Hubera44153c2010-12-03 16:12:25 -0800695 }
696
Andreas Huber22fc52f2011-01-05 16:24:27 -0800697 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 }
Andreas Huber3831a062010-12-21 10:22:33 -0800706
707 if (explicitDiscontinuity || bandwidthChanged) {
Andreas Hubera44153c2010-12-03 16:12:25 -0800708 // Signal discontinuity.
709
Andreas Huber43c3e6c2011-01-05 12:17:08 -0800710 LOGI("queueing discontinuity (explicit=%d, bandwidthChanged=%d)",
711 explicitDiscontinuity, bandwidthChanged);
712
Andreas Hubera44153c2010-12-03 16:12:25 -0800713 sp<ABuffer> tmp = new ABuffer(188);
714 memset(tmp->data(), 0, tmp->size());
Andreas Huber3831a062010-12-21 10:22:33 -0800715 tmp->data()[1] = bandwidthChanged;
Andreas Hubera44153c2010-12-03 16:12:25 -0800716
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
Andreas Huber1156dc92011-03-08 15:59:28 -0800778 sp<HTTPBase> keySource =
779 HTTPBase::Create(
780 (mFlags & kFlagIncognito)
781 ? HTTPBase::kFlagIncognito
782 : 0);
783
Andreas Huber9b80c2b2011-06-30 15:47:02 -0700784 if (mUIDValid) {
785 keySource->setUID(mUID);
786 }
787
Andreas Hubera44153c2010-12-03 16:12:25 -0800788 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