| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "webkit/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 "media/base/android/demuxer_stream_player_params.h" | |
| 10 #include "media/base/bind_to_loop.h" | |
| 11 #include "media/base/demuxer_stream.h" | |
| 12 #include "media/base/media_log.h" | |
| 13 #include "media/filters/chunk_demuxer.h" | |
| 14 #include "third_party/WebKit/public/platform/WebString.h" | |
| 15 #include "third_party/WebKit/public/web/WebMediaSource.h" | |
| 16 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" | |
| 17 #include "webkit/renderer/media/android/webmediaplayer_proxy_android.h" | |
| 18 #include "webkit/renderer/media/webmediaplayer_util.h" | |
| 19 #include "webkit/renderer/media/webmediasourceclient_impl.h" | |
| 20 | |
| 21 using media::DemuxerStream; | |
| 22 using media::MediaPlayerHostMsg_DemuxerReady_Params; | |
| 23 using media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params; | |
| 24 using WebKit::WebMediaPlayer; | |
| 25 using WebKit::WebString; | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 // The size of the access unit to transfer in an IPC in case of MediaSource. | |
| 30 // 16: approximately 250ms of content in 60 fps movies. | |
| 31 const size_t kAccessUnitSizeForMediaSource = 16; | |
| 32 | |
| 33 const uint8 kVorbisPadding[] = { 0xff, 0xff, 0xff, 0xff }; | |
| 34 | |
| 35 } // namespace | |
| 36 | |
| 37 namespace webkit_media { | |
| 38 | |
| 39 #define BIND_TO_RENDER_LOOP(function) \ | |
| 40 media::BindToLoop(base::MessageLoopProxy::current(), \ | |
| 41 base::Bind(function, weak_this_.GetWeakPtr())) | |
| 42 | |
| 43 #define BIND_TO_RENDER_LOOP_1(function, arg1) \ | |
| 44 media::BindToLoop(base::MessageLoopProxy::current(), \ | |
| 45 base::Bind(function, weak_this_.GetWeakPtr(), arg1)) | |
| 46 | |
| 47 #define BIND_TO_RENDER_LOOP_2(function, arg1, arg2) \ | |
| 48 media::BindToLoop(base::MessageLoopProxy::current(), \ | |
| 49 base::Bind(function, weak_this_.GetWeakPtr(), arg1, arg2)) | |
| 50 | |
| 51 #define BIND_TO_RENDER_LOOP_3(function, arg1, arg2, arg3) \ | |
| 52 media::BindToLoop(base::MessageLoopProxy::current(), \ | |
| 53 base::Bind(function, \ | |
| 54 weak_this_.GetWeakPtr(), arg1, arg2, arg3)) | |
| 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(WebMediaPlayerProxyAndroid* proxy, | |
| 62 int player_id, | |
| 63 media::MediaLog* media_log) | |
| 64 : weak_this_(this), | |
| 65 proxy_(proxy), | |
| 66 player_id_(player_id), | |
| 67 media_log_(media_log), | |
| 68 demuxer_(NULL), | |
| 69 audio_params_(new MediaPlayerHostMsg_ReadFromDemuxerAck_Params), | |
| 70 video_params_(new MediaPlayerHostMsg_ReadFromDemuxerAck_Params), | |
| 71 seeking_(false), | |
| 72 access_unit_size_(0) { | |
| 73 } | |
| 74 | |
| 75 MediaSourceDelegate::~MediaSourceDelegate() { | |
| 76 DVLOG(1) << "MediaSourceDelegate::~MediaSourceDelegate() : " << player_id_; | |
| 77 DCHECK(!chunk_demuxer_); | |
| 78 DCHECK(!demuxer_); | |
| 79 } | |
| 80 | |
| 81 void MediaSourceDelegate::Destroy() { | |
| 82 DVLOG(1) << "MediaSourceDelegate::Destroy() : " << player_id_; | |
| 83 if (!demuxer_) { | |
| 84 delete this; | |
| 85 return; | |
| 86 } | |
| 87 | |
| 88 duration_change_cb_.Reset(); | |
| 89 update_network_state_cb_.Reset(); | |
| 90 media_source_.reset(); | |
| 91 proxy_ = NULL; | |
| 92 | |
| 93 demuxer_ = NULL; | |
| 94 if (chunk_demuxer_) | |
| 95 chunk_demuxer_->Stop( | |
| 96 BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerStopDone)); | |
| 97 } | |
| 98 | |
| 99 void MediaSourceDelegate::InitializeMediaSource( | |
| 100 WebKit::WebMediaSource* media_source, | |
| 101 const media::NeedKeyCB& need_key_cb, | |
| 102 const UpdateNetworkStateCB& update_network_state_cb, | |
| 103 const DurationChangeCB& duration_change_cb) { | |
| 104 DCHECK(media_source); | |
| 105 media_source_.reset(media_source); | |
| 106 need_key_cb_ = need_key_cb; | |
| 107 update_network_state_cb_ = update_network_state_cb; | |
| 108 duration_change_cb_ = duration_change_cb; | |
| 109 | |
| 110 chunk_demuxer_.reset(new media::ChunkDemuxer( | |
| 111 BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerOpened), | |
| 112 BIND_TO_RENDER_LOOP_1(&MediaSourceDelegate::OnNeedKey, ""), | |
| 113 base::Bind(&MediaSourceDelegate::OnAddTextTrack, | |
| 114 base::Unretained(this)), | |
| 115 base::Bind(&LogMediaSourceError, media_log_))); | |
| 116 chunk_demuxer_->Initialize(this, | |
| 117 BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerInitDone)); | |
| 118 demuxer_ = chunk_demuxer_.get(); | |
| 119 access_unit_size_ = kAccessUnitSizeForMediaSource; | |
| 120 } | |
| 121 | |
| 122 #if defined(GOOGLE_TV) | |
| 123 void MediaSourceDelegate::InitializeMediaStream( | |
| 124 media::Demuxer* demuxer, | |
| 125 const UpdateNetworkStateCB& update_network_state_cb) { | |
| 126 DCHECK(demuxer); | |
| 127 demuxer_ = demuxer; | |
| 128 update_network_state_cb_ = update_network_state_cb; | |
| 129 | |
| 130 demuxer_->Initialize(this, | |
| 131 BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerInitDone)); | |
| 132 // When playing Media Stream, don't wait to accumulate multiple packets per | |
| 133 // IPC communication. | |
| 134 access_unit_size_ = 1; | |
| 135 } | |
| 136 #endif | |
| 137 | |
| 138 const WebKit::WebTimeRanges& MediaSourceDelegate::Buffered() { | |
| 139 buffered_web_time_ranges_ = | |
| 140 ConvertToWebTimeRanges(buffered_time_ranges_); | |
| 141 return buffered_web_time_ranges_; | |
| 142 } | |
| 143 | |
| 144 size_t MediaSourceDelegate::DecodedFrameCount() const { | |
| 145 return statistics_.video_frames_decoded; | |
| 146 } | |
| 147 | |
| 148 size_t MediaSourceDelegate::DroppedFrameCount() const { | |
| 149 return statistics_.video_frames_dropped; | |
| 150 } | |
| 151 | |
| 152 size_t MediaSourceDelegate::AudioDecodedByteCount() const { | |
| 153 return statistics_.audio_bytes_decoded; | |
| 154 } | |
| 155 | |
| 156 size_t MediaSourceDelegate::VideoDecodedByteCount() const { | |
| 157 return statistics_.video_bytes_decoded; | |
| 158 } | |
| 159 | |
| 160 void MediaSourceDelegate::Seek(base::TimeDelta time) { | |
| 161 DVLOG(1) << "MediaSourceDelegate::Seek(" << time.InSecondsF() << ") : " | |
| 162 << player_id_; | |
| 163 seeking_ = true; | |
| 164 DCHECK(demuxer_); | |
| 165 if (chunk_demuxer_) | |
| 166 chunk_demuxer_->StartWaitingForSeek(); | |
| 167 demuxer_->Seek(time, | |
| 168 BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerError)); | |
| 169 } | |
| 170 | |
| 171 void MediaSourceDelegate::CancelPendingSeek() { | |
| 172 if (chunk_demuxer_) | |
| 173 chunk_demuxer_->CancelPendingSeek(); | |
| 174 } | |
| 175 | |
| 176 void MediaSourceDelegate::SetTotalBytes(int64 total_bytes) { | |
| 177 NOTIMPLEMENTED(); | |
| 178 } | |
| 179 | |
| 180 void MediaSourceDelegate::AddBufferedByteRange(int64 start, int64 end) { | |
| 181 NOTIMPLEMENTED(); | |
| 182 } | |
| 183 | |
| 184 void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start, | |
| 185 base::TimeDelta end) { | |
| 186 buffered_time_ranges_.Add(start, end); | |
| 187 } | |
| 188 | |
| 189 void MediaSourceDelegate::SetDuration(base::TimeDelta duration) { | |
| 190 DVLOG(1) << "MediaSourceDelegate::SetDuration(" << duration.InSecondsF() | |
| 191 << ") : " << player_id_; | |
| 192 // Notify our owner (e.g. WebMediaPlayerAndroid) that | |
| 193 // duration has changed. | |
| 194 if (!duration_change_cb_.is_null()) | |
| 195 duration_change_cb_.Run(duration); | |
| 196 } | |
| 197 | |
| 198 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type, | |
| 199 bool seek_done) { | |
| 200 DVLOG(1) << "MediaSourceDelegate::OnReadFromDemuxer(" << type | |
| 201 << ", " << seek_done << ") : " << player_id_; | |
| 202 if (seeking_ && !seek_done) | |
| 203 return; // Drop the request during seeking. | |
| 204 seeking_ = false; | |
| 205 | |
| 206 DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO); | |
| 207 // The access unit size should have been initialized properly at this stage. | |
| 208 DCHECK_GT(access_unit_size_, 0u); | |
| 209 MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params = | |
| 210 type == DemuxerStream::AUDIO ? audio_params_.get() : video_params_.get(); | |
| 211 params->type = type; | |
| 212 params->access_units.resize(access_unit_size_); | |
| 213 DemuxerStream* stream = demuxer_->GetStream(type); | |
| 214 DCHECK(stream != NULL); | |
| 215 ReadFromDemuxerStream(stream, params, 0); | |
| 216 } | |
| 217 | |
| 218 void MediaSourceDelegate::ReadFromDemuxerStream( | |
| 219 DemuxerStream* stream, | |
| 220 MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params, | |
| 221 size_t index) { | |
| 222 stream->Read(BIND_TO_RENDER_LOOP_3(&MediaSourceDelegate::OnBufferReady, | |
| 223 stream, params, index)); | |
| 224 } | |
| 225 | |
| 226 void MediaSourceDelegate::OnBufferReady( | |
| 227 DemuxerStream* stream, | |
| 228 MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params, | |
| 229 size_t index, | |
| 230 DemuxerStream::Status status, | |
| 231 const scoped_refptr<media::DecoderBuffer>& buffer) { | |
| 232 DVLOG(1) << "MediaSourceDelegate::OnBufferReady() : " << player_id_; | |
| 233 DCHECK(status == DemuxerStream::kAborted || | |
| 234 index < params->access_units.size()); | |
| 235 bool is_audio = stream->type() == DemuxerStream::AUDIO; | |
| 236 if (status != DemuxerStream::kAborted && | |
| 237 index >= params->access_units.size()) { | |
| 238 LOG(ERROR) << "The internal state inconsistency onBufferReady: " | |
| 239 << (is_audio ? "Audio" : "Video") << ", index " << index | |
| 240 <<", size " << params->access_units.size() | |
| 241 << ", status " << static_cast<int>(status); | |
| 242 return; | |
| 243 } | |
| 244 switch (status) { | |
| 245 case DemuxerStream::kAborted: | |
| 246 // Because the abort was caused by the seek, don't respond ack. | |
| 247 return; | |
| 248 | |
| 249 case DemuxerStream::kConfigChanged: | |
| 250 // In case of kConfigChanged, need to read decoder_config once | |
| 251 // for the next reads. | |
| 252 if (is_audio) { | |
| 253 stream->audio_decoder_config(); | |
| 254 } else { | |
| 255 gfx::Size size = stream->video_decoder_config().coded_size(); | |
| 256 DVLOG(1) << "Video config is changed: " << | |
| 257 size.width() << "x" << size.height(); | |
| 258 } | |
| 259 params->access_units[index].status = status; | |
| 260 params->access_units.resize(index + 1); | |
| 261 break; | |
| 262 | |
| 263 case DemuxerStream::kOk: | |
| 264 params->access_units[index].status = status; | |
| 265 if (buffer->IsEndOfStream()) { | |
| 266 params->access_units[index].end_of_stream = true; | |
| 267 params->access_units.resize(index + 1); | |
| 268 break; | |
| 269 } | |
| 270 // TODO(ycheo): We assume that the inputed stream will be decoded | |
| 271 // right away. | |
| 272 // Need to implement this properly using MediaPlayer.OnInfoListener. | |
| 273 if (is_audio) { | |
| 274 statistics_.audio_bytes_decoded += buffer->GetDataSize(); | |
| 275 } else { | |
| 276 statistics_.video_bytes_decoded += buffer->GetDataSize(); | |
| 277 statistics_.video_frames_decoded++; | |
| 278 } | |
| 279 params->access_units[index].timestamp = buffer->GetTimestamp(); | |
| 280 params->access_units[index].data = std::vector<uint8>( | |
| 281 buffer->GetData(), | |
| 282 buffer->GetData() + buffer->GetDataSize()); | |
| 283 #if !defined(GOOGLE_TV) | |
| 284 // Vorbis needs 4 extra bytes padding on Android. Check | |
| 285 // NuMediaExtractor.cpp in Android source code. | |
| 286 if (is_audio && media::kCodecVorbis == | |
| 287 stream->audio_decoder_config().codec()) { | |
| 288 params->access_units[index].data.insert( | |
| 289 params->access_units[index].data.end(), kVorbisPadding, | |
| 290 kVorbisPadding + 4); | |
| 291 } | |
| 292 #endif | |
| 293 if (buffer->GetDecryptConfig()) { | |
| 294 params->access_units[index].key_id = std::vector<char>( | |
| 295 buffer->GetDecryptConfig()->key_id().begin(), | |
| 296 buffer->GetDecryptConfig()->key_id().end()); | |
| 297 params->access_units[index].iv = std::vector<char>( | |
| 298 buffer->GetDecryptConfig()->iv().begin(), | |
| 299 buffer->GetDecryptConfig()->iv().end()); | |
| 300 params->access_units[index].subsamples = | |
| 301 buffer->GetDecryptConfig()->subsamples(); | |
| 302 } | |
| 303 if (++index < params->access_units.size()) { | |
| 304 ReadFromDemuxerStream(stream, params, index); | |
| 305 return; | |
| 306 } | |
| 307 break; | |
| 308 | |
| 309 default: | |
| 310 NOTREACHED(); | |
| 311 } | |
| 312 | |
| 313 if (proxy_) | |
| 314 proxy_->ReadFromDemuxerAck(player_id_, *params); | |
| 315 params->access_units.resize(0); | |
| 316 } | |
| 317 | |
| 318 void MediaSourceDelegate::OnDemuxerError( | |
| 319 media::PipelineStatus status) { | |
| 320 DVLOG(1) << "MediaSourceDelegate::OnDemuxerError(" << status << ") : " | |
| 321 << player_id_; | |
| 322 if (status != media::PIPELINE_OK && !update_network_state_cb_.is_null()) | |
| 323 update_network_state_cb_.Run(PipelineErrorToNetworkState(status)); | |
| 324 } | |
| 325 | |
| 326 void MediaSourceDelegate::OnDemuxerInitDone( | |
| 327 media::PipelineStatus status) { | |
| 328 DVLOG(1) << "MediaSourceDelegate::OnDemuxerInitDone(" << status << ") : " | |
| 329 << player_id_; | |
| 330 if (status != media::PIPELINE_OK) { | |
| 331 OnDemuxerError(status); | |
| 332 return; | |
| 333 } | |
| 334 NotifyDemuxerReady(""); | |
| 335 } | |
| 336 | |
| 337 void MediaSourceDelegate::OnDemuxerStopDone() { | |
| 338 DVLOG(1) << "MediaSourceDelegate::OnDemuxerStopDone() : " << player_id_; | |
| 339 chunk_demuxer_.reset(); | |
| 340 delete this; | |
| 341 } | |
| 342 | |
| 343 void MediaSourceDelegate::OnMediaConfigRequest() { | |
| 344 NotifyDemuxerReady(""); | |
| 345 } | |
| 346 | |
| 347 void MediaSourceDelegate::NotifyDemuxerReady(const std::string& key_system) { | |
| 348 if (!demuxer_) | |
| 349 return; | |
| 350 MediaPlayerHostMsg_DemuxerReady_Params params; | |
| 351 DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO); | |
| 352 if (audio_stream) { | |
| 353 const media::AudioDecoderConfig& config = | |
| 354 audio_stream->audio_decoder_config(); | |
| 355 params.audio_codec = config.codec(); | |
| 356 params.audio_channels = | |
| 357 media::ChannelLayoutToChannelCount(config.channel_layout()); | |
| 358 params.audio_sampling_rate = config.samples_per_second(); | |
| 359 params.is_audio_encrypted = config.is_encrypted(); | |
| 360 params.audio_extra_data = std::vector<uint8>( | |
| 361 config.extra_data(), config.extra_data() + config.extra_data_size()); | |
| 362 } | |
| 363 DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO); | |
| 364 if (video_stream) { | |
| 365 const media::VideoDecoderConfig& config = | |
| 366 video_stream->video_decoder_config(); | |
| 367 params.video_codec = config.codec(); | |
| 368 params.video_size = config.natural_size(); | |
| 369 params.is_video_encrypted = config.is_encrypted(); | |
| 370 params.video_extra_data = std::vector<uint8>( | |
| 371 config.extra_data(), config.extra_data() + config.extra_data_size()); | |
| 372 } | |
| 373 params.duration_ms = GetDurationMs(); | |
| 374 params.key_system = key_system; | |
| 375 | |
| 376 bool ready_to_send = (!params.is_audio_encrypted && | |
| 377 !params.is_video_encrypted) || !key_system.empty(); | |
| 378 if (proxy_ && ready_to_send) | |
| 379 proxy_->DemuxerReady(player_id_, params); | |
| 380 } | |
| 381 | |
| 382 int MediaSourceDelegate::GetDurationMs() { | |
| 383 if (!chunk_demuxer_) | |
| 384 return -1; | |
| 385 | |
| 386 double duration_ms = chunk_demuxer_->GetDuration() * 1000; | |
| 387 if (duration_ms > std::numeric_limits<int32>::max()) { | |
| 388 LOG(WARNING) << "Duration from ChunkDemuxer is too large; probably " | |
| 389 "something has gone wrong."; | |
| 390 return std::numeric_limits<int32>::max(); | |
| 391 } | |
| 392 return duration_ms; | |
| 393 } | |
| 394 | |
| 395 void MediaSourceDelegate::OnDemuxerOpened() { | |
| 396 if (!media_source_) | |
| 397 return; | |
| 398 | |
| 399 media_source_->open(new WebMediaSourceClientImpl( | |
| 400 chunk_demuxer_.get(), base::Bind(&LogMediaSourceError, media_log_))); | |
| 401 } | |
| 402 | |
| 403 void MediaSourceDelegate::OnNeedKey(const std::string& session_id, | |
| 404 const std::string& type, | |
| 405 scoped_ptr<uint8[]> init_data, | |
| 406 int init_data_size) { | |
| 407 if (need_key_cb_.is_null()) | |
| 408 return; | |
| 409 | |
| 410 need_key_cb_.Run(session_id, type, init_data.Pass(), init_data_size); | |
| 411 } | |
| 412 | |
| 413 scoped_ptr<media::TextTrack> MediaSourceDelegate::OnAddTextTrack( | |
| 414 media::TextKind kind, | |
| 415 const std::string& label, | |
| 416 const std::string& language) { | |
| 417 return scoped_ptr<media::TextTrack>(); | |
| 418 } | |
| 419 | |
| 420 } // namespace webkit_media | |
| OLD | NEW |