LiveSession.cpp revision 8dae4ca229d21f68f3ee76c76b49b6f0a47aad53
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        LOGI("onConnect '%s'", url.c_str());
172    } else {
173        LOGI("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        LOGE("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    LOGI("onDisconnect");
211
212    mDataSource->queueEOS(ERROR_END_OF_STREAM);
213
214    Mutex::Autolock autoLock(mLock);
215    mDisconnectPending = false;
216}
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);
225    } else if (strncasecmp(url, "http://", 7)
226            && strncasecmp(url, "https://", 8)) {
227        return ERROR_UNSUPPORTED;
228    } else {
229        {
230            Mutex::Autolock autoLock(mLock);
231
232            if (mDisconnectPending) {
233                return ERROR_IO;
234            }
235        }
236
237        status_t err = mHTTPDataSource->connect(
238                url, mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
239
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) {
278            return n;
279        }
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
293sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) {
294    *unchanged = false;
295
296    sp<ABuffer> buffer;
297    status_t err = fetchFile(url, &buffer);
298
299    if (err != OK) {
300        return NULL;
301    }
302
303    // 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
335    sp<M3UParser> playlist =
336        new M3UParser(url, buffer->data(), buffer->size());
337
338    if (playlist->initCheck() != OK) {
339        LOGE("failed to parse .m3u8 playlist");
340
341        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
422bool 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
479void LiveSession::onDownloadNext() {
480    size_t bandwidthIndex = getBandwidthIndex();
481
482rinse_repeat:
483    int64_t nowUs = ALooper::GetNowUs();
484
485    if (mLastPlaylistFetchTimeUs < 0
486            || (ssize_t)bandwidthIndex != mPrevBandwidthIndex
487            || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) {
488        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
497        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
503        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;
516        }
517
518        if (firstTime) {
519            Mutex::Autolock autoLock(mLock);
520
521            if (!mPlaylist->isComplete()) {
522                mDurationUs = -1;
523            } else {
524                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                }
535            }
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;
548    bool bandwidthChanged = false;
549
550    if (mSeekTimeUs >= 0) {
551        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));
558
559                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()) {
571                int32_t newSeqNumber = firstSeqNumberInPlaylist + index;
572
573                if (newSeqNumber != mSeqNumber) {
574                    LOGI("seeking to seq no %d", newSeqNumber);
575
576                    mSeqNumber = newSeqNumber;
577
578                    mDataSource->reset();
579
580                    // 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.
586                    explicitDiscontinuity = true;
587                    bandwidthChanged = true;
588                }
589            }
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) {
612        if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) {
613            // 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
623        if (!mPlaylist->isComplete()
624                && mSeqNumber > lastSeqNumberInPlaylist
625                && mNumRetries < kMaxNumRetries) {
626            ++mNumRetries;
627
628            mLastPlaylistFetchTimeUs = -1;
629            postMonitorQueue(3000000ll);
630            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);
658    if (err != OK) {
659        LOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
660        mDataSource->queueEOS(err);
661        return;
662    }
663
664    CHECK(buffer != NULL);
665
666    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    }
674
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
680        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;
695    }
696
697    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    }
706
707    if (explicitDiscontinuity || bandwidthChanged) {
708        // Signal discontinuity.
709
710        LOGI("queueing discontinuity (explicit=%d, bandwidthChanged=%d)",
711             explicitDiscontinuity, bandwidthChanged);
712
713        sp<ABuffer> tmp = new ABuffer(188);
714        memset(tmp->data(), 0, tmp->size());
715        tmp->data()[1] = bandwidthChanged;
716
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
778        sp<HTTPBase> keySource =
779              HTTPBase::Create(
780                  (mFlags & kFlagIncognito)
781                    ? HTTPBase::kFlagIncognito
782                    : 0);
783
784        if (mUIDValid) {
785            keySource->setUID(mUID);
786        }
787
788        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
902