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

Commit 949f7d90 authored by Andreas Huber's avatar Andreas Huber Committed by Android (Google) Code Review
Browse files

Merge "Work to support switching transport streams mid-stream and signalling...

Merge "Work to support switching transport streams mid-stream and signalling discontinuities to the decoder." into gingerbread
parents b74941e3 4c19bf98
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ enum {

    // Not technically an error.
    INFO_FORMAT_CHANGED    = MEDIA_ERROR_BASE - 12,
    INFO_DISCONTINUITY     = MEDIA_ERROR_BASE - 13,
};

}  // namespace android
+3 −2
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ struct OMXCodec : public MediaSource,
                  public MediaBufferObserver {
    enum CreationFlags {
        kPreferSoftwareCodecs    = 1,
        kIgnoreCodecSpecificData = 2
    };
    static sp<MediaSource> Create(
            const sp<IOMX> &omx,
@@ -248,7 +249,7 @@ private:

    void dumpPortStatus(OMX_U32 portIndex);

    status_t configureCodec(const sp<MetaData> &meta);
    status_t configureCodec(const sp<MetaData> &meta, uint32_t flags);

    static uint32_t getComponentQuirks(
            const char *componentName, bool isEncoder);
+49 −3
Original line number Diff line number Diff line
@@ -561,6 +561,39 @@ void AwesomePlayer::onBufferingUpdate() {
    postBufferingEvent_l();
}

void AwesomePlayer::partial_reset_l() {
    // Only reset the video renderer and shut down the video decoder.
    // Then instantiate a new video decoder and resume video playback.

    mVideoRenderer.clear();

    if (mLastVideoBuffer) {
        mLastVideoBuffer->release();
        mLastVideoBuffer = NULL;
    }

    if (mVideoBuffer) {
        mVideoBuffer->release();
        mVideoBuffer = NULL;
    }

    {
        mVideoSource->stop();

        // The following hack is necessary to ensure that the OMX
        // component is completely released by the time we may try
        // to instantiate it again.
        wp<MediaSource> tmp = mVideoSource;
        mVideoSource.clear();
        while (tmp.promote() != NULL) {
            usleep(1000);
        }
        IPCThreadState::self()->flushCommands();
    }

    CHECK_EQ(OK, initVideoDecoder(OMXCodec::kIgnoreCodecSpecificData));
}

void AwesomePlayer::onStreamDone() {
    // Posted whenever any stream finishes playing.

@@ -570,7 +603,21 @@ void AwesomePlayer::onStreamDone() {
    }
    mStreamDoneEventPending = false;

    if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
    if (mStreamDoneStatus == INFO_DISCONTINUITY) {
        // This special status is returned because an http live stream's
        // video stream switched to a different bandwidth at this point
        // and future data may have been encoded using different parameters.
        // This requires us to shutdown the video decoder and reinstantiate
        // a fresh one.

        LOGV("INFO_DISCONTINUITY");

        CHECK(mVideoSource != NULL);

        partial_reset_l();
        postVideoEvent_l();
        return;
    } else if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
        LOGV("MEDIA_ERROR %d", mStreamDoneStatus);

        notifyListener_l(
@@ -939,8 +986,7 @@ void AwesomePlayer::setVideoSource(sp<MediaSource> source) {
    mVideoTrack = source;
}

status_t AwesomePlayer::initVideoDecoder() {
    uint32_t flags = 0;
status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {
    mVideoSource = OMXCodec::Create(
            mClient.interface(), mVideoTrack->getFormat(),
            false, // createEncoder
+67 −65
Original line number Diff line number Diff line
@@ -504,7 +504,7 @@ sp<MediaSource> OMXCodec::Create(

            observer->setCodec(codec);

            err = codec->configureCodec(meta);
            err = codec->configureCodec(meta, flags);

            if (err == OK) {
                return codec;
@@ -517,7 +517,8 @@ sp<MediaSource> OMXCodec::Create(
    return NULL;
}

status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) {
    if (!(flags & kIgnoreCodecSpecificData)) {
        uint32_t type;
        const void *data;
        size_t size;
@@ -606,6 +607,7 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
                return ERROR_UNSUPPORTED;
            }
        }
    }

    int32_t bitRate = 0;
    if (mIsEncoder) {
+173 −22
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "LiveSource"
#include <utils/Log.h>

@@ -22,18 +23,21 @@
#include "include/NuHTTPDataSource.h"

#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaDebug.h>

namespace android {

LiveSource::LiveSource(const char *url)
    : mURL(url),
    : mMasterURL(url),
      mInitCheck(NO_INIT),
      mPlaylistIndex(0),
      mLastFetchTimeUs(-1),
      mSource(new NuHTTPDataSource),
      mSourceSize(0),
      mOffsetBias(0) {
      mOffsetBias(0),
      mSignalDiscontinuity(false),
      mPrevBandwidthIndex(-1) {
    if (switchToNext()) {
        mInitCheck = OK;
    }
@@ -46,10 +50,113 @@ status_t LiveSource::initCheck() const {
    return mInitCheck;
}

bool LiveSource::loadPlaylist() {
// static
int LiveSource::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) {
    if (a->mBandwidth < b->mBandwidth) {
        return -1;
    } else if (a->mBandwidth == b->mBandwidth) {
        return 0;
    }

    return 1;
}

static double uniformRand() {
    return (double)rand() / RAND_MAX;
}

bool LiveSource::loadPlaylist(bool fetchMaster) {
    mSignalDiscontinuity = false;

    mPlaylist.clear();
    mPlaylistIndex = 0;

    if (fetchMaster) {
        mPrevBandwidthIndex = -1;

        sp<ABuffer> buffer;
        status_t err = fetchM3U(mMasterURL.c_str(), &buffer);

        if (err != OK) {
            return false;
        }

        mPlaylist = new M3UParser(
                mMasterURL.c_str(), buffer->data(), buffer->size());

        if (mPlaylist->initCheck() != OK) {
            return false;
        }

        if (mPlaylist->isVariantPlaylist()) {
            for (size_t i = 0; i < mPlaylist->size(); ++i) {
                BandwidthItem item;

                sp<AMessage> meta;
                mPlaylist->itemAt(i, &item.mURI, &meta);

                unsigned long bandwidth;
                CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth));

                mBandwidthItems.push(item);
            }
            mPlaylist.clear();

            // fall through
            if (mBandwidthItems.size() == 0) {
                return false;
            }

            mBandwidthItems.sort(SortByBandwidth);

            for (size_t i = 0; i < mBandwidthItems.size(); ++i) {
                const BandwidthItem &item = mBandwidthItems.itemAt(i);
                LOGV("item #%d: %s", i, item.mURI.c_str());
            }
        }
    }

    if (mBandwidthItems.size() > 0) {
#if 0
        // Change bandwidth at random()
        size_t index = uniformRand() * mBandwidthItems.size();
#elif 0
        // There's a 50% chance to stay on the current bandwidth and
        // a 50% chance to switch to the next higher bandwidth (wrapping around
        // to lowest)
        size_t index;
        if (uniformRand() < 0.5) {
            index = mPrevBandwidthIndex < 0 ? 0 : (size_t)mPrevBandwidthIndex;
        } else {
            if (mPrevBandwidthIndex < 0) {
                index = 0;
            } else {
                index = mPrevBandwidthIndex + 1;
                if (index == mBandwidthItems.size()) {
                    index = 0;
                }
            }
        }
#else
        // Stay on the lowest bandwidth available.
        size_t index = 0;  // Lowest bandwidth stream
#endif

        mURL = mBandwidthItems.editItemAt(index).mURI;

        if (mPrevBandwidthIndex >= 0 && (size_t)mPrevBandwidthIndex != index) {
            // If we switched streams because of bandwidth changes,
            // we'll signal this discontinuity by inserting a
            // special transport stream packet into the stream.
            mSignalDiscontinuity = true;
        }

        mPrevBandwidthIndex = index;
    } else {
        mURL = mMasterURL;
    }

    if (mPlaylist == NULL) {
        sp<ABuffer> buffer;
        status_t err = fetchM3U(mURL.c_str(), &buffer);

@@ -63,6 +170,11 @@ bool LiveSource::loadPlaylist() {
            return false;
        }

        if (mPlaylist->isVariantPlaylist()) {
            return false;
        }
    }

    if (!mPlaylist->meta()->findInt32(
                "media-sequence", &mFirstItemSequenceNumber)) {
        mFirstItemSequenceNumber = 0;
@@ -79,6 +191,8 @@ static int64_t getNowUs() {
}

bool LiveSource::switchToNext() {
    mSignalDiscontinuity = false;

    mOffsetBias += mSourceSize;
    mSourceSize = 0;

@@ -87,7 +201,7 @@ bool LiveSource::switchToNext() {
        int32_t nextSequenceNumber =
            mPlaylistIndex + mFirstItemSequenceNumber;

        if (!loadPlaylist()) {
        if (!loadPlaylist(mLastFetchTimeUs < 0)) {
            LOGE("failed to reload playlist");
            return false;
        }
@@ -111,35 +225,62 @@ bool LiveSource::switchToNext() {
    }

    AString uri;
    CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri));
    LOGI("switching to %s", uri.c_str());
    sp<AMessage> itemMeta;
    CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri, &itemMeta));
    LOGV("switching to %s", uri.c_str());

    if (mSource->connect(uri.c_str()) != OK
            || mSource->getSize(&mSourceSize) != OK) {
        return false;
    }

    int32_t val;
    if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
        mSignalDiscontinuity = true;
    }

    mPlaylistIndex++;
    return true;
}

static const ssize_t kHeaderSize = 188;

ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) {
    CHECK(offset >= mOffsetBias);
    offset -= mOffsetBias;

    if (offset >= mSourceSize) {
        CHECK_EQ(offset, mSourceSize);
    off_t delta = mSignalDiscontinuity ? kHeaderSize : 0;

        offset -= mSourceSize;
    if (offset >= mSourceSize + delta) {
        CHECK_EQ(offset, mSourceSize + delta);

        offset -= mSourceSize + delta;
        if (!switchToNext()) {
            return ERROR_END_OF_STREAM;
        }

        if (mSignalDiscontinuity) {
            LOGV("switchToNext changed streams");
        } else {
            LOGV("switchToNext stayed within the same stream");
        }

        mOffsetBias += delta;

        delta = mSignalDiscontinuity ? kHeaderSize : 0;
    }

    if (offset < delta) {
        size_t avail = delta - offset;
        memset(data, 0, avail);
        return avail;
    }

    size_t numRead = 0;
    while (numRead < size) {
        ssize_t n = mSource->readAt(
                offset + numRead, (uint8_t *)data + numRead, size - numRead);
                offset + numRead - delta,
                (uint8_t *)data + numRead, size - numRead);

        if (n <= 0) {
            break;
@@ -154,14 +295,24 @@ ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) {
status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
    *out = NULL;

    sp<DataSource> source;

    if (!strncasecmp(url, "file://", 7)) {
        source = new FileSource(url + 7);
    } else {
        CHECK(!strncasecmp(url, "http://", 7));

        status_t err = mSource->connect(url);

        if (err != OK) {
            return err;
        }

        source = mSource;
    }

    off_t size;
    err = mSource->getSize(&size);
    status_t err = source->getSize(&size);

    if (err != OK) {
        return err;
@@ -170,7 +321,7 @@ status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
    sp<ABuffer> buffer = new ABuffer(size);
    size_t offset = 0;
    while (offset < (size_t)size) {
        ssize_t n = mSource->readAt(
        ssize_t n = source->readAt(
                offset, buffer->data() + offset, size - offset);

        if (n <= 0) {
Loading