From 95fe22fc145662f89b3f12c4217038c0cb929761 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Thu, 19 Sep 2024 09:26:22 +0900 Subject: [PATCH] [video_player] Live streaming content starts playing immediately when seekTo is called (#742) --- packages/video_player/CHANGELOG.md | 4 + packages/video_player/README.md | 2 +- packages/video_player/pubspec.yaml | 2 +- .../tizen/src/media_player_proxy.cc | 45 +++++++ .../tizen/src/media_player_proxy.h | 60 ++++++++++ .../video_player/tizen/src/video_player.cc | 111 +++++++++++++++--- .../video_player/tizen/src/video_player.h | 7 ++ 7 files changed, 213 insertions(+), 18 deletions(-) create mode 100644 packages/video_player/tizen/src/media_player_proxy.cc create mode 100644 packages/video_player/tizen/src/media_player_proxy.h diff --git a/packages/video_player/CHANGELOG.md b/packages/video_player/CHANGELOG.md index d9376a3aa..e9bf2c2b3 100644 --- a/packages/video_player/CHANGELOG.md +++ b/packages/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.2 + +* Live streaming content starts playing immediately when SeekTo() is called. + ## 2.5.1 * Update pigeon to 22.3.0. diff --git a/packages/video_player/README.md b/packages/video_player/README.md index ca0acd386..ce8c8861b 100644 --- a/packages/video_player/README.md +++ b/packages/video_player/README.md @@ -15,7 +15,7 @@ This package is not an _endorsed_ implementation of `video_player`. Therefore, y ```yaml dependencies: video_player: ^2.9.1 - video_player_tizen: ^2.5.1 + video_player_tizen: ^2.5.2 ``` Then you can import `video_player` in your Dart code: diff --git a/packages/video_player/pubspec.yaml b/packages/video_player/pubspec.yaml index 2eec82c41..657b4faed 100644 --- a/packages/video_player/pubspec.yaml +++ b/packages/video_player/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_tizen description: Tizen implementation of the video_player plugin. homepage: https://github.com/flutter-tizen/plugins repository: https://github.com/flutter-tizen/plugins/tree/master/packages/video_player -version: 2.5.1 +version: 2.5.2 environment: sdk: ">=3.3.0 <4.0.0" diff --git a/packages/video_player/tizen/src/media_player_proxy.cc b/packages/video_player/tizen/src/media_player_proxy.cc new file mode 100644 index 000000000..08128faa8 --- /dev/null +++ b/packages/video_player/tizen/src/media_player_proxy.cc @@ -0,0 +1,45 @@ +// Copyright 2024 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media_player_proxy.h" + +#include + +#include "log.h" + +typedef int (*FuncPlayerGetAdaptiveStreamingInfo)(player_h player, + void* adaptive_info, + int adaptive_type); + +MediaPlayerProxy::MediaPlayerProxy() { + media_player_handle_ = dlopen("libcapi-media-player.so.0", RTLD_LAZY); + if (media_player_handle_ == nullptr) { + LOG_ERROR("Failed to open media player."); + } +} + +MediaPlayerProxy::~MediaPlayerProxy() { + if (media_player_handle_) { + dlclose(media_player_handle_); + media_player_handle_ = nullptr; + } +} + +int MediaPlayerProxy::player_get_adaptive_streaming_info(player_h player, + void* adaptive_info, + int adaptive_type) { + if (!media_player_handle_) { + LOG_ERROR("media_player_handle_ not valid"); + return PLAYER_ERROR_NOT_AVAILABLE; + } + FuncPlayerGetAdaptiveStreamingInfo player_get_adaptive_streaming_info = + reinterpret_cast( + dlsym(media_player_handle_, "player_get_adaptive_streaming_info")); + if (!player_get_adaptive_streaming_info) { + LOG_ERROR("Fail to find player_get_adaptive_streaming_info."); + return PLAYER_ERROR_NOT_AVAILABLE; + } + return player_get_adaptive_streaming_info(player, adaptive_info, + adaptive_type); +} diff --git a/packages/video_player/tizen/src/media_player_proxy.h b/packages/video_player/tizen/src/media_player_proxy.h new file mode 100644 index 000000000..d50b48181 --- /dev/null +++ b/packages/video_player/tizen/src/media_player_proxy.h @@ -0,0 +1,60 @@ +// Copyright 2024 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_PLUGIN_MEDIA_PLAYER_PROXY_H_ +#define FLUTTER_PLUGIN_MEDIA_PLAYER_PROXY_H_ + +#include + +typedef enum { + PLAYER_ADAPTIVE_INFO_BITRATE_INIT, + PLAYER_ADAPTIVE_INFO_BITRATE_INIT_NUM, + PLAYER_ADAPTIVE_INFO_DURATION, + PLAYER_ADAPTIVE_INFO_LIVE_DURATION, + PLAYER_ADAPTIVE_INFO_AVAILABLE_BITRATES, + PLAYER_ADAPTIVE_INFO_RATE_RETURNED, + PLAYER_ADAPTIVE_INFO_CURRENT_BITRATE, + PLAYER_ADAPTIVE_INFO_GET_BUFFER_SIZE, + PLAYER_ADAPTIVE_INFO_FIXED_BITRATE, + PLAYER_ADAPTIVE_INFO_ADAPTIVE_BITRATE, + PLAYER_ADAPTIVE_INFO_MAX_BYTES, + PLAYER_ADAPTIVE_INFO_DRM_TYPE, + PLAYER_ADAPTIVE_INFO_RATE_REQUESTED, + PLAYER_ADAPTIVE_INFO_AUDIO_TRACK_REQUESTED, + PLAYER_ADAPTIVE_INFO_FORMAT, + PLAYER_ADAPTIVE_INFO_BLOCK, + PLAYER_ADAPTIVE_INFO_MIN_PERCENT, + PLAYER_ADAPTIVE_INFO_MIN_LATENCY, + PLAYER_ADAPTIVE_INFO_MAX_LATENCY, + PLAYER_ADAPTIVE_INFO_IS_LIVE, + PLAYER_ADAPTIVE_INFO_EMIT_SIGNAL, + PLAYER_ADAPTIVE_INFO_LOG_LEVEL, + PLAYER_ADAPTIVE_INFO_CURRENT_BANDWIDTH, + PLAYER_ADAPTIVE_INFO_URL_CUSTOM, + PLAYER_ADAPTIVE_INFO_GET_AUDIO_INFO, + PLAYER_ADAPTIVE_INFO_GET_VIDEO_INFO, + PLAYER_ADAPTIVE_INFO_GET_TEXT_INFO, + PLAYER_ADAPTIVE_INFO_RESUME_TIME, + PLAYER_ADAPTIVE_INFO_AUDIO_INDEX, + PLAYER_ADAPTIVE_INFO_PROXY_SETTING, + PLAYER_ADAPTIVE_INFO_ATSC3_L1_SERVER_TIME, + PLAYER_ADAPTIVE_INFO_VIDEO_FRAMES_DROPPED, + PLAYER_ADAPTIVE_INFO_STREAMING_IS_BUFFERING, + PLAYER_ADAPTIVE_INFO_PRESELECTION_ID, + PLAYER_ADAPTIVE_INFO_URI_TYPE +} player_adaptive_Info_e; + +class MediaPlayerProxy { + public: + MediaPlayerProxy(); + ~MediaPlayerProxy(); + + int player_get_adaptive_streaming_info(player_h player, void* adaptive_info, + int adaptive_type); + + private: + void* media_player_handle_ = nullptr; +}; + +#endif // FLUTTER_PLUGIN_MEDIA_PLAYER_PROXY_H_ diff --git a/packages/video_player/tizen/src/video_player.cc b/packages/video_player/tizen/src/video_player.cc index 53f3b35fe..a54a81697 100644 --- a/packages/video_player/tizen/src/video_player.cc +++ b/packages/video_player/tizen/src/video_player.cc @@ -9,6 +9,7 @@ #include #include +#include #include "log.h" #include "video_player_error.h" @@ -130,6 +131,8 @@ VideoPlayer::VideoPlayer(flutter::PluginRegistrar *plugin_registrar, gpu_surface_ = std::make_unique(); texture_id_ = texture_registrar->RegisterTexture(texture_variant_.get()); + media_player_proxy_ = std::make_unique(); + int ret = player_create(&player_); if (ret != PLAYER_ERROR_NONE) { throw VideoPlayerError("player_create failed", get_error_message(ret)); @@ -140,6 +143,7 @@ VideoPlayer::VideoPlayer(flutter::PluginRegistrar *plugin_registrar, player_destroy(player_); throw VideoPlayerError("player_set_uri failed", get_error_message(ret)); } + uri_ = uri; ret = player_set_display_visible(player_, true); if (ret != PLAYER_ERROR_NONE) { @@ -336,8 +340,15 @@ void VideoPlayer::SeekTo(int32_t position, SeekCompletedCallback callback) { player_set_play_position(player_, position, true, OnSeekCompleted, this); if (ret != PLAYER_ERROR_NONE) { on_seek_completed_ = nullptr; - throw VideoPlayerError("player_set_play_position failed", - get_error_message(ret)); + // TODO(jsuya):Live content does not provide a duration that allows + // SeekTo(), so we call Play() to start playback immediately from the + // current. + if (position == 0 && IsLive()) { + Play(); + } else { + throw VideoPlayerError("player_set_play_position failed", + get_error_message(ret)); + } } } @@ -436,16 +447,11 @@ void VideoPlayer::Initialize() { void VideoPlayer::SendInitialized() { if (!is_initialized_ && event_sink_) { - int duration = 0; - int ret = player_get_duration(player_, &duration); - if (ret != PLAYER_ERROR_NONE) { - SendError("player_get_duration failed", get_error_message(ret)); - return; - } + int duration = GetDuration(); LOG_DEBUG("[VideoPlayer] Video duration: %d", duration); int width = 0, height = 0; - ret = player_get_video_size(player_, &width, &height); + int ret = player_get_video_size(player_, &width, &height); if (ret != PLAYER_ERROR_NONE) { SendError("player_get_video_size failed", get_error_message(ret)); return; @@ -465,13 +471,15 @@ void VideoPlayer::SendInitialized() { } } - // TODO(jsuya):Some streaming resources may have a duration of 0 during the - // initialization step. If the duration is 0, it may affect the progress of - // video_player and cause unnecessary errors. Therefore, set it to 1 - // temporarily. In the future, It can be updated depending on - // the loading status. - if (width != 0 && height != 0 && duration == 0) { - duration = 1; + // TODO(jsuya): Since media_player_proxy is not supported in Tizen + // profile(common), we cannot know whether the content is live or not. When + // the content is live, duration is always returned as 0, so we check if + // duration is 1(Because of we set it to 1 to prevent video_player from + // crashing when duration is returned as 0). + if (uri_.substr(0, 4) == "http" && duration == 1) { + is_live_ = true; + } else { + is_live_ = false; } is_initialized_ = true; @@ -599,3 +607,74 @@ void VideoPlayer::OnRenderingCompleted() { } RequestRendering(); } + +int64_t VideoPlayer::GetDuration() { + int duration = 0; + if (IsLive()) { + duration = GetLiveDuration(); + } else { + int ret = player_get_duration(player_, &duration); + if (ret != PLAYER_ERROR_NONE) { + LOG_ERROR("[MediaPlayer] player_get_duration failed: %s.", + get_error_message(ret)); + } + LOG_INFO("[MediaPlayer] Video duration: %d.", duration); + } + + // TODO(jsuya):Some streaming resources may have a duration of 0 during the + // initialization step. If the duration is 0, it may affect the progress of + // video_player and cause unnecessary errors. Therefore, set it to 1 + // temporarily. In the future, It can be updated depending on + // the loading status. + if (duration == 0) { + duration = 1; + } + + return duration; +} + +static std::vector split(const std::string &s, char delim) { + std::stringstream ss(s); + std::string item; + std::vector tokens; + while (getline(ss, item, delim)) { + tokens.push_back(item); + } + return tokens; +} + +int64_t VideoPlayer::GetLiveDuration() { + std::string live_duration_str = ""; + char *live_duration_buff = static_cast(malloc(sizeof(char) * 64)); + memset(live_duration_buff, 0, sizeof(char) * 64); + int ret = media_player_proxy_->player_get_adaptive_streaming_info( + player_, (void *)&live_duration_buff, PLAYER_ADAPTIVE_INFO_LIVE_DURATION); + if (ret != PLAYER_ERROR_NONE) { + LOG_ERROR("[MediaPlayer] player_get_adaptive_streaming_info failed: %s", + get_error_message(ret)); + free(live_duration_buff); + return 0; + } + if (*live_duration_buff) { + live_duration_str = std::string(live_duration_buff); + } + free(live_duration_buff); + if (live_duration_str.empty()) { + return 0; + } + std::vector time_vec = split(live_duration_str, '|'); + return std::stoll(time_vec[1]); +} + +bool VideoPlayer::IsLive() { + int is_live = 0; + int ret = media_player_proxy_->player_get_adaptive_streaming_info( + player_, &is_live, PLAYER_ADAPTIVE_INFO_IS_LIVE); + if (ret != PLAYER_ERROR_NONE) { + LOG_ERROR("[MediaPlayer] player_get_adaptive_streaming_info failed: %s", + get_error_message(ret)); + return is_live_; + } + is_live_ = is_live != 0; + return is_live_; +} diff --git a/packages/video_player/tizen/src/video_player.h b/packages/video_player/tizen/src/video_player.h index 6833a5789..16dc85ad2 100644 --- a/packages/video_player/tizen/src/video_player.h +++ b/packages/video_player/tizen/src/video_player.h @@ -18,6 +18,7 @@ #include #include +#include "media_player_proxy.h" #include "video_player_options.h" typedef int (*ScreensaverResetTimeout)(void); @@ -69,19 +70,25 @@ class VideoPlayer { void RequestRendering(); void OnRenderingCompleted(); + int64_t GetDuration(); + int64_t GetLiveDuration(); + bool IsLive(); media_packet_h current_media_packet_ = nullptr; media_packet_h previous_media_packet_ = nullptr; bool is_initialized_ = false; bool is_rendering_ = false; + bool is_live_ = false; std::unique_ptr> event_channel_; std::unique_ptr> event_sink_; player_h player_ = nullptr; + std::unique_ptr media_player_proxy_ = nullptr; int64_t texture_id_ = -1; + std::string uri_; flutter::TextureRegistrar *texture_registrar_; std::unique_ptr texture_variant_;