Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(258)

Side by Side Diff: chromecast/media/cma/pipeline/av_pipeline_impl.cc

Issue 974623002: Chromecast: mitigate threading issue between AvPipeline/MediaKeys. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixes some bugs Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chromecast/media/cma/pipeline/av_pipeline_impl.h" 5 #include "chromecast/media/cma/pipeline/av_pipeline_impl.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/location.h" 8 #include "base/location.h"
9 #include "base/message_loop/message_loop_proxy.h" 9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/strings/string_number_conversions.h" 10 #include "base/strings/string_number_conversions.h"
(...skipping 27 matching lines...) Expand all
38 media_component_device_(media_component_device), 38 media_component_device_(media_component_device),
39 state_(kUninitialized), 39 state_(kUninitialized),
40 buffered_time_(::media::kNoTimestamp()), 40 buffered_time_(::media::kNoTimestamp()),
41 playable_buffered_time_(::media::kNoTimestamp()), 41 playable_buffered_time_(::media::kNoTimestamp()),
42 enable_feeding_(false), 42 enable_feeding_(false),
43 pending_read_(false), 43 pending_read_(false),
44 pending_push_(false), 44 pending_push_(false),
45 enable_time_update_(false), 45 enable_time_update_(false),
46 pending_time_update_task_(false), 46 pending_time_update_task_(false),
47 media_keys_(NULL), 47 media_keys_(NULL),
48 media_keys_callback_id_(kNoCallbackId), 48 media_keys_callback_id_(kNoCallbackId) {
49 weak_factory_(this) {
50 DCHECK(media_component_device); 49 DCHECK(media_component_device);
51 weak_this_ = weak_factory_.GetWeakPtr();
52 thread_checker_.DetachFromThread(); 50 thread_checker_.DetachFromThread();
53 } 51 }
54 52
55 AvPipelineImpl::~AvPipelineImpl() { 53 AvPipelineImpl::~AvPipelineImpl() {
56 // If there are weak pointers in the wild, they must be invalidated 54 // If there are weak pointers in the wild, they must be invalidated
57 // on the right thread. 55 // on the right thread.
58 DCHECK(thread_checker_.CalledOnValidThread()); 56 DCHECK(thread_checker_.CalledOnValidThread());
59 media_component_device_->SetClient(MediaComponentDevice::Client()); 57 media_component_device_->SetClient(MediaComponentDevice::Client());
60 58
61 if (media_keys_ && media_keys_callback_id_ != kNoCallbackId) 59 {
62 media_keys_->UnregisterPlayer(media_keys_callback_id_); 60 base::AutoLock lock(media_keys_lock_);
61 if (media_keys_ && media_keys_callback_id_ != kNoCallbackId)
62 media_keys_->UnregisterPlayer(media_keys_callback_id_);
63 }
63 } 64 }
64 65
65 void AvPipelineImpl::TransitionToState(State state) { 66 void AvPipelineImpl::TransitionToState(State state) {
66 DCHECK(thread_checker_.CalledOnValidThread()); 67 DCHECK(thread_checker_.CalledOnValidThread());
67 state_ = state; 68 state_ = state;
68 } 69 }
69 70
70 void AvPipelineImpl::SetCodedFrameProvider( 71 void AvPipelineImpl::SetCodedFrameProvider(
71 scoped_ptr<CodedFrameProvider> frame_provider, 72 scoped_ptr<CodedFrameProvider> frame_provider,
72 size_t max_buffer_size, 73 size_t max_buffer_size,
73 size_t max_frame_size) { 74 size_t max_frame_size) {
74 DCHECK_EQ(state_, kUninitialized); 75 DCHECK_EQ(state_, kUninitialized);
75 DCHECK(frame_provider); 76 DCHECK(frame_provider);
76 77
77 // Wrap the incoming frame provider to add some buffering capabilities. 78 // Wrap the incoming frame provider to add some buffering capabilities.
78 frame_provider_.reset( 79 frame_provider_.reset(
79 new BufferingFrameProvider( 80 new BufferingFrameProvider(
80 frame_provider.Pass(), 81 frame_provider.Pass(),
81 max_buffer_size, 82 max_buffer_size,
82 max_frame_size, 83 max_frame_size,
83 base::Bind(&AvPipelineImpl::OnFrameBuffered, weak_this_))); 84 base::Bind(&AvPipelineImpl::OnFrameBuffered, this)));
84 } 85 }
85 86
86 void AvPipelineImpl::SetClient(const AvPipelineClient& client) { 87 void AvPipelineImpl::SetClient(const AvPipelineClient& client) {
87 DCHECK(thread_checker_.CalledOnValidThread()); 88 DCHECK(thread_checker_.CalledOnValidThread());
88 DCHECK_EQ(state_, kUninitialized); 89 DCHECK_EQ(state_, kUninitialized);
89 client_ = client; 90 client_ = client;
90 } 91 }
91 92
92 bool AvPipelineImpl::Initialize() { 93 bool AvPipelineImpl::Initialize() {
93 DCHECK(thread_checker_.CalledOnValidThread()); 94 DCHECK(thread_checker_.CalledOnValidThread());
94 DCHECK_EQ(state_, kUninitialized); 95 DCHECK_EQ(state_, kUninitialized);
95 96
96 MediaComponentDevice::Client client; 97 MediaComponentDevice::Client client;
97 client.eos_cb = base::Bind(&AvPipelineImpl::OnEos, weak_this_); 98 client.eos_cb = base::Bind(&AvPipelineImpl::OnEos, this);
98 media_component_device_->SetClient(client); 99 media_component_device_->SetClient(client);
99 if (!media_component_device_->SetState(MediaComponentDevice::kStateIdle)) 100 if (!media_component_device_->SetState(MediaComponentDevice::kStateIdle))
100 return false; 101 return false;
101 102
102 return true; 103 return true;
103 } 104 }
104 105
105 bool AvPipelineImpl::StartPlayingFrom( 106 bool AvPipelineImpl::StartPlayingFrom(
106 base::TimeDelta time, 107 base::TimeDelta time,
107 const scoped_refptr<BufferingState>& buffering_state) { 108 const scoped_refptr<BufferingState>& buffering_state) {
(...skipping 13 matching lines...) Expand all
121 if (buffering_state_.get()) 122 if (buffering_state_.get())
122 buffering_state_->SetMediaTime(time); 123 buffering_state_->SetMediaTime(time);
123 124
124 if (!media_component_device_->SetState(MediaComponentDevice::kStateRunning)) 125 if (!media_component_device_->SetState(MediaComponentDevice::kStateRunning))
125 return false; 126 return false;
126 127
127 // Start feeding the pipeline. 128 // Start feeding the pipeline.
128 enable_feeding_ = true; 129 enable_feeding_ = true;
129 base::MessageLoopProxy::current()->PostTask( 130 base::MessageLoopProxy::current()->PostTask(
130 FROM_HERE, 131 FROM_HERE,
131 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); 132 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, this));
132 133
133 return true; 134 return true;
134 } 135 }
135 136
136 void AvPipelineImpl::Flush(const base::Closure& done_cb) { 137 void AvPipelineImpl::Flush(const base::Closure& done_cb) {
137 DCHECK(thread_checker_.CalledOnValidThread()); 138 DCHECK(thread_checker_.CalledOnValidThread());
138 DCHECK_EQ(state_, kFlushing); 139 DCHECK_EQ(state_, kFlushing);
139 DCHECK_EQ( 140 DCHECK_EQ(
140 media_component_device_->GetState(), MediaComponentDevice::kStateRunning); 141 media_component_device_->GetState(), MediaComponentDevice::kStateRunning);
141 // Note: returning to idle state aborts any pending frame push. 142 // Note: returning to idle state aborts any pending frame push.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 if (media_component_device_->GetState() == MediaComponentDevice::kStateIdle) { 177 if (media_component_device_->GetState() == MediaComponentDevice::kStateIdle) {
177 media_component_device_->SetState( 178 media_component_device_->SetState(
178 MediaComponentDevice::kStateUninitialized); 179 MediaComponentDevice::kStateUninitialized);
179 } 180 }
180 } 181 }
181 182
182 void AvPipelineImpl::SetCdm(BrowserCdmCast* media_keys) { 183 void AvPipelineImpl::SetCdm(BrowserCdmCast* media_keys) {
183 DCHECK(thread_checker_.CalledOnValidThread()); 184 DCHECK(thread_checker_.CalledOnValidThread());
184 DCHECK(media_keys); 185 DCHECK(media_keys);
185 186
186 if (media_keys_ && media_keys_callback_id_ != kNoCallbackId) 187 {
187 media_keys_->UnregisterPlayer(media_keys_callback_id_); 188 base::AutoLock lock(media_keys_lock_);
189 if (media_keys_ && media_keys_callback_id_ != kNoCallbackId)
190 media_keys_->UnregisterPlayer(media_keys_callback_id_);
188 191
189 media_keys_ = media_keys; 192 media_keys_ = media_keys;
190 media_keys_callback_id_ = media_keys_->RegisterPlayer( 193 media_keys_callback_id_ = media_keys_->RegisterPlayer(
191 ::media::BindToCurrentLoop( 194 ::media::BindToCurrentLoop(
192 base::Bind(&AvPipelineImpl::OnCdmStateChanged, weak_this_)), 195 base::Bind(&AvPipelineImpl::OnCdmStateChanged, this)),
193 ::media::BindToCurrentLoop( 196 // Note: this callback gets invoked in ~BrowserCdmCast. Posting
194 base::Bind(&AvPipelineImpl::OnCdmDestroyed, weak_this_))); 197 // OnCdmDestroyed to the media thread results in a race condition
198 // with media_keys_ accesses. This callback must run synchronously,
199 // otherwise media_keys_ access might occur after it is deleted.
200 base::Bind(&AvPipelineImpl::OnCdmDestroyed, this));
201 }
195 } 202 }
196 203
197 void AvPipelineImpl::OnEos() { 204 void AvPipelineImpl::OnEos() {
198 DCHECK(thread_checker_.CalledOnValidThread()); 205 DCHECK(thread_checker_.CalledOnValidThread());
199 CMALOG(kLogControl) << __FUNCTION__; 206 CMALOG(kLogControl) << __FUNCTION__;
200 if (state_ != kPlaying) 207 if (state_ != kPlaying)
201 return; 208 return;
202 209
203 if (!client_.eos_cb.is_null()) 210 if (!client_.eos_cb.is_null())
204 client_.eos_cb.Run(); 211 client_.eos_cb.Run();
205 } 212 }
206 213
207 void AvPipelineImpl::FetchBufferIfNeeded() { 214 void AvPipelineImpl::FetchBufferIfNeeded() {
208 DCHECK(thread_checker_.CalledOnValidThread()); 215 DCHECK(thread_checker_.CalledOnValidThread());
209 if (!enable_feeding_) 216 if (!enable_feeding_)
210 return; 217 return;
211 218
212 if (pending_read_ || pending_buffer_.get()) 219 if (pending_read_ || pending_buffer_.get())
213 return; 220 return;
214 221
215 pending_read_ = true; 222 pending_read_ = true;
216 frame_provider_->Read( 223 frame_provider_->Read(
217 base::Bind(&AvPipelineImpl::OnNewFrame, weak_this_)); 224 base::Bind(&AvPipelineImpl::OnNewFrame, this));
218 } 225 }
219 226
220 void AvPipelineImpl::OnNewFrame( 227 void AvPipelineImpl::OnNewFrame(
221 const scoped_refptr<DecoderBufferBase>& buffer, 228 const scoped_refptr<DecoderBufferBase>& buffer,
222 const ::media::AudioDecoderConfig& audio_config, 229 const ::media::AudioDecoderConfig& audio_config,
223 const ::media::VideoDecoderConfig& video_config) { 230 const ::media::VideoDecoderConfig& video_config) {
224 DCHECK(thread_checker_.CalledOnValidThread()); 231 DCHECK(thread_checker_.CalledOnValidThread());
225 pending_read_ = false; 232 pending_read_ = false;
226 233
227 if (audio_config.IsValidConfig() || video_config.IsValidConfig()) 234 if (audio_config.IsValidConfig() || video_config.IsValidConfig())
228 update_config_cb_.Run(audio_config, video_config); 235 update_config_cb_.Run(audio_config, video_config);
229 236
230 pending_buffer_ = buffer; 237 pending_buffer_ = buffer;
231 ProcessPendingBuffer(); 238 ProcessPendingBuffer();
232 239
233 base::MessageLoopProxy::current()->PostTask( 240 base::MessageLoopProxy::current()->PostTask(
234 FROM_HERE, 241 FROM_HERE,
235 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); 242 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, this));
236 } 243 }
237 244
238 void AvPipelineImpl::ProcessPendingBuffer() { 245 void AvPipelineImpl::ProcessPendingBuffer() {
239 if (!enable_feeding_) 246 if (!enable_feeding_)
240 return; 247 return;
241 248
242 // Initiate a read if there isn't already one. 249 // Initiate a read if there isn't already one.
243 if (!pending_buffer_.get() && !pending_read_) { 250 if (!pending_buffer_.get() && !pending_read_) {
244 base::MessageLoopProxy::current()->PostTask( 251 base::MessageLoopProxy::current()->PostTask(
245 FROM_HERE, 252 FROM_HERE,
246 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, weak_this_)); 253 base::Bind(&AvPipelineImpl::FetchBufferIfNeeded, this));
247 return; 254 return;
248 } 255 }
249 256
250 if (!pending_buffer_.get() || pending_push_) 257 if (!pending_buffer_.get() || pending_push_)
251 return; 258 return;
252 259
253 // Break the feeding loop when the end of stream is reached. 260 // Break the feeding loop when the end of stream is reached.
254 if (pending_buffer_->end_of_stream()) { 261 if (pending_buffer_->end_of_stream()) {
255 CMALOG(kLogControl) << __FUNCTION__ << ": EOS reached, stopped feeding"; 262 CMALOG(kLogControl) << __FUNCTION__ << ": EOS reached, stopped feeding";
256 enable_feeding_ = false; 263 enable_feeding_ = false;
257 } 264 }
258 265
259 scoped_refptr<DecryptContext> decrypt_context; 266 scoped_refptr<DecryptContext> decrypt_context;
260 if (!pending_buffer_->end_of_stream() && 267 if (!pending_buffer_->end_of_stream() &&
261 pending_buffer_->decrypt_config()) { 268 pending_buffer_->decrypt_config()) {
262 // Verify that CDM has the key ID. 269 // Verify that CDM has the key ID.
263 // Should not send the frame if the key ID is not available yet. 270 // Should not send the frame if the key ID is not available yet.
264 std::string key_id(pending_buffer_->decrypt_config()->key_id()); 271 std::string key_id(pending_buffer_->decrypt_config()->key_id());
265 if (!media_keys_) { 272 {
266 CMALOG(kLogControl) << "No CDM for frame: pts=" 273 base::AutoLock lock(media_keys_lock_);
267 << pending_buffer_->timestamp().InMilliseconds(); 274 if (!media_keys_) {
268 return; 275 CMALOG(kLogControl) << "No CDM for frame: pts="
276 << pending_buffer_->timestamp().InMilliseconds();
277 return;
278 }
279 decrypt_context = media_keys_->GetDecryptContext(key_id);
269 } 280 }
270 decrypt_context = media_keys_->GetDecryptContext(key_id);
271 if (!decrypt_context.get()) { 281 if (!decrypt_context.get()) {
272 CMALOG(kLogControl) << "frame(pts=" 282 CMALOG(kLogControl) << "frame(pts="
273 << pending_buffer_->timestamp().InMilliseconds() 283 << pending_buffer_->timestamp().InMilliseconds()
274 << "): waiting for key id " 284 << "): waiting for key id "
275 << base::HexEncode(&key_id[0], key_id.size()); 285 << base::HexEncode(&key_id[0], key_id.size());
276 return; 286 return;
277 } 287 }
278 288
279 // If we do have the clear key, decrypt the pending buffer 289 // If we do have the clear key, decrypt the pending buffer
280 // and reset the decryption context (not needed anymore). 290 // and reset the decryption context (not needed anymore).
281 crypto::SymmetricKey* key = decrypt_context->GetKey(); 291 crypto::SymmetricKey* key = decrypt_context->GetKey();
282 if (key != NULL) { 292 if (key != NULL) {
283 pending_buffer_ = DecryptDecoderBuffer(pending_buffer_, key); 293 pending_buffer_ = DecryptDecoderBuffer(pending_buffer_, key);
284 decrypt_context = scoped_refptr<DecryptContext>(); 294 decrypt_context = scoped_refptr<DecryptContext>();
285 } 295 }
286 } 296 }
287 297
288 if (!pending_buffer_->end_of_stream() && buffering_state_.get()) { 298 if (!pending_buffer_->end_of_stream() && buffering_state_.get()) {
289 base::TimeDelta timestamp = pending_buffer_->timestamp(); 299 base::TimeDelta timestamp = pending_buffer_->timestamp();
290 if (timestamp != ::media::kNoTimestamp()) 300 if (timestamp != ::media::kNoTimestamp())
291 buffering_state_->SetMaxRenderingTime(timestamp); 301 buffering_state_->SetMaxRenderingTime(timestamp);
292 } 302 }
293 303
294 MediaComponentDevice::FrameStatus status = media_component_device_->PushFrame( 304 MediaComponentDevice::FrameStatus status = media_component_device_->PushFrame(
295 decrypt_context, 305 decrypt_context,
296 pending_buffer_, 306 pending_buffer_,
297 base::Bind(&AvPipelineImpl::OnFramePushed, weak_this_)); 307 base::Bind(&AvPipelineImpl::OnFramePushed, this));
298 pending_buffer_ = scoped_refptr<DecoderBufferBase>(); 308 pending_buffer_ = scoped_refptr<DecoderBufferBase>();
299 309
300 pending_push_ = (status == MediaComponentDevice::kFramePending); 310 pending_push_ = (status == MediaComponentDevice::kFramePending);
301 if (!pending_push_) 311 if (!pending_push_)
302 OnFramePushed(status); 312 OnFramePushed(status);
303 } 313 }
304 314
305 void AvPipelineImpl::OnFramePushed(MediaComponentDevice::FrameStatus status) { 315 void AvPipelineImpl::OnFramePushed(MediaComponentDevice::FrameStatus status) {
306 DCHECK(thread_checker_.CalledOnValidThread()); 316 DCHECK(thread_checker_.CalledOnValidThread());
307 pending_push_ = false; 317 pending_push_ = false;
308 if (status == MediaComponentDevice::kFrameFailed) { 318 if (status == MediaComponentDevice::kFrameFailed) {
309 LOG(WARNING) << "AvPipelineImpl: PushFrame failed"; 319 LOG(WARNING) << "AvPipelineImpl: PushFrame failed";
310 enable_feeding_ = false; 320 enable_feeding_ = false;
311 state_ = kError; 321 state_ = kError;
312 return; 322 return;
313 } 323 }
314 base::MessageLoopProxy::current()->PostTask( 324 base::MessageLoopProxy::current()->PostTask(
315 FROM_HERE, 325 FROM_HERE,
316 base::Bind(&AvPipelineImpl::ProcessPendingBuffer, weak_this_)); 326 base::Bind(&AvPipelineImpl::ProcessPendingBuffer, this));
317 } 327 }
318 328
319 void AvPipelineImpl::OnCdmStateChanged() { 329 void AvPipelineImpl::OnCdmStateChanged() {
320 DCHECK(thread_checker_.CalledOnValidThread()); 330 DCHECK(thread_checker_.CalledOnValidThread());
321 331
322 // Update the buffering state if needed. 332 // Update the buffering state if needed.
323 if (buffering_state_.get()) 333 if (buffering_state_.get())
324 UpdatePlayableFrames(); 334 UpdatePlayableFrames();
325 335
326 // Process the pending buffer in case the CDM now has the frame key id. 336 // Process the pending buffer in case the CDM now has the frame key id.
327 ProcessPendingBuffer(); 337 ProcessPendingBuffer();
328 } 338 }
329 339
330 void AvPipelineImpl::OnCdmDestroyed() { 340 void AvPipelineImpl::OnCdmDestroyed() {
331 DCHECK(thread_checker_.CalledOnValidThread()); 341 base::AutoLock lock(media_keys_lock_);
332 media_keys_ = NULL; 342 media_keys_ = NULL;
333 } 343 }
334 344
335 void AvPipelineImpl::OnFrameBuffered( 345 void AvPipelineImpl::OnFrameBuffered(
336 const scoped_refptr<DecoderBufferBase>& buffer, 346 const scoped_refptr<DecoderBufferBase>& buffer,
337 bool is_at_max_capacity) { 347 bool is_at_max_capacity) {
338 DCHECK(thread_checker_.CalledOnValidThread()); 348 DCHECK(thread_checker_.CalledOnValidThread());
339 349
340 if (!buffering_state_.get()) 350 if (!buffering_state_.get())
341 return; 351 return;
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
382 } 392 }
383 } 393 }
384 394
385 // The frame is playable: remove it from the list of non playable frames. 395 // The frame is playable: remove it from the list of non playable frames.
386 non_playable_frames_.pop_front(); 396 non_playable_frames_.pop_front();
387 } 397 }
388 } 398 }
389 399
390 } // namespace media 400 } // namespace media
391 } // namespace chromecast 401 } // namespace chromecast
OLDNEW
« no previous file with comments | « chromecast/media/cma/pipeline/av_pipeline_impl.h ('k') | chromecast/media/cma/pipeline/video_pipeline_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698