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