Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / renderer / media / android / media_source_delegate.cc
blob139be4a38b43aa1bec90112f38505dee64cd5f52
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"
7 #include <limits>
8 #include <string>
9 #include <vector>
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;
32 namespace {
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 };
40 } // namespace
42 namespace content {
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),
53 audio_stream_(NULL),
54 video_stream_(NULL),
55 seeking_(false),
56 is_video_encrypted_(false),
57 doing_browser_seek_(false),
58 browser_seek_time_(media::kNoTimestamp()),
59 expecting_regular_seek_(false),
60 access_unit_size_(0),
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_);
86 return;
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(
100 FROM_HERE,
101 base::Bind(
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());
113 if (!chunk_demuxer_)
114 return base::Time();
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
141 // |stop_cb|.
142 stop_cb.Run();
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_)),
169 media_log_, false));
171 // |this| will be retained until StopDemuxer() is posted, so Unretained() is
172 // safe here.
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()),
184 false);
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_;
212 if (!chunk_demuxer_)
213 return;
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_;
236 if (!chunk_demuxer_)
237 return;
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.
247 if (seeking_) {
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_);
268 DCHECK(!seeking_);
269 seeking_ = true;
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.
277 seeking_ = false;
278 doing_browser_seek_ = false;
279 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, seek_time);
280 return;
283 if (doing_browser_seek_) {
284 internal_seek_time = FindBufferedBrowserSeekTime_Locked(seek_time);
285 browser_seek_time_ = internal_seek_time;
286 } else {
287 expecting_regular_seek_ = false;
288 browser_seek_time_ = media::kNoTimestamp();
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());
303 DCHECK(IsSeeking());
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
320 // ChunkDemxuer.
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());
327 if (demuxer_client_)
328 demuxer_client_->DurationChanged(demuxer_client_id_, duration);
329 if (!duration_change_cb_.is_null())
330 duration_change_cb_.Run(duration);
333 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type) {
334 DCHECK(media_task_runner_->BelongsToCurrentThread());
335 DVLOG(1) << __FUNCTION__ << "(" << type << ") : " << demuxer_client_id_;
336 if (IsSeeking())
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());
343 data->type = type;
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,
350 size_t index) {
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,
363 size_t index,
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())
369 ? -1
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.
376 if (IsSeeking()) {
377 DVLOG(1) << __FUNCTION__ << ": Ignore previous read during seeking.";
378 return;
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;
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;
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));
403 if (!is_audio) {
404 gfx::Size size = data->demuxer_configs[0].video_size;
405 DVLOG(1) << "Video config is changed: " << size.width() << "x"
406 << size.height();
408 data->access_units[index].status = status;
409 data->access_units.resize(index + 1);
410 break;
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);
417 break;
419 data->access_units[index].is_key_frame = buffer->is_key_frame();
420 // TODO(ycheo): We assume that the inputed stream will be decoded
421 // right away.
422 // Need to implement this properly using MediaPlayer.OnInfoListener.
423 if (is_audio) {
424 statistics_.audio_bytes_decoded += buffer->data_size();
425 } else {
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,
439 kVorbisPadding + 4);
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);
453 return;
455 break;
457 default:
458 NOTREACHED();
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).
476 NOTIMPLEMENTED();
479 void MediaSourceDelegate::RemoveTextStream(
480 media::DemuxerStream* /* text_stream */ ) {
481 // TODO(matthewjheaney): remove text stream (http://crbug/322115).
482 NOTIMPLEMENTED();
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);
492 return;
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().
503 return;
506 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted() &&
507 !set_decryptor_ready_cb_.is_null()) {
508 InitVideoDecryptingDemuxerStream();
509 return;
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(
526 audio_stream_,
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(
540 video_stream_,
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();
553 else
554 audio_stream_ = audio_decrypting_demuxer_stream_.get();
556 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted()) {
557 InitVideoDecryptingDemuxerStream();
558 return;
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();
575 else
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_;
586 DCHECK(IsSeeking());
588 if (status != media::PIPELINE_OK) {
589 OnDemuxerError(status);
590 return;
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()));
603 return;
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()));
616 return;
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_);
627 DCHECK(seeking_);
628 seeking_ = false;
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();
643 if (demuxer_client_)
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());
652 if (!chunk_demuxer_)
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())
665 return;
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())
676 return;
678 encrypted_media_init_data_cb_.Run(init_data_type, init_data);
681 bool MediaSourceDelegate::IsSeeking() const {
682 base::AutoLock auto_lock(seeking_lock_);
683 return seeking_;
686 base::TimeDelta MediaSourceDelegate::FindBufferedBrowserSeekTime_Locked(
687 const base::TimeDelta& seek_time) const {
688 seeking_lock_.AssertAcquired();
689 DCHECK(seeking_);
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)
701 return seek_time;
702 continue;
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))
708 break;
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.
713 return range_start;
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.
724 return seek_time;
727 bool MediaSourceDelegate::GetDemuxerConfigFromStream(
728 media::DemuxerConfigs* configs, bool is_audio) {
729 DCHECK(media_task_runner_->BelongsToCurrentThread());
730 if (!is_demuxer_ready_)
731 return false;
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;
748 return true;
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());
757 return true;
759 return false;
762 } // namespace content