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/strings/string_number_conversions.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "content/renderer/media/android/renderer_demuxer_android.h"
14 #include "media/base/android/demuxer_stream_player_params.h"
15 #include "media/base/bind_to_current_loop.h"
16 #include "media/base/demuxer_stream.h"
17 #include "media/base/media_log.h"
18 #include "media/base/timestamp_constants.h"
19 #include "media/blink/webmediaplayer_util.h"
20 #include "media/blink/webmediasource_impl.h"
21 #include "media/filters/chunk_demuxer.h"
22 #include "media/filters/decrypting_demuxer_stream.h"
23 #include "third_party/WebKit/public/platform/WebString.h"
24 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
26 using media::DemuxerStream
;
27 using media::DemuxerConfigs
;
28 using media::DemuxerData
;
29 using blink::WebMediaPlayer
;
30 using blink::WebString
;
34 // The size of the access unit to transfer in an IPC in case of MediaSource.
35 // 4: approximately 64ms of content in 60 fps movies.
36 const size_t kAccessUnitSizeForMediaSource
= 4;
38 const uint8 kVorbisPadding
[] = { 0xff, 0xff, 0xff, 0xff };
44 MediaSourceDelegate::MediaSourceDelegate(
45 RendererDemuxerAndroid
* demuxer_client
,
46 int demuxer_client_id
,
47 const scoped_refptr
<base::SingleThreadTaskRunner
>& media_task_runner
,
48 const scoped_refptr
<media::MediaLog
> media_log
)
49 : demuxer_client_(demuxer_client
),
50 demuxer_client_id_(demuxer_client_id
),
51 media_log_(media_log
),
52 is_demuxer_ready_(false),
56 is_video_encrypted_(false),
57 doing_browser_seek_(false),
58 browser_seek_time_(media::kNoTimestamp()),
59 expecting_regular_seek_(false),
61 main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
62 media_task_runner_(media_task_runner
),
63 main_weak_factory_(this),
64 media_weak_factory_(this) {
65 main_weak_this_
= main_weak_factory_
.GetWeakPtr();
66 DCHECK(main_task_runner_
->BelongsToCurrentThread());
69 MediaSourceDelegate::~MediaSourceDelegate() {
70 DCHECK(main_task_runner_
->BelongsToCurrentThread());
71 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
72 DCHECK(!chunk_demuxer_
);
73 DCHECK(!demuxer_client_
);
74 DCHECK(!audio_decrypting_demuxer_stream_
);
75 DCHECK(!video_decrypting_demuxer_stream_
);
76 DCHECK(!audio_stream_
);
77 DCHECK(!video_stream_
);
80 void MediaSourceDelegate::Stop(const base::Closure
& stop_cb
) {
81 DCHECK(main_task_runner_
->BelongsToCurrentThread());
82 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
84 if (!chunk_demuxer_
) {
85 DCHECK(!demuxer_client_
);
89 duration_change_cb_
.Reset();
90 update_network_state_cb_
.Reset();
91 media_source_opened_cb_
.Reset();
93 main_weak_factory_
.InvalidateWeakPtrs();
94 DCHECK(!main_weak_factory_
.HasWeakPtrs());
96 chunk_demuxer_
->Shutdown();
98 // Continue to stop objects on the media thread.
99 media_task_runner_
->PostTask(
102 &MediaSourceDelegate::StopDemuxer
, base::Unretained(this), stop_cb
));
105 bool MediaSourceDelegate::IsVideoEncrypted() {
106 DCHECK(main_task_runner_
->BelongsToCurrentThread());
107 base::AutoLock
auto_lock(is_video_encrypted_lock_
);
108 return is_video_encrypted_
;
111 base::Time
MediaSourceDelegate::GetTimelineOffset() const {
112 DCHECK(main_task_runner_
->BelongsToCurrentThread());
116 return chunk_demuxer_
->GetTimelineOffset();
119 void MediaSourceDelegate::StopDemuxer(const base::Closure
& stop_cb
) {
120 DVLOG(2) << __FUNCTION__
;
121 DCHECK(media_task_runner_
->BelongsToCurrentThread());
122 DCHECK(chunk_demuxer_
);
124 demuxer_client_
->RemoveDelegate(demuxer_client_id_
);
125 demuxer_client_
= NULL
;
127 audio_stream_
= NULL
;
128 video_stream_
= NULL
;
129 // TODO(xhwang): Figure out if we need to Reset the DDSs after Seeking or
130 // before destroying them.
131 audio_decrypting_demuxer_stream_
.reset();
132 video_decrypting_demuxer_stream_
.reset();
134 media_weak_factory_
.InvalidateWeakPtrs();
135 DCHECK(!media_weak_factory_
.HasWeakPtrs());
137 chunk_demuxer_
->Stop();
138 chunk_demuxer_
.reset();
140 // |this| may be destroyed at this point in time as a result of running
145 void MediaSourceDelegate::InitializeMediaSource(
146 const MediaSourceOpenedCB
& media_source_opened_cb
,
147 const media::Demuxer::EncryptedMediaInitDataCB
&
148 encrypted_media_init_data_cb
,
149 const media::SetDecryptorReadyCB
& set_decryptor_ready_cb
,
150 const UpdateNetworkStateCB
& update_network_state_cb
,
151 const DurationChangeCB
& duration_change_cb
,
152 const base::Closure
& waiting_for_decryption_key_cb
) {
153 DCHECK(main_task_runner_
->BelongsToCurrentThread());
154 DCHECK(!media_source_opened_cb
.is_null());
155 media_source_opened_cb_
= media_source_opened_cb
;
156 encrypted_media_init_data_cb_
= encrypted_media_init_data_cb
;
157 set_decryptor_ready_cb_
= media::BindToCurrentLoop(set_decryptor_ready_cb
);
158 update_network_state_cb_
= media::BindToCurrentLoop(update_network_state_cb
);
159 duration_change_cb_
= duration_change_cb
;
160 waiting_for_decryption_key_cb_
=
161 media::BindToCurrentLoop(waiting_for_decryption_key_cb
);
162 access_unit_size_
= kAccessUnitSizeForMediaSource
;
164 chunk_demuxer_
.reset(new media::ChunkDemuxer(
165 media::BindToCurrentLoop(
166 base::Bind(&MediaSourceDelegate::OnDemuxerOpened
, main_weak_this_
)),
167 media::BindToCurrentLoop(base::Bind(
168 &MediaSourceDelegate::OnEncryptedMediaInitData
, main_weak_this_
)),
171 // |this| will be retained until StopDemuxer() is posted, so Unretained() is
173 media_task_runner_
->PostTask(FROM_HERE
,
174 base::Bind(&MediaSourceDelegate::InitializeDemuxer
,
175 base::Unretained(this)));
178 void MediaSourceDelegate::InitializeDemuxer() {
179 DCHECK(media_task_runner_
->BelongsToCurrentThread());
180 demuxer_client_
->AddDelegate(demuxer_client_id_
, this);
181 chunk_demuxer_
->Initialize(this,
182 base::Bind(&MediaSourceDelegate::OnDemuxerInitDone
,
183 media_weak_factory_
.GetWeakPtr()),
187 blink::WebTimeRanges
MediaSourceDelegate::Buffered() const {
188 return media::ConvertToWebTimeRanges(buffered_time_ranges_
);
191 size_t MediaSourceDelegate::DecodedFrameCount() const {
192 return statistics_
.video_frames_decoded
;
195 size_t MediaSourceDelegate::DroppedFrameCount() const {
196 return statistics_
.video_frames_dropped
;
199 size_t MediaSourceDelegate::AudioDecodedByteCount() const {
200 return statistics_
.audio_bytes_decoded
;
203 size_t MediaSourceDelegate::VideoDecodedByteCount() const {
204 return statistics_
.video_bytes_decoded
;
207 void MediaSourceDelegate::CancelPendingSeek(const base::TimeDelta
& seek_time
) {
208 DCHECK(main_task_runner_
->BelongsToCurrentThread());
209 DVLOG(1) << __FUNCTION__
<< "(" << seek_time
.InSecondsF() << ") : "
210 << demuxer_client_id_
;
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;
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
);
230 void MediaSourceDelegate::StartWaitingForSeek(
231 const base::TimeDelta
& seek_time
) {
232 DCHECK(main_task_runner_
->BelongsToCurrentThread());
233 DVLOG(1) << __FUNCTION__
<< "(" << seek_time
.InSecondsF() << ") : "
234 << demuxer_client_id_
;
239 bool cancel_browser_seek
= false;
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;
246 // Remember to cancel any in-progress browser seek.
248 DCHECK(doing_browser_seek_
);
249 cancel_browser_seek
= true;
253 if (cancel_browser_seek
)
254 chunk_demuxer_
->CancelPendingSeek(seek_time
);
255 chunk_demuxer_
->StartWaitingForSeek(seek_time
);
258 void MediaSourceDelegate::Seek(
259 const base::TimeDelta
& seek_time
, bool is_browser_seek
) {
260 DCHECK(media_task_runner_
->BelongsToCurrentThread());
261 DVLOG(1) << __FUNCTION__
<< "(" << seek_time
.InSecondsF() << ", "
262 << (is_browser_seek
? "browser seek" : "regular seek") << ") : "
263 << demuxer_client_id_
;
265 base::TimeDelta internal_seek_time
= seek_time
;
267 base::AutoLock
auto_lock(seeking_lock_
);
270 doing_browser_seek_
= is_browser_seek
;
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.
278 doing_browser_seek_
= false;
279 demuxer_client_
->DemuxerSeekDone(demuxer_client_id_
, seek_time
);
283 if (doing_browser_seek_
) {
284 internal_seek_time
= FindBufferedBrowserSeekTime_Locked(seek_time
);
285 browser_seek_time_
= internal_seek_time
;
287 expecting_regular_seek_
= false;
288 browser_seek_time_
= media::kNoTimestamp();
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
);
298 SeekInternal(internal_seek_time
);
301 void MediaSourceDelegate::SeekInternal(const base::TimeDelta
& seek_time
) {
302 DCHECK(media_task_runner_
->BelongsToCurrentThread());
304 chunk_demuxer_
->Seek(seek_time
, base::Bind(
305 &MediaSourceDelegate::OnDemuxerSeekDone
,
306 media_weak_factory_
.GetWeakPtr()));
309 void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start
,
310 base::TimeDelta end
) {
311 buffered_time_ranges_
.Add(start
, end
);
314 void MediaSourceDelegate::SetDuration(base::TimeDelta duration
) {
315 DCHECK(main_task_runner_
->BelongsToCurrentThread());
316 DVLOG(1) << __FUNCTION__
<< "(" << duration
.InSecondsF() << ") : "
317 << demuxer_client_id_
;
319 // Force duration change notification to be async to avoid reentrancy into
321 main_task_runner_
->PostTask(FROM_HERE
, base::Bind(
322 &MediaSourceDelegate::OnDurationChanged
, main_weak_this_
, duration
));
325 void MediaSourceDelegate::OnDurationChanged(const base::TimeDelta
& duration
) {
326 DCHECK(main_task_runner_
->BelongsToCurrentThread());
328 demuxer_client_
->DurationChanged(demuxer_client_id_
, duration
);
329 if (!duration_change_cb_
.is_null())
330 duration_change_cb_
.Run(duration
);
333 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type
) {
334 DCHECK(media_task_runner_
->BelongsToCurrentThread());
335 DVLOG(1) << __FUNCTION__
<< "(" << type
<< ") : " << demuxer_client_id_
;
337 return; // Drop the request during seeking.
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());
344 data
->access_units
.resize(access_unit_size_
);
345 ReadFromDemuxerStream(type
, data
.Pass(), 0);
348 void MediaSourceDelegate::ReadFromDemuxerStream(media::DemuxerStream::Type type
,
349 scoped_ptr
<DemuxerData
> data
,
351 DCHECK(media_task_runner_
->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
));
360 void MediaSourceDelegate::OnBufferReady(
361 media::DemuxerStream::Type type
,
362 scoped_ptr
<DemuxerData
> data
,
364 DemuxerStream::Status status
,
365 const scoped_refptr
<media::DecoderBuffer
>& buffer
) {
366 DCHECK(media_task_runner_
->BelongsToCurrentThread());
367 DVLOG(1) << __FUNCTION__
<< "(" << index
<< ", " << status
<< ", "
368 << ((!buffer
.get() || buffer
->end_of_stream())
370 : buffer
->timestamp().InMilliseconds())
371 << ") : " << demuxer_client_id_
;
372 DCHECK(chunk_demuxer_
);
374 // No new OnReadFromDemuxer() will be called during seeking. So this callback
375 // must be from previous OnReadFromDemuxer() call and should be ignored.
377 DVLOG(1) << __FUNCTION__
<< ": Ignore previous read during seeking.";
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
);
393 case DemuxerStream::kAborted
:
394 DVLOG(1) << __FUNCTION__
<< " : Aborted";
395 data
->access_units
[index
].status
= status
;
396 data
->access_units
.resize(index
+ 1);
399 case DemuxerStream::kConfigChanged
:
400 CHECK((is_audio
&& audio_stream_
) || (!is_audio
&& video_stream_
));
401 data
->demuxer_configs
.resize(1);
402 CHECK(GetDemuxerConfigFromStream(&data
->demuxer_configs
[0], is_audio
));
404 gfx::Size size
= data
->demuxer_configs
[0].video_size
;
405 DVLOG(1) << "Video config is changed: " << size
.width() << "x"
408 data
->access_units
[index
].status
= status
;
409 data
->access_units
.resize(index
+ 1);
412 case DemuxerStream::kOk
:
413 data
->access_units
[index
].status
= status
;
414 if (buffer
->end_of_stream()) {
415 data
->access_units
[index
].is_end_of_stream
= true;
416 data
->access_units
.resize(index
+ 1);
419 data
->access_units
[index
].is_key_frame
= buffer
->is_key_frame();
420 // TODO(ycheo): We assume that the inputed stream will be decoded
422 // Need to implement this properly using MediaPlayer.OnInfoListener.
424 statistics_
.audio_bytes_decoded
+= buffer
->data_size();
426 statistics_
.video_bytes_decoded
+= buffer
->data_size();
427 statistics_
.video_frames_decoded
++;
429 data
->access_units
[index
].timestamp
= buffer
->timestamp();
431 data
->access_units
[index
].data
.assign(
432 buffer
->data(), buffer
->data() + buffer
->data_size());
433 // Vorbis needs 4 extra bytes padding on Android. Check
434 // NuMediaExtractor.cpp in Android source code.
435 if (is_audio
&& media::kCodecVorbis
==
436 audio_stream_
->audio_decoder_config().codec()) {
437 data
->access_units
[index
].data
.insert(
438 data
->access_units
[index
].data
.end(), kVorbisPadding
,
441 if (buffer
->decrypt_config()) {
442 data
->access_units
[index
].key_id
= std::vector
<char>(
443 buffer
->decrypt_config()->key_id().begin(),
444 buffer
->decrypt_config()->key_id().end());
445 data
->access_units
[index
].iv
= std::vector
<char>(
446 buffer
->decrypt_config()->iv().begin(),
447 buffer
->decrypt_config()->iv().end());
448 data
->access_units
[index
].subsamples
=
449 buffer
->decrypt_config()->subsamples();
451 if (++index
< data
->access_units
.size()) {
452 ReadFromDemuxerStream(type
, data
.Pass(), index
);
461 if (!IsSeeking() && demuxer_client_
)
462 demuxer_client_
->ReadFromDemuxerAck(demuxer_client_id_
, *data
);
465 void MediaSourceDelegate::OnDemuxerError(media::PipelineStatus status
) {
466 DVLOG(1) << __FUNCTION__
<< "(" << status
<< ") : " << demuxer_client_id_
;
467 // |update_network_state_cb_| is bound to the main thread.
468 if (status
!= media::PIPELINE_OK
&& !update_network_state_cb_
.is_null())
469 update_network_state_cb_
.Run(PipelineErrorToNetworkState(status
));
472 void MediaSourceDelegate::AddTextStream(
473 media::DemuxerStream
* /* text_stream */ ,
474 const media::TextTrackConfig
& /* config */ ) {
475 // TODO(matthewjheaney): add text stream (http://crbug/322115).
479 void MediaSourceDelegate::RemoveTextStream(
480 media::DemuxerStream
* /* text_stream */ ) {
481 // TODO(matthewjheaney): remove text stream (http://crbug/322115).
485 void MediaSourceDelegate::OnDemuxerInitDone(media::PipelineStatus status
) {
486 DCHECK(media_task_runner_
->BelongsToCurrentThread());
487 DVLOG(1) << __FUNCTION__
<< "(" << status
<< ") : " << demuxer_client_id_
;
488 DCHECK(chunk_demuxer_
);
490 if (status
!= media::PIPELINE_OK
) {
491 OnDemuxerError(status
);
495 audio_stream_
= chunk_demuxer_
->GetStream(DemuxerStream::AUDIO
);
496 video_stream_
= chunk_demuxer_
->GetStream(DemuxerStream::VIDEO
);
498 if (audio_stream_
&& audio_stream_
->audio_decoder_config().is_encrypted() &&
499 !set_decryptor_ready_cb_
.is_null()) {
500 InitAudioDecryptingDemuxerStream();
501 // InitVideoDecryptingDemuxerStream() will be called in
502 // OnAudioDecryptingDemuxerStreamInitDone().
506 if (video_stream_
&& video_stream_
->video_decoder_config().is_encrypted() &&
507 !set_decryptor_ready_cb_
.is_null()) {
508 InitVideoDecryptingDemuxerStream();
512 // Notify demuxer ready when both streams are not encrypted.
513 is_demuxer_ready_
= true;
514 NotifyDemuxerReady();
517 void MediaSourceDelegate::InitAudioDecryptingDemuxerStream() {
518 DCHECK(media_task_runner_
->BelongsToCurrentThread());
519 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
520 DCHECK(!set_decryptor_ready_cb_
.is_null());
522 audio_decrypting_demuxer_stream_
.reset(new media::DecryptingDemuxerStream(
523 media_task_runner_
, media_log_
, set_decryptor_ready_cb_
,
524 waiting_for_decryption_key_cb_
));
525 audio_decrypting_demuxer_stream_
->Initialize(
527 base::Bind(&MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone
,
528 media_weak_factory_
.GetWeakPtr()));
531 void MediaSourceDelegate::InitVideoDecryptingDemuxerStream() {
532 DCHECK(media_task_runner_
->BelongsToCurrentThread());
533 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
534 DCHECK(!set_decryptor_ready_cb_
.is_null());
536 video_decrypting_demuxer_stream_
.reset(new media::DecryptingDemuxerStream(
537 media_task_runner_
, media_log_
, set_decryptor_ready_cb_
,
538 waiting_for_decryption_key_cb_
));
539 video_decrypting_demuxer_stream_
->Initialize(
541 base::Bind(&MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone
,
542 media_weak_factory_
.GetWeakPtr()));
545 void MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone(
546 media::PipelineStatus status
) {
547 DCHECK(media_task_runner_
->BelongsToCurrentThread());
548 DVLOG(1) << __FUNCTION__
<< "(" << status
<< ") : " << demuxer_client_id_
;
549 DCHECK(chunk_demuxer_
);
551 if (status
!= media::PIPELINE_OK
)
552 audio_decrypting_demuxer_stream_
.reset();
554 audio_stream_
= audio_decrypting_demuxer_stream_
.get();
556 if (video_stream_
&& video_stream_
->video_decoder_config().is_encrypted()) {
557 InitVideoDecryptingDemuxerStream();
561 // Try to notify demuxer ready when audio DDS initialization finished and
562 // video is not encrypted.
563 is_demuxer_ready_
= true;
564 NotifyDemuxerReady();
567 void MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone(
568 media::PipelineStatus status
) {
569 DCHECK(media_task_runner_
->BelongsToCurrentThread());
570 DVLOG(1) << __FUNCTION__
<< "(" << status
<< ") : " << demuxer_client_id_
;
571 DCHECK(chunk_demuxer_
);
573 if (status
!= media::PIPELINE_OK
)
574 video_decrypting_demuxer_stream_
.reset();
576 video_stream_
= video_decrypting_demuxer_stream_
.get();
578 // Try to notify demuxer ready when video DDS initialization finished.
579 is_demuxer_ready_
= true;
580 NotifyDemuxerReady();
583 void MediaSourceDelegate::OnDemuxerSeekDone(media::PipelineStatus status
) {
584 DCHECK(media_task_runner_
->BelongsToCurrentThread());
585 DVLOG(1) << __FUNCTION__
<< "(" << status
<< ") : " << demuxer_client_id_
;
588 if (status
!= media::PIPELINE_OK
) {
589 OnDemuxerError(status
);
593 ResetAudioDecryptingDemuxerStream();
596 void MediaSourceDelegate::ResetAudioDecryptingDemuxerStream() {
597 DCHECK(media_task_runner_
->BelongsToCurrentThread());
598 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
599 if (audio_decrypting_demuxer_stream_
) {
600 audio_decrypting_demuxer_stream_
->Reset(
601 base::Bind(&MediaSourceDelegate::ResetVideoDecryptingDemuxerStream
,
602 media_weak_factory_
.GetWeakPtr()));
606 ResetVideoDecryptingDemuxerStream();
609 void MediaSourceDelegate::ResetVideoDecryptingDemuxerStream() {
610 DCHECK(media_task_runner_
->BelongsToCurrentThread());
611 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
612 if (video_decrypting_demuxer_stream_
) {
613 video_decrypting_demuxer_stream_
->Reset(base::Bind(
614 &MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams
,
615 media_weak_factory_
.GetWeakPtr()));
619 FinishResettingDecryptingDemuxerStreams();
622 void MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams() {
623 DCHECK(media_task_runner_
->BelongsToCurrentThread());
624 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
626 base::AutoLock
auto_lock(seeking_lock_
);
629 doing_browser_seek_
= false;
630 demuxer_client_
->DemuxerSeekDone(demuxer_client_id_
, browser_seek_time_
);
633 void MediaSourceDelegate::NotifyDemuxerReady() {
634 DCHECK(media_task_runner_
->BelongsToCurrentThread());
635 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
636 DCHECK(is_demuxer_ready_
);
638 scoped_ptr
<DemuxerConfigs
> configs(new DemuxerConfigs());
639 GetDemuxerConfigFromStream(configs
.get(), true);
640 GetDemuxerConfigFromStream(configs
.get(), false);
641 configs
->duration
= GetDuration();
644 demuxer_client_
->DemuxerReady(demuxer_client_id_
, *configs
);
646 base::AutoLock
auto_lock(is_video_encrypted_lock_
);
647 is_video_encrypted_
= configs
->is_video_encrypted
;
650 base::TimeDelta
MediaSourceDelegate::GetDuration() const {
651 DCHECK(media_task_runner_
->BelongsToCurrentThread());
653 return media::kNoTimestamp();
655 double duration
= chunk_demuxer_
->GetDuration();
656 if (duration
== std::numeric_limits
<double>::infinity())
657 return media::kInfiniteDuration();
659 return base::TimeDelta::FromSecondsD(duration
);
662 void MediaSourceDelegate::OnDemuxerOpened() {
663 DCHECK(main_task_runner_
->BelongsToCurrentThread());
664 if (media_source_opened_cb_
.is_null())
667 media_source_opened_cb_
.Run(
668 new media::WebMediaSourceImpl(chunk_demuxer_
.get(), media_log_
));
671 void MediaSourceDelegate::OnEncryptedMediaInitData(
672 media::EmeInitDataType init_data_type
,
673 const std::vector
<uint8
>& init_data
) {
674 DCHECK(main_task_runner_
->BelongsToCurrentThread());
675 if (encrypted_media_init_data_cb_
.is_null())
678 encrypted_media_init_data_cb_
.Run(init_data_type
, init_data
);
681 bool MediaSourceDelegate::IsSeeking() const {
682 base::AutoLock
auto_lock(seeking_lock_
);
686 base::TimeDelta
MediaSourceDelegate::FindBufferedBrowserSeekTime_Locked(
687 const base::TimeDelta
& seek_time
) const {
688 seeking_lock_
.AssertAcquired();
690 DCHECK(doing_browser_seek_
);
691 DCHECK(chunk_demuxer_
) << "Browser seek requested, but no chunk demuxer";
693 media::Ranges
<base::TimeDelta
> buffered
=
694 chunk_demuxer_
->GetBufferedRanges();
696 for (size_t i
= 0; i
< buffered
.size(); ++i
) {
697 base::TimeDelta range_start
= buffered
.start(i
);
698 base::TimeDelta range_end
= buffered
.end(i
);
699 if (range_start
<= seek_time
) {
700 if (range_end
>= seek_time
)
705 // If the start of the next buffered range after |seek_time| is too far
706 // into the future, do not jump forward.
707 if ((range_start
- seek_time
) > base::TimeDelta::FromMilliseconds(100))
710 // TODO(wolenetz): Remove possibility that this browser seek jumps
711 // into future when the requested range is unbuffered but there is some
712 // other buffered range after it. See http://crbug.com/304234.
716 // We found no range containing |seek_time| or beginning shortly after
717 // |seek_time|. While possible that such data at and beyond the player's
718 // current time have been garbage collected or removed by the web app, this is
719 // unlikely. This may cause unexpected playback stall due to seek pending an
720 // append for a GOP prior to the last GOP demuxed.
721 // TODO(wolenetz): Remove the possibility for this seek to cause unexpected
722 // player stall by replaying cached data since last keyframe in browser player
723 // rather than issuing browser seek. See http://crbug.com/304234.
727 bool MediaSourceDelegate::GetDemuxerConfigFromStream(
728 media::DemuxerConfigs
* configs
, bool is_audio
) {
729 DCHECK(media_task_runner_
->BelongsToCurrentThread());
730 if (!is_demuxer_ready_
)
732 if (is_audio
&& audio_stream_
) {
733 media::AudioDecoderConfig config
= audio_stream_
->audio_decoder_config();
734 configs
->audio_codec
= config
.codec();
735 configs
->audio_channels
=
736 media::ChannelLayoutToChannelCount(config
.channel_layout());
737 configs
->audio_sampling_rate
= config
.samples_per_second();
738 configs
->is_audio_encrypted
= config
.is_encrypted();
739 configs
->audio_extra_data
= std::vector
<uint8
>(
740 config
.extra_data(), config
.extra_data() + config
.extra_data_size());
741 configs
->audio_codec_delay_ns
= static_cast<int64_t>(
742 config
.codec_delay() *
743 (static_cast<double>(base::Time::kNanosecondsPerSecond
) /
744 config
.samples_per_second()));
745 configs
->audio_seek_preroll_ns
=
746 config
.seek_preroll().InMicroseconds() *
747 base::Time::kNanosecondsPerMicrosecond
;
750 if (!is_audio
&& video_stream_
) {
751 media::VideoDecoderConfig config
= video_stream_
->video_decoder_config();
752 configs
->video_codec
= config
.codec();
753 configs
->video_size
= config
.natural_size();
754 configs
->is_video_encrypted
= config
.is_encrypted();
755 configs
->video_extra_data
= std::vector
<uint8
>(
756 config
.extra_data(), config
.extra_data() + config
.extra_data_size());
762 } // namespace content