LiveSession.cpp revision 3856b090cd04ba5dd4a59a12430ed724d5995909
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            ALOGV("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        ALOGV("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        ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
361    } else {
362        ALOGV("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                ALOGV("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 seekDiscontinuity = false;
548    bool explicitDiscontinuity = false;
549    bool bandwidthChanged = false;
550
551    if (mSeekTimeUs >= 0) {
552        if (mPlaylist->isComplete()) {
553            size_t index = 0;
554            int64_t segmentStartUs = 0;
555            while (index < mPlaylist->size()) {
556                sp<AMessage> itemMeta;
557                CHECK(mPlaylist->itemAt(
558                            index, NULL /* uri */, &itemMeta));
559
560                int64_t itemDurationUs;
561                CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
562
563                if (mSeekTimeUs < segmentStartUs + itemDurationUs) {
564                    break;
565                }
566
567                segmentStartUs += itemDurationUs;
568                ++index;
569            }
570
571            if (index < mPlaylist->size()) {
572                int32_t newSeqNumber = firstSeqNumberInPlaylist + index;
573
574                if (newSeqNumber != mSeqNumber) {
575                    LOGI("seeking to seq no %d", newSeqNumber);
576
577                    mSeqNumber = newSeqNumber;
578
579                    mDataSource->reset();
580
581                    // reseting the data source will have had the
582                    // side effect of discarding any previously queued
583                    // bandwidth change discontinuity.
584                    // Therefore we'll need to treat these seek
585                    // discontinuities as involving a bandwidth change
586                    // even if they aren't directly.
587                    seekDiscontinuity = true;
588                    bandwidthChanged = true;
589                }
590            }
591        }
592
593        mSeekTimeUs = -1;
594
595        Mutex::Autolock autoLock(mLock);
596        mSeekDone = true;
597        mCondition.broadcast();
598    }
599
600    if (mSeqNumber < 0) {
601        mSeqNumber = firstSeqNumberInPlaylist;
602    }
603
604    int32_t lastSeqNumberInPlaylist =
605        firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
606
607    if (mSeqNumber < firstSeqNumberInPlaylist
608            || mSeqNumber > lastSeqNumberInPlaylist) {
609        if (mPrevBandwidthIndex != (ssize_t)bandwidthIndex) {
610            // Go back to the previous bandwidth.
611
612            LOGI("new bandwidth does not have the sequence number "
613                 "we're looking for, switching back to previous bandwidth");
614
615            mLastPlaylistFetchTimeUs = -1;
616            bandwidthIndex = mPrevBandwidthIndex;
617            goto rinse_repeat;
618        }
619
620        if (!mPlaylist->isComplete() && mNumRetries < kMaxNumRetries) {
621            ++mNumRetries;
622
623            if (mSeqNumber > lastSeqNumberInPlaylist) {
624                mLastPlaylistFetchTimeUs = -1;
625                postMonitorQueue(3000000ll);
626                return;
627            }
628
629            // we've missed the boat, let's start from the lowest sequence
630            // number available and signal a discontinuity.
631
632            LOGI("We've missed the boat, restarting playback.");
633            mSeqNumber = lastSeqNumberInPlaylist;
634            explicitDiscontinuity = true;
635
636            // fall through
637        } else {
638            LOGE("Cannot find sequence number %d in playlist "
639                 "(contains %d - %d)",
640                 mSeqNumber, firstSeqNumberInPlaylist,
641                 firstSeqNumberInPlaylist + mPlaylist->size() - 1);
642
643            mDataSource->queueEOS(ERROR_END_OF_STREAM);
644            return;
645        }
646    }
647
648    mNumRetries = 0;
649
650    AString uri;
651    sp<AMessage> itemMeta;
652    CHECK(mPlaylist->itemAt(
653                mSeqNumber - firstSeqNumberInPlaylist,
654                &uri,
655                &itemMeta));
656
657    int32_t val;
658    if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
659        explicitDiscontinuity = true;
660    }
661
662    sp<ABuffer> buffer;
663    status_t err = fetchFile(uri.c_str(), &buffer);
664    if (err != OK) {
665        LOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
666        mDataSource->queueEOS(err);
667        return;
668    }
669
670    CHECK(buffer != NULL);
671
672    err = decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer);
673
674    if (err != OK) {
675        LOGE("decryptBuffer failed w/ error %d", err);
676
677        mDataSource->queueEOS(err);
678        return;
679    }
680
681    if (buffer->size() == 0 || buffer->data()[0] != 0x47) {
682        // Not a transport stream???
683
684        LOGE("This doesn't look like a transport stream...");
685
686        mBandwidthItems.removeAt(bandwidthIndex);
687
688        if (mBandwidthItems.isEmpty()) {
689            mDataSource->queueEOS(ERROR_UNSUPPORTED);
690            return;
691        }
692
693        LOGI("Retrying with a different bandwidth stream.");
694
695        mLastPlaylistFetchTimeUs = -1;
696        bandwidthIndex = getBandwidthIndex();
697        mPrevBandwidthIndex = bandwidthIndex;
698        mSeqNumber = -1;
699
700        goto rinse_repeat;
701    }
702
703    if ((size_t)mPrevBandwidthIndex != bandwidthIndex) {
704        bandwidthChanged = true;
705    }
706
707    if (mPrevBandwidthIndex < 0) {
708        // Don't signal a bandwidth change at the very beginning of
709        // playback.
710        bandwidthChanged = false;
711    }
712
713    if (seekDiscontinuity || explicitDiscontinuity || bandwidthChanged) {
714        // Signal discontinuity.
715
716        LOGI("queueing discontinuity (seek=%d, explicit=%d, bandwidthChanged=%d)",
717             seekDiscontinuity, explicitDiscontinuity, bandwidthChanged);
718
719        sp<ABuffer> tmp = new ABuffer(188);
720        memset(tmp->data(), 0, tmp->size());
721
722        // signal a 'hard' discontinuity for explicit or bandwidthChanged.
723        tmp->data()[1] = (explicitDiscontinuity || bandwidthChanged) ? 1 : 0;
724
725        mDataSource->queueBuffer(tmp);
726    }
727
728    mDataSource->queueBuffer(buffer);
729
730    mPrevBandwidthIndex = bandwidthIndex;
731    ++mSeqNumber;
732
733    postMonitorQueue();
734}
735
736void LiveSession::onMonitorQueue() {
737    if (mSeekTimeUs >= 0
738            || mDataSource->countQueuedBuffers() < kMaxNumQueuedFragments) {
739        onDownloadNext();
740    } else {
741        postMonitorQueue(1000000ll);
742    }
743}
744
745status_t LiveSession::decryptBuffer(
746        size_t playlistIndex, const sp<ABuffer> &buffer) {
747    sp<AMessage> itemMeta;
748    bool found = false;
749    AString method;
750
751    for (ssize_t i = playlistIndex; i >= 0; --i) {
752        AString uri;
753        CHECK(mPlaylist->itemAt(i, &uri, &itemMeta));
754
755        if (itemMeta->findString("cipher-method", &method)) {
756            found = true;
757            break;
758        }
759    }
760
761    if (!found) {
762        method = "NONE";
763    }
764
765    if (method == "NONE") {
766        return OK;
767    } else if (!(method == "AES-128")) {
768        LOGE("Unsupported cipher method '%s'", method.c_str());
769        return ERROR_UNSUPPORTED;
770    }
771
772    AString keyURI;
773    if (!itemMeta->findString("cipher-uri", &keyURI)) {
774        LOGE("Missing key uri");
775        return ERROR_MALFORMED;
776    }
777
778    ssize_t index = mAESKeyForURI.indexOfKey(keyURI);
779
780    sp<ABuffer> key;
781    if (index >= 0) {
782        key = mAESKeyForURI.valueAt(index);
783    } else {
784        key = new ABuffer(16);
785
786        sp<HTTPBase> keySource =
787              HTTPBase::Create(
788                  (mFlags & kFlagIncognito)
789                    ? HTTPBase::kFlagIncognito
790                    : 0);
791
792        if (mUIDValid) {
793            keySource->setUID(mUID);
794        }
795
796        status_t err =
797            keySource->connect(
798                    keyURI.c_str(),
799                    mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
800
801        if (err == OK) {
802            size_t offset = 0;
803            while (offset < 16) {
804                ssize_t n = keySource->readAt(
805                        offset, key->data() + offset, 16 - offset);
806                if (n <= 0) {
807                    err = ERROR_IO;
808                    break;
809                }
810
811                offset += n;
812            }
813        }
814
815        if (err != OK) {
816            LOGE("failed to fetch cipher key from '%s'.", keyURI.c_str());
817            return ERROR_IO;
818        }
819
820        mAESKeyForURI.add(keyURI, key);
821    }
822
823    AES_KEY aes_key;
824    if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) {
825        LOGE("failed to set AES decryption key.");
826        return UNKNOWN_ERROR;
827    }
828
829    unsigned char aes_ivec[16];
830
831    AString iv;
832    if (itemMeta->findString("cipher-iv", &iv)) {
833        if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
834                || iv.size() != 16 * 2 + 2) {
835            LOGE("malformed cipher IV '%s'.", iv.c_str());
836            return ERROR_MALFORMED;
837        }
838
839        memset(aes_ivec, 0, sizeof(aes_ivec));
840        for (size_t i = 0; i < 16; ++i) {
841            char c1 = tolower(iv.c_str()[2 + 2 * i]);
842            char c2 = tolower(iv.c_str()[3 + 2 * i]);
843            if (!isxdigit(c1) || !isxdigit(c2)) {
844                LOGE("malformed cipher IV '%s'.", iv.c_str());
845                return ERROR_MALFORMED;
846            }
847            uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
848            uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
849
850            aes_ivec[i] = nibble1 << 4 | nibble2;
851        }
852    } else {
853        memset(aes_ivec, 0, sizeof(aes_ivec));
854        aes_ivec[15] = mSeqNumber & 0xff;
855        aes_ivec[14] = (mSeqNumber >> 8) & 0xff;
856        aes_ivec[13] = (mSeqNumber >> 16) & 0xff;
857        aes_ivec[12] = (mSeqNumber >> 24) & 0xff;
858    }
859
860    AES_cbc_encrypt(
861            buffer->data(), buffer->data(), buffer->size(),
862            &aes_key, aes_ivec, AES_DECRYPT);
863
864    // hexdump(buffer->data(), buffer->size());
865
866    size_t n = buffer->size();
867    CHECK_GT(n, 0u);
868
869    size_t pad = buffer->data()[n - 1];
870
871    CHECK_GT(pad, 0u);
872    CHECK_LE(pad, 16u);
873    CHECK_GE((size_t)n, pad);
874    for (size_t i = 0; i < pad; ++i) {
875        CHECK_EQ((unsigned)buffer->data()[n - 1 - i], pad);
876    }
877
878    n -= pad;
879
880    buffer->setRange(buffer->offset(), n);
881
882    return OK;
883}
884
885void LiveSession::postMonitorQueue(int64_t delayUs) {
886    sp<AMessage> msg = new AMessage(kWhatMonitorQueue, id());
887    msg->setInt32("generation", ++mMonitorQueueGeneration);
888    msg->post(delayUs);
889}
890
891void LiveSession::onSeek(const sp<AMessage> &msg) {
892    int64_t timeUs;
893    CHECK(msg->findInt64("timeUs", &timeUs));
894
895    mSeekTimeUs = timeUs;
896    postMonitorQueue();
897}
898
899status_t LiveSession::getDuration(int64_t *durationUs) {
900    Mutex::Autolock autoLock(mLock);
901    *durationUs = mDurationUs;
902
903    return OK;
904}
905
906bool LiveSession::isSeekable() {
907    int64_t durationUs;
908    return getDuration(&durationUs) == OK && durationUs >= 0;
909}
910
911}  // namespace android
912
913