• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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