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