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.
5 #include "content/renderer/media/android/media_source_delegate.h"
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"
25 using media::DemuxerStream;
26 using media::DemuxerConfigs;
27 using media::DemuxerData;
28 using blink::WebMediaPlayer;
29 using blink::WebString;
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;
37 const uint8 kVorbisPadding[] = { 0xff, 0xff, 0xff, 0xff };
43 static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log,
44 const std::string& error) {
45 media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error));
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),
60 is_video_encrypted_(false),
61 doing_browser_seek_(false),
62 browser_seek_time_(media::kNoTimestamp()),
63 expecting_regular_seek_(false),
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());
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_);
84 void MediaSourceDelegate::Destroy() {
85 DCHECK(main_loop_->BelongsToCurrentThread());
86 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
88 if (!chunk_demuxer_) {
89 DCHECK(!demuxer_client_);
94 duration_change_cb_.Reset();
95 update_network_state_cb_.Reset();
96 media_source_opened_cb_.Reset();
98 main_weak_factory_.InvalidateWeakPtrs();
99 DCHECK(!main_weak_factory_.HasWeakPtrs());
101 chunk_demuxer_->Shutdown();
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)));
111 bool MediaSourceDelegate::IsVideoEncrypted() {
112 DCHECK(main_loop_->BelongsToCurrentThread());
113 base::AutoLock auto_lock(is_video_encrypted_lock_);
114 return is_video_encrypted_;
117 void MediaSourceDelegate::StopDemuxer() {
118 DCHECK(media_loop_->BelongsToCurrentThread());
119 DCHECK(chunk_demuxer_);
121 demuxer_client_->RemoveDelegate(demuxer_client_id_);
122 demuxer_client_ = NULL;
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();
131 media_weak_factory_.InvalidateWeakPtrs();
132 DCHECK(!media_weak_factory_.HasWeakPtrs());
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)));
140 void 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;
155 chunk_demuxer_.reset(new media::ChunkDemuxer(
156 media::BindToCurrentLoop(
157 base::Bind(&MediaSourceDelegate::OnDemuxerOpened, main_weak_this_)),
158 media::BindToCurrentLoop(
159 base::Bind(&MediaSourceDelegate::OnNeedKey, main_weak_this_)),
160 base::Bind(&LogMediaSourceError, media_log_),
163 // |this| will be retained until StopDemuxer() is posted, so Unretained() is
165 media_loop_->PostTask(FROM_HERE,
166 base::Bind(&MediaSourceDelegate::InitializeDemuxer,
167 base::Unretained(this)));
170 void MediaSourceDelegate::InitializeDemuxer() {
171 DCHECK(media_loop_->BelongsToCurrentThread());
172 demuxer_client_->AddDelegate(demuxer_client_id_, this);
173 chunk_demuxer_->Initialize(this,
174 base::Bind(&MediaSourceDelegate::OnDemuxerInitDone,
175 media_weak_factory_.GetWeakPtr()),
179 const blink::WebTimeRanges& MediaSourceDelegate::Buffered() {
180 buffered_web_time_ranges_ =
181 ConvertToWebTimeRanges(buffered_time_ranges_);
182 return buffered_web_time_ranges_;
185 size_t MediaSourceDelegate::DecodedFrameCount() const {
186 return statistics_.video_frames_decoded;
189 size_t MediaSourceDelegate::DroppedFrameCount() const {
190 return statistics_.video_frames_dropped;
193 size_t MediaSourceDelegate::AudioDecodedByteCount() const {
194 return statistics_.audio_bytes_decoded;
197 size_t MediaSourceDelegate::VideoDecodedByteCount() const {
198 return statistics_.video_bytes_decoded;
201 void MediaSourceDelegate::CancelPendingSeek(const base::TimeDelta& seek_time) {
202 DCHECK(main_loop_->BelongsToCurrentThread());
203 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : "
204 << demuxer_client_id_;
210 // Remember to trivially finish any newly arriving browser seek requests
211 // that may arrive prior to the next regular seek request.
212 base::AutoLock auto_lock(seeking_lock_);
213 expecting_regular_seek_ = true;
216 // Cancel any previously expected or in-progress regular or browser seek.
217 // It is possible that we have just finished the seek, but caller does
218 // not know this yet. It is still safe to cancel in this case because the
219 // caller will always call StartWaitingForSeek() when it is notified of
220 // the finished seek.
221 chunk_demuxer_->CancelPendingSeek(seek_time);
224 void MediaSourceDelegate::StartWaitingForSeek(
225 const base::TimeDelta& seek_time) {
226 DCHECK(main_loop_->BelongsToCurrentThread());
227 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : "
228 << demuxer_client_id_;
233 bool cancel_browser_seek = false;
235 // Remember to trivially finish any newly arriving browser seek requests
236 // that may arrive prior to the next regular seek request.
237 base::AutoLock auto_lock(seeking_lock_);
238 expecting_regular_seek_ = true;
240 // Remember to cancel any in-progress browser seek.
242 DCHECK(doing_browser_seek_);
243 cancel_browser_seek = true;
247 if (cancel_browser_seek)
248 chunk_demuxer_->CancelPendingSeek(seek_time);
249 chunk_demuxer_->StartWaitingForSeek(seek_time);
252 void MediaSourceDelegate::Seek(
253 const base::TimeDelta& seek_time, bool is_browser_seek) {
254 DCHECK(media_loop_->BelongsToCurrentThread());
255 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ", "
256 << (is_browser_seek ? "browser seek" : "regular seek") << ") : "
257 << demuxer_client_id_;
259 base::TimeDelta internal_seek_time = seek_time;
261 base::AutoLock auto_lock(seeking_lock_);
264 doing_browser_seek_ = is_browser_seek;
266 if (doing_browser_seek_ && (!chunk_demuxer_ || expecting_regular_seek_)) {
267 // Trivially finish the browser seek without actually doing it. Reads will
268 // continue to be |kAborted| until the next regular seek is done. Browser
269 // seeking is not supported unless using a ChunkDemuxer; browser seeks are
270 // trivially finished if |chunk_demuxer_| is NULL.
272 doing_browser_seek_ = false;
273 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, seek_time);
277 if (doing_browser_seek_) {
278 internal_seek_time = FindBufferedBrowserSeekTime_Locked(seek_time);
279 browser_seek_time_ = internal_seek_time;
281 expecting_regular_seek_ = false;
282 browser_seek_time_ = media::kNoTimestamp();
286 // Prepare |chunk_demuxer_| for browser seek.
287 if (is_browser_seek) {
288 chunk_demuxer_->CancelPendingSeek(internal_seek_time);
289 chunk_demuxer_->StartWaitingForSeek(internal_seek_time);
292 SeekInternal(internal_seek_time);
295 void MediaSourceDelegate::SeekInternal(const base::TimeDelta& seek_time) {
296 DCHECK(media_loop_->BelongsToCurrentThread());
298 chunk_demuxer_->Seek(seek_time, base::Bind(
299 &MediaSourceDelegate::OnDemuxerSeekDone,
300 media_weak_factory_.GetWeakPtr()));
303 void MediaSourceDelegate::SetTotalBytes(int64 total_bytes) {
307 void MediaSourceDelegate::AddBufferedByteRange(int64 start, int64 end) {
311 void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start,
312 base::TimeDelta end) {
313 buffered_time_ranges_.Add(start, end);
316 void MediaSourceDelegate::SetDuration(base::TimeDelta duration) {
317 DCHECK(main_loop_->BelongsToCurrentThread());
318 DVLOG(1) << __FUNCTION__ << "(" << duration.InSecondsF() << ") : "
319 << demuxer_client_id_;
321 // Force duration change notification to be async to avoid reentrancy into
323 main_loop_->PostTask(FROM_HERE, base::Bind(
324 &MediaSourceDelegate::OnDurationChanged, main_weak_this_, duration));
327 void MediaSourceDelegate::OnDurationChanged(const base::TimeDelta& duration) {
328 DCHECK(main_loop_->BelongsToCurrentThread());
330 demuxer_client_->DurationChanged(demuxer_client_id_, duration);
331 if (!duration_change_cb_.is_null())
332 duration_change_cb_.Run(duration);
335 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type) {
336 DCHECK(media_loop_->BelongsToCurrentThread());
337 DVLOG(1) << __FUNCTION__ << "(" << type << ") : " << demuxer_client_id_;
339 return; // Drop the request during seeking.
341 DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO);
342 // The access unit size should have been initialized properly at this stage.
343 DCHECK_GT(access_unit_size_, 0u);
344 scoped_ptr<DemuxerData> data(new DemuxerData());
346 data->access_units.resize(access_unit_size_);
347 ReadFromDemuxerStream(type, data.Pass(), 0);
350 void MediaSourceDelegate::ReadFromDemuxerStream(media::DemuxerStream::Type type,
351 scoped_ptr<DemuxerData> data,
353 DCHECK(media_loop_->BelongsToCurrentThread());
354 // DemuxerStream::Read() always returns the read callback asynchronously.
355 DemuxerStream* stream =
356 (type == DemuxerStream::AUDIO) ? audio_stream_ : video_stream_;
357 stream->Read(base::Bind(
358 &MediaSourceDelegate::OnBufferReady,
359 media_weak_factory_.GetWeakPtr(), type, base::Passed(&data), index));
362 void MediaSourceDelegate::OnBufferReady(
363 media::DemuxerStream::Type type,
364 scoped_ptr<DemuxerData> data,
366 DemuxerStream::Status status,
367 const scoped_refptr<media::DecoderBuffer>& buffer) {
368 DCHECK(media_loop_->BelongsToCurrentThread());
369 DVLOG(1) << __FUNCTION__ << "(" << index << ", " << status << ", "
370 << ((!buffer || buffer->end_of_stream()) ?
371 -1 : buffer->timestamp().InMilliseconds())
372 << ") : " << demuxer_client_id_;
373 DCHECK(chunk_demuxer_);
375 // No new OnReadFromDemuxer() will be called during seeking. So this callback
376 // must be from previous OnReadFromDemuxer() call and should be ignored.
378 DVLOG(1) << __FUNCTION__ << ": Ignore previous read during seeking.";
382 bool is_audio = (type == DemuxerStream::AUDIO);
383 if (status != DemuxerStream::kAborted &&
384 index >= data->access_units.size()) {
385 LOG(ERROR) << "The internal state inconsistency onBufferReady: "
386 << (is_audio ? "Audio" : "Video") << ", index " << index
387 << ", size " << data->access_units.size()
388 << ", status " << static_cast<int>(status);
394 case DemuxerStream::kAborted:
395 DVLOG(1) << __FUNCTION__ << " : Aborted";
396 data->access_units[index].status = status;
397 data->access_units.resize(index + 1);
400 case DemuxerStream::kConfigChanged:
401 // In case of kConfigChanged, need to read decoder_config once
402 // for the next reads.
403 // TODO(kjyoun): Investigate if we need to use this new config. See
404 // http://crbug.com/255783
406 audio_stream_->audio_decoder_config();
408 gfx::Size size = video_stream_->video_decoder_config().coded_size();
409 DVLOG(1) << "Video config is changed: " << size.width() << "x"
412 data->access_units[index].status = status;
413 data->access_units.resize(index + 1);
416 case DemuxerStream::kOk:
417 data->access_units[index].status = status;
418 if (buffer->end_of_stream()) {
419 data->access_units[index].end_of_stream = true;
420 data->access_units.resize(index + 1);
423 // TODO(ycheo): We assume that the inputed stream will be decoded
425 // Need to implement this properly using MediaPlayer.OnInfoListener.
427 statistics_.audio_bytes_decoded += buffer->data_size();
429 statistics_.video_bytes_decoded += buffer->data_size();
430 statistics_.video_frames_decoded++;
432 data->access_units[index].timestamp = buffer->timestamp();
434 data->access_units[index].data.assign(
435 buffer->data(), buffer->data() + buffer->data_size());
436 // Vorbis needs 4 extra bytes padding on Android. Check
437 // NuMediaExtractor.cpp in Android source code.
438 if (is_audio && media::kCodecVorbis ==
439 audio_stream_->audio_decoder_config().codec()) {
440 data->access_units[index].data.insert(
441 data->access_units[index].data.end(), kVorbisPadding,
444 if (buffer->decrypt_config()) {
445 data->access_units[index].key_id = std::vector<char>(
446 buffer->decrypt_config()->key_id().begin(),
447 buffer->decrypt_config()->key_id().end());
448 data->access_units[index].iv = std::vector<char>(
449 buffer->decrypt_config()->iv().begin(),
450 buffer->decrypt_config()->iv().end());
451 data->access_units[index].subsamples =
452 buffer->decrypt_config()->subsamples();
454 if (++index < data->access_units.size()) {
455 ReadFromDemuxerStream(type, data.Pass(), index);
464 if (!IsSeeking() && demuxer_client_)
465 demuxer_client_->ReadFromDemuxerAck(demuxer_client_id_, *data);
468 void MediaSourceDelegate::OnDemuxerError(media::PipelineStatus status) {
469 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
470 // |update_network_state_cb_| is bound to the main thread.
471 if (status != media::PIPELINE_OK && !update_network_state_cb_.is_null())
472 update_network_state_cb_.Run(PipelineErrorToNetworkState(status));
475 void MediaSourceDelegate::AddTextStream(
476 media::DemuxerStream* /* text_stream */ ,
477 const media::TextTrackConfig& /* config */ ) {
478 // TODO(matthewjheaney): add text stream (http://crbug/322115).
482 void MediaSourceDelegate::RemoveTextStream(
483 media::DemuxerStream* /* text_stream */ ) {
484 // TODO(matthewjheaney): remove text stream (http://crbug/322115).
488 void MediaSourceDelegate::OnDemuxerInitDone(media::PipelineStatus status) {
489 DCHECK(media_loop_->BelongsToCurrentThread());
490 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
491 DCHECK(chunk_demuxer_);
493 if (status != media::PIPELINE_OK) {
494 OnDemuxerError(status);
498 audio_stream_ = chunk_demuxer_->GetStream(DemuxerStream::AUDIO);
499 video_stream_ = chunk_demuxer_->GetStream(DemuxerStream::VIDEO);
501 if (audio_stream_ && audio_stream_->audio_decoder_config().is_encrypted() &&
502 !set_decryptor_ready_cb_.is_null()) {
503 InitAudioDecryptingDemuxerStream();
504 // InitVideoDecryptingDemuxerStream() will be called in
505 // OnAudioDecryptingDemuxerStreamInitDone().
509 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted() &&
510 !set_decryptor_ready_cb_.is_null()) {
511 InitVideoDecryptingDemuxerStream();
515 // Notify demuxer ready when both streams are not encrypted.
516 is_demuxer_ready_ = true;
517 if (CanNotifyDemuxerReady())
518 NotifyDemuxerReady();
521 void MediaSourceDelegate::InitAudioDecryptingDemuxerStream() {
522 DCHECK(media_loop_->BelongsToCurrentThread());
523 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
524 DCHECK(!set_decryptor_ready_cb_.is_null());
526 audio_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
527 media_loop_, set_decryptor_ready_cb_));
528 audio_decrypting_demuxer_stream_->Initialize(
530 base::Bind(&MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone,
531 media_weak_factory_.GetWeakPtr()));
534 void MediaSourceDelegate::InitVideoDecryptingDemuxerStream() {
535 DCHECK(media_loop_->BelongsToCurrentThread());
536 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
537 DCHECK(!set_decryptor_ready_cb_.is_null());
539 video_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
540 media_loop_, set_decryptor_ready_cb_));
541 video_decrypting_demuxer_stream_->Initialize(
543 base::Bind(&MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone,
544 media_weak_factory_.GetWeakPtr()));
547 void MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone(
548 media::PipelineStatus status) {
549 DCHECK(media_loop_->BelongsToCurrentThread());
550 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
551 DCHECK(chunk_demuxer_);
553 if (status != media::PIPELINE_OK)
554 audio_decrypting_demuxer_stream_.reset();
556 audio_stream_ = audio_decrypting_demuxer_stream_.get();
558 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted()) {
559 InitVideoDecryptingDemuxerStream();
563 // Try to notify demuxer ready when audio DDS initialization finished and
564 // video is not encrypted.
565 is_demuxer_ready_ = true;
566 if (CanNotifyDemuxerReady())
567 NotifyDemuxerReady();
570 void MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone(
571 media::PipelineStatus status) {
572 DCHECK(media_loop_->BelongsToCurrentThread());
573 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
574 DCHECK(chunk_demuxer_);
576 if (status != media::PIPELINE_OK)
577 video_decrypting_demuxer_stream_.reset();
579 video_stream_ = video_decrypting_demuxer_stream_.get();
581 // Try to notify demuxer ready when video DDS initialization finished.
582 is_demuxer_ready_ = true;
583 if (CanNotifyDemuxerReady())
584 NotifyDemuxerReady();
587 void MediaSourceDelegate::OnDemuxerSeekDone(media::PipelineStatus status) {
588 DCHECK(media_loop_->BelongsToCurrentThread());
589 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
592 if (status != media::PIPELINE_OK) {
593 OnDemuxerError(status);
597 ResetAudioDecryptingDemuxerStream();
600 void MediaSourceDelegate::ResetAudioDecryptingDemuxerStream() {
601 DCHECK(media_loop_->BelongsToCurrentThread());
602 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
603 if (audio_decrypting_demuxer_stream_) {
604 audio_decrypting_demuxer_stream_->Reset(
605 base::Bind(&MediaSourceDelegate::ResetVideoDecryptingDemuxerStream,
606 media_weak_factory_.GetWeakPtr()));
610 ResetVideoDecryptingDemuxerStream();
613 void MediaSourceDelegate::ResetVideoDecryptingDemuxerStream() {
614 DCHECK(media_loop_->BelongsToCurrentThread());
615 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
616 if (video_decrypting_demuxer_stream_) {
617 video_decrypting_demuxer_stream_->Reset(base::Bind(
618 &MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams,
619 media_weak_factory_.GetWeakPtr()));
623 FinishResettingDecryptingDemuxerStreams();
626 void MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams() {
627 DCHECK(media_loop_->BelongsToCurrentThread());
628 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
630 base::AutoLock auto_lock(seeking_lock_);
633 doing_browser_seek_ = false;
634 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, browser_seek_time_);
637 void MediaSourceDelegate::OnDemuxerStopDone() {
638 DCHECK(media_loop_->BelongsToCurrentThread());
639 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
640 main_loop_->PostTask(
642 base::Bind(&MediaSourceDelegate::DeleteSelf, base::Unretained(this)));
645 void MediaSourceDelegate::DeleteSelf() {
646 DCHECK(main_loop_->BelongsToCurrentThread());
647 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
648 chunk_demuxer_.reset();
652 void MediaSourceDelegate::OnMediaConfigRequest() {
653 DCHECK(media_loop_->BelongsToCurrentThread());
654 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
655 if (CanNotifyDemuxerReady())
656 NotifyDemuxerReady();
659 bool MediaSourceDelegate::CanNotifyDemuxerReady() {
660 DCHECK(media_loop_->BelongsToCurrentThread());
661 return is_demuxer_ready_;
664 void MediaSourceDelegate::NotifyDemuxerReady() {
665 DCHECK(media_loop_->BelongsToCurrentThread());
666 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
667 DCHECK(CanNotifyDemuxerReady());
669 scoped_ptr<DemuxerConfigs> configs(new DemuxerConfigs());
671 media::AudioDecoderConfig config = audio_stream_->audio_decoder_config();
672 configs->audio_codec = config.codec();
673 configs->audio_channels =
674 media::ChannelLayoutToChannelCount(config.channel_layout());
675 configs->audio_sampling_rate = config.samples_per_second();
676 configs->is_audio_encrypted = config.is_encrypted();
677 configs->audio_extra_data = std::vector<uint8>(
678 config.extra_data(), config.extra_data() + config.extra_data_size());
681 media::VideoDecoderConfig config = video_stream_->video_decoder_config();
682 configs->video_codec = config.codec();
683 configs->video_size = config.natural_size();
684 configs->is_video_encrypted = config.is_encrypted();
685 configs->video_extra_data = std::vector<uint8>(
686 config.extra_data(), config.extra_data() + config.extra_data_size());
688 configs->duration_ms = GetDurationMs();
691 demuxer_client_->DemuxerReady(demuxer_client_id_, *configs);
693 base::AutoLock auto_lock(is_video_encrypted_lock_);
694 is_video_encrypted_ = configs->is_video_encrypted;
697 int MediaSourceDelegate::GetDurationMs() {
698 DCHECK(media_loop_->BelongsToCurrentThread());
702 double duration_ms = chunk_demuxer_->GetDuration() * 1000;
703 if (duration_ms > std::numeric_limits<int32>::max()) {
704 LOG(WARNING) << "Duration from ChunkDemuxer is too large; probably "
705 "something has gone wrong.";
706 return std::numeric_limits<int32>::max();
711 void MediaSourceDelegate::OnDemuxerOpened() {
712 DCHECK(main_loop_->BelongsToCurrentThread());
713 if (media_source_opened_cb_.is_null())
716 media_source_opened_cb_.Run(new WebMediaSourceImpl(
717 chunk_demuxer_.get(), base::Bind(&LogMediaSourceError, media_log_)));
720 void MediaSourceDelegate::OnNeedKey(const std::string& type,
721 const std::vector<uint8>& init_data) {
722 DCHECK(main_loop_->BelongsToCurrentThread());
723 if (need_key_cb_.is_null())
726 need_key_cb_.Run(type, init_data);
729 bool MediaSourceDelegate::IsSeeking() const {
730 base::AutoLock auto_lock(seeking_lock_);
734 base::TimeDelta MediaSourceDelegate::FindBufferedBrowserSeekTime_Locked(
735 const base::TimeDelta& seek_time) const {
736 seeking_lock_.AssertAcquired();
738 DCHECK(doing_browser_seek_);
739 DCHECK(chunk_demuxer_) << "Browser seek requested, but no chunk demuxer";
741 media::Ranges<base::TimeDelta> buffered =
742 chunk_demuxer_->GetBufferedRanges();
744 for (size_t i = 0; i < buffered.size(); ++i) {
745 base::TimeDelta range_start = buffered.start(i);
746 base::TimeDelta range_end = buffered.end(i);
747 if (range_start <= seek_time) {
748 if (range_end >= seek_time)
753 // If the start of the next buffered range after |seek_time| is too far
754 // into the future, do not jump forward.
755 if ((range_start - seek_time) > base::TimeDelta::FromMilliseconds(100))
758 // TODO(wolenetz): Remove possibility that this browser seek jumps
759 // into future when the requested range is unbuffered but there is some
760 // other buffered range after it. See http://crbug.com/304234.
764 // We found no range containing |seek_time| or beginning shortly after
765 // |seek_time|. While possible that such data at and beyond the player's
766 // current time have been garbage collected or removed by the web app, this is
767 // unlikely. This may cause unexpected playback stall due to seek pending an
768 // append for a GOP prior to the last GOP demuxed.
769 // TODO(wolenetz): Remove the possibility for this seek to cause unexpected
770 // player stall by replaying cached data since last keyframe in browser player
771 // rather than issuing browser seek. See http://crbug.com/304234.
775 } // namespace content