Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(116)

Side by Side Diff: content/renderer/media/android/media_source_delegate.cc

Issue 2283493003: Delete browser MSE implementation. (Closed)
Patch Set: Actually delete MSP. Cleanse references. Remove AudioTrack usage. Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "content/renderer/media/android/media_source_delegate.h"
6
7 #include <limits>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/callback_helpers.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/threading/thread_task_runner_handle.h"
15 #include "content/renderer/media/android/renderer_demuxer_android.h"
16 #include "media/base/android/demuxer_stream_player_params.h"
17 #include "media/base/bind_to_current_loop.h"
18 #include "media/base/demuxer_stream.h"
19 #include "media/base/media_log.h"
20 #include "media/base/timestamp_constants.h"
21 #include "media/blink/webmediaplayer_util.h"
22 #include "media/blink/webmediasource_impl.h"
23 #include "media/filters/chunk_demuxer.h"
24 #include "media/filters/decrypting_demuxer_stream.h"
25 #include "third_party/WebKit/public/platform/WebString.h"
26 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
27
28 using media::DemuxerStream;
29 using media::DemuxerConfigs;
30 using media::DemuxerData;
31 using blink::WebMediaPlayer;
32 using blink::WebString;
33
34 namespace {
35
36 // The size of the access unit to transfer in an IPC in case of MediaSource.
37 // 4: approximately 64ms of content in 60 fps movies.
38 const size_t kAccessUnitSizeForMediaSource = 4;
39
40 const uint8_t kVorbisPadding[] = {0xff, 0xff, 0xff, 0xff};
41
42 } // namespace
43
44 namespace content {
45
46 MediaSourceDelegate::MediaSourceDelegate(
47 RendererDemuxerAndroid* demuxer_client,
48 int demuxer_client_id,
49 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
50 const scoped_refptr<media::MediaLog> media_log)
51 : demuxer_client_(demuxer_client),
52 demuxer_client_id_(demuxer_client_id),
53 media_log_(media_log),
54 is_demuxer_ready_(false),
55 audio_stream_(NULL),
56 video_stream_(NULL),
57 seeking_(false),
58 is_video_encrypted_(false),
59 doing_browser_seek_(false),
60 browser_seek_time_(media::kNoTimestamp),
61 expecting_regular_seek_(false),
62 access_unit_size_(0),
63 main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
64 media_task_runner_(media_task_runner),
65 main_weak_factory_(this),
66 media_weak_factory_(this) {
67 main_weak_this_ = main_weak_factory_.GetWeakPtr();
68 DCHECK(main_task_runner_->BelongsToCurrentThread());
69 }
70
71 MediaSourceDelegate::~MediaSourceDelegate() {
72 DCHECK(main_task_runner_->BelongsToCurrentThread());
73 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
74 DCHECK(!chunk_demuxer_);
75 DCHECK(!demuxer_client_);
76 DCHECK(!audio_decrypting_demuxer_stream_);
77 DCHECK(!video_decrypting_demuxer_stream_);
78 DCHECK(!audio_stream_);
79 DCHECK(!video_stream_);
80 }
81
82 void MediaSourceDelegate::Stop(const base::Closure& stop_cb) {
83 DCHECK(main_task_runner_->BelongsToCurrentThread());
84 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
85
86 if (!chunk_demuxer_) {
87 DCHECK(!demuxer_client_);
88 return;
89 }
90
91 duration_change_cb_.Reset();
92 update_network_state_cb_.Reset();
93 media_source_opened_cb_.Reset();
94
95 main_weak_factory_.InvalidateWeakPtrs();
96 DCHECK(!main_weak_factory_.HasWeakPtrs());
97
98 chunk_demuxer_->Shutdown();
99
100 // Continue to stop objects on the media thread.
101 media_task_runner_->PostTask(
102 FROM_HERE,
103 base::Bind(
104 &MediaSourceDelegate::StopDemuxer, base::Unretained(this), stop_cb));
105 }
106
107 bool MediaSourceDelegate::IsVideoEncrypted() {
108 DCHECK(main_task_runner_->BelongsToCurrentThread());
109 base::AutoLock auto_lock(is_video_encrypted_lock_);
110 return is_video_encrypted_;
111 }
112
113 base::Time MediaSourceDelegate::GetTimelineOffset() const {
114 DCHECK(main_task_runner_->BelongsToCurrentThread());
115 if (!chunk_demuxer_)
116 return base::Time();
117
118 return chunk_demuxer_->GetTimelineOffset();
119 }
120
121 void MediaSourceDelegate::StopDemuxer(const base::Closure& stop_cb) {
122 DVLOG(2) << __FUNCTION__;
123 DCHECK(media_task_runner_->BelongsToCurrentThread());
124 DCHECK(chunk_demuxer_);
125
126 demuxer_client_->RemoveDelegate(demuxer_client_id_);
127 demuxer_client_ = NULL;
128
129 audio_stream_ = NULL;
130 video_stream_ = NULL;
131 // TODO(xhwang): Figure out if we need to Reset the DDSs after Seeking or
132 // before destroying them.
133 audio_decrypting_demuxer_stream_.reset();
134 video_decrypting_demuxer_stream_.reset();
135
136 media_weak_factory_.InvalidateWeakPtrs();
137 DCHECK(!media_weak_factory_.HasWeakPtrs());
138
139 chunk_demuxer_->Stop();
140 chunk_demuxer_.reset();
141
142 // |this| may be destroyed at this point in time as a result of running
143 // |stop_cb|.
144 stop_cb.Run();
145 }
146
147 void MediaSourceDelegate::InitializeMediaSource(
148 const MediaSourceOpenedCB& media_source_opened_cb,
149 const media::Demuxer::EncryptedMediaInitDataCB&
150 encrypted_media_init_data_cb,
151 const SetCdmReadyCB& set_cdm_ready_cb,
152 const UpdateNetworkStateCB& update_network_state_cb,
153 const DurationChangeCB& duration_change_cb,
154 const base::Closure& waiting_for_decryption_key_cb) {
155 DCHECK(main_task_runner_->BelongsToCurrentThread());
156 DCHECK(!media_source_opened_cb.is_null());
157 DCHECK(!encrypted_media_init_data_cb.is_null());
158 DCHECK(!set_cdm_ready_cb.is_null());
159 DCHECK(!update_network_state_cb.is_null());
160 DCHECK(!duration_change_cb.is_null());
161 DCHECK(!waiting_for_decryption_key_cb.is_null());
162
163 media_source_opened_cb_ = media_source_opened_cb;
164 encrypted_media_init_data_cb_ = encrypted_media_init_data_cb;
165 set_cdm_ready_cb_ = media::BindToCurrentLoop(set_cdm_ready_cb);
166 update_network_state_cb_ = media::BindToCurrentLoop(update_network_state_cb);
167 duration_change_cb_ = duration_change_cb;
168 waiting_for_decryption_key_cb_ =
169 media::BindToCurrentLoop(waiting_for_decryption_key_cb);
170 access_unit_size_ = kAccessUnitSizeForMediaSource;
171
172 chunk_demuxer_.reset(new media::ChunkDemuxer(
173 media::BindToCurrentLoop(
174 base::Bind(&MediaSourceDelegate::OnDemuxerOpened, main_weak_this_)),
175 media::BindToCurrentLoop(base::Bind(
176 &MediaSourceDelegate::OnEncryptedMediaInitData, main_weak_this_)),
177 media_log_, false));
178
179 // |this| will be retained until StopDemuxer() is posted, so Unretained() is
180 // safe here.
181 media_task_runner_->PostTask(FROM_HERE,
182 base::Bind(&MediaSourceDelegate::InitializeDemuxer,
183 base::Unretained(this)));
184 }
185
186 void MediaSourceDelegate::InitializeDemuxer() {
187 DCHECK(media_task_runner_->BelongsToCurrentThread());
188 demuxer_client_->AddDelegate(demuxer_client_id_, this);
189 chunk_demuxer_->Initialize(this,
190 base::Bind(&MediaSourceDelegate::OnDemuxerInitDone,
191 media_weak_factory_.GetWeakPtr()),
192 false);
193 }
194
195 blink::WebTimeRanges MediaSourceDelegate::Buffered() const {
196 return media::ConvertToWebTimeRanges(buffered_time_ranges_);
197 }
198
199 size_t MediaSourceDelegate::DecodedFrameCount() const {
200 return statistics_.video_frames_decoded;
201 }
202
203 size_t MediaSourceDelegate::DroppedFrameCount() const {
204 return statistics_.video_frames_dropped;
205 }
206
207 size_t MediaSourceDelegate::AudioDecodedByteCount() const {
208 return statistics_.audio_bytes_decoded;
209 }
210
211 size_t MediaSourceDelegate::VideoDecodedByteCount() const {
212 return statistics_.video_bytes_decoded;
213 }
214
215 void MediaSourceDelegate::CancelPendingSeek(const base::TimeDelta& seek_time) {
216 DCHECK(main_task_runner_->BelongsToCurrentThread());
217 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : "
218 << demuxer_client_id_;
219
220 if (!chunk_demuxer_)
221 return;
222
223 {
224 // Remember to trivially finish any newly arriving browser seek requests
225 // that may arrive prior to the next regular seek request.
226 base::AutoLock auto_lock(seeking_lock_);
227 expecting_regular_seek_ = true;
228 }
229
230 // Cancel any previously expected or in-progress regular or browser seek.
231 // It is possible that we have just finished the seek, but caller does
232 // not know this yet. It is still safe to cancel in this case because the
233 // caller will always call StartWaitingForSeek() when it is notified of
234 // the finished seek.
235 chunk_demuxer_->CancelPendingSeek(seek_time);
236 }
237
238 void MediaSourceDelegate::StartWaitingForSeek(
239 const base::TimeDelta& seek_time) {
240 DCHECK(main_task_runner_->BelongsToCurrentThread());
241 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : "
242 << demuxer_client_id_;
243
244 if (!chunk_demuxer_)
245 return;
246
247 bool cancel_browser_seek = false;
248 {
249 // Remember to trivially finish any newly arriving browser seek requests
250 // that may arrive prior to the next regular seek request.
251 base::AutoLock auto_lock(seeking_lock_);
252 expecting_regular_seek_ = true;
253
254 // Remember to cancel any in-progress browser seek.
255 if (seeking_) {
256 DCHECK(doing_browser_seek_);
257 cancel_browser_seek = true;
258 }
259 }
260
261 if (cancel_browser_seek)
262 chunk_demuxer_->CancelPendingSeek(seek_time);
263 chunk_demuxer_->StartWaitingForSeek(seek_time);
264 }
265
266 void MediaSourceDelegate::Seek(
267 const base::TimeDelta& seek_time, bool is_browser_seek) {
268 DCHECK(media_task_runner_->BelongsToCurrentThread());
269 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ", "
270 << (is_browser_seek ? "browser seek" : "regular seek") << ") : "
271 << demuxer_client_id_;
272
273 base::TimeDelta internal_seek_time = seek_time;
274 {
275 base::AutoLock auto_lock(seeking_lock_);
276 DCHECK(!seeking_);
277 seeking_ = true;
278 doing_browser_seek_ = is_browser_seek;
279
280 if (doing_browser_seek_ && (!chunk_demuxer_ || expecting_regular_seek_)) {
281 // Trivially finish the browser seek without actually doing it. Reads will
282 // continue to be |kAborted| until the next regular seek is done. Browser
283 // seeking is not supported unless using a ChunkDemuxer; browser seeks are
284 // trivially finished if |chunk_demuxer_| is NULL.
285 seeking_ = false;
286 doing_browser_seek_ = false;
287 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, seek_time);
288 return;
289 }
290
291 if (doing_browser_seek_) {
292 internal_seek_time = FindBufferedBrowserSeekTime_Locked(seek_time);
293 browser_seek_time_ = internal_seek_time;
294 } else {
295 expecting_regular_seek_ = false;
296 browser_seek_time_ = media::kNoTimestamp;
297 }
298 }
299
300 // Prepare |chunk_demuxer_| for browser seek.
301 if (is_browser_seek) {
302 chunk_demuxer_->CancelPendingSeek(internal_seek_time);
303 chunk_demuxer_->StartWaitingForSeek(internal_seek_time);
304 }
305
306 SeekInternal(internal_seek_time);
307 }
308
309 void MediaSourceDelegate::SeekInternal(const base::TimeDelta& seek_time) {
310 DCHECK(media_task_runner_->BelongsToCurrentThread());
311 DCHECK(IsSeeking());
312 chunk_demuxer_->Seek(seek_time, base::Bind(
313 &MediaSourceDelegate::OnDemuxerSeekDone,
314 media_weak_factory_.GetWeakPtr()));
315 }
316
317 void MediaSourceDelegate::OnBufferedTimeRangesChanged(
318 const media::Ranges<base::TimeDelta>& ranges) {
319 buffered_time_ranges_ = ranges;
320 }
321
322 void MediaSourceDelegate::SetDuration(base::TimeDelta duration) {
323 DCHECK(main_task_runner_->BelongsToCurrentThread());
324 DVLOG(1) << __FUNCTION__ << "(" << duration.InSecondsF() << ") : "
325 << demuxer_client_id_;
326
327 // Force duration change notification to be async to avoid reentrancy into
328 // ChunkDemxuer.
329 main_task_runner_->PostTask(FROM_HERE, base::Bind(
330 &MediaSourceDelegate::OnDurationChanged, main_weak_this_, duration));
331 }
332
333 void MediaSourceDelegate::OnDurationChanged(const base::TimeDelta& duration) {
334 DCHECK(main_task_runner_->BelongsToCurrentThread());
335 if (demuxer_client_)
336 demuxer_client_->DurationChanged(demuxer_client_id_, duration);
337 if (!duration_change_cb_.is_null())
338 duration_change_cb_.Run(duration);
339 }
340
341 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type) {
342 DCHECK(media_task_runner_->BelongsToCurrentThread());
343 DVLOG(1) << __FUNCTION__ << "(" << type << ") : " << demuxer_client_id_;
344 if (IsSeeking())
345 return; // Drop the request during seeking.
346
347 DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO);
348 // The access unit size should have been initialized properly at this stage.
349 DCHECK_GT(access_unit_size_, 0u);
350 std::unique_ptr<DemuxerData> data(new DemuxerData());
351 data->type = type;
352 data->access_units.resize(access_unit_size_);
353 ReadFromDemuxerStream(type, std::move(data), 0);
354 }
355
356 void MediaSourceDelegate::ReadFromDemuxerStream(
357 media::DemuxerStream::Type type,
358 std::unique_ptr<DemuxerData> data,
359 size_t index) {
360 DCHECK(media_task_runner_->BelongsToCurrentThread());
361 // DemuxerStream::Read() always returns the read callback asynchronously.
362 DemuxerStream* stream =
363 (type == DemuxerStream::AUDIO) ? audio_stream_ : video_stream_;
364 stream->Read(base::Bind(
365 &MediaSourceDelegate::OnBufferReady,
366 media_weak_factory_.GetWeakPtr(), type, base::Passed(&data), index));
367 }
368
369 void MediaSourceDelegate::OnBufferReady(
370 media::DemuxerStream::Type type,
371 std::unique_ptr<DemuxerData> data,
372 size_t index,
373 DemuxerStream::Status status,
374 const scoped_refptr<media::DecoderBuffer>& buffer) {
375 DCHECK(media_task_runner_->BelongsToCurrentThread());
376 DVLOG(1) << __FUNCTION__ << "(" << index << ", " << status << ", "
377 << ((!buffer.get() || buffer->end_of_stream())
378 ? -1
379 : buffer->timestamp().InMilliseconds())
380 << ") : " << demuxer_client_id_;
381 DCHECK(chunk_demuxer_);
382
383 // No new OnReadFromDemuxer() will be called during seeking. So this callback
384 // must be from previous OnReadFromDemuxer() call and should be ignored.
385 if (IsSeeking()) {
386 DVLOG(1) << __FUNCTION__ << ": Ignore previous read during seeking.";
387 return;
388 }
389
390 bool is_audio = (type == DemuxerStream::AUDIO);
391 if (status != DemuxerStream::kAborted &&
392 index >= data->access_units.size()) {
393 LOG(ERROR) << "The internal state inconsistency onBufferReady: "
394 << (is_audio ? "Audio" : "Video") << ", index " << index
395 << ", size " << data->access_units.size()
396 << ", status " << static_cast<int>(status);
397 NOTREACHED();
398 return;
399 }
400
401 switch (status) {
402 case DemuxerStream::kAborted:
403 DVLOG(1) << __FUNCTION__ << " : Aborted";
404 data->access_units[index].status = status;
405 data->access_units.resize(index + 1);
406 break;
407
408 case DemuxerStream::kConfigChanged:
409 CHECK((is_audio && audio_stream_) || (!is_audio && video_stream_));
410 data->demuxer_configs.resize(1);
411 CHECK(GetDemuxerConfigFromStream(&data->demuxer_configs[0], is_audio));
412 if (!is_audio) {
413 gfx::Size size = data->demuxer_configs[0].video_size;
414 DVLOG(1) << "Video config is changed: " << size.width() << "x"
415 << size.height();
416 }
417 data->access_units[index].status = status;
418 data->access_units.resize(index + 1);
419 break;
420
421 case DemuxerStream::kOk:
422 data->access_units[index].status = status;
423 if (buffer->end_of_stream()) {
424 data->access_units[index].is_end_of_stream = true;
425 data->access_units.resize(index + 1);
426 break;
427 }
428 data->access_units[index].is_key_frame = buffer->is_key_frame();
429 // TODO(ycheo): We assume that the inputed stream will be decoded
430 // right away.
431 // Need to implement this properly using MediaPlayer.OnInfoListener.
432 if (is_audio) {
433 statistics_.audio_bytes_decoded += buffer->data_size();
434 } else {
435 statistics_.video_bytes_decoded += buffer->data_size();
436 statistics_.video_frames_decoded++;
437 }
438 data->access_units[index].timestamp = buffer->timestamp();
439
440 data->access_units[index].data.assign(
441 buffer->data(), buffer->data() + buffer->data_size());
442 // Vorbis needs 4 extra bytes padding on Android. Check
443 // NuMediaExtractor.cpp in Android source code.
444 if (is_audio && media::kCodecVorbis ==
445 audio_stream_->audio_decoder_config().codec()) {
446 data->access_units[index].data.insert(
447 data->access_units[index].data.end(), kVorbisPadding,
448 kVorbisPadding + 4);
449 }
450 if (buffer->decrypt_config()) {
451 data->access_units[index].key_id = std::vector<char>(
452 buffer->decrypt_config()->key_id().begin(),
453 buffer->decrypt_config()->key_id().end());
454 data->access_units[index].iv = std::vector<char>(
455 buffer->decrypt_config()->iv().begin(),
456 buffer->decrypt_config()->iv().end());
457 data->access_units[index].subsamples =
458 buffer->decrypt_config()->subsamples();
459 }
460 if (++index < data->access_units.size()) {
461 ReadFromDemuxerStream(type, std::move(data), index);
462 return;
463 }
464 break;
465
466 default:
467 NOTREACHED();
468 }
469
470 if (!IsSeeking() && demuxer_client_)
471 demuxer_client_->ReadFromDemuxerAck(demuxer_client_id_, *data);
472 }
473
474 void MediaSourceDelegate::OnDemuxerError(media::PipelineStatus status) {
475 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
476 // |update_network_state_cb_| is bound to the main thread.
477 if (status != media::PIPELINE_OK && !update_network_state_cb_.is_null())
478 update_network_state_cb_.Run(PipelineErrorToNetworkState(status));
479 }
480
481 void MediaSourceDelegate::AddTextStream(
482 media::DemuxerStream* /* text_stream */ ,
483 const media::TextTrackConfig& /* config */ ) {
484 // TODO(matthewjheaney): add text stream (http://crbug/322115).
485 NOTIMPLEMENTED();
486 }
487
488 void MediaSourceDelegate::RemoveTextStream(
489 media::DemuxerStream* /* text_stream */ ) {
490 // TODO(matthewjheaney): remove text stream (http://crbug/322115).
491 NOTIMPLEMENTED();
492 }
493
494 void MediaSourceDelegate::OnDemuxerInitDone(media::PipelineStatus status) {
495 DCHECK(media_task_runner_->BelongsToCurrentThread());
496 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
497 DCHECK(chunk_demuxer_);
498
499 if (status != media::PIPELINE_OK) {
500 OnDemuxerError(status);
501 return;
502 }
503
504 audio_stream_ = chunk_demuxer_->GetStream(DemuxerStream::AUDIO);
505 video_stream_ = chunk_demuxer_->GetStream(DemuxerStream::VIDEO);
506 DCHECK(audio_stream_ || video_stream_);
507
508 if (HasEncryptedStream()) {
509 set_cdm_ready_cb_.Run(BindToCurrentLoop(base::Bind(
510 &MediaSourceDelegate::SetCdm, media_weak_factory_.GetWeakPtr())));
511 return;
512 }
513
514 // Notify demuxer ready when both streams are not encrypted.
515 NotifyDemuxerReady(false);
516 }
517
518 bool MediaSourceDelegate::HasEncryptedStream() {
519 DCHECK(media_task_runner_->BelongsToCurrentThread());
520 DCHECK(audio_stream_ || video_stream_);
521
522 return (audio_stream_ &&
523 audio_stream_->audio_decoder_config().is_encrypted()) ||
524 (video_stream_ &&
525 video_stream_->video_decoder_config().is_encrypted());
526 }
527
528 void MediaSourceDelegate::SetCdm(media::CdmContext* cdm_context,
529 const media::CdmAttachedCB& cdm_attached_cb) {
530 DCHECK(media_task_runner_->BelongsToCurrentThread());
531 DCHECK(cdm_context);
532 DCHECK(!cdm_attached_cb.is_null());
533 DCHECK(!is_demuxer_ready_);
534 DCHECK(HasEncryptedStream());
535
536 cdm_context_ = cdm_context;
537 pending_cdm_attached_cb_ = cdm_attached_cb;
538
539 if (audio_stream_ && audio_stream_->audio_decoder_config().is_encrypted()) {
540 InitAudioDecryptingDemuxerStream();
541 // InitVideoDecryptingDemuxerStream() will be called in
542 // OnAudioDecryptingDemuxerStreamInitDone().
543 return;
544 }
545
546 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted()) {
547 InitVideoDecryptingDemuxerStream();
548 return;
549 }
550
551 NOTREACHED() << "No encrytped stream.";
552 }
553
554 void MediaSourceDelegate::InitAudioDecryptingDemuxerStream() {
555 DCHECK(media_task_runner_->BelongsToCurrentThread());
556 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
557 DCHECK(cdm_context_);
558 audio_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
559 media_task_runner_, media_log_, waiting_for_decryption_key_cb_));
560 audio_decrypting_demuxer_stream_->Initialize(
561 audio_stream_, cdm_context_,
562 base::Bind(&MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone,
563 media_weak_factory_.GetWeakPtr()));
564 }
565
566 void MediaSourceDelegate::InitVideoDecryptingDemuxerStream() {
567 DCHECK(media_task_runner_->BelongsToCurrentThread());
568 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
569 DCHECK(cdm_context_);
570
571 video_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
572 media_task_runner_, media_log_, waiting_for_decryption_key_cb_));
573 video_decrypting_demuxer_stream_->Initialize(
574 video_stream_, cdm_context_,
575 base::Bind(&MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone,
576 media_weak_factory_.GetWeakPtr()));
577 }
578
579 void MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone(
580 media::PipelineStatus status) {
581 DCHECK(media_task_runner_->BelongsToCurrentThread());
582 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
583 DCHECK(chunk_demuxer_);
584
585 if (status != media::PIPELINE_OK) {
586 audio_decrypting_demuxer_stream_.reset();
587 // Different CDMs are supported differently. For CDMs that support a
588 // Decryptor, we'll try to use DecryptingDemuxerStream in the render side.
589 // Otherwise, we'll try to use the CDMs in the browser side. Therefore, if
590 // DecryptingDemuxerStream initialization failed, it's still possible that
591 // we can handle the audio with a CDM in the browser. Declare demuxer ready
592 // now to try that path. Note there's no need to try DecryptingDemuxerStream
593 // for video here since it is impossible to handle audio in the browser and
594 // handle video in the render process.
595 NotifyDemuxerReady(false);
596 return;
597 }
598
599 audio_stream_ = audio_decrypting_demuxer_stream_.get();
600
601 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted()) {
602 InitVideoDecryptingDemuxerStream();
603 return;
604 }
605
606 // Try to notify demuxer ready when audio DDS initialization finished and
607 // video is not encrypted.
608 NotifyDemuxerReady(true);
609 }
610
611 void MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone(
612 media::PipelineStatus status) {
613 DCHECK(media_task_runner_->BelongsToCurrentThread());
614 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
615 DCHECK(chunk_demuxer_);
616
617 bool success = status == media::PIPELINE_OK;
618
619 if (!success)
620 video_decrypting_demuxer_stream_.reset();
621 else
622 video_stream_ = video_decrypting_demuxer_stream_.get();
623
624 // Try to notify demuxer ready when video DDS initialization finished.
625 NotifyDemuxerReady(success);
626 }
627
628 void MediaSourceDelegate::OnDemuxerSeekDone(media::PipelineStatus status) {
629 DCHECK(media_task_runner_->BelongsToCurrentThread());
630 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
631 DCHECK(IsSeeking());
632
633 if (status != media::PIPELINE_OK) {
634 OnDemuxerError(status);
635 return;
636 }
637
638 ResetAudioDecryptingDemuxerStream();
639 }
640
641 void MediaSourceDelegate::ResetAudioDecryptingDemuxerStream() {
642 DCHECK(media_task_runner_->BelongsToCurrentThread());
643 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
644 if (audio_decrypting_demuxer_stream_) {
645 audio_decrypting_demuxer_stream_->Reset(
646 base::Bind(&MediaSourceDelegate::ResetVideoDecryptingDemuxerStream,
647 media_weak_factory_.GetWeakPtr()));
648 return;
649 }
650
651 ResetVideoDecryptingDemuxerStream();
652 }
653
654 void MediaSourceDelegate::ResetVideoDecryptingDemuxerStream() {
655 DCHECK(media_task_runner_->BelongsToCurrentThread());
656 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
657 if (video_decrypting_demuxer_stream_) {
658 video_decrypting_demuxer_stream_->Reset(base::Bind(
659 &MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams,
660 media_weak_factory_.GetWeakPtr()));
661 return;
662 }
663
664 FinishResettingDecryptingDemuxerStreams();
665 }
666
667 void MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams() {
668 DCHECK(media_task_runner_->BelongsToCurrentThread());
669 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
670
671 base::AutoLock auto_lock(seeking_lock_);
672 DCHECK(seeking_);
673 seeking_ = false;
674 doing_browser_seek_ = false;
675 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, browser_seek_time_);
676 }
677
678 void MediaSourceDelegate::NotifyDemuxerReady(bool is_cdm_attached) {
679 DCHECK(media_task_runner_->BelongsToCurrentThread());
680 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_
681 << ", is_cdm_attached: " << is_cdm_attached;
682 DCHECK(!is_demuxer_ready_);
683
684 is_demuxer_ready_ = true;
685
686 if (!pending_cdm_attached_cb_.is_null())
687 base::ResetAndReturn(&pending_cdm_attached_cb_).Run(is_cdm_attached);
688
689 std::unique_ptr<DemuxerConfigs> configs(new DemuxerConfigs());
690 GetDemuxerConfigFromStream(configs.get(), true);
691 GetDemuxerConfigFromStream(configs.get(), false);
692 configs->duration = GetDuration();
693
694 if (demuxer_client_)
695 demuxer_client_->DemuxerReady(demuxer_client_id_, *configs);
696
697 base::AutoLock auto_lock(is_video_encrypted_lock_);
698 is_video_encrypted_ = configs->is_video_encrypted;
699 }
700
701 base::TimeDelta MediaSourceDelegate::GetDuration() const {
702 DCHECK(media_task_runner_->BelongsToCurrentThread());
703 if (!chunk_demuxer_)
704 return media::kNoTimestamp;
705
706 double duration = chunk_demuxer_->GetDuration();
707 if (duration == std::numeric_limits<double>::infinity())
708 return media::kInfiniteDuration;
709
710 return base::TimeDelta::FromSecondsD(duration);
711 }
712
713 void MediaSourceDelegate::OnDemuxerOpened() {
714 DCHECK(main_task_runner_->BelongsToCurrentThread());
715 if (media_source_opened_cb_.is_null())
716 return;
717
718 media_source_opened_cb_.Run(
719 new media::WebMediaSourceImpl(chunk_demuxer_.get(), media_log_));
720 }
721
722 void MediaSourceDelegate::OnEncryptedMediaInitData(
723 media::EmeInitDataType init_data_type,
724 const std::vector<uint8_t>& init_data) {
725 DCHECK(main_task_runner_->BelongsToCurrentThread());
726 if (encrypted_media_init_data_cb_.is_null())
727 return;
728
729 encrypted_media_init_data_cb_.Run(init_data_type, init_data);
730 }
731
732 bool MediaSourceDelegate::IsSeeking() const {
733 base::AutoLock auto_lock(seeking_lock_);
734 return seeking_;
735 }
736
737 base::TimeDelta MediaSourceDelegate::FindBufferedBrowserSeekTime_Locked(
738 const base::TimeDelta& seek_time) const {
739 seeking_lock_.AssertAcquired();
740 DCHECK(seeking_);
741 DCHECK(doing_browser_seek_);
742 DCHECK(chunk_demuxer_) << "Browser seek requested, but no chunk demuxer";
743
744 media::Ranges<base::TimeDelta> buffered =
745 chunk_demuxer_->GetBufferedRanges();
746
747 for (size_t i = 0; i < buffered.size(); ++i) {
748 base::TimeDelta range_start = buffered.start(i);
749 base::TimeDelta range_end = buffered.end(i);
750 if (range_start <= seek_time) {
751 if (range_end >= seek_time)
752 return seek_time;
753 continue;
754 }
755
756 // If the start of the next buffered range after |seek_time| is too far
757 // into the future, do not jump forward.
758 if ((range_start - seek_time) > base::TimeDelta::FromMilliseconds(100))
759 break;
760
761 // TODO(wolenetz): Remove possibility that this browser seek jumps
762 // into future when the requested range is unbuffered but there is some
763 // other buffered range after it. See http://crbug.com/304234.
764 return range_start;
765 }
766
767 // We found no range containing |seek_time| or beginning shortly after
768 // |seek_time|. While possible that such data at and beyond the player's
769 // current time have been garbage collected or removed by the web app, this is
770 // unlikely. This may cause unexpected playback stall due to seek pending an
771 // append for a GOP prior to the last GOP demuxed.
772 // TODO(wolenetz): Remove the possibility for this seek to cause unexpected
773 // player stall by replaying cached data since last keyframe in browser player
774 // rather than issuing browser seek. See http://crbug.com/304234.
775 return seek_time;
776 }
777
778 bool MediaSourceDelegate::GetDemuxerConfigFromStream(
779 media::DemuxerConfigs* configs, bool is_audio) {
780 DCHECK(media_task_runner_->BelongsToCurrentThread());
781 if (!is_demuxer_ready_)
782 return false;
783 if (is_audio && audio_stream_) {
784 media::AudioDecoderConfig config = audio_stream_->audio_decoder_config();
785 configs->audio_codec = config.codec();
786 configs->audio_channels =
787 media::ChannelLayoutToChannelCount(config.channel_layout());
788 configs->audio_sampling_rate = config.samples_per_second();
789 configs->is_audio_encrypted = config.is_encrypted();
790 configs->audio_extra_data = config.extra_data();
791 configs->audio_codec_delay_ns = static_cast<int64_t>(
792 config.codec_delay() *
793 (static_cast<double>(base::Time::kNanosecondsPerSecond) /
794 config.samples_per_second()));
795 configs->audio_seek_preroll_ns =
796 config.seek_preroll().InMicroseconds() *
797 base::Time::kNanosecondsPerMicrosecond;
798 return true;
799 }
800 if (!is_audio && video_stream_) {
801 media::VideoDecoderConfig config = video_stream_->video_decoder_config();
802 configs->video_codec = config.codec();
803 configs->video_size = config.natural_size();
804 configs->is_video_encrypted = config.is_encrypted();
805 configs->video_extra_data = config.extra_data();
806 return true;
807 }
808 return false;
809 }
810
811 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698