LiveSession.cpp revision 29357bc2c0dd7c43ad3bd0c8e3efa4e6fd9bfd47
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        ALOGI("onConnect '%s'", url.c_str());
172    } else {
173        ALOGI("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        ALOGE("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    ALOGI("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        ALOGE("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                ALOGE("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                    ALOGI("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            ALOGI("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            ALOGI("We've missed the boat, restarting playback.");
657            mSeqNumber = lastSeqNumberInPlaylist;
658            explicitDiscontinuity = true;
659
660            // fall through
661        } else {
662            ALOGE("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        ALOGE("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        ALOGE("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        ALOGE("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        ALOGI("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        ALOGI("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        ALOGE("Unsupported cipher method '%s'", method.c_str());
800        return ERROR_UNSUPPORTED;
801    }
802
803    AString keyURI;
804    if (!itemMeta->findString("cipher-uri", &keyURI)) {
805        ALOGE("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            ALOGE("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        ALOGE("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            ALOGE("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                ALOGE("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