1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/renderer/media/android/media_source_delegate.h" 6 7 #include <limits> 8 #include <string> 9 #include <vector> 10 11 #include "base/message_loop/message_loop_proxy.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "content/renderer/media/android/renderer_demuxer_android.h" 14 #include "content/renderer/media/webmediaplayer_util.h" 15 #include "content/renderer/media/webmediasource_impl.h" 16 #include "media/base/android/demuxer_stream_player_params.h" 17 #include "media/base/bind_to_current_loop.h" 18 #include "media/base/demuxer_stream.h" 19 #include "media/base/media_log.h" 20 #include "media/filters/chunk_demuxer.h" 21 #include "media/filters/decrypting_demuxer_stream.h" 22 #include "third_party/WebKit/public/platform/WebString.h" 23 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" 24 25 using media::DemuxerStream; 26 using media::DemuxerConfigs; 27 using media::DemuxerData; 28 using blink::WebMediaPlayer; 29 using blink::WebString; 30 31 namespace { 32 33 // The size of the access unit to transfer in an IPC in case of MediaSource. 34 // 4: approximately 64ms of content in 60 fps movies. 35 const size_t kAccessUnitSizeForMediaSource = 4; 36 37 const uint8 kVorbisPadding[] = { 0xff, 0xff, 0xff, 0xff }; 38 39 } // namespace 40 41 namespace content { 42 43 static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log, 44 const std::string& error) { 45 media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error)); 46 } 47 48 MediaSourceDelegate::MediaSourceDelegate( 49 RendererDemuxerAndroid* demuxer_client, 50 int demuxer_client_id, 51 const scoped_refptr<base::MessageLoopProxy>& media_loop, 52 media::MediaLog* media_log) 53 : demuxer_client_(demuxer_client), 54 demuxer_client_id_(demuxer_client_id), 55 media_log_(media_log), 56 is_demuxer_ready_(false), 57 audio_stream_(NULL), 58 video_stream_(NULL), 59 seeking_(false), 60 is_video_encrypted_(false), 61 doing_browser_seek_(false), 62 browser_seek_time_(media::kNoTimestamp()), 63 expecting_regular_seek_(false), 64 access_unit_size_(0), 65 main_loop_(base::MessageLoopProxy::current()), 66 media_loop_(media_loop), 67 main_weak_factory_(this), 68 media_weak_factory_(this), 69 main_weak_this_(main_weak_factory_.GetWeakPtr()) { 70 DCHECK(main_loop_->BelongsToCurrentThread()); 71 } 72 73 MediaSourceDelegate::~MediaSourceDelegate() { 74 DCHECK(main_loop_->BelongsToCurrentThread()); 75 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_; 76 DCHECK(!chunk_demuxer_); 77 DCHECK(!demuxer_client_); 78 DCHECK(!audio_decrypting_demuxer_stream_); 79 DCHECK(!video_decrypting_demuxer_stream_); 80 DCHECK(!audio_stream_); 81 DCHECK(!video_stream_); 82 } 83 84 void MediaSourceDelegate::Destroy() { 85 DCHECK(main_loop_->BelongsToCurrentThread()); 86 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_; 87 88 if (!chunk_demuxer_) { 89 DCHECK(!demuxer_client_); 90 delete this; 91 return; 92 } 93 94 duration_change_cb_.Reset(); 95 update_network_state_cb_.Reset(); 96 media_source_opened_cb_.Reset(); 97 98 main_weak_factory_.InvalidateWeakPtrs(); 99 DCHECK(!main_weak_factory_.HasWeakPtrs()); 100 101 chunk_demuxer_->Shutdown(); 102 103 // |this| will be transferred to the callback StopDemuxer() and 104 // OnDemuxerStopDone(). They own |this| and OnDemuxerStopDone() will delete 105 // it when called, hence using base::Unretained(this) is safe here. 106 media_loop_->PostTask(FROM_HERE, 107 base::Bind(&MediaSourceDelegate::StopDemuxer, 108 base::Unretained(this))); 109 } 110 111 bool MediaSourceDelegate::IsVideoEncrypted() { 112 DCHECK(main_loop_->BelongsToCurrentThread()); 113 base::AutoLock auto_lock(is_video_encrypted_lock_); 114 return is_video_encrypted_; 115 } 116 117 base::Time MediaSourceDelegate::GetTimelineOffset() const { 118 DCHECK(main_loop_->BelongsToCurrentThread()); 119 if (!chunk_demuxer_) 120 return base::Time(); 121 122 return chunk_demuxer_->GetTimelineOffset(); 123 } 124 125 void MediaSourceDelegate::StopDemuxer() { 126 DCHECK(media_loop_->BelongsToCurrentThread()); 127 DCHECK(chunk_demuxer_); 128 129 demuxer_client_->RemoveDelegate(demuxer_client_id_); 130 demuxer_client_ = NULL; 131 132 audio_stream_ = NULL; 133 video_stream_ = NULL; 134 // TODO(xhwang): Figure out if we need to Reset the DDSs after Seeking or 135 // before destroying them. 136 audio_decrypting_demuxer_stream_.reset(); 137 video_decrypting_demuxer_stream_.reset(); 138 139 media_weak_factory_.InvalidateWeakPtrs(); 140 DCHECK(!media_weak_factory_.HasWeakPtrs()); 141 142 // The callback OnDemuxerStopDone() owns |this| and will delete it when 143 // called. Hence using base::Unretained(this) is safe here. 144 chunk_demuxer_->Stop(base::Bind(&MediaSourceDelegate::OnDemuxerStopDone, 145 base::Unretained(this))); 146 } 147 148 void MediaSourceDelegate::InitializeMediaSource( 149 const MediaSourceOpenedCB& media_source_opened_cb, 150 const media::Demuxer::NeedKeyCB& need_key_cb, 151 const media::SetDecryptorReadyCB& set_decryptor_ready_cb, 152 const UpdateNetworkStateCB& update_network_state_cb, 153 const DurationChangeCB& duration_change_cb) { 154 DCHECK(main_loop_->BelongsToCurrentThread()); 155 DCHECK(!media_source_opened_cb.is_null()); 156 media_source_opened_cb_ = media_source_opened_cb; 157 need_key_cb_ = need_key_cb; 158 set_decryptor_ready_cb_ = set_decryptor_ready_cb; 159 update_network_state_cb_ = media::BindToCurrentLoop(update_network_state_cb); 160 duration_change_cb_ = duration_change_cb; 161 access_unit_size_ = kAccessUnitSizeForMediaSource; 162 163 chunk_demuxer_.reset(new media::ChunkDemuxer( 164 media::BindToCurrentLoop( 165 base::Bind(&MediaSourceDelegate::OnDemuxerOpened, main_weak_this_)), 166 media::BindToCurrentLoop( 167 base::Bind(&MediaSourceDelegate::OnNeedKey, main_weak_this_)), 168 base::Bind(&LogMediaSourceError, media_log_), 169 false)); 170 171 // |this| will be retained until StopDemuxer() is posted, so Unretained() is 172 // safe here. 173 media_loop_->PostTask(FROM_HERE, 174 base::Bind(&MediaSourceDelegate::InitializeDemuxer, 175 base::Unretained(this))); 176 } 177 178 void MediaSourceDelegate::InitializeDemuxer() { 179 DCHECK(media_loop_->BelongsToCurrentThread()); 180 demuxer_client_->AddDelegate(demuxer_client_id_, this); 181 chunk_demuxer_->Initialize(this, 182 base::Bind(&MediaSourceDelegate::OnDemuxerInitDone, 183 media_weak_factory_.GetWeakPtr()), 184 false); 185 } 186 187 blink::WebTimeRanges MediaSourceDelegate::Buffered() const { 188 return ConvertToWebTimeRanges(buffered_time_ranges_); 189 } 190 191 size_t MediaSourceDelegate::DecodedFrameCount() const { 192 return statistics_.video_frames_decoded; 193 } 194 195 size_t MediaSourceDelegate::DroppedFrameCount() const { 196 return statistics_.video_frames_dropped; 197 } 198 199 size_t MediaSourceDelegate::AudioDecodedByteCount() const { 200 return statistics_.audio_bytes_decoded; 201 } 202 203 size_t MediaSourceDelegate::VideoDecodedByteCount() const { 204 return statistics_.video_bytes_decoded; 205 } 206 207 void MediaSourceDelegate::CancelPendingSeek(const base::TimeDelta& seek_time) { 208 DCHECK(main_loop_->BelongsToCurrentThread()); 209 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : " 210 << demuxer_client_id_; 211 212 if (!chunk_demuxer_) 213 return; 214 215 { 216 // Remember to trivially finish any newly arriving browser seek requests 217 // that may arrive prior to the next regular seek request. 218 base::AutoLock auto_lock(seeking_lock_); 219 expecting_regular_seek_ = true; 220 } 221 222 // Cancel any previously expected or in-progress regular or browser seek. 223 // It is possible that we have just finished the seek, but caller does 224 // not know this yet. It is still safe to cancel in this case because the 225 // caller will always call StartWaitingForSeek() when it is notified of 226 // the finished seek. 227 chunk_demuxer_->CancelPendingSeek(seek_time); 228 } 229 230 void MediaSourceDelegate::StartWaitingForSeek( 231 const base::TimeDelta& seek_time) { 232 DCHECK(main_loop_->BelongsToCurrentThread()); 233 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : " 234 << demuxer_client_id_; 235 236 if (!chunk_demuxer_) 237 return; 238 239 bool cancel_browser_seek = false; 240 { 241 // Remember to trivially finish any newly arriving browser seek requests 242 // that may arrive prior to the next regular seek request. 243 base::AutoLock auto_lock(seeking_lock_); 244 expecting_regular_seek_ = true; 245 246 // Remember to cancel any in-progress browser seek. 247 if (seeking_) { 248 DCHECK(doing_browser_seek_); 249 cancel_browser_seek = true; 250 } 251 } 252 253 if (cancel_browser_seek) 254 chunk_demuxer_->CancelPendingSeek(seek_time); 255 chunk_demuxer_->StartWaitingForSeek(seek_time); 256 } 257 258 void MediaSourceDelegate::Seek( 259 const base::TimeDelta& seek_time, bool is_browser_seek) { 260 DCHECK(media_loop_->BelongsToCurrentThread()); 261 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ", " 262 << (is_browser_seek ? "browser seek" : "regular seek") << ") : " 263 << demuxer_client_id_; 264 265 base::TimeDelta internal_seek_time = seek_time; 266 { 267 base::AutoLock auto_lock(seeking_lock_); 268 DCHECK(!seeking_); 269 seeking_ = true; 270 doing_browser_seek_ = is_browser_seek; 271 272 if (doing_browser_seek_ && (!chunk_demuxer_ || expecting_regular_seek_)) { 273 // Trivially finish the browser seek without actually doing it. Reads will 274 // continue to be |kAborted| until the next regular seek is done. Browser 275 // seeking is not supported unless using a ChunkDemuxer; browser seeks are 276 // trivially finished if |chunk_demuxer_| is NULL. 277 seeking_ = false; 278 doing_browser_seek_ = false; 279 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, seek_time); 280 return; 281 } 282 283 if (doing_browser_seek_) { 284 internal_seek_time = FindBufferedBrowserSeekTime_Locked(seek_time); 285 browser_seek_time_ = internal_seek_time; 286 } else { 287 expecting_regular_seek_ = false; 288 browser_seek_time_ = media::kNoTimestamp(); 289 } 290 } 291 292 // Prepare |chunk_demuxer_| for browser seek. 293 if (is_browser_seek) { 294 chunk_demuxer_->CancelPendingSeek(internal_seek_time); 295 chunk_demuxer_->StartWaitingForSeek(internal_seek_time); 296 } 297 298 SeekInternal(internal_seek_time); 299 } 300 301 void MediaSourceDelegate::SeekInternal(const base::TimeDelta& seek_time) { 302 DCHECK(media_loop_->BelongsToCurrentThread()); 303 DCHECK(IsSeeking()); 304 chunk_demuxer_->Seek(seek_time, base::Bind( 305 &MediaSourceDelegate::OnDemuxerSeekDone, 306 media_weak_factory_.GetWeakPtr())); 307 } 308 309 void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start, 310 base::TimeDelta end) { 311 buffered_time_ranges_.Add(start, end); 312 } 313 314 void MediaSourceDelegate::SetDuration(base::TimeDelta duration) { 315 DCHECK(main_loop_->BelongsToCurrentThread()); 316 DVLOG(1) << __FUNCTION__ << "(" << duration.InSecondsF() << ") : " 317 << demuxer_client_id_; 318 319 // Force duration change notification to be async to avoid reentrancy into 320 // ChunkDemxuer. 321 main_loop_->PostTask(FROM_HERE, base::Bind( 322 &MediaSourceDelegate::OnDurationChanged, main_weak_this_, duration)); 323 } 324 325 void MediaSourceDelegate::OnDurationChanged(const base::TimeDelta& duration) { 326 DCHECK(main_loop_->BelongsToCurrentThread()); 327 if (demuxer_client_) 328 demuxer_client_->DurationChanged(demuxer_client_id_, duration); 329 if (!duration_change_cb_.is_null()) 330 duration_change_cb_.Run(duration); 331 } 332 333 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type) { 334 DCHECK(media_loop_->BelongsToCurrentThread()); 335 DVLOG(1) << __FUNCTION__ << "(" << type << ") : " << demuxer_client_id_; 336 if (IsSeeking()) 337 return; // Drop the request during seeking. 338 339 DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO); 340 // The access unit size should have been initialized properly at this stage. 341 DCHECK_GT(access_unit_size_, 0u); 342 scoped_ptr<DemuxerData> data(new DemuxerData()); 343 data->type = type; 344 data->access_units.resize(access_unit_size_); 345 ReadFromDemuxerStream(type, data.Pass(), 0); 346 } 347 348 void MediaSourceDelegate::ReadFromDemuxerStream(media::DemuxerStream::Type type, 349 scoped_ptr<DemuxerData> data, 350 size_t index) { 351 DCHECK(media_loop_->BelongsToCurrentThread()); 352 // DemuxerStream::Read() always returns the read callback asynchronously. 353 DemuxerStream* stream = 354 (type == DemuxerStream::AUDIO) ? audio_stream_ : video_stream_; 355 stream->Read(base::Bind( 356 &MediaSourceDelegate::OnBufferReady, 357 media_weak_factory_.GetWeakPtr(), type, base::Passed(&data), index)); 358 } 359 360 void MediaSourceDelegate::OnBufferReady( 361 media::DemuxerStream::Type type, 362 scoped_ptr<DemuxerData> data, 363 size_t index, 364 DemuxerStream::Status status, 365 const scoped_refptr<media::DecoderBuffer>& buffer) { 366 DCHECK(media_loop_->BelongsToCurrentThread()); 367 DVLOG(1) << __FUNCTION__ << "(" << index << ", " << status << ", " 368 << ((!buffer || buffer->end_of_stream()) ? 369 -1 : buffer->timestamp().InMilliseconds()) 370 << ") : " << demuxer_client_id_; 371 DCHECK(chunk_demuxer_); 372 373 // No new OnReadFromDemuxer() will be called during seeking. So this callback 374 // must be from previous OnReadFromDemuxer() call and should be ignored. 375 if (IsSeeking()) { 376 DVLOG(1) << __FUNCTION__ << ": Ignore previous read during seeking."; 377 return; 378 } 379 380 bool is_audio = (type == DemuxerStream::AUDIO); 381 if (status != DemuxerStream::kAborted && 382 index >= data->access_units.size()) { 383 LOG(ERROR) << "The internal state inconsistency onBufferReady: " 384 << (is_audio ? "Audio" : "Video") << ", index " << index 385 << ", size " << data->access_units.size() 386 << ", status " << static_cast<int>(status); 387 NOTREACHED(); 388 return; 389 } 390 391 switch (status) { 392 case DemuxerStream::kAborted: 393 DVLOG(1) << __FUNCTION__ << " : Aborted"; 394 data->access_units[index].status = status; 395 data->access_units.resize(index + 1); 396 break; 397 398 case DemuxerStream::kConfigChanged: 399 CHECK((is_audio && audio_stream_) || (!is_audio && video_stream_)); 400 data->demuxer_configs.resize(1); 401 CHECK(GetDemuxerConfigFromStream(&data->demuxer_configs[0], is_audio)); 402 if (!is_audio) { 403 gfx::Size size = data->demuxer_configs[0].video_size; 404 DVLOG(1) << "Video config is changed: " << size.width() << "x" 405 << size.height(); 406 } 407 data->access_units[index].status = status; 408 data->access_units.resize(index + 1); 409 break; 410 411 case DemuxerStream::kOk: 412 data->access_units[index].status = status; 413 if (buffer->end_of_stream()) { 414 data->access_units[index].end_of_stream = true; 415 data->access_units.resize(index + 1); 416 break; 417 } 418 // TODO(ycheo): We assume that the inputed stream will be decoded 419 // right away. 420 // Need to implement this properly using MediaPlayer.OnInfoListener. 421 if (is_audio) { 422 statistics_.audio_bytes_decoded += buffer->data_size(); 423 } else { 424 statistics_.video_bytes_decoded += buffer->data_size(); 425 statistics_.video_frames_decoded++; 426 } 427 data->access_units[index].timestamp = buffer->timestamp(); 428 429 data->access_units[index].data.assign( 430 buffer->data(), buffer->data() + buffer->data_size()); 431 // Vorbis needs 4 extra bytes padding on Android. Check 432 // NuMediaExtractor.cpp in Android source code. 433 if (is_audio && media::kCodecVorbis == 434 audio_stream_->audio_decoder_config().codec()) { 435 data->access_units[index].data.insert( 436 data->access_units[index].data.end(), kVorbisPadding, 437 kVorbisPadding + 4); 438 } 439 if (buffer->decrypt_config()) { 440 data->access_units[index].key_id = std::vector<char>( 441 buffer->decrypt_config()->key_id().begin(), 442 buffer->decrypt_config()->key_id().end()); 443 data->access_units[index].iv = std::vector<char>( 444 buffer->decrypt_config()->iv().begin(), 445 buffer->decrypt_config()->iv().end()); 446 data->access_units[index].subsamples = 447 buffer->decrypt_config()->subsamples(); 448 } 449 if (++index < data->access_units.size()) { 450 ReadFromDemuxerStream(type, data.Pass(), index); 451 return; 452 } 453 break; 454 455 default: 456 NOTREACHED(); 457 } 458 459 if (!IsSeeking() && demuxer_client_) 460 demuxer_client_->ReadFromDemuxerAck(demuxer_client_id_, *data); 461 } 462 463 void MediaSourceDelegate::OnDemuxerError(media::PipelineStatus status) { 464 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_; 465 // |update_network_state_cb_| is bound to the main thread. 466 if (status != media::PIPELINE_OK && !update_network_state_cb_.is_null()) 467 update_network_state_cb_.Run(PipelineErrorToNetworkState(status)); 468 } 469 470 void MediaSourceDelegate::AddTextStream( 471 media::DemuxerStream* /* text_stream */ , 472 const media::TextTrackConfig& /* config */ ) { 473 // TODO(matthewjheaney): add text stream (http://crbug/322115). 474 NOTIMPLEMENTED(); 475 } 476 477 void MediaSourceDelegate::RemoveTextStream( 478 media::DemuxerStream* /* text_stream */ ) { 479 // TODO(matthewjheaney): remove text stream (http://crbug/322115). 480 NOTIMPLEMENTED(); 481 } 482 483 void MediaSourceDelegate::OnDemuxerInitDone(media::PipelineStatus status) { 484 DCHECK(media_loop_->BelongsToCurrentThread()); 485 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_; 486 DCHECK(chunk_demuxer_); 487 488 if (status != media::PIPELINE_OK) { 489 OnDemuxerError(status); 490 return; 491 } 492 493 audio_stream_ = chunk_demuxer_->GetStream(DemuxerStream::AUDIO); 494 video_stream_ = chunk_demuxer_->GetStream(DemuxerStream::VIDEO); 495 496 if (audio_stream_ && audio_stream_->audio_decoder_config().is_encrypted() && 497 !set_decryptor_ready_cb_.is_null()) { 498 InitAudioDecryptingDemuxerStream(); 499 // InitVideoDecryptingDemuxerStream() will be called in 500 // OnAudioDecryptingDemuxerStreamInitDone(). 501 return; 502 } 503 504 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted() && 505 !set_decryptor_ready_cb_.is_null()) { 506 InitVideoDecryptingDemuxerStream(); 507 return; 508 } 509 510 // Notify demuxer ready when both streams are not encrypted. 511 is_demuxer_ready_ = true; 512 NotifyDemuxerReady(); 513 } 514 515 void MediaSourceDelegate::InitAudioDecryptingDemuxerStream() { 516 DCHECK(media_loop_->BelongsToCurrentThread()); 517 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_; 518 DCHECK(!set_decryptor_ready_cb_.is_null()); 519 520 audio_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream( 521 media_loop_, set_decryptor_ready_cb_)); 522 audio_decrypting_demuxer_stream_->Initialize( 523 audio_stream_, 524 base::Bind(&MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone, 525 media_weak_factory_.GetWeakPtr())); 526 } 527 528 void MediaSourceDelegate::InitVideoDecryptingDemuxerStream() { 529 DCHECK(media_loop_->BelongsToCurrentThread()); 530 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_; 531 DCHECK(!set_decryptor_ready_cb_.is_null()); 532 533 video_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream( 534 media_loop_, set_decryptor_ready_cb_)); 535 video_decrypting_demuxer_stream_->Initialize( 536 video_stream_, 537 base::Bind(&MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone, 538 media_weak_factory_.GetWeakPtr())); 539 } 540 541 void MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone( 542 media::PipelineStatus status) { 543 DCHECK(media_loop_->BelongsToCurrentThread()); 544 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_; 545 DCHECK(chunk_demuxer_); 546 547 if (status != media::PIPELINE_OK) 548 audio_decrypting_demuxer_stream_.reset(); 549 else 550 audio_stream_ = audio_decrypting_demuxer_stream_.get(); 551 552 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted()) { 553 InitVideoDecryptingDemuxerStream(); 554 return; 555 } 556 557 // Try to notify demuxer ready when audio DDS initialization finished and 558 // video is not encrypted. 559 is_demuxer_ready_ = true; 560 NotifyDemuxerReady(); 561 } 562 563 void MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone( 564 media::PipelineStatus status) { 565 DCHECK(media_loop_->BelongsToCurrentThread()); 566 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_; 567 DCHECK(chunk_demuxer_); 568 569 if (status != media::PIPELINE_OK) 570 video_decrypting_demuxer_stream_.reset(); 571 else 572 video_stream_ = video_decrypting_demuxer_stream_.get(); 573 574 // Try to notify demuxer ready when video DDS initialization finished. 575 is_demuxer_ready_ = true; 576 NotifyDemuxerReady(); 577 } 578 579 void MediaSourceDelegate::OnDemuxerSeekDone(media::PipelineStatus status) { 580 DCHECK(media_loop_->BelongsToCurrentThread()); 581 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_; 582 DCHECK(IsSeeking()); 583 584 if (status != media::PIPELINE_OK) { 585 OnDemuxerError(status); 586 return; 587 } 588 589 ResetAudioDecryptingDemuxerStream(); 590 } 591 592 void MediaSourceDelegate::ResetAudioDecryptingDemuxerStream() { 593 DCHECK(media_loop_->BelongsToCurrentThread()); 594 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_; 595 if (audio_decrypting_demuxer_stream_) { 596 audio_decrypting_demuxer_stream_->Reset( 597 base::Bind(&MediaSourceDelegate::ResetVideoDecryptingDemuxerStream, 598 media_weak_factory_.GetWeakPtr())); 599 return; 600 } 601 602 ResetVideoDecryptingDemuxerStream(); 603 } 604 605 void MediaSourceDelegate::ResetVideoDecryptingDemuxerStream() { 606 DCHECK(media_loop_->BelongsToCurrentThread()); 607 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_; 608 if (video_decrypting_demuxer_stream_) { 609 video_decrypting_demuxer_stream_->Reset(base::Bind( 610 &MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams, 611 media_weak_factory_.GetWeakPtr())); 612 return; 613 } 614 615 FinishResettingDecryptingDemuxerStreams(); 616 } 617 618 void MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams() { 619 DCHECK(media_loop_->BelongsToCurrentThread()); 620 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_; 621 622 base::AutoLock auto_lock(seeking_lock_); 623 DCHECK(seeking_); 624 seeking_ = false; 625 doing_browser_seek_ = false; 626 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, browser_seek_time_); 627 } 628 629 void MediaSourceDelegate::OnDemuxerStopDone() { 630 DCHECK(media_loop_->BelongsToCurrentThread()); 631 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_; 632 main_loop_->PostTask( 633 FROM_HERE, 634 base::Bind(&MediaSourceDelegate::DeleteSelf, base::Unretained(this))); 635 } 636 637 void MediaSourceDelegate::DeleteSelf() { 638 DCHECK(main_loop_->BelongsToCurrentThread()); 639 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_; 640 chunk_demuxer_.reset(); 641 delete this; 642 } 643 644 void MediaSourceDelegate::NotifyDemuxerReady() { 645 DCHECK(media_loop_->BelongsToCurrentThread()); 646 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_; 647 DCHECK(is_demuxer_ready_); 648 649 scoped_ptr<DemuxerConfigs> configs(new DemuxerConfigs()); 650 GetDemuxerConfigFromStream(configs.get(), true); 651 GetDemuxerConfigFromStream(configs.get(), false); 652 configs->duration = GetDuration(); 653 654 if (demuxer_client_) 655 demuxer_client_->DemuxerReady(demuxer_client_id_, *configs); 656 657 base::AutoLock auto_lock(is_video_encrypted_lock_); 658 is_video_encrypted_ = configs->is_video_encrypted; 659 } 660 661 base::TimeDelta MediaSourceDelegate::GetDuration() const { 662 DCHECK(media_loop_->BelongsToCurrentThread()); 663 if (!chunk_demuxer_) 664 return media::kNoTimestamp(); 665 666 double duration = chunk_demuxer_->GetDuration(); 667 if (duration == std::numeric_limits<double>::infinity()) 668 return media::kInfiniteDuration(); 669 670 return ConvertSecondsToTimestamp(duration); 671 } 672 673 void MediaSourceDelegate::OnDemuxerOpened() { 674 DCHECK(main_loop_->BelongsToCurrentThread()); 675 if (media_source_opened_cb_.is_null()) 676 return; 677 678 media_source_opened_cb_.Run(new WebMediaSourceImpl( 679 chunk_demuxer_.get(), base::Bind(&LogMediaSourceError, media_log_))); 680 } 681 682 void MediaSourceDelegate::OnNeedKey(const std::string& type, 683 const std::vector<uint8>& init_data) { 684 DCHECK(main_loop_->BelongsToCurrentThread()); 685 if (need_key_cb_.is_null()) 686 return; 687 688 need_key_cb_.Run(type, init_data); 689 } 690 691 bool MediaSourceDelegate::IsSeeking() const { 692 base::AutoLock auto_lock(seeking_lock_); 693 return seeking_; 694 } 695 696 base::TimeDelta MediaSourceDelegate::FindBufferedBrowserSeekTime_Locked( 697 const base::TimeDelta& seek_time) const { 698 seeking_lock_.AssertAcquired(); 699 DCHECK(seeking_); 700 DCHECK(doing_browser_seek_); 701 DCHECK(chunk_demuxer_) << "Browser seek requested, but no chunk demuxer"; 702 703 media::Ranges<base::TimeDelta> buffered = 704 chunk_demuxer_->GetBufferedRanges(); 705 706 for (size_t i = 0; i < buffered.size(); ++i) { 707 base::TimeDelta range_start = buffered.start(i); 708 base::TimeDelta range_end = buffered.end(i); 709 if (range_start <= seek_time) { 710 if (range_end >= seek_time) 711 return seek_time; 712 continue; 713 } 714 715 // If the start of the next buffered range after |seek_time| is too far 716 // into the future, do not jump forward. 717 if ((range_start - seek_time) > base::TimeDelta::FromMilliseconds(100)) 718 break; 719 720 // TODO(wolenetz): Remove possibility that this browser seek jumps 721 // into future when the requested range is unbuffered but there is some 722 // other buffered range after it. See http://crbug.com/304234. 723 return range_start; 724 } 725 726 // We found no range containing |seek_time| or beginning shortly after 727 // |seek_time|. While possible that such data at and beyond the player's 728 // current time have been garbage collected or removed by the web app, this is 729 // unlikely. This may cause unexpected playback stall due to seek pending an 730 // append for a GOP prior to the last GOP demuxed. 731 // TODO(wolenetz): Remove the possibility for this seek to cause unexpected 732 // player stall by replaying cached data since last keyframe in browser player 733 // rather than issuing browser seek. See http://crbug.com/304234. 734 return seek_time; 735 } 736 737 bool MediaSourceDelegate::GetDemuxerConfigFromStream( 738 media::DemuxerConfigs* configs, bool is_audio) { 739 DCHECK(media_loop_->BelongsToCurrentThread()); 740 if (!is_demuxer_ready_) 741 return false; 742 if (is_audio && audio_stream_) { 743 media::AudioDecoderConfig config = audio_stream_->audio_decoder_config(); 744 configs->audio_codec = config.codec(); 745 configs->audio_channels = 746 media::ChannelLayoutToChannelCount(config.channel_layout()); 747 configs->audio_sampling_rate = config.samples_per_second(); 748 configs->is_audio_encrypted = config.is_encrypted(); 749 configs->audio_extra_data = std::vector<uint8>( 750 config.extra_data(), config.extra_data() + config.extra_data_size()); 751 return true; 752 } 753 if (!is_audio && video_stream_) { 754 media::VideoDecoderConfig config = video_stream_->video_decoder_config(); 755 configs->video_codec = config.codec(); 756 configs->video_size = config.natural_size(); 757 configs->is_video_encrypted = config.is_encrypted(); 758 configs->video_extra_data = std::vector<uint8>( 759 config.extra_data(), config.extra_data() + config.extra_data_size()); 760 return true; 761 } 762 return false; 763 } 764 765 } // namespace content 766