Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[video_player] Live streaming content starts playing immediately when seekTo is called #742

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/video_player/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion packages/video_player/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion packages/video_player/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
45 changes: 45 additions & 0 deletions packages/video_player/tizen/src/media_player_proxy.cc
Original file line number Diff line number Diff line change
@@ -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 <dlfcn.h>

#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<FuncPlayerGetAdaptiveStreamingInfo>(
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);
}
60 changes: 60 additions & 0 deletions packages/video_player/tizen/src/media_player_proxy.h
Original file line number Diff line number Diff line change
@@ -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 <player.h>

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_
111 changes: 95 additions & 16 deletions packages/video_player/tizen/src/video_player.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <flutter/standard_method_codec.h>

#include <algorithm>
#include <sstream>

#include "log.h"
#include "video_player_error.h"
Expand Down Expand Up @@ -130,6 +131,8 @@ VideoPlayer::VideoPlayer(flutter::PluginRegistrar *plugin_registrar,
gpu_surface_ = std::make_unique<FlutterDesktopGpuSurfaceDescriptor>();
texture_id_ = texture_registrar->RegisterTexture(texture_variant_.get());

media_player_proxy_ = std::make_unique<MediaPlayerProxy>();

int ret = player_create(&player_);
if (ret != PLAYER_ERROR_NONE) {
throw VideoPlayerError("player_create failed", get_error_message(ret));
Expand All @@ -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) {
Expand Down Expand Up @@ -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));
}
}
}

Expand Down Expand Up @@ -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;
Expand All @@ -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;
xiaowei-guan marked this conversation as resolved.
Show resolved Hide resolved
} else {
is_live_ = false;
}

is_initialized_ = true;
Expand Down Expand Up @@ -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<std::string> split(const std::string &s, char delim) {
std::stringstream ss(s);
std::string item;
std::vector<std::string> 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<char *>(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<std::string> 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_;
}
7 changes: 7 additions & 0 deletions packages/video_player/tizen/src/video_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <queue>
#include <string>

#include "media_player_proxy.h"
#include "video_player_options.h"

typedef int (*ScreensaverResetTimeout)(void);
Expand Down Expand Up @@ -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<flutter::EventChannel<flutter::EncodableValue>>
event_channel_;
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> event_sink_;

player_h player_ = nullptr;
std::unique_ptr<MediaPlayerProxy> media_player_proxy_ = nullptr;
int64_t texture_id_ = -1;
std::string uri_;

flutter::TextureRegistrar *texture_registrar_;
std::unique_ptr<flutter::TextureVariant> texture_variant_;
Expand Down
Loading