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"
11 #include <jsoncpp/json/json.h>
14 const int kSeekableTimeUpdateInterval = 500;
17 static void PlayerPreLoadingCb(void* data) {
19 LOG(ERROR) << "input data is null";
22 media::MediaPlayerBridgeCapiTV* player =
23 static_cast<media::MediaPlayerBridgeCapiTV*>(data);
24 player->OnPlayerPreloading();
27 static void DrmErrorCb(int err_code, char* err_str, void* data) {
29 media::MediaPlayerBridgeCapiTV* player =
30 static_cast<media::MediaPlayerBridgeCapiTV*>(data);
32 player->OnDrmError(err_code, err_str);
37 MediaPlayerBridgeCapiTV::MediaPlayerBridgeCapiTV(const GURL& url,
38 const std::string& user_agent,
40 : MediaPlayerBridgeCapi(url, user_agent, volume), weak_factory_(this) {
41 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
44 MediaPlayerBridgeCapiTV::~MediaPlayerBridgeCapiTV() {
45 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
46 weak_factory_.InvalidateWeakPtrs();
49 void MediaPlayerBridgeCapiTV::SetContentMimeType(const std::string& mime_type) {
50 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
51 << " mime_type : " << mime_type;
52 mime_type_ = mime_type;
55 void MediaPlayerBridgeCapiTV::Prepare() {
56 bool media_resource_acquired = false;
57 std::string translated_url;
60 if (GetMediaPlayerClient())
61 GetMediaPlayerClient()->NotifyPlaybackState(
62 kPlaybackLoad, player_id_, url_.spec(), mime_type_,
63 &media_resource_acquired, &translated_url, &drm_info);
65 LOG(INFO) << "media_resource_acquired: " << media_resource_acquired
66 << ",translated_url:" << translated_url
67 << ",drm_info: " << drm_info;
69 if (mime_type_.find("application/vnd.oipf.contentaccessstreaming+xml") !=
71 LOG(INFO) << "CASD url,waiting for hbbtv parse the real url and set "
76 if (GetMediaPlayerClient() &&
77 GetMediaPlayerClient()->PlaybackNotificationEnabled() &&
79 if (!SetDrmInfo(drm_info)) {
80 LOG(ERROR) << "SetDrmInfo failed";
83 if (!translated_url.empty())
84 url_ = media::GetCleanURL(translated_url);
87 if (url_.spec().find(".mpd") != std::string::npos)
88 stream_type_ = DASH_STREAM;
89 else if (url_.spec().find(".m3u") != std::string::npos)
90 stream_type_ = HLS_STREAM;
91 else if (mime_type_.find("application/dash+xml") != std::string::npos) {
92 char steaming_type_dash[] = "DASH";
93 char steaming_type_dash_ex[] = "DASHEX";
94 player_set_streaming_type(
95 player_, blink::IsHbbTV() ? steaming_type_dash_ex : steaming_type_dash);
96 stream_type_ = DASH_STREAM;
99 if (blink::IsHbbTV() && CheckHighBitRate() && stream_type_ == DASH_STREAM)
100 AppendUrlHighBitRate(url_.spec());
101 player_prepared_ = false;
103 parental_rating_pass_ = true;
104 MediaPlayerBridgeCapi::Prepare();
105 if (GetMediaPlayerClient())
106 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackReady, player_id_);
109 void MediaPlayerBridgeCapiTV::Release() {
110 is_preloaded_ = false;
111 player_prepared_ = false;
112 StopSeekableTimeUpdateTimer();
113 MediaPlayerBridgeCapi::Release();
114 if (GetMediaPlayerClient())
115 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStop, player_id_);
118 bool MediaPlayerBridgeCapiTV::Play() {
119 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
120 << ",current_time:" << GetCurrentTime().InSecondsF();
122 // HBBTV run in preloading and preplay mode. If no prepread do prepare
123 // in PlayerPrePlay(), otherwise do normal Play.
124 if (blink::IsHbbTV() && !player_prepared_) {
125 if (!PlayerPrePlay())
126 LOG(ERROR) << "HBBTV prePlay fail.";
130 if (blink::IsHbbTV() && !parental_rating_pass_) {
131 LOG(INFO) << "parental rating authenticatoin is not pass yet,waiting...";
132 delayed_player_state_ = PLAYER_STATE_DELAYED_PLAY;
133 MediaPlayerBridgeCapi::ExecuteDelayedPlayerState();
137 if (!MediaPlayerBridgeCapi::Play())
139 if (GetMediaPlayerClient())
140 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStart, player_id_);
144 void MediaPlayerBridgeCapiTV::Pause(bool is_media_related_action) {
145 if (!player_prepared_) {
146 LOG(INFO) << "(" << static_cast<void*>(this)
147 << "), pause while player is not prepared, pause delay";
148 delayed_player_state_ = PLAYER_STATE_DELAYED_PAUSE;
151 MediaPlayerBridgeCapi::Pause(is_media_related_action);
154 void MediaPlayerBridgeCapiTV::PlaybackCompleteUpdate() {
155 MediaPlayerBridgeCapi::PlaybackCompleteUpdate();
156 if (GetMediaPlayerClient()) {
157 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackFinish, player_id_);
159 GetMediaPlayerClient()->OnLivePlaybackComplete();
163 void MediaPlayerBridgeCapiTV::PlayerPrepared() {
164 player_prepared_ = true;
165 MediaPlayerBridgeCapi::PlayerPrepared();
168 int retVal = player_seek_available(player_, &canSeek);
169 if (retVal == PLAYER_ERROR_NONE && !canSeek)
170 is_player_seek_available_ = false;
172 GetAdaptiveStreamingInfo();
173 if (!blink::IsHbbTV() && is_live_stream_) {
174 UpdateSeekableTime();
175 StartSeekableTimeUpdateTimer();
179 void MediaPlayerBridgeCapiTV::StartSeekableTimeUpdateTimer() {
180 if (!seekable_time_update_timer_.IsRunning()) {
181 seekable_time_update_timer_.Start(
182 FROM_HERE, base::Milliseconds(kSeekableTimeUpdateInterval), this,
183 &MediaPlayerBridgeCapiTV::OnSeekableTimeUpdateTimerFired);
187 void MediaPlayerBridgeCapiTV::StopSeekableTimeUpdateTimer() {
188 if (seekable_time_update_timer_.IsRunning())
189 seekable_time_update_timer_.Stop();
192 void MediaPlayerBridgeCapiTV::OnSeekableTimeUpdateTimerFired() {
193 UpdateSeekableTime();
196 void MediaPlayerBridgeCapiTV::GetAdaptiveStreamingInfo() {
197 int ret = player_get_adaptive_streaming_info(player_, &is_live_stream_,
198 PLAYER_ADAPTIVE_INFO_IS_LIVE);
199 if (ret == PLAYER_ERROR_NONE && is_live_stream_) {
200 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
201 << " A live stream.";
205 void MediaPlayerBridgeCapiTV::UpdateSeekableTime() {
206 if (GetPlayerState() == PLAYER_STATE_NONE || !is_live_stream_)
209 int64_t min = 0, max = 0;
210 if (GetLiveStreamingDuration(&min, &max)) {
211 if (min_seekable_time_.InMilliseconds() != min ||
212 max_seekable_time_.InMilliseconds() != max) {
213 min_seekable_time_ = base::Milliseconds(min);
214 max_seekable_time_ = base::Milliseconds(max);
215 if (GetMediaPlayerClient())
216 GetMediaPlayerClient()->OnSeekableTimeChange(
217 min_seekable_time_, max_seekable_time_, is_live_stream_);
223 GetAdaptiveStreamingInfo();
224 if (!is_live_stream_) {
225 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
226 << " changed to static stream";
227 if (GetMediaPlayerClient())
228 GetMediaPlayerClient()->OnSeekableTimeChange({}, {}, is_live_stream_);
229 MediaPlayerBridgeCapi::UpdateDuration();
233 bool MediaPlayerBridgeCapiTV::GetLiveStreamingDuration(int64_t* min,
237 constexpr const size_t kDurationBufferSize = 64;
238 char live_duration[kDurationBufferSize] = {
241 char* live_ptr = live_duration;
242 const auto err = player_get_adaptive_streaming_info(
243 player_, &live_ptr, PLAYER_ADAPTIVE_INFO_LIVE_DURATION);
244 if (err != PLAYER_ERROR_NONE) {
245 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
246 << " |player_get_adaptive_streaming_info| failed " << err;
250 const std::string duration(live_duration);
251 const std::string::size_type delimiter = duration.find('|');
252 if (delimiter == std::string::npos) {
253 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
254 << " Failed to find delimiter | in duration: " << duration;
258 const std::string duration_min = duration.substr(0, delimiter);
259 if (duration_min.empty()) {
260 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
261 << " Failed empty min sub str: " << duration << ", "
267 if (!base::StringToInt64(duration_min, &out_min)) {
268 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
269 << " Failed to get min from duration: " << duration;
273 const std::string duration_max = duration.substr(delimiter + 1);
274 if (duration_max.empty()) {
275 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
276 << " empty max sub str: " << duration << ", " << delimiter;
281 if (!base::StringToInt64(duration_max, &out_max)) {
282 LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
283 << " Failed to get max from duration: " << duration;
292 void MediaPlayerBridgeCapiTV::ParseDashInfo() {
293 std::string dash_info = GetDashInfo();
294 Json::CharReaderBuilder builder;
295 Json::CharReader* reader(builder.newCharReader());
298 if (reader->parse(dash_info.c_str(), dash_info.c_str() + dash_info.length(),
300 mrs_url_ = value["mrsUrl"].asString();
301 std::string periodId = value["periodId"].asString();
303 content_id_ = clean_url_ + "#period=" + periodId;
305 LOG(INFO) << "mrsUrl: " << mrs_url_ << ",contentId: " << content_id_;
308 std::string MediaPlayerBridgeCapiTV::GetDashInfo() {
309 char* dash_info = nullptr;
311 // {"type":0,"minBufferTime":4000,"mrsUrl":"","periodId":"first"}
312 const int ret = player_get_dash_info(player_, &dash_info);
313 if (ret != PLAYER_ERROR_NONE) {
316 LOG(ERROR) << "Fail to call player_get_dash_info,ret:" << ret;
321 LOG(ERROR) << "empty dash_info.";
325 const std::string info(dash_info);
327 LOG(INFO) << "dash info str: " << info;
332 bool MediaPlayerBridgeCapiTV::GetDashLiveDuration(int64_t* duration) {
334 std::string dash_info = GetDashInfo();
335 Json::CharReaderBuilder builder;
336 Json::CharReader* reader(builder.newCharReader());
338 int64_t out_duration = 0;
340 if (reader->parse(dash_info.c_str(), dash_info.c_str() + dash_info.length(),
342 out_duration = value["mediaPresentationDuration"].asInt();
345 // According to dash spec, if duration == -1, set max time as duration.
346 if (out_duration == -1)
347 out_duration = base::TimeDelta::Max().InMilliseconds();
348 *duration = out_duration;
353 void MediaPlayerBridgeCapiTV::AppendUrlHighBitRate(const std::string& url) {
354 // don't use url.append("|STARTBITRATE=HIGHEST") to get hbbtv url
355 // "|" will be replaced with "%7C"
356 const char high_bitrate[] = "|STARTBITRATE=HIGHEST";
357 int len = url.length() + strlen(high_bitrate) + 1;
358 char* str_url = new char[len];
363 memset(str_url, 0, len);
364 strncat(str_url, url.c_str(), url.length());
365 strncat(str_url, high_bitrate, strlen(high_bitrate));
366 hbbtv_url_ = str_url;
368 LOG(INFO) << "hbbtv url:" << hbbtv_url_.c_str();
371 bool MediaPlayerBridgeCapiTV::CheckHighBitRate() {
372 if (!GetMediaPlayerClient()) {
373 LOG(ERROR) << "MediaPlayerClient is null";
376 content::WebContentsDelegate* web_contents_delegate =
377 GetMediaPlayerClient()->GetWebContentsDelegate();
378 if (!web_contents_delegate) {
379 LOG(ERROR) << "get web_contents_delegate fail";
382 bool ret = web_contents_delegate->IsHighBitRate();
383 LOG(INFO) << "get high bit rate: " << std::boolalpha << ret;
387 void MediaPlayerBridgeCapiTV::HandleDownloadableFontInfo(
388 const std::string& scheme_id_uri,
389 const std::string& value,
390 const std::string& data,
392 content::WebContentsDelegate* web_contents_delegate =
393 GetMediaPlayerClient()->GetWebContentsDelegate();
394 if (!web_contents_delegate)
396 web_contents_delegate->NotifyDownloadableFontInfo(scheme_id_uri, value, data,
400 bool MediaPlayerBridgeCapiTV::PreloadIfNeeded(int& ret) {
401 LOG(INFO) << "PreloadIfNeeded, is_preloaded_ " << is_preloaded_;
402 if (blink::IsHbbTV() && !is_preloaded_) {
403 ret = player_preloading_async(player_, -1, PlayerPreLoadingCb, this);
407 // fixup : kPlaybackReady status need checked by HBBTVResourceAcquired() patch
408 if (GetMediaPlayerClient())
409 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackReady, player_id_);
411 LOG(ERROR) << "(" << static_cast<void*>(this)
412 << "), GetMediaPlayerClient return null";
413 SetDisplayAtPausedState();
417 void MediaPlayerBridgeCapiTV::SetDisplayAtPausedState() {
418 int ret = PLAYER_ERROR_NONE;
419 if (!pending_seek_duration_.is_zero())
420 ret = player_display_video_at_paused_state(player_, false);
422 ret = player_display_video_at_paused_state(player_, true);
424 if (ret != PLAYER_ERROR_NONE)
425 LOG(ERROR) << "player_display_video_at_paused_state() failed";
428 void MediaPlayerBridgeCapiTV::UpdateDuration() {
429 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
430 if (blink::IsHbbTV() && !is_preloaded_) {
431 LOG(INFO) << "HBBTV preload not finished, no need update duration. ";
434 if (is_live_stream_) {
435 int64_t duration = 0;
436 if (stream_type_ == HLS_STREAM) {
438 if (!GetLiveStreamingDuration(&min, &duration)) {
439 LOG(ERROR) << "Fail to get duration for hls live stream.";
442 } else if (stream_type_ == DASH_STREAM) {
443 if (!GetDashLiveDuration(&duration)) {
444 if (duration_ != base::TimeDelta::Max()) {
445 duration_ = base::TimeDelta::Max();
446 OnDurationChange(player_id_, duration_.InSecondsF());
451 LOG(ERROR) << "Unknown live stream type : " << stream_type_;
455 LOG(WARNING) << "live stream type : convert duration to max";
456 duration_ = base::TimeDelta::Max();
457 OnDurationChange(player_id_, duration_.InSecondsF());
458 } else if (duration_.InSecondsF() !=
459 ConvertMilliSecondsToSeconds(duration)) {
460 duration_ = base::Milliseconds(duration);
461 OnDurationChange(player_id_, duration_.InSecondsF());
463 } else { // non-live stream sequence
464 MediaPlayerBridgeCapi::UpdateDuration();
468 void MediaPlayerBridgeCapiTV::UpdateMediaType() {
469 LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
471 if (blink::IsHbbTV() && !is_preloaded_) {
472 LOG(INFO) << "HBBTV preload not finished, no need update media type. ";
475 MediaPlayerBridgeCapi::UpdateMediaType();
478 bool MediaPlayerBridgeCapiTV::CheckLiveStreaming() const {
480 int retVal = player_get_adaptive_streaming_info(player_, &isLive,
481 PLAYER_ADAPTIVE_INFO_IS_LIVE);
482 if (retVal != PLAYER_ERROR_NONE) {
483 LOG(ERROR) << "player_get_adaptive_streaming_info failed, error: "
491 LOG(INFO) << "This is a live streaming.";
495 void MediaPlayerBridgeCapiTV::OnPlayerPreloading() {
496 task_runner_->PostTask(
497 FROM_HERE, base::BindOnce(&MediaPlayerBridgeCapiTV::PlayerPreloaded,
498 weak_factory_.GetWeakPtr()));
501 bool MediaPlayerBridgeCapiTV::HBBTVResourceAcquired() {
502 bool media_resource_acquired = false;
503 if (!GetMediaPlayerClient()) {
504 LOG(ERROR) << "MediaPlayerTizenClient is nullptr";
508 GetMediaPlayerClient()->NotifyPlaybackState(
509 kPlaybackReady, player_id_, url_.spec(), mime_type_,
510 &media_resource_acquired, NULL, NULL);
511 if (!media_resource_acquired) {
512 GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackStop, player_id_);
513 LOG(ERROR) << "HBBTV media resource acquired failed";
515 return media_resource_acquired;
518 void MediaPlayerBridgeCapiTV::PlayerPreloaded() {
519 LOG(INFO) << "PlayerPreloaded,this: " << this
520 << ",player_prepared_:" << player_prepared_;
522 is_preloaded_ = true;
523 is_live_stream_ = CheckLiveStreaming();
524 if (stream_type_ == DASH_STREAM)
526 if (is_live_stream_ && stream_type_ != OTHER_STREAM) {
527 // UpdateSeekableTime();
528 // StartSeekableTimeUpdateTimer();
530 // UpdateAudioTrackInfo();
531 // UpdateVideoTrackInfo();
532 // UpdateTextTrackInfo();
535 if (GetMediaType() == MediaType::Invalid) {
536 LOG(ERROR) << "Media type is not valid!";
540 OnReadyStateChange(player_id_,
541 blink::WebMediaPlayer::kReadyStateHaveFutureData);
542 OnNetworkStateChange(player_id_, blink::WebMediaPlayer::kNetworkStateLoading);
544 if (!player_prepared_ && HBBTVResourceAcquired()) {
545 int ret = SetPlayerPrepareAsync();
546 if (ret != PLAYER_ERROR_NONE) {
547 OnHandlePlayerError(ret, FROM_HERE);
552 bool MediaPlayerBridgeCapiTV::PlayerPrePlay() {
553 LOG(INFO) << "PlayerPrePlay, this: " << this
554 << ",is_player_prepared : " << player_prepared_;
555 if (IsPlayerSuspended()) {
556 LOG(INFO) << "PlayerPrePlay while player is suspended";
560 if (!HBBTVResourceAcquired()) {
562 << "PlayerPrePlay while it's not in case of HBBTV Resource Acquired";
566 delayed_player_state_ = PLAYER_STATE_DELAYED_PLAY;
567 LOG(INFO) << "begin to |player_prepare_async|";
569 SetDisplayAtPausedState();
570 int ret = SetPlayerPrepareAsync();
571 if (ret != PLAYER_ERROR_NONE) {
572 OnHandlePlayerError(ret, FROM_HERE);
578 void MediaPlayerBridgeCapiTV::Initialize(VideoRendererSink* sink) {
579 player_set_drm_error_cb(player_, DrmErrorCb, this);
580 MediaPlayerBridgeCapi::Initialize(sink);
583 bool MediaPlayerBridgeCapiTV::SetDrmInfo(std::string& drm_info) {
584 // The DRM info from HbbTV comes as a JSON-like string in the following
586 // pair = Property ':' Value
587 // string = pair {',' pair}
588 std::vector<std::string> drm_info_pairs = base::SplitStringUsingSubstr(
589 drm_info, ", ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
590 for (auto it = drm_info_pairs.begin(); it != drm_info_pairs.end(); ++it) {
591 std::vector<std::string> drm_info_pair = base::SplitStringUsingSubstr(
592 *it, ": ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
594 if (drm_info_pair.size() != 2)
597 std::string trim_key = drm_info_pair.at(0);
598 std::string trim_value = drm_info_pair.at(1);
599 LOG(INFO) << "trim_key: " << trim_key.c_str()
600 << ",trim_value: " << trim_value.c_str();
602 if (!SetMediaDRMInfo(trim_key, trim_value)) {
604 << "MediaPlayerBridgeCapiTV::SetDrmInfo SetMediaDRMInfo failed";
612 bool MediaPlayerBridgeCapiTV::SetMediaDRMInfo(const std::string& type_data,
613 const std::string& value_data) {
614 LOG(INFO) << "player_set_drm_info(type_length(" << type_data.length() << ") "
615 << " value_length(" << value_data.length() << ")) ";
616 int ret = PLAYER_ERROR_INVALID_OPERATION;
617 const void* type_data_ptr = type_data.c_str();
618 const void* value_data_ptr = value_data.c_str();
619 player_drm_type_e drm_type = PLAYER_DRM_TYPE_PLAYREADY;
620 ret = player_set_drm_info(player_, type_data_ptr, type_data.length(),
621 value_data_ptr, value_data.length(), drm_type);
622 if (ret != PLAYER_ERROR_NONE) {
623 MediaPlayerBridgeCapi::OnHandlePlayerError(ret, FROM_HERE);
630 void MediaPlayerBridgeCapiTV::OnDrmError(int err_code, char* err_str) {
631 LOG(ERROR) << "OnDrmError err_str[" << err_str << "]";
632 if (!task_runner_->BelongsToCurrentThread()) {
633 task_runner_->PostTask(
635 base::BindOnce(&MediaPlayerBridgeCapiTV::OnDrmError,
636 weak_factory_.GetWeakPtr(), err_code, err_str));
638 MediaPlayerBridgeCapi::OnHandlePlayerError(err_code, FROM_HERE);
641 void MediaPlayerBridgeCapiTV::HandleParentalRatingInfo(const std::string& info,
642 const std::string& url) {
643 content::WebContentsDelegate* web_contents_delegate =
644 GetMediaPlayerClient()->GetWebContentsDelegate();
645 if (!web_contents_delegate){
646 LOG(ERROR) << "web_contents_delegate is null";
649 web_contents_delegate->NotifyParentalRatingInfo(info, url);
652 void MediaPlayerBridgeCapiTV::SetParentalRatingResult(bool is_pass) {
653 LOG(INFO) << "ParentalRatingResult:" << std::boolalpha << is_pass;
654 parental_rating_pass_ = is_pass;
656 // if authentication fail, raise MEDIA_ERROR_DECODE
657 if (!parental_rating_pass_) {
658 OnHandlePlayerError(PLAYER_ERROR_INVALID_OPERATION, FROM_HERE);
662 if (player_prepared_) {
663 LOG(INFO) << "player already prepared,execute play";
664 ExecuteDelayedPlayerState();