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 <vector>
10
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"
24
25 using media::DemuxerStream;
26 using media::DemuxerConfigs;
27 using media::DemuxerData;
28 using blink::WebMediaPlayer;
29 using blink::WebString;
30
31 namespace {
32
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;
36
37 const uint8 kVorbisPadding[] = { 0xff, 0xff, 0xff, 0xff };
38
39 } // namespace
40
41 namespace content {
42
LogMediaSourceError(const scoped_refptr<media::MediaLog> & media_log,const std::string & error)43 static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log,
44 const std::string& error) {
45 media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error));
46 }
47
MediaSourceDelegate(RendererDemuxerAndroid * demuxer_client,int demuxer_client_id,const scoped_refptr<base::SingleThreadTaskRunner> & media_task_runner,const scoped_refptr<media::MediaLog> media_log)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());
71 }
72
~MediaSourceDelegate()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_);
82 }
83
Stop(const base::Closure & stop_cb)84 void MediaSourceDelegate::Stop(const base::Closure& stop_cb) {
85 DCHECK(main_task_runner_->BelongsToCurrentThread());
86 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
87
88 if (!chunk_demuxer_) {
89 DCHECK(!demuxer_client_);
90 return;
91 }
92
93 duration_change_cb_.Reset();
94 update_network_state_cb_.Reset();
95 media_source_opened_cb_.Reset();
96
97 main_weak_factory_.InvalidateWeakPtrs();
98 DCHECK(!main_weak_factory_.HasWeakPtrs());
99
100 chunk_demuxer_->Shutdown();
101
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));
107 }
108
IsVideoEncrypted()109 bool MediaSourceDelegate::IsVideoEncrypted() {
110 DCHECK(main_task_runner_->BelongsToCurrentThread());
111 base::AutoLock auto_lock(is_video_encrypted_lock_);
112 return is_video_encrypted_;
113 }
114
GetTimelineOffset() const115 base::Time MediaSourceDelegate::GetTimelineOffset() const {
116 DCHECK(main_task_runner_->BelongsToCurrentThread());
117 if (!chunk_demuxer_)
118 return base::Time();
119
120 return chunk_demuxer_->GetTimelineOffset();
121 }
122
StopDemuxer(const base::Closure & stop_cb)123 void MediaSourceDelegate::StopDemuxer(const base::Closure& stop_cb) {
124 DVLOG(2) << __FUNCTION__;
125 DCHECK(media_task_runner_->BelongsToCurrentThread());
126 DCHECK(chunk_demuxer_);
127
128 demuxer_client_->RemoveDelegate(demuxer_client_id_);
129 demuxer_client_ = NULL;
130
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();
137
138 media_weak_factory_.InvalidateWeakPtrs();
139 DCHECK(!media_weak_factory_.HasWeakPtrs());
140
141 chunk_demuxer_->Stop();
142 chunk_demuxer_.reset();
143
144 // |this| may be destroyed at this point in time as a result of running
145 // |stop_cb|.
146 stop_cb.Run();
147 }
148
InitializeMediaSource(const MediaSourceOpenedCB & media_source_opened_cb,const media::Demuxer::NeedKeyCB & need_key_cb,const media::SetDecryptorReadyCB & set_decryptor_ready_cb,const UpdateNetworkStateCB & update_network_state_cb,const DurationChangeCB & duration_change_cb)149 void MediaSourceDelegate::InitializeMediaSource(
150 const MediaSourceOpenedCB& media_source_opened_cb,
151 const media::Demuxer::NeedKeyCB& need_key_cb,
152 const media::SetDecryptorReadyCB& set_decryptor_ready_cb,
153 const UpdateNetworkStateCB& update_network_state_cb,
154 const DurationChangeCB& duration_change_cb) {
155 DCHECK(main_task_runner_->BelongsToCurrentThread());
156 DCHECK(!media_source_opened_cb.is_null());
157 media_source_opened_cb_ = media_source_opened_cb;
158 need_key_cb_ = need_key_cb;
159 set_decryptor_ready_cb_ = set_decryptor_ready_cb;
160 update_network_state_cb_ = media::BindToCurrentLoop(update_network_state_cb);
161 duration_change_cb_ = duration_change_cb;
162 access_unit_size_ = kAccessUnitSizeForMediaSource;
163
164 chunk_demuxer_.reset(new media::ChunkDemuxer(
165 media::BindToCurrentLoop(
166 base::Bind(&MediaSourceDelegate::OnDemuxerOpened, main_weak_this_)),
167 media::BindToCurrentLoop(
168 base::Bind(&MediaSourceDelegate::OnNeedKey, main_weak_this_)),
169 base::Bind(&LogMediaSourceError, media_log_),
170 false));
171
172 // |this| will be retained until StopDemuxer() is posted, so Unretained() is
173 // safe here.
174 media_task_runner_->PostTask(FROM_HERE,
175 base::Bind(&MediaSourceDelegate::InitializeDemuxer,
176 base::Unretained(this)));
177 }
178
InitializeDemuxer()179 void MediaSourceDelegate::InitializeDemuxer() {
180 DCHECK(media_task_runner_->BelongsToCurrentThread());
181 demuxer_client_->AddDelegate(demuxer_client_id_, this);
182 chunk_demuxer_->Initialize(this,
183 base::Bind(&MediaSourceDelegate::OnDemuxerInitDone,
184 media_weak_factory_.GetWeakPtr()),
185 false);
186 }
187
Buffered() const188 blink::WebTimeRanges MediaSourceDelegate::Buffered() const {
189 return media::ConvertToWebTimeRanges(buffered_time_ranges_);
190 }
191
DecodedFrameCount() const192 size_t MediaSourceDelegate::DecodedFrameCount() const {
193 return statistics_.video_frames_decoded;
194 }
195
DroppedFrameCount() const196 size_t MediaSourceDelegate::DroppedFrameCount() const {
197 return statistics_.video_frames_dropped;
198 }
199
AudioDecodedByteCount() const200 size_t MediaSourceDelegate::AudioDecodedByteCount() const {
201 return statistics_.audio_bytes_decoded;
202 }
203
VideoDecodedByteCount() const204 size_t MediaSourceDelegate::VideoDecodedByteCount() const {
205 return statistics_.video_bytes_decoded;
206 }
207
CancelPendingSeek(const base::TimeDelta & seek_time)208 void MediaSourceDelegate::CancelPendingSeek(const base::TimeDelta& seek_time) {
209 DCHECK(main_task_runner_->BelongsToCurrentThread());
210 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : "
211 << demuxer_client_id_;
212
213 if (!chunk_demuxer_)
214 return;
215
216 {
217 // Remember to trivially finish any newly arriving browser seek requests
218 // that may arrive prior to the next regular seek request.
219 base::AutoLock auto_lock(seeking_lock_);
220 expecting_regular_seek_ = true;
221 }
222
223 // Cancel any previously expected or in-progress regular or browser seek.
224 // It is possible that we have just finished the seek, but caller does
225 // not know this yet. It is still safe to cancel in this case because the
226 // caller will always call StartWaitingForSeek() when it is notified of
227 // the finished seek.
228 chunk_demuxer_->CancelPendingSeek(seek_time);
229 }
230
StartWaitingForSeek(const base::TimeDelta & seek_time)231 void MediaSourceDelegate::StartWaitingForSeek(
232 const base::TimeDelta& seek_time) {
233 DCHECK(main_task_runner_->BelongsToCurrentThread());
234 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : "
235 << demuxer_client_id_;
236
237 if (!chunk_demuxer_)
238 return;
239
240 bool cancel_browser_seek = false;
241 {
242 // Remember to trivially finish any newly arriving browser seek requests
243 // that may arrive prior to the next regular seek request.
244 base::AutoLock auto_lock(seeking_lock_);
245 expecting_regular_seek_ = true;
246
247 // Remember to cancel any in-progress browser seek.
248 if (seeking_) {
249 DCHECK(doing_browser_seek_);
250 cancel_browser_seek = true;
251 }
252 }
253
254 if (cancel_browser_seek)
255 chunk_demuxer_->CancelPendingSeek(seek_time);
256 chunk_demuxer_->StartWaitingForSeek(seek_time);
257 }
258
Seek(const base::TimeDelta & seek_time,bool is_browser_seek)259 void MediaSourceDelegate::Seek(
260 const base::TimeDelta& seek_time, bool is_browser_seek) {
261 DCHECK(media_task_runner_->BelongsToCurrentThread());
262 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ", "
263 << (is_browser_seek ? "browser seek" : "regular seek") << ") : "
264 << demuxer_client_id_;
265
266 base::TimeDelta internal_seek_time = seek_time;
267 {
268 base::AutoLock auto_lock(seeking_lock_);
269 DCHECK(!seeking_);
270 seeking_ = true;
271 doing_browser_seek_ = is_browser_seek;
272
273 if (doing_browser_seek_ && (!chunk_demuxer_ || expecting_regular_seek_)) {
274 // Trivially finish the browser seek without actually doing it. Reads will
275 // continue to be |kAborted| until the next regular seek is done. Browser
276 // seeking is not supported unless using a ChunkDemuxer; browser seeks are
277 // trivially finished if |chunk_demuxer_| is NULL.
278 seeking_ = false;
279 doing_browser_seek_ = false;
280 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, seek_time);
281 return;
282 }
283
284 if (doing_browser_seek_) {
285 internal_seek_time = FindBufferedBrowserSeekTime_Locked(seek_time);
286 browser_seek_time_ = internal_seek_time;
287 } else {
288 expecting_regular_seek_ = false;
289 browser_seek_time_ = media::kNoTimestamp();
290 }
291 }
292
293 // Prepare |chunk_demuxer_| for browser seek.
294 if (is_browser_seek) {
295 chunk_demuxer_->CancelPendingSeek(internal_seek_time);
296 chunk_demuxer_->StartWaitingForSeek(internal_seek_time);
297 }
298
299 SeekInternal(internal_seek_time);
300 }
301
SeekInternal(const base::TimeDelta & seek_time)302 void MediaSourceDelegate::SeekInternal(const base::TimeDelta& seek_time) {
303 DCHECK(media_task_runner_->BelongsToCurrentThread());
304 DCHECK(IsSeeking());
305 chunk_demuxer_->Seek(seek_time, base::Bind(
306 &MediaSourceDelegate::OnDemuxerSeekDone,
307 media_weak_factory_.GetWeakPtr()));
308 }
309
AddBufferedTimeRange(base::TimeDelta start,base::TimeDelta end)310 void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start,
311 base::TimeDelta end) {
312 buffered_time_ranges_.Add(start, end);
313 }
314
SetDuration(base::TimeDelta duration)315 void MediaSourceDelegate::SetDuration(base::TimeDelta duration) {
316 DCHECK(main_task_runner_->BelongsToCurrentThread());
317 DVLOG(1) << __FUNCTION__ << "(" << duration.InSecondsF() << ") : "
318 << demuxer_client_id_;
319
320 // Force duration change notification to be async to avoid reentrancy into
321 // ChunkDemxuer.
322 main_task_runner_->PostTask(FROM_HERE, base::Bind(
323 &MediaSourceDelegate::OnDurationChanged, main_weak_this_, duration));
324 }
325
OnDurationChanged(const base::TimeDelta & duration)326 void MediaSourceDelegate::OnDurationChanged(const base::TimeDelta& duration) {
327 DCHECK(main_task_runner_->BelongsToCurrentThread());
328 if (demuxer_client_)
329 demuxer_client_->DurationChanged(demuxer_client_id_, duration);
330 if (!duration_change_cb_.is_null())
331 duration_change_cb_.Run(duration);
332 }
333
OnReadFromDemuxer(media::DemuxerStream::Type type)334 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type) {
335 DCHECK(media_task_runner_->BelongsToCurrentThread());
336 DVLOG(1) << __FUNCTION__ << "(" << type << ") : " << demuxer_client_id_;
337 if (IsSeeking())
338 return; // Drop the request during seeking.
339
340 DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO);
341 // The access unit size should have been initialized properly at this stage.
342 DCHECK_GT(access_unit_size_, 0u);
343 scoped_ptr<DemuxerData> data(new DemuxerData());
344 data->type = type;
345 data->access_units.resize(access_unit_size_);
346 ReadFromDemuxerStream(type, data.Pass(), 0);
347 }
348
ReadFromDemuxerStream(media::DemuxerStream::Type type,scoped_ptr<DemuxerData> data,size_t index)349 void MediaSourceDelegate::ReadFromDemuxerStream(media::DemuxerStream::Type type,
350 scoped_ptr<DemuxerData> data,
351 size_t index) {
352 DCHECK(media_task_runner_->BelongsToCurrentThread());
353 // DemuxerStream::Read() always returns the read callback asynchronously.
354 DemuxerStream* stream =
355 (type == DemuxerStream::AUDIO) ? audio_stream_ : video_stream_;
356 stream->Read(base::Bind(
357 &MediaSourceDelegate::OnBufferReady,
358 media_weak_factory_.GetWeakPtr(), type, base::Passed(&data), index));
359 }
360
OnBufferReady(media::DemuxerStream::Type type,scoped_ptr<DemuxerData> data,size_t index,DemuxerStream::Status status,const scoped_refptr<media::DecoderBuffer> & buffer)361 void MediaSourceDelegate::OnBufferReady(
362 media::DemuxerStream::Type type,
363 scoped_ptr<DemuxerData> data,
364 size_t index,
365 DemuxerStream::Status status,
366 const scoped_refptr<media::DecoderBuffer>& buffer) {
367 DCHECK(media_task_runner_->BelongsToCurrentThread());
368 DVLOG(1) << __FUNCTION__ << "(" << index << ", " << status << ", "
369 << ((!buffer || buffer->end_of_stream()) ?
370 -1 : buffer->timestamp().InMilliseconds())
371 << ") : " << demuxer_client_id_;
372 DCHECK(chunk_demuxer_);
373
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;
379 }
380
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;
390 }
391
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;
398
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();
407 }
408 data->access_units[index].status = status;
409 data->access_units.resize(index + 1);
410 break;
411
412 case DemuxerStream::kOk:
413 data->access_units[index].status = status;
414 if (buffer->end_of_stream()) {
415 data->access_units[index].end_of_stream = true;
416 data->access_units.resize(index + 1);
417 break;
418 }
419 // TODO(ycheo): We assume that the inputed stream will be decoded
420 // right away.
421 // Need to implement this properly using MediaPlayer.OnInfoListener.
422 if (is_audio) {
423 statistics_.audio_bytes_decoded += buffer->data_size();
424 } else {
425 statistics_.video_bytes_decoded += buffer->data_size();
426 statistics_.video_frames_decoded++;
427 }
428 data->access_units[index].timestamp = buffer->timestamp();
429
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,
438 kVorbisPadding + 4);
439 }
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();
449 }
450 if (++index < data->access_units.size()) {
451 ReadFromDemuxerStream(type, data.Pass(), index);
452 return;
453 }
454 break;
455
456 default:
457 NOTREACHED();
458 }
459
460 if (!IsSeeking() && demuxer_client_)
461 demuxer_client_->ReadFromDemuxerAck(demuxer_client_id_, *data);
462 }
463
OnDemuxerError(media::PipelineStatus status)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));
469 }
470
AddTextStream(media::DemuxerStream *,const media::TextTrackConfig &)471 void MediaSourceDelegate::AddTextStream(
472 media::DemuxerStream* /* text_stream */ ,
473 const media::TextTrackConfig& /* config */ ) {
474 // TODO(matthewjheaney): add text stream (http://crbug/322115).
475 NOTIMPLEMENTED();
476 }
477
RemoveTextStream(media::DemuxerStream *)478 void MediaSourceDelegate::RemoveTextStream(
479 media::DemuxerStream* /* text_stream */ ) {
480 // TODO(matthewjheaney): remove text stream (http://crbug/322115).
481 NOTIMPLEMENTED();
482 }
483
OnDemuxerInitDone(media::PipelineStatus status)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_);
488
489 if (status != media::PIPELINE_OK) {
490 OnDemuxerError(status);
491 return;
492 }
493
494 audio_stream_ = chunk_demuxer_->GetStream(DemuxerStream::AUDIO);
495 video_stream_ = chunk_demuxer_->GetStream(DemuxerStream::VIDEO);
496
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().
502 return;
503 }
504
505 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted() &&
506 !set_decryptor_ready_cb_.is_null()) {
507 InitVideoDecryptingDemuxerStream();
508 return;
509 }
510
511 // Notify demuxer ready when both streams are not encrypted.
512 is_demuxer_ready_ = true;
513 NotifyDemuxerReady();
514 }
515
InitAudioDecryptingDemuxerStream()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());
520
521 audio_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
522 media_task_runner_, set_decryptor_ready_cb_));
523 audio_decrypting_demuxer_stream_->Initialize(
524 audio_stream_,
525 base::Bind(&MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone,
526 media_weak_factory_.GetWeakPtr()));
527 }
528
InitVideoDecryptingDemuxerStream()529 void MediaSourceDelegate::InitVideoDecryptingDemuxerStream() {
530 DCHECK(media_task_runner_->BelongsToCurrentThread());
531 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
532 DCHECK(!set_decryptor_ready_cb_.is_null());
533
534 video_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
535 media_task_runner_, set_decryptor_ready_cb_));
536 video_decrypting_demuxer_stream_->Initialize(
537 video_stream_,
538 base::Bind(&MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone,
539 media_weak_factory_.GetWeakPtr()));
540 }
541
OnAudioDecryptingDemuxerStreamInitDone(media::PipelineStatus status)542 void MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone(
543 media::PipelineStatus status) {
544 DCHECK(media_task_runner_->BelongsToCurrentThread());
545 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
546 DCHECK(chunk_demuxer_);
547
548 if (status != media::PIPELINE_OK)
549 audio_decrypting_demuxer_stream_.reset();
550 else
551 audio_stream_ = audio_decrypting_demuxer_stream_.get();
552
553 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted()) {
554 InitVideoDecryptingDemuxerStream();
555 return;
556 }
557
558 // Try to notify demuxer ready when audio DDS initialization finished and
559 // video is not encrypted.
560 is_demuxer_ready_ = true;
561 NotifyDemuxerReady();
562 }
563
OnVideoDecryptingDemuxerStreamInitDone(media::PipelineStatus status)564 void MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone(
565 media::PipelineStatus status) {
566 DCHECK(media_task_runner_->BelongsToCurrentThread());
567 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
568 DCHECK(chunk_demuxer_);
569
570 if (status != media::PIPELINE_OK)
571 video_decrypting_demuxer_stream_.reset();
572 else
573 video_stream_ = video_decrypting_demuxer_stream_.get();
574
575 // Try to notify demuxer ready when video DDS initialization finished.
576 is_demuxer_ready_ = true;
577 NotifyDemuxerReady();
578 }
579
OnDemuxerSeekDone(media::PipelineStatus status)580 void MediaSourceDelegate::OnDemuxerSeekDone(media::PipelineStatus status) {
581 DCHECK(media_task_runner_->BelongsToCurrentThread());
582 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
583 DCHECK(IsSeeking());
584
585 if (status != media::PIPELINE_OK) {
586 OnDemuxerError(status);
587 return;
588 }
589
590 ResetAudioDecryptingDemuxerStream();
591 }
592
ResetAudioDecryptingDemuxerStream()593 void MediaSourceDelegate::ResetAudioDecryptingDemuxerStream() {
594 DCHECK(media_task_runner_->BelongsToCurrentThread());
595 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
596 if (audio_decrypting_demuxer_stream_) {
597 audio_decrypting_demuxer_stream_->Reset(
598 base::Bind(&MediaSourceDelegate::ResetVideoDecryptingDemuxerStream,
599 media_weak_factory_.GetWeakPtr()));
600 return;
601 }
602
603 ResetVideoDecryptingDemuxerStream();
604 }
605
ResetVideoDecryptingDemuxerStream()606 void MediaSourceDelegate::ResetVideoDecryptingDemuxerStream() {
607 DCHECK(media_task_runner_->BelongsToCurrentThread());
608 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
609 if (video_decrypting_demuxer_stream_) {
610 video_decrypting_demuxer_stream_->Reset(base::Bind(
611 &MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams,
612 media_weak_factory_.GetWeakPtr()));
613 return;
614 }
615
616 FinishResettingDecryptingDemuxerStreams();
617 }
618
FinishResettingDecryptingDemuxerStreams()619 void MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams() {
620 DCHECK(media_task_runner_->BelongsToCurrentThread());
621 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
622
623 base::AutoLock auto_lock(seeking_lock_);
624 DCHECK(seeking_);
625 seeking_ = false;
626 doing_browser_seek_ = false;
627 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, browser_seek_time_);
628 }
629
NotifyDemuxerReady()630 void MediaSourceDelegate::NotifyDemuxerReady() {
631 DCHECK(media_task_runner_->BelongsToCurrentThread());
632 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
633 DCHECK(is_demuxer_ready_);
634
635 scoped_ptr<DemuxerConfigs> configs(new DemuxerConfigs());
636 GetDemuxerConfigFromStream(configs.get(), true);
637 GetDemuxerConfigFromStream(configs.get(), false);
638 configs->duration = GetDuration();
639
640 if (demuxer_client_)
641 demuxer_client_->DemuxerReady(demuxer_client_id_, *configs);
642
643 base::AutoLock auto_lock(is_video_encrypted_lock_);
644 is_video_encrypted_ = configs->is_video_encrypted;
645 }
646
GetDuration() const647 base::TimeDelta MediaSourceDelegate::GetDuration() const {
648 DCHECK(media_task_runner_->BelongsToCurrentThread());
649 if (!chunk_demuxer_)
650 return media::kNoTimestamp();
651
652 double duration = chunk_demuxer_->GetDuration();
653 if (duration == std::numeric_limits<double>::infinity())
654 return media::kInfiniteDuration();
655
656 return media::ConvertSecondsToTimestamp(duration);
657 }
658
OnDemuxerOpened()659 void MediaSourceDelegate::OnDemuxerOpened() {
660 DCHECK(main_task_runner_->BelongsToCurrentThread());
661 if (media_source_opened_cb_.is_null())
662 return;
663
664 media_source_opened_cb_.Run(new media::WebMediaSourceImpl(
665 chunk_demuxer_.get(), base::Bind(&LogMediaSourceError, media_log_)));
666 }
667
OnNeedKey(const std::string & type,const std::vector<uint8> & init_data)668 void MediaSourceDelegate::OnNeedKey(const std::string& type,
669 const std::vector<uint8>& init_data) {
670 DCHECK(main_task_runner_->BelongsToCurrentThread());
671 if (need_key_cb_.is_null())
672 return;
673
674 need_key_cb_.Run(type, init_data);
675 }
676
IsSeeking() const677 bool MediaSourceDelegate::IsSeeking() const {
678 base::AutoLock auto_lock(seeking_lock_);
679 return seeking_;
680 }
681
FindBufferedBrowserSeekTime_Locked(const base::TimeDelta & seek_time) const682 base::TimeDelta MediaSourceDelegate::FindBufferedBrowserSeekTime_Locked(
683 const base::TimeDelta& seek_time) const {
684 seeking_lock_.AssertAcquired();
685 DCHECK(seeking_);
686 DCHECK(doing_browser_seek_);
687 DCHECK(chunk_demuxer_) << "Browser seek requested, but no chunk demuxer";
688
689 media::Ranges<base::TimeDelta> buffered =
690 chunk_demuxer_->GetBufferedRanges();
691
692 for (size_t i = 0; i < buffered.size(); ++i) {
693 base::TimeDelta range_start = buffered.start(i);
694 base::TimeDelta range_end = buffered.end(i);
695 if (range_start <= seek_time) {
696 if (range_end >= seek_time)
697 return seek_time;
698 continue;
699 }
700
701 // If the start of the next buffered range after |seek_time| is too far
702 // into the future, do not jump forward.
703 if ((range_start - seek_time) > base::TimeDelta::FromMilliseconds(100))
704 break;
705
706 // TODO(wolenetz): Remove possibility that this browser seek jumps
707 // into future when the requested range is unbuffered but there is some
708 // other buffered range after it. See http://crbug.com/304234.
709 return range_start;
710 }
711
712 // We found no range containing |seek_time| or beginning shortly after
713 // |seek_time|. While possible that such data at and beyond the player's
714 // current time have been garbage collected or removed by the web app, this is
715 // unlikely. This may cause unexpected playback stall due to seek pending an
716 // append for a GOP prior to the last GOP demuxed.
717 // TODO(wolenetz): Remove the possibility for this seek to cause unexpected
718 // player stall by replaying cached data since last keyframe in browser player
719 // rather than issuing browser seek. See http://crbug.com/304234.
720 return seek_time;
721 }
722
GetDemuxerConfigFromStream(media::DemuxerConfigs * configs,bool is_audio)723 bool MediaSourceDelegate::GetDemuxerConfigFromStream(
724 media::DemuxerConfigs* configs, bool is_audio) {
725 DCHECK(media_task_runner_->BelongsToCurrentThread());
726 if (!is_demuxer_ready_)
727 return false;
728 if (is_audio && audio_stream_) {
729 media::AudioDecoderConfig config = audio_stream_->audio_decoder_config();
730 configs->audio_codec = config.codec();
731 configs->audio_channels =
732 media::ChannelLayoutToChannelCount(config.channel_layout());
733 configs->audio_sampling_rate = config.samples_per_second();
734 configs->is_audio_encrypted = config.is_encrypted();
735 configs->audio_extra_data = std::vector<uint8>(
736 config.extra_data(), config.extra_data() + config.extra_data_size());
737 return true;
738 }
739 if (!is_audio && video_stream_) {
740 media::VideoDecoderConfig config = video_stream_->video_decoder_config();
741 configs->video_codec = config.codec();
742 configs->video_size = config.natural_size();
743 configs->is_video_encrypted = config.is_encrypted();
744 configs->video_extra_data = std::vector<uint8>(
745 config.extra_data(), config.extra_data() + config.extra_data_size());
746 return true;
747 }
748 return false;
749 }
750
751 } // namespace content
752