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

Side by Side Diff: chromecast/renderer/media/cma_renderer.cc

Issue 2300993003: CmaRenderer is dead. Long live MojoRenderer. (Closed)
Patch Set: update OWNERS file Created 4 years, 3 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
(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/renderer/media/cma_renderer.h"
6
7 #include <algorithm>
8 #include <utility>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/location.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/threading/thread_task_runner_handle.h"
16 #include "chromecast/media/cma/base/balanced_media_task_runner_factory.h"
17 #include "chromecast/media/cma/base/cma_logging.h"
18 #include "chromecast/media/cma/base/demuxer_stream_adapter.h"
19 #include "chromecast/media/cma/pipeline/av_pipeline_client.h"
20 #include "chromecast/media/cma/pipeline/media_pipeline_client.h"
21 #include "chromecast/media/cma/pipeline/video_pipeline_client.h"
22 #include "chromecast/renderer/media/audio_pipeline_proxy.h"
23 #include "chromecast/renderer/media/media_pipeline_proxy.h"
24 #include "chromecast/renderer/media/video_pipeline_proxy.h"
25 #include "media/base/bind_to_current_loop.h"
26 #include "media/base/demuxer_stream_provider.h"
27 #include "media/base/pipeline_status.h"
28 #include "media/base/renderer_client.h"
29 #include "media/base/time_delta_interpolator.h"
30 #include "media/base/video_renderer_sink.h"
31 #include "media/renderers/video_overlay_factory.h"
32 #include "ui/gfx/geometry/size.h"
33
34 namespace chromecast {
35 namespace media {
36
37 namespace {
38
39 // Maximum difference between audio frame PTS and video frame PTS
40 // for frames read from the DemuxerStream.
41 const base::TimeDelta kMaxDeltaFetcher(base::TimeDelta::FromMilliseconds(2000));
42
43 void MediaPipelineClientDummyCallback() {
44 }
45
46 } // namespace
47
48 CmaRenderer::CmaRenderer(std::unique_ptr<MediaPipelineProxy> media_pipeline,
49 ::media::VideoRendererSink* video_renderer_sink,
50 ::media::GpuVideoAcceleratorFactories* gpu_factories)
51 : media_task_runner_factory_(
52 new BalancedMediaTaskRunnerFactory(kMaxDeltaFetcher)),
53 media_pipeline_(std::move(media_pipeline)),
54 audio_pipeline_(media_pipeline_->GetAudioPipeline()),
55 video_pipeline_(media_pipeline_->GetVideoPipeline()),
56 video_renderer_sink_(video_renderer_sink),
57 state_(kUninitialized),
58 is_pending_transition_(false),
59 has_audio_(false),
60 has_video_(false),
61 received_audio_eos_(false),
62 received_video_eos_(false),
63 gpu_factories_(gpu_factories),
64 time_interpolator_(
65 new ::media::TimeDeltaInterpolator(&default_tick_clock_)),
66 playback_rate_(1.0),
67 weak_factory_(this) {
68 weak_this_ = weak_factory_.GetWeakPtr();
69 thread_checker_.DetachFromThread();
70
71 time_interpolator_->SetUpperBound(base::TimeDelta());
72 }
73
74 CmaRenderer::~CmaRenderer() {
75 DCHECK(thread_checker_.CalledOnValidThread());
76 if (!init_cb_.is_null())
77 base::ResetAndReturn(&init_cb_).Run(::media::PIPELINE_ERROR_ABORT);
78 else if (!flush_cb_.is_null())
79 base::ResetAndReturn(&flush_cb_).Run();
80
81 if (has_audio_ || has_video_)
82 media_pipeline_->Stop();
83 }
84
85 void CmaRenderer::Initialize(
86 ::media::DemuxerStreamProvider* demuxer_stream_provider,
87 ::media::RendererClient* client,
88 const ::media::PipelineStatusCB& init_cb) {
89 CMALOG(kLogControl) << __FUNCTION__;
90 DCHECK(thread_checker_.CalledOnValidThread());
91 DCHECK_EQ(state_, kUninitialized) << state_;
92 DCHECK(!init_cb.is_null());
93 DCHECK(demuxer_stream_provider->GetStream(::media::DemuxerStream::AUDIO) ||
94 demuxer_stream_provider->GetStream(::media::DemuxerStream::VIDEO));
95
96 // Deferred from ctor so as to initialise on correct thread.
97 video_overlay_factory_.reset(
98 new ::media::VideoOverlayFactory(gpu_factories_));
99
100 BeginStateTransition();
101
102 demuxer_stream_provider_ = demuxer_stream_provider;
103 client_ = client;
104
105 MediaPipelineClient media_pipeline_client;
106 media_pipeline_client.error_cb =
107 ::media::BindToCurrentLoop(base::Bind(&CmaRenderer::OnError, weak_this_));
108 media_pipeline_client.buffering_state_cb = ::media::BindToCurrentLoop(
109 base::Bind(&CmaRenderer::OnBufferingNotification, weak_this_));
110 media_pipeline_client.time_update_cb = ::media::BindToCurrentLoop(
111 base::Bind(&CmaRenderer::OnPlaybackTimeUpdated, weak_this_));
112 media_pipeline_client.pipeline_backend_created_cb =
113 base::Bind(&MediaPipelineClientDummyCallback);
114 media_pipeline_client.pipeline_backend_destroyed_cb =
115 base::Bind(&MediaPipelineClientDummyCallback);
116 media_pipeline_->SetClient(media_pipeline_client);
117
118 init_cb_ = init_cb;
119 InitializeAudioPipeline();
120 }
121
122 void CmaRenderer::Flush(const base::Closure& flush_cb) {
123 CMALOG(kLogControl) << __FUNCTION__;
124 DCHECK(thread_checker_.CalledOnValidThread());
125 BeginStateTransition();
126
127 DCHECK(flush_cb_.is_null());
128 flush_cb_ = flush_cb;
129
130 if (state_ == kError) {
131 OnError(::media::PIPELINE_ERROR_ABORT);
132 return;
133 }
134
135 DCHECK_EQ(state_, kPlaying) << state_;
136 media_pipeline_->Flush(::media::BindToCurrentLoop(
137 base::Bind(&CmaRenderer::OnFlushDone, weak_this_)));
138
139 {
140 base::AutoLock auto_lock(time_interpolator_lock_);
141 time_interpolator_->StopInterpolating();
142 }
143 }
144
145 void CmaRenderer::StartPlayingFrom(base::TimeDelta time) {
146 CMALOG(kLogControl) << __FUNCTION__ << ": " << time.InMilliseconds();
147 DCHECK(thread_checker_.CalledOnValidThread());
148 BeginStateTransition();
149
150 if (state_ == kError) {
151 client_->OnError(::media::PIPELINE_ERROR_ABORT);
152 CompleteStateTransition(kError);
153 return;
154 }
155
156 {
157 base::AutoLock auto_lock(time_interpolator_lock_);
158 time_interpolator_.reset(
159 new ::media::TimeDeltaInterpolator(&default_tick_clock_));
160 time_interpolator_->SetPlaybackRate(playback_rate_);
161 time_interpolator_->SetBounds(time, time);
162 time_interpolator_->StartInterpolating();
163 }
164
165 received_audio_eos_ = false;
166 received_video_eos_ = false;
167
168 DCHECK_EQ(state_, kFlushed) << state_;
169 // Immediately update transition to playing.
170 // Error case will be handled on response from host.
171 media_pipeline_->StartPlayingFrom(time);
172 CompleteStateTransition(kPlaying);
173 }
174
175 void CmaRenderer::SetPlaybackRate(double playback_rate) {
176 CMALOG(kLogControl) << __FUNCTION__ << ": " << playback_rate;
177 DCHECK(thread_checker_.CalledOnValidThread());
178 media_pipeline_->SetPlaybackRate(playback_rate);
179 playback_rate_ = playback_rate;
180
181 {
182 base::AutoLock auto_lock(time_interpolator_lock_);
183 time_interpolator_->SetPlaybackRate(playback_rate);
184 }
185 }
186
187 void CmaRenderer::SetVolume(float volume) {
188 CMALOG(kLogControl) << __FUNCTION__ << ": " << volume;
189 DCHECK(thread_checker_.CalledOnValidThread());
190 audio_pipeline_->SetVolume(volume);
191 }
192
193 base::TimeDelta CmaRenderer::GetMediaTime() {
194 base::AutoLock auto_lock(time_interpolator_lock_);
195 return time_interpolator_->GetInterpolatedTime();
196 }
197
198 bool CmaRenderer::HasAudio() {
199 DCHECK(thread_checker_.CalledOnValidThread());
200 return has_audio_;
201 }
202
203 bool CmaRenderer::HasVideo() {
204 DCHECK(thread_checker_.CalledOnValidThread());
205 return has_video_;
206 }
207
208 void CmaRenderer::SetCdm(::media::CdmContext* cdm_context,
209 const ::media::CdmAttachedCB& cdm_attached_cb) {
210 DCHECK(thread_checker_.CalledOnValidThread());
211 #if defined(ENABLE_BROWSER_CDMS)
212 media_pipeline_->SetCdm(cdm_context->GetCdmId());
213 #endif
214 cdm_attached_cb.Run(true);
215 }
216
217 void CmaRenderer::InitializeAudioPipeline() {
218 DCHECK(thread_checker_.CalledOnValidThread());
219 DCHECK_EQ(state_, kUninitialized) << state_;
220 DCHECK(!init_cb_.is_null());
221
222 ::media::DemuxerStream* stream =
223 demuxer_stream_provider_->GetStream(::media::DemuxerStream::AUDIO);
224 ::media::PipelineStatusCB audio_initialization_done_cb =
225 ::media::BindToCurrentLoop(
226 base::Bind(&CmaRenderer::OnAudioPipelineInitializeDone,
227 weak_this_,
228 stream != nullptr));
229 if (!stream) {
230 CMALOG(kLogControl) << __FUNCTION__ << ": no audio stream, skipping init.";
231 audio_initialization_done_cb.Run(::media::PIPELINE_OK);
232 return;
233 }
234
235 // Receive events from the audio pipeline.
236 AvPipelineClient av_pipeline_client;
237 av_pipeline_client.wait_for_key_cb = ::media::BindToCurrentLoop(
238 base::Bind(&CmaRenderer::OnWaitForKey, weak_this_, true));
239 av_pipeline_client.eos_cb = ::media::BindToCurrentLoop(
240 base::Bind(&CmaRenderer::OnEosReached, weak_this_, true));
241 av_pipeline_client.playback_error_cb =
242 ::media::BindToCurrentLoop(base::Bind(&CmaRenderer::OnError, weak_this_));
243 av_pipeline_client.statistics_cb = ::media::BindToCurrentLoop(
244 base::Bind(&CmaRenderer::OnStatisticsUpdated, weak_this_));
245 audio_pipeline_->SetClient(av_pipeline_client);
246
247 std::unique_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter(
248 base::ThreadTaskRunnerHandle::Get(), media_task_runner_factory_, stream));
249
250 const ::media::AudioDecoderConfig& config = stream->audio_decoder_config();
251 if (config.codec() == ::media::kCodecAAC)
252 stream->EnableBitstreamConverter();
253
254 media_pipeline_->InitializeAudio(config, std::move(frame_provider),
255 audio_initialization_done_cb);
256 }
257
258 void CmaRenderer::OnAudioPipelineInitializeDone(
259 bool audio_stream_present,
260 ::media::PipelineStatus status) {
261 CMALOG(kLogControl) << __FUNCTION__ << ": state=" << state_;
262 DCHECK(thread_checker_.CalledOnValidThread());
263
264 // OnError() may be fired at any time, even before initialization is complete.
265 if (state_ == kError)
266 return;
267
268 DCHECK_EQ(state_, kUninitialized) << state_;
269 DCHECK(!init_cb_.is_null());
270 if (status != ::media::PIPELINE_OK) {
271 base::ResetAndReturn(&init_cb_).Run(status);
272 return;
273 }
274
275 has_audio_ = audio_stream_present;
276 InitializeVideoPipeline();
277 }
278
279 void CmaRenderer::InitializeVideoPipeline() {
280 DCHECK(thread_checker_.CalledOnValidThread());
281 DCHECK_EQ(state_, kUninitialized) << state_;
282 DCHECK(!init_cb_.is_null());
283
284 ::media::DemuxerStream* stream =
285 demuxer_stream_provider_->GetStream(::media::DemuxerStream::VIDEO);
286 ::media::PipelineStatusCB video_initialization_done_cb =
287 ::media::BindToCurrentLoop(
288 base::Bind(&CmaRenderer::OnVideoPipelineInitializeDone,
289 weak_this_,
290 stream != nullptr));
291 if (!stream) {
292 CMALOG(kLogControl) << __FUNCTION__ << ": no video stream, skipping init.";
293 video_initialization_done_cb.Run(::media::PIPELINE_OK);
294 return;
295 }
296
297 // Receive events from the video pipeline.
298 VideoPipelineClient client;
299 client.av_pipeline_client.wait_for_key_cb = ::media::BindToCurrentLoop(
300 base::Bind(&CmaRenderer::OnWaitForKey, weak_this_, false));
301 client.av_pipeline_client.eos_cb = ::media::BindToCurrentLoop(
302 base::Bind(&CmaRenderer::OnEosReached, weak_this_, false));
303 client.av_pipeline_client.playback_error_cb =
304 ::media::BindToCurrentLoop(base::Bind(&CmaRenderer::OnError, weak_this_));
305 client.av_pipeline_client.statistics_cb = ::media::BindToCurrentLoop(
306 base::Bind(&CmaRenderer::OnStatisticsUpdated, weak_this_));
307 client.natural_size_changed_cb = ::media::BindToCurrentLoop(
308 base::Bind(&CmaRenderer::OnNaturalSizeChanged, weak_this_));
309 video_pipeline_->SetClient(client);
310
311 std::unique_ptr<CodedFrameProvider> frame_provider(new DemuxerStreamAdapter(
312 base::ThreadTaskRunnerHandle::Get(), media_task_runner_factory_, stream));
313
314 const ::media::VideoDecoderConfig& config = stream->video_decoder_config();
315 if (config.codec() == ::media::kCodecH264)
316 stream->EnableBitstreamConverter();
317
318 std::vector<::media::VideoDecoderConfig> configs;
319 configs.push_back(config);
320 media_pipeline_->InitializeVideo(configs, std::move(frame_provider),
321 video_initialization_done_cb);
322 }
323
324 void CmaRenderer::OnVideoPipelineInitializeDone(
325 bool video_stream_present,
326 ::media::PipelineStatus status) {
327 CMALOG(kLogControl) << __FUNCTION__ << ": state=" << state_;
328 DCHECK(thread_checker_.CalledOnValidThread());
329
330 // OnError() may be fired at any time, even before initialization is complete.
331 if (state_ == kError)
332 return;
333
334 DCHECK_EQ(state_, kUninitialized) << state_;
335 DCHECK(!init_cb_.is_null());
336 if (status != ::media::PIPELINE_OK) {
337 base::ResetAndReturn(&init_cb_).Run(status);
338 return;
339 }
340
341 has_video_ = video_stream_present;
342 CompleteStateTransition(kFlushed);
343 base::ResetAndReturn(&init_cb_).Run(::media::PIPELINE_OK);
344 }
345
346 void CmaRenderer::OnWaitForKey(bool is_audio) {
347 client_->OnWaitingForDecryptionKey();
348 }
349
350 void CmaRenderer::OnEosReached(bool is_audio) {
351 DCHECK(thread_checker_.CalledOnValidThread());
352 if (state_ != kPlaying) {
353 LOG(WARNING) << __FUNCTION__ << " Ignoring a late EOS event";
354 return;
355 }
356
357 if (is_audio) {
358 DCHECK(!received_audio_eos_);
359 received_audio_eos_ = true;
360 } else {
361 DCHECK(!received_video_eos_);
362 received_video_eos_ = true;
363 }
364
365 bool audio_finished = !has_audio_ || received_audio_eos_;
366 bool video_finished = !has_video_ || received_video_eos_;
367 CMALOG(kLogControl) << __FUNCTION__ << " audio_finished=" << audio_finished
368 << " video_finished=" << video_finished;
369 if (audio_finished && video_finished)
370 client_->OnEnded();
371 }
372
373 void CmaRenderer::OnStatisticsUpdated(
374 const ::media::PipelineStatistics& stats) {
375 DCHECK(thread_checker_.CalledOnValidThread());
376 client_->OnStatisticsUpdate(stats);
377 }
378
379 void CmaRenderer::OnNaturalSizeChanged(const gfx::Size& size) {
380 DCHECK(thread_checker_.CalledOnValidThread());
381 video_renderer_sink_->PaintSingleFrame(
382 video_overlay_factory_->CreateFrame(size));
383 client_->OnVideoNaturalSizeChange(size);
384 }
385
386 void CmaRenderer::OnPlaybackTimeUpdated(base::TimeDelta time,
387 base::TimeDelta max_time,
388 base::TimeTicks capture_time) {
389 DCHECK(thread_checker_.CalledOnValidThread());
390 if (state_ != kPlaying) {
391 LOG(WARNING) << "Ignoring a late time update";
392 return;
393 }
394
395 // TODO(halliwell): arguably, TimeDeltaInterpolator::SetBounds should perform
396 // this calculation to avoid calling TimeTicks::Now twice (it's slower and has
397 // potential accuracy problems).
398 base::TimeDelta lower_bound =
399 std::min(max_time, time + base::TimeTicks::Now() - capture_time);
400
401 base::AutoLock auto_lock(time_interpolator_lock_);
402 time_interpolator_->SetBounds(lower_bound, max_time);
403 }
404
405 void CmaRenderer::OnBufferingNotification(
406 ::media::BufferingState buffering_state) {
407 CMALOG(kLogControl) << __FUNCTION__ << ": state=" << state_
408 << ", buffering=" << buffering_state;
409 DCHECK(thread_checker_.CalledOnValidThread());
410 client_->OnBufferingStateChange(buffering_state);
411 }
412
413 void CmaRenderer::OnFlushDone() {
414 DCHECK(thread_checker_.CalledOnValidThread());
415
416 if (state_ == kError) {
417 // If OnError was called while the flush was in progress,
418 // |flush_cb_| must be null.
419 DCHECK(flush_cb_.is_null());
420 return;
421 }
422
423 CompleteStateTransition(kFlushed);
424 base::ResetAndReturn(&flush_cb_).Run();
425 }
426
427 void CmaRenderer::OnError(::media::PipelineStatus error) {
428 DCHECK(thread_checker_.CalledOnValidThread());
429 DCHECK_NE(::media::PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
430 LOG(ERROR) << "CMA error encountered: " << error;
431
432 State old_state = state_;
433 CompleteStateTransition(kError);
434
435 if (old_state != kError) {
436 if (!init_cb_.is_null()) {
437 base::ResetAndReturn(&init_cb_).Run(error);
438 return;
439 }
440 client_->OnError(error);
441 }
442
443 // After OnError() returns, the pipeline may destroy |this|.
444 if (!flush_cb_.is_null())
445 base::ResetAndReturn(&flush_cb_).Run();
446 }
447
448 void CmaRenderer::BeginStateTransition() {
449 DCHECK(!is_pending_transition_) << state_;
450 is_pending_transition_ = true;
451 }
452
453 void CmaRenderer::CompleteStateTransition(State new_state) {
454 state_ = new_state;
455 is_pending_transition_ = false;
456 }
457
458 } // namespace media
459 } // namespace chromecast
OLDNEW
« no previous file with comments | « chromecast/renderer/media/cma_renderer.h ('k') | chromecast/renderer/media/media_channel_proxy.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698