1 // Copyright 2022 Samsung Electronics Inc. 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.
5 #include "tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.h"
7 #include "base/command_line.h"
8 #include "base/functional/bind.h"
9 #include "base/task/single_thread_task_runner.h"
10 #include "base/trace_event/trace_event.h"
11 #include "content/browser/web_contents/web_contents_impl.h"
12 #include "content/common/content_client_export.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/browser_task_traits.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/content_browser_client.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/common/content_client.h"
20 #include "media/base/audio_decoder_config.h"
21 #include "media/base/media_resource.h"
22 #include "media/base/media_switches.h"
23 #include "media/base/renderer_client.h"
24 #include "media/base/video_decoder_config.h"
25 #include "tizen_src/chromium_impl/content/browser/media/media_player_renderer_web_contents_observer.h"
26 #include "tizen_src/chromium_impl/media/filters/media_player_registry.h"
27 #include "tizen_src/chromium_impl/media/filters/media_player_tizen.h"
29 #if defined(TIZEN_VIDEO_HOLE)
30 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
33 #if defined(TIZEN_TBM_SUPPORT)
35 void DestroyMediaPacket(void* media_packet, int player_id) {
36 MediaPlayerTizen* player =
37 MediaPlayerRegistry::GetInstance()->GetMediaPlayer(player_id);
39 // TODO: All media packets should be removed before player is destroyed.
40 LOG(WARNING) << __func__
41 << " player already destroyed. player_id: " << player_id;
44 player->DestroyMediaPacket(media_packet);
51 TizenRendererImpl::TizenRendererImpl(
54 WebContents* web_contents,
55 mojo::PendingReceiver<RendererExtension> renderer_extension_receiver,
56 mojo::PendingRemote<ClientExtension> client_extension_remote)
57 : client_extension_(std::move(client_extension_remote)),
58 render_process_id_(process_id),
59 routing_id_(routing_id),
60 volume_(kDefaultVolume),
61 #if defined(TIZEN_VIDEO_HOLE)
62 video_rect_(gfx::RectF()),
64 #if BUILDFLAG(IS_TIZEN_TV)
65 notify_playback_state_(media::kPlaybackStop),
67 renderer_extension_receiver_(this,
68 std::move(renderer_extension_receiver)),
69 web_contents_(web_contents) {
70 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
71 DCHECK_EQ(WebContents::FromRenderFrameHost(
72 RenderFrameHost::FromID(process_id, routing_id)),
75 WebContentsImpl* web_contents_impl =
76 static_cast<WebContentsImpl*>(web_contents);
77 web_contents_muted_ = web_contents_impl && web_contents_impl->IsAudioMuted();
78 task_runner_ = base::SingleThreadTaskRunner::GetCurrentDefault();
81 MediaPlayerRendererWebContentsObserver::CreateForWebContents(web_contents);
82 web_contents_observer_ =
83 MediaPlayerRendererWebContentsObserver::FromWebContents(web_contents);
84 if (web_contents_observer_)
85 web_contents_observer_->AddMediaPlayerRenderer(this);
89 TizenRendererImpl::TizenRendererImpl(
90 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
91 media::VideoRendererSink* sink)
92 : state_(STATE_UNINITIALIZED),
93 task_runner_(task_runner),
95 #if defined(TIZEN_VIDEO_HOLE)
96 video_rect_(gfx::RectF()),
98 renderer_extension_receiver_(this) {
99 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
100 // TODO(dalecurtis): Remove once experiments for http://crbug.com/470940 are
102 int threshold_ms = 0;
103 std::string threshold_ms_str(
104 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
105 switches::kVideoUnderflowThresholdMs));
106 if (base::StringToInt(threshold_ms_str, &threshold_ms) && threshold_ms > 0) {
111 TizenRendererImpl::~TizenRendererImpl() {
112 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
113 DCHECK(task_runner_->BelongsToCurrentThread());
115 weak_factory_.InvalidateWeakPtrs();
117 media::MediaPlayerRegistry::GetInstance()->UnregisterMediaPlayer(player_id_);
120 media_player_->Release();
121 media_player_->SetMediaPlayerClient(nullptr);
124 if (web_contents_observer_)
125 web_contents_observer_->RemoveMediaPlayerRenderer(this);
128 void TizenRendererImpl::Initialize(media::MediaResource* media_resource,
129 media::RendererClient* client,
130 media::PipelineStatusCallback init_cb) {
131 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
132 DCHECK(task_runner_->BelongsToCurrentThread());
133 DCHECK_EQ(state_, STATE_UNINITIALIZED);
136 TRACE_EVENT_ASYNC_BEGIN0("media", "TizenRendererImpl::Initialize", this);
139 init_cb_ = std::move(init_cb);
140 media_resource_ = media_resource;
142 std::string user_agent;
143 content::WebContents* web_contents = GetWebContents();
145 user_agent = web_contents->GetUserAgentOverride().ua_string_override;
146 if (user_agent.empty())
147 user_agent = content::GetContentClientExport()->browser()->GetUserAgent();
150 // Create a media player and get player id for the created media player.
151 media_player_ = media::MediaPlayerRegistry::GetInstance()->CreateMediaPlayer(
152 media_resource_, user_agent, volume_, player_id_);
154 if (!media_player_->CreatePlayer(player_id_)) {
155 media::MediaPlayerRegistry::GetInstance()->UnregisterMediaPlayer(
157 std::move(init_cb_).Run(media::PIPELINE_ERROR_INITIALIZATION_FAILED);
161 if (media_resource_->GetType() != media::MediaResource::Type::KUrl) {
163 media_resource_->GetFirstStream(media::DemuxerStream::AUDIO);
165 media_player_->SetMediaType(media::DemuxerStream::AUDIO);
168 media_resource_->GetFirstStream(media::DemuxerStream::VIDEO);
170 media_player_->SetMediaType(media::DemuxerStream::VIDEO);
174 #if defined(TIZEN_VIDEO_HOLE)
176 if (RenderWidgetHostViewAura* view_aura =
177 static_cast<RenderWidgetHostViewAura*>(
178 web_contents->GetRenderWidgetHostView())) {
179 view_aura->SetWebViewMovedCallback(base::BindRepeating(
180 &TizenRendererImpl::OnWebViewMoved, base::Unretained(this)));
181 LOG(INFO) << "SetPositionMovedCallbacks called!";
186 // TODO: return unsupported error for CDM.
187 std::move(init_cb_).Run(media::PIPELINE_OK);
189 media_player_->SetMediaPlayerClient(this);
190 media_player_->SetTaskRunner(task_runner_.get());
192 if (!media::MediaPlayerRegistry::GetInstance()->ActivateMediaPlayer(
193 player_id_, !resource_conflicted_)) {
194 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
195 << " Can not initialize the player id: " << player_id_;
199 is_suspended_ = false;
200 resource_conflicted_ = false;
202 // Initialize and Prepare early when resource type is URL.
203 if (media_resource_->GetType() == media::MediaResource::Type::KUrl) {
204 SetPlayerInitialize();
209 void TizenRendererImpl::SetStreamInfo() {
210 if (media_resource_->GetType() == media::MediaResource::Type::KUrl) {
211 media_player_->SetStreamInfo(media::DemuxerStream::VIDEO, 0);
216 media_player_->SetStreamInfo(media::DemuxerStream::AUDIO, audio_stream_);
218 media_player_->SetStreamInfo(media::DemuxerStream::VIDEO, video_stream_);
221 void TizenRendererImpl::SetPlayerInitialize() {
222 if (media_player_->IsInitialized()) {
223 LOG(INFO) << __func__ << " Already initialized.";
227 #if defined(TIZEN_VIDEO_HOLE)
228 if (is_video_hole_) {
229 SetPlayerVideoHole();
230 SetPlayerMediaGeometry();
234 media_player_->Initialize(sink_);
237 void TizenRendererImpl::SetPlayerPrepare() {
238 if (!media_player_->CanPrepare()) {
239 LOG(INFO) << __func__ << " Already preparing or prepared.";
246 #if defined(TIZEN_VIDEO_HOLE)
247 media_player_->PrepareVideoHole();
250 media_player_->Prepare();
253 void TizenRendererImpl::SetCdm(media::CdmContext* cdm_context,
254 CdmAttachedCB cdm_attached_cb) {
255 DCHECK(task_runner_->BelongsToCurrentThread());
257 TRACE_EVENT0("media", "TizenRendererImpl::SetCdm");
261 void TizenRendererImpl::SetLatencyHint(
262 absl::optional<base::TimeDelta> latency_hint) {
263 DCHECK(!latency_hint || (*latency_hint >= base::TimeDelta()));
264 DCHECK(task_runner_->BelongsToCurrentThread());
268 void TizenRendererImpl::SetPreservesPitch(bool preserves_pitch) {
269 DCHECK(task_runner_->BelongsToCurrentThread());
273 void TizenRendererImpl::Flush(base::OnceClosure flush_cb) {
274 DCHECK(task_runner_->BelongsToCurrentThread());
275 TRACE_EVENT_ASYNC_BEGIN0("media", "TizenRendererImpl::Flush", this);
277 if (state_ != STATE_PLAYING) {
278 DCHECK_EQ(state_, STATE_ERROR);
282 media_player_->Flush(std::move(flush_cb));
285 void TizenRendererImpl::Seek(base::TimeDelta time, base::OnceClosure seek_cb) {
286 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
287 << " time : " << time.InMicroseconds();
288 media_player_->Seek(time, std::move(seek_cb));
291 #if defined(TIZEN_VIDEO_HOLE)
292 void TizenRendererImpl::SetVideoHole(bool is_video_hole) {
293 if (is_video_hole_ == is_video_hole)
296 is_video_hole_ = is_video_hole;
297 SetPlayerVideoHole();
300 void TizenRendererImpl::SetPlayerVideoHole() {
302 media_player_->SetVideoHole(is_video_hole_);
305 void TizenRendererImpl::SetMediaGeometry(const gfx::RectF& rect) {
306 if (video_rect_ == rect)
310 SetPlayerMediaGeometry();
313 void TizenRendererImpl::SetPlayerMediaGeometry() {
315 // Always get the latest viewport rect.
316 media_player_->SetMediaGeometry(GetViewportRect(), video_rect_);
320 gfx::Rect TizenRendererImpl::GetViewportRect() const {
321 content::WebContents* web_contents = GetWebContents();
325 if (RenderWidgetHostViewAura* rwhv = static_cast<RenderWidgetHostViewAura*>(
326 web_contents->GetRenderWidgetHostView())) {
327 if (rwhv->offscreen_helper()) {
328 // offscreen rendering mode
329 return rwhv->offscreen_helper()->GetViewBoundsInPix();
331 // onscreen rendering mode
332 return rwhv->window()->bounds();
338 void TizenRendererImpl::OnWebViewMoved() {
340 LOG(INFO) << __func__ << " Reset WebView-Position.";
341 SetPlayerMediaGeometry();
346 content::WebContents* TizenRendererImpl::GetWebContents() const {
347 return WebContents::FromRenderFrameHost(
348 RenderFrameHost::FromID(render_process_id_, routing_id_));
351 void TizenRendererImpl::Suspend() {
352 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
356 if (!media_player_) {
357 LOG(INFO) << "media_player_ is not created yet";
361 media::MediaPlayerRegistry::GetInstance()->DeactivateMediaPlayer(
362 player_id_, !resource_conflicted_);
363 media_player_->Release();
364 is_suspended_ = true;
367 #if BUILDFLAG(IS_TIZEN_TV)
368 content::WebContentsDelegate* TizenRendererImpl::GetWebContentsDelegate()
370 content::WebContents* web_contents = GetWebContents();
372 LOG(ERROR) << "web_contents is nullptr";
375 return web_contents->GetDelegate();
379 void TizenRendererImpl::ToggleFullscreenMode(bool is_fullscreen,
380 ToggledFullscreenCB cb) {
382 media_player_->ToggleFullscreenMode(is_fullscreen);
386 void TizenRendererImpl::StartPlayingFrom(base::TimeDelta time) {
387 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
388 << " time : " << time.InMicroseconds();
389 DCHECK(media_player_);
390 DCHECK(task_runner_->BelongsToCurrentThread());
391 TRACE_EVENT1("media", "TizenRendererImpl::StartPlayingFrom", "time_us",
392 time.InMicroseconds());
394 SetPlayerInitialize();
397 state_ = STATE_INITIALIZING;
398 media_player_->Seek(time, base::OnceClosure());
399 state_ = STATE_PLAYING;
402 void TizenRendererImpl::SetPlaybackRate(double playback_rate) {
403 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__ << " "
404 << playback_rate << "/ " << playback_rate_;
405 DCHECK(task_runner_->BelongsToCurrentThread());
406 TRACE_EVENT1("media", "TizenRendererImpl::SetPlaybackRate", "rate",
409 if (playback_rate_ == playback_rate)
412 playback_rate_ = playback_rate;
414 if (playback_rate > 0) {
415 media_player_->SetRate(playback_rate_);
416 media_player_->Play();
418 media_player_->Pause();
422 void TizenRendererImpl::SetVolume(float volume) {
423 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
424 DCHECK(task_runner_->BelongsToCurrentThread());
425 if (volume_ == volume)
430 // |SetVolume| could be called before initializing.
435 void TizenRendererImpl::SetPlayerVolume() {
436 media_player_->SetVolume(volume_);
439 base::TimeDelta TizenRendererImpl::GetMediaTime() {
440 return media_player_->GetCurrentTime();
443 media::RendererType TizenRendererImpl::GetRendererType() {
444 return media::RendererType::kMediaPlayer;
447 void TizenRendererImpl::OnSelectedVideoTracksChanged(
448 const std::vector<media::DemuxerStream*>& enabled_tracks,
449 base::OnceClosure change_completed_cb) {
453 void TizenRendererImpl::OnEnabledAudioTracksChanged(
454 const std::vector<media::DemuxerStream*>& enabled_tracks,
455 base::OnceClosure change_completed_cb) {
459 void TizenRendererImpl::OnWebContentsDestroyed() {
460 web_contents_observer_ = nullptr;
463 void TizenRendererImpl::OnEnded() {
464 playback_rate_ = 0.0;
468 void TizenRendererImpl::OnError(media::PipelineStatus error) {
469 playback_rate_ = 0.0;
470 state_ = STATE_ERROR;
471 client_->OnError(error);
474 void TizenRendererImpl::OnStatisticsUpdate(
475 const media::PipelineStatistics& stats) {
478 #if BUILDFLAG(IS_TIZEN_TV)
479 void TizenRendererImpl::SetContentMimeType(const std::string& mime_type) {
480 mime_type_ = mime_type;
482 media_player_->SetContentMimeType(mime_type);
485 void TizenRendererImpl::SetParentalRatingResult(bool is_pass) {
487 media_player_->SetParentalRatingResult(is_pass);
489 LOG(ERROR) << "media_player_ is null";
492 bool TizenRendererImpl::PlaybackNotificationEnabled() {
493 content::WebContents* web_contents = GetWebContents();
495 LOG(ERROR) << "web_contents is nullptr";
498 blink::web_pref::WebPreferences web_preference =
499 web_contents->GetOrCreateWebPreferences();
500 bool enable = web_preference.media_playback_notification_enabled;
501 LOG(INFO) << "media_playback_notification_enabled:" << enable;
505 void TizenRendererImpl::NotifyPlaybackState(int state,
507 const std::string& url,
508 const std::string& mime_type,
509 bool* media_resource_acquired,
510 std::string* translated_url,
511 std::string* drm_info) {
512 if (!PlaybackNotificationEnabled())
514 content::WebContentsDelegate* web_contents_delegate =
515 GetWebContentsDelegate();
516 if (!web_contents_delegate) {
517 LOG(ERROR) << "GetWebContentsDelegate failed";
521 if (notify_playback_state_ < media::kPlaybackReady &&
522 state == media::kPlaybackStop) {
523 LOG(ERROR) << "player not Ready but notify Stop";
526 notify_playback_state_ = state;
527 web_contents_delegate->NotifyPlaybackState(state, player_id, url, mime_type,
528 media_resource_acquired,
529 translated_url, drm_info);
532 void TizenRendererImpl::OnLivePlaybackComplete() {
534 LOG(ERROR) << "client is not exist";
537 client_->OnLivePlaybackComplete();
541 void TizenRendererImpl::OnSeekableTimeChange(base::TimeDelta min_time,
542 base::TimeDelta max_time,
544 client_->OnSeekableTimeChange(min_time, max_time, is_live);
547 void TizenRendererImpl::OnBufferingStateChange(
548 media::BufferingState new_buffering_state,
549 media::BufferingStateChangeReason reason) {
550 client_->OnBufferingStateChange(new_buffering_state, reason);
553 void TizenRendererImpl::OnWaiting(media::WaitingReason reason) {
554 client_->OnWaiting(reason);
557 void TizenRendererImpl::OnAudioConfigChange(
558 const media::AudioDecoderConfig& config) {
559 DCHECK(task_runner_->BelongsToCurrentThread());
560 client_->OnAudioConfigChange(config);
563 void TizenRendererImpl::OnVideoConfigChange(
564 const media::VideoDecoderConfig& config) {
565 DCHECK(task_runner_->BelongsToCurrentThread());
566 client_->OnVideoConfigChange(config);
569 void TizenRendererImpl::OnVideoNaturalSizeChange(const gfx::Size& size) {
570 client_->OnVideoNaturalSizeChange(size);
573 void TizenRendererImpl::OnVideoOpacityChange(bool opaque) {
577 void TizenRendererImpl::OnVideoFrameRateChange(absl::optional<int> fps) {
581 void TizenRendererImpl::InitiateScopedSurfaceRequest(
582 InitiateScopedSurfaceRequestCallback callback) {
586 void TizenRendererImpl::OnVideoSizeChange(const gfx::Size& size) {
587 client_extension_->OnVideoSizeChange(size);
590 void TizenRendererImpl::OnDurationChange(base::TimeDelta duration) {
591 client_extension_->OnDurationChange(duration);
594 void TizenRendererImpl::OnBufferUpdate(base::TimeDelta time) {
595 client_extension_->OnBufferUpdate(time);
598 void TizenRendererImpl::OnRequestSeek(base::TimeDelta time) {
599 client_->OnRequestSeek(time);
602 void TizenRendererImpl::OnRequestSuspend(bool resource_conflicted) {
603 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
606 LOG(INFO) << " Media is already suspended.";
610 resource_conflicted_ = resource_conflicted;
611 media::MediaPlayerRegistry::GetInstance()->DeactivateMediaPlayer(
612 player_id_, !resource_conflicted_);
613 is_suspended_ = true;
614 client_->OnRequestSuspend(resource_conflicted);
617 #if defined(TIZEN_TBM_SUPPORT)
618 void TizenRendererImpl::OnNewTbmFrameAvailable(uint32_t player_id,
619 gfx::TbmBufferHandle tbm_handle,
620 base::TimeDelta timestamp) {
621 client_extension_->OnNewTbmFrameAvailable(player_id, tbm_handle, timestamp);
624 void TizenRendererImpl::OnTbmBufferExhausted(
625 const gfx::TbmBufferHandle& tbm_handle) {
626 media_player_->DestroyMediaPacket(
627 reinterpret_cast<void*>(tbm_handle.media_packet));
630 void TizenRendererImpl::OnNewFrameAvailable(
632 base::UnsafeSharedMemoryRegion frame,
634 base::TimeDelta timestamp,
637 client_extension_->OnNewFrameAvailable(playerId, std::move(frame), size,
638 timestamp, width, height);
642 } // namespace content