blob: 6f4b8757d40b9dee31657c3c16bb8864ae5d5c8e [file] [log] [blame]
Andreas Huberb5590842010-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 Huber5f5719e2011-03-08 15:59:28 -080026#include "include/HTTPBase.h"
Andreas Huberb5590842010-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 Hubercc0b9f12011-07-15 11:30:16 -070039#include <openssl/md5.h>
Andreas Huberb5590842010-12-03 16:12:25 -080040
41namespace android {
42
Andreas Huber603d7392011-06-30 15:47:02 -070043LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid)
Andreas Huber53182c42011-02-24 14:42:48 -080044 : mFlags(flags),
Andreas Huber603d7392011-06-30 15:47:02 -070045 mUIDValid(uidValid),
46 mUID(uid),
Andreas Huber53182c42011-02-24 14:42:48 -080047 mDataSource(new LiveDataSource),
48 mHTTPDataSource(
Andreas Huber5f5719e2011-03-08 15:59:28 -080049 HTTPBase::Create(
Andreas Huber53182c42011-02-24 14:42:48 -080050 (mFlags & kFlagIncognito)
Andreas Huber5f5719e2011-03-08 15:59:28 -080051 ? HTTPBase::kFlagIncognito
Andreas Huber53182c42011-02-24 14:42:48 -080052 : 0)),
Andreas Huberb5590842010-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 Huber17ab86c2011-01-31 16:18:49 -080060 mDisconnectPending(false),
Andreas Hubercc0b9f12011-07-15 11:30:16 -070061 mMonitorQueueGeneration(0),
62 mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY) {
Andreas Huber603d7392011-06-30 15:47:02 -070063 if (mUIDValid) {
64 mHTTPDataSource->setUID(mUID);
65 }
Andreas Huberb5590842010-12-03 16:12:25 -080066}
67
68LiveSession::~LiveSession() {
69}
70
71sp<DataSource> LiveSession::getDataSource() {
72 return mDataSource;
73}
74
Andreas Huber50874942011-04-19 11:50:27 -070075void LiveSession::connect(
76 const char *url, const KeyedVector<String8, String8> *headers) {
Andreas Huberb5590842010-12-03 16:12:25 -080077 sp<AMessage> msg = new AMessage(kWhatConnect, id());
78 msg->setString("url", url);
Andreas Huber50874942011-04-19 11:50:27 -070079
80 if (headers != NULL) {
81 msg->setPointer(
82 "headers",
83 new KeyedVector<String8, String8>(*headers));
84 }
85
Andreas Huberb5590842010-12-03 16:12:25 -080086 msg->post();
87}
88
89void LiveSession::disconnect() {
Andreas Huber17ab86c2011-01-31 16:18:49 -080090 Mutex::Autolock autoLock(mLock);
91 mDisconnectPending = true;
92
93 mHTTPDataSource->disconnect();
94
Andreas Huberb5590842010-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 Huber50874942011-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 Huber53182c42011-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 Huberb5590842010-12-03 16:12:25 -0800175
176 mMasterURL = url;
177
Andreas Hubercc0b9f12011-07-15 11:30:16 -0700178 bool dummy;
179 sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &dummy);
Andreas Huber17ab86c2011-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 Huberb5590842010-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 Huberb5590842010-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 Huber17ab86c2011-01-31 16:18:49 -0800213
214 Mutex::Autolock autoLock(mLock);
215 mDisconnectPending = false;
Andreas Huberb5590842010-12-03 16:12:25 -0800216}
217
Andreas Huber796da702011-08-08 11:20:53 -0700218status_t LiveSession::fetchFile(
219 const char *url, sp<ABuffer> *out,
220 int64_t range_offset, int64_t range_length) {
Andreas Huberb5590842010-12-03 16:12:25 -0800221 *out = NULL;
222
223 sp<DataSource> source;
224
225 if (!strncasecmp(url, "file://", 7)) {
226 source = new FileSource(url + 7);
Andreas Huber118a1502011-02-17 13:35:08 -0800227 } else if (strncasecmp(url, "http://", 7)
228 && strncasecmp(url, "https://", 8)) {
Andreas Huberc0bfdb22010-12-21 14:36:19 -0800229 return ERROR_UNSUPPORTED;
Andreas Huberb5590842010-12-03 16:12:25 -0800230 } else {
Andreas Huber17ab86c2011-01-31 16:18:49 -0800231 {
232 Mutex::Autolock autoLock(mLock);
233
234 if (mDisconnectPending) {
235 return ERROR_IO;
236 }
237 }
238
Andreas Huber796da702011-08-08 11:20:53 -0700239 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);
Andreas Huberb5590842010-12-03 16:12:25 -0800251
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
Steve Block71f2cf12011-10-20 11:56:00 +0100275 ALOGV("increasing download buffer to %d bytes",
Andreas Huberb5590842010-12-03 16:12:25 -0800276 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
Andreas Huber796da702011-08-08 11:20:53 -0700285 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
Andreas Huberb5590842010-12-03 16:12:25 -0800297 ssize_t n = source->readAt(
298 buffer->size(), buffer->data() + buffer->size(),
Andreas Huber796da702011-08-08 11:20:53 -0700299 maxBytesToRead);
Andreas Huberb5590842010-12-03 16:12:25 -0800300
301 if (n < 0) {
Andreas Huber24b3a4c2011-01-06 17:32:00 -0800302 return n;
Andreas Huberb5590842010-12-03 16:12:25 -0800303 }
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
Andreas Hubercc0b9f12011-07-15 11:30:16 -0700317sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) {
318 *unchanged = false;
319
Andreas Huberb5590842010-12-03 16:12:25 -0800320 sp<ABuffer> buffer;
321 status_t err = fetchFile(url, &buffer);
322
323 if (err != OK) {
324 return NULL;
325 }
326
Andreas Hubercc0b9f12011-07-15 11:30:16 -0700327 // 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
Steve Block71f2cf12011-10-20 11:56:00 +0100348 ALOGV("Playlist unchanged, refresh state is now %d",
Andreas Hubercc0b9f12011-07-15 11:30:16 -0700349 (int)mRefreshState);
350
351 return NULL;
352 }
353
354 memcpy(mPlaylistHash, hash, sizeof(hash));
355
356 mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY;
357#endif
358
Andreas Huberb5590842010-12-03 16:12:25 -0800359 sp<M3UParser> playlist =
360 new M3UParser(url, buffer->data(), buffer->size());
361
362 if (playlist->initCheck() != OK) {
Andreas Huber467bc252011-06-21 11:55:34 -0700363 LOGE("failed to parse .m3u8 playlist");
364
Andreas Huberb5590842010-12-03 16:12:25 -0800365 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)) {
Steve Block71f2cf12011-10-20 11:56:00 +0100384 ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
Andreas Huberb5590842010-12-03 16:12:25 -0800385 } else {
Steve Block71f2cf12011-10-20 11:56:00 +0100386 ALOGV("no bandwidth estimate.");
Andreas Huberb5590842010-12-03 16:12:25 -0800387 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) {
Steve Block71f2cf12011-10-20 11:56:00 +0100396 ALOGV("bandwidth capped to %ld bps", maxBw);
Andreas Huberb5590842010-12-03 16:12:25 -0800397 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
Andreas Hubercc0b9f12011-07-15 11:30:16 -0700446bool 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
Andreas Huberb5590842010-12-03 16:12:25 -0800503void LiveSession::onDownloadNext() {
504 size_t bandwidthIndex = getBandwidthIndex();
505
Andreas Huberba572aa2011-01-06 16:32:05 -0800506rinse_repeat:
Andreas Huberb5590842010-12-03 16:12:25 -0800507 int64_t nowUs = ALooper::GetNowUs();
508
509 if (mLastPlaylistFetchTimeUs < 0
510 || (ssize_t)bandwidthIndex != mPrevBandwidthIndex
Andreas Hubercc0b9f12011-07-15 11:30:16 -0700511 || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) {
Andreas Huberb5590842010-12-03 16:12:25 -0800512 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
Andreas Huber86806f72011-07-15 16:53:31 -0700521 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
Andreas Hubercc0b9f12011-07-15 11:30:16 -0700527 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;
Andreas Huberc4f297d2010-12-16 11:31:13 -0800540 }
Andreas Huberb5590842010-12-03 16:12:25 -0800541
542 if (firstTime) {
543 Mutex::Autolock autoLock(mLock);
544
Andreas Hubere9a41882011-06-30 16:52:33 -0700545 if (!mPlaylist->isComplete()) {
Andreas Huberb5590842010-12-03 16:12:25 -0800546 mDurationUs = -1;
547 } else {
Andreas Hubere9a41882011-06-30 16:52:33 -0700548 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 }
Andreas Huberb5590842010-12-03 16:12:25 -0800559 }
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
Andreas Huber37e477c2011-07-22 09:36:24 -0700571 bool seekDiscontinuity = false;
Andreas Huberb5590842010-12-03 16:12:25 -0800572 bool explicitDiscontinuity = false;
Andreas Huber847551c2011-01-05 16:24:27 -0800573 bool bandwidthChanged = false;
Andreas Huberb5590842010-12-03 16:12:25 -0800574
575 if (mSeekTimeUs >= 0) {
Andreas Hubere9a41882011-06-30 16:52:33 -0700576 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));
Andreas Huberb5590842010-12-03 16:12:25 -0800583
Andreas Hubere9a41882011-06-30 16:52:33 -0700584 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()) {
Andreas Huber08e10cb2011-01-05 12:17:08 -0800596 int32_t newSeqNumber = firstSeqNumberInPlaylist + index;
Andreas Huberb5590842010-12-03 16:12:25 -0800597
Andreas Huber08e10cb2011-01-05 12:17:08 -0800598 if (newSeqNumber != mSeqNumber) {
599 LOGI("seeking to seq no %d", newSeqNumber);
600
601 mSeqNumber = newSeqNumber;
602
603 mDataSource->reset();
604
Andreas Huber847551c2011-01-05 16:24:27 -0800605 // reseting the data source will have had the
606 // side effect of discarding any previously queued
607 // bandwidth change discontinuity.
Andreas Huber37e477c2011-07-22 09:36:24 -0700608 // Therefore we'll need to treat these seek
Andreas Huber847551c2011-01-05 16:24:27 -0800609 // discontinuities as involving a bandwidth change
610 // even if they aren't directly.
Andreas Huber37e477c2011-07-22 09:36:24 -0700611 seekDiscontinuity = true;
Andreas Huber847551c2011-01-05 16:24:27 -0800612 bandwidthChanged = true;
Andreas Huber08e10cb2011-01-05 12:17:08 -0800613 }
Andreas Huberb5590842010-12-03 16:12:25 -0800614 }
615 }
616
617 mSeekTimeUs = -1;
618
619 Mutex::Autolock autoLock(mLock);
620 mSeekDone = true;
621 mCondition.broadcast();
622 }
623
624 if (mSeqNumber < 0) {
Andreas Huber37e477c2011-07-22 09:36:24 -0700625 mSeqNumber = firstSeqNumberInPlaylist;
Andreas Huberb5590842010-12-03 16:12:25 -0800626 }
627
628 int32_t lastSeqNumberInPlaylist =
629 firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
630
631 if (mSeqNumber < firstSeqNumberInPlaylist
632 || mSeqNumber > lastSeqNumberInPlaylist) {
Andreas Huber283e0312011-01-19 11:30:43 -0800633 if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) {
Andreas Huberba572aa2011-01-06 16:32:05 -0800634 // 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
Andreas Huber323e3172011-08-04 12:02:47 -0700644 if (!mPlaylist->isComplete() && mNumRetries < kMaxNumRetries) {
Andreas Huberb5590842010-12-03 16:12:25 -0800645 ++mNumRetries;
646
Andreas Huber323e3172011-08-04 12:02:47 -0700647 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);
Andreas Huberb5590842010-12-03 16:12:25 -0800668 return;
669 }
Andreas Huberb5590842010-12-03 16:12:25 -0800670 }
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
Andreas Huber796da702011-08-08 11:20:53 -0700686 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
Andreas Huberb5590842010-12-03 16:12:25 -0800693 sp<ABuffer> buffer;
Andreas Huber796da702011-08-08 11:20:53 -0700694 status_t err = fetchFile(uri.c_str(), &buffer, range_offset, range_length);
Andreas Huberc4f297d2010-12-16 11:31:13 -0800695 if (err != OK) {
696 LOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
697 mDataSource->queueEOS(err);
698 return;
699 }
Andreas Huberb5590842010-12-03 16:12:25 -0800700
Andreas Huber08e10cb2011-01-05 12:17:08 -0800701 CHECK(buffer != NULL);
702
Andreas Huber0cf26162011-01-19 11:24:39 -0800703 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 }
Andreas Huberb5590842010-12-03 16:12:25 -0800711
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
Andreas Huber19922ad2011-01-21 10:15:23 -0800717 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;
Andreas Huberb5590842010-12-03 16:12:25 -0800732 }
733
Andreas Huber847551c2011-01-05 16:24:27 -0800734 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 }
Andreas Huber41c3f742010-12-21 10:22:33 -0800743
Andreas Huber37e477c2011-07-22 09:36:24 -0700744 if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) {
Andreas Huberb5590842010-12-03 16:12:25 -0800745 // Signal discontinuity.
746
Andreas Huber37e477c2011-07-22 09:36:24 -0700747 LOGI("queueing discontinuity (seek=%d, explicit=%d, bandwidthChanged=%d)",
748 seekDiscontinuity, explicitDiscontinuity, bandwidthChanged);
Andreas Huber08e10cb2011-01-05 12:17:08 -0800749
Andreas Huberb5590842010-12-03 16:12:25 -0800750 sp<ABuffer> tmp = new ABuffer(188);
751 memset(tmp->data(), 0, tmp->size());
Andreas Huber37e477c2011-07-22 09:36:24 -0700752
753 // signal a 'hard' discontinuity for explicit or bandwidthChanged.
754 tmp->data()[1] = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0;
Andreas Huberb5590842010-12-03 16:12:25 -0800755
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
Andreas Huber5f5719e2011-03-08 15:59:28 -0800817 sp<HTTPBase> keySource =
818 HTTPBase::Create(
819 (mFlags & kFlagIncognito)
820 ? HTTPBase::kFlagIncognito
821 : 0);
822
Andreas Huber603d7392011-06-30 15:47:02 -0700823 if (mUIDValid) {
824 keySource->setUID(mUID);
825 }
826
Andreas Huber90737252011-07-19 15:04:53 -0700827 status_t err =
828 keySource->connect(
829 keyURI.c_str(),
830 mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
Andreas Huberb5590842010-12-03 16:12:25 -0800831
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