[M120 Migration][MM] Handle live stream duration and currenttime
[platform/framework/web/chromium-efl.git] / media / mojo / clients / mojo_renderer.cc
1 // Copyright 2014 The Chromium Authors
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 "media/mojo/clients/mojo_renderer.h"
6
7 #include <utility>
8
9 #include "base/functional/bind.h"
10 #include "base/functional/callback_helpers.h"
11 #include "base/location.h"
12 #include "base/task/sequenced_task_runner.h"
13 #include "base/task/single_thread_task_runner.h"
14 #include "media/base/cdm_context.h"
15 #include "media/base/media_resource.h"
16 #include "media/base/pipeline_status.h"
17 #include "media/base/renderer_client.h"
18 #include "media/base/video_renderer_sink.h"
19 #include "media/mojo/clients/mojo_demuxer_stream_impl.h"
20 #include "media/mojo/common/media_type_converters.h"
21 #include "media/renderers/video_overlay_factory.h"
22
23 namespace media {
24
25 MojoRenderer::MojoRenderer(
26     const scoped_refptr<base::SequencedTaskRunner>& task_runner,
27     std::unique_ptr<VideoOverlayFactory> video_overlay_factory,
28     VideoRendererSink* video_renderer_sink,
29     mojo::PendingRemote<mojom::Renderer> remote_renderer)
30     : task_runner_(task_runner),
31       video_overlay_factory_(std::move(video_overlay_factory)),
32       video_renderer_sink_(video_renderer_sink),
33       remote_renderer_pending_remote_(std::move(remote_renderer)),
34       media_time_interpolator_(base::DefaultTickClock::GetInstance()) {
35   DVLOG(1) << __func__;
36 }
37
38 MojoRenderer::~MojoRenderer() {
39   DVLOG(1) << __func__;
40   DCHECK(task_runner_->RunsTasksInCurrentSequence());
41
42   CancelPendingCallbacks();
43 }
44
45 void MojoRenderer::Initialize(MediaResource* media_resource,
46                               media::RendererClient* client,
47                               PipelineStatusCallback init_cb) {
48   DVLOG(1) << __func__;
49   DCHECK(task_runner_->RunsTasksInCurrentSequence());
50   DCHECK(media_resource);
51
52   if (encountered_error_) {
53     task_runner_->PostTask(
54         FROM_HERE, base::BindOnce(std::move(init_cb),
55                                   PIPELINE_ERROR_INITIALIZATION_FAILED));
56     return;
57   }
58
59   media_resource_ = media_resource;
60   init_cb_ = std::move(init_cb);
61
62   switch (media_resource_->GetType()) {
63     case MediaResource::Type::kStream:
64       InitializeRendererFromStreams(client);
65       break;
66     case MediaResource::Type::KUrl:
67       InitializeRendererFromUrl(client);
68       break;
69   }
70 }
71
72 void MojoRenderer::InitializeRendererFromStreams(
73     media::RendererClient* client) {
74   DVLOG(1) << __func__;
75   DCHECK(task_runner_->RunsTasksInCurrentSequence());
76
77   // Create mojom::DemuxerStream for each demuxer stream and bind its lifetime
78   // to the pipe.
79   std::vector<DemuxerStream*> streams = media_resource_->GetAllStreams();
80   std::vector<mojo::PendingRemote<mojom::DemuxerStream>> stream_proxies;
81
82   for (auto* stream : streams) {
83     mojo::PendingRemote<mojom::DemuxerStream> stream_proxy;
84     auto mojo_stream = std::make_unique<MojoDemuxerStreamImpl>(
85         stream, stream_proxy.InitWithNewPipeAndPassReceiver());
86
87     // Using base::Unretained(this) is safe because |this| owns |mojo_stream|,
88     // and the error handler can't be invoked once |mojo_stream| is destroyed.
89     mojo_stream->set_disconnect_handler(
90         base::BindOnce(&MojoRenderer::OnDemuxerStreamConnectionError,
91                        base::Unretained(this), mojo_stream.get()));
92
93     streams_.push_back(std::move(mojo_stream));
94     stream_proxies.push_back(std::move(stream_proxy));
95   }
96
97   BindRemoteRendererIfNeeded();
98
99   // Using base::Unretained(this) is safe because |this| owns
100   // |remote_renderer_|, and the callback won't be dispatched if
101   // |remote_renderer_| is destroyed.
102   remote_renderer_->Initialize(client_receiver_.BindNewEndpointAndPassRemote(),
103                                std::move(stream_proxies), nullptr,
104                                base::BindOnce(&MojoRenderer::OnInitialized,
105                                               base::Unretained(this), client));
106 }
107
108 void MojoRenderer::InitializeRendererFromUrl(media::RendererClient* client) {
109   DVLOG(2) << __func__;
110   DCHECK(task_runner_->RunsTasksInCurrentSequence());
111
112   BindRemoteRendererIfNeeded();
113
114   const MediaUrlParams& url_params = media_resource_->GetMediaUrlParams();
115
116   // Using base::Unretained(this) is safe because |this| owns
117   // |remote_renderer_|, and the callback won't be dispatched if
118   // |remote_renderer_| is destroyed.
119   mojom::MediaUrlParamsPtr media_url_params = mojom::MediaUrlParams::New(
120       url_params.media_url, url_params.site_for_cookies,
121       url_params.top_frame_origin, url_params.has_storage_access,
122       url_params.allow_credentials, url_params.is_hls);
123   remote_renderer_->Initialize(client_receiver_.BindNewEndpointAndPassRemote(),
124                                absl::nullopt, std::move(media_url_params),
125                                base::BindOnce(&MojoRenderer::OnInitialized,
126                                               base::Unretained(this), client));
127 }
128
129 void MojoRenderer::SetCdm(CdmContext* cdm_context,
130                           CdmAttachedCB cdm_attached_cb) {
131   DVLOG(1) << __func__;
132   DCHECK(task_runner_->RunsTasksInCurrentSequence());
133   DCHECK(cdm_context);
134   DCHECK(cdm_attached_cb);
135   DCHECK(!cdm_attached_cb_);
136
137   if (encountered_error_) {
138     task_runner_->PostTask(FROM_HERE,
139                            base::BindOnce(std::move(cdm_attached_cb), false));
140     return;
141   }
142
143   absl::optional<base::UnguessableToken> cdm_id = cdm_context->GetCdmId();
144   if (!cdm_id) {
145     DVLOG(2) << "MojoRenderer only works with remote CDMs but the CDM ID "
146                 "is invalid.";
147     task_runner_->PostTask(FROM_HERE,
148                            base::BindOnce(std::move(cdm_attached_cb), false));
149     return;
150   }
151
152   BindRemoteRendererIfNeeded();
153
154   cdm_attached_cb_ = std::move(cdm_attached_cb);
155   remote_renderer_->SetCdm(cdm_id, base::BindOnce(&MojoRenderer::OnCdmAttached,
156                                                   base::Unretained(this)));
157 }
158
159 void MojoRenderer::SetLatencyHint(
160     absl::optional<base::TimeDelta> latency_hint) {
161   // TODO(chcunningham): Proxy to remote renderer if needed.
162 }
163
164 void MojoRenderer::Flush(base::OnceClosure flush_cb) {
165   DVLOG(2) << __func__;
166   DCHECK(task_runner_->RunsTasksInCurrentSequence());
167   DCHECK(remote_renderer_.is_bound());
168   DCHECK(flush_cb);
169   DCHECK(!flush_cb_);
170
171   if (encountered_error_) {
172     task_runner_->PostTask(FROM_HERE, std::move(flush_cb));
173     return;
174   }
175
176   {
177     base::AutoLock auto_lock(lock_);
178     if (media_time_interpolator_.interpolating())
179       media_time_interpolator_.StopInterpolating();
180   }
181
182   flush_cb_ = std::move(flush_cb);
183   remote_renderer_->Flush(
184       base::BindOnce(&MojoRenderer::OnFlushed, base::Unretained(this)));
185 }
186
187 #if defined(TIZEN_MULTIMEDIA)
188 void MojoRenderer::Seek(base::TimeDelta time, base::OnceClosure seek_cb) {
189   DVLOG(2) << __func__ << "(" << time << ")";
190   DCHECK(task_runner_->RunsTasksInCurrentSequence());
191   DCHECK(seek_cb);
192
193   if (encountered_error_) {
194     task_runner_->PostTask(FROM_HERE, std::move(seek_cb));
195     return;
196   }
197
198   seek_cb_ = std::move(seek_cb);
199   remote_renderer_->Seek(time, base::BindOnce(&MojoRenderer::OnSeekCompleted,
200                                               base::Unretained(this)));
201 }
202
203 void MojoRenderer::Suspend() {
204   DVLOG(2) << __func__;
205   DCHECK(task_runner_->RunsTasksInCurrentSequence());
206
207   remote_renderer_->Suspend();
208 }
209
210 void MojoRenderer::ToggleFullscreenMode(bool is_fullscreen,
211                                         ToggledFullscreenCB cb) {
212   if (remote_renderer_.is_bound())
213     remote_renderer_->ToggleFullscreenMode(is_fullscreen, std::move(cb));
214 }
215 #endif
216
217 void MojoRenderer::StartPlayingFrom(base::TimeDelta time) {
218   DVLOG(2) << __func__ << "(" << time << ")";
219   DCHECK(task_runner_->RunsTasksInCurrentSequence());
220   DCHECK(remote_renderer_.is_bound());
221
222   {
223     base::AutoLock auto_lock(lock_);
224     media_time_interpolator_.SetBounds(time, time, base::TimeTicks::Now());
225     media_time_interpolator_.StartInterpolating();
226   }
227
228   remote_renderer_->StartPlayingFrom(time);
229 }
230
231 void MojoRenderer::SetPlaybackRate(double playback_rate) {
232   DVLOG(2) << __func__ << "(" << playback_rate << ")";
233   DCHECK(task_runner_->RunsTasksInCurrentSequence());
234   DCHECK(remote_renderer_.is_bound());
235
236   remote_renderer_->SetPlaybackRate(playback_rate);
237
238   {
239     base::AutoLock auto_lock(lock_);
240     media_time_interpolator_.SetPlaybackRate(playback_rate);
241   }
242 }
243
244 void MojoRenderer::SetVolume(float volume) {
245   DVLOG(2) << __func__ << "(" << volume << ")";
246   DCHECK(task_runner_->RunsTasksInCurrentSequence());
247
248   volume_ = volume;
249   if (remote_renderer_.is_bound())
250     remote_renderer_->SetVolume(volume);
251 }
252
253 base::TimeDelta MojoRenderer::GetMediaTime() {
254   base::AutoLock auto_lock(lock_);
255   return media_time_interpolator_.GetInterpolatedTime();
256 }
257
258 #if defined(TIZEN_VIDEO_HOLE)
259 void MojoRenderer::SetVideoHole(bool is_video_hole) {
260   BindRemoteRendererIfNeeded();
261   remote_renderer_->SetVideoHole(is_video_hole);
262 }
263
264 void MojoRenderer::SetMediaGeometry(const gfx::RectF& rect) {
265   if (remote_renderer_.is_bound())
266     remote_renderer_->SetMediaGeometry(rect);
267 }
268 #endif
269
270 RendererType MojoRenderer::GetRendererType() {
271   return RendererType::kMojo;
272 }
273
274 void MojoRenderer::OnTimeUpdate(base::TimeDelta time,
275                                 base::TimeDelta max_time,
276                                 base::TimeTicks capture_time) {
277   DVLOG(4) << __func__ << "(" << time << ", " << max_time << ", "
278            << capture_time << ")";
279   DCHECK(task_runner_->RunsTasksInCurrentSequence());
280
281   base::AutoLock auto_lock(lock_);
282   media_time_interpolator_.SetBounds(time, max_time, capture_time);
283 }
284
285 void MojoRenderer::OnBufferingStateChange(BufferingState state,
286                                           BufferingStateChangeReason reason) {
287   DVLOG(2) << __func__;
288   DCHECK(task_runner_->RunsTasksInCurrentSequence());
289   client_->OnBufferingStateChange(state, reason);
290 }
291
292 void MojoRenderer::OnEnded() {
293   DVLOG(1) << __func__;
294   DCHECK(task_runner_->RunsTasksInCurrentSequence());
295   client_->OnEnded();
296 }
297
298 void MojoRenderer::OnError(const PipelineStatus& status) {
299   DVLOG(1) << __func__;
300   DCHECK(task_runner_->RunsTasksInCurrentSequence());
301   DCHECK(!init_cb_);
302
303   encountered_error_ = true;
304   client_->OnError(status);
305 }
306
307 void MojoRenderer::OnVideoNaturalSizeChange(const gfx::Size& size) {
308   DVLOG(2) << __func__ << ": " << size.ToString();
309   DCHECK(task_runner_->RunsTasksInCurrentSequence());
310
311   if (video_overlay_factory_) {
312     video_renderer_sink_->PaintSingleFrame(
313         video_overlay_factory_->CreateFrame(size));
314   }
315   client_->OnVideoNaturalSizeChange(size);
316 }
317
318 void MojoRenderer::OnVideoOpacityChange(bool opaque) {
319   DVLOG(2) << __func__ << ": " << opaque;
320   DCHECK(task_runner_->RunsTasksInCurrentSequence());
321   client_->OnVideoOpacityChange(opaque);
322 }
323
324 void MojoRenderer::OnAudioConfigChange(const AudioDecoderConfig& config) {
325   DVLOG(2) << __func__ << ": " << config.AsHumanReadableString();
326   DCHECK(task_runner_->RunsTasksInCurrentSequence());
327   client_->OnAudioConfigChange(config);
328 }
329
330 void MojoRenderer::OnVideoConfigChange(const VideoDecoderConfig& config) {
331   DVLOG(2) << __func__ << ": " << config.AsHumanReadableString();
332   DCHECK(task_runner_->RunsTasksInCurrentSequence());
333   client_->OnVideoConfigChange(config);
334 }
335
336 void MojoRenderer::OnStatisticsUpdate(const PipelineStatistics& stats) {
337   DVLOG(3) << __func__;
338   DCHECK(task_runner_->RunsTasksInCurrentSequence());
339   if (!client_) {
340     pending_stats_ = stats;
341     return;
342   }
343   client_->OnStatisticsUpdate(stats);
344 }
345
346 #if BUILDFLAG(IS_TIZEN_TV)
347 void MojoRenderer::SetContentMimeType(const std::string& mime_type) {
348   DVLOG(2) << __func__ << " mime_type : " << mime_type;
349   DCHECK(task_runner_->RunsTasksInCurrentSequence());
350
351   if (remote_renderer_.is_bound())
352     remote_renderer_->SetContentMimeType(mime_type);
353 }
354
355 void MojoRenderer::SetParentalRatingResult(bool is_pass) {
356   DCHECK(task_runner_->RunsTasksInCurrentSequence());
357
358   if (remote_renderer_.is_bound())
359     remote_renderer_->SetParentalRatingResult(is_pass);
360   else
361     LOG(ERROR) << "remote_renderer is null";
362 }
363 #endif
364
365 void MojoRenderer::OnWaiting(WaitingReason reason) {
366   DVLOG(1) << __func__;
367   DCHECK(task_runner_->RunsTasksInCurrentSequence());
368   client_->OnWaiting(reason);
369 }
370
371 void MojoRenderer::OnConnectionError() {
372   DVLOG(1) << __func__;
373   DCHECK(task_runner_->RunsTasksInCurrentSequence());
374
375   encountered_error_ = true;
376   CancelPendingCallbacks();
377
378   if (client_)
379     client_->OnError(PIPELINE_ERROR_DISCONNECTED);
380 }
381
382 void MojoRenderer::OnDemuxerStreamConnectionError(
383     MojoDemuxerStreamImpl* stream) {
384   DVLOG(1) << __func__ << ": stream=" << stream;
385   DCHECK(task_runner_->RunsTasksInCurrentSequence());
386
387   for (auto& s : streams_) {
388     if (s.get() == stream) {
389       s.reset();
390       return;
391     }
392   }
393   NOTREACHED() << "Unrecognized demuxer stream=" << stream;
394 }
395
396 void MojoRenderer::BindRemoteRendererIfNeeded() {
397   DVLOG(2) << __func__;
398   DCHECK(task_runner_->RunsTasksInCurrentSequence());
399
400   // If |remote_renderer_| has already been bound, do nothing.
401   // Note that after Bind() is called, |remote_renderer_| is always bound even
402   // after connection error.
403   if (remote_renderer_.is_bound())
404     return;
405
406   // Bind |remote_renderer_| to the |remote_renderer_pending_remote_|.
407   remote_renderer_.Bind(std::move(remote_renderer_pending_remote_));
408
409   // Otherwise, set an error handler to catch the connection error.
410   // Using base::Unretained(this) is safe because |this| owns
411   // |remote_renderer_|, and the error handler can't be invoked once
412   // |remote_renderer_| is destroyed.
413   remote_renderer_.set_disconnect_handler(
414       base::BindOnce(&MojoRenderer::OnConnectionError, base::Unretained(this)));
415 }
416
417 void MojoRenderer::OnInitialized(media::RendererClient* client, bool success) {
418   DVLOG(1) << __func__;
419   DCHECK(task_runner_->RunsTasksInCurrentSequence());
420   DCHECK(init_cb_);
421
422   // Only set |client_| after initialization succeeded. No client methods should
423   // be called before this.
424   if (success) {
425     client_ = client;
426
427     // It'd be nice to provide this before Initialize(), but that causes some
428     // MojoRenderer implementations to crash.
429     SetVolume(volume_);
430   }
431
432   std::move(init_cb_).Run(success ? PIPELINE_OK
433                                   : PIPELINE_ERROR_INITIALIZATION_FAILED);
434
435   if (client_ && pending_stats_.has_value())
436     client_->OnStatisticsUpdate(pending_stats_.value());
437   pending_stats_.reset();
438 }
439
440 void MojoRenderer::OnFlushed() {
441   DVLOG(1) << __func__;
442   DCHECK(task_runner_->RunsTasksInCurrentSequence());
443   DCHECK(flush_cb_);
444
445   std::move(flush_cb_).Run();
446 }
447
448 #if defined(TIZEN_MULTIMEDIA)
449 void MojoRenderer::OnRequestSuspend(bool resource_conflicted) {
450   DVLOG(2) << __func__;
451   client_->OnRequestSuspend(resource_conflicted);
452 }
453
454 void MojoRenderer::OnRequestSeek(base::TimeDelta time) {
455   DVLOG(2) << __func__ << "(" << time << ")";
456   client_->OnRequestSeek(time);
457 }
458
459 void MojoRenderer::OnSeekableTimeChange(base::TimeDelta min_time,
460                                         base::TimeDelta max_time,
461                                         bool is_live) {
462   DVLOG(2) << __func__ << " min_time " << min_time << " max_time " << max_time
463            << "is_live " << is_live;
464   client_->OnSeekableTimeChange(min_time, max_time, is_live);
465 }
466
467 void MojoRenderer::OnSeekCompleted() {
468   DVLOG(1) << __func__;
469   DCHECK(task_runner_->RunsTasksInCurrentSequence());
470   DCHECK(seek_cb_);
471
472   std::move(seek_cb_).Run();
473 }
474
475 void MojoRenderer::OnLivePlaybackComplete() {
476   DVLOG(1) << __func__;
477   client_->OnLivePlaybackComplete();
478 }
479 #endif
480
481 void MojoRenderer::OnCdmAttached(bool success) {
482   DVLOG(1) << __func__;
483   DCHECK(task_runner_->RunsTasksInCurrentSequence());
484   DCHECK(cdm_attached_cb_);
485
486   std::move(cdm_attached_cb_).Run(success);
487 }
488
489 void MojoRenderer::CancelPendingCallbacks() {
490   DVLOG(1) << __func__;
491   DCHECK(task_runner_->RunsTasksInCurrentSequence());
492
493   if (init_cb_)
494     std::move(init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
495
496   if (flush_cb_)
497     std::move(flush_cb_).Run();
498
499   if (cdm_attached_cb_)
500     std::move(cdm_attached_cb_).Run(false);
501 }
502
503 }  // namespace media