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
+2
View File
@@ -0,0 +1,2 @@
# This file was copied from flutter/plugins/.clang-format.
BasedOnStyle: Google
+53
View File
@@ -0,0 +1,53 @@
# This file was copied from flutter/plugins/.gitignore.
.DS_Store
.atom/
.idea/
.vscode/
.packages
.pub/
.dart_tool/
pubspec.lock
flutter_export_environment.sh
examples/all_plugins/pubspec.yaml
Podfile.lock
Pods/
.symlinks/
**/Flutter/App.framework/
**/Flutter/ephemeral/
**/Flutter/Flutter.podspec
**/Flutter/Flutter.framework/
**/Flutter/Generated.xcconfig
**/Flutter/flutter_assets/
ServiceDefinitions.json
xcuserdata/
**/DerivedData/
local.properties
keystore.properties
.gradle/
gradlew
gradlew.bat
gradle-wrapper.jar
.flutter-plugins-dependencies
*.iml
generated_plugin_registrant.dart
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
generated_plugin_registrant.cc
GeneratedPluginRegistrant.java
GeneratedPluginRegistrant.swift
build/
.flutter-plugins
.project
.classpath
.settings
# Downloaded by the plugin tools.
google-java-format-1.3-all-deps.jar
+26
View File
@@ -0,0 +1,26 @@
Copyright (c) 2021 Sony Group Corporation. All rights reserved.
Copyright (c) 2013 The Flutter Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the names of the copyright holders nor the names of the
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+6 -1
View File
@@ -2,7 +2,12 @@
This repo is a companion repo to the [flutter-elinux](https://github.com/sony/flutter-elinux). It contains the source code for Flutter plugins for eLinux. Check the packages directory for all plugins.
## Plugins
Coming soon.
Basically, the plugins for elinux are designed to be API compatible with the the official Flutter plugins.
| Package | Original package |
| -------- | ---------------- |
| [video_player_elinux](packages/video_player) | [video_player](https://github.com/flutter/plugins/tree/master/packages/video_player) (Flutter official) |
## Companion repos
| Repo | Purpose |
+252
View File
@@ -0,0 +1,252 @@
# This file was copied from flutter/plugins/analysis_options.yaml.
# This is a copy (as of March 2021) of flutter/flutter's analysis_options file,
# with minimal changes for this repository. The goal is to move toward using a
# shared set of analysis options as much as possible, and eventually a shared
# file.
#
# Plugins that have not yet switched from the previous set of options have a
# local analysis_options.yaml that points to analysis_options_legacy.yaml
# instead.
# Specify analysis options.
#
# Until there are meta linter rules, each desired lint must be explicitly enabled.
# See: https://github.com/dart-lang/linter/issues/288
#
# For a list of lints, see: http://dart-lang.github.io/linter/lints/
# See the configuration guide for more
# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
#
# There are other similar analysis options files in the flutter repos,
# which should be kept in sync with this file:
#
# - analysis_options.yaml (this file)
# - packages/flutter/lib/analysis_options_user.yaml
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
#
# This file contains the analysis options used by Flutter tools, such as IntelliJ,
# Android Studio, and the `flutter analyze` command.
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning
# treat missing returns as a warning (not a hint)
missing_return: warning
# allow having TODOs in the code
todo: ignore
# allow self-reference to deprecated members (we do this because otherwise we have
# to annotate every member in every test, assert, etc, when we deprecate something)
deprecated_member_use_from_same_package: ignore
# Ignore analyzer hints for updating pubspecs when using Future or
# Stream and not importing dart:async
# Please see https://github.com/flutter/flutter/pull/24528 for details.
sdk_version_async_exported_from_core: ignore
### Local flutter/plugins changes ###
# Allow null checks for as long as mixed mode is officially supported.
unnecessary_null_comparison: false
always_require_non_null_named_parameters: false # not needed with nnbd
# TODO(https://github.com/flutter/flutter/issues/74381):
# Clean up existing unnecessary imports, and remove line to ignore.
unnecessary_import: ignore
exclude:
# Ignore generated files
- '**/*.g.dart'
- 'lib/src/generated/*.dart'
- '**/*.mocks.dart' # Mockito @GenerateMocks
linter:
rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types
- always_put_control_body_on_new_line
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_require_non_null_named_parameters
- always_specify_types
# - always_use_package_imports # we do this commonly
- annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
# - avoid_as # required for implicit-casts: true
- avoid_bool_literals_in_conditional_expressions
# - avoid_catches_without_on_clauses # we do this commonly
# - avoid_catching_errors # we do this commonly
- avoid_classes_with_only_static_members
# - avoid_double_and_int_checks # only useful when targeting JS runtime
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
# - avoid_escaping_inner_quotes # not yet tested
- avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
# - avoid_implementing_value_types # not yet tested
- avoid_init_to_null
# - avoid_js_rounded_ints # only useful when targeting JS runtime
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # not yet tested
# - avoid_print # not yet tested
# - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
# - avoid_redundant_argument_values # not yet tested
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
# - avoid_returning_null # there are plenty of valid reasons to return null
# - avoid_returning_null_for_future # not yet tested
- avoid_returning_null_for_void
# - avoid_returning_this # there are plenty of valid reasons to return this
# - avoid_setters_without_getters # not yet tested
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_slow_async_io
# - avoid_type_to_string # we do this commonly
- avoid_types_as_parameter_names
# - avoid_types_on_closure_parameters # conflicts with always_specify_types
# - avoid_unnecessary_containers # not yet tested
- avoid_unused_constructor_parameters
- avoid_void_async
# - avoid_web_libraries_in_flutter # not yet tested
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
# - cascade_invocations # not yet tested
- cast_nullable_to_non_nullable
# - close_sinks # not reliable enough
# - comment_references # blocked on https://github.com/flutter/flutter/issues/20765
# - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally
# - curly_braces_in_flow_control_structures # not required by flutter style
# - diagnostic_describe_all_properties # not yet tested
- directives_ordering
# - do_not_use_environment # we do this commonly
- empty_catches
- empty_constructor_bodies
- empty_statements
- exhaustive_cases
# - file_names # not yet tested
- flutter_style_todos
- hash_and_equals
- implementation_imports
# - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
- iterable_contains_unrelated_type
# - join_return_with_assignment # not required by flutter style
- leading_newlines_in_multiline_strings
- library_names
- library_prefixes
# - lines_longer_than_80_chars # not required by flutter style
- list_remove_unrelated_type
# - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181
# - missing_whitespace_between_adjacent_strings # not yet tested
- no_adjacent_strings_in_list
# - no_default_cases # too many false positives
- no_duplicate_case_values
- no_logic_in_create_state
# - no_runtimeType_toString # ok in tests; we enable this only in packages/
- non_constant_identifier_names
- null_check_on_nullable_type_parameter
# - null_closures # not required by flutter style
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
# - only_throw_errors # https://github.com/flutter/flutter/issues/5792
- overridden_fields
- package_api_docs
# - package_names # non conforming packages in sdk
- package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
# - prefer_asserts_with_message # not required by flutter style
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # far too many false positives
- prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes
- prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_for_elements_to_map_fromIterable
- prefer_foreach
# - prefer_function_declarations_over_variables # not yet tested
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
# - prefer_int_literals # not yet tested
# - prefer_interpolation_to_compose_strings # not yet tested
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
# - prefer_mixin # https://github.com/dart-lang/language/issues/32
# - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932
# - prefer_relative_imports # not yet tested
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
# - provide_deprecation_message # not yet tested
# - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
- recursive_getters
# - sized_box_for_whitespace # not yet tested
- slash_for_doc_comments
# - sort_child_properties_last # not yet tested
- sort_constructors_first
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- tighten_type_of_initializing_formals
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # too many false positives
# - unnecessary_await_in_return # not yet tested
- unnecessary_brace_in_string_interps
- unnecessary_const
# - unnecessary_final # conflicts with prefer_final_locals
- unnecessary_getters_setters
# - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
- unnecessary_new
- unnecessary_null_aware_assignments
# - unnecessary_null_checks # not yet tested
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- unnecessary_parenthesis
# - unnecessary_raw_strings # not yet tested
- unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- unrelated_type_equality_checks
# - unsafe_html # not yet tested
- use_full_hex_values_for_flutter_colors
# - use_function_type_syntax_for_parameters # not yet tested
- use_is_even_rather_than_modulo
# - use_key_in_widget_constructors # not yet tested
- use_late_for_private_fields_and_variables
- use_raw_strings
- use_rethrow_when_possible
# - use_setters_to_change_properties # not yet tested
# - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
- valid_regexps
- void_checks
### Local flutter/plugins changes ###
# These are from flutter/flutter/packages, so will need to be preserved
# separately when moving to a shared file.
- no_runtimeType_toString # use objectRuntimeType from package:foundation
- public_member_api_docs # see https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#documentation-dartdocs-javadocs-etc
# Flutter has a specific use case for dependencies that are intentionally
# not sorted, which doesn't apply to this repo.
- sort_pub_dependencies
+7
View File
@@ -0,0 +1,7 @@
.DS_Store
.dart_tool/
.packages
.pub/
build/
+2
View File
@@ -0,0 +1,2 @@
## 0.9.0
* First release.
+26
View File
@@ -0,0 +1,26 @@
Copyright (c) 2021 Sony Group Corporation. All rights reserved.
Copyright (c) 2013 The Flutter Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the names of the copyright holders nor the names of the
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+33
View File
@@ -0,0 +1,33 @@
# video_player_elinux
The implementation of the Video Player plugin for flutter elinux. APIs are designed to be API compatible with the the official [`video_player`](https://github.com/flutter/plugins/tree/master/packages/video_player).
![image](https://user-images.githubusercontent.com/62131389/124210378-43f06400-db26-11eb-8723-40dad0eb67b0.png)
## Required libraries
This plugin uses [GStreamer](https://gstreamer.freedesktop.org/) internally.
```Shell
$ sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-0 \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav
```
## Getting Started
### `pubspec.yaml`
```yaml
dependencies:
video_player: ^2.1.12
video_player_elinux:
git:
url: https://github.com/sony/flutter-elinux-plugins/tree/main/packages/video_player/video_player
ref: ^0.9.0
```
### Source code
Import `video_player` in your Dart code:
```dart
import 'package:video_player/video_player.dart';
```
+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_
+1
View File
@@ -0,0 +1 @@
lib/generated_plugin_registrant.dart
+8
View File
@@ -0,0 +1,8 @@
# video_player_example
Demonstrates how to use the video_player plugin.
## Getting Started
For help getting started with Flutter for eLinux, view our online
[documentation](https://github.com/sony/flutter-elinux/wiki).
@@ -0,0 +1,110 @@
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
set(BINARY_NAME "video_player_elinux_example")
cmake_policy(SET CMP0079 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# Basically we use this include when we got the following error:
# fatal error: 'bits/c++config.h' file not found
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
include_directories(SYSTEM ${FLUTTER_SYSTEM_INCLUDE_DIRECTORIES})
endif()
endif()
# Configure build options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Configure build option to target backend.
if (NOT FLUTTER_TARGET_BACKEND_TYPE)
set(FLUTTER_TARGET_BACKEND_TYPE "wayland" CACHE
STRING "Flutter target backend type" FORCE)
set_property(CACHE FLUTTER_TARGET_BACKEND_TYPE PROPERTY STRINGS
"wayland" "gbm" "eglstream" "x11")
endif()
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build
add_subdirectory("runner")
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_EMBEDDER_LIBRARY}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
@@ -0,0 +1,108 @@
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_engine.so")
if(FLUTTER_TARGET_BACKEND_TYPE MATCHES "gbm")
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_gbm.so")
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "eglstream")
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_eglstream.so")
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "x11")
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_x11.so")
else()
set(FLUTTER_EMBEDDER_LIBRARY "${EPHEMERAL_DIR}/libflutter_elinux_wayland.so")
endif()
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_EMBEDDER_LIBRARY ${FLUTTER_EMBEDDER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/elinux/" PARENT_SCOPE)
set(AOT_LIBRARY "${EPHEMERAL_DIR}/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"flutter_export.h"
"flutter_plugin_registrar.h"
"flutter_messenger.h"
"flutter_texture_registrar.h"
"flutter_elinux.h"
"flutter_platform_views.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE "${FLUTTER_EMBEDDER_LIBRARY}")
add_dependencies(flutter flutter_assemble)
# === Wrapper ===
list(APPEND CPP_WRAPPER_SOURCES_CORE
"core_implementations.cc"
"standard_codec.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
"plugin_registrar.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_APP
"flutter_engine.cc"
"flutter_view_controller.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
# Wrapper sources needed for a plugin.
add_library(flutter_wrapper_plugin STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
)
apply_standard_settings(flutter_wrapper_plugin)
set_target_properties(flutter_wrapper_plugin PROPERTIES
POSITION_INDEPENDENT_CODE ON)
set_target_properties(flutter_wrapper_plugin PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
target_include_directories(flutter_wrapper_plugin PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_plugin flutter_assemble)
# Wrapper sources needed for the runner.
add_library(flutter_wrapper_app STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_APP}
)
apply_standard_settings(flutter_wrapper_app)
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
target_include_directories(flutter_wrapper_app PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_app flutter_assemble)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
"${FLUTTER_EMBEDDER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)
@@ -0,0 +1,13 @@
//
// Generated file. Do not edit.
//
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter/plugin_registry.h>
// Registers Flutter plugins.
void RegisterPlugins(flutter::PluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_
@@ -0,0 +1,16 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
video_player_elinux
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/elinux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
if(FLUTTER_TARGET_BACKEND_TYPE MATCHES "gbm")
add_definitions(-DFLUTTER_TARGET_BACKEND_GBM)
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "eglstream")
add_definitions(-DFLUTTER_TARGET_BACKEND_EGLSTREAM)
elseif(FLUTTER_TARGET_BACKEND_TYPE MATCHES "x11")
add_definitions(-DFLUTTER_TARGET_BACKEND_X11)
else()
add_definitions(-DFLUTTER_TARGET_BACKEND_WAYLAND)
endif()
add_executable(${BINARY_NAME}
"flutter_window.cc"
"main.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
apply_standard_settings(${BINARY_NAME})
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
add_dependencies(${BINARY_NAME} flutter_assemble)
@@ -0,0 +1,367 @@
// Copyright 2021 Sony 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 COMMAND_OPTIONS_
#define COMMAND_OPTIONS_
#include <iostream>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>
// todo: Supports other types besides int, string.
namespace commandline {
namespace {
constexpr char kOptionStyleNormal[] = "--";
constexpr char kOptionStyleShort[] = "-";
constexpr char kOptionValueForHelpMessage[] = "=<value>";
} // namespace
class Exception : public std::exception {
public:
Exception(const std::string& msg) : msg_(msg) {}
~Exception() throw() {}
const char* what() const throw() { return msg_.c_str(); }
private:
std::string msg_;
};
class CommandOptions {
public:
CommandOptions() = default;
~CommandOptions() = default;
void AddWithoutValue(const std::string& name, const std::string& short_name,
const std::string& description, bool required) {
Add<std::string, ReaderString>(name, short_name, description, "",
ReaderString(), required, false);
}
void AddInt(const std::string& name, const std::string& short_name,
const std::string& description, const int& default_value,
bool required) {
Add<int, ReaderInt>(name, short_name, description, default_value,
ReaderInt(), required, true);
}
void AddString(const std::string& name, const std::string& short_name,
const std::string& description,
const std::string& default_value, bool required) {
Add<std::string, ReaderString>(name, short_name, description, default_value,
ReaderString(), required, true);
}
template <typename T, typename F>
void Add(const std::string& name, const std::string& short_name,
const std::string& description, const T default_value,
F reader = F(), bool required = true, bool required_value = true) {
if (options_.find(name) != options_.end()) {
std::cerr << "Already registered option: " << name << std::endl;
return;
}
if (lut_short_options_.find(short_name) != lut_short_options_.end()) {
std::cerr << short_name << "is already registered" << std::endl;
return;
}
lut_short_options_[short_name] = name;
options_[name] = std::make_unique<OptionValueReader<T, F>>(
name, short_name, description, default_value, reader, required,
required_value);
// register to show help message.
registration_order_options_.push_back(options_[name].get());
}
bool Exist(const std::string& name) {
auto itr = options_.find(name);
return itr != options_.end() && itr->second->HasValue();
}
template <typename T>
const T& GetValue(const std::string& name) {
auto itr = options_.find(name);
if (itr == options_.end()) {
throw Exception("Not found: " + name);
}
auto* option_value = dynamic_cast<const OptionValue<T>*>(itr->second.get());
if (!option_value) {
throw Exception("Type mismatch: " + name);
}
return option_value->GetValue();
}
bool Parse(int argc, const char* const* argv) {
if (argc < 1) {
errors_.push_back("No options");
return false;
}
command_name_ = argv[0];
for (auto i = 1; i < argc; i++) {
const std::string arg(argv[i]);
// normal options: e.g. --bundle=/data/sample/bundle --fullscreen
if (arg.length() > 2 &&
arg.substr(0, 2).compare(kOptionStyleNormal) == 0) {
const size_t option_value_len = arg.find("=") != std::string::npos
? (arg.length() - arg.find("="))
: 0;
const bool has_value = option_value_len != 0;
std::string option_name =
arg.substr(2, arg.length() - 2 - option_value_len);
if (options_.find(option_name) == options_.end()) {
errors_.push_back("Not found option: " + option_name);
continue;
}
if (!has_value && options_[option_name]->IsRequiredValue()) {
errors_.push_back(option_name + " requres an option value");
continue;
}
if (has_value && !options_[option_name]->IsRequiredValue()) {
errors_.push_back(option_name + " doesn't requres an option value");
continue;
}
if (has_value) {
SetOptionValue(option_name, arg.substr(arg.find("=") + 1));
} else {
SetOption(option_name);
}
}
// short options: e.g. -f /foo/file.txt -h 640 -abc
else if (arg.length() > 1 &&
arg.substr(0, 1).compare(kOptionStyleShort) == 0) {
for (size_t j = 1; j < arg.length(); j++) {
const std::string option_name{argv[i][j]};
if (lut_short_options_.find(option_name) ==
lut_short_options_.end()) {
errors_.push_back("Not found short option: " + option_name);
break;
}
if (j == arg.length() - 1 &&
options_[lut_short_options_[option_name]]->IsRequiredValue()) {
if (i == argc - 1) {
errors_.push_back("Invalid format option: " + option_name);
break;
}
SetOptionValue(lut_short_options_[option_name], argv[++i]);
} else {
SetOption(lut_short_options_[option_name]);
}
}
} else {
errors_.push_back("Invalid format option: " + arg);
}
}
for (size_t i = 0; i < registration_order_options_.size(); i++) {
if (registration_order_options_[i]->IsRequired() &&
!registration_order_options_[i]->HasValue()) {
errors_.push_back(
std::string(registration_order_options_[i]->GetName()) +
" option is mandatory.");
}
}
return errors_.size() == 0;
}
std::string GetError() { return errors_.size() > 0 ? errors_[0] : ""; }
std::vector<std::string>& GetErrors() { return errors_; }
std::string ShowHelp() {
std::ostringstream ostream;
ostream << "Usage: " << command_name_ << " ";
for (size_t i = 0; i < registration_order_options_.size(); i++) {
if (registration_order_options_[i]->IsRequired()) {
ostream << registration_order_options_[i]->GetHelpShortMessage() << " ";
}
}
ostream << std::endl;
ostream << "Global options:" << std::endl;
size_t max_name_len = 0;
for (size_t i = 0; i < registration_order_options_.size(); i++) {
max_name_len = std::max(
max_name_len, registration_order_options_[i]->GetName().length());
}
for (size_t i = 0; i < registration_order_options_.size(); i++) {
if (!registration_order_options_[i]->GetShortName().empty()) {
ostream << kOptionStyleShort
<< registration_order_options_[i]->GetShortName() << ", ";
} else {
ostream << std::string(4, ' ');
}
size_t index_adjust = 0;
constexpr int kSpacerNum = 5;
auto need_value = registration_order_options_[i]->IsRequiredValue();
ostream << kOptionStyleNormal
<< registration_order_options_[i]->GetName();
if (need_value) {
ostream << kOptionValueForHelpMessage;
index_adjust += std::string(kOptionValueForHelpMessage).length();
}
ostream << std::string(
max_name_len + kSpacerNum - index_adjust -
registration_order_options_[i]->GetName().length(),
' ');
ostream << registration_order_options_[i]->GetDescription() << std::endl;
}
return ostream.str();
}
private:
struct ReaderInt {
int operator()(const std::string& value) { return std::stoi(value); }
};
struct ReaderString {
std::string operator()(const std::string& value) { return value; }
};
class Option {
public:
Option(const std::string& name, const std::string& short_name,
const std::string& description, bool required, bool required_value)
: name_(name),
short_name_(short_name),
description_(description),
is_required_(required),
is_required_value_(required_value),
value_set_(false){};
virtual ~Option() = default;
const std::string& GetName() const { return name_; };
const std::string& GetShortName() const { return short_name_; };
const std::string& GetDescription() const { return description_; };
const std::string GetHelpShortMessage() const {
std::string message = kOptionStyleNormal + name_;
if (is_required_value_) {
message += kOptionValueForHelpMessage;
}
return message;
}
bool IsRequired() const { return is_required_; };
bool IsRequiredValue() const { return is_required_value_; };
void Set() { value_set_ = true; };
virtual bool SetValue(const std::string& value) = 0;
virtual bool HasValue() const = 0;
protected:
std::string name_;
std::string short_name_;
std::string description_;
bool is_required_;
bool is_required_value_;
bool value_set_;
};
template <typename T>
class OptionValue : public Option {
public:
OptionValue(const std::string& name, const std::string& short_name,
const std::string& description, const T& default_value,
bool required, bool required_value)
: Option(name, short_name, description, required, required_value),
default_value_(default_value),
value_(default_value){};
virtual ~OptionValue() = default;
bool SetValue(const std::string& value) {
value_ = Read(value);
value_set_ = true;
return true;
}
bool HasValue() const { return value_set_; }
const T& GetValue() const { return value_; }
protected:
virtual T Read(const std::string& s) = 0;
T default_value_;
T value_;
};
template <typename T, typename F>
class OptionValueReader : public OptionValue<T> {
public:
OptionValueReader(const std::string& name, const std::string& short_name,
const std::string& description, const T default_value,
F reader, bool required, bool required_value)
: OptionValue<T>(name, short_name, description, default_value, required,
required_value),
reader_(reader) {}
~OptionValueReader() = default;
private:
T Read(const std::string& value) { return reader_(value); }
F reader_;
};
bool SetOption(const std::string& name) {
auto itr = options_.find(name);
if (itr == options_.end()) {
errors_.push_back("Unknown option: " + name);
return false;
}
itr->second->Set();
return true;
}
bool SetOptionValue(const std::string& name, const std::string& value) {
auto itr = options_.find(name);
if (itr == options_.end()) {
errors_.push_back("Unknown option: " + name);
return false;
}
if (!itr->second->SetValue(value)) {
errors_.push_back("Invalid option value: " + name + " = " + value);
return false;
}
return true;
}
std::string command_name_;
std::unordered_map<std::string, std::unique_ptr<Option>> options_;
std::unordered_map<std::string, std::string> lut_short_options_;
std::vector<Option*> registration_order_options_;
std::vector<std::string> errors_;
};
} // namespace commandline
#endif // COMMAND_OPTIONS_
@@ -0,0 +1,103 @@
// Copyright 2021 Sony 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 FLUTTER_EMBEDDER_OPTIONS_
#define FLUTTER_EMBEDDER_OPTIONS_
#include <flutter/flutter_view_controller.h>
#include <string>
#include "command_options.h"
class FlutterEmbedderOptions {
public:
FlutterEmbedderOptions() {
options_.AddString("bundle", "b", "Path to Flutter app bundle", "./bundle",
true);
options_.AddWithoutValue("no-cursor", "n", "No mouse cursor/pointer",
false);
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
// no more options.
#elif defined(FLUTTER_TARGET_BACKEND_X11)
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
false);
options_.AddInt("width", "w", "Flutter app window width", 1280, false);
options_.AddInt("height", "h", "Flutter app window height", 720, false);
#else // FLUTTER_TARGET_BACKEND_WAYLAND
options_.AddWithoutValue("onscreen-keyboard", "k",
"Enable on-screen keyboard", false);
options_.AddWithoutValue("window-decoration", "d",
"Enable window decorations", false);
options_.AddWithoutValue("fullscreen", "f", "Always full-screen display",
false);
options_.AddInt("width", "w", "Flutter app window width", 1280, false);
options_.AddInt("height", "h", "Flutter app window height", 720, false);
#endif
}
~FlutterEmbedderOptions() = default;
bool Parse(int argc, char** argv) {
if (!options_.Parse(argc, argv)) {
std::cerr << options_.GetError() << std::endl;
std::cout << options_.ShowHelp();
return false;
}
bundle_path_ = options_.GetValue<std::string>("bundle");
use_mouse_cursor_ = !options_.Exist("no-cursor");
#if defined(FLUTTER_TARGET_BACKEND_GBM) || \
defined(FLUTTER_TARGET_BACKEND_EGLSTREAM)
use_onscreen_keyboard_ = false;
use_window_decoration_ = false;
window_view_mode_ = flutter::FlutterViewController::ViewMode::kFullscreen;
#elif defined(FLUTTER_TARGET_BACKEND_X11)
use_onscreen_keyboard_ = false;
use_window_decoration_ = false;
window_view_mode_ =
options_.Exist("fullscreen")
? flutter::FlutterViewController::ViewMode::kFullscreen
: flutter::FlutterViewController::ViewMode::kNormal;
window_width_ = options_.GetValue<int>("width");
window_height_ = options_.GetValue<int>("height");
#else // FLUTTER_TARGET_BACKEND_WAYLAND
use_onscreen_keyboard_ = options_.Exist("onscreen-keyboard");
use_window_decoration_ = options_.Exist("window-decoration");
window_view_mode_ =
options_.Exist("fullscreen")
? flutter::FlutterViewController::ViewMode::kFullscreen
: flutter::FlutterViewController::ViewMode::kNormal;
window_width_ = options_.GetValue<int>("width");
window_height_ = options_.GetValue<int>("height");
#endif
return true;
}
std::string BundlePath() const { return bundle_path_; }
bool IsUseMouseCursor() const { return use_mouse_cursor_; }
bool IsUseOnscreenKeyboard() const { return use_onscreen_keyboard_; }
bool IsUseWindowDecoraation() const { return use_window_decoration_; }
flutter::FlutterViewController::ViewMode WindowViewMode() const {
return window_view_mode_;
}
int WindowWidth() const { return window_width_; }
int WindowHeight() const { return window_height_; }
private:
commandline::CommandOptions options_;
std::string bundle_path_;
bool use_mouse_cursor_ = true;
bool use_onscreen_keyboard_ = false;
bool use_window_decoration_ = false;
flutter::FlutterViewController::ViewMode window_view_mode_ =
flutter::FlutterViewController::ViewMode::kNormal;
int window_width_ = 1280;
int window_height_ = 720;
};
#endif // FLUTTER_EMBEDDER_OPTIONS_
@@ -0,0 +1,79 @@
// Copyright 2021 Sony 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 "flutter_window.h"
#include <chrono>
#include <cmath>
#include <iostream>
#include <thread>
#include "flutter/generated_plugin_registrant.h"
FlutterWindow::FlutterWindow(
const flutter::FlutterViewController::ViewProperties view_properties,
const flutter::DartProject project)
: view_properties_(view_properties), project_(project) {}
bool FlutterWindow::OnCreate() {
flutter_view_controller_ = std::make_unique<flutter::FlutterViewController>(
view_properties_, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_view_controller_->engine() ||
!flutter_view_controller_->view()) {
return false;
}
// Register Flutter plugins.
RegisterPlugins(flutter_view_controller_->engine());
return true;
}
void FlutterWindow::OnDestroy() {
if (flutter_view_controller_) {
flutter_view_controller_ = nullptr;
}
}
void FlutterWindow::Run() {
// Main loop.
auto next_flutter_event_time =
std::chrono::steady_clock::time_point::clock::now();
while (flutter_view_controller_->view()->DispatchEvent()) {
// Wait until the next event.
{
auto wait_duration =
std::max(std::chrono::nanoseconds(0),
next_flutter_event_time -
std::chrono::steady_clock::time_point::clock::now());
std::this_thread::sleep_for(
std::chrono::duration_cast<std::chrono::milliseconds>(wait_duration));
}
// Processes any pending events in the Flutter engine, and returns the
// number of nanoseconds until the next scheduled event (or max, if none).
auto wait_duration = flutter_view_controller_->engine()->ProcessMessages();
{
auto next_event_time = std::chrono::steady_clock::time_point::max();
if (wait_duration != std::chrono::nanoseconds::max()) {
next_event_time =
std::min(next_event_time,
std::chrono::steady_clock::time_point::clock::now() +
wait_duration);
} else {
// Wait for the next frame if no events.
auto frame_rate = flutter_view_controller_->view()->GetFrameRate();
next_event_time = std::min(
next_event_time,
std::chrono::steady_clock::time_point::clock::now() +
std::chrono::milliseconds(
static_cast<int>(std::trunc(1000000.0 / frame_rate))));
}
next_flutter_event_time =
std::max(next_flutter_event_time, next_event_time);
}
}
}
@@ -0,0 +1,34 @@
// Copyright 2021 Sony 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 FLUTTER_WINDOW_
#define FLUTTER_WINDOW_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <memory>
class FlutterWindow {
public:
explicit FlutterWindow(
const flutter::FlutterViewController::ViewProperties view_properties,
const flutter::DartProject project);
~FlutterWindow() = default;
// Prevent copying.
FlutterWindow(FlutterWindow const&) = delete;
FlutterWindow& operator=(FlutterWindow const&) = delete;
bool OnCreate();
void OnDestroy();
void Run();
private:
flutter::FlutterViewController::ViewProperties view_properties_;
flutter::DartProject project_;
std::unique_ptr<flutter::FlutterViewController> flutter_view_controller_;
};
#endif // FLUTTER_WINDOW_
@@ -0,0 +1,46 @@
// Copyright 2021 Sony 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 <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <iostream>
#include <memory>
#include <string>
#include "flutter_embedder_options.h"
#include "flutter_window.h"
int main(int argc, char** argv) {
FlutterEmbedderOptions options;
if (!options.Parse(argc, argv)) {
return 0;
}
// Creates the Flutter project.
const auto bundle_path = options.BundlePath();
const std::wstring fl_path(bundle_path.begin(), bundle_path.end());
flutter::DartProject project(fl_path);
auto command_line_arguments = std::vector<std::string>();
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
flutter::FlutterViewController::ViewProperties view_properties = {};
view_properties.width = options.WindowWidth();
view_properties.height = options.WindowHeight();
view_properties.view_mode = options.WindowViewMode();
view_properties.use_mouse_cursor = options.IsUseMouseCursor();
view_properties.use_onscreen_keyboard = options.IsUseOnscreenKeyboard();
view_properties.use_window_decoration = options.IsUseWindowDecoraation();
// The Flutter instance hosted by this window.
FlutterWindow window(view_properties, project);
if (!window.OnCreate()) {
std::cerr << "Failed to create a Flutter window." << std::endl;
return 0;
}
window.Run();
window.OnDestroy();
return 0;
}
+182
View File
@@ -0,0 +1,182 @@
// 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.
// ignore_for_file: public_member_api_docs
/// An example of using the plugin, controlling lifecycle and playback of the
/// video.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
void main() {
runApp(
MaterialApp(
home: _App(),
),
);
}
class _App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 1,
child: Scaffold(
key: const ValueKey<String>('home_page'),
appBar: AppBar(
title: const Text('Video player example'),
bottom: const TabBar(
isScrollable: true,
tabs: <Widget>[
Tab(
icon: Icon(Icons.cloud),
text: "Remote",
),
],
),
),
body: TabBarView(
children: <Widget>[
_BumbleBeeRemoteVideo(),
],
),
),
);
}
}
class _BumbleBeeRemoteVideo extends StatefulWidget {
@override
_BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState();
}
class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> {
late VideoPlayerController _controller;
@override
void initState() {
super.initState();
_controller = VideoPlayerController.network(
'https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_cropped_multilingual.webm',
videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
);
_controller.addListener(() {
setState(() {});
});
_controller.setLooping(true);
_controller.initialize();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
Container(padding: const EdgeInsets.only(top: 20.0)),
const Text('With remote video'),
Container(
padding: const EdgeInsets.all(20),
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
VideoPlayer(_controller),
ClosedCaption(text: _controller.value.caption.text),
_ControlsOverlay(controller: _controller),
VideoProgressIndicator(_controller, allowScrubbing: true),
],
),
),
),
],
),
);
}
}
class _ControlsOverlay extends StatelessWidget {
const _ControlsOverlay({Key? key, required this.controller})
: super(key: key);
static const _examplePlaybackRates = [
0.25,
0.5,
1.0,
1.5,
2.0,
3.0,
5.0,
10.0,
];
final VideoPlayerController controller;
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
AnimatedSwitcher(
duration: Duration(milliseconds: 50),
reverseDuration: Duration(milliseconds: 200),
child: controller.value.isPlaying
? SizedBox.shrink()
: Container(
color: Colors.black26,
child: Center(
child: Icon(
Icons.play_arrow,
color: Colors.white,
size: 100.0,
),
),
),
),
GestureDetector(
onTap: () {
controller.value.isPlaying ? controller.pause() : controller.play();
},
),
Align(
alignment: Alignment.topRight,
child: PopupMenuButton<double>(
initialValue: controller.value.playbackSpeed,
tooltip: 'Playback speed',
onSelected: (speed) {
controller.setPlaybackSpeed(speed);
},
itemBuilder: (context) {
return [
for (final speed in _examplePlaybackRates)
PopupMenuItem(
value: speed,
child: Text('${speed}x'),
)
];
},
child: Padding(
padding: const EdgeInsets.symmetric(
// Using less vertical padding as the text is also longer
// horizontally, so it feels like it would need more spacing
// horizontally (matching the aspect ratio of the video).
vertical: 12,
horizontal: 16,
),
child: Text('${controller.value.playbackSpeed}x'),
),
),
),
],
);
}
}
@@ -0,0 +1,21 @@
name: video_player_example
description: Demonstrates how to use the video_player plugin.
publish_to: none
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=2.0.0"
dependencies:
flutter:
sdk: flutter
video_player: ^2.1.12
video_player_elinux:
path: ../
dev_dependencies:
pedantic: ^1.10.0
test: any
flutter:
uses-material-design: true
+20
View File
@@ -0,0 +1,20 @@
name: video_player_elinux
description: Flutter plugin for displaying inline video with other Flutter widgets on Embedded Linux.
version: 0.9.0
homepage: https://github.com/sony/flutter-elinux-plugins
repository: https://github.com/sony/flutter-elinux-plugins/tree/main/packages/video_player/video_player
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=1.20.0"
dependencies:
flutter:
sdk: flutter
video_player_platform_interface: ^4.1.0
flutter:
plugin:
platforms:
elinux:
pluginClass: VideoPlayerElinuxPlugin