1 /* 2 * Copyright 2017 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 "HTTPLiveSource2" 19 #include <utils/Log.h> 20 21 #include "HTTPLiveSource2.h" 22 23 #include "AnotherPacketSource.h" 24 #include "LiveDataSource.h" 25 26 #include <media/MediaHTTPService.h> 27 #include <media/stagefright/foundation/ABuffer.h> 28 #include <media/stagefright/foundation/ADebug.h> 29 #include <media/stagefright/foundation/AMessage.h> 30 #include <media/stagefright/MediaErrors.h> 31 #include <media/stagefright/MetaData.h> 32 #include <media/stagefright/MediaDefs.h> 33 #include <media/stagefright/Utils.h> 34 35 // default buffer prepare/ready/underflow marks 36 static const int kReadyMarkMs = 5000; // 5 seconds 37 static const int kPrepareMarkMs = 1500; // 1.5 seconds 38 39 namespace android { 40 41 NuPlayer2::HTTPLiveSource2::HTTPLiveSource2( 42 const sp<AMessage> ¬ify, 43 const sp<MediaHTTPService> &httpService, 44 const char *url, 45 const KeyedVector<String8, String8> *headers) 46 : Source(notify), 47 mHTTPService(httpService), 48 mURL(url), 49 mFlags(0), 50 mFinalResult(OK), 51 mOffset(0), 52 mFetchSubtitleDataGeneration(0), 53 mFetchMetaDataGeneration(0), 54 mHasMetadata(false), 55 mMetadataSelected(false) { 56 mBufferingSettings.mInitialMarkMs = kPrepareMarkMs; 57 mBufferingSettings.mResumePlaybackMarkMs = kReadyMarkMs; 58 if (headers) { 59 mExtraHeaders = *headers; 60 61 ssize_t index = 62 mExtraHeaders.indexOfKey(String8("x-hide-urls-from-log")); 63 64 if (index >= 0) { 65 mFlags |= kFlagIncognito; 66 67 mExtraHeaders.removeItemsAt(index); 68 } 69 } 70 } 71 72 NuPlayer2::HTTPLiveSource2::~HTTPLiveSource2() { 73 if (mLiveSession != NULL) { 74 mLiveSession->disconnect(); 75 76 mLiveLooper->unregisterHandler(mLiveSession->id()); 77 mLiveLooper->unregisterHandler(id()); 78 mLiveLooper->stop(); 79 80 mLiveSession.clear(); 81 mLiveLooper.clear(); 82 } 83 } 84 85 status_t NuPlayer2::HTTPLiveSource2::getBufferingSettings( 86 BufferingSettings* buffering /* nonnull */) { 87 *buffering = mBufferingSettings; 88 89 return OK; 90 } 91 92 status_t NuPlayer2::HTTPLiveSource2::setBufferingSettings(const BufferingSettings& buffering) { 93 mBufferingSettings = buffering; 94 95 if (mLiveSession != NULL) { 96 mLiveSession->setBufferingSettings(mBufferingSettings); 97 } 98 99 return OK; 100 } 101 102 void NuPlayer2::HTTPLiveSource2::prepareAsync() { 103 if (mLiveLooper == NULL) { 104 mLiveLooper = new ALooper; 105 mLiveLooper->setName("http live"); 106 mLiveLooper->start(false, /* runOnCallingThread */ 107 true /* canCallJava */); 108 109 mLiveLooper->registerHandler(this); 110 } 111 112 sp<AMessage> notify = new AMessage(kWhatSessionNotify, this); 113 114 mLiveSession = new LiveSession( 115 notify, 116 (mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0, 117 mHTTPService); 118 119 mLiveLooper->registerHandler(mLiveSession); 120 121 mLiveSession->setBufferingSettings(mBufferingSettings); 122 mLiveSession->connectAsync( 123 mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders); 124 } 125 126 void NuPlayer2::HTTPLiveSource2::start() { 127 } 128 129 sp<MetaData> NuPlayer2::HTTPLiveSource2::getFormatMeta(bool audio) { 130 sp<MetaData> meta; 131 if (mLiveSession != NULL) { 132 mLiveSession->getStreamFormatMeta( 133 audio ? LiveSession::STREAMTYPE_AUDIO 134 : LiveSession::STREAMTYPE_VIDEO, 135 &meta); 136 } 137 138 return meta; 139 } 140 141 sp<AMessage> NuPlayer2::HTTPLiveSource2::getFormat(bool audio) { 142 sp<MetaData> meta; 143 status_t err = -EWOULDBLOCK; 144 if (mLiveSession != NULL) { 145 err = mLiveSession->getStreamFormatMeta( 146 audio ? LiveSession::STREAMTYPE_AUDIO 147 : LiveSession::STREAMTYPE_VIDEO, 148 &meta); 149 } 150 151 sp<AMessage> format; 152 if (err == -EWOULDBLOCK) { 153 format = new AMessage(); 154 format->setInt32("err", err); 155 return format; 156 } 157 158 if (err != OK || convertMetaDataToMessage(meta, &format) != OK) { 159 return NULL; 160 } 161 return format; 162 } 163 164 status_t NuPlayer2::HTTPLiveSource2::feedMoreTSData() { 165 return OK; 166 } 167 168 status_t NuPlayer2::HTTPLiveSource2::dequeueAccessUnit( 169 bool audio, sp<ABuffer> *accessUnit) { 170 return mLiveSession->dequeueAccessUnit( 171 audio ? LiveSession::STREAMTYPE_AUDIO 172 : LiveSession::STREAMTYPE_VIDEO, 173 accessUnit); 174 } 175 176 status_t NuPlayer2::HTTPLiveSource2::getDuration(int64_t *durationUs) { 177 return mLiveSession->getDuration(durationUs); 178 } 179 180 size_t NuPlayer2::HTTPLiveSource2::getTrackCount() const { 181 return mLiveSession->getTrackCount(); 182 } 183 184 sp<AMessage> NuPlayer2::HTTPLiveSource2::getTrackInfo(size_t trackIndex) const { 185 return mLiveSession->getTrackInfo(trackIndex); 186 } 187 188 ssize_t NuPlayer2::HTTPLiveSource2::getSelectedTrack(media_track_type type) const { 189 if (mLiveSession == NULL) { 190 return -1; 191 } else if (type == MEDIA_TRACK_TYPE_METADATA) { 192 // MEDIA_TRACK_TYPE_METADATA is always last track 193 // mMetadataSelected can only be true when mHasMetadata is true 194 return mMetadataSelected ? (mLiveSession->getTrackCount() - 1) : -1; 195 } else { 196 return mLiveSession->getSelectedTrack(type); 197 } 198 } 199 200 status_t NuPlayer2::HTTPLiveSource2::selectTrack(size_t trackIndex, bool select, int64_t /*timeUs*/) { 201 if (mLiveSession == NULL) { 202 return INVALID_OPERATION; 203 } 204 205 status_t err = INVALID_OPERATION; 206 bool postFetchMsg = false, isSub = false; 207 if (!mHasMetadata || trackIndex != mLiveSession->getTrackCount() - 1) { 208 err = mLiveSession->selectTrack(trackIndex, select); 209 postFetchMsg = select; 210 isSub = true; 211 } else { 212 // metadata track; i.e. (mHasMetadata && trackIndex == mLiveSession->getTrackCount() - 1) 213 if (mMetadataSelected && !select) { 214 err = OK; 215 } else if (!mMetadataSelected && select) { 216 postFetchMsg = true; 217 err = OK; 218 } else { 219 err = BAD_VALUE; // behave as LiveSession::selectTrack 220 } 221 222 mMetadataSelected = select; 223 } 224 225 if (err == OK) { 226 int32_t &generation = isSub ? mFetchSubtitleDataGeneration : mFetchMetaDataGeneration; 227 generation++; 228 if (postFetchMsg) { 229 int32_t what = isSub ? kWhatFetchSubtitleData : kWhatFetchMetaData; 230 sp<AMessage> msg = new AMessage(what, this); 231 msg->setInt32("generation", generation); 232 msg->post(); 233 } 234 } 235 236 // LiveSession::selectTrack returns BAD_VALUE when selecting the currently 237 // selected track, or unselecting a non-selected track. In this case it's an 238 // no-op so we return OK. 239 return (err == OK || err == BAD_VALUE) ? (status_t)OK : err; 240 } 241 242 status_t NuPlayer2::HTTPLiveSource2::seekTo(int64_t seekTimeUs, MediaPlayer2SeekMode mode) { 243 if (mLiveSession->isSeekable()) { 244 return mLiveSession->seekTo(seekTimeUs, mode); 245 } else { 246 return INVALID_OPERATION; 247 } 248 } 249 250 void NuPlayer2::HTTPLiveSource2::pollForRawData( 251 const sp<AMessage> &msg, int32_t currentGeneration, 252 LiveSession::StreamType fetchType, int32_t pushWhat) { 253 254 int32_t generation; 255 CHECK(msg->findInt32("generation", &generation)); 256 257 if (generation != currentGeneration) { 258 return; 259 } 260 261 sp<ABuffer> buffer; 262 while (mLiveSession->dequeueAccessUnit(fetchType, &buffer) == OK) { 263 264 sp<AMessage> notify = dupNotify(); 265 notify->setInt32("what", pushWhat); 266 notify->setBuffer("buffer", buffer); 267 268 int64_t timeUs, baseUs, delayUs; 269 CHECK(buffer->meta()->findInt64("baseUs", &baseUs)); 270 CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); 271 delayUs = baseUs + timeUs - ALooper::GetNowUs(); 272 273 if (fetchType == LiveSession::STREAMTYPE_SUBTITLES) { 274 notify->post(); 275 msg->post(delayUs > 0ll ? delayUs : 0ll); 276 return; 277 } else if (fetchType == LiveSession::STREAMTYPE_METADATA) { 278 if (delayUs < -1000000ll) { // 1 second 279 continue; 280 } 281 notify->post(); 282 // push all currently available metadata buffers in each invocation of pollForRawData 283 // continue; 284 } else { 285 TRESPASS(); 286 } 287 } 288 289 // try again in 1 second 290 msg->post(1000000ll); 291 } 292 293 void NuPlayer2::HTTPLiveSource2::onMessageReceived(const sp<AMessage> &msg) { 294 switch (msg->what()) { 295 case kWhatSessionNotify: 296 { 297 onSessionNotify(msg); 298 break; 299 } 300 301 case kWhatFetchSubtitleData: 302 { 303 pollForRawData( 304 msg, mFetchSubtitleDataGeneration, 305 /* fetch */ LiveSession::STREAMTYPE_SUBTITLES, 306 /* push */ kWhatSubtitleData); 307 308 break; 309 } 310 311 case kWhatFetchMetaData: 312 { 313 if (!mMetadataSelected) { 314 break; 315 } 316 317 pollForRawData( 318 msg, mFetchMetaDataGeneration, 319 /* fetch */ LiveSession::STREAMTYPE_METADATA, 320 /* push */ kWhatTimedMetaData); 321 322 break; 323 } 324 325 default: 326 Source::onMessageReceived(msg); 327 break; 328 } 329 } 330 331 void NuPlayer2::HTTPLiveSource2::onSessionNotify(const sp<AMessage> &msg) { 332 int32_t what; 333 CHECK(msg->findInt32("what", &what)); 334 335 switch (what) { 336 case LiveSession::kWhatPrepared: 337 { 338 // notify the current size here if we have it, otherwise report an initial size of (0,0) 339 sp<AMessage> format = getFormat(false /* audio */); 340 int32_t width; 341 int32_t height; 342 if (format != NULL && 343 format->findInt32("width", &width) && format->findInt32("height", &height)) { 344 notifyVideoSizeChanged(format); 345 } else { 346 notifyVideoSizeChanged(); 347 } 348 349 uint32_t flags = 0; 350 if (mLiveSession->isSeekable()) { 351 flags |= FLAG_CAN_PAUSE; 352 flags |= FLAG_CAN_SEEK; 353 flags |= FLAG_CAN_SEEK_BACKWARD; 354 flags |= FLAG_CAN_SEEK_FORWARD; 355 } 356 357 if (mLiveSession->hasDynamicDuration()) { 358 flags |= FLAG_DYNAMIC_DURATION; 359 } 360 361 notifyFlagsChanged(flags); 362 363 notifyPrepared(); 364 break; 365 } 366 367 case LiveSession::kWhatPreparationFailed: 368 { 369 status_t err; 370 CHECK(msg->findInt32("err", &err)); 371 372 notifyPrepared(err); 373 break; 374 } 375 376 case LiveSession::kWhatStreamsChanged: 377 { 378 uint32_t changedMask; 379 CHECK(msg->findInt32( 380 "changedMask", (int32_t *)&changedMask)); 381 382 bool audio = changedMask & LiveSession::STREAMTYPE_AUDIO; 383 bool video = changedMask & LiveSession::STREAMTYPE_VIDEO; 384 385 sp<AMessage> reply; 386 CHECK(msg->findMessage("reply", &reply)); 387 388 sp<AMessage> notify = dupNotify(); 389 notify->setInt32("what", kWhatQueueDecoderShutdown); 390 notify->setInt32("audio", audio); 391 notify->setInt32("video", video); 392 notify->setMessage("reply", reply); 393 notify->post(); 394 break; 395 } 396 397 case LiveSession::kWhatBufferingStart: 398 { 399 sp<AMessage> notify = dupNotify(); 400 notify->setInt32("what", kWhatPauseOnBufferingStart); 401 notify->post(); 402 break; 403 } 404 405 case LiveSession::kWhatBufferingEnd: 406 { 407 sp<AMessage> notify = dupNotify(); 408 notify->setInt32("what", kWhatResumeOnBufferingEnd); 409 notify->post(); 410 break; 411 } 412 413 414 case LiveSession::kWhatBufferingUpdate: 415 { 416 sp<AMessage> notify = dupNotify(); 417 int32_t percentage; 418 CHECK(msg->findInt32("percentage", &percentage)); 419 notify->setInt32("what", kWhatBufferingUpdate); 420 notify->setInt32("percentage", percentage); 421 notify->post(); 422 break; 423 } 424 425 case LiveSession::kWhatMetadataDetected: 426 { 427 if (!mHasMetadata) { 428 mHasMetadata = true; 429 430 sp<AMessage> notify = dupNotify(); 431 // notification without buffer triggers MEDIA2_INFO_METADATA_UPDATE 432 notify->setInt32("what", kWhatTimedMetaData); 433 notify->post(); 434 } 435 break; 436 } 437 438 case LiveSession::kWhatError: 439 { 440 break; 441 } 442 443 default: 444 TRESPASS(); 445 } 446 } 447 448 } // namespace android 449 450