virtual void OnSeekableTimeChange(base::TimeDelta min_time,
base::TimeDelta max_time,
bool is_live) = 0;
+ virtual void OnLivePlaybackComplete() = 0;
#endif
};
void OnSeekableTimeChange(base::TimeDelta min_time,
base::TimeDelta max_time,
bool is_live) final;
+ void OnLivePlaybackComplete() final;
#endif
// Common handlers for notifications from renderers and demuxer.
FROM_HERE, base::BindOnce(&PipelineImpl::OnSeekableTimeChange,
weak_pipeline_, min_time, max_time, is_live));
}
+
+void PipelineImpl::RendererWrapper::OnLivePlaybackComplete() {
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&PipelineImpl::OnLivePlaybackComplete, weak_pipeline_));
+}
#endif
void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) {
DVLOG(3) << __func__;
client_->OnSeekableTimeChange(min_time, max_time, is_live);
}
+
+void PipelineImpl::OnLivePlaybackComplete() {
+ LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__;
+ client_->OnLivePlaybackComplete();
+}
#endif
void PipelineImpl::OnSeekDone(bool is_suspended) {
void OnSeekableTimeChange(base::TimeDelta min_time,
base::TimeDelta max_time,
bool is_live);
+ void OnLivePlaybackComplete();
#endif
// Task completion callbacks from RendererWrapper.
virtual void OnRequestSuspend(bool resource_conflicted) {}
virtual void OnRequestSeek(base::TimeDelta time) {}
+
+ virtual void OnLivePlaybackComplete() {}
#endif
};
std::move(seek_cb_).Run();
}
+
+void MojoRenderer::OnLivePlaybackComplete() {
+ DVLOG(1) << __func__;
+ client_->OnLivePlaybackComplete();
+}
#endif
void MojoRenderer::OnCdmAttached(bool success) {
void OnSeekableTimeChange(base::TimeDelta min_time,
base::TimeDelta max_time,
bool is_live) override;
+ void OnLivePlaybackComplete() override;
#endif
// Binds |remote_renderer_| to the mojo message pipe. Can be called multiple
OnSeekableTimeChange(mojo_base.mojom.TimeDelta min_time,
mojo_base.mojom.TimeDelta max_time,
bool is_live);
+
+ [EnableIf=tizen_multimedia]
+ OnLivePlaybackComplete();
};
<< "is_live " << is_live;
client_->OnSeekableTimeChange(min_time, max_time, is_live);
}
+
+void MojoRendererService::OnLivePlaybackComplete() {
+ DVLOG(2) << __func__;
+ client_->OnLivePlaybackComplete();
+}
#endif
void MojoRendererService::OnVideoFrameRateChange(absl::optional<int> fps) {
void OnSeekableTimeChange(base::TimeDelta min_time,
base::TimeDelta max_time,
bool is_live) final;
+ void OnLivePlaybackComplete() final;
#endif
// Called when the MediaResourceShim is ready to go (has a config,
virtual void Suspend() {}
virtual void Resume() {}
virtual bool SuspendedByPlayer() { return false; }
+ virtual void OnLivePlaybackComplete() {}
#endif
// Called when the backing media element and the page it is attached to is
#if defined(TIZEN_MULTIMEDIA)
virtual void SuspendPlayer() {}
+ virtual void OnLivePlaybackComplete() = 0;
#endif
#if BUILDFLAG(IS_TIZEN_TV)
is_deactivate_(false),
is_translated_url_(false),
#endif
+#if defined(TIZEN_MULTIMEDIA)
+ live_playback_complete_(false),
+#endif
audio_tracks_(MakeGarbageCollected<AudioTrackList>(*this)),
video_tracks_(MakeGarbageCollected<VideoTrackList>(*this)),
audio_source_node_(nullptr),
}
}
UpdatePlayState();
+
+#if defined(TIZEN_MULTIMEDIA)
+ live_playback_complete_ = false;
+#endif
}
void HTMLMediaElement::DurationChanged() {
SetShouldDelayLoadEvent(false);
SetNetworkState(kNetworkIdle);
}
+
+void HTMLMediaElement::OnLivePlaybackComplete() {
+ live_playback_complete_ = true;
+}
#endif
media::mojom::blink::MediaPlayerHost&
void SetParentalRatingResult(bool is_pass) override;
#endif
+#if defined(TIZEN_MULTIMEDIA)
+ void OnLivePlaybackComplete() override;
+#endif
+
bool HasMediaSource() const { return media_source_attachment_.get(); }
// Return true if element is paused and won't resume automatically if it
max_seekable_time_ = max_time;
}
+void WebMediaPlayerImpl::OnLivePlaybackComplete() {
+ LOG(INFO) << __func__;
+ if (!client_) {
+ LOG(ERROR) << __func__ << ", client is null";
+ return;
+ }
+ client_->OnLivePlaybackComplete();
+}
+
void WebMediaPlayerImpl::OnRequestSuspend(bool resource_conflicted) {
if (pipeline_controller_->IsSuspended()) {
LOG(INFO) << __func__ << " Already suspended.";
void OnSeekableTimeChange(base::TimeDelta min_time,
base::TimeDelta max_time,
bool is_live) override;
+ void OnLivePlaybackComplete() override;
// Called if a player in the browser process is suspended.
void OnRequestSuspend(bool resource_conflicted) override;
media_resource_acquired,
translated_url, drm_info);
}
+
+void TizenRendererImpl::OnLivePlaybackComplete() {
+ if (!client_) {
+ LOG(ERROR) << "client is not exist";
+ return;
+ }
+ client_->OnLivePlaybackComplete();
+}
#endif
void TizenRendererImpl::OnSeekableTimeChange(base::TimeDelta min_time,
bool* media_resource_acquired = NULL,
std::string* translated_url = NULL,
std::string* drm_info = NULL) override;
+ void OnLivePlaybackComplete() override;
#endif
#if defined(TIZEN_TBM_SUPPORT)
if (is_end_reached_) {
if (playback_rate_ < 0)
return base::TimeDelta();
- if (duration_.InSecondsF())
+ if (!is_live_stream_ && duration_.InSecondsF())
return duration_;
}
}
void MediaPlayerBridgeCapi::OnBufferingUpdateTimerFired() {
+ /* player_get_streaming_download_progress is not apply for live stream
+ here no need triggered*/
+ if (is_live_stream_)
+ return;
+
int start = 0, current = 0;
if (player_get_streaming_download_progress(player_, &start, ¤t) ==
PLAYER_ERROR_NONE) {
virtual void UpdateDuration();
GURL url_;
+ bool is_live_stream_{false};
int player_id_ = 0;
int delayed_player_state_;
player_h player_ = nullptr;
#include "media/base/efl/media_player_util_efl.h"
#include "tizen_src/chromium_impl/media/filters/media_player_tizen_client.h"
+#include <jsoncpp/json/json.h>
+
namespace {
const int kSeekableTimeUpdateInterval = 500;
}
void MediaPlayerBridgeCapiTV::PlaybackCompleteUpdate() {
MediaPlayerBridgeCapi::PlaybackCompleteUpdate();
- if (GetMediaPlayerClient())
+ if (GetMediaPlayerClient()) {
GetMediaPlayerClient()->NotifyPlaybackState(kPlaybackFinish, player_id_);
+ if (is_live_stream_)
+ GetMediaPlayerClient()->OnLivePlaybackComplete();
+ }
} // namespace media
void MediaPlayerBridgeCapiTV::PlayerPrepared() {
void MediaPlayerBridgeCapiTV::GetAdaptiveStreamingInfo() {
int ret = player_get_adaptive_streaming_info(player_, &is_live_stream_,
PLAYER_ADAPTIVE_INFO_IS_LIVE);
- if (ret != PLAYER_ERROR_NONE || is_live_stream_) {
+ if (ret == PLAYER_ERROR_NONE && is_live_stream_) {
LOG(INFO) << "(" << static_cast<void*>(this) << ") " << __func__
<< " A live stream.";
}
};
char* live_ptr = live_duration;
const auto err = player_get_adaptive_streaming_info(
- player_, live_duration, PLAYER_ADAPTIVE_INFO_LIVE_DURATION);
+ player_, &live_ptr, PLAYER_ADAPTIVE_INFO_LIVE_DURATION);
if (err != PLAYER_ERROR_NONE) {
LOG(ERROR) << "(" << static_cast<void*>(this) << ") " << __func__
<< " |player_get_adaptive_streaming_info| failed " << err;
return true;
}
+void MediaPlayerBridgeCapiTV::ParseDashInfo() {
+ std::string dash_info = GetDashInfo();
+ Json::CharReaderBuilder builder;
+ Json::CharReader* reader(builder.newCharReader());
+ Json::Value value;
+
+ if (reader->parse(dash_info.c_str(), dash_info.c_str() + dash_info.length(),
+ &value, nullptr)) {
+ mrs_url_ = value["mrsUrl"].asString();
+ std::string periodId = value["periodId"].asString();
+ if (periodId != "")
+ content_id_ = clean_url_ + "#period=" + periodId;
+ }
+ LOG(INFO) << "mrsUrl: " << mrs_url_ << ",contentId: " << content_id_;
+}
+
+std::string MediaPlayerBridgeCapiTV::GetDashInfo() {
+ char* dash_info = nullptr;
+ // dash_info format:
+ // {"type":0,"minBufferTime":4000,"mrsUrl":"","periodId":"first"}
+ const int ret = player_get_dash_info(player_, &dash_info);
+ if (ret != PLAYER_ERROR_NONE) {
+ if (dash_info)
+ free(dash_info);
+ LOG(ERROR) << "Fail to call player_get_dash_info,ret:" << ret;
+ return "";
+ }
+
+ if (!dash_info) {
+ LOG(ERROR) << "empty dash_info.";
+ return "";
+ }
+
+ const std::string info(dash_info);
+ free(dash_info);
+ LOG(INFO) << "dash info str: " << info;
+
+ return info;
+}
+
+bool MediaPlayerBridgeCapiTV::GetDashLiveDuration(int64_t* duration) {
+ DCHECK(duration);
+ std::string dash_info = GetDashInfo();
+ Json::CharReaderBuilder builder;
+ Json::CharReader* reader(builder.newCharReader());
+ Json::Value value;
+ int64_t out_duration = 0;
+
+ if (reader->parse(dash_info.c_str(), dash_info.c_str() + dash_info.length(),
+ &value, nullptr)) {
+ out_duration = value["mediaPresentationDuration"].asInt();
+ }
+
+ // According to dash spec, if duration == -1, set max time as duration.
+ if (out_duration == -1)
+ out_duration = base::TimeDelta::Max().InMilliseconds();
+ *duration = out_duration;
+
+ return true;
+}
+
void MediaPlayerBridgeCapiTV::AppendUrlHighBitRate(const std::string& url) {
// don't use url.append("|STARTBITRATE=HIGHEST") to get hbbtv url
// "|" will be replaced with "%7C"
LOG(INFO) << "HBBTV preload not finished, no need update duration. ";
return;
}
- MediaPlayerBridgeCapi::UpdateDuration();
+ if (is_live_stream_) {
+ int64_t duration = 0;
+ if (stream_type_ == HLS_STREAM) {
+ int64_t min = 0;
+ if (!GetLiveStreamingDuration(&min, &duration)) {
+ LOG(ERROR) << "Fail to get duration for hls live stream.";
+ return;
+ }
+ } else if (stream_type_ == DASH_STREAM) {
+ if (!GetDashLiveDuration(&duration)) {
+ if (duration_ != base::TimeDelta::Max()) {
+ duration_ = base::TimeDelta::Max();
+ OnDurationChange(player_id_, duration_.InSecondsF());
+ }
+ return;
+ }
+ } else {
+ LOG(ERROR) << "Unknown live stream type : " << stream_type_;
+ }
+
+ if (duration == 0) {
+ LOG(WARNING) << "live stream type : convert duration to max";
+ duration_ = base::TimeDelta::Max();
+ OnDurationChange(player_id_, duration_.InSecondsF());
+ } else if (duration_.InSecondsF() !=
+ ConvertMilliSecondsToSeconds(duration)) {
+ duration_ = base::Milliseconds(duration);
+ OnDurationChange(player_id_, duration_.InSecondsF());
+ }
+ } else { // non-live stream sequence
+ MediaPlayerBridgeCapi::UpdateDuration();
+ }
}
void MediaPlayerBridgeCapiTV::UpdateMediaType() {
is_preloaded_ = true;
is_live_stream_ = CheckLiveStreaming();
- // if (stream_type_ == DASH_STREAM)
- // ParseDashInfo();
+ if (stream_type_ == DASH_STREAM)
+ ParseDashInfo();
if (is_live_stream_ && stream_type_ != OTHER_STREAM) {
// UpdateSeekableTime();
// StartSeekableTimeUpdateTimer();
void OnSeekableTimeUpdateTimerFired();
void GetAdaptiveStreamingInfo();
void UpdateSeekableTime();
+ void ParseDashInfo();
bool GetLiveStreamingDuration(int64_t* min, int64_t* max);
+ bool GetDashLiveDuration(int64_t* duration);
+ std::string GetDashInfo();
StreamType stream_type_{OTHER_STREAM};
std::string hbbtv_url_{""}; // url_ + HIGHBITRATE(if mpd)
std::string mime_type_ = "";
- bool is_live_stream_ = false;
+ std::string clean_url_{""}; // Original url without suffix t/period
+ std::string mrs_url_{""};
+ std::string content_id_{""}; // clean_url + period,only report to APP
+
bool parental_rating_pass_{false};
base::TimeDelta min_seekable_time_{base::TimeDelta()};
bool* media_resource_acquired = NULL,
std::string* translated_url = NULL,
std::string* drm_info = NULL) = 0;
+ virtual void OnLivePlaybackComplete() = 0;
virtual content::WebContentsDelegate* GetWebContentsDelegate() const = 0;
#endif
};