Home | History | Annotate | Download | only in android
      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 "base/message_loop/message_loop_proxy.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "content/renderer/media/android/webmediaplayer_proxy_android.h"
     10 #include "content/renderer/media/webmediaplayer_util.h"
     11 #include "content/renderer/media/webmediasourceclient_impl.h"
     12 #include "media/base/android/demuxer_stream_player_params.h"
     13 #include "media/base/bind_to_loop.h"
     14 #include "media/base/demuxer_stream.h"
     15 #include "media/base/media_log.h"
     16 #include "media/filters/chunk_demuxer.h"
     17 #include "media/filters/decrypting_demuxer_stream.h"
     18 #include "third_party/WebKit/public/platform/WebString.h"
     19 #include "third_party/WebKit/public/web/WebMediaSource.h"
     20 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
     21 
     22 using media::DemuxerStream;
     23 using media::MediaPlayerHostMsg_DemuxerReady_Params;
     24 using media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params;
     25 using WebKit::WebMediaPlayer;
     26 using WebKit::WebString;
     27 
     28 namespace {
     29 
     30 // The size of the access unit to transfer in an IPC in case of MediaSource.
     31 // 16: approximately 250ms of content in 60 fps movies.
     32 const size_t kAccessUnitSizeForMediaSource = 16;
     33 
     34 const uint8 kVorbisPadding[] = { 0xff, 0xff, 0xff, 0xff };
     35 
     36 }  // namespace
     37 
     38 namespace content {
     39 
     40 #define BIND_TO_RENDER_LOOP(function) \
     41   media::BindToLoop(main_loop_, \
     42                     base::Bind(function, main_weak_this_.GetWeakPtr()))
     43 
     44 #define BIND_TO_RENDER_LOOP_1(function, arg1) \
     45   media::BindToLoop(main_loop_, \
     46                     base::Bind(function, main_weak_this_.GetWeakPtr(), arg1))
     47 
     48 #if defined(GOOGLE_TV)
     49 #define DCHECK_BELONG_TO_MEDIA_LOOP() \
     50     DCHECK(media_loop_->BelongsToCurrentThread())
     51 #else
     52 #define DCHECK_BELONG_TO_MEDIA_LOOP() \
     53     DCHECK(main_loop_->BelongsToCurrentThread())
     54 #endif
     55 
     56 static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log,
     57                                 const std::string& error) {
     58   media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error));
     59 }
     60 
     61 MediaSourceDelegate::MediaSourceDelegate(
     62     WebMediaPlayerProxyAndroid* proxy,
     63     int player_id,
     64     const scoped_refptr<base::MessageLoopProxy>& media_loop,
     65     media::MediaLog* media_log)
     66     : main_weak_this_(this),
     67       media_weak_this_(this),
     68       main_loop_(base::MessageLoopProxy::current()),
     69 #if defined(GOOGLE_TV)
     70       media_loop_(media_loop),
     71       send_read_from_demuxer_ack_cb_(
     72           BIND_TO_RENDER_LOOP(&MediaSourceDelegate::SendReadFromDemuxerAck)),
     73       send_seek_request_ack_cb_(
     74           BIND_TO_RENDER_LOOP(&MediaSourceDelegate::SendSeekRequestAck)),
     75       send_demuxer_ready_cb_(
     76           BIND_TO_RENDER_LOOP(&MediaSourceDelegate::SendDemuxerReady)),
     77 #endif
     78       proxy_(proxy),
     79       player_id_(player_id),
     80       media_log_(media_log),
     81       demuxer_(NULL),
     82       is_demuxer_ready_(false),
     83       audio_stream_(NULL),
     84       video_stream_(NULL),
     85       seeking_(false),
     86       last_seek_request_id_(0),
     87       key_added_(false),
     88       access_unit_size_(0) {
     89 }
     90 
     91 MediaSourceDelegate::~MediaSourceDelegate() {
     92   DCHECK(main_loop_->BelongsToCurrentThread());
     93   DVLOG(1) << "~MediaSourceDelegate() : " << player_id_;
     94   DCHECK(!chunk_demuxer_);
     95   DCHECK(!demuxer_);
     96   DCHECK(!audio_decrypting_demuxer_stream_);
     97   DCHECK(!video_decrypting_demuxer_stream_);
     98   DCHECK(!audio_stream_);
     99   DCHECK(!video_stream_);
    100 }
    101 
    102 void MediaSourceDelegate::Destroy() {
    103   DCHECK(main_loop_->BelongsToCurrentThread());
    104   DVLOG(1) << "Destroy() : " << player_id_;
    105   if (!demuxer_) {
    106     delete this;
    107     return;
    108   }
    109 
    110   duration_change_cb_.Reset();
    111   update_network_state_cb_.Reset();
    112   media_source_.reset();
    113   proxy_ = NULL;
    114 
    115   main_weak_this_.InvalidateWeakPtrs();
    116   DCHECK(!main_weak_this_.HasWeakPtrs());
    117 
    118   if (chunk_demuxer_)
    119     chunk_demuxer_->Shutdown();
    120 #if defined(GOOGLE_TV)
    121   // |this| will be transfered to the callback StopDemuxer() and
    122   // OnDemuxerStopDone(). they own |this| and OnDemuxerStopDone() will delete
    123   // it when called. Hence using base::Unretained(this) is safe here.
    124   media_loop_->PostTask(FROM_HERE,
    125                         base::Bind(&MediaSourceDelegate::StopDemuxer,
    126                         base::Unretained(this)));
    127 #else
    128   StopDemuxer();
    129 #endif
    130 }
    131 
    132 void MediaSourceDelegate::StopDemuxer() {
    133   DCHECK_BELONG_TO_MEDIA_LOOP();
    134   DCHECK(demuxer_);
    135 
    136   audio_stream_ = NULL;
    137   video_stream_ = NULL;
    138   // TODO(xhwang): Figure out if we need to Reset the DDSs after Seeking or
    139   // before destroying them.
    140   audio_decrypting_demuxer_stream_.reset();
    141   video_decrypting_demuxer_stream_.reset();
    142 
    143   media_weak_this_.InvalidateWeakPtrs();
    144   DCHECK(!media_weak_this_.HasWeakPtrs());
    145 
    146   // The callback OnDemuxerStopDone() owns |this| and will delete it when
    147   // called. Hence using base::Unretained(this) is safe here.
    148   demuxer_->Stop(media::BindToLoop(main_loop_,
    149       base::Bind(&MediaSourceDelegate::OnDemuxerStopDone,
    150                  base::Unretained(this))));
    151 }
    152 
    153 void MediaSourceDelegate::InitializeMediaSource(
    154     WebKit::WebMediaSource* media_source,
    155     const media::NeedKeyCB& need_key_cb,
    156     const media::SetDecryptorReadyCB& set_decryptor_ready_cb,
    157     const UpdateNetworkStateCB& update_network_state_cb,
    158     const DurationChangeCB& duration_change_cb) {
    159   DCHECK(main_loop_->BelongsToCurrentThread());
    160   DCHECK(media_source);
    161   media_source_.reset(media_source);
    162   need_key_cb_ = need_key_cb;
    163   set_decryptor_ready_cb_ = set_decryptor_ready_cb;
    164   update_network_state_cb_ = media::BindToCurrentLoop(update_network_state_cb);
    165   duration_change_cb_ = media::BindToCurrentLoop(duration_change_cb);
    166   access_unit_size_ = kAccessUnitSizeForMediaSource;
    167 
    168   chunk_demuxer_.reset(new media::ChunkDemuxer(
    169       BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerOpened),
    170       BIND_TO_RENDER_LOOP_1(&MediaSourceDelegate::OnNeedKey, ""),
    171       // WeakPtrs can only bind to methods without return values.
    172       base::Bind(&MediaSourceDelegate::OnAddTextTrack, base::Unretained(this)),
    173       base::Bind(&LogMediaSourceError, media_log_)));
    174   demuxer_ = chunk_demuxer_.get();
    175 
    176 #if defined(GOOGLE_TV)
    177   // |this| will be retained until StopDemuxer() is posted, so Unretained() is
    178   // safe here.
    179   media_loop_->PostTask(FROM_HERE,
    180                         base::Bind(&MediaSourceDelegate::InitializeDemuxer,
    181                         base::Unretained(this)));
    182 #else
    183   InitializeDemuxer();
    184 #endif
    185 }
    186 
    187 void MediaSourceDelegate::InitializeDemuxer() {
    188   DCHECK_BELONG_TO_MEDIA_LOOP();
    189   demuxer_->Initialize(this, base::Bind(&MediaSourceDelegate::OnDemuxerInitDone,
    190                                         media_weak_this_.GetWeakPtr()));
    191 }
    192 
    193 #if defined(GOOGLE_TV)
    194 void MediaSourceDelegate::InitializeMediaStream(
    195     media::Demuxer* demuxer,
    196     const UpdateNetworkStateCB& update_network_state_cb) {
    197   DCHECK(main_loop_->BelongsToCurrentThread());
    198   DCHECK(demuxer);
    199   demuxer_ = demuxer;
    200   update_network_state_cb_ = media::BindToCurrentLoop(update_network_state_cb);
    201   // When playing Media Stream, don't wait to accumulate multiple packets per
    202   // IPC communication.
    203   access_unit_size_ = 1;
    204 
    205   // |this| will be retained until StopDemuxer() is posted, so Unretained() is
    206   // safe here.
    207   media_loop_->PostTask(FROM_HERE,
    208                         base::Bind(&MediaSourceDelegate::InitializeDemuxer,
    209                         base::Unretained(this)));
    210 }
    211 #endif
    212 
    213 const WebKit::WebTimeRanges& MediaSourceDelegate::Buffered() {
    214   buffered_web_time_ranges_ =
    215       ConvertToWebTimeRanges(buffered_time_ranges_);
    216   return buffered_web_time_ranges_;
    217 }
    218 
    219 size_t MediaSourceDelegate::DecodedFrameCount() const {
    220   return statistics_.video_frames_decoded;
    221 }
    222 
    223 size_t MediaSourceDelegate::DroppedFrameCount() const {
    224   return statistics_.video_frames_dropped;
    225 }
    226 
    227 size_t MediaSourceDelegate::AudioDecodedByteCount() const {
    228   return statistics_.audio_bytes_decoded;
    229 }
    230 
    231 size_t MediaSourceDelegate::VideoDecodedByteCount() const {
    232   return statistics_.video_bytes_decoded;
    233 }
    234 
    235 void MediaSourceDelegate::Seek(base::TimeDelta time, unsigned seek_request_id) {
    236   DCHECK(main_loop_->BelongsToCurrentThread());
    237   DVLOG(1) << "Seek(" << time.InSecondsF() << ") : " << player_id_;
    238 
    239   last_seek_time_ = time;
    240   last_seek_request_id_ = seek_request_id;
    241 
    242   if (chunk_demuxer_) {
    243     if (IsSeeking()) {
    244       chunk_demuxer_->CancelPendingSeek(time);
    245       return;
    246     }
    247 
    248     chunk_demuxer_->StartWaitingForSeek(time);
    249   }
    250 
    251   SetSeeking(true);
    252 #if defined(GOOGLE_TV)
    253   media_loop_->PostTask(FROM_HERE,
    254                         base::Bind(&MediaSourceDelegate::SeekInternal,
    255                                    base::Unretained(this),
    256                                    time, seek_request_id));
    257 #else
    258   SeekInternal(time, seek_request_id);
    259 #endif
    260 }
    261 
    262 void MediaSourceDelegate::SeekInternal(base::TimeDelta time,
    263                                        unsigned request_id) {
    264   DCHECK_BELONG_TO_MEDIA_LOOP();
    265   demuxer_->Seek(time, base::Bind(&MediaSourceDelegate::OnDemuxerSeekDone,
    266                                   media_weak_this_.GetWeakPtr(), request_id));
    267 }
    268 
    269 void MediaSourceDelegate::SetTotalBytes(int64 total_bytes) {
    270   NOTIMPLEMENTED();
    271 }
    272 
    273 void MediaSourceDelegate::AddBufferedByteRange(int64 start, int64 end) {
    274   NOTIMPLEMENTED();
    275 }
    276 
    277 void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start,
    278                                                base::TimeDelta end) {
    279   buffered_time_ranges_.Add(start, end);
    280 }
    281 
    282 void MediaSourceDelegate::SetDuration(base::TimeDelta duration) {
    283   DVLOG(1) << "SetDuration(" << duration.InSecondsF() << ") : " << player_id_;
    284   // Notify our owner (e.g. WebMediaPlayerAndroid) that duration has changed.
    285   // |duration_change_cb_| is bound to the main thread.
    286   if (!duration_change_cb_.is_null())
    287     duration_change_cb_.Run(duration);
    288 }
    289 
    290 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type) {
    291   DCHECK(main_loop_->BelongsToCurrentThread());
    292 #if defined(GOOGLE_TV)
    293   media_loop_->PostTask(
    294       FROM_HERE,
    295       base::Bind(&MediaSourceDelegate::OnReadFromDemuxerInternal,
    296                  base::Unretained(this), type));
    297 #else
    298   OnReadFromDemuxerInternal(type);
    299 #endif
    300 }
    301 
    302 void MediaSourceDelegate::OnReadFromDemuxerInternal(
    303     media::DemuxerStream::Type type) {
    304   DCHECK_BELONG_TO_MEDIA_LOOP();
    305   DVLOG(1) << "OnReadFromDemuxer(" << type << ") : " << player_id_;
    306   if (IsSeeking())
    307     return;  // Drop the request during seeking.
    308 
    309   DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO);
    310   // The access unit size should have been initialized properly at this stage.
    311   DCHECK_GT(access_unit_size_, 0u);
    312   scoped_ptr<MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params(
    313       new MediaPlayerHostMsg_ReadFromDemuxerAck_Params());
    314   params->type = type;
    315   params->access_units.resize(access_unit_size_);
    316   ReadFromDemuxerStream(type, params.Pass(), 0);
    317 }
    318 
    319 void MediaSourceDelegate::ReadFromDemuxerStream(
    320     media::DemuxerStream::Type type,
    321     scoped_ptr<MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params,
    322     size_t index) {
    323   DCHECK_BELONG_TO_MEDIA_LOOP();
    324   // DemuxerStream::Read() always returns the read callback asynchronously.
    325   DemuxerStream* stream =
    326       (type == DemuxerStream::AUDIO) ? audio_stream_ : video_stream_;
    327   stream->Read(base::Bind(
    328       &MediaSourceDelegate::OnBufferReady,
    329       media_weak_this_.GetWeakPtr(), type, base::Passed(&params), index));
    330 }
    331 
    332 void MediaSourceDelegate::OnBufferReady(
    333     media::DemuxerStream::Type type,
    334     scoped_ptr<MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params,
    335     size_t index,
    336     DemuxerStream::Status status,
    337     const scoped_refptr<media::DecoderBuffer>& buffer) {
    338   DCHECK_BELONG_TO_MEDIA_LOOP();
    339   DVLOG(1) << "OnBufferReady(" << index << ", " << status << ", "
    340            << ((!buffer || buffer->end_of_stream()) ?
    341                -1 : buffer->timestamp().InMilliseconds())
    342            << ") : " << player_id_;
    343   DCHECK(demuxer_);
    344 
    345   // No new OnReadFromDemuxer() will be called during seeking. So this callback
    346   // must be from previous OnReadFromDemuxer() call and should be ignored.
    347   if (IsSeeking()) {
    348     DVLOG(1) << "OnBufferReady(): Ignore previous read during seeking.";
    349     return;
    350   }
    351 
    352   bool is_audio = (type == DemuxerStream::AUDIO);
    353   if (status != DemuxerStream::kAborted &&
    354       index >= params->access_units.size()) {
    355     LOG(ERROR) << "The internal state inconsistency onBufferReady: "
    356                << (is_audio ? "Audio" : "Video") << ", index " << index
    357                <<", size " << params->access_units.size()
    358                << ", status " << static_cast<int>(status);
    359     NOTREACHED();
    360     return;
    361   }
    362 
    363   switch (status) {
    364     case DemuxerStream::kAborted:
    365       // Because the abort was caused by the seek, don't respond ack.
    366       DVLOG(1) << "OnBufferReady() : Aborted";
    367       return;
    368 
    369     case DemuxerStream::kConfigChanged:
    370       // In case of kConfigChanged, need to read decoder_config once
    371       // for the next reads.
    372       // TODO(kjyoun): Investigate if we need to use this new config. See
    373       // http://crbug.com/255783
    374       if (is_audio) {
    375         audio_stream_->audio_decoder_config();
    376       } else {
    377         gfx::Size size = video_stream_->video_decoder_config().coded_size();
    378         DVLOG(1) << "Video config is changed: " << size.width() << "x"
    379                  << size.height();
    380       }
    381       params->access_units[index].status = status;
    382       params->access_units.resize(index + 1);
    383       break;
    384 
    385     case DemuxerStream::kOk:
    386       params->access_units[index].status = status;
    387       if (buffer->end_of_stream()) {
    388         params->access_units[index].end_of_stream = true;
    389         params->access_units.resize(index + 1);
    390         break;
    391       }
    392       // TODO(ycheo): We assume that the inputed stream will be decoded
    393       // right away.
    394       // Need to implement this properly using MediaPlayer.OnInfoListener.
    395       if (is_audio) {
    396         statistics_.audio_bytes_decoded += buffer->data_size();
    397       } else {
    398         statistics_.video_bytes_decoded += buffer->data_size();
    399         statistics_.video_frames_decoded++;
    400       }
    401       params->access_units[index].timestamp = buffer->timestamp();
    402       params->access_units[index].data = std::vector<uint8>(
    403           buffer->data(),
    404           buffer->data() + buffer->data_size());
    405 #if !defined(GOOGLE_TV)
    406       // Vorbis needs 4 extra bytes padding on Android. Check
    407       // NuMediaExtractor.cpp in Android source code.
    408       if (is_audio && media::kCodecVorbis ==
    409           audio_stream_->audio_decoder_config().codec()) {
    410         params->access_units[index].data.insert(
    411             params->access_units[index].data.end(), kVorbisPadding,
    412             kVorbisPadding + 4);
    413       }
    414 #endif
    415       if (buffer->decrypt_config()) {
    416         params->access_units[index].key_id = std::vector<char>(
    417             buffer->decrypt_config()->key_id().begin(),
    418             buffer->decrypt_config()->key_id().end());
    419         params->access_units[index].iv = std::vector<char>(
    420             buffer->decrypt_config()->iv().begin(),
    421             buffer->decrypt_config()->iv().end());
    422         params->access_units[index].subsamples =
    423             buffer->decrypt_config()->subsamples();
    424       }
    425       if (++index < params->access_units.size()) {
    426         ReadFromDemuxerStream(type, params.Pass(), index);
    427         return;
    428       }
    429       break;
    430 
    431     default:
    432       NOTREACHED();
    433   }
    434 
    435 #if defined(GOOGLE_TV)
    436   send_read_from_demuxer_ack_cb_.Run(params.Pass());
    437 #else
    438   SendReadFromDemuxerAck(params.Pass());
    439 #endif
    440 }
    441 
    442 void MediaSourceDelegate::SendReadFromDemuxerAck(
    443     scoped_ptr<MediaPlayerHostMsg_ReadFromDemuxerAck_Params> params) {
    444   DCHECK(main_loop_->BelongsToCurrentThread());
    445   if (!IsSeeking() && proxy_)
    446     proxy_->ReadFromDemuxerAck(player_id_, *params);
    447 }
    448 
    449 void MediaSourceDelegate::OnDemuxerError(media::PipelineStatus status) {
    450   DVLOG(1) << "OnDemuxerError(" << status << ") : " << player_id_;
    451   // |update_network_state_cb_| is bound to the main thread.
    452   if (status != media::PIPELINE_OK && !update_network_state_cb_.is_null())
    453     update_network_state_cb_.Run(PipelineErrorToNetworkState(status));
    454 }
    455 
    456 void MediaSourceDelegate::OnDemuxerInitDone(media::PipelineStatus status) {
    457   DCHECK_BELONG_TO_MEDIA_LOOP();
    458   DVLOG(1) << "OnDemuxerInitDone(" << status << ") : " << player_id_;
    459   DCHECK(demuxer_);
    460 
    461   if (status != media::PIPELINE_OK) {
    462     OnDemuxerError(status);
    463     return;
    464   }
    465 
    466   audio_stream_ = demuxer_->GetStream(DemuxerStream::AUDIO);
    467   video_stream_ = demuxer_->GetStream(DemuxerStream::VIDEO);
    468 
    469   if (audio_stream_ && audio_stream_->audio_decoder_config().is_encrypted() &&
    470       !set_decryptor_ready_cb_.is_null()) {
    471     InitAudioDecryptingDemuxerStream();
    472     // InitVideoDecryptingDemuxerStream() will be called in
    473     // OnAudioDecryptingDemuxerStreamInitDone().
    474     return;
    475   }
    476 
    477   if (video_stream_ && video_stream_->video_decoder_config().is_encrypted() &&
    478       !set_decryptor_ready_cb_.is_null()) {
    479     InitVideoDecryptingDemuxerStream();
    480     return;
    481   }
    482 
    483   // Notify demuxer ready when both streams are not encrypted.
    484   is_demuxer_ready_ = true;
    485   if (CanNotifyDemuxerReady())
    486     NotifyDemuxerReady();
    487 }
    488 
    489 void MediaSourceDelegate::InitAudioDecryptingDemuxerStream() {
    490   DCHECK_BELONG_TO_MEDIA_LOOP();
    491   DVLOG(1) << "InitAudioDecryptingDemuxerStream() : " << player_id_;
    492   DCHECK(!set_decryptor_ready_cb_.is_null());
    493 
    494   audio_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
    495       base::MessageLoopProxy::current(), set_decryptor_ready_cb_));
    496   audio_decrypting_demuxer_stream_->Initialize(
    497       audio_stream_,
    498       base::Bind(&MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone,
    499                  media_weak_this_.GetWeakPtr()));
    500 }
    501 
    502 void MediaSourceDelegate::InitVideoDecryptingDemuxerStream() {
    503   DCHECK_BELONG_TO_MEDIA_LOOP();
    504   DVLOG(1) << "InitVideoDecryptingDemuxerStream() : " << player_id_;
    505   DCHECK(!set_decryptor_ready_cb_.is_null());
    506 
    507   video_decrypting_demuxer_stream_.reset(new media::DecryptingDemuxerStream(
    508       base::MessageLoopProxy::current(), set_decryptor_ready_cb_));
    509   video_decrypting_demuxer_stream_->Initialize(
    510       video_stream_,
    511       base::Bind(&MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone,
    512                  media_weak_this_.GetWeakPtr()));
    513 }
    514 
    515 void MediaSourceDelegate::OnAudioDecryptingDemuxerStreamInitDone(
    516     media::PipelineStatus status) {
    517   DCHECK_BELONG_TO_MEDIA_LOOP();
    518   DVLOG(1) << "OnAudioDecryptingDemuxerStreamInitDone(" << status
    519            << ") : " << player_id_;
    520   DCHECK(demuxer_);
    521 
    522   if (status != media::PIPELINE_OK)
    523     audio_decrypting_demuxer_stream_.reset();
    524   else
    525     audio_stream_ = audio_decrypting_demuxer_stream_.get();
    526 
    527   if (video_stream_ && video_stream_->video_decoder_config().is_encrypted()) {
    528     InitVideoDecryptingDemuxerStream();
    529     return;
    530   }
    531 
    532   // Try to notify demuxer ready when audio DDS initialization finished and
    533   // video is not encrypted.
    534   is_demuxer_ready_ = true;
    535   if (CanNotifyDemuxerReady())
    536     NotifyDemuxerReady();
    537 }
    538 
    539 void MediaSourceDelegate::OnVideoDecryptingDemuxerStreamInitDone(
    540     media::PipelineStatus status) {
    541   DCHECK_BELONG_TO_MEDIA_LOOP();
    542   DVLOG(1) << "OnVideoDecryptingDemuxerStreamInitDone(" << status
    543            << ") : " << player_id_;
    544   DCHECK(demuxer_);
    545 
    546   if (status != media::PIPELINE_OK)
    547     video_decrypting_demuxer_stream_.reset();
    548   else
    549     video_stream_ = video_decrypting_demuxer_stream_.get();
    550 
    551   // Try to notify demuxer ready when video DDS initialization finished.
    552   is_demuxer_ready_ = true;
    553   if (CanNotifyDemuxerReady())
    554     NotifyDemuxerReady();
    555 }
    556 
    557 void MediaSourceDelegate::OnDemuxerSeekDone(unsigned seek_request_id,
    558                                             media::PipelineStatus status) {
    559   DCHECK_BELONG_TO_MEDIA_LOOP();
    560   DVLOG(1) << "OnDemuxerSeekDone(" << status << ") : " << player_id_;
    561   DCHECK(IsSeeking());
    562 
    563   if (status != media::PIPELINE_OK) {
    564     OnDemuxerError(status);
    565     return;
    566   }
    567 
    568   // Newer seek has been issued. Resume the last seek request.
    569   if (seek_request_id != last_seek_request_id_) {
    570     if (chunk_demuxer_)
    571       chunk_demuxer_->StartWaitingForSeek(last_seek_time_);
    572     SeekInternal(last_seek_time_, last_seek_request_id_);
    573     return;
    574   }
    575 
    576   ResetAudioDecryptingDemuxerStream();
    577 }
    578 
    579 void MediaSourceDelegate::ResetAudioDecryptingDemuxerStream() {
    580   DCHECK_BELONG_TO_MEDIA_LOOP();
    581   DVLOG(1) << "ResetAudioDecryptingDemuxerStream() : " << player_id_;
    582   if (audio_decrypting_demuxer_stream_) {
    583     audio_decrypting_demuxer_stream_->Reset(
    584         base::Bind(&MediaSourceDelegate::ResetVideoDecryptingDemuxerStream,
    585                    media_weak_this_.GetWeakPtr()));
    586   } else {
    587     ResetVideoDecryptingDemuxerStream();
    588   }
    589 }
    590 
    591 void MediaSourceDelegate::ResetVideoDecryptingDemuxerStream() {
    592   DCHECK_BELONG_TO_MEDIA_LOOP();
    593   DVLOG(1) << "ResetVideoDecryptingDemuxerStream()";
    594 #if defined(GOOGLE_TV)
    595   if (video_decrypting_demuxer_stream_)
    596     video_decrypting_demuxer_stream_->Reset(send_seek_request_ack_cb_);
    597   else
    598     send_seek_request_ack_cb_.Run();
    599 #else
    600   if (video_decrypting_demuxer_stream_) {
    601     video_decrypting_demuxer_stream_->Reset(
    602         base::Bind(&MediaSourceDelegate::SendSeekRequestAck,
    603                    main_weak_this_.GetWeakPtr()));
    604   } else {
    605     SendSeekRequestAck();
    606   }
    607 #endif
    608 }
    609 
    610 void MediaSourceDelegate::SendSeekRequestAck() {
    611   DVLOG(1) << "SendSeekRequestAck() : " << player_id_;
    612   SetSeeking(false);
    613   proxy_->SeekRequestAck(player_id_, last_seek_request_id_);
    614   last_seek_request_id_ = 0;
    615 }
    616 
    617 void MediaSourceDelegate::OnDemuxerStopDone() {
    618   DCHECK(main_loop_->BelongsToCurrentThread());
    619   DVLOG(1) << "OnDemuxerStopDone() : " << player_id_;
    620   chunk_demuxer_.reset();
    621   demuxer_ = NULL;
    622   delete this;
    623 }
    624 
    625 void MediaSourceDelegate::OnMediaConfigRequest() {
    626 #if defined(GOOGLE_TV)
    627   if (!media_loop_->BelongsToCurrentThread()) {
    628     media_loop_->PostTask(FROM_HERE,
    629         base::Bind(&MediaSourceDelegate::OnMediaConfigRequest,
    630                    base::Unretained(this)));
    631     return;
    632   }
    633 #endif
    634   if (CanNotifyDemuxerReady())
    635     NotifyDemuxerReady();
    636 }
    637 
    638 void MediaSourceDelegate::NotifyKeyAdded(const std::string& key_system) {
    639 #if defined(GOOGLE_TV)
    640   if (!media_loop_->BelongsToCurrentThread()) {
    641     media_loop_->PostTask(FROM_HERE,
    642         base::Bind(&MediaSourceDelegate::NotifyKeyAdded,
    643                    base::Unretained(this), key_system));
    644     return;
    645   }
    646 #endif
    647   DVLOG(1) << "NotifyKeyAdded() : " << player_id_;
    648   // TODO(kjyoun): Enhance logic to detect when to call NotifyDemuxerReady()
    649   // For now, we calls it when the first key is added. See
    650   // http://crbug.com/255781
    651   if (key_added_)
    652     return;
    653   key_added_ = true;
    654   key_system_ = key_system;
    655   if (!CanNotifyDemuxerReady())
    656     return;
    657   if (HasEncryptedStream())
    658     NotifyDemuxerReady();
    659 }
    660 
    661 bool MediaSourceDelegate::CanNotifyDemuxerReady() {
    662   DCHECK_BELONG_TO_MEDIA_LOOP();
    663   // This can happen when a key is added before the demuxer is initialized.
    664   // See NotifyKeyAdded().
    665   // TODO(kjyoun): Remove NotifyDemxuerReady() call from NotifyKeyAdded() so
    666   // that we can remove all is_demuxer_ready_/key_added_/key_system_ madness.
    667   // See http://crbug.com/255781
    668   if (!is_demuxer_ready_)
    669     return false;
    670   if (HasEncryptedStream() && !key_added_)
    671     return false;
    672   return true;
    673 }
    674 
    675 void MediaSourceDelegate::NotifyDemuxerReady() {
    676   DCHECK_BELONG_TO_MEDIA_LOOP();
    677   DVLOG(1) << "NotifyDemuxerReady() : " << player_id_;
    678   DCHECK(CanNotifyDemuxerReady());
    679 
    680   scoped_ptr<MediaPlayerHostMsg_DemuxerReady_Params> params(
    681       new MediaPlayerHostMsg_DemuxerReady_Params());
    682   if (audio_stream_) {
    683     media::AudioDecoderConfig config = audio_stream_->audio_decoder_config();
    684     params->audio_codec = config.codec();
    685     params->audio_channels =
    686         media::ChannelLayoutToChannelCount(config.channel_layout());
    687     params->audio_sampling_rate = config.samples_per_second();
    688     params->is_audio_encrypted = config.is_encrypted();
    689     params->audio_extra_data = std::vector<uint8>(
    690         config.extra_data(), config.extra_data() + config.extra_data_size());
    691   }
    692   if (video_stream_) {
    693     media::VideoDecoderConfig config = video_stream_->video_decoder_config();
    694     params->video_codec = config.codec();
    695     params->video_size = config.natural_size();
    696     params->is_video_encrypted = config.is_encrypted();
    697     params->video_extra_data = std::vector<uint8>(
    698         config.extra_data(), config.extra_data() + config.extra_data_size());
    699   }
    700   params->duration_ms = GetDurationMs();
    701   params->key_system = HasEncryptedStream() ? key_system_ : "";
    702 
    703 #if defined(GOOGLE_TV)
    704   send_demuxer_ready_cb_.Run(params.Pass());
    705 #else
    706   SendDemuxerReady(params.Pass());
    707 #endif
    708 }
    709 
    710 void MediaSourceDelegate::SendDemuxerReady(
    711     scoped_ptr<MediaPlayerHostMsg_DemuxerReady_Params> params) {
    712   DCHECK(main_loop_->BelongsToCurrentThread());
    713   if (proxy_)
    714     proxy_->DemuxerReady(player_id_, *params);
    715 }
    716 
    717 int MediaSourceDelegate::GetDurationMs() {
    718   DCHECK_BELONG_TO_MEDIA_LOOP();
    719   if (!chunk_demuxer_)
    720     return -1;
    721 
    722   double duration_ms = chunk_demuxer_->GetDuration() * 1000;
    723   if (duration_ms > std::numeric_limits<int32>::max()) {
    724     LOG(WARNING) << "Duration from ChunkDemuxer is too large; probably "
    725                     "something has gone wrong.";
    726     return std::numeric_limits<int32>::max();
    727   }
    728   return duration_ms;
    729 }
    730 
    731 void MediaSourceDelegate::OnDemuxerOpened() {
    732   DCHECK(main_loop_->BelongsToCurrentThread());
    733   if (!media_source_)
    734     return;
    735 
    736   media_source_->open(new WebMediaSourceClientImpl(
    737       chunk_demuxer_.get(), base::Bind(&LogMediaSourceError, media_log_)));
    738 }
    739 
    740 void MediaSourceDelegate::OnNeedKey(const std::string& session_id,
    741                                     const std::string& type,
    742                                     scoped_ptr<uint8[]> init_data,
    743                                     int init_data_size) {
    744   DCHECK(main_loop_->BelongsToCurrentThread());
    745   if (need_key_cb_.is_null())
    746     return;
    747 
    748   need_key_cb_.Run(session_id, type, init_data.Pass(), init_data_size);
    749 }
    750 
    751 scoped_ptr<media::TextTrack> MediaSourceDelegate::OnAddTextTrack(
    752     media::TextKind kind,
    753     const std::string& label,
    754     const std::string& language) {
    755   return scoped_ptr<media::TextTrack>();
    756 }
    757 
    758 bool MediaSourceDelegate::HasEncryptedStream() {
    759   DCHECK_BELONG_TO_MEDIA_LOOP();
    760   return (audio_stream_ &&
    761           audio_stream_->audio_decoder_config().is_encrypted()) ||
    762          (video_stream_ &&
    763           video_stream_->video_decoder_config().is_encrypted());
    764 }
    765 
    766 void MediaSourceDelegate::SetSeeking(bool seeking) {
    767   base::AutoLock auto_lock(seeking_lock_);
    768   seeking_ = seeking;
    769 }
    770 
    771 bool MediaSourceDelegate::IsSeeking() const {
    772   base::AutoLock auto_lock(seeking_lock_);
    773   return seeking_;
    774 }
    775 
    776 }  // namespace content
    777