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

Side by Side Diff: media/renderers/renderer_impl.cc

Issue 2684103005: Allow media track switching. (Closed)
Patch Set: Updated log message Created 3 years, 8 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 "media/renderers/renderer_impl.h" 5 #include "media/renderers/renderer_impl.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback.h" 10 #include "base/callback.h"
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 }; 73 };
74 74
75 RendererImpl::RendererImpl( 75 RendererImpl::RendererImpl(
76 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 76 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
77 std::unique_ptr<AudioRenderer> audio_renderer, 77 std::unique_ptr<AudioRenderer> audio_renderer,
78 std::unique_ptr<VideoRenderer> video_renderer) 78 std::unique_ptr<VideoRenderer> video_renderer)
79 : state_(STATE_UNINITIALIZED), 79 : state_(STATE_UNINITIALIZED),
80 task_runner_(task_runner), 80 task_runner_(task_runner),
81 audio_renderer_(std::move(audio_renderer)), 81 audio_renderer_(std::move(audio_renderer)),
82 video_renderer_(std::move(video_renderer)), 82 video_renderer_(std::move(video_renderer)),
83 current_audio_stream_(nullptr),
84 current_video_stream_(nullptr),
83 time_source_(NULL), 85 time_source_(NULL),
84 time_ticking_(false), 86 time_ticking_(false),
85 playback_rate_(0.0), 87 playback_rate_(0.0),
86 audio_buffering_state_(BUFFERING_HAVE_NOTHING), 88 audio_buffering_state_(BUFFERING_HAVE_NOTHING),
87 video_buffering_state_(BUFFERING_HAVE_NOTHING), 89 video_buffering_state_(BUFFERING_HAVE_NOTHING),
88 audio_ended_(false), 90 audio_ended_(false),
89 video_ended_(false), 91 video_ended_(false),
90 cdm_context_(nullptr), 92 cdm_context_(nullptr),
91 underflow_disabled_for_testing_(false), 93 underflow_disabled_for_testing_(false),
92 clockless_video_playback_enabled_for_testing_(false), 94 clockless_video_playback_enabled_for_testing_(false),
(...skipping 26 matching lines...) Expand all
119 121
120 // Tear down in opposite order of construction as |video_renderer_| can still 122 // Tear down in opposite order of construction as |video_renderer_| can still
121 // need |time_source_| (which can be |audio_renderer_|) to be alive. 123 // need |time_source_| (which can be |audio_renderer_|) to be alive.
122 video_renderer_.reset(); 124 video_renderer_.reset();
123 audio_renderer_.reset(); 125 audio_renderer_.reset();
124 126
125 if (!init_cb_.is_null()) { 127 if (!init_cb_.is_null()) {
126 FinishInitialization(PIPELINE_ERROR_ABORT); 128 FinishInitialization(PIPELINE_ERROR_ABORT);
127 } else if (!flush_cb_.is_null()) { 129 } else if (!flush_cb_.is_null()) {
128 base::ResetAndReturn(&flush_cb_).Run(); 130 base::ResetAndReturn(&flush_cb_).Run();
131 } else if (pending_flush_cb_) {
132 base::ResetAndReturn(&pending_flush_cb_).Run();
129 } 133 }
130 } 134 }
131 135
132 void RendererImpl::Initialize(MediaResource* media_resource, 136 void RendererImpl::Initialize(MediaResource* media_resource,
133 RendererClient* client, 137 RendererClient* client,
134 const PipelineStatusCB& init_cb) { 138 const PipelineStatusCB& init_cb) {
135 DVLOG(1) << __func__; 139 DVLOG(1) << __func__;
136 DCHECK(task_runner_->BelongsToCurrentThread()); 140 DCHECK(task_runner_->BelongsToCurrentThread());
137 DCHECK_EQ(state_, STATE_UNINITIALIZED); 141 DCHECK_EQ(state_, STATE_UNINITIALIZED);
138 DCHECK(!init_cb.is_null()); 142 DCHECK(!init_cb.is_null());
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
182 void RendererImpl::Flush(const base::Closure& flush_cb) { 186 void RendererImpl::Flush(const base::Closure& flush_cb) {
183 DVLOG(1) << __func__; 187 DVLOG(1) << __func__;
184 DCHECK(task_runner_->BelongsToCurrentThread()); 188 DCHECK(task_runner_->BelongsToCurrentThread());
185 DCHECK(flush_cb_.is_null()); 189 DCHECK(flush_cb_.is_null());
186 190
187 if (state_ != STATE_PLAYING) { 191 if (state_ != STATE_PLAYING) {
188 DCHECK_EQ(state_, STATE_ERROR); 192 DCHECK_EQ(state_, STATE_ERROR);
189 return; 193 return;
190 } 194 }
191 195
196 // If we are currently handling a media stream status change, then postpone
197 // Flush until after that's done (because stream status changes also flush
198 // audio_renderer_/video_renderer_ and they need to be restarted before they
199 // can be flushed again). OnStreamRestartCompleted will resume Flush
200 // processing after audio/video restart has completed and there are no other
201 // pending stream status changes.
202 if (restarting_audio_ || restarting_video_) {
203 DCHECK(!pending_flush_cb_);
204 pending_flush_cb_ = flush_cb;
205 pending_actions_.push_back(
206 base::Bind(&RendererImpl::Flush, weak_this_, pending_flush_cb_));
207 return;
208 }
209
210 if (pending_flush_cb_) {
DaleCurtis 2017/04/03 19:17:59 Is this necessary? Is something calling with the s
servolk 2017/04/03 20:27:46 External clients aren't calling Flush twice. But w
DaleCurtis 2017/04/03 21:32:41 Ah, in that case I think this should call somethin
servolk 2017/04/03 22:38:33 Wait, I don't follow. Where would we set flush_cb_
DaleCurtis 2017/04/03 22:50:35 I was first thinking you'd post to a Flush that do
servolk 2017/04/04 00:04:13 What I think you missed here is the fact that only
211 DCHECK(flush_cb.Equals(pending_flush_cb_));
212 base::ResetAndReturn(&pending_flush_cb_);
213 }
214
192 flush_cb_ = flush_cb; 215 flush_cb_ = flush_cb;
193 state_ = STATE_FLUSHING; 216 state_ = STATE_FLUSHING;
194 217
195 if (time_ticking_) 218 if (time_ticking_)
196 PausePlayback(); 219 PausePlayback();
197 220
198 FlushAudioRenderer(); 221 FlushAudioRenderer();
199 } 222 }
200 223
201 void RendererImpl::StartPlayingFrom(base::TimeDelta time) { 224 void RendererImpl::StartPlayingFrom(base::TimeDelta time) {
(...skipping 14 matching lines...) Expand all
216 } 239 }
217 240
218 void RendererImpl::OnStreamStatusChanged(DemuxerStream* stream, 241 void RendererImpl::OnStreamStatusChanged(DemuxerStream* stream,
219 bool enabled, 242 bool enabled,
220 base::TimeDelta time) { 243 base::TimeDelta time) {
221 DCHECK(task_runner_->BelongsToCurrentThread()); 244 DCHECK(task_runner_->BelongsToCurrentThread());
222 DCHECK(stream); 245 DCHECK(stream);
223 bool video = (stream->type() == DemuxerStream::VIDEO); 246 bool video = (stream->type() == DemuxerStream::VIDEO);
224 DVLOG(1) << __func__ << (video ? " video" : " audio") << " stream=" << stream 247 DVLOG(1) << __func__ << (video ? " video" : " audio") << " stream=" << stream
225 << " enabled=" << enabled << " time=" << time.InSecondsF(); 248 << " enabled=" << enabled << " time=" << time.InSecondsF();
226 if ((state_ != STATE_PLAYING) || (audio_ended_ && video_ended_)) 249 if ((state_ != STATE_PLAYING && state_ != STATE_FLUSHING) ||
250 (audio_ended_ && video_ended_))
227 return; 251 return;
228 if (restarting_audio_ || restarting_video_) { 252 if (restarting_audio_ || restarting_video_ || flush_cb_) {
229 DVLOG(3) << __func__ << ": postponed stream " << stream 253 DVLOG(3) << __func__ << ": postponed stream " << stream
230 << " status change handling."; 254 << " status change handling.";
231 pending_stream_status_notifications_.push_back( 255 pending_actions_.push_back(base::Bind(&RendererImpl::OnStreamStatusChanged,
232 base::Bind(&RendererImpl::OnStreamStatusChanged, weak_this_, stream, 256 weak_this_, stream, enabled, time));
233 enabled, time));
234 return; 257 return;
235 } 258 }
236 if (stream->type() == DemuxerStream::VIDEO) { 259 if (stream->type() == DemuxerStream::VIDEO) {
237 DCHECK(video_renderer_); 260 DCHECK(video_renderer_);
238 restarting_video_ = true; 261 restarting_video_ = true;
239 video_renderer_->Flush( 262 video_renderer_->Flush(
240 base::Bind(&RendererImpl::RestartVideoRenderer, weak_this_, time)); 263 base::Bind(stream == current_video_stream_
264 ? &RendererImpl::RestartVideoRenderer
265 : &RendererImpl::ReinitializeVideoRenderer,
266 weak_this_, stream, time));
241 } else if (stream->type() == DemuxerStream::AUDIO) { 267 } else if (stream->type() == DemuxerStream::AUDIO) {
242 DCHECK(audio_renderer_); 268 DCHECK(audio_renderer_);
243 DCHECK(time_source_); 269 DCHECK(time_source_);
244 restarting_audio_ = true; 270 restarting_audio_ = true;
245 // Stop ticking (transition into paused state) in audio renderer before 271 // Stop ticking (transition into paused state) in audio renderer before
246 // calling Flush, since after Flush we are going to restart playback by 272 // calling Flush, since after Flush we are going to restart playback by
247 // calling audio renderer StartPlaying which would fail in playing state. 273 // calling audio renderer StartPlaying which would fail in playing state.
248 if (time_ticking_) { 274 if (time_ticking_) {
249 time_ticking_ = false; 275 time_ticking_ = false;
250 time_source_->StopTicking(); 276 time_source_->StopTicking();
251 } 277 }
252 audio_renderer_->Flush( 278 audio_renderer_->Flush(
253 base::Bind(&RendererImpl::RestartAudioRenderer, weak_this_, time)); 279 base::Bind(stream == current_audio_stream_
280 ? &RendererImpl::RestartAudioRenderer
281 : &RendererImpl::ReinitializeAudioRenderer,
282 weak_this_, stream, time));
254 } 283 }
255 } 284 }
256 285
257 void RendererImpl::RestartVideoRenderer(base::TimeDelta time) { 286 void RendererImpl::ReinitializeVideoRenderer(DemuxerStream* stream,
258 DVLOG(3) << __func__; 287 base::TimeDelta time) {
288 DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
289 DCHECK(task_runner_->BelongsToCurrentThread());
290 DCHECK_NE(stream, current_video_stream_);
291
292 current_video_stream_ = stream;
293 video_renderer_->OnTimeStopped();
294 video_renderer_->Initialize(
295 stream, cdm_context_, video_renderer_client_.get(),
296 base::Bind(&RendererImpl::GetWallClockTimes, base::Unretained(this)),
297 base::Bind(&RendererImpl::OnVideoRendererReinitialized, weak_this_,
298 stream, time));
299 }
300
301 void RendererImpl::OnVideoRendererReinitialized(DemuxerStream* stream,
302 base::TimeDelta time,
303 PipelineStatus status) {
304 DVLOG(2) << __func__ << ": status=" << status;
305 DCHECK_EQ(stream, current_video_stream_);
306
307 if (status != PIPELINE_OK) {
308 OnError(status);
309 return;
310 }
311 RestartVideoRenderer(stream, time);
312 }
313
314 void RendererImpl::RestartVideoRenderer(DemuxerStream* stream,
315 base::TimeDelta time) {
316 DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
259 DCHECK(task_runner_->BelongsToCurrentThread()); 317 DCHECK(task_runner_->BelongsToCurrentThread());
260 DCHECK(video_renderer_); 318 DCHECK(video_renderer_);
261 DCHECK_EQ(state_, STATE_PLAYING); 319 DCHECK(state_ == STATE_PLAYING || state_ == STATE_FLUSHING);
320 DCHECK_EQ(stream, current_video_stream_);
321
262 video_ended_ = false; 322 video_ended_ = false;
263 video_renderer_->StartPlayingFrom(time); 323 video_renderer_->StartPlayingFrom(time);
264 } 324 }
265 325
266 void RendererImpl::RestartAudioRenderer(base::TimeDelta time) { 326 void RendererImpl::ReinitializeAudioRenderer(DemuxerStream* stream,
267 DVLOG(3) << __func__; 327 base::TimeDelta time) {
328 DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
268 DCHECK(task_runner_->BelongsToCurrentThread()); 329 DCHECK(task_runner_->BelongsToCurrentThread());
269 DCHECK_EQ(state_, STATE_PLAYING); 330 DCHECK_NE(stream, current_audio_stream_);
331
332 current_audio_stream_ = stream;
333 audio_renderer_->Initialize(
334 stream, cdm_context_, audio_renderer_client_.get(),
335 base::Bind(&RendererImpl::OnAudioRendererReinitialized, weak_this_,
336 stream, time));
337 }
338
339 void RendererImpl::OnAudioRendererReinitialized(DemuxerStream* stream,
340 base::TimeDelta time,
341 PipelineStatus status) {
342 DVLOG(2) << __func__ << ": status=" << status;
343 DCHECK_EQ(stream, current_audio_stream_);
344
345 if (status != PIPELINE_OK) {
346 OnError(status);
347 return;
348 }
349 RestartAudioRenderer(stream, time);
350 }
351
352 void RendererImpl::RestartAudioRenderer(DemuxerStream* stream,
353 base::TimeDelta time) {
354 DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
355 DCHECK(task_runner_->BelongsToCurrentThread());
356 DCHECK(state_ == STATE_PLAYING || state_ == STATE_FLUSHING);
270 DCHECK(time_source_); 357 DCHECK(time_source_);
271 DCHECK(audio_renderer_); 358 DCHECK(audio_renderer_);
359 DCHECK_EQ(stream, current_audio_stream_);
360
272 audio_ended_ = false; 361 audio_ended_ = false;
273 audio_renderer_->StartPlaying(); 362 audio_renderer_->StartPlaying();
274 } 363 }
275 364
276 void RendererImpl::SetPlaybackRate(double playback_rate) { 365 void RendererImpl::SetPlaybackRate(double playback_rate) {
277 DVLOG(1) << __func__ << "(" << playback_rate << ")"; 366 DVLOG(1) << __func__ << "(" << playback_rate << ")";
278 DCHECK(task_runner_->BelongsToCurrentThread()); 367 DCHECK(task_runner_->BelongsToCurrentThread());
279 368
280 // Playback rate changes are only carried out while playing. 369 // Playback rate changes are only carried out while playing.
281 if (state_ != STATE_PLAYING) 370 if (state_ != STATE_PLAYING)
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
387 // TODO(servolk): Implement proper support for multiple streams. But for now 476 // TODO(servolk): Implement proper support for multiple streams. But for now
388 // pick the first enabled stream to preserve the existing behavior. 477 // pick the first enabled stream to preserve the existing behavior.
389 DemuxerStream* audio_stream = 478 DemuxerStream* audio_stream =
390 media_resource_->GetFirstStream(DemuxerStream::AUDIO); 479 media_resource_->GetFirstStream(DemuxerStream::AUDIO);
391 if (!audio_stream) { 480 if (!audio_stream) {
392 audio_renderer_.reset(); 481 audio_renderer_.reset();
393 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK)); 482 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
394 return; 483 return;
395 } 484 }
396 485
486 current_audio_stream_ = audio_stream;
487
397 audio_renderer_client_.reset( 488 audio_renderer_client_.reset(
398 new RendererClientInternal(DemuxerStream::AUDIO, this)); 489 new RendererClientInternal(DemuxerStream::AUDIO, this));
399 // Note: After the initialization of a renderer, error events from it may 490 // Note: After the initialization of a renderer, error events from it may
400 // happen at any time and all future calls must guard against STATE_ERROR. 491 // happen at any time and all future calls must guard against STATE_ERROR.
401 audio_renderer_->Initialize(audio_stream, cdm_context_, 492 audio_renderer_->Initialize(audio_stream, cdm_context_,
402 audio_renderer_client_.get(), done_cb); 493 audio_renderer_client_.get(), done_cb);
403 } 494 }
404 495
405 void RendererImpl::OnAudioRendererInitializeDone(PipelineStatus status) { 496 void RendererImpl::OnAudioRendererInitializeDone(PipelineStatus status) {
406 DVLOG(1) << __func__ << ": " << status; 497 DVLOG(1) << __func__ << ": " << status;
(...skipping 28 matching lines...) Expand all
435 // TODO(servolk): Implement proper support for multiple streams. But for now 526 // TODO(servolk): Implement proper support for multiple streams. But for now
436 // pick the first enabled stream to preserve the existing behavior. 527 // pick the first enabled stream to preserve the existing behavior.
437 DemuxerStream* video_stream = 528 DemuxerStream* video_stream =
438 media_resource_->GetFirstStream(DemuxerStream::VIDEO); 529 media_resource_->GetFirstStream(DemuxerStream::VIDEO);
439 if (!video_stream) { 530 if (!video_stream) {
440 video_renderer_.reset(); 531 video_renderer_.reset();
441 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK)); 532 task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
442 return; 533 return;
443 } 534 }
444 535
536 current_video_stream_ = video_stream;
537
445 video_renderer_client_.reset( 538 video_renderer_client_.reset(
446 new RendererClientInternal(DemuxerStream::VIDEO, this)); 539 new RendererClientInternal(DemuxerStream::VIDEO, this));
447 video_renderer_->Initialize( 540 video_renderer_->Initialize(
448 video_stream, cdm_context_, video_renderer_client_.get(), 541 video_stream, cdm_context_, video_renderer_client_.get(),
449 base::Bind(&RendererImpl::GetWallClockTimes, base::Unretained(this)), 542 base::Bind(&RendererImpl::GetWallClockTimes, base::Unretained(this)),
450 done_cb); 543 done_cb);
451 } 544 }
452 545
453 void RendererImpl::OnVideoRendererInitializeDone(PipelineStatus status) { 546 void RendererImpl::OnVideoRendererInitializeDone(PipelineStatus status) {
454 DVLOG(1) << __func__ << ": " << status; 547 DVLOG(1) << __func__ << ": " << status;
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
547 return; 640 return;
548 } 641 }
549 642
550 DCHECK_EQ(state_, STATE_FLUSHING); 643 DCHECK_EQ(state_, STATE_FLUSHING);
551 DCHECK(!flush_cb_.is_null()); 644 DCHECK(!flush_cb_.is_null());
552 645
553 DCHECK_EQ(video_buffering_state_, BUFFERING_HAVE_NOTHING); 646 DCHECK_EQ(video_buffering_state_, BUFFERING_HAVE_NOTHING);
554 video_ended_ = false; 647 video_ended_ = false;
555 state_ = STATE_PLAYING; 648 state_ = STATE_PLAYING;
556 base::ResetAndReturn(&flush_cb_).Run(); 649 base::ResetAndReturn(&flush_cb_).Run();
650
651 if (!pending_actions_.empty()) {
652 base::Closure closure = pending_actions_.front();
653 pending_actions_.pop_front();
654 closure.Run();
655 }
557 } 656 }
558 657
559 void RendererImpl::OnStatisticsUpdate(const PipelineStatistics& stats) { 658 void RendererImpl::OnStatisticsUpdate(const PipelineStatistics& stats) {
560 DCHECK(task_runner_->BelongsToCurrentThread()); 659 DCHECK(task_runner_->BelongsToCurrentThread());
561 client_->OnStatisticsUpdate(stats); 660 client_->OnStatisticsUpdate(stats);
562 } 661 }
563 662
564 bool RendererImpl::HandleRestartedStreamBufferingChanges( 663 bool RendererImpl::HandleRestartedStreamBufferingChanges(
565 DemuxerStream::Type type, 664 DemuxerStream::Type type,
566 BufferingState new_buffering_state) { 665 BufferingState new_buffering_state) {
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
621 PausePlayback(); 720 PausePlayback();
622 task_runner_->PostTask( 721 task_runner_->PostTask(
623 FROM_HERE, 722 FROM_HERE,
624 base::Bind(&RendererImpl::OnStreamRestartCompleted, weak_this_)); 723 base::Bind(&RendererImpl::OnStreamRestartCompleted, weak_this_));
625 } 724 }
626 } 725 }
627 return false; 726 return false;
628 } 727 }
629 728
630 void RendererImpl::OnStreamRestartCompleted() { 729 void RendererImpl::OnStreamRestartCompleted() {
730 DVLOG(3) << __func__ << " restarting_audio_=" << restarting_audio_
731 << " restarting_video_=" << restarting_video_;
631 DCHECK(restarting_audio_ || restarting_video_); 732 DCHECK(restarting_audio_ || restarting_video_);
632 restarting_audio_ = false; 733 restarting_audio_ = false;
633 restarting_video_ = false; 734 restarting_video_ = false;
634 if (!pending_stream_status_notifications_.empty()) { 735 if (!pending_actions_.empty()) {
635 pending_stream_status_notifications_.front().Run(); 736 base::Closure closure = pending_actions_.front();
636 pending_stream_status_notifications_.pop_front(); 737 pending_actions_.pop_front();
738 closure.Run();
637 } 739 }
638 } 740 }
639 741
640 void RendererImpl::OnBufferingStateChange(DemuxerStream::Type type, 742 void RendererImpl::OnBufferingStateChange(DemuxerStream::Type type,
641 BufferingState new_buffering_state) { 743 BufferingState new_buffering_state) {
642 DCHECK((type == DemuxerStream::AUDIO) || (type == DemuxerStream::VIDEO)); 744 DCHECK((type == DemuxerStream::AUDIO) || (type == DemuxerStream::VIDEO));
643 BufferingState* buffering_state = type == DemuxerStream::AUDIO 745 BufferingState* buffering_state = type == DemuxerStream::AUDIO
644 ? &audio_buffering_state_ 746 ? &audio_buffering_state_
645 : &video_buffering_state_; 747 : &video_buffering_state_;
646 748
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
849 DCHECK(task_runner_->BelongsToCurrentThread()); 951 DCHECK(task_runner_->BelongsToCurrentThread());
850 client_->OnVideoNaturalSizeChange(size); 952 client_->OnVideoNaturalSizeChange(size);
851 } 953 }
852 954
853 void RendererImpl::OnVideoOpacityChange(bool opaque) { 955 void RendererImpl::OnVideoOpacityChange(bool opaque) {
854 DCHECK(task_runner_->BelongsToCurrentThread()); 956 DCHECK(task_runner_->BelongsToCurrentThread());
855 client_->OnVideoOpacityChange(opaque); 957 client_->OnVideoOpacityChange(opaque);
856 } 958 }
857 959
858 } // namespace media 960 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698