Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cc0b9f13 authored by Andreas Huber's avatar Andreas Huber
Browse files

Refetch playlists according to the HLS specs, varying delay if playlists unchanged

since the previous fetch.

Change-Id: I75e167a650c65a23e284381db5203beba7be7420
related-to-bug: 4983546
parent 37ccb4b5
Loading
Loading
Loading
Loading
+111 −12
Original line number Diff line number Diff line
@@ -36,11 +36,10 @@

#include <ctype.h>
#include <openssl/aes.h>
#include <openssl/md5.h>

namespace android {

const int64_t LiveSession::kMaxPlaylistAgeUs = 15000000ll;

LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid)
    : mFlags(flags),
      mUIDValid(uidValid),
@@ -59,7 +58,8 @@ LiveSession::LiveSession(uint32_t flags, bool uidValid, uid_t uid)
      mDurationUs(-1),
      mSeekDone(false),
      mDisconnectPending(false),
      mMonitorQueueGeneration(0) {
      mMonitorQueueGeneration(0),
      mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY) {
    if (mUIDValid) {
        mHTTPDataSource->setUID(mUID);
    }
@@ -175,7 +175,8 @@ void LiveSession::onConnect(const sp<AMessage> &msg) {

    mMasterURL = url;

    sp<M3UParser> playlist = fetchPlaylist(url.c_str());
    bool dummy;
    sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &dummy);

    if (playlist == NULL) {
        LOGE("unable to fetch master playlist '%s'.", url.c_str());
@@ -289,7 +290,9 @@ status_t LiveSession::fetchFile(const char *url, sp<ABuffer> *out) {
    return OK;
}

sp<M3UParser> LiveSession::fetchPlaylist(const char *url) {
sp<M3UParser> LiveSession::fetchPlaylist(const char *url, bool *unchanged) {
    *unchanged = false;

    sp<ABuffer> buffer;
    status_t err = fetchFile(url, &buffer);

@@ -297,6 +300,38 @@ sp<M3UParser> LiveSession::fetchPlaylist(const char *url) {
        return NULL;
    }

    // MD5 functionality is not available on the simulator, treat all
    // playlists as changed.

#if defined(HAVE_ANDROID_OS)
    uint8_t hash[16];

    MD5_CTX m;
    MD5_Init(&m);
    MD5_Update(&m, buffer->data(), buffer->size());

    MD5_Final(hash, &m);

    if (mPlaylist != NULL && !memcmp(hash, mPlaylistHash, 16)) {
        // playlist unchanged

        if (mRefreshState != THIRD_UNCHANGED_RELOAD_ATTEMPT) {
            mRefreshState = (RefreshState)(mRefreshState + 1);
        }

        *unchanged = true;

        LOGV("Playlist unchanged, refresh state is now %d",
             (int)mRefreshState);

        return NULL;
    }

    memcpy(mPlaylistHash, hash, sizeof(hash));

    mRefreshState = INITIAL_MINIMUM_RELOAD_DELAY;
#endif

    sp<M3UParser> playlist =
        new M3UParser(url, buffer->data(), buffer->size());

@@ -384,6 +419,63 @@ size_t LiveSession::getBandwidthIndex() {
    return index;
}

bool LiveSession::timeToRefreshPlaylist(int64_t nowUs) const {
    if (mPlaylist == NULL) {
        CHECK_EQ((int)mRefreshState, (int)INITIAL_MINIMUM_RELOAD_DELAY);
        return true;
    }

    int32_t targetDurationSecs;
    CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));

    int64_t targetDurationUs = targetDurationSecs * 1000000ll;

    int64_t minPlaylistAgeUs;

    switch (mRefreshState) {
        case INITIAL_MINIMUM_RELOAD_DELAY:
        {
            size_t n = mPlaylist->size();
            if (n > 0) {
                sp<AMessage> itemMeta;
                CHECK(mPlaylist->itemAt(n - 1, NULL /* uri */, &itemMeta));

                int64_t itemDurationUs;
                CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));

                minPlaylistAgeUs = itemDurationUs;
                break;
            }

            // fall through
        }

        case FIRST_UNCHANGED_RELOAD_ATTEMPT:
        {
            minPlaylistAgeUs = targetDurationUs / 2;
            break;
        }

        case SECOND_UNCHANGED_RELOAD_ATTEMPT:
        {
            minPlaylistAgeUs = (targetDurationUs * 3) / 2;
            break;
        }

        case THIRD_UNCHANGED_RELOAD_ATTEMPT:
        {
            minPlaylistAgeUs = targetDurationUs * 3;
            break;
        }

        default:
            TRESPASS();
            break;
    }

    return mLastPlaylistFetchTimeUs + minPlaylistAgeUs <= nowUs;
}

void LiveSession::onDownloadNext() {
    size_t bandwidthIndex = getBandwidthIndex();

@@ -392,8 +484,7 @@ rinse_repeat:

    if (mLastPlaylistFetchTimeUs < 0
            || (ssize_t)bandwidthIndex != mPrevBandwidthIndex
            || (!mPlaylist->isComplete()
                && mLastPlaylistFetchTimeUs + kMaxPlaylistAgeUs <= nowUs)) {
            || (!mPlaylist->isComplete() && timeToRefreshPlaylist(nowUs))) {
        AString url;
        if (mBandwidthItems.size() > 0) {
            url = mBandwidthItems.editItemAt(bandwidthIndex).mURI;
@@ -403,12 +494,20 @@ rinse_repeat:

        bool firstTime = (mPlaylist == NULL);

        mPlaylist = fetchPlaylist(url.c_str());
        if (mPlaylist == NULL) {
        bool unchanged;
        sp<M3UParser> playlist = fetchPlaylist(url.c_str(), &unchanged);
        if (playlist == NULL) {
            if (unchanged) {
                // We succeeded in fetching the playlist, but it was
                // unchanged from the last time we tried.
            } else {
                LOGE("failed to load playlist at url '%s'", url.c_str());
                mDataSource->queueEOS(ERROR_IO);
                return;
            }
        } else {
            mPlaylist = playlist;
        }

        if (firstTime) {
            Mutex::Autolock autoLock(mLock);
+13 −3
Original line number Diff line number Diff line
@@ -62,8 +62,6 @@ private:
        kMaxNumRetries         = 5,
    };

    static const int64_t kMaxPlaylistAgeUs;

    enum {
        kWhatConnect        = 'conn',
        kWhatDisconnect     = 'disc',
@@ -106,6 +104,16 @@ private:

    int32_t mMonitorQueueGeneration;

    enum RefreshState {
        INITIAL_MINIMUM_RELOAD_DELAY,
        FIRST_UNCHANGED_RELOAD_ATTEMPT,
        SECOND_UNCHANGED_RELOAD_ATTEMPT,
        THIRD_UNCHANGED_RELOAD_ATTEMPT
    };
    RefreshState mRefreshState;

    uint8_t mPlaylistHash[16];

    void onConnect(const sp<AMessage> &msg);
    void onDisconnect();
    void onDownloadNext();
@@ -113,7 +121,7 @@ private:
    void onSeek(const sp<AMessage> &msg);

    status_t fetchFile(const char *url, sp<ABuffer> *out);
    sp<M3UParser> fetchPlaylist(const char *url);
    sp<M3UParser> fetchPlaylist(const char *url, bool *unchanged);
    size_t getBandwidthIndex();

    status_t decryptBuffer(
@@ -121,6 +129,8 @@ private:

    void postMonitorQueue(int64_t delayUs = 0);

    bool timeToRefreshPlaylist(int64_t nowUs) const;

    static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);

    DISALLOW_EVIL_CONSTRUCTORS(LiveSession);