1 // Copyright 2023 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 "media/filters/media_player_bridge_capi_tv.h"
7 #include "base/strings/string_split.h"
8 #include "media/base/efl/media_player_util_efl.h"
9 #include "tizen_src/chromium_impl/media/filters/media_player_tizen_client.h"
12 const int kSeekableTimeUpdateInterval = 500;
15 static void PlayerPreLoadingCb(void* data) {
17 LOG(ERROR) << "input data is null";
20 media::MediaPlayerBridgeCapiTV* player =
21 static_cast<media::MediaPlayerBridgeCapiTV*>(data);
22 player->OnPlayerPreloading();
25 static void DrmErrorCb(int err_code, char* err_str, void* data) {
27 media::MediaPlayerBridgeCapiTV* player =
28 static_cast<media::MediaPlayerBridgeCapiTV*>(data);
30 player->OnDrmError(err_code, err_str);
35 MediaPlayerBridgeCapiTV::MediaPlayerBridgeCapiTV(const GURL& url,
36 const std::string& user_agent,
38 : MediaPlayerBridgeCapi(url, user_agent, volume), weak_factory_(this) {
39 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
42 MediaPlayerBridgeCapiTV::~MediaPlayerBridgeCapiTV() {
43 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
44 weak_factory_.InvalidateWeakPtrs();
47 void MediaPlayerBridgeCapiTV::SetContentMimeType(const std::string& mime_type) {
48 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
49 << " mime_type : " << mime_type;
50 mime_type_ = mime_type;
53 void MediaPlayerBridgeCapiTV::Prepare() {
54 bool media_resource_acquired = false;
55 std::string translated_url;
58 if (GetMediaPlayerClient())
59 GetMediaPlayerClient()->NotifyPlaybackState(
60 kPlaybackLoad, player_id_, url_.spec(), mime_type_,
61 &media_resource_acquired, &translated_url, &drm_info);
63 LOG(INFO) << "media_resource_acquired: " << media_resource_acquired
64 << ",translated_url:" << translated_url
65 << ",drm_info: " << drm_info;
67 if (mime_type_.find("application/vnd.oipf.contentaccessstreaming+xml") !=
69 LOG(INFO) << "CASD url,waiting for hbbtv parse the real url and set "
74 if (GetMediaPlayerClient() &&
75 GetMediaPlayerClient()->PlaybackNotificationEnabled() &&
77 if (!SetDrmInfo(drm_info)) {
78 LOG(ERROR) << "SetDrmInfo failed";
81 if (!translated_url.empty())
82 url_ = media::GetCleanURL(translated_url);
85 if (url_.spec().find(".mpd") != std::string::npos)
86 stream_type_ = DASH_STREAM;
87 else if (url_.spec().find(".m3u") != std::string::npos)
88 stream_type_ = HLS_STREAM;
89 else if (mime_type_.find("application/dash+xml") != std::string::npos) {
90 char steaming_type_dash[] = "DASH";
91 char steaming_type_dash_ex[] = "DASHEX";
92 player_set_streaming_type(
93 player_, blink::IsHbbTV() ? steaming_type_dash_ex : steaming_type_dash);
94 stream_type_ = DASH_STREAM;
97 if (blink::IsHbbTV() && CheckHighBitRate() && stream_type_ == DASH_STREAM)
98 AppendUrlHighBitRate(url_.spec());
99 player_prepared_ = false;
101 parental_rating_pass_ = true;
102 MediaPlayerBridgeCapi::Prepare();
103 if (GetMediaPlayerClient())
104 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackReady, player_id_);
107 void MediaPlayerBridgeCapiTV::Release() {
108 is_preloaded_ = false;
109 player_prepared_ = false;
110 StopSeekableTimeUpdateTimer();
111 MediaPlayerBridgeCapi::Release();
112 if (GetMediaPlayerClient())
113 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStop, player_id_);
116 bool MediaPlayerBridgeCapiTV::Play() {
117 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
118 << ",current_time:" << GetCurrentTime().InSecondsF();
120 // HBBTV run in preloading and preplay mode. If no prepread do prepare
121 // in PlayerPrePlay(), otherwise do normal Play.
122 if (blink::IsHbbTV() && !player_prepared_) {
123 if (!PlayerPrePlay())
124 LOG(ERROR) << "HBBTV prePlay fail.";
128 if (blink::IsHbbTV() && !parental_rating_pass_) {
129 LOG(INFO) << "parental rating authenticatoin is not pass yet,waiting...";
130 delayed_player_state_ = PLAYER_STATE_DELAYED_PLAY;
131 MediaPlayerBridgeCapi::ExecuteDelayedPlayerState();
135 if (!MediaPlayerBridgeCapi::Play())
137 if (GetMediaPlayerClient())
138 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStart, player_id_);
142 void MediaPlayerBridgeCapiTV::Pause(bool is_media_related_action) {
143 if (!player_prepared_) {
144 LOG(INFO) << "(" << static_cast<void*>(this)
145 << "), pause while player is not prepared, pause delay";
146 delayed_player_state_ = PLAYER_STATE_DELAYED_PAUSE;
149 MediaPlayerBridgeCapi::Pause(is_media_related_action);
152 void MediaPlayerBridgeCapiTV::PlaybackCompleteUpdate() {
153 MediaPlayerBridgeCapi::PlaybackCompleteUpdate();
154 if (GetMediaPlayerClient())
155 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackFinish, player_id_);
158 void MediaPlayerBridgeCapiTV::PlayerPrepared() {
159 player_prepared_ = true;
160 MediaPlayerBridgeCapi::PlayerPrepared();
163 int retVal = player_seek_available(player_, &canSeek);
164 if (retVal == PLAYER_ERROR_NONE && !canSeek)
165 is_player_seek_available_ = false;
167 GetAdaptiveStreamingInfo();
168 if (!blink::IsHbbTV() && is_live_stream_) {
169 UpdateSeekableTime();
170 StartSeekableTimeUpdateTimer();
174 void MediaPlayerBridgeCapiTV::StartSeekableTimeUpdateTimer() {
175 if (!seekable_time_update_timer_.IsRunning()) {
176 seekable_time_update_timer_.Start(
177 FROM_HERE, base::Milliseconds(kSeekableTimeUpdateInterval), this,
178 &MediaPlayerBridgeCapiTV::OnSeekableTimeUpdateTimerFired);
182 void MediaPlayerBridgeCapiTV::StopSeekableTimeUpdateTimer() {
183 if (seekable_time_update_timer_.IsRunning())
184 seekable_time_update_timer_.Stop();
187 void MediaPlayerBridgeCapiTV::OnSeekableTimeUpdateTimerFired() {
188 UpdateSeekableTime();
191 void MediaPlayerBridgeCapiTV::GetAdaptiveStreamingInfo() {
192 int ret = player_get_adaptive_streaming_info(player_, &is_live_stream_,
193 PLAYER_ADAPTIVE_INFO_IS_LIVE);
194 if (ret != PLAYER_ERROR_NONE || is_live_stream_) {
195 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
196 << " A live stream.";
200 void MediaPlayerBridgeCapiTV::UpdateSeekableTime() {
201 if (GetPlayerState() == PLAYER_STATE_NONE || !is_live_stream_)
204 int64_t min = 0, max = 0;
205 if (GetLiveStreamingDuration(&min, &max)) {
206 if (min_seekable_time_.InMilliseconds() != min ||
207 max_seekable_time_.InMilliseconds() != max) {
208 min_seekable_time_ = base::Milliseconds(min);
209 max_seekable_time_ = base::Milliseconds(max);
210 if (GetMediaPlayerClient())
211 GetMediaPlayerClient()->OnSeekableTimeChange(
212 min_seekable_time_, max_seekable_time_, is_live_stream_);
218 GetAdaptiveStreamingInfo();
219 if (!is_live_stream_) {
220 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
221 << " changed to static stream";
222 if (GetMediaPlayerClient())
223 GetMediaPlayerClient()->OnSeekableTimeChange({}, {}, is_live_stream_);
224 MediaPlayerBridgeCapi::UpdateDuration();
228 bool MediaPlayerBridgeCapiTV::GetLiveStreamingDuration(int64_t* min,
232 constexpr const size_t kDurationBufferSize = 64;
233 char live_duration[kDurationBufferSize] = {
236 char* live_ptr = live_duration;
237 const auto err = player_get_adaptive_streaming_info(
238 player_, live_duration, PLAYER_ADAPTIVE_INFO_LIVE_DURATION);
239 if (err != PLAYER_ERROR_NONE) {
240 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
241 << " |player_get_adaptive_streaming_info| failed " << err;
245 const std::string duration(live_duration);
246 const std::string::size_type delimiter = duration.find('|');
247 if (delimiter == std::string::npos) {
248 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
249 << " Failed to find delimiter | in duration: " << duration;
253 const std::string duration_min = duration.substr(0, delimiter);
254 if (duration_min.empty()) {
255 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
256 << " Failed empty min sub str: " << duration << ", "
262 if (!base::StringToInt64(duration_min, &out_min)) {
263 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
264 << " Failed to get min from duration: " << duration;
268 const std::string duration_max = duration.substr(delimiter + 1);
269 if (duration_max.empty()) {
270 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
271 << " empty max sub str: " << duration << ", " << delimiter;
276 if (!base::StringToInt64(duration_max, &out_max)) {
277 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
278 << " Failed to get max from duration: " << duration;
287 void MediaPlayerBridgeCapiTV::AppendUrlHighBitRate(const std::string& url) {
288 // don't use url.append("|STARTBITRATE=HIGHEST") to get hbbtv url
289 // "|" will be replaced with "%7C"
290 const char high_bitrate[] = "|STARTBITRATE=HIGHEST";
291 int len = url.length() + strlen(high_bitrate) + 1;
292 char* str_url = new char[len];
297 memset(str_url, 0, len);
298 strncat(str_url, url.c_str(), url.length());
299 strncat(str_url, high_bitrate, strlen(high_bitrate));
300 hbbtv_url_ = str_url;
302 LOG(INFO) << "hbbtv url:" << hbbtv_url_.c_str();
305 bool MediaPlayerBridgeCapiTV::CheckHighBitRate() {
306 if (!GetMediaPlayerClient()) {
307 LOG(ERROR) << "MediaPlayerClient is null";
310 content::WebContentsDelegate* web_contents_delegate =
311 GetMediaPlayerClient()->GetWebContentsDelegate();
312 if (!web_contents_delegate) {
313 LOG(ERROR) << "get web_contents_delegate fail";
316 bool ret = web_contents_delegate->IsHighBitRate();
317 LOG(INFO) << "get high bit rate: " << std::boolalpha << ret;
321 void MediaPlayerBridgeCapiTV::HandleDownloadableFontInfo(
322 const std::string& scheme_id_uri,
323 const std::string& value,
324 const std::string& data,
326 content::WebContentsDelegate* web_contents_delegate =
327 GetMediaPlayerClient()->GetWebContentsDelegate();
328 if (!web_contents_delegate)
330 web_contents_delegate->NotifyDownloadableFontInfo(scheme_id_uri, value, data,
334 bool MediaPlayerBridgeCapiTV::PreloadIfNeeded(int& ret) {
335 LOG(INFO) << "PreloadIfNeeded, is_preloaded_ " << is_preloaded_;
336 if (blink::IsHbbTV() && !is_preloaded_) {
337 ret = player_preloading_async(player_, -1, PlayerPreLoadingCb, this);
341 // fixup : kPlaybackReady status need checked by HBBTVResourceAcquired() patch
342 if (GetMediaPlayerClient())
343 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackReady, player_id_);
345 LOG(ERROR) << "(" << static_cast<void*>(this)
346 << "), GetMediaPlayerClient return null";
347 SetDisplayAtPausedState();
351 void MediaPlayerBridgeCapiTV::SetDisplayAtPausedState() {
352 int ret = PLAYER_ERROR_NONE;
353 if (!pending_seek_duration_.is_zero())
354 ret = player_display_video_at_paused_state(player_, false);
356 ret = player_display_video_at_paused_state(player_, true);
358 if (ret != PLAYER_ERROR_NONE)
359 LOG(ERROR) << "player_display_video_at_paused_state() failed";
362 void MediaPlayerBridgeCapiTV::UpdateDuration() {
363 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
364 if (blink::IsHbbTV() && !is_preloaded_) {
365 LOG(INFO) << "HBBTV preload not finished, no need update duration. ";
368 MediaPlayerBridgeCapi::UpdateDuration();
371 void MediaPlayerBridgeCapiTV::UpdateMediaType() {
372 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
374 if (blink::IsHbbTV() && !is_preloaded_) {
375 LOG(INFO) << "HBBTV preload not finished, no need update media type. ";
378 MediaPlayerBridgeCapi::UpdateMediaType();
381 bool MediaPlayerBridgeCapiTV::CheckLiveStreaming() const {
383 int retVal = player_get_adaptive_streaming_info(player_, &isLive,
384 PLAYER_ADAPTIVE_INFO_IS_LIVE);
385 if (retVal != PLAYER_ERROR_NONE) {
386 LOG(ERROR) << "player_get_adaptive_streaming_info failed, error: "
394 LOG(INFO) << "This is a live streaming.";
398 void MediaPlayerBridgeCapiTV::OnPlayerPreloading() {
399 task_runner_->PostTask(
400 FROM_HERE, base::BindOnce(&MediaPlayerBridgeCapiTV::PlayerPreloaded,
401 weak_factory_.GetWeakPtr()));
404 bool MediaPlayerBridgeCapiTV::HBBTVResourceAcquired() {
405 bool media_resource_acquired = false;
406 if (!GetMediaPlayerClient()) {
407 LOG(ERROR) << "MediaPlayerTizenClient is nullptr";
411 GetMediaPlayerClient()->NotifyPlaybackState(
412 kPlaybackReady, player_id_, url_.spec(), mime_type_,
413 &media_resource_acquired, NULL, NULL);
414 if (!media_resource_acquired) {
415 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStop, player_id_);
416 LOG(ERROR) << "HBBTV media resource acquired failed";
418 return media_resource_acquired;
421 void MediaPlayerBridgeCapiTV::PlayerPreloaded() {
422 LOG(INFO) << "PlayerPreloaded,this: " << this
423 << ",player_prepared_:" << player_prepared_;
425 is_preloaded_ = true;
426 is_live_stream_ = CheckLiveStreaming();
427 // if (stream_type_ == DASH_STREAM)
429 if (is_live_stream_ && stream_type_ != OTHER_STREAM) {
430 // UpdateSeekableTime();
431 // StartSeekableTimeUpdateTimer();
433 // UpdateAudioTrackInfo();
434 // UpdateVideoTrackInfo();
435 // UpdateTextTrackInfo();
438 if (GetMediaType() == MediaType::Invalid) {
439 LOG(ERROR) << "Media type is not valid!";
443 OnReadyStateChange(player_id_,
444 blink::WebMediaPlayer::kReadyStateHaveFutureData);
445 OnNetworkStateChange(player_id_, blink::WebMediaPlayer::kNetworkStateLoading);
447 if (!player_prepared_ && HBBTVResourceAcquired()) {
448 int ret = SetPlayerPrepareAsync();
449 if (ret != PLAYER_ERROR_NONE) {
450 OnHandlePlayerError(ret, FROM_HERE);
455 bool MediaPlayerBridgeCapiTV::PlayerPrePlay() {
456 LOG(INFO) << "PlayerPrePlay, this: " << this
457 << ",is_player_prepared : " << player_prepared_;
458 if (IsPlayerSuspended()) {
459 LOG(INFO) << "PlayerPrePlay while player is suspended";
463 if (!HBBTVResourceAcquired()) {
465 << "PlayerPrePlay while it's not in case of HBBTV Resource Acquired";
469 delayed_player_state_ = PLAYER_STATE_DELAYED_PLAY;
470 LOG(INFO) << "begin to |player_prepare_async|";
472 SetDisplayAtPausedState();
473 int ret = SetPlayerPrepareAsync();
474 if (ret != PLAYER_ERROR_NONE) {
475 OnHandlePlayerError(ret, FROM_HERE);
481 void MediaPlayerBridgeCapiTV::Initialize(VideoRendererSink* sink) {
482 player_set_drm_error_cb(player_, DrmErrorCb, this);
483 MediaPlayerBridgeCapi::Initialize(sink);
486 bool MediaPlayerBridgeCapiTV::SetDrmInfo(std::string& drm_info) {
487 // The DRM info from HbbTV comes as a JSON-like string in the following
489 // pair = Property ':' Value
490 // string = pair {',' pair}
491 std::vector<std::string> drm_info_pairs = base::SplitStringUsingSubstr(
492 drm_info, ", ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
493 for (auto it = drm_info_pairs.begin(); it != drm_info_pairs.end(); ++it) {
494 std::vector<std::string> drm_info_pair = base::SplitStringUsingSubstr(
495 *it, ": ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
497 if (drm_info_pair.size() != 2)
500 std::string trim_key = drm_info_pair.at(0);
501 std::string trim_value = drm_info_pair.at(1);
502 LOG(INFO) << "trim_key: " << trim_key.c_str()
503 << ",trim_value: " << trim_value.c_str();
505 if (!SetMediaDRMInfo(trim_key, trim_value)) {
507 << "MediaPlayerBridgeCapiTV::SetDrmInfo SetMediaDRMInfo failed";
515 bool MediaPlayerBridgeCapiTV::SetMediaDRMInfo(const std::string& type_data,
516 const std::string& value_data) {
517 LOG(INFO) << "player_set_drm_info(type_length(" << type_data.length() << ") "
518 << " value_length(" << value_data.length() << ")) ";
519 int ret = PLAYER_ERROR_INVALID_OPERATION;
520 const void* type_data_ptr = type_data.c_str();
521 const void* value_data_ptr = value_data.c_str();
522 player_drm_type_e drm_type = PLAYER_DRM_TYPE_PLAYREADY;
523 ret = player_set_drm_info(player_, type_data_ptr, type_data.length(),
524 value_data_ptr, value_data.length(), drm_type);
525 if (ret != PLAYER_ERROR_NONE) {
526 MediaPlayerBridgeCapi::OnHandlePlayerError(ret, FROM_HERE);
533 void MediaPlayerBridgeCapiTV::OnDrmError(int err_code, char* err_str) {
534 LOG(ERROR) << "OnDrmError err_str[" << err_str << "]";
535 if (!task_runner_->BelongsToCurrentThread()) {
536 task_runner_->PostTask(
538 base::BindOnce(&MediaPlayerBridgeCapiTV::OnDrmError,
539 weak_factory_.GetWeakPtr(), err_code, err_str));
541 MediaPlayerBridgeCapi::OnHandlePlayerError(err_code, FROM_HERE);
544 void MediaPlayerBridgeCapiTV::HandleParentalRatingInfo(const std::string& info,
545 const std::string& url) {
546 content::WebContentsDelegate* web_contents_delegate =
547 GetMediaPlayerClient()->GetWebContentsDelegate();
548 if (!web_contents_delegate){
549 LOG(ERROR) << "web_contents_delegate is null";
552 web_contents_delegate->NotifyParentalRatingInfo(info, url);
555 void MediaPlayerBridgeCapiTV::SetParentalRatingResult(bool is_pass) {
556 LOG(INFO) << "ParentalRatingResult:" << std::boolalpha << is_pass;
557 parental_rating_pass_ = is_pass;
559 // if authentication fail, raise MEDIA_ERROR_DECODE
560 if (!parental_rating_pass_) {
561 OnHandlePlayerError(PLAYER_ERROR_INVALID_OPERATION, FROM_HERE);
565 if (player_prepared_) {
566 LOG(INFO) << "player already prepared,execute play";
567 ExecuteDelayedPlayerState();