Don't send a SHChangeNotify for creating an app icon when creating a shortcut.
[chromium-blink-merge.git] / content / renderer / media / android / media_source_delegate.cc
blob1a457465b2824b997423f237e88456c0867b5453
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/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 "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;
31 namespace {
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 };
39 } // namespace
41 namespace content {
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::SingleThreadTaskRunner>& media_task_runner,
52 const scoped_refptr<media::MediaLog> media_log)
53 : demuxer_client_(demuxer_client),
54 demuxer_client_id_(demuxer_client_id),
55 media_log_(media_log),
56 is_demuxer_ready_(false),
57 audio_stream_(NULL),
58 video_stream_(NULL),
59 seeking_(false),
60 is_video_encrypted_(false),
61 doing_browser_seek_(false),
62 browser_seek_time_(media::kNoTimestamp()),
63 expecting_regular_seek_(false),
64 access_unit_size_(0),
65 main_task_runner_(base::MessageLoopProxy::current()),
66 media_task_runner_(media_task_runner),
67 main_weak_factory_(this),
68 media_weak_factory_(this) {
69 main_weak_this_ = main_weak_factory_.GetWeakPtr();
70 DCHECK(main_task_runner_->BelongsToCurrentThread());
73 MediaSourceDelegate::~MediaSourceDelegate() {
74 DCHECK(main_task_runner_->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::Stop(const base::Closure& stop_cb) {
85 DCHECK(main_task_runner_->BelongsToCurrentThread());
86 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
88 if (!chunk_demuxer_) {
89 DCHECK(!demuxer_client_);
90 return;
93 duration_change_cb_.Reset();
94 update_network_state_cb_.Reset();
95 media_source_opened_cb_.Reset();
97 main_weak_factory_.InvalidateWeakPtrs();
98 DCHECK(!main_weak_factory_.HasWeakPtrs());
100 chunk_demuxer_->Shutdown();
102 // Continue to stop objects on the media thread.
103 media_task_runner_->PostTask(
104 FROM_HERE,
105 base::Bind(
106 &MediaSourceDelegate::StopDemuxer, base::Unretained(this), stop_cb));
109 bool MediaSourceDelegate::IsVideoEncrypted() {
110 DCHECK(main_task_runner_->BelongsToCurrentThread());
111 base::AutoLock auto_lock(is_video_encrypted_lock_);
112 return is_video_encrypted_;
115 base::Time MediaSourceDelegate::GetTimelineOffset() const {
116 DCHECK(main_task_runner_->BelongsToCurrentThread());
117 if (!chunk_demuxer_)
118 return base::Time();
120 return chunk_demuxer_->GetTimelineOffset();
123 void MediaSourceDelegate::StopDemuxer(const base::Closure& stop_cb) {
124 DVLOG(2) << __FUNCTION__;
125 DCHECK(media_task_runner_->BelongsToCurrentThread());
126 DCHECK(chunk_demuxer_);
128 demuxer_client_->RemoveDelegate(demuxer_client_id_);
129 demuxer_client_ = NULL;
131 audio_stream_ = NULL;
132 video_stream_ = NULL;
133 // TODO(xhwang): Figure out if we need to Reset the DDSs after Seeking or
134 // before destroying them.
135 audio_decrypting_demuxer_stream_.reset();
136 video_decrypting_demuxer_stream_.reset();
138 media_weak_factory_.InvalidateWeakPtrs();
139 DCHECK(!media_weak_factory_.HasWeakPtrs());
141 chunk_demuxer_->Stop();
142 chunk_demuxer_.reset();
144 // |this| may be destroyed at this point in time as a result of running
145 // |stop_cb|.
146 stop_cb.Run();
149 void MediaSourceDelegate::InitializeMediaSource(
150 const MediaSourceOpenedCB& media_source_opened_cb,
151 const media::Demuxer::EncryptedMediaInitDataCB&
152 encrypted_media_init_data_cb,
153 const media::SetDecryptorReadyCB& set_decryptor_ready_cb,
154 const UpdateNetworkStateCB& update_network_state_cb,
155 const DurationChangeCB& duration_change_cb,
156 const base::Closure& waiting_for_decryption_key_cb) {
157 DCHECK(main_task_runner_->BelongsToCurrentThread());
158 DCHECK(!media_source_opened_cb.is_null());
159 media_source_opened_cb_ = media_source_opened_cb;
160 encrypted_media_init_data_cb_ = encrypted_media_init_data_cb;
161 set_decryptor_ready_cb_ = media::BindToCurrentLoop(set_decryptor_ready_cb);
162 update_network_state_cb_ = media::BindToCurrentLoop(update_network_state_cb);
163 duration_change_cb_ = duration_change_cb;
164 waiting_for_decryption_key_cb_ =
165 media::BindToCurrentLoop(waiting_for_decryption_key_cb);
166 access_unit_size_ = kAccessUnitSizeForMediaSource;
168 chunk_demuxer_.reset(new media::ChunkDemuxer(
169 media::BindToCurrentLoop(
170 base::Bind(&MediaSourceDelegate::OnDemuxerOpened, main_weak_this_)),
171 media::BindToCurrentLoop(base::Bind(
172 &MediaSourceDelegate::OnEncryptedMediaInitData, main_weak_this_)),
173 base::Bind(&LogMediaSourceError, media_log_), media_log_, false));
175 // |this| will be retained until StopDemuxer() is posted, so Unretained() is
176 // safe here.
177 media_task_runner_->PostTask(FROM_HERE,
178 base::Bind(&MediaSourceDelegate::InitializeDemuxer,
179 base::Unretained(this)));
182 void MediaSourceDelegate::InitializeDemuxer() {
183 DCHECK(media_task_runner_->BelongsToCurrentThread());
184 demuxer_client_->AddDelegate(demuxer_client_id_, this);
185 chunk_demuxer_->Initialize(this,
186 base::Bind(&MediaSourceDelegate::OnDemuxerInitDone,
187 media_weak_factory_.GetWeakPtr()),
188 false);
191 blink::WebTimeRanges MediaSourceDelegate::Buffered() const {
192 return media::ConvertToWebTimeRanges(buffered_time_ranges_);
195 size_t MediaSourceDelegate::DecodedFrameCount() const {
196 return statistics_.video_frames_decoded;
199 size_t MediaSourceDelegate::DroppedFrameCount() const {
200 return statistics_.video_frames_dropped;
203 size_t MediaSourceDelegate::AudioDecodedByteCount() const {
204 return statistics_.audio_bytes_decoded;
207 size_t MediaSourceDelegate::VideoDecodedByteCount() const {
208 return statistics_.video_bytes_decoded;
211 void MediaSourceDelegate::CancelPendingSeek(const base::TimeDelta& seek_time) {
212 DCHECK(main_task_runner_->BelongsToCurrentThread());
213 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : "
214 << demuxer_client_id_;
216 if (!chunk_demuxer_)
217 return;
220 // Remember to trivially finish any newly arriving browser seek requests
221 // that may arrive prior to the next regular seek request.
222 base::AutoLock auto_lock(seeking_lock_);
223 expecting_regular_seek_ = true;
226 // Cancel any previously expected or in-progress regular or browser seek.
227 // It is possible that we have just finished the seek, but caller does
228 // not know this yet. It is still safe to cancel in this case because the
229 // caller will always call StartWaitingForSeek() when it is notified of
230 // the finished seek.
231 chunk_demuxer_->CancelPendingSeek(seek_time);
234 void MediaSourceDelegate::StartWaitingForSeek(
235 const base::TimeDelta& seek_time) {
236 DCHECK(main_task_runner_->BelongsToCurrentThread());
237 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : "
238 << demuxer_client_id_;
240 if (!chunk_demuxer_)
241 return;
243 bool cancel_browser_seek = false;
245 // Remember to trivially finish any newly arriving browser seek requests
246 // that may arrive prior to the next regular seek request.
247 base::AutoLock auto_lock(seeking_lock_);
248 expecting_regular_seek_ = true;
250 // Remember to cancel any in-progress browser seek.
251 if (seeking_) {
252 DCHECK(doing_browser_seek_);
253 cancel_browser_seek = true;
257 if (cancel_browser_seek)
258 chunk_demuxer_->CancelPendingSeek(seek_time);
259 chunk_demuxer_->StartWaitingForSeek(seek_time);
262 void MediaSourceDelegate::Seek(
263 const base::TimeDelta& seek_time, bool is_browser_seek) {
264 DCHECK(media_task_runner_->BelongsToCurrentThread());
265 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ", "
266 << (is_browser_seek ? "browser seek" : "regular seek") << ") : "
267 << demuxer_client_id_;
269 base::TimeDelta internal_seek_time = seek_time;
271 base::AutoLock auto_lock(seeking_lock_);
272 DCHECK(!seeking_);
273 seeking_ = true;
274 doing_browser_seek_ = is_browser_seek;
276 if (doing_browser_seek_ && (!chunk_demuxer_ || expecting_regular_seek_)) {
277 // Trivially finish the browser seek without actually doing it. Reads will
278 // continue to be |kAborted| until the next regular seek is done. Browser
279 // seeking is not supported unless using a ChunkDemuxer; browser seeks are
280 // trivially finished if |chunk_demuxer_| is NULL.
281 seeking_ = false;
282 doing_browser_seek_ = false;
283 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, seek_time);
284 return;
287 if (doing_browser_seek_) {
288 internal_seek_time = FindBufferedBrowserSeekTime_Locked(seek_time);
289 browser_seek_time_ = internal_seek_time;
290 } else {
291 expecting_regular_seek_ = false;
292 browser_seek_time_ = media::kNoTimestamp();
296 // Prepare |chunk_demuxer_| for browser seek.
297 if (is_browser_seek) {
298 chunk_demuxer_->CancelPendingSeek(internal_seek_time);
299 chunk_demuxer_->StartWaitingForSeek(internal_seek_time);
302 SeekInternal(internal_seek_time);
305 void MediaSourceDelegate::SeekInternal(const base::TimeDelta& seek_time) {
306 DCHECK(media_task_runner_->BelongsToCurrentThread());
307 DCHECK(IsSeeking());
308 chunk_demuxer_->Seek(seek_time, base::Bind(
309 &MediaSourceDelegate::OnDemuxerSeekDone,
310 media_weak_factory_.GetWeakPtr()));
313 void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start,
314 base::TimeDelta end) {
315 buffered_time_ranges_.Add(start, end);
318 void MediaSourceDelegate::SetDuration(base::TimeDelta duration) {
319 DCHECK(main_task_runner_->BelongsToCurrentThread());
320 DVLOG(1) << __FUNCTION__ << "(" << duration.InSecondsF() << ") : "
321 << demuxer_client_id_;
323 // Force duration change notification to be async to avoid reentrancy into
324 // ChunkDemxuer.
325 main_task_runner_->PostTask(FROM_HERE, base::Bind(
326 &MediaSourceDelegate::OnDurationChanged, main_weak_this_, duration));
329 void MediaSourceDelegate::OnDurationChanged(const base::TimeDelta& duration) {
330 DCHECK(main_task_runner_->BelongsToCurrentThread());
331 if (demuxer_client_)
332 demuxer_client_->DurationChanged(demuxer_client_id_, duration);
333 if (!duration_change_cb_.is_null())
334 duration_change_cb_.Run(duration);
337 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type) {
338 DCHECK(media_task_runner_->BelongsToCurrentThread());
339 DVLOG(1) << __FUNCTION__ << "(" << type << ") : " << demuxer_client_id_;
340 if (IsSeeking())
341 return; // Drop the request during seeking.
343 DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO);
344 // The access unit size should have been initialized properly at this stage.
345 DCHECK_GT(access_unit_size_, 0u);
346 scoped_ptr<DemuxerData> data(new DemuxerData());
347 data->type = type;
348 data->access_units.resize(access_unit_size_);
349 ReadFromDemuxerStream(type, data.Pass(), 0);
352 void MediaSourceDelegate::ReadFromDemuxerStream(media::DemuxerStream::Type type,
353 scoped_ptr<DemuxerData> data,
354 size_t index) {
355 DCHECK(media_task_runner_->BelongsToCurrentThread());
356 // DemuxerStream::Read() always returns the read callback asynchronously.
357 DemuxerStream* stream =
358 (type == DemuxerStream::AUDIO) ? audio_stream_ : video_stream_;
359 stream->Read(base::Bind(
360 &MediaSourceDelegate::OnBufferReady,
361 media_weak_factory_.GetWeakPtr(), type, base::Passed(&data), index));
364 void MediaSourceDelegate::OnBufferReady(
365 media::DemuxerStream::Type type,
366 scoped_ptr<DemuxerData> data,
367 size_t index,
368 DemuxerStream::Status status,
369 const scoped_refptr<media::DecoderBuffer>& buffer) {
370 DCHECK(media_task_runner_->BelongsToCurrentThread());
371 DVLOG(1) << __FUNCTION__ << "(" << index << ", " << status << ", "
372 << ((!buffer.get() || buffer->end_of_stream())
373 ? -1
374 : buffer->timestamp().InMilliseconds())
375 << ") : " << demuxer_client_id_;
376 DCHECK(chunk_demuxer_);
378 // No new OnReadFromDemuxer() will be called during seeking. So this callback
379 // must be from previous OnReadFromDemuxer() call and should be ignored.
380 if (IsSeeking()) {
381 DVLOG(1) << __FUNCTION__ << ": Ignore previous read during seeking.";
382 return;
385 bool is_audio = (type == DemuxerStream::AUDIO);
386 if (status != DemuxerStream::kAborted &&
387 index >= data->access_units.size()) {
388 LOG(ERROR) << "The internal state inconsistency onBufferReady: "
389 << (is_audio ? "Audio" : "Video") << ", index " << index
390 << ", size " << data->access_units.size()
391 << ", status " << static_cast<int>(status);
392 NOTREACHED();
393 return;
396 switch (status) {
397 case DemuxerStream::kAborted:
398 DVLOG(1) << __FUNCTION__ << " : Aborted";
399 data->access_units[index].status = status;
400 data->access_units.resize(index + 1);
401 break;
403 case DemuxerStream::kConfigChanged:
404 CHECK((is_audio && audio_stream_) || (!is_audio && video_stream_));
405 data->demuxer_configs.resize(1);
406 CHECK(GetDemuxerConfigFromStream(&data->demuxer_configs[0], is_audio));
407 if (!is_audio) {
408 gfx::Size size = data->demuxer_configs[0].video_size;
409 DVLOG(1) << "Video config is changed: " << size.width() << "x"
410 << size.height();
412 data->access_units[index].status = status;
413 data->access_units.resize(index + 1);
414 break;
416 case DemuxerStream::kOk:
417 data->access_units[index].status = status;
418 if (buffer->end_of_stream()) {
419 data->access_units[index].is_end_of_stream = true;
420 data->access_units.resize(index + 1);
421 break;
423 data->access_units[index].is_key_frame = buffer->is_key_frame();
424 // TODO(ycheo): We assume that the inputed stream will be decoded
425 // right away.
426 // Need to implement this properly using MediaPlayer.OnInfoListener.
427 if (is_audio) {
428 statistics_.audio_bytes_decoded += buffer->data_size();
429 } else {
430 statistics_.video_bytes_decoded += buffer->data_size();
431 statistics_.video_frames_decoded++;
433 data->access_units[index].timestamp = buffer->timestamp();
435 data->access_units[index].data.assign(
436 buffer->data(), buffer->data() + buffer->data_size());
437 // Vorbis needs 4 extra bytes padding on Android. Check
438 // NuMediaExtractor.cpp in Android source code.
439 if (is_audio && media::kCodecVorbis ==
440 audio_stream_->audio_decoder_config().codec()) {
441 data->access_units[index].data.insert(
442 data->access_units[index].data.end(), kVorbisPadding,
443 kVorbisPadding + 4);
445 if (buffer->decrypt_config()) {
446 data->access_units[index].key_id = std::vector<char>(
447 buffer->decrypt_config()->key_id().begin(),
448 buffer->decrypt_config()->key_id().end());
449 data->access_units[index].iv = std::vector<char>(
450 buffer->decrypt_config()->iv().begin(),
451 buffer->decrypt_config()->iv().end());
452 data->access_units[index].subsamples =
453 buffer->decrypt_config()->subsamples();
455 if (++index < data->access_units.size()) {
456 ReadFromDemuxerStream(type, data.Pass(), index);
457 return;
459 break;
461 default:
462 NOTREACHED();
465 if (!IsSeeking() && demuxer_client_)
466 demuxer_client_->ReadFromDemuxerAck(demuxer_client_id_, *data);
469 void MediaSourceDelegate::OnDemuxerError(media::PipelineStatus status) {
470 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
471 // |update_network_state_cb_| is bound to the main thread.
472 if (status != media::PIPELINE_OK && !update_network_state_cb_.is_null())
473 update_network_state_cb_.Run(PipelineErrorToNetworkState(status));
476 void MediaSourceDelegate::AddTextStream(
477 media::DemuxerStream* /* text_stream */ ,
478 const media::TextTrackConfig& /* config */ ) {
479 // TODO(matthewjheaney): add text stream (http://crbug/322115).
480 NOTIMPLEMENTED();
483 void MediaSourceDelegate::RemoveTextStream(
484 media::DemuxerStream* /* text_stream */ ) {
485 // TODO(matthewjheaney): remove text stream (http://crbug/322115).
486 NOTIMPLEMENTED();
489 void MediaSourceDelegate::OnDemuxerInitDone(media::PipelineStatus status) {
490 DCHECK(media_task_runner_->BelongsToCurrentThread());
491 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
492 DCHECK(chunk_demuxer_);
494 if (status != media::PIPELINE_OK) {
495 OnDemuxerError(status);
496 return;
499 audio_stream_ = chunk_demuxer_->GetStream(DemuxerStream::AUDIO);
500 video_stream_ = chunk_demuxer_->GetStream(DemuxerStream::VIDEO);
502 if (audio_stream_ && audio_stream_->audio_decoder_config().is_encrypted() &&
503 !set_decryptor_ready_cb_.is_null()) {
504 InitAudioDecryptingDemuxerStream();
505 // InitVideoDecryptingDemuxerStream() will be called in
506 // OnAudioDecryptingDemuxerStreamInitDone().
507 return;
510 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted() &&
511 !set_decryptor_ready_cb_.is_null()) {
512 InitVideoDecryptingDemuxerStream();
513 return;
516 // Notify demuxer ready when both streams are not encrypted.
517 is_demuxer_ready_ = true;
518 NotifyDemuxerReady();
521 void MediaSourceDelegate::InitAudioDecryptingDemuxerStream() {
522 DCHECK(media_task_runner_->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_task_runner_, set_decryptor_ready_cb_,
528 waiting_for_decryption_key_cb_));
529 audio_decrypting_demuxer_stream_->Initialize(
530 audio_stream_,
531 base::Bind(&MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone,
532 media_weak_factory_.GetWeakPtr()));
535 void MediaSourceDelegate::InitVideoDecryptingDemuxerStream() {
536 DCHECK(media_task_runner_->BelongsToCurrentThread());
537 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
538 DCHECK(!set_decryptor_ready_cb_.is_null());
540 video_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
541 media_task_runner_, set_decryptor_ready_cb_,
542 waiting_for_decryption_key_cb_));
543 video_decrypting_demuxer_stream_->Initialize(
544 video_stream_,
545 base::Bind(&MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone,
546 media_weak_factory_.GetWeakPtr()));
549 void MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone(
550 media::PipelineStatus status) {
551 DCHECK(media_task_runner_->BelongsToCurrentThread());
552 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
553 DCHECK(chunk_demuxer_);
555 if (status != media::PIPELINE_OK)
556 audio_decrypting_demuxer_stream_.reset();
557 else
558 audio_stream_ = audio_decrypting_demuxer_stream_.get();
560 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted()) {
561 InitVideoDecryptingDemuxerStream();
562 return;
565 // Try to notify demuxer ready when audio DDS initialization finished and
566 // video is not encrypted.
567 is_demuxer_ready_ = true;
568 NotifyDemuxerReady();
571 void MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone(
572 media::PipelineStatus status) {
573 DCHECK(media_task_runner_->BelongsToCurrentThread());
574 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
575 DCHECK(chunk_demuxer_);
577 if (status != media::PIPELINE_OK)
578 video_decrypting_demuxer_stream_.reset();
579 else
580 video_stream_ = video_decrypting_demuxer_stream_.get();
582 // Try to notify demuxer ready when video DDS initialization finished.
583 is_demuxer_ready_ = true;
584 NotifyDemuxerReady();
587 void MediaSourceDelegate::OnDemuxerSeekDone(media::PipelineStatus status) {
588 DCHECK(media_task_runner_->BelongsToCurrentThread());
589 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
590 DCHECK(IsSeeking());
592 if (status != media::PIPELINE_OK) {
593 OnDemuxerError(status);
594 return;
597 ResetAudioDecryptingDemuxerStream();
600 void MediaSourceDelegate::ResetAudioDecryptingDemuxerStream() {
601 DCHECK(media_task_runner_->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()));
607 return;
610 ResetVideoDecryptingDemuxerStream();
613 void MediaSourceDelegate::ResetVideoDecryptingDemuxerStream() {
614 DCHECK(media_task_runner_->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()));
620 return;
623 FinishResettingDecryptingDemuxerStreams();
626 void MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams() {
627 DCHECK(media_task_runner_->BelongsToCurrentThread());
628 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
630 base::AutoLock auto_lock(seeking_lock_);
631 DCHECK(seeking_);
632 seeking_ = false;
633 doing_browser_seek_ = false;
634 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, browser_seek_time_);
637 void MediaSourceDelegate::NotifyDemuxerReady() {
638 DCHECK(media_task_runner_->BelongsToCurrentThread());
639 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
640 DCHECK(is_demuxer_ready_);
642 scoped_ptr<DemuxerConfigs> configs(new DemuxerConfigs());
643 GetDemuxerConfigFromStream(configs.get(), true);
644 GetDemuxerConfigFromStream(configs.get(), false);
645 configs->duration = GetDuration();
647 if (demuxer_client_)
648 demuxer_client_->DemuxerReady(demuxer_client_id_, *configs);
650 base::AutoLock auto_lock(is_video_encrypted_lock_);
651 is_video_encrypted_ = configs->is_video_encrypted;
654 base::TimeDelta MediaSourceDelegate::GetDuration() const {
655 DCHECK(media_task_runner_->BelongsToCurrentThread());
656 if (!chunk_demuxer_)
657 return media::kNoTimestamp();
659 double duration = chunk_demuxer_->GetDuration();
660 if (duration == std::numeric_limits<double>::infinity())
661 return media::kInfiniteDuration();
663 return media::ConvertSecondsToTimestamp(duration);
666 void MediaSourceDelegate::OnDemuxerOpened() {
667 DCHECK(main_task_runner_->BelongsToCurrentThread());
668 if (media_source_opened_cb_.is_null())
669 return;
671 media_source_opened_cb_.Run(new media::WebMediaSourceImpl(
672 chunk_demuxer_.get(), base::Bind(&LogMediaSourceError, media_log_)));
675 void MediaSourceDelegate::OnEncryptedMediaInitData(
676 const std::string& init_data_type,
677 const std::vector<uint8>& init_data) {
678 DCHECK(main_task_runner_->BelongsToCurrentThread());
679 if (encrypted_media_init_data_cb_.is_null())
680 return;
682 encrypted_media_init_data_cb_.Run(init_data_type, init_data);
685 bool MediaSourceDelegate::IsSeeking() const {
686 base::AutoLock auto_lock(seeking_lock_);
687 return seeking_;
690 base::TimeDelta MediaSourceDelegate::FindBufferedBrowserSeekTime_Locked(
691 const base::TimeDelta& seek_time) const {
692 seeking_lock_.AssertAcquired();
693 DCHECK(seeking_);
694 DCHECK(doing_browser_seek_);
695 DCHECK(chunk_demuxer_) << "Browser seek requested, but no chunk demuxer";
697 media::Ranges<base::TimeDelta> buffered =
698 chunk_demuxer_->GetBufferedRanges();
700 for (size_t i = 0; i < buffered.size(); ++i) {
701 base::TimeDelta range_start = buffered.start(i);
702 base::TimeDelta range_end = buffered.end(i);
703 if (range_start <= seek_time) {
704 if (range_end >= seek_time)
705 return seek_time;
706 continue;
709 // If the start of the next buffered range after |seek_time| is too far
710 // into the future, do not jump forward.
711 if ((range_start - seek_time) > base::TimeDelta::FromMilliseconds(100))
712 break;
714 // TODO(wolenetz): Remove possibility that this browser seek jumps
715 // into future when the requested range is unbuffered but there is some
716 // other buffered range after it. See http://crbug.com/304234.
717 return range_start;
720 // We found no range containing |seek_time| or beginning shortly after
721 // |seek_time|. While possible that such data at and beyond the player's
722 // current time have been garbage collected or removed by the web app, this is
723 // unlikely. This may cause unexpected playback stall due to seek pending an
724 // append for a GOP prior to the last GOP demuxed.
725 // TODO(wolenetz): Remove the possibility for this seek to cause unexpected
726 // player stall by replaying cached data since last keyframe in browser player
727 // rather than issuing browser seek. See http://crbug.com/304234.
728 return seek_time;
731 bool MediaSourceDelegate::GetDemuxerConfigFromStream(
732 media::DemuxerConfigs* configs, bool is_audio) {
733 DCHECK(media_task_runner_->BelongsToCurrentThread());
734 if (!is_demuxer_ready_)
735 return false;
736 if (is_audio && audio_stream_) {
737 media::AudioDecoderConfig config = audio_stream_->audio_decoder_config();
738 configs->audio_codec = config.codec();
739 configs->audio_channels =
740 media::ChannelLayoutToChannelCount(config.channel_layout());
741 configs->audio_sampling_rate = config.samples_per_second();
742 configs->is_audio_encrypted = config.is_encrypted();
743 configs->audio_extra_data = std::vector<uint8>(
744 config.extra_data(), config.extra_data() + config.extra_data_size());
745 configs->audio_codec_delay_ns = static_cast<int64_t>(
746 config.codec_delay() *
747 (static_cast<double>(base::Time::kNanosecondsPerSecond) /
748 config.samples_per_second()));
749 configs->audio_seek_preroll_ns =
750 config.seek_preroll().InMicroseconds() *
751 base::Time::kNanosecondsPerMicrosecond;
752 return true;
754 if (!is_audio && video_stream_) {
755 media::VideoDecoderConfig config = video_stream_->video_decoder_config();
756 configs->video_codec = config.codec();
757 configs->video_size = config.natural_size();
758 configs->is_video_encrypted = config.is_encrypted();
759 configs->video_extra_data = std::vector<uint8>(
760 config.extra_data(), config.extra_data() + config.extra_data_size());
761 return true;
763 return false;
766 } // namespace content