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 "content/renderer/media/webmediaplayer_util.h"
15 #include "content/renderer/media/webmediasource_impl.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/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::MessageLoopProxy> & media_loop,media::MediaLog * media_log)48 MediaSourceDelegate::MediaSourceDelegate(
49 RendererDemuxerAndroid* demuxer_client,
50 int demuxer_client_id,
51 const scoped_refptr<base::MessageLoopProxy>& media_loop,
52 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_loop_(base::MessageLoopProxy::current()),
66 media_loop_(media_loop),
67 main_weak_factory_(this),
68 media_weak_factory_(this),
69 main_weak_this_(main_weak_factory_.GetWeakPtr()) {
70 DCHECK(main_loop_->BelongsToCurrentThread());
71 }
72
~MediaSourceDelegate()73 MediaSourceDelegate::~MediaSourceDelegate() {
74 DCHECK(main_loop_->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
Destroy()84 void MediaSourceDelegate::Destroy() {
85 DCHECK(main_loop_->BelongsToCurrentThread());
86 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
87
88 if (!chunk_demuxer_) {
89 DCHECK(!demuxer_client_);
90 delete this;
91 return;
92 }
93
94 duration_change_cb_.Reset();
95 update_network_state_cb_.Reset();
96 media_source_opened_cb_.Reset();
97
98 main_weak_factory_.InvalidateWeakPtrs();
99 DCHECK(!main_weak_factory_.HasWeakPtrs());
100
101 chunk_demuxer_->Shutdown();
102
103 // |this| will be transferred to the callback StopDemuxer() and
104 // OnDemuxerStopDone(). They own |this| and OnDemuxerStopDone() will delete
105 // it when called, hence using base::Unretained(this) is safe here.
106 media_loop_->PostTask(FROM_HERE,
107 base::Bind(&MediaSourceDelegate::StopDemuxer,
108 base::Unretained(this)));
109 }
110
IsVideoEncrypted()111 bool MediaSourceDelegate::IsVideoEncrypted() {
112 DCHECK(main_loop_->BelongsToCurrentThread());
113 base::AutoLock auto_lock(is_video_encrypted_lock_);
114 return is_video_encrypted_;
115 }
116
GetTimelineOffset() const117 base::Time MediaSourceDelegate::GetTimelineOffset() const {
118 DCHECK(main_loop_->BelongsToCurrentThread());
119 if (!chunk_demuxer_)
120 return base::Time();
121
122 return chunk_demuxer_->GetTimelineOffset();
123 }
124
StopDemuxer()125 void MediaSourceDelegate::StopDemuxer() {
126 DCHECK(media_loop_->BelongsToCurrentThread());
127 DCHECK(chunk_demuxer_);
128
129 demuxer_client_->RemoveDelegate(demuxer_client_id_);
130 demuxer_client_ = NULL;
131
132 audio_stream_ = NULL;
133 video_stream_ = NULL;
134 // TODO(xhwang): Figure out if we need to Reset the DDSs after Seeking or
135 // before destroying them.
136 audio_decrypting_demuxer_stream_.reset();
137 video_decrypting_demuxer_stream_.reset();
138
139 media_weak_factory_.InvalidateWeakPtrs();
140 DCHECK(!media_weak_factory_.HasWeakPtrs());
141
142 // The callback OnDemuxerStopDone() owns |this| and will delete it when
143 // called. Hence using base::Unretained(this) is safe here.
144 chunk_demuxer_->Stop(base::Bind(&MediaSourceDelegate::OnDemuxerStopDone,
145 base::Unretained(this)));
146 }
147
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)148 void MediaSourceDelegate::InitializeMediaSource(
149 const MediaSourceOpenedCB& media_source_opened_cb,
150 const media::Demuxer::NeedKeyCB& need_key_cb,
151 const media::SetDecryptorReadyCB& set_decryptor_ready_cb,
152 const UpdateNetworkStateCB& update_network_state_cb,
153 const DurationChangeCB& duration_change_cb) {
154 DCHECK(main_loop_->BelongsToCurrentThread());
155 DCHECK(!media_source_opened_cb.is_null());
156 media_source_opened_cb_ = media_source_opened_cb;
157 need_key_cb_ = need_key_cb;
158 set_decryptor_ready_cb_ = set_decryptor_ready_cb;
159 update_network_state_cb_ = media::BindToCurrentLoop(update_network_state_cb);
160 duration_change_cb_ = duration_change_cb;
161 access_unit_size_ = kAccessUnitSizeForMediaSource;
162
163 chunk_demuxer_.reset(new media::ChunkDemuxer(
164 media::BindToCurrentLoop(
165 base::Bind(&MediaSourceDelegate::OnDemuxerOpened, main_weak_this_)),
166 media::BindToCurrentLoop(
167 base::Bind(&MediaSourceDelegate::OnNeedKey, main_weak_this_)),
168 base::Bind(&LogMediaSourceError, media_log_),
169 false));
170
171 // |this| will be retained until StopDemuxer() is posted, so Unretained() is
172 // safe here.
173 media_loop_->PostTask(FROM_HERE,
174 base::Bind(&MediaSourceDelegate::InitializeDemuxer,
175 base::Unretained(this)));
176 }
177
InitializeDemuxer()178 void MediaSourceDelegate::InitializeDemuxer() {
179 DCHECK(media_loop_->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);
185 }
186
Buffered() const187 blink::WebTimeRanges MediaSourceDelegate::Buffered() const {
188 return ConvertToWebTimeRanges(buffered_time_ranges_);
189 }
190
DecodedFrameCount() const191 size_t MediaSourceDelegate::DecodedFrameCount() const {
192 return statistics_.video_frames_decoded;
193 }
194
DroppedFrameCount() const195 size_t MediaSourceDelegate::DroppedFrameCount() const {
196 return statistics_.video_frames_dropped;
197 }
198
AudioDecodedByteCount() const199 size_t MediaSourceDelegate::AudioDecodedByteCount() const {
200 return statistics_.audio_bytes_decoded;
201 }
202
VideoDecodedByteCount() const203 size_t MediaSourceDelegate::VideoDecodedByteCount() const {
204 return statistics_.video_bytes_decoded;
205 }
206
CancelPendingSeek(const base::TimeDelta & seek_time)207 void MediaSourceDelegate::CancelPendingSeek(const base::TimeDelta& seek_time) {
208 DCHECK(main_loop_->BelongsToCurrentThread());
209 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : "
210 << demuxer_client_id_;
211
212 if (!chunk_demuxer_)
213 return;
214
215 {
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;
220 }
221
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);
228 }
229
StartWaitingForSeek(const base::TimeDelta & seek_time)230 void MediaSourceDelegate::StartWaitingForSeek(
231 const base::TimeDelta& seek_time) {
232 DCHECK(main_loop_->BelongsToCurrentThread());
233 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ") : "
234 << demuxer_client_id_;
235
236 if (!chunk_demuxer_)
237 return;
238
239 bool cancel_browser_seek = false;
240 {
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;
245
246 // Remember to cancel any in-progress browser seek.
247 if (seeking_) {
248 DCHECK(doing_browser_seek_);
249 cancel_browser_seek = true;
250 }
251 }
252
253 if (cancel_browser_seek)
254 chunk_demuxer_->CancelPendingSeek(seek_time);
255 chunk_demuxer_->StartWaitingForSeek(seek_time);
256 }
257
Seek(const base::TimeDelta & seek_time,bool is_browser_seek)258 void MediaSourceDelegate::Seek(
259 const base::TimeDelta& seek_time, bool is_browser_seek) {
260 DCHECK(media_loop_->BelongsToCurrentThread());
261 DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ", "
262 << (is_browser_seek ? "browser seek" : "regular seek") << ") : "
263 << demuxer_client_id_;
264
265 base::TimeDelta internal_seek_time = seek_time;
266 {
267 base::AutoLock auto_lock(seeking_lock_);
268 DCHECK(!seeking_);
269 seeking_ = true;
270 doing_browser_seek_ = is_browser_seek;
271
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;
281 }
282
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();
289 }
290 }
291
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);
296 }
297
298 SeekInternal(internal_seek_time);
299 }
300
SeekInternal(const base::TimeDelta & seek_time)301 void MediaSourceDelegate::SeekInternal(const base::TimeDelta& seek_time) {
302 DCHECK(media_loop_->BelongsToCurrentThread());
303 DCHECK(IsSeeking());
304 chunk_demuxer_->Seek(seek_time, base::Bind(
305 &MediaSourceDelegate::OnDemuxerSeekDone,
306 media_weak_factory_.GetWeakPtr()));
307 }
308
AddBufferedTimeRange(base::TimeDelta start,base::TimeDelta end)309 void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start,
310 base::TimeDelta end) {
311 buffered_time_ranges_.Add(start, end);
312 }
313
SetDuration(base::TimeDelta duration)314 void MediaSourceDelegate::SetDuration(base::TimeDelta duration) {
315 DCHECK(main_loop_->BelongsToCurrentThread());
316 DVLOG(1) << __FUNCTION__ << "(" << duration.InSecondsF() << ") : "
317 << demuxer_client_id_;
318
319 // Force duration change notification to be async to avoid reentrancy into
320 // ChunkDemxuer.
321 main_loop_->PostTask(FROM_HERE, base::Bind(
322 &MediaSourceDelegate::OnDurationChanged, main_weak_this_, duration));
323 }
324
OnDurationChanged(const base::TimeDelta & duration)325 void MediaSourceDelegate::OnDurationChanged(const base::TimeDelta& duration) {
326 DCHECK(main_loop_->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);
331 }
332
OnReadFromDemuxer(media::DemuxerStream::Type type)333 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type) {
334 DCHECK(media_loop_->BelongsToCurrentThread());
335 DVLOG(1) << __FUNCTION__ << "(" << type << ") : " << demuxer_client_id_;
336 if (IsSeeking())
337 return; // Drop the request during seeking.
338
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);
346 }
347
ReadFromDemuxerStream(media::DemuxerStream::Type type,scoped_ptr<DemuxerData> data,size_t index)348 void MediaSourceDelegate::ReadFromDemuxerStream(media::DemuxerStream::Type type,
349 scoped_ptr<DemuxerData> data,
350 size_t index) {
351 DCHECK(media_loop_->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));
358 }
359
OnBufferReady(media::DemuxerStream::Type type,scoped_ptr<DemuxerData> data,size_t index,DemuxerStream::Status status,const scoped_refptr<media::DecoderBuffer> & buffer)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_loop_->BelongsToCurrentThread());
367 DVLOG(1) << __FUNCTION__ << "(" << index << ", " << status << ", "
368 << ((!buffer || buffer->end_of_stream()) ?
369 -1 : buffer->timestamp().InMilliseconds())
370 << ") : " << demuxer_client_id_;
371 DCHECK(chunk_demuxer_);
372
373 // No new OnReadFromDemuxer() will be called during seeking. So this callback
374 // must be from previous OnReadFromDemuxer() call and should be ignored.
375 if (IsSeeking()) {
376 DVLOG(1) << __FUNCTION__ << ": Ignore previous read during seeking.";
377 return;
378 }
379
380 bool is_audio = (type == DemuxerStream::AUDIO);
381 if (status != DemuxerStream::kAborted &&
382 index >= data->access_units.size()) {
383 LOG(ERROR) << "The internal state inconsistency onBufferReady: "
384 << (is_audio ? "Audio" : "Video") << ", index " << index
385 << ", size " << data->access_units.size()
386 << ", status " << static_cast<int>(status);
387 NOTREACHED();
388 return;
389 }
390
391 switch (status) {
392 case DemuxerStream::kAborted:
393 DVLOG(1) << __FUNCTION__ << " : Aborted";
394 data->access_units[index].status = status;
395 data->access_units.resize(index + 1);
396 break;
397
398 case DemuxerStream::kConfigChanged:
399 CHECK((is_audio && audio_stream_) || (!is_audio && video_stream_));
400 data->demuxer_configs.resize(1);
401 CHECK(GetDemuxerConfigFromStream(&data->demuxer_configs[0], is_audio));
402 if (!is_audio) {
403 gfx::Size size = data->demuxer_configs[0].video_size;
404 DVLOG(1) << "Video config is changed: " << size.width() << "x"
405 << size.height();
406 }
407 data->access_units[index].status = status;
408 data->access_units.resize(index + 1);
409 break;
410
411 case DemuxerStream::kOk:
412 data->access_units[index].status = status;
413 if (buffer->end_of_stream()) {
414 data->access_units[index].end_of_stream = true;
415 data->access_units.resize(index + 1);
416 break;
417 }
418 // TODO(ycheo): We assume that the inputed stream will be decoded
419 // right away.
420 // Need to implement this properly using MediaPlayer.OnInfoListener.
421 if (is_audio) {
422 statistics_.audio_bytes_decoded += buffer->data_size();
423 } else {
424 statistics_.video_bytes_decoded += buffer->data_size();
425 statistics_.video_frames_decoded++;
426 }
427 data->access_units[index].timestamp = buffer->timestamp();
428
429 data->access_units[index].data.assign(
430 buffer->data(), buffer->data() + buffer->data_size());
431 // Vorbis needs 4 extra bytes padding on Android. Check
432 // NuMediaExtractor.cpp in Android source code.
433 if (is_audio && media::kCodecVorbis ==
434 audio_stream_->audio_decoder_config().codec()) {
435 data->access_units[index].data.insert(
436 data->access_units[index].data.end(), kVorbisPadding,
437 kVorbisPadding + 4);
438 }
439 if (buffer->decrypt_config()) {
440 data->access_units[index].key_id = std::vector<char>(
441 buffer->decrypt_config()->key_id().begin(),
442 buffer->decrypt_config()->key_id().end());
443 data->access_units[index].iv = std::vector<char>(
444 buffer->decrypt_config()->iv().begin(),
445 buffer->decrypt_config()->iv().end());
446 data->access_units[index].subsamples =
447 buffer->decrypt_config()->subsamples();
448 }
449 if (++index < data->access_units.size()) {
450 ReadFromDemuxerStream(type, data.Pass(), index);
451 return;
452 }
453 break;
454
455 default:
456 NOTREACHED();
457 }
458
459 if (!IsSeeking() && demuxer_client_)
460 demuxer_client_->ReadFromDemuxerAck(demuxer_client_id_, *data);
461 }
462
OnDemuxerError(media::PipelineStatus status)463 void MediaSourceDelegate::OnDemuxerError(media::PipelineStatus status) {
464 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
465 // |update_network_state_cb_| is bound to the main thread.
466 if (status != media::PIPELINE_OK && !update_network_state_cb_.is_null())
467 update_network_state_cb_.Run(PipelineErrorToNetworkState(status));
468 }
469
AddTextStream(media::DemuxerStream *,const media::TextTrackConfig &)470 void MediaSourceDelegate::AddTextStream(
471 media::DemuxerStream* /* text_stream */ ,
472 const media::TextTrackConfig& /* config */ ) {
473 // TODO(matthewjheaney): add text stream (http://crbug/322115).
474 NOTIMPLEMENTED();
475 }
476
RemoveTextStream(media::DemuxerStream *)477 void MediaSourceDelegate::RemoveTextStream(
478 media::DemuxerStream* /* text_stream */ ) {
479 // TODO(matthewjheaney): remove text stream (http://crbug/322115).
480 NOTIMPLEMENTED();
481 }
482
OnDemuxerInitDone(media::PipelineStatus status)483 void MediaSourceDelegate::OnDemuxerInitDone(media::PipelineStatus status) {
484 DCHECK(media_loop_->BelongsToCurrentThread());
485 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
486 DCHECK(chunk_demuxer_);
487
488 if (status != media::PIPELINE_OK) {
489 OnDemuxerError(status);
490 return;
491 }
492
493 audio_stream_ = chunk_demuxer_->GetStream(DemuxerStream::AUDIO);
494 video_stream_ = chunk_demuxer_->GetStream(DemuxerStream::VIDEO);
495
496 if (audio_stream_ && audio_stream_->audio_decoder_config().is_encrypted() &&
497 !set_decryptor_ready_cb_.is_null()) {
498 InitAudioDecryptingDemuxerStream();
499 // InitVideoDecryptingDemuxerStream() will be called in
500 // OnAudioDecryptingDemuxerStreamInitDone().
501 return;
502 }
503
504 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted() &&
505 !set_decryptor_ready_cb_.is_null()) {
506 InitVideoDecryptingDemuxerStream();
507 return;
508 }
509
510 // Notify demuxer ready when both streams are not encrypted.
511 is_demuxer_ready_ = true;
512 NotifyDemuxerReady();
513 }
514
InitAudioDecryptingDemuxerStream()515 void MediaSourceDelegate::InitAudioDecryptingDemuxerStream() {
516 DCHECK(media_loop_->BelongsToCurrentThread());
517 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
518 DCHECK(!set_decryptor_ready_cb_.is_null());
519
520 audio_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
521 media_loop_, set_decryptor_ready_cb_));
522 audio_decrypting_demuxer_stream_->Initialize(
523 audio_stream_,
524 base::Bind(&MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone,
525 media_weak_factory_.GetWeakPtr()));
526 }
527
InitVideoDecryptingDemuxerStream()528 void MediaSourceDelegate::InitVideoDecryptingDemuxerStream() {
529 DCHECK(media_loop_->BelongsToCurrentThread());
530 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
531 DCHECK(!set_decryptor_ready_cb_.is_null());
532
533 video_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
534 media_loop_, set_decryptor_ready_cb_));
535 video_decrypting_demuxer_stream_->Initialize(
536 video_stream_,
537 base::Bind(&MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone,
538 media_weak_factory_.GetWeakPtr()));
539 }
540
OnAudioDecryptingDemuxerStreamInitDone(media::PipelineStatus status)541 void MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone(
542 media::PipelineStatus status) {
543 DCHECK(media_loop_->BelongsToCurrentThread());
544 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
545 DCHECK(chunk_demuxer_);
546
547 if (status != media::PIPELINE_OK)
548 audio_decrypting_demuxer_stream_.reset();
549 else
550 audio_stream_ = audio_decrypting_demuxer_stream_.get();
551
552 if (video_stream_ && video_stream_->video_decoder_config().is_encrypted()) {
553 InitVideoDecryptingDemuxerStream();
554 return;
555 }
556
557 // Try to notify demuxer ready when audio DDS initialization finished and
558 // video is not encrypted.
559 is_demuxer_ready_ = true;
560 NotifyDemuxerReady();
561 }
562
OnVideoDecryptingDemuxerStreamInitDone(media::PipelineStatus status)563 void MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone(
564 media::PipelineStatus status) {
565 DCHECK(media_loop_->BelongsToCurrentThread());
566 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
567 DCHECK(chunk_demuxer_);
568
569 if (status != media::PIPELINE_OK)
570 video_decrypting_demuxer_stream_.reset();
571 else
572 video_stream_ = video_decrypting_demuxer_stream_.get();
573
574 // Try to notify demuxer ready when video DDS initialization finished.
575 is_demuxer_ready_ = true;
576 NotifyDemuxerReady();
577 }
578
OnDemuxerSeekDone(media::PipelineStatus status)579 void MediaSourceDelegate::OnDemuxerSeekDone(media::PipelineStatus status) {
580 DCHECK(media_loop_->BelongsToCurrentThread());
581 DVLOG(1) << __FUNCTION__ << "(" << status << ") : " << demuxer_client_id_;
582 DCHECK(IsSeeking());
583
584 if (status != media::PIPELINE_OK) {
585 OnDemuxerError(status);
586 return;
587 }
588
589 ResetAudioDecryptingDemuxerStream();
590 }
591
ResetAudioDecryptingDemuxerStream()592 void MediaSourceDelegate::ResetAudioDecryptingDemuxerStream() {
593 DCHECK(media_loop_->BelongsToCurrentThread());
594 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
595 if (audio_decrypting_demuxer_stream_) {
596 audio_decrypting_demuxer_stream_->Reset(
597 base::Bind(&MediaSourceDelegate::ResetVideoDecryptingDemuxerStream,
598 media_weak_factory_.GetWeakPtr()));
599 return;
600 }
601
602 ResetVideoDecryptingDemuxerStream();
603 }
604
ResetVideoDecryptingDemuxerStream()605 void MediaSourceDelegate::ResetVideoDecryptingDemuxerStream() {
606 DCHECK(media_loop_->BelongsToCurrentThread());
607 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
608 if (video_decrypting_demuxer_stream_) {
609 video_decrypting_demuxer_stream_->Reset(base::Bind(
610 &MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams,
611 media_weak_factory_.GetWeakPtr()));
612 return;
613 }
614
615 FinishResettingDecryptingDemuxerStreams();
616 }
617
FinishResettingDecryptingDemuxerStreams()618 void MediaSourceDelegate::FinishResettingDecryptingDemuxerStreams() {
619 DCHECK(media_loop_->BelongsToCurrentThread());
620 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
621
622 base::AutoLock auto_lock(seeking_lock_);
623 DCHECK(seeking_);
624 seeking_ = false;
625 doing_browser_seek_ = false;
626 demuxer_client_->DemuxerSeekDone(demuxer_client_id_, browser_seek_time_);
627 }
628
OnDemuxerStopDone()629 void MediaSourceDelegate::OnDemuxerStopDone() {
630 DCHECK(media_loop_->BelongsToCurrentThread());
631 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
632 main_loop_->PostTask(
633 FROM_HERE,
634 base::Bind(&MediaSourceDelegate::DeleteSelf, base::Unretained(this)));
635 }
636
DeleteSelf()637 void MediaSourceDelegate::DeleteSelf() {
638 DCHECK(main_loop_->BelongsToCurrentThread());
639 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
640 chunk_demuxer_.reset();
641 delete this;
642 }
643
NotifyDemuxerReady()644 void MediaSourceDelegate::NotifyDemuxerReady() {
645 DCHECK(media_loop_->BelongsToCurrentThread());
646 DVLOG(1) << __FUNCTION__ << " : " << demuxer_client_id_;
647 DCHECK(is_demuxer_ready_);
648
649 scoped_ptr<DemuxerConfigs> configs(new DemuxerConfigs());
650 GetDemuxerConfigFromStream(configs.get(), true);
651 GetDemuxerConfigFromStream(configs.get(), false);
652 configs->duration = GetDuration();
653
654 if (demuxer_client_)
655 demuxer_client_->DemuxerReady(demuxer_client_id_, *configs);
656
657 base::AutoLock auto_lock(is_video_encrypted_lock_);
658 is_video_encrypted_ = configs->is_video_encrypted;
659 }
660
GetDuration() const661 base::TimeDelta MediaSourceDelegate::GetDuration() const {
662 DCHECK(media_loop_->BelongsToCurrentThread());
663 if (!chunk_demuxer_)
664 return media::kNoTimestamp();
665
666 double duration = chunk_demuxer_->GetDuration();
667 if (duration == std::numeric_limits<double>::infinity())
668 return media::kInfiniteDuration();
669
670 return ConvertSecondsToTimestamp(duration);
671 }
672
OnDemuxerOpened()673 void MediaSourceDelegate::OnDemuxerOpened() {
674 DCHECK(main_loop_->BelongsToCurrentThread());
675 if (media_source_opened_cb_.is_null())
676 return;
677
678 media_source_opened_cb_.Run(new WebMediaSourceImpl(
679 chunk_demuxer_.get(), base::Bind(&LogMediaSourceError, media_log_)));
680 }
681
OnNeedKey(const std::string & type,const std::vector<uint8> & init_data)682 void MediaSourceDelegate::OnNeedKey(const std::string& type,
683 const std::vector<uint8>& init_data) {
684 DCHECK(main_loop_->BelongsToCurrentThread());
685 if (need_key_cb_.is_null())
686 return;
687
688 need_key_cb_.Run(type, init_data);
689 }
690
IsSeeking() const691 bool MediaSourceDelegate::IsSeeking() const {
692 base::AutoLock auto_lock(seeking_lock_);
693 return seeking_;
694 }
695
FindBufferedBrowserSeekTime_Locked(const base::TimeDelta & seek_time) const696 base::TimeDelta MediaSourceDelegate::FindBufferedBrowserSeekTime_Locked(
697 const base::TimeDelta& seek_time) const {
698 seeking_lock_.AssertAcquired();
699 DCHECK(seeking_);
700 DCHECK(doing_browser_seek_);
701 DCHECK(chunk_demuxer_) << "Browser seek requested, but no chunk demuxer";
702
703 media::Ranges<base::TimeDelta> buffered =
704 chunk_demuxer_->GetBufferedRanges();
705
706 for (size_t i = 0; i < buffered.size(); ++i) {
707 base::TimeDelta range_start = buffered.start(i);
708 base::TimeDelta range_end = buffered.end(i);
709 if (range_start <= seek_time) {
710 if (range_end >= seek_time)
711 return seek_time;
712 continue;
713 }
714
715 // If the start of the next buffered range after |seek_time| is too far
716 // into the future, do not jump forward.
717 if ((range_start - seek_time) > base::TimeDelta::FromMilliseconds(100))
718 break;
719
720 // TODO(wolenetz): Remove possibility that this browser seek jumps
721 // into future when the requested range is unbuffered but there is some
722 // other buffered range after it. See http://crbug.com/304234.
723 return range_start;
724 }
725
726 // We found no range containing |seek_time| or beginning shortly after
727 // |seek_time|. While possible that such data at and beyond the player's
728 // current time have been garbage collected or removed by the web app, this is
729 // unlikely. This may cause unexpected playback stall due to seek pending an
730 // append for a GOP prior to the last GOP demuxed.
731 // TODO(wolenetz): Remove the possibility for this seek to cause unexpected
732 // player stall by replaying cached data since last keyframe in browser player
733 // rather than issuing browser seek. See http://crbug.com/304234.
734 return seek_time;
735 }
736
GetDemuxerConfigFromStream(media::DemuxerConfigs * configs,bool is_audio)737 bool MediaSourceDelegate::GetDemuxerConfigFromStream(
738 media::DemuxerConfigs* configs, bool is_audio) {
739 DCHECK(media_loop_->BelongsToCurrentThread());
740 if (!is_demuxer_ready_)
741 return false;
742 if (is_audio && audio_stream_) {
743 media::AudioDecoderConfig config = audio_stream_->audio_decoder_config();
744 configs->audio_codec = config.codec();
745 configs->audio_channels =
746 media::ChannelLayoutToChannelCount(config.channel_layout());
747 configs->audio_sampling_rate = config.samples_per_second();
748 configs->is_audio_encrypted = config.is_encrypted();
749 configs->audio_extra_data = std::vector<uint8>(
750 config.extra_data(), config.extra_data() + config.extra_data_size());
751 return true;
752 }
753 if (!is_audio && video_stream_) {
754 media::VideoDecoderConfig config = video_stream_->video_decoder_config();
755 configs->video_codec = config.codec();
756 configs->video_size = config.natural_size();
757 configs->is_video_encrypted = config.is_encrypted();
758 configs->video_extra_data = std::vector<uint8>(
759 config.extra_data(), config.extra_data() + config.extra_data_size());
760 return true;
761 }
762 return false;
763 }
764
765 } // namespace content
766