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