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/blink/webmediaplayer_util.h"
19 #include "media/blink/webmediasource_impl.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 MediaSourceDelegate::MediaSourceDelegate(
44 RendererDemuxerAndroid
* demuxer_client
,
45 int demuxer_client_id
,
46 const scoped_refptr
<base::SingleThreadTaskRunner
>& media_task_runner
,
47 const scoped_refptr
<media::MediaLog
> media_log
)
48 : demuxer_client_(demuxer_client
),
49 demuxer_client_id_(demuxer_client_id
),
50 media_log_(media_log
),
51 is_demuxer_ready_(false),
55 is_video_encrypted_(false),
56 doing_browser_seek_(false),
57 browser_seek_time_(media::kNoTimestamp()),
58 expecting_regular_seek_(false),
60 main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
61 media_task_runner_(media_task_runner
),
62 main_weak_factory_(this),
63 media_weak_factory_(this) {
64 main_weak_this_
= main_weak_factory_
.GetWeakPtr();
65 DCHECK(main_task_runner_
->BelongsToCurrentThread());
68 MediaSourceDelegate::~MediaSourceDelegate() {
69 DCHECK(main_task_runner_
->BelongsToCurrentThread());
70 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
71 DCHECK(!chunk_demuxer_
);
72 DCHECK(!demuxer_client_
);
73 DCHECK(!audio_decrypting_demuxer_stream_
);
74 DCHECK(!video_decrypting_demuxer_stream_
);
75 DCHECK(!audio_stream_
);
76 DCHECK(!video_stream_
);
79 void MediaSourceDelegate::Stop(const base::Closure
& stop_cb
) {
80 DCHECK(main_task_runner_
->BelongsToCurrentThread());
81 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
83 if (!chunk_demuxer_
) {
84 DCHECK(!demuxer_client_
);
88 duration_change_cb_
.Reset();
89 update_network_state_cb_
.Reset();
90 media_source_opened_cb_
.Reset();
92 main_weak_factory_
.InvalidateWeakPtrs();
93 DCHECK(!main_weak_factory_
.HasWeakPtrs());
95 chunk_demuxer_
->Shutdown();
97 // Continue to stop objects on the media thread.
98 media_task_runner_
->PostTask(
101 &MediaSourceDelegate::StopDemuxer
, base::Unretained(this), stop_cb
));
104 bool MediaSourceDelegate::IsVideoEncrypted() {
105 DCHECK(main_task_runner_
->BelongsToCurrentThread());
106 base::AutoLock
auto_lock(is_video_encrypted_lock_
);
107 return is_video_encrypted_
;
110 base::Time
MediaSourceDelegate::GetTimelineOffset() const {
111 DCHECK(main_task_runner_
->BelongsToCurrentThread());
115 return chunk_demuxer_
->GetTimelineOffset();
118 void MediaSourceDelegate::StopDemuxer(const base::Closure
& stop_cb
) {
119 DVLOG(2) << __FUNCTION__
;
120 DCHECK(media_task_runner_
->BelongsToCurrentThread());
121 DCHECK(chunk_demuxer_
);
123 demuxer_client_
->RemoveDelegate(demuxer_client_id_
);
124 demuxer_client_
= NULL
;
126 audio_stream_
= NULL
;
127 video_stream_
= NULL
;
128 // TODO(xhwang): Figure out if we need to Reset the DDSs after Seeking or
129 // before destroying them.
130 audio_decrypting_demuxer_stream_
.reset();
131 video_decrypting_demuxer_stream_
.reset();
133 media_weak_factory_
.InvalidateWeakPtrs();
134 DCHECK(!media_weak_factory_
.HasWeakPtrs());
136 chunk_demuxer_
->Stop();
137 chunk_demuxer_
.reset();
139 // |this| may be destroyed at this point in time as a result of running
144 void MediaSourceDelegate::InitializeMediaSource(
145 const MediaSourceOpenedCB
& media_source_opened_cb
,
146 const media::Demuxer::EncryptedMediaInitDataCB
&
147 encrypted_media_init_data_cb
,
148 const media::SetDecryptorReadyCB
& set_decryptor_ready_cb
,
149 const UpdateNetworkStateCB
& update_network_state_cb
,
150 const DurationChangeCB
& duration_change_cb
,
151 const base::Closure
& waiting_for_decryption_key_cb
) {
152 DCHECK(main_task_runner_
->BelongsToCurrentThread());
153 DCHECK(!media_source_opened_cb
.is_null());
154 media_source_opened_cb_
= media_source_opened_cb
;
155 encrypted_media_init_data_cb_
= encrypted_media_init_data_cb
;
156 set_decryptor_ready_cb_
= media::BindToCurrentLoop(set_decryptor_ready_cb
);
157 update_network_state_cb_
= media::BindToCurrentLoop(update_network_state_cb
);
158 duration_change_cb_
= duration_change_cb
;
159 waiting_for_decryption_key_cb_
=
160 media::BindToCurrentLoop(waiting_for_decryption_key_cb
);
161 access_unit_size_
= kAccessUnitSizeForMediaSource
;
163 chunk_demuxer_
.reset(new media::ChunkDemuxer(
164 media::BindToCurrentLoop(
165 base::Bind(&MediaSourceDelegate::OnDemuxerOpened
, main_weak_this_
)),
166 media::BindToCurrentLoop(base::Bind(
167 &MediaSourceDelegate::OnEncryptedMediaInitData
, main_weak_this_
)),
170 // |this| will be retained until StopDemuxer() is posted, so Unretained() is
172 media_task_runner_
->PostTask(FROM_HERE
,
173 base::Bind(&MediaSourceDelegate::InitializeDemuxer
,
174 base::Unretained(this)));
177 void MediaSourceDelegate::InitializeDemuxer() {
178 DCHECK(media_task_runner_
->BelongsToCurrentThread());
179 demuxer_client_
->AddDelegate(demuxer_client_id_
, this);
180 chunk_demuxer_
->Initialize(this,
181 base::Bind(&MediaSourceDelegate::OnDemuxerInitDone
,
182 media_weak_factory_
.GetWeakPtr()),
186 blink::WebTimeRanges
MediaSourceDelegate::Buffered() const {
187 return media::ConvertToWebTimeRanges(buffered_time_ranges_
);
190 size_t MediaSourceDelegate::DecodedFrameCount() const {
191 return statistics_
.video_frames_decoded
;
194 size_t MediaSourceDelegate::DroppedFrameCount() const {
195 return statistics_
.video_frames_dropped
;
198 size_t MediaSourceDelegate::AudioDecodedByteCount() const {
199 return statistics_
.audio_bytes_decoded
;
202 size_t MediaSourceDelegate::VideoDecodedByteCount() const {
203 return statistics_
.video_bytes_decoded
;
206 void MediaSourceDelegate::CancelPendingSeek(const base::TimeDelta
& seek_time
) {
207 DCHECK(main_task_runner_
->BelongsToCurrentThread());
208 DVLOG(1) << __FUNCTION__
<< "(" << seek_time
.InSecondsF() << ") : "
209 << demuxer_client_id_
;
215 // Remember to trivially finish any newly arriving browser seek requests
216 // that may arrive prior to the next regular seek request.
217 base::AutoLock
auto_lock(seeking_lock_
);
218 expecting_regular_seek_
= true;
221 // Cancel any previously expected or in-progress regular or browser seek.
222 // It is possible that we have just finished the seek, but caller does
223 // not know this yet. It is still safe to cancel in this case because the
224 // caller will always call StartWaitingForSeek() when it is notified of
225 // the finished seek.
226 chunk_demuxer_
->CancelPendingSeek(seek_time
);
229 void MediaSourceDelegate::StartWaitingForSeek(
230 const base::TimeDelta
& seek_time
) {
231 DCHECK(main_task_runner_
->BelongsToCurrentThread());
232 DVLOG(1) << __FUNCTION__
<< "(" << seek_time
.InSecondsF() << ") : "
233 << demuxer_client_id_
;
238 bool cancel_browser_seek
= false;
240 // Remember to trivially finish any newly arriving browser seek requests
241 // that may arrive prior to the next regular seek request.
242 base::AutoLock
auto_lock(seeking_lock_
);
243 expecting_regular_seek_
= true;
245 // Remember to cancel any in-progress browser seek.
247 DCHECK(doing_browser_seek_
);
248 cancel_browser_seek
= true;
252 if (cancel_browser_seek
)
253 chunk_demuxer_
->CancelPendingSeek(seek_time
);
254 chunk_demuxer_
->StartWaitingForSeek(seek_time
);
257 void MediaSourceDelegate::Seek(
258 const base::TimeDelta
& seek_time
, bool is_browser_seek
) {
259 DCHECK(media_task_runner_
->BelongsToCurrentThread());
260 DVLOG(1) << __FUNCTION__
<< "(" << seek_time
.InSecondsF() << ", "
261 << (is_browser_seek
? "browser seek" : "regular seek") << ") : "
262 << demuxer_client_id_
;
264 base::TimeDelta internal_seek_time
= seek_time
;
266 base::AutoLock
auto_lock(seeking_lock_
);
269 doing_browser_seek_
= is_browser_seek
;
271 if (doing_browser_seek_
&& (!chunk_demuxer_
|| expecting_regular_seek_
)) {
272 // Trivially finish the browser seek without actually doing it. Reads will
273 // continue to be |kAborted| until the next regular seek is done. Browser
274 // seeking is not supported unless using a ChunkDemuxer; browser seeks are
275 // trivially finished if |chunk_demuxer_| is NULL.
277 doing_browser_seek_
= false;
278 demuxer_client_
->DemuxerSeekDone(demuxer_client_id_
, seek_time
);
282 if (doing_browser_seek_
) {
283 internal_seek_time
= FindBufferedBrowserSeekTime_Locked(seek_time
);
284 browser_seek_time_
= internal_seek_time
;
286 expecting_regular_seek_
= false;
287 browser_seek_time_
= media::kNoTimestamp();
291 // Prepare |chunk_demuxer_| for browser seek.
292 if (is_browser_seek
) {
293 chunk_demuxer_
->CancelPendingSeek(internal_seek_time
);
294 chunk_demuxer_
->StartWaitingForSeek(internal_seek_time
);
297 SeekInternal(internal_seek_time
);
300 void MediaSourceDelegate::SeekInternal(const base::TimeDelta
& seek_time
) {
301 DCHECK(media_task_runner_
->BelongsToCurrentThread());
303 chunk_demuxer_
->Seek(seek_time
, base::Bind(
304 &MediaSourceDelegate::OnDemuxerSeekDone
,
305 media_weak_factory_
.GetWeakPtr()));
308 void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start
,
309 base::TimeDelta end
) {
310 buffered_time_ranges_
.Add(start
, end
);
313 void MediaSourceDelegate::SetDuration(base::TimeDelta duration
) {
314 DCHECK(main_task_runner_
->BelongsToCurrentThread());
315 DVLOG(1) << __FUNCTION__
<< "(" << duration
.InSecondsF() << ") : "
316 << demuxer_client_id_
;
318 // Force duration change notification to be async to avoid reentrancy into
320 main_task_runner_
->PostTask(FROM_HERE
, base::Bind(
321 &MediaSourceDelegate::OnDurationChanged
, main_weak_this_
, duration
));
324 void MediaSourceDelegate::OnDurationChanged(const base::TimeDelta
& duration
) {
325 DCHECK(main_task_runner_
->BelongsToCurrentThread());
327 demuxer_client_
->DurationChanged(demuxer_client_id_
, duration
);
328 if (!duration_change_cb_
.is_null())
329 duration_change_cb_
.Run(duration
);
332 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type
) {
333 DCHECK(media_task_runner_
->BelongsToCurrentThread());
334 DVLOG(1) << __FUNCTION__
<< "(" << type
<< ") : " << demuxer_client_id_
;
336 return; // Drop the request during seeking.
338 DCHECK(type
== DemuxerStream::AUDIO
|| type
== DemuxerStream::VIDEO
);
339 // The access unit size should have been initialized properly at this stage.
340 DCHECK_GT(access_unit_size_
, 0u);
341 scoped_ptr
<DemuxerData
> data(new DemuxerData());
343 data
->access_units
.resize(access_unit_size_
);
344 ReadFromDemuxerStream(type
, data
.Pass(), 0);
347 void MediaSourceDelegate::ReadFromDemuxerStream(media::DemuxerStream::Type type
,
348 scoped_ptr
<DemuxerData
> data
,
350 DCHECK(media_task_runner_
->BelongsToCurrentThread());
351 // DemuxerStream::Read() always returns the read callback asynchronously.
352 DemuxerStream
* stream
=
353 (type
== DemuxerStream::AUDIO
) ? audio_stream_
: video_stream_
;
354 stream
->Read(base::Bind(
355 &MediaSourceDelegate::OnBufferReady
,
356 media_weak_factory_
.GetWeakPtr(), type
, base::Passed(&data
), index
));
359 void MediaSourceDelegate::OnBufferReady(
360 media::DemuxerStream::Type type
,
361 scoped_ptr
<DemuxerData
> data
,
363 DemuxerStream::Status status
,
364 const scoped_refptr
<media::DecoderBuffer
>& buffer
) {
365 DCHECK(media_task_runner_
->BelongsToCurrentThread());
366 DVLOG(1) << __FUNCTION__
<< "(" << index
<< ", " << status
<< ", "
367 << ((!buffer
.get() || buffer
->end_of_stream())
369 : buffer
->timestamp().InMilliseconds())
370 << ") : " << demuxer_client_id_
;
371 DCHECK(chunk_demuxer_
);
373 // No new OnReadFromDemuxer() will be called during seeking. So this callback
374 // must be from previous OnReadFromDemuxer() call and should be ignored.
376 DVLOG(1) << __FUNCTION__
<< ": Ignore previous read during seeking.";
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
);
392 case DemuxerStream::kAborted
:
393 DVLOG(1) << __FUNCTION__
<< " : Aborted";
394 data
->access_units
[index
].status
= status
;
395 data
->access_units
.resize(index
+ 1);
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
));
403 gfx::Size size
= data
->demuxer_configs
[0].video_size
;
404 DVLOG(1) << "Video config is changed: " << size
.width() << "x"
407 data
->access_units
[index
].status
= status
;
408 data
->access_units
.resize(index
+ 1);
411 case DemuxerStream::kOk
:
412 data
->access_units
[index
].status
= status
;
413 if (buffer
->end_of_stream()) {
414 data
->access_units
[index
].is_end_of_stream
= true;
415 data
->access_units
.resize(index
+ 1);
418 data
->access_units
[index
].is_key_frame
= buffer
->is_key_frame();
419 // TODO(ycheo): We assume that the inputed stream will be decoded
421 // Need to implement this properly using MediaPlayer.OnInfoListener.
423 statistics_
.audio_bytes_decoded
+= buffer
->data_size();
425 statistics_
.video_bytes_decoded
+= buffer
->data_size();
426 statistics_
.video_frames_decoded
++;
428 data
->access_units
[index
].timestamp
= buffer
->timestamp();
430 data
->access_units
[index
].data
.assign(
431 buffer
->data(), buffer
->data() + buffer
->data_size());
432 // Vorbis needs 4 extra bytes padding on Android. Check
433 // NuMediaExtractor.cpp in Android source code.
434 if (is_audio
&& media::kCodecVorbis
==
435 audio_stream_
->audio_decoder_config().codec()) {
436 data
->access_units
[index
].data
.insert(
437 data
->access_units
[index
].data
.end(), kVorbisPadding
,
440 if (buffer
->decrypt_config()) {
441 data
->access_units
[index
].key_id
= std::vector
<char>(
442 buffer
->decrypt_config()->key_id().begin(),
443 buffer
->decrypt_config()->key_id().end());
444 data
->access_units
[index
].iv
= std::vector
<char>(
445 buffer
->decrypt_config()->iv().begin(),
446 buffer
->decrypt_config()->iv().end());
447 data
->access_units
[index
].subsamples
=
448 buffer
->decrypt_config()->subsamples();
450 if (++index
< data
->access_units
.size()) {
451 ReadFromDemuxerStream(type
, data
.Pass(), index
);
460 if (!IsSeeking() && demuxer_client_
)
461 demuxer_client_
->ReadFromDemuxerAck(demuxer_client_id_
, *data
);
464 void MediaSourceDelegate::OnDemuxerError(media::PipelineStatus status
) {
465 DVLOG(1) << __FUNCTION__
<< "(" << status
<< ") : " << demuxer_client_id_
;
466 // |update_network_state_cb_| is bound to the main thread.
467 if (status
!= media::PIPELINE_OK
&& !update_network_state_cb_
.is_null())
468 update_network_state_cb_
.Run(PipelineErrorToNetworkState(status
));
471 void MediaSourceDelegate::AddTextStream(
472 media::DemuxerStream
* /* text_stream */ ,
473 const media::TextTrackConfig
& /* config */ ) {
474 // TODO(matthewjheaney): add text stream (http://crbug/322115).
478 void MediaSourceDelegate::RemoveTextStream(
479 media::DemuxerStream
* /* text_stream */ ) {
480 // TODO(matthewjheaney): remove text stream (http://crbug/322115).
484 void MediaSourceDelegate::OnDemuxerInitDone(media::PipelineStatus status
) {
485 DCHECK(media_task_runner_
->BelongsToCurrentThread());
486 DVLOG(1) << __FUNCTION__
<< "(" << status
<< ") : " << demuxer_client_id_
;
487 DCHECK(chunk_demuxer_
);
489 if (status
!= media::PIPELINE_OK
) {
490 OnDemuxerError(status
);
494 audio_stream_
= chunk_demuxer_
->GetStream(DemuxerStream::AUDIO
);
495 video_stream_
= chunk_demuxer_
->GetStream(DemuxerStream::VIDEO
);
497 if (audio_stream_
&& audio_stream_
->audio_decoder_config().is_encrypted() &&
498 !set_decryptor_ready_cb_
.is_null()) {
499 InitAudioDecryptingDemuxerStream();
500 // InitVideoDecryptingDemuxerStream() will be called in
501 // OnAudioDecryptingDemuxerStreamInitDone().
505 if (video_stream_
&& video_stream_
->video_decoder_config().is_encrypted() &&
506 !set_decryptor_ready_cb_
.is_null()) {
507 InitVideoDecryptingDemuxerStream();
511 // Notify demuxer ready when both streams are not encrypted.
512 is_demuxer_ready_
= true;
513 NotifyDemuxerReady();
516 void MediaSourceDelegate::InitAudioDecryptingDemuxerStream() {
517 DCHECK(media_task_runner_
->BelongsToCurrentThread());
518 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
519 DCHECK(!set_decryptor_ready_cb_
.is_null());
521 audio_decrypting_demuxer_stream_
.reset(new media::DecryptingDemuxerStream(
522 media_task_runner_
, media_log_
, set_decryptor_ready_cb_
,
523 waiting_for_decryption_key_cb_
));
524 audio_decrypting_demuxer_stream_
->Initialize(
526 base::Bind(&MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone
,
527 media_weak_factory_
.GetWeakPtr()));
530 void MediaSourceDelegate::InitVideoDecryptingDemuxerStream() {
531 DCHECK(media_task_runner_
->BelongsToCurrentThread());
532 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
533 DCHECK(!set_decryptor_ready_cb_
.is_null());
535 video_decrypting_demuxer_stream_
.reset(new media::DecryptingDemuxerStream(
536 media_task_runner_
, media_log_
, set_decryptor_ready_cb_
,
537 waiting_for_decryption_key_cb_
));
538 video_decrypting_demuxer_stream_
->Initialize(
540 base::Bind(&MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone
,
541 media_weak_factory_
.GetWeakPtr()));
544 void MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone(
545 media::PipelineStatus status
) {
546 DCHECK(media_task_runner_
->BelongsToCurrentThread());
547 DVLOG(1) << __FUNCTION__
<< "(" << status
<< ") : " << demuxer_client_id_
;
548 DCHECK(chunk_demuxer_
);
550 if (status
!= media::PIPELINE_OK
)
551 audio_decrypting_demuxer_stream_
.reset();
553 audio_stream_
= audio_decrypting_demuxer_stream_
.get();
555 if (video_stream_
&& video_stream_
->video_decoder_config().is_encrypted()) {
556 InitVideoDecryptingDemuxerStream();
560 // Try to notify demuxer ready when audio DDS initialization finished and
561 // video is not encrypted.
562 is_demuxer_ready_
= true;
563 NotifyDemuxerReady();
566 void MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone(
567 media::PipelineStatus status
) {
568 DCHECK(media_task_runner_
->BelongsToCurrentThread());
569 DVLOG(1) << __FUNCTION__
<< "(" << status
<< ") : " << demuxer_client_id_
;
570 DCHECK(chunk_demuxer_
);
572 if (status
!= media::PIPELINE_OK
)
573 video_decrypting_demuxer_stream_
.reset();
575 video_stream_
= video_decrypting_demuxer_stream_
.get();
577 // Try to notify demuxer ready when video DDS initialization finished.
578 is_demuxer_ready_
= true;
579 NotifyDemuxerReady();
582 void MediaSourceDelegate::OnDemuxerSeekDone(media::PipelineStatus status
) {
583 DCHECK(media_task_runner_
->BelongsToCurrentThread());
584 DVLOG(1) << __FUNCTION__
<< "(" << status
<< ") : " << demuxer_client_id_
;
587 if (status
!= media::PIPELINE_OK
) {
588 OnDemuxerError(status
);
592 ResetAudioDecryptingDemuxerStream();
595 void MediaSourceDelegate::ResetAudioDecryptingDemuxerStream() {
596 DCHECK(media_task_runner_
->BelongsToCurrentThread());
597 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
598 if (audio_decrypting_demuxer_stream_
) {
599 audio_decrypting_demuxer_stream_
->Reset(
600 base::Bind(&MediaSourceDelegate::ResetVideoDecryptingDemuxerStream
,
601 media_weak_factory_
.GetWeakPtr()));
605 ResetVideoDecryptingDemuxerStream();
608 void MediaSourceDelegate::ResetVideoDecryptingDemuxerStream() {
609 DCHECK(media_task_runner_
->BelongsToCurrentThread());
610 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
611 if (video_decrypting_demuxer_stream_
) {
612 video_decrypting_demuxer_stream_
->Reset(base::Bind(
613 &MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams
,
614 media_weak_factory_
.GetWeakPtr()));
618 FinishResettingDecryptingDemuxerStreams();
621 void MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams() {
622 DCHECK(media_task_runner_
->BelongsToCurrentThread());
623 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
625 base::AutoLock
auto_lock(seeking_lock_
);
628 doing_browser_seek_
= false;
629 demuxer_client_
->DemuxerSeekDone(demuxer_client_id_
, browser_seek_time_
);
632 void MediaSourceDelegate::NotifyDemuxerReady() {
633 DCHECK(media_task_runner_
->BelongsToCurrentThread());
634 DVLOG(1) << __FUNCTION__
<< " : " << demuxer_client_id_
;
635 DCHECK(is_demuxer_ready_
);
637 scoped_ptr
<DemuxerConfigs
> configs(new DemuxerConfigs());
638 GetDemuxerConfigFromStream(configs
.get(), true);
639 GetDemuxerConfigFromStream(configs
.get(), false);
640 configs
->duration
= GetDuration();
643 demuxer_client_
->DemuxerReady(demuxer_client_id_
, *configs
);
645 base::AutoLock
auto_lock(is_video_encrypted_lock_
);
646 is_video_encrypted_
= configs
->is_video_encrypted
;
649 base::TimeDelta
MediaSourceDelegate::GetDuration() const {
650 DCHECK(media_task_runner_
->BelongsToCurrentThread());
652 return media::kNoTimestamp();
654 double duration
= chunk_demuxer_
->GetDuration();
655 if (duration
== std::numeric_limits
<double>::infinity())
656 return media::kInfiniteDuration();
658 return media::ConvertSecondsToTimestamp(duration
);
661 void MediaSourceDelegate::OnDemuxerOpened() {
662 DCHECK(main_task_runner_
->BelongsToCurrentThread());
663 if (media_source_opened_cb_
.is_null())
666 media_source_opened_cb_
.Run(
667 new media::WebMediaSourceImpl(chunk_demuxer_
.get(), media_log_
));
670 void MediaSourceDelegate::OnEncryptedMediaInitData(
671 media::EmeInitDataType init_data_type
,
672 const std::vector
<uint8
>& init_data
) {
673 DCHECK(main_task_runner_
->BelongsToCurrentThread());
674 if (encrypted_media_init_data_cb_
.is_null())
677 encrypted_media_init_data_cb_
.Run(init_data_type
, init_data
);
680 bool MediaSourceDelegate::IsSeeking() const {
681 base::AutoLock
auto_lock(seeking_lock_
);
685 base::TimeDelta
MediaSourceDelegate::FindBufferedBrowserSeekTime_Locked(
686 const base::TimeDelta
& seek_time
) const {
687 seeking_lock_
.AssertAcquired();
689 DCHECK(doing_browser_seek_
);
690 DCHECK(chunk_demuxer_
) << "Browser seek requested, but no chunk demuxer";
692 media::Ranges
<base::TimeDelta
> buffered
=
693 chunk_demuxer_
->GetBufferedRanges();
695 for (size_t i
= 0; i
< buffered
.size(); ++i
) {
696 base::TimeDelta range_start
= buffered
.start(i
);
697 base::TimeDelta range_end
= buffered
.end(i
);
698 if (range_start
<= seek_time
) {
699 if (range_end
>= seek_time
)
704 // If the start of the next buffered range after |seek_time| is too far
705 // into the future, do not jump forward.
706 if ((range_start
- seek_time
) > base::TimeDelta::FromMilliseconds(100))
709 // TODO(wolenetz): Remove possibility that this browser seek jumps
710 // into future when the requested range is unbuffered but there is some
711 // other buffered range after it. See http://crbug.com/304234.
715 // We found no range containing |seek_time| or beginning shortly after
716 // |seek_time|. While possible that such data at and beyond the player's
717 // current time have been garbage collected or removed by the web app, this is
718 // unlikely. This may cause unexpected playback stall due to seek pending an
719 // append for a GOP prior to the last GOP demuxed.
720 // TODO(wolenetz): Remove the possibility for this seek to cause unexpected
721 // player stall by replaying cached data since last keyframe in browser player
722 // rather than issuing browser seek. See http://crbug.com/304234.
726 bool MediaSourceDelegate::GetDemuxerConfigFromStream(
727 media::DemuxerConfigs
* configs
, bool is_audio
) {
728 DCHECK(media_task_runner_
->BelongsToCurrentThread());
729 if (!is_demuxer_ready_
)
731 if (is_audio
&& audio_stream_
) {
732 media::AudioDecoderConfig config
= audio_stream_
->audio_decoder_config();
733 configs
->audio_codec
= config
.codec();
734 configs
->audio_channels
=
735 media::ChannelLayoutToChannelCount(config
.channel_layout());
736 configs
->audio_sampling_rate
= config
.samples_per_second();
737 configs
->is_audio_encrypted
= config
.is_encrypted();
738 configs
->audio_extra_data
= std::vector
<uint8
>(
739 config
.extra_data(), config
.extra_data() + config
.extra_data_size());
740 configs
->audio_codec_delay_ns
= static_cast<int64_t>(
741 config
.codec_delay() *
742 (static_cast<double>(base::Time::kNanosecondsPerSecond
) /
743 config
.samples_per_second()));
744 configs
->audio_seek_preroll_ns
=
745 config
.seek_preroll().InMicroseconds() *
746 base::Time::kNanosecondsPerMicrosecond
;
749 if (!is_audio
&& video_stream_
) {
750 media::VideoDecoderConfig config
= video_stream_
->video_decoder_config();
751 configs
->video_codec
= config
.codec();
752 configs
->video_size
= config
.natural_size();
753 configs
->is_video_encrypted
= config
.is_encrypted();
754 configs
->video_extra_data
= std::vector
<uint8
>(
755 config
.extra_data(), config
.extra_data() + config
.extra_data_size());
761 } // namespace content