Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 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 "chromecast/media/cma/pipeline/av_pipeline_impl.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/location.h" | |
| 9 #include "base/message_loop/message_loop_proxy.h" | |
| 10 #include "base/strings/string_number_conversions.h" | |
| 11 #include "chromecast/media/base/decrypt_context.h" | |
| 12 #include "chromecast/media/cdm/browser_cdm_cast.h" | |
| 13 #include "chromecast/media/cma/backend/media_clock_device.h" | |
| 14 #include "chromecast/media/cma/backend/media_component_device.h" | |
| 15 #include "chromecast/media/cma/base/buffering_frame_provider.h" | |
| 16 #include "chromecast/media/cma/base/buffering_state.h" | |
| 17 #include "chromecast/media/cma/base/cma_logging.h" | |
| 18 #include "chromecast/media/cma/base/coded_frame_provider.h" | |
| 19 #include "chromecast/media/cma/base/decoder_buffer_base.h" | |
| 20 #include "chromecast/media/cma/pipeline/decrypt_util.h" | |
| 21 #include "media/base/audio_decoder_config.h" | |
| 22 #include "media/base/decrypt_config.h" | |
| 23 | |
| 24 namespace chromecast { | |
| 25 namespace media { | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 const int kNoCallbackId = -1; | |
| 30 | |
| 31 } // namespace | |
| 32 | |
| 33 AvPipelineImpl::AvPipelineImpl( | |
| 34 MediaComponentDevice* media_component_device, | |
| 35 const UpdateConfigCB& update_config_cb) | |
| 36 : update_config_cb_(update_config_cb), | |
| 37 media_component_device_(media_component_device), | |
| 38 state_(kUninitialized), | |
| 39 buffered_time_(::media::kNoTimestamp()), | |
| 40 playable_buffered_time_(::media::kNoTimestamp()), | |
| 41 enable_feeding_(false), | |
| 42 pending_read_(false), | |
| 43 pending_push_(false), | |
| 44 enable_time_update_(false), | |
| 45 pending_time_update_task_(false), | |
| 46 media_keys_(NULL), | |
| 47 media_keys_callback_id_(kNoCallbackId), | |
| 48 weak_factory_(this) { | |
| 49 DCHECK(media_component_device); | |
| 50 weak_this_ = weak_factory_.GetWeakPtr(); | |
| 51 thread_checker_.DetachFromThread(); | |
| 52 } | |
| 53 | |
| 54 AvPipelineImpl::~AvPipelineImpl() { | |
| 55 // If there are weak pointers in the wild, they must be invalidated | |
| 56 // on the right thread. | |
| 57 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 58 media_component_device_->SetClient(MediaComponentDevice::Client()); | |
| 59 | |
| 60 if (media_keys_ && media_keys_callback_id_ != kNoCallbackId) { | |
|
damienv1
2014/11/20 17:08:26
nit: remove curly brace
gunsch
2014/11/20 19:50:27
Done.
| |
| 61 media_keys_->UnregisterPlayer(media_keys_callback_id_); | |
| 62 } | |
| 63 } | |
| 64 | |
| 65 void AvPipelineImpl::TransitionToState(State state) { | |
| 66 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 67 state_ = state; | |
| 68 } | |
| 69 | |
| 70 void AvPipelineImpl::SetCodedFrameProvider( | |
| 71 scoped_ptr<CodedFrameProvider> frame_provider, | |
| 72 size_t max_buffer_size, | |
| 73 size_t max_frame_size) { | |
| 74 DCHECK_EQ(state_, kUninitialized); | |
| 75 DCHECK(frame_provider); | |
| 76 | |
| 77 // Wrap the incoming frame provider to add some buffering capabilities. | |
| 78 frame_provider_.reset( | |
| 79 new BufferingFrameProvider( | |
| 80 frame_provider.Pass(), | |
| 81 max_buffer_size, | |
| 82 max_frame_size, | |
| 83 base::Bind(&AvPipelineImpl::OnFrameBuffered, weak_this_))); | |
| 84 } | |
| 85 | |
| 86 void AvPipelineImpl::SetClient(const AvPipelineClient& client) { | |
| 87 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 88 DCHECK_EQ(state_, kUninitialized); | |
| 89 client_ = client; | |
| 90 } | |
| 91 | |
| 92 bool AvPipelineImpl::Initialize() { | |
| 93 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 94 DCHECK_EQ(state_, kUninitialized); | |
| 95 | |
| 96 MediaComponentDevice::Client client; | |
| 97 client.eos_cb = base::Bind(&AvPipelineImpl::OnEos, weak_this_); | |
| 98 media_component_device_->SetClient(client); | |
| 99 if (!media_component_device_->SetState(MediaComponentDevice::kStateIdle)) | |
| 100 return false; | |
| 101 | |
| 102 return true; | |
| 103 } | |
| 104 | |
| 105 bool AvPipelineImpl::StartPlayingFrom( | |
| 106 base::TimeDelta time, | |
| 107 const scoped_refptr<BufferingState>& buffering_state) { | |
| 108 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 109 DCHECK_EQ(state_, kFlushed); | |
| 110 | |
| 111 // Media time where rendering should start | |
| 112 // and switch to a state where the audio device accepts incoming buffers. | |
| 113 if (!media_component_device_->SetStartPts(time) || | |
| 114 !media_component_device_->SetState(MediaComponentDevice::kStatePaused)) { | |
| 115 return false; | |
| 116 } | |
| 117 | |
| 118 // Buffering related initialization. | |
| 119 DCHECK(frame_provider_); | |
| 120 buffering_state_ = buffering_state; | |
| 121 if (buffering_state_.get()) | |
| 122 buffering_state_->SetMediaTime(time); | |
| 123 | |
| 124 // Start feeding the pipeline. | |
| 125 enable_feeding_ = true; | |
| 126 base::MessageLoopProxy::current()->PostTask( | |
| 127 FROM_HERE, | |
| 128 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); | |
| 129 | |
| 130 if (!media_component_device_->SetState(MediaComponentDevice::kStateRunning)) | |
| 131 return false; | |
|
damienv1
2014/11/20 17:08:26
StartPlayingFrom used to be 2 different actions (P
gunsch
2014/11/20 19:50:27
Strictly speaking, this code is in order from when
| |
| 132 | |
| 133 return true; | |
| 134 } | |
| 135 | |
| 136 bool AvPipelineImpl::Pause() { | |
| 137 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 138 DCHECK_EQ(state_, kPlaying); | |
| 139 | |
| 140 DCHECK_EQ( | |
| 141 media_component_device_->GetState(), MediaComponentDevice::kStateRunning); | |
|
damienv1
2014/11/20 17:08:26
Can you add a comment for this block of code to me
gunsch
2014/11/20 19:50:27
Done.
| |
| 142 media_component_device_->SetState(MediaComponentDevice::kStateIdle); | |
| 143 pending_push_ = false; | |
| 144 | |
| 145 // Break the feeding loop. | |
| 146 enable_feeding_ = false; | |
| 147 | |
| 148 return true; | |
| 149 } | |
| 150 | |
| 151 void AvPipelineImpl::Flush(const base::Closure& done_cb) { | |
|
damienv1
2014/11/20 17:33:39
Could you merge Pause and Flush (always used toget
gunsch
2014/11/20 19:50:27
Done.
| |
| 152 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 153 DCHECK_EQ(state_, kFlushing); | |
| 154 | |
| 155 // Remove any pending buffer. | |
| 156 pending_buffer_ = scoped_refptr<DecoderBufferBase>(); | |
| 157 | |
| 158 // Finally, remove any frames left in the frame provider. | |
| 159 pending_read_ = false; | |
| 160 buffered_time_ = ::media::kNoTimestamp(); | |
| 161 playable_buffered_time_ = ::media::kNoTimestamp(); | |
| 162 non_playable_frames_.clear(); | |
| 163 frame_provider_->Flush(done_cb); | |
| 164 } | |
| 165 | |
| 166 void AvPipelineImpl::Stop() { | |
| 167 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 168 | |
| 169 // Stop can be called from any state. | |
| 170 if (state_ == kUninitialized || state_ == kStopped) | |
| 171 return; | |
| 172 | |
| 173 // Stop feeding the pipeline. | |
| 174 enable_feeding_ = false; | |
| 175 | |
| 176 // Release hardware resources on Stop. | |
| 177 if (media_component_device_->GetState() == | |
| 178 MediaComponentDevice::kStatePaused || | |
| 179 media_component_device_->GetState() == | |
| 180 MediaComponentDevice::kStateRunning) { | |
| 181 media_component_device_->SetState(MediaComponentDevice::kStateIdle); | |
| 182 } | |
| 183 if (media_component_device_->GetState() == MediaComponentDevice::kStateIdle) { | |
| 184 media_component_device_->SetState( | |
| 185 MediaComponentDevice::kStateUninitialized); | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 void AvPipelineImpl::SetCdm(BrowserCdmCast* media_keys) { | |
| 190 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 191 DCHECK(media_keys); | |
| 192 | |
| 193 if (media_keys_ && media_keys_callback_id_ != kNoCallbackId) { | |
|
damienv1
2014/11/20 17:08:26
nit: remove curly braces.
gunsch
2014/11/20 19:50:27
Done.
| |
| 194 media_keys_->UnregisterPlayer(media_keys_callback_id_); | |
| 195 } | |
| 196 | |
| 197 media_keys_ = media_keys; | |
| 198 media_keys_callback_id_ = media_keys_->RegisterPlayer( | |
| 199 base::Bind(&AvPipelineImpl::OnCdmStateChanged, weak_this_), | |
| 200 base::Bind(&AvPipelineImpl::OnCdmDestroyed, weak_this_)); | |
| 201 } | |
| 202 | |
| 203 void AvPipelineImpl::OnEos() { | |
| 204 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 205 CMALOG(kLogControl) << __FUNCTION__; | |
| 206 if (state_ != kPlaying) | |
| 207 return; | |
| 208 | |
| 209 if (!client_.eos_cb.is_null()) | |
| 210 client_.eos_cb.Run(); | |
| 211 } | |
| 212 | |
| 213 void AvPipelineImpl::FetchBufferIfNeeded() { | |
| 214 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 215 if (!enable_feeding_) | |
| 216 return; | |
| 217 | |
| 218 if (pending_read_ || pending_buffer_.get()) | |
| 219 return; | |
| 220 | |
| 221 pending_read_ = true; | |
| 222 frame_provider_->Read( | |
| 223 base::Bind(&AvPipelineImpl::OnNewFrame, weak_this_)); | |
| 224 } | |
| 225 | |
| 226 void AvPipelineImpl::OnNewFrame( | |
| 227 const scoped_refptr<DecoderBufferBase>& buffer, | |
| 228 const ::media::AudioDecoderConfig& audio_config, | |
| 229 const ::media::VideoDecoderConfig& video_config) { | |
| 230 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 231 pending_read_ = false; | |
| 232 | |
| 233 if (audio_config.IsValidConfig() || video_config.IsValidConfig()) { | |
|
damienv1
2014/11/20 17:08:26
nit: remove curly braces.
gunsch
2014/11/20 19:50:27
Done.
| |
| 234 update_config_cb_.Run(audio_config, video_config); | |
| 235 } | |
| 236 | |
| 237 pending_buffer_ = buffer; | |
| 238 ProcessPendingBuffer(); | |
| 239 | |
| 240 base::MessageLoopProxy::current()->PostTask( | |
| 241 FROM_HERE, | |
| 242 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); | |
| 243 } | |
| 244 | |
| 245 void AvPipelineImpl::ProcessPendingBuffer() { | |
| 246 if (!enable_feeding_) | |
| 247 return; | |
| 248 | |
| 249 // Initiate a read if there isn't already one. | |
| 250 if (!pending_buffer_.get() && !pending_read_) { | |
| 251 base::MessageLoopProxy::current()->PostTask( | |
| 252 FROM_HERE, | |
| 253 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); | |
| 254 return; | |
| 255 } | |
| 256 | |
| 257 if (!pending_buffer_.get() || pending_push_) | |
| 258 return; | |
| 259 | |
| 260 // Break the feeding loop when the end of stream is reached. | |
| 261 if (pending_buffer_->end_of_stream()) { | |
| 262 CMALOG(kLogControl) << __FUNCTION__ << ": EOS reached, stopped feeding"; | |
| 263 enable_feeding_ = false; | |
| 264 } | |
| 265 | |
| 266 scoped_refptr<DecryptContext> decrypt_context; | |
| 267 if (!pending_buffer_->end_of_stream() && | |
| 268 pending_buffer_->decrypt_config()) { | |
| 269 // Verify that CDM has the key ID. | |
| 270 // Should not send the frame if the key ID is not available yet. | |
| 271 std::string key_id(pending_buffer_->decrypt_config()->key_id()); | |
| 272 if (!media_keys_) { | |
| 273 CMALOG(kLogControl) << "No CDM for frame: pts=" | |
| 274 << pending_buffer_->timestamp().InMilliseconds(); | |
| 275 return; | |
| 276 } | |
| 277 decrypt_context = media_keys_->GetDecryptContext(key_id); | |
| 278 if (!decrypt_context.get()) { | |
| 279 CMALOG(kLogControl) << "frame(pts=" | |
| 280 << pending_buffer_->timestamp().InMilliseconds() | |
| 281 << "): waiting for key id " | |
| 282 << base::HexEncode(&key_id[0], key_id.size()); | |
| 283 return; | |
| 284 } | |
| 285 | |
| 286 // If we do have the clear key, decrypt the pending buffer | |
| 287 // and reset the decryption context (not needed anymore). | |
| 288 crypto::SymmetricKey* key = decrypt_context->GetKey(); | |
| 289 if (key != NULL) { | |
| 290 pending_buffer_ = DecryptDecoderBuffer(pending_buffer_, key); | |
| 291 decrypt_context = scoped_refptr<DecryptContext>(); | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 MediaComponentDevice::FrameStatus status = media_component_device_->PushFrame( | |
| 296 decrypt_context, | |
| 297 pending_buffer_, | |
| 298 base::Bind(&AvPipelineImpl::OnFramePushed, weak_this_)); | |
| 299 pending_buffer_ = scoped_refptr<DecoderBufferBase>(); | |
| 300 | |
| 301 pending_push_ = (status == MediaComponentDevice::kFramePending); | |
| 302 if (!pending_push_) | |
| 303 OnFramePushed(status); | |
| 304 } | |
| 305 | |
| 306 void AvPipelineImpl::OnFramePushed(MediaComponentDevice::FrameStatus status) { | |
| 307 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 308 pending_push_ = false; | |
| 309 if (status == MediaComponentDevice::kFrameFailed) { | |
| 310 LOG(WARNING) << "AvPipelineImpl: PushFrame failed"; | |
| 311 enable_feeding_ = false; | |
| 312 state_ = kError; | |
| 313 return; | |
| 314 } | |
| 315 base::MessageLoopProxy::current()->PostTask( | |
| 316 FROM_HERE, | |
| 317 base::Bind(&AvPipelineImpl::ProcessPendingBuffer, weak_this_)); | |
| 318 } | |
| 319 | |
| 320 void AvPipelineImpl::OnCdmStateChanged() { | |
| 321 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 322 | |
| 323 // Update the buffering state if needed. | |
| 324 if (buffering_state_.get()) | |
| 325 UpdatePlayableFrames(); | |
| 326 | |
| 327 // Process the pending buffer in case the CDM now has the frame key id. | |
| 328 ProcessPendingBuffer(); | |
| 329 } | |
| 330 | |
| 331 void AvPipelineImpl::OnCdmDestroyed() { | |
| 332 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 333 media_keys_ = NULL; | |
| 334 } | |
| 335 | |
| 336 void AvPipelineImpl::OnFrameBuffered( | |
| 337 const scoped_refptr<DecoderBufferBase>& buffer, | |
| 338 bool is_at_max_capacity) { | |
| 339 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 340 | |
| 341 if (!buffering_state_.get()) | |
| 342 return; | |
| 343 | |
| 344 if (!buffer->end_of_stream() && | |
| 345 (buffered_time_ == ::media::kNoTimestamp() || | |
| 346 buffered_time_ < buffer->timestamp())) { | |
| 347 buffered_time_ = buffer->timestamp(); | |
| 348 } | |
| 349 | |
| 350 if (is_at_max_capacity) | |
| 351 buffering_state_->NotifyMaxCapacity(buffered_time_); | |
| 352 | |
| 353 // No need to update the list of playable frames, | |
| 354 // if we are already blocking on a frame. | |
| 355 bool update_playable_frames = non_playable_frames_.empty(); | |
| 356 non_playable_frames_.push_back(buffer); | |
| 357 if (update_playable_frames) | |
| 358 UpdatePlayableFrames(); | |
| 359 } | |
| 360 | |
| 361 void AvPipelineImpl::UpdatePlayableFrames() { | |
| 362 while (!non_playable_frames_.empty()) { | |
| 363 const scoped_refptr<DecoderBufferBase>& non_playable_frame = | |
| 364 non_playable_frames_.front(); | |
| 365 | |
| 366 if (non_playable_frame->end_of_stream()) { | |
| 367 buffering_state_->NotifyEos(); | |
| 368 } else { | |
| 369 const ::media::DecryptConfig* decrypt_config = | |
| 370 non_playable_frame->decrypt_config(); | |
| 371 if (decrypt_config && | |
| 372 !(media_keys_ && | |
| 373 media_keys_->GetDecryptContext(decrypt_config->key_id()).get())) { | |
| 374 // The frame is still not playable. All the following are thus not | |
| 375 // playable. | |
| 376 break; | |
| 377 } | |
| 378 | |
| 379 if (playable_buffered_time_ == ::media::kNoTimestamp() || | |
| 380 playable_buffered_time_ < non_playable_frame->timestamp()) { | |
| 381 playable_buffered_time_ = non_playable_frame->timestamp(); | |
| 382 buffering_state_->SetBufferedTime(playable_buffered_time_); | |
| 383 } | |
| 384 } | |
| 385 | |
| 386 // The frame is playable: remove it from the list of non playable frames. | |
| 387 non_playable_frames_.pop_front(); | |
| 388 } | |
| 389 } | |
| 390 | |
| 391 } // namespace media | |
| 392 } // namespace chromecast | |
| OLD | NEW |