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