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