Home | History | Annotate | Download | only in httplive
      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 "LiveSource"
     19 #include <utils/Log.h>
     20 
     21 #include "include/LiveSource.h"
     22 #include "include/M3UParser.h"
     23 #include "include/NuHTTPDataSource.h"
     24 
     25 #include <media/stagefright/foundation/ABuffer.h>
     26 #include <media/stagefright/FileSource.h>
     27 #include <media/stagefright/MediaDebug.h>
     28 
     29 namespace android {
     30 
     31 LiveSource::LiveSource(const char *url)
     32     : mMasterURL(url),
     33       mInitCheck(NO_INIT),
     34       mDurationUs(-1),
     35       mPlaylistIndex(0),
     36       mLastFetchTimeUs(-1),
     37       mSource(new NuHTTPDataSource),
     38       mSourceSize(0),
     39       mOffsetBias(0),
     40       mSignalDiscontinuity(false),
     41       mPrevBandwidthIndex(-1) {
     42     if (switchToNext()) {
     43         mInitCheck = OK;
     44 
     45         determineSeekability();
     46     }
     47 }
     48 
     49 LiveSource::~LiveSource() {
     50 }
     51 
     52 status_t LiveSource::initCheck() const {
     53     return mInitCheck;
     54 }
     55 
     56 // static
     57 int LiveSource::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) {
     58     if (a->mBandwidth < b->mBandwidth) {
     59         return -1;
     60     } else if (a->mBandwidth == b->mBandwidth) {
     61         return 0;
     62     }
     63 
     64     return 1;
     65 }
     66 
     67 static double uniformRand() {
     68     return (double)rand() / RAND_MAX;
     69 }
     70 
     71 bool LiveSource::loadPlaylist(bool fetchMaster) {
     72     mSignalDiscontinuity = false;
     73 
     74     mPlaylist.clear();
     75     mPlaylistIndex = 0;
     76 
     77     if (fetchMaster) {
     78         mPrevBandwidthIndex = -1;
     79 
     80         sp<ABuffer> buffer;
     81         status_t err = fetchM3U(mMasterURL.c_str(), &buffer);
     82 
     83         if (err != OK) {
     84             return false;
     85         }
     86 
     87         mPlaylist = new M3UParser(
     88                 mMasterURL.c_str(), buffer->data(), buffer->size());
     89 
     90         if (mPlaylist->initCheck() != OK) {
     91             return false;
     92         }
     93 
     94         if (mPlaylist->isVariantPlaylist()) {
     95             for (size_t i = 0; i < mPlaylist->size(); ++i) {
     96                 BandwidthItem item;
     97 
     98                 sp<AMessage> meta;
     99                 mPlaylist->itemAt(i, &item.mURI, &meta);
    100 
    101                 unsigned long bandwidth;
    102                 CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth));
    103 
    104                 mBandwidthItems.push(item);
    105             }
    106             mPlaylist.clear();
    107 
    108             // fall through
    109             if (mBandwidthItems.size() == 0) {
    110                 return false;
    111             }
    112 
    113             mBandwidthItems.sort(SortByBandwidth);
    114 
    115             for (size_t i = 0; i < mBandwidthItems.size(); ++i) {
    116                 const BandwidthItem &item = mBandwidthItems.itemAt(i);
    117                 LOGV("item #%d: %s", i, item.mURI.c_str());
    118             }
    119         }
    120     }
    121 
    122     if (mBandwidthItems.size() > 0) {
    123 #if 0
    124         // Change bandwidth at random()
    125         size_t index = uniformRand() * mBandwidthItems.size();
    126 #elif 0
    127         // There's a 50% chance to stay on the current bandwidth and
    128         // a 50% chance to switch to the next higher bandwidth (wrapping around
    129         // to lowest)
    130         size_t index;
    131         if (uniformRand() < 0.5) {
    132             index = mPrevBandwidthIndex < 0 ? 0 : (size_t)mPrevBandwidthIndex;
    133         } else {
    134             if (mPrevBandwidthIndex < 0) {
    135                 index = 0;
    136             } else {
    137                 index = mPrevBandwidthIndex + 1;
    138                 if (index == mBandwidthItems.size()) {
    139                     index = 0;
    140                 }
    141             }
    142         }
    143 #else
    144         // Stay on the lowest bandwidth available.
    145         size_t index = mBandwidthItems.size() - 1;  // Highest bandwidth stream
    146 #endif
    147 
    148         mURL = mBandwidthItems.editItemAt(index).mURI;
    149 
    150         if (mPrevBandwidthIndex >= 0 && (size_t)mPrevBandwidthIndex != index) {
    151             // If we switched streams because of bandwidth changes,
    152             // we'll signal this discontinuity by inserting a
    153             // special transport stream packet into the stream.
    154             mSignalDiscontinuity = true;
    155         }
    156 
    157         mPrevBandwidthIndex = index;
    158     } else {
    159         mURL = mMasterURL;
    160     }
    161 
    162     if (mPlaylist == NULL) {
    163         sp<ABuffer> buffer;
    164         status_t err = fetchM3U(mURL.c_str(), &buffer);
    165 
    166         if (err != OK) {
    167             return false;
    168         }
    169 
    170         mPlaylist = new M3UParser(mURL.c_str(), buffer->data(), buffer->size());
    171 
    172         if (mPlaylist->initCheck() != OK) {
    173             return false;
    174         }
    175 
    176         if (mPlaylist->isVariantPlaylist()) {
    177             return false;
    178         }
    179     }
    180 
    181     if (!mPlaylist->meta()->findInt32(
    182                 "media-sequence", &mFirstItemSequenceNumber)) {
    183         mFirstItemSequenceNumber = 0;
    184     }
    185 
    186     return true;
    187 }
    188 
    189 static int64_t getNowUs() {
    190     struct timeval tv;
    191     gettimeofday(&tv, NULL);
    192 
    193     return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
    194 }
    195 
    196 bool LiveSource::switchToNext() {
    197     mSignalDiscontinuity = false;
    198 
    199     mOffsetBias += mSourceSize;
    200     mSourceSize = 0;
    201 
    202     if (mLastFetchTimeUs < 0 || getNowUs() >= mLastFetchTimeUs + 15000000ll
    203         || mPlaylistIndex == mPlaylist->size()) {
    204         int32_t nextSequenceNumber =
    205             mPlaylistIndex + mFirstItemSequenceNumber;
    206 
    207         if (!loadPlaylist(mLastFetchTimeUs < 0)) {
    208             LOGE("failed to reload playlist");
    209             return false;
    210         }
    211 
    212         if (mLastFetchTimeUs < 0) {
    213             mPlaylistIndex = 0;
    214         } else {
    215             if (nextSequenceNumber < mFirstItemSequenceNumber
    216                     || nextSequenceNumber
    217                             >= mFirstItemSequenceNumber + (int32_t)mPlaylist->size()) {
    218                 LOGE("Cannot find sequence number %d in new playlist",
    219                      nextSequenceNumber);
    220 
    221                 return false;
    222             }
    223 
    224             mPlaylistIndex = nextSequenceNumber - mFirstItemSequenceNumber;
    225         }
    226 
    227         mLastFetchTimeUs = getNowUs();
    228     }
    229 
    230     AString uri;
    231     sp<AMessage> itemMeta;
    232     CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri, &itemMeta));
    233     LOGV("switching to %s", uri.c_str());
    234 
    235     if (mSource->connect(uri.c_str()) != OK
    236             || mSource->getSize(&mSourceSize) != OK) {
    237         return false;
    238     }
    239 
    240     int32_t val;
    241     if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
    242         mSignalDiscontinuity = true;
    243     }
    244 
    245     mPlaylistIndex++;
    246     return true;
    247 }
    248 
    249 static const ssize_t kHeaderSize = 188;
    250 
    251 ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) {
    252     CHECK(offset >= mOffsetBias);
    253     offset -= mOffsetBias;
    254 
    255     off_t delta = mSignalDiscontinuity ? kHeaderSize : 0;
    256 
    257     if (offset >= mSourceSize + delta) {
    258         CHECK_EQ(offset, mSourceSize + delta);
    259 
    260         offset -= mSourceSize + delta;
    261         if (!switchToNext()) {
    262             return ERROR_END_OF_STREAM;
    263         }
    264 
    265         if (mSignalDiscontinuity) {
    266             LOGV("switchToNext changed streams");
    267         } else {
    268             LOGV("switchToNext stayed within the same stream");
    269         }
    270 
    271         mOffsetBias += delta;
    272 
    273         delta = mSignalDiscontinuity ? kHeaderSize : 0;
    274     }
    275 
    276     if (offset < delta) {
    277         size_t avail = delta - offset;
    278         memset(data, 0, avail);
    279         return avail;
    280     }
    281 
    282     size_t numRead = 0;
    283     while (numRead < size) {
    284         ssize_t n = mSource->readAt(
    285                 offset + numRead - delta,
    286                 (uint8_t *)data + numRead, size - numRead);
    287 
    288         if (n <= 0) {
    289             break;
    290         }
    291 
    292         numRead += n;
    293     }
    294 
    295     return numRead;
    296 }
    297 
    298 status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
    299     *out = NULL;
    300 
    301     sp<DataSource> source;
    302 
    303     if (!strncasecmp(url, "file://", 7)) {
    304         source = new FileSource(url + 7);
    305     } else {
    306         CHECK(!strncasecmp(url, "http://", 7));
    307 
    308         status_t err = mSource->connect(url);
    309 
    310         if (err != OK) {
    311             return err;
    312         }
    313 
    314         source = mSource;
    315     }
    316 
    317     off_t size;
    318     status_t err = source->getSize(&size);
    319 
    320     if (err != OK) {
    321         return err;
    322     }
    323 
    324     sp<ABuffer> buffer = new ABuffer(size);
    325     size_t offset = 0;
    326     while (offset < (size_t)size) {
    327         ssize_t n = source->readAt(
    328                 offset, buffer->data() + offset, size - offset);
    329 
    330         if (n <= 0) {
    331             return ERROR_IO;
    332         }
    333 
    334         offset += n;
    335     }
    336 
    337     *out = buffer;
    338 
    339     return OK;
    340 }
    341 
    342 bool LiveSource::seekTo(int64_t seekTimeUs) {
    343     LOGV("seek to %lld us", seekTimeUs);
    344 
    345     if (!mPlaylist->isComplete()) {
    346         return false;
    347     }
    348 
    349     int32_t targetDuration;
    350     if (!mPlaylist->meta()->findInt32("target-duration", &targetDuration)) {
    351         return false;
    352     }
    353 
    354     int64_t seekTimeSecs = (seekTimeUs + 500000ll) / 1000000ll;
    355 
    356     int64_t index = seekTimeSecs / targetDuration;
    357 
    358     if (index < 0 || index >= mPlaylist->size()) {
    359         return false;
    360     }
    361 
    362     size_t newPlaylistIndex = mFirstItemSequenceNumber + index;
    363 
    364     if (newPlaylistIndex == mPlaylistIndex) {
    365         return false;
    366     }
    367 
    368     mPlaylistIndex = newPlaylistIndex;
    369 
    370     switchToNext();
    371     mOffsetBias = 0;
    372 
    373     LOGV("seeking to index %lld", index);
    374 
    375     return true;
    376 }
    377 
    378 bool LiveSource::getDuration(int64_t *durationUs) const {
    379     if (mDurationUs >= 0) {
    380         *durationUs = mDurationUs;
    381         return true;
    382     }
    383 
    384     *durationUs = 0;
    385     return false;
    386 }
    387 
    388 bool LiveSource::isSeekable() const {
    389     return mDurationUs >= 0;
    390 }
    391 
    392 void LiveSource::determineSeekability() {
    393     mDurationUs = -1;
    394 
    395     if (!mPlaylist->isComplete()) {
    396         return;
    397     }
    398 
    399     int32_t targetDuration;
    400     if (!mPlaylist->meta()->findInt32("target-duration", &targetDuration)) {
    401         return;
    402     }
    403 
    404     mDurationUs = targetDuration * 1000000ll * mPlaylist->size();
    405 }
    406 
    407 }  // namespace android
    408