Add video player (#2)

https://github.com/sony/flutter-embedded-linux/issues/124
This commit is contained in:
Hidenori Matsubayashi
2021-07-29 13:20:33 +09:00
committed by GitHub
parent e6d61c00af
commit 0fe779fc3a
40 changed files with 3214 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
flutter/
@@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.10)
set(PROJECT_NAME "video_player_elinux")
project(${PROJECT_NAME} LANGUAGES CXX)
# This value is used when generating builds using this plugin, so it must
# not be changed
set(PLUGIN_NAME "video_player_elinux_plugin")
find_package(PkgConfig)
pkg_check_modules(GLIB REQUIRED glib-2.0)
pkg_check_modules(GSTREAMER REQUIRED
gstreamer-1.0
gstreamer-app-1.0
gstreamer-video-1.0
gstreamer-audio-1.0
)
add_library(${PLUGIN_NAME} SHARED
"video_player_elinux_plugin.cc"
"gst_video_player.cc"
)
apply_standard_settings(${PLUGIN_NAME})
set_target_properties(${PLUGIN_NAME} PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include")
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)
target_include_directories(${PLUGIN_NAME}
PRIVATE
${GLIB_INCLUDE_DIRS}
${GSTREAMER_INCLUDE_DIRS}
)
target_link_libraries(${PLUGIN_NAME}
PRIVATE
${GLIB_LIBRARIES}
${GSTREAMER_LIBRARIES}
)
# List of absolute paths to libraries that should be bundled with the plugin
set(video_player_elinux_bundled_libraries
""
PARENT_SCOPE
)
@@ -0,0 +1,395 @@
// Copyright 2021 Sony Group Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gst_video_player.h"
#include <gst/audio/audio.h>
#include <gst/video/gstvideometa.h>
#include <gst/video/video.h>
#include <iostream>
GstVideoPlayer::GstVideoPlayer(
const std::string& uri, std::unique_ptr<VideoPlayerStreamHandler> handler)
: stream_handler_(std::move(handler)) {
gst_.pipeline = nullptr;
gst_.playbin = nullptr;
gst_.video_convert = nullptr;
gst_.video_sink = nullptr;
gst_.output = nullptr;
gst_.bus = nullptr;
gst_.buffer = nullptr;
uri_ = ParseUri(uri);
if (!CreatePipeline()) {
std::cerr << "Failed to create a pipeline" << std::endl;
DestroyPipeline();
return;
}
// Prerolls before getting information from the pipeline.
Preroll();
// Sets internal video size and buffier.
GetVideoSize(width_, height_);
pixels_.reset(new uint32_t[width_ * height_]);
stream_handler_->OnNotifyInitialized();
}
GstVideoPlayer::~GstVideoPlayer() {
Stop();
DestroyPipeline();
}
// static
void GstVideoPlayer::GstLibraryLoad() { gst_init(NULL, NULL); }
// static
void GstVideoPlayer::GstLibraryUnload() { gst_deinit(); }
bool GstVideoPlayer::Play() {
if (gst_element_set_state(gst_.pipeline, GST_STATE_PLAYING) ==
GST_STATE_CHANGE_FAILURE) {
std::cerr << "Failed to change the state to PLAYING" << std::endl;
return false;
}
return true;
}
bool GstVideoPlayer::Pause() {
if (gst_element_set_state(gst_.pipeline, GST_STATE_PAUSED) ==
GST_STATE_CHANGE_FAILURE) {
std::cerr << "Failed to change the state to PAUSED" << std::endl;
return false;
}
return true;
}
bool GstVideoPlayer::Stop() {
if (gst_element_set_state(gst_.pipeline, GST_STATE_READY) ==
GST_STATE_CHANGE_FAILURE) {
std::cerr << "Failed to change the state to READY" << std::endl;
return false;
}
return true;
}
bool GstVideoPlayer::SetVolume(double volume) {
if (!gst_.playbin) {
return false;
}
volume_ = volume;
g_object_set(gst_.playbin, "volume", volume, NULL);
return true;
}
bool GstVideoPlayer::SetPlaybackRate(double rate) {
if (!gst_.playbin) {
return false;
}
if (rate <= 0) {
std::cerr << "Rate " << rate << " is not supported" << std::endl;
return false;
}
if (!gst_element_seek(gst_.pipeline, rate, GST_FORMAT_TIME,
GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET,
GetCurrentPosition() * GST_MSECOND, GST_SEEK_TYPE_SET,
GST_CLOCK_TIME_NONE)) {
std::cerr << "Failed to set playback rate to " << rate
<< " (gst_element_seek failed)" << std::endl;
return false;
}
playback_rate_ = rate;
mute_ = (rate < 0.5 || rate > 2);
g_object_set(gst_.playbin, "mute", mute_, NULL);
return true;
}
bool GstVideoPlayer::SetSeek(int64_t position) {
auto nanosecond = position * 1000 * 1000;
if (!gst_element_seek(
gst_.pipeline, playback_rate_, GST_FORMAT_TIME,
(GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT),
GST_SEEK_TYPE_SET, nanosecond, GST_SEEK_TYPE_SET,
GST_CLOCK_TIME_NONE)) {
std::cerr << "Failed to seek " << nanosecond << std::endl;
return false;
}
return true;
}
int64_t GstVideoPlayer::GetDuration() {
GstFormat fmt = GST_FORMAT_TIME;
int64_t duration_msec;
if (!gst_element_query_duration(gst_.pipeline, fmt, &duration_msec)) {
std::cerr << "Failed to get duration" << std::endl;
return -1;
}
duration_msec /= GST_MSECOND;
return duration_msec;
}
int64_t GstVideoPlayer::GetCurrentPosition() {
gint64 position = 0;
if (!gst_element_query_position(gst_.pipeline, GST_FORMAT_TIME, &position)) {
std::cerr << "Failed to get current position" << std::endl;
}
return position / GST_MSECOND;
}
const uint8_t* GstVideoPlayer::GetFrameBuffer() {
std::shared_lock<std::shared_mutex> lock(mutex_buffer_);
if (!gst_.buffer) {
return nullptr;
}
const uint32_t pixel_bytes = width_ * height_ * 4;
gst_buffer_extract(gst_.buffer, 0, pixels_.get(), pixel_bytes);
return reinterpret_cast<const uint8_t*>(pixels_.get());
}
// Creats a video pipeline using playbin.
// $ playbin uri=<file> video-sink="videoconvert ! video/x-raw,format=RGBA !
// fakesink"
bool GstVideoPlayer::CreatePipeline() {
gst_.pipeline = gst_pipeline_new("pipeline");
if (!gst_.pipeline) {
std::cerr << "Failed to create a pipeline" << std::endl;
return false;
}
gst_.playbin = gst_element_factory_make("playbin", "playbin");
if (!gst_.playbin) {
std::cerr << "Failed to create a source" << std::endl;
return false;
}
gst_.video_convert = gst_element_factory_make("videoconvert", "videoconvert");
if (!gst_.video_convert) {
std::cerr << "Failed to create a videoconvert" << std::endl;
return false;
}
gst_.video_sink = gst_element_factory_make("fakesink", "videosink");
if (!gst_.video_sink) {
std::cerr << "Failed to create a videosink" << std::endl;
return false;
}
gst_.output = gst_bin_new("output");
if (!gst_.output) {
std::cerr << "Failed to create an output" << std::endl;
return false;
}
gst_.bus = gst_pipeline_get_bus(GST_PIPELINE(gst_.pipeline));
if (!gst_.bus) {
std::cerr << "Failed to create a bus" << std::endl;
return false;
}
gst_bus_set_sync_handler(gst_.bus, (GstBusSyncHandler)HandleGstMessage, this,
NULL);
// Sets properties to fakesink to get the callback of a decoded frame.
g_object_set(G_OBJECT(gst_.video_sink), "sync", TRUE, "qos", FALSE, NULL);
g_object_set(G_OBJECT(gst_.video_sink), "signal-handoffs", TRUE, NULL);
g_signal_connect(G_OBJECT(gst_.video_sink), "handoff",
G_CALLBACK(HandoffHandler), this);
gst_bin_add_many(GST_BIN(gst_.output), gst_.video_convert, gst_.video_sink,
NULL);
// Adds caps to the converter to convert the color format to RGBA.
auto* caps = gst_caps_from_string("video/x-raw,format=RGBA");
auto link_ok =
gst_element_link_filtered(gst_.video_convert, gst_.video_sink, caps);
gst_caps_unref(caps);
if (!link_ok) {
std::cerr << "Failed to link elements" << std::endl;
return false;
}
auto* sinkpad = gst_element_get_static_pad(gst_.video_convert, "sink");
auto* ghost_sinkpad = gst_ghost_pad_new("sink", sinkpad);
gst_pad_set_active(ghost_sinkpad, TRUE);
gst_element_add_pad(gst_.output, ghost_sinkpad);
// Sets properties to playbin.
g_object_set(gst_.playbin, "uri", uri_.c_str(), NULL);
g_object_set(gst_.playbin, "video-sink", gst_.output, NULL);
gst_bin_add_many(GST_BIN(gst_.pipeline), gst_.playbin, NULL);
return true;
}
void GstVideoPlayer::Preroll() {
if (!gst_.playbin) {
return;
}
auto result = gst_element_set_state(gst_.pipeline, GST_STATE_PAUSED);
if (result == GST_STATE_CHANGE_FAILURE) {
std::cerr << "Failed to change the state to PAUSED" << std::endl;
return;
}
// Waits until the state becomes GST_STATE_PAUSED.
if (result == GST_STATE_CHANGE_ASYNC) {
GstState state;
result =
gst_element_get_state(gst_.pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
if (result == GST_STATE_CHANGE_FAILURE) {
std::cerr << "Failed to get the current state" << std::endl;
}
}
}
void GstVideoPlayer::DestroyPipeline() {
if (gst_.video_sink) {
g_object_set(G_OBJECT(gst_.video_sink), "signal-handoffs", FALSE, NULL);
}
if (gst_.pipeline) {
gst_element_set_state(gst_.pipeline, GST_STATE_NULL);
}
if (gst_.buffer) {
gst_buffer_unref(gst_.buffer);
gst_.buffer = nullptr;
}
if (gst_.bus) {
gst_object_unref(gst_.bus);
gst_.bus = nullptr;
}
if (gst_.pipeline) {
gst_object_unref(gst_.pipeline);
gst_.pipeline = nullptr;
}
if (gst_.playbin) {
gst_.playbin = nullptr;
}
if (gst_.output) {
gst_.output = nullptr;
}
if (gst_.video_sink) {
gst_.video_sink = nullptr;
}
if (gst_.video_convert) {
gst_.video_convert = nullptr;
}
}
std::string GstVideoPlayer::ParseUri(const std::string& uri) {
if (gst_uri_is_valid(uri.c_str())) {
return uri;
}
const auto* filename_uri = gst_filename_to_uri(uri.c_str(), NULL);
if (!filename_uri) {
std::cerr << "Faild to open " << uri.c_str() << std::endl;
return uri;
}
std::string result_uri(filename_uri);
delete filename_uri;
return result_uri;
}
void GstVideoPlayer::GetVideoSize(int32_t& width, int32_t& height) {
if (!gst_.pipeline || !gst_.video_sink) {
std::cerr
<< "Failed to get video size. The pileline hasn't initialized yet.";
return;
}
auto* sink_pad = gst_element_get_static_pad(gst_.video_sink, "sink");
if (!sink_pad) {
std::cerr << "Failed to get a pad";
return;
}
auto* caps = gst_pad_get_current_caps(sink_pad);
auto* structure = gst_caps_get_structure(caps, 0);
if (!structure) {
std::cerr << "Failed to get a structure";
return;
}
gst_structure_get_int(structure, "width", &width);
gst_structure_get_int(structure, "height", &height);
}
// static
void GstVideoPlayer::HandoffHandler(GstElement* fakesink, GstBuffer* buf,
GstPad* new_pad, gpointer user_data) {
auto* self = reinterpret_cast<GstVideoPlayer*>(user_data);
auto* caps = gst_pad_get_current_caps(new_pad);
auto* structure = gst_caps_get_structure(caps, 0);
int width;
int height;
gst_structure_get_int(structure, "width", &width);
gst_structure_get_int(structure, "height", &height);
if (width != self->width_ || height != self->height_) {
self->width_ = width;
self->height_ = height;
self->pixels_.reset(new uint32_t[width * height]);
std::cout << "Pixel buffer size: width = " << width
<< ", height = " << height << std::endl;
}
std::lock_guard<std::shared_mutex> lock(self->mutex_buffer_);
if (self->gst_.buffer) {
gst_buffer_unref(self->gst_.buffer);
self->gst_.buffer = nullptr;
}
self->gst_.buffer = gst_buffer_ref(buf);
self->stream_handler_->OnNotifyFrameDecoded();
}
// static
gboolean GstVideoPlayer::HandleGstMessage(GstBus* bus, GstMessage* message,
gpointer user_data) {
switch (GST_MESSAGE_TYPE(message)) {
case GST_MESSAGE_EOS: {
auto* self = reinterpret_cast<GstVideoPlayer*>(user_data);
self->stream_handler_->OnNotifyCompleted();
if (self->auto_repeat_) {
self->SetSeek(0);
}
break;
}
case GST_MESSAGE_WARNING: {
gchar* debug;
GError* error;
gst_message_parse_warning(message, &error, &debug);
g_printerr("WARNING from element %s: %s\n", GST_OBJECT_NAME(message->src),
error->message);
g_printerr("Warning details: %s\n", debug);
g_free(debug);
g_error_free(error);
break;
}
case GST_MESSAGE_ERROR: {
gchar* debug;
GError* error;
gst_message_parse_error(message, &error, &debug);
g_printerr("ERROR from element %s: %s\n", GST_OBJECT_NAME(message->src),
error->message);
g_printerr("Error details: %s\n", debug);
g_free(debug);
g_error_free(error);
break;
}
default:
break;
}
return TRUE;
}
@@ -0,0 +1,72 @@
// Copyright 2021 Sony Group Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_GST_VIDEO_PLAYER_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_GST_VIDEO_PLAYER_H_
#include <gst/gst.h>
#include <memory>
#include <shared_mutex>
#include <string>
#include "video_player_stream_handler.h"
class GstVideoPlayer {
public:
GstVideoPlayer(const std::string& uri,
std::unique_ptr<VideoPlayerStreamHandler> handler);
~GstVideoPlayer();
static void GstLibraryLoad();
static void GstLibraryUnload();
bool Play();
bool Pause();
bool Stop();
bool SetVolume(double volume);
bool SetPlaybackRate(double rate);
void SetAutoRepeat(bool auto_repeat) { auto_repeat_ = auto_repeat; };
bool SetSeek(int64_t position);
int64_t GetDuration();
int64_t GetCurrentPosition();
const uint8_t* GetFrameBuffer();
int32_t GetWidth() const { return width_; };
int32_t GetHeight() const { return height_; };
private:
struct GstVideoElements {
GstElement* pipeline;
GstElement* playbin;
GstElement* video_convert;
GstElement* video_sink;
GstElement* output;
GstBus* bus;
GstBuffer* buffer;
};
static void HandoffHandler(GstElement* fakesink, GstBuffer* buf,
GstPad* new_pad, gpointer user_data);
static gboolean HandleGstMessage(GstBus* bus, GstMessage* message,
gpointer user_data);
std::string ParseUri(const std::string& uri);
bool CreatePipeline();
void DestroyPipeline();
void Preroll();
void GetVideoSize(int32_t& width, int32_t& height);
GstVideoElements gst_;
std::string uri_;
std::unique_ptr<uint32_t> pixels_;
int32_t width_;
int32_t height_;
double volume_ = 1.0;
double playback_rate_ = 1.0;
bool mute_ = false;
bool auto_repeat_ = false;
std::shared_mutex mutex_buffer_;
std::unique_ptr<VideoPlayerStreamHandler> stream_handler_;
};
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_GST_VIDEO_PLAYER_H_
@@ -0,0 +1,23 @@
#ifndef FLUTTER_PLUGIN_VIDEO_PLAYER_ELINUX_PLUGIN_H_
#define FLUTTER_PLUGIN_VIDEO_PLAYER_ELINUX_PLUGIN_H_
#include <flutter_plugin_registrar.h>
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
#else
#define FLUTTER_PLUGIN_EXPORT
#endif
#if defined(__cplusplus)
extern "C" {
#endif
FLUTTER_PLUGIN_EXPORT void VideoPlayerElinuxPluginRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar);
#if defined(__cplusplus)
} // extern "C"
#endif
#endif // FLUTTER_PLUGIN_VIDEO_PLAYER_ELINUX_PLUGIN_H_
@@ -0,0 +1,90 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_CREATE_MESSAGE_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_CREATE_MESSAGE_H_
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
class CreateMessage {
public:
CreateMessage() = default;
~CreateMessage() = default;
// Prevent copying.
CreateMessage(CreateMessage const&) = default;
CreateMessage& operator=(CreateMessage const&) = default;
void SetAsset(const std::string& asset) { asset_ = asset; }
std::string GetAsset() const { return asset_; }
void SetUri(const std::string& uri) { uri_ = uri; }
std::string GetUri() const { return uri_; }
void SetPackageName(const std::string& packageName) {
package_name_ = packageName;
}
std::string GetPackageName() const { return package_name_; }
void SetFormatHint(const std::string& formatHint) {
format_hint_ = formatHint;
}
std::string GetFormatHint() const { return format_hint_; }
flutter::EncodableValue ToMap() {
// todo: Add httpHeaders.
flutter::EncodableMap map = {
{flutter::EncodableValue("asset"), flutter::EncodableValue(asset_)},
{flutter::EncodableValue("uri"), flutter::EncodableValue(uri_)},
{flutter::EncodableValue("packageName"),
flutter::EncodableValue(package_name_)},
{flutter::EncodableValue("formatHint"),
flutter::EncodableValue(format_hint_)}};
return flutter::EncodableValue(map);
}
static CreateMessage FromMap(const flutter::EncodableValue& value) {
CreateMessage message;
if (std::holds_alternative<flutter::EncodableMap>(value)) {
auto map = std::get<flutter::EncodableMap>(value);
flutter::EncodableValue& asset = map[flutter::EncodableValue("asset")];
if (std::holds_alternative<std::string>(asset)) {
message.SetAsset(std::get<std::string>(asset));
}
flutter::EncodableValue& uri = map[flutter::EncodableValue("uri")];
if (std::holds_alternative<std::string>(uri)) {
message.SetUri(std::get<std::string>(uri));
}
flutter::EncodableValue& packageName =
map[flutter::EncodableValue("packageName")];
if (std::holds_alternative<std::string>(packageName)) {
message.SetPackageName(std::get<std::string>(uri));
}
flutter::EncodableValue& formatHint =
map[flutter::EncodableValue("formatHint")];
if (std::holds_alternative<std::string>(formatHint)) {
message.SetFormatHint(std::get<std::string>(formatHint));
}
}
return message;
}
private:
std::string asset_;
std::string uri_;
std::string package_name_;
std::string format_hint_;
};
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_CREATE_MESSAGE_H_
@@ -0,0 +1,63 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_LOOPING_MESSAGE_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_LOOPING_MESSAGE_H_
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
class LoopingMessage {
public:
LoopingMessage() = default;
~LoopingMessage() = default;
// Prevent copying.
LoopingMessage(LoopingMessage const&) = default;
LoopingMessage& operator=(LoopingMessage const&) = default;
void SetTextureId(int64_t texture_id) { texture_id_ = texture_id; }
int64_t GetTextureId() const { return texture_id_; }
void SetIsLooping(bool is_looping) { is_looping_ = is_looping; }
bool GetIsLooping() const { return is_looping_; }
flutter::EncodableValue ToMap() {
flutter::EncodableMap map = {{flutter::EncodableValue("textureId"),
flutter::EncodableValue(texture_id_)},
{flutter::EncodableValue("isLooping"),
flutter::EncodableValue(is_looping_)}};
return flutter::EncodableValue(map);
}
static LoopingMessage FromMap(const flutter::EncodableValue& value) {
LoopingMessage message;
if (std::holds_alternative<flutter::EncodableMap>(value)) {
auto map = std::get<flutter::EncodableMap>(value);
flutter::EncodableValue& texture_id =
map[flutter::EncodableValue("textureId")];
if (std::holds_alternative<int32_t>(texture_id) ||
std::holds_alternative<int64_t>(texture_id)) {
message.SetTextureId(texture_id.LongValue());
}
flutter::EncodableValue& is_looping =
map[flutter::EncodableValue("isLooping")];
if (std::holds_alternative<bool>(is_looping)) {
message.SetIsLooping(std::get<bool>(is_looping));
}
}
return message;
}
private:
int64_t texture_id_ = 0;
bool is_looping_ = false;
};
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_LOOPING_MESSAGE_H_
@@ -0,0 +1,16 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_MESSAGES_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_MESSAGES_H_
#include "create_message.h"
#include "looping_message.h"
#include "mix_with_others_message.h"
#include "playback_speed_message.h"
#include "position_message.h"
#include "texture_message.h"
#include "volume_message.h"
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_MESSAGES_H_
@@ -0,0 +1,52 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_MIX_WITH_OTHERS_MESSAGE_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_MIX_WITH_OTHERS_MESSAGE_H_
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
class MixWithOthersMessage {
public:
MixWithOthersMessage() = default;
~MixWithOthersMessage() = default;
// Prevent copying.
MixWithOthersMessage(MixWithOthersMessage const&) = default;
MixWithOthersMessage& operator=(MixWithOthersMessage const&) = default;
void SetMixWithOthers(bool mixWithOthers) {
mix_with_others_ = mixWithOthers;
}
bool GetMixWithOthers() const { return mix_with_others_; }
flutter::EncodableValue ToMap() {
flutter::EncodableMap map = {{flutter::EncodableValue("mixWithOthers"),
flutter::EncodableValue(mix_with_others_)}};
return flutter::EncodableValue(map);
}
static MixWithOthersMessage FromMap(const flutter::EncodableValue& value) {
MixWithOthersMessage message;
if (std::holds_alternative<flutter::EncodableMap>(value)) {
auto map = std::get<flutter::EncodableMap>(value);
flutter::EncodableValue& mixWithOthers =
map[flutter::EncodableValue("mixWithOthers")];
if (std::holds_alternative<bool>(mixWithOthers)) {
message.SetMixWithOthers(std::get<bool>(mixWithOthers));
}
}
return message;
}
private:
bool mix_with_others_ = false;
};
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_MIX_WITH_OTHERS_MESSAGE_H_
@@ -0,0 +1,62 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_PLAYBACK_SPEED_MESSAGE_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_PLAYBACK_SPEED_MESSAGE_H_
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
class PlaybackSpeedMessage {
public:
PlaybackSpeedMessage() = default;
~PlaybackSpeedMessage() = default;
// Prevent copying.
PlaybackSpeedMessage(PlaybackSpeedMessage const&) = default;
PlaybackSpeedMessage& operator=(PlaybackSpeedMessage const&) = default;
void SetTextureId(int64_t texture_id) { texture_id_ = texture_id; }
int64_t GetTextureId() const { return texture_id_; }
void SetSpeed(double speed) { speed_ = speed; }
double GetSpeed() const { return speed_; }
flutter::EncodableValue ToMap() {
flutter::EncodableMap map = {
{flutter::EncodableValue("textureId"),
flutter::EncodableValue(texture_id_)},
{flutter::EncodableValue("speed"), flutter::EncodableValue(speed_)}};
return flutter::EncodableValue(map);
}
static PlaybackSpeedMessage FromMap(const flutter::EncodableValue& value) {
PlaybackSpeedMessage message;
if (std::holds_alternative<flutter::EncodableMap>(value)) {
auto map = std::get<flutter::EncodableMap>(value);
flutter::EncodableValue& texture_id =
map[flutter::EncodableValue("textureId")];
if (std::holds_alternative<int32_t>(texture_id) ||
std::holds_alternative<int64_t>(texture_id)) {
message.SetTextureId(texture_id.LongValue());
}
flutter::EncodableValue& speed = map[flutter::EncodableValue("speed")];
if (std::holds_alternative<double>(speed)) {
message.SetSpeed(std::get<double>(speed));
}
}
return message;
}
private:
int64_t texture_id_ = 0;
double speed_ = 1.0;
};
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_PLAYBACK_SPEED_MESSAGE_H_
@@ -0,0 +1,65 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_POSITION_MESSAGE_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_POSITION_MESSAGE_H_
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
class PositionMessage {
public:
PositionMessage() = default;
~PositionMessage() = default;
// Prevent copying.
PositionMessage(PositionMessage const&) = default;
PositionMessage& operator=(PositionMessage const&) = default;
void SetTextureId(int64_t texture_id) { texture_id_ = texture_id; }
int64_t GetTextureId() const { return texture_id_; }
void SetPosition(int64_t position) { position_ = position; }
int64_t GetPosition() const { return position_; }
flutter::EncodableValue ToMap() {
flutter::EncodableMap toMapResult = {{flutter::EncodableValue("textureId"),
flutter::EncodableValue(texture_id_)},
{flutter::EncodableValue("position"),
flutter::EncodableValue(position_)}};
return flutter::EncodableValue(toMapResult);
}
static PositionMessage FromMap(const flutter::EncodableValue& value) {
PositionMessage message;
if (std::holds_alternative<flutter::EncodableMap>(value)) {
auto map = std::get<flutter::EncodableMap>(value);
flutter::EncodableValue& texture_id =
map[flutter::EncodableValue("textureId")];
if (std::holds_alternative<int32_t>(texture_id) ||
std::holds_alternative<int64_t>(texture_id)) {
message.SetTextureId(texture_id.LongValue());
}
flutter::EncodableValue& position =
map[flutter::EncodableValue("position")];
if (std::holds_alternative<int32_t>(position) ||
std::holds_alternative<int64_t>(position)) {
message.SetPosition(position.LongValue());
}
}
return message;
}
private:
int64_t texture_id_ = 0;
int64_t position_ = 0;
};
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_POSITION_MESSAGE_H_
@@ -0,0 +1,49 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_TEXTURE_MESSAGE_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_TEXTURE_MESSAGE_H_
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
class TextureMessage {
public:
TextureMessage() = default;
~TextureMessage() = default;
// Prevent copying.
TextureMessage(TextureMessage const&) = default;
TextureMessage& operator=(TextureMessage const&) = default;
void SetTextureId(int64_t texture_id) { texture_id_ = texture_id; }
int64_t GetTextureId() const { return texture_id_; }
flutter::EncodableValue ToMap() {
flutter::EncodableMap map = {{flutter::EncodableValue("textureId"),
flutter::EncodableValue(texture_id_)}};
return flutter::EncodableValue(map);
}
static TextureMessage FromMap(const flutter::EncodableValue& value) {
TextureMessage message;
if (std::holds_alternative<flutter::EncodableMap>(value)) {
auto map = std::get<flutter::EncodableMap>(value);
flutter::EncodableValue& texture_id =
map[flutter::EncodableValue("textureId")];
if (std::holds_alternative<int32_t>(texture_id) ||
std::holds_alternative<int64_t>(texture_id)) {
message.SetTextureId(texture_id.LongValue());
}
}
return message;
}
private:
int64_t texture_id_ = 0;
};
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_TEXTURE_MESSAGE_H_
@@ -0,0 +1,62 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_VOLUME_MESSAGE_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_VOLUME_MESSAGE_H_
#include <flutter/binary_messenger.h>
#include <flutter/encodable_value.h>
class VolumeMessage {
public:
VolumeMessage() = default;
~VolumeMessage() = default;
// Prevent copying.
VolumeMessage(VolumeMessage const&) = default;
VolumeMessage& operator=(VolumeMessage const&) = default;
void SetTextureId(int64_t texture_id) { texture_id_ = texture_id; }
int64_t GetTextureId() const { return texture_id_; }
void SetVolume(double volume) { volume_ = volume; }
double GetVolume() const { return volume_; }
flutter::EncodableValue ToMap() {
flutter::EncodableMap map = {
{flutter::EncodableValue("textureId"),
flutter::EncodableValue(texture_id_)},
{flutter::EncodableValue("volume"), flutter::EncodableValue(volume_)}};
return flutter::EncodableValue(map);
}
static VolumeMessage FromMap(const flutter::EncodableValue& value) {
VolumeMessage message;
if (std::holds_alternative<flutter::EncodableMap>(value)) {
auto map = std::get<flutter::EncodableMap>(value);
flutter::EncodableValue& texture_id =
map[flutter::EncodableValue("textureId")];
if (std::holds_alternative<int32_t>(texture_id) ||
std::holds_alternative<int64_t>(texture_id)) {
message.SetTextureId(texture_id.LongValue());
}
flutter::EncodableValue& volume = map[flutter::EncodableValue("volume")];
if (std::holds_alternative<double>(volume)) {
message.SetVolume(std::get<double>(volume));
}
}
return message;
}
private:
int64_t texture_id_ = 0;
double volume_ = 0;
};
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_MESSAGES_VOLUME_MESSAGE_H_
@@ -0,0 +1,590 @@
// Copyright 2021 Sony Group Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/video_player_elinux/video_player_elinux_plugin.h"
#include <flutter/basic_message_channel.h>
#include <flutter/encodable_value.h>
#include <flutter/event_channel.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar.h>
#include <flutter/standard_message_codec.h>
#include <flutter/standard_method_codec.h>
#include <unordered_map>
#include "gst_video_player.h"
#include "messages/messages.h"
#include "video_player_stream_handler_impl.h"
namespace {
constexpr char kVideoPlayerApiChannelInitializeName[] =
"dev.flutter.pigeon.VideoPlayerApi.initialize";
constexpr char kVideoPlayerApiChannelSetMixWithOthersName[] =
"dev.flutter.pigeon.VideoPlayerApi.setMixWithOthers";
constexpr char kVideoPlayerApiChannelCreateName[] =
"dev.flutter.pigeon.VideoPlayerApi.create";
constexpr char kVideoPlayerApiChannelDisposeName[] =
"dev.flutter.pigeon.VideoPlayerApi.dispose";
constexpr char kVideoPlayerApiChannelSetLoopingName[] =
"dev.flutter.pigeon.VideoPlayerApi.setLooping";
constexpr char kVideoPlayerApiChannelSetVolumeName[] =
"dev.flutter.pigeon.VideoPlayerApi.setVolume";
constexpr char kVideoPlayerApiChannelPauseName[] =
"dev.flutter.pigeon.VideoPlayerApi.pause";
constexpr char kVideoPlayerApiChannelPlayName[] =
"dev.flutter.pigeon.VideoPlayerApi.play";
constexpr char kVideoPlayerApiChannelPositionName[] =
"dev.flutter.pigeon.VideoPlayerApi.position";
constexpr char kVideoPlayerApiChannelSetPlaybackSpeedName[] =
"dev.flutter.pigeon.VideoPlayerApi.setPlaybackSpeed";
constexpr char kVideoPlayerApiChannelSeekToName[] =
"dev.flutter.pigeon.VideoPlayerApi.seekTo";
constexpr char kVideoPlayerVideoEventsChannelName[] =
"flutter.io/videoPlayer/videoEvents";
constexpr char kEncodableMapkeyResult[] = "result";
constexpr char kEncodableMapkeyError[] = "error";
class VideoPlayerPlugin : public flutter::Plugin {
public:
static void RegisterWithRegistrar(flutter::PluginRegistrar* registrar);
VideoPlayerPlugin(flutter::PluginRegistrar* plugin_registrar,
flutter::TextureRegistrar* texture_registrar)
: plugin_registrar_(plugin_registrar),
texture_registrar_(texture_registrar) {
// Needs to call 'gst_init' that initializing the GStreamer library before
// using it.
GstVideoPlayer::GstLibraryLoad();
}
virtual ~VideoPlayerPlugin() {
for (auto itr = players_.begin(); itr != players_.end(); itr++) {
auto texture_id = itr->first;
auto* player = itr->second.get();
player->event_sink = nullptr;
if (player->event_channel) {
player->event_channel->SetStreamHandler(nullptr);
}
player->player = nullptr;
player->buffer = nullptr;
player->texture = nullptr;
texture_registrar_->UnregisterTexture(texture_id);
}
players_.clear();
GstVideoPlayer::GstLibraryUnload();
}
private:
struct FlutterVideoPlayer {
int64_t texture_id;
std::unique_ptr<GstVideoPlayer> player;
std::unique_ptr<flutter::TextureVariant> texture;
std::unique_ptr<FlutterDesktopPixelBuffer> buffer;
std::unique_ptr<flutter::EventChannel<flutter::EncodableValue>>
event_channel;
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>> event_sink;
};
void HandleInitializeMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void HandleCreateMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void HandleDisposeMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void HandlePauseMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void HandlePlayMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void HandleSetLoopingMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void HandleSetVolumeMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void HandleSetMixWithOthersMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void HandleSetPlaybackSpeedMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void HandleSeekToMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void HandlePositionMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply);
void SendInitializedEventMessage(int64_t texture_id);
void SendPlayCompletedEventMessage(int64_t texture_id);
flutter::EncodableValue WrapError(const std::string& message,
const std::string& code = std::string(),
const std::string& details = std::string());
flutter::PluginRegistrar* plugin_registrar_;
flutter::TextureRegistrar* texture_registrar_;
std::unordered_map<int64_t, std::unique_ptr<FlutterVideoPlayer>> players_;
};
// static
void VideoPlayerPlugin::RegisterWithRegistrar(
flutter::PluginRegistrar* registrar) {
auto plugin = std::make_unique<VideoPlayerPlugin>(
registrar, registrar->texture_registrar());
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelInitializeName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandleInitializeMethodCall(message, reply);
});
}
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelCreateName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandleCreateMethodCall(message, reply);
});
}
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelDisposeName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandleDisposeMethodCall(message, reply);
});
}
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelPauseName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandlePauseMethodCall(message, reply);
});
}
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelPlayName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandlePlayMethodCall(message, reply);
});
}
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelSetLoopingName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandleSetLoopingMethodCall(message, reply);
});
}
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelSetVolumeName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandleSetVolumeMethodCall(message, reply);
});
}
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelSetMixWithOthersName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandleSetMixWithOthersMethodCall(message, reply);
});
}
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelSetPlaybackSpeedName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandleSetPlaybackSpeedMethodCall(message, reply);
});
}
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelSeekToName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandleSeekToMethodCall(message, reply);
});
}
{
auto channel =
std::make_unique<flutter::BasicMessageChannel<flutter::EncodableValue>>(
registrar->messenger(), kVideoPlayerApiChannelPositionName,
&flutter::StandardMessageCodec::GetInstance());
channel->SetMessageHandler(
[plugin_pointer = plugin.get()](const auto& message, auto reply) {
plugin_pointer->HandlePositionMethodCall(message, reply);
});
}
registrar->AddPlugin(std::move(plugin));
}
void VideoPlayerPlugin::HandleInitializeMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
flutter::EncodableMap result;
result.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
flutter::EncodableValue());
reply(flutter::EncodableValue(result));
}
void VideoPlayerPlugin::HandleCreateMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
auto meta = CreateMessage::FromMap(message);
std::string uri;
if (!meta.GetAsset().empty()) {
// todo: gets propery path of the Flutter project.
std::string flutter_project_path = "./bundle/data/";
uri = flutter_project_path + "flutter_assets/" + meta.GetAsset();
} else {
uri = meta.GetUri();
}
auto instance = std::make_unique<FlutterVideoPlayer>();
instance->buffer = std::make_unique<FlutterDesktopPixelBuffer>();
instance->texture =
std::make_unique<flutter::TextureVariant>(flutter::PixelBufferTexture(
[instance = instance.get()](
size_t width, size_t height) -> const FlutterDesktopPixelBuffer* {
instance->buffer->width = instance->player->GetWidth();
instance->buffer->height = instance->player->GetHeight();
instance->buffer->buffer = instance->player->GetFrameBuffer();
return instance->buffer.get();
}));
const auto texture_id =
texture_registrar_->RegisterTexture(instance->texture.get());
instance->texture_id = texture_id;
{
auto event_channel =
std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(
plugin_registrar_->messenger(),
kVideoPlayerVideoEventsChannelName + std::to_string(texture_id),
&flutter::StandardMethodCodec::GetInstance());
auto event_channel_handler = std::make_unique<
flutter::StreamHandlerFunctions<flutter::EncodableValue>>(
[instance = instance.get(), host = this](
const flutter::EncodableValue* arguments,
std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&&
events)
-> std::unique_ptr<
flutter::StreamHandlerError<flutter::EncodableValue>> {
instance->event_sink = std::move(events);
host->SendInitializedEventMessage(instance->texture_id);
return nullptr;
},
[instance = instance.get()](const flutter::EncodableValue* arguments)
-> std::unique_ptr<
flutter::StreamHandlerError<flutter::EncodableValue>> {
instance->event_sink = nullptr;
return nullptr;
});
event_channel->SetStreamHandler(std::move(event_channel_handler));
instance->event_channel = std::move(event_channel);
}
{
auto player_handler = std::make_unique<VideoPlayerStreamHandlerImpl>(
// OnNotifyInitialized
[texture_id, host = this]() {
host->SendInitializedEventMessage(texture_id);
},
// OnNotifyFrameDecoded
[texture_id, host = this]() {
host->texture_registrar_->MarkTextureFrameAvailable(texture_id);
},
// OnNotifyCompleted
[texture_id, host = this]() {
host->SendPlayCompletedEventMessage(texture_id);
});
instance->player =
std::make_unique<GstVideoPlayer>(uri, std::move(player_handler));
players_[texture_id] = std::move(instance);
}
flutter::EncodableMap value;
TextureMessage result;
result.SetTextureId(texture_id);
value.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
result.ToMap());
reply(flutter::EncodableValue(value));
}
void VideoPlayerPlugin::HandleDisposeMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
auto parameter = TextureMessage::FromMap(message);
const auto texture_id = parameter.GetTextureId();
flutter::EncodableMap result;
if (players_.find(texture_id) != players_.end()) {
auto* player = players_[texture_id].get();
player->event_sink = nullptr;
player->event_channel->SetStreamHandler(nullptr);
player->player = nullptr;
player->buffer = nullptr;
player->texture = nullptr;
players_.erase(texture_id);
texture_registrar_->UnregisterTexture(texture_id);
result.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
flutter::EncodableValue());
} else {
auto error_message = "Couldn't find the player with texture id: " +
std::to_string(texture_id);
result.emplace(flutter::EncodableValue(kEncodableMapkeyError),
flutter::EncodableValue(WrapError(error_message)));
}
reply(flutter::EncodableValue(result));
}
void VideoPlayerPlugin::HandlePauseMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
auto parameter = TextureMessage::FromMap(message);
const auto texture_id = parameter.GetTextureId();
flutter::EncodableMap result;
if (players_.find(texture_id) != players_.end()) {
players_[texture_id]->player->Pause();
result.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
flutter::EncodableValue());
} else {
auto error_message = "Couldn't find the player with texture id: " +
std::to_string(texture_id);
result.emplace(flutter::EncodableValue(kEncodableMapkeyError),
flutter::EncodableValue(WrapError(error_message)));
}
reply(flutter::EncodableValue(result));
}
void VideoPlayerPlugin::HandlePlayMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
auto parameter = TextureMessage::FromMap(message);
const auto texture_id = parameter.GetTextureId();
flutter::EncodableMap result;
if (players_.find(texture_id) != players_.end()) {
players_[texture_id]->player->Play();
result.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
flutter::EncodableValue());
} else {
auto error_message = "Couldn't find the player with texture id: " +
std::to_string(texture_id);
result.emplace(flutter::EncodableValue(kEncodableMapkeyError),
flutter::EncodableValue(WrapError(error_message)));
}
reply(flutter::EncodableValue(result));
}
void VideoPlayerPlugin::HandleSetLoopingMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
auto parameter = LoopingMessage::FromMap(message);
const auto texture_id = parameter.GetTextureId();
flutter::EncodableMap result;
if (players_.find(texture_id) != players_.end()) {
players_[texture_id]->player->SetAutoRepeat(parameter.GetIsLooping());
result.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
flutter::EncodableValue());
} else {
auto error_message = "Couldn't find the player with texture id: " +
std::to_string(texture_id);
result.emplace(flutter::EncodableValue(kEncodableMapkeyError),
flutter::EncodableValue(WrapError(error_message)));
}
reply(flutter::EncodableValue(result));
}
void VideoPlayerPlugin::HandleSetVolumeMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
auto parameter = VolumeMessage::FromMap(message);
const auto texture_id = parameter.GetTextureId();
flutter::EncodableMap result;
if (players_.find(texture_id) != players_.end()) {
players_[texture_id]->player->SetVolume(parameter.GetVolume());
result.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
flutter::EncodableValue());
} else {
auto error_message = "Couldn't find the player with texture id: " +
std::to_string(texture_id);
result.emplace(flutter::EncodableValue(kEncodableMapkeyError),
flutter::EncodableValue(WrapError(error_message)));
}
reply(flutter::EncodableValue(result));
}
void VideoPlayerPlugin::HandleSetMixWithOthersMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
// todo: implements here.
flutter::EncodableMap result;
result.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
flutter::EncodableValue());
reply(flutter::EncodableValue(result));
}
void VideoPlayerPlugin::HandlePositionMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
auto parameter = TextureMessage::FromMap(message);
const auto texture_id = parameter.GetTextureId();
flutter::EncodableMap result;
if (players_.find(texture_id) != players_.end()) {
PositionMessage send_message;
send_message.SetTextureId(texture_id);
send_message.SetPosition(
players_[texture_id]->player->GetCurrentPosition());
result.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
send_message.ToMap());
} else {
auto error_message = "Couldn't find the player with texture id: " +
std::to_string(texture_id);
result.emplace(flutter::EncodableValue(kEncodableMapkeyError),
flutter::EncodableValue(WrapError(error_message)));
}
reply(flutter::EncodableValue(result));
}
void VideoPlayerPlugin::HandleSetPlaybackSpeedMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
auto parameter = PlaybackSpeedMessage::FromMap(message);
const auto texture_id = parameter.GetTextureId();
flutter::EncodableMap result;
if (players_.find(texture_id) != players_.end()) {
players_[texture_id]->player->SetPlaybackRate(parameter.GetSpeed());
result.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
flutter::EncodableValue());
} else {
auto error_message = "Couldn't find the player with texture id: " +
std::to_string(texture_id);
result.emplace(flutter::EncodableValue(kEncodableMapkeyError),
flutter::EncodableValue(WrapError(error_message)));
}
reply(flutter::EncodableValue(result));
}
void VideoPlayerPlugin::HandleSeekToMethodCall(
const flutter::EncodableValue& message,
flutter::MessageReply<flutter::EncodableValue> reply) {
auto parameter = PositionMessage::FromMap(message);
const auto texture_id = parameter.GetTextureId();
flutter::EncodableMap result;
if (players_.find(texture_id) != players_.end()) {
players_[texture_id]->player->SetSeek(parameter.GetPosition());
result.emplace(flutter::EncodableValue(kEncodableMapkeyResult),
flutter::EncodableValue());
} else {
auto error_message = "Couldn't find the player with texture id: " +
std::to_string(texture_id);
result.emplace(flutter::EncodableValue(kEncodableMapkeyError),
flutter::EncodableValue(WrapError(error_message)));
}
reply(flutter::EncodableValue(result));
}
void VideoPlayerPlugin::SendInitializedEventMessage(int64_t texture_id) {
if (players_.find(texture_id) == players_.end() ||
!players_[texture_id]->event_sink) {
return;
}
auto duration = players_[texture_id]->player->GetDuration();
auto width = players_[texture_id]->player->GetWidth();
auto height = players_[texture_id]->player->GetHeight();
flutter::EncodableMap encodables = {
{flutter::EncodableValue("event"),
flutter::EncodableValue("initialized")},
{flutter::EncodableValue("duration"), flutter::EncodableValue(duration)},
{flutter::EncodableValue("width"), flutter::EncodableValue(width)},
{flutter::EncodableValue("height"), flutter::EncodableValue(height)}};
flutter::EncodableValue event(encodables);
players_[texture_id]->event_sink->Success(event);
}
void VideoPlayerPlugin::SendPlayCompletedEventMessage(int64_t texture_id) {
if (players_.find(texture_id) == players_.end() ||
!players_[texture_id]->event_sink) {
return;
}
flutter::EncodableMap encodables = {
{flutter::EncodableValue("event"), flutter::EncodableValue("completed")}};
flutter::EncodableValue event(encodables);
players_[texture_id]->event_sink->Success(event);
}
flutter::EncodableValue VideoPlayerPlugin::WrapError(
const std::string& message, const std::string& code,
const std::string& details) {
flutter::EncodableMap map = {
{flutter::EncodableValue("message"), flutter::EncodableValue(message)},
{flutter::EncodableValue("code"), flutter::EncodableValue(code)},
{flutter::EncodableValue("details"), flutter::EncodableValue(details)}};
return flutter::EncodableValue(map);
}
} // namespace
void VideoPlayerElinuxPluginRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) {
VideoPlayerPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarManager::GetInstance()
->GetRegistrar<flutter::PluginRegistrar>(registrar));
}
@@ -0,0 +1,32 @@
// Copyright 2021 Sony Group Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_VIDEO_PLAYER_STREAM_HANDLER_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_VIDEO_PLAYER_STREAM_HANDLER_H_
class VideoPlayerStreamHandler {
public:
VideoPlayerStreamHandler() = default;
virtual ~VideoPlayerStreamHandler() = default;
// Prevent copying.
VideoPlayerStreamHandler(VideoPlayerStreamHandler const&) = delete;
VideoPlayerStreamHandler& operator=(VideoPlayerStreamHandler const&) = delete;
// Notifies the completion of initializing the video player.
void OnNotifyInitialized() { OnNotifyInitializedInternal(); }
// Notifies the completion of decoding a video frame.
void OnNotifyFrameDecoded() { OnNotifyFrameDecodedInternal(); }
// Notifies the completion of playing a video.
void OnNotifyCompleted() { OnNotifyCompletedInternal(); }
protected:
virtual void OnNotifyInitializedInternal() = 0;
virtual void OnNotifyFrameDecodedInternal() = 0;
virtual void OnNotifyCompletedInternal() = 0;
};
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_VIDEO_PLAYER_STREAM_HANDLER_H_
@@ -0,0 +1,58 @@
// Copyright 2021 Sony Group Corporation. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_VIDEO_PLAYER_STREAM_HANDLER_IMPL_H_
#define PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_VIDEO_PLAYER_STREAM_HANDLER_IMPL_H_
#include <functional>
#include "video_player_stream_handler.h"
class VideoPlayerStreamHandlerImpl : public VideoPlayerStreamHandler {
public:
using OnNotifyInitialized = std::function<void()>;
using OnNotifyFrameDecoded = std::function<void()>;
using OnNotifyCompleted = std::function<void()>;
VideoPlayerStreamHandlerImpl(OnNotifyInitialized on_notify_initialized,
OnNotifyFrameDecoded on_notify_frame_decoded,
OnNotifyCompleted on_notify_completed)
: on_notify_initialized_(on_notify_initialized),
on_notify_frame_decoded_(on_notify_frame_decoded),
on_notify_completed_(on_notify_completed) {}
virtual ~VideoPlayerStreamHandlerImpl() = default;
// Prevent copying.
VideoPlayerStreamHandlerImpl(VideoPlayerStreamHandlerImpl const&) = delete;
VideoPlayerStreamHandlerImpl& operator=(VideoPlayerStreamHandlerImpl const&) =
delete;
protected:
// |VideoPlayerStreamHandler|
void OnNotifyInitializedInternal() {
if (on_notify_initialized_) {
on_notify_initialized_();
}
}
// |VideoPlayerStreamHandler|
void OnNotifyFrameDecodedInternal() {
if (on_notify_frame_decoded_) {
on_notify_frame_decoded_();
}
}
// |VideoPlayerStreamHandler|
void OnNotifyCompletedInternal() {
if (on_notify_completed_) {
on_notify_completed_();
}
}
OnNotifyInitialized on_notify_initialized_;
OnNotifyFrameDecoded on_notify_frame_decoded_;
OnNotifyCompleted on_notify_completed_;
};
#endif // PACKAGES_VIDEO_PLAYER_VIDEO_PLAYER_ELINUX_VIDEO_PLAYER_STREAM_HANDLER_IMPL_H_