Initial commit

This commit is contained in:
hpdragon
2022-04-19 21:40:16 +07:00
commit 0bc07eed97
33 changed files with 1527 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.packages
build/
+10
View File
@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: c860cba910319332564e1e9d470a17074c1f2dfd
channel: stable
project_type: plugin
+3
View File
@@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2022 Nguyen Hoai Phong <phongnh.36@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+54
View File
@@ -0,0 +1,54 @@
# GStreamer Player Plugin
A GStreamer-powered alternative to Flutter's video_player that support Linux.
![](doc/screenshot.png)
## Installation
Follow GStreamer's [Install on Linux](https://gstreamer.freedesktop.org/documentation/installing/on-linux.html?gi-language=c) instuction
## Getting Started
To start using the plugin, copy this code or follow the example project in `example`
```
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gstreamer_player/flutter_gstreamer_player.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: GstPlayer(
pipeline:
'''rtspsrc location=
rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4 !
rtph264depay !
h264parse !
decodebin !
videoconvert !
video/x-raw,format=RGBA !
appsink name=sink''',
),
),
),
);
}
}
```
## License
Copyright (c) 2022, Nguyen Hoai Phong <phongnh.36@gmail.com>.
This library & work under this repository is licensed under MIT License.
+4
View File
@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

+46
View File
@@ -0,0 +1,46 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
+10
View File
@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: c860cba910319332564e1e9d470a17074c1f2dfd
channel: stable
project_type: app
+16
View File
@@ -0,0 +1,16 @@
# flutter_gstreamer_player_example
Demonstrates how to use the flutter_gstreamer_player plugin.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
+29
View File
@@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
+88
View File
@@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gstreamer_player/flutter_gstreamer_player.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: Row(
children: <Widget> [
Expanded(
child: GstPlayer(
pipeline:
'''rtspsrc location=
rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4 !
rtph264depay !
h264parse !
decodebin !
videoconvert !
video/x-raw,format=RGBA !
appsink name=sink''',
),
),
Expanded(
child: GstPlayer(
pipeline:
'''rtspsrc location=
rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4 !
rtph264depay !
h264parse !
decodebin !
videoconvert !
video/x-raw,format=RGBA !
appsink name=sink''',
),
),
],
),
),
Expanded(
child: Row(
children: <Widget> [
Expanded(
child: GstPlayer(
pipeline:
'''rtspsrc location=
rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4 !
rtph264depay !
h264parse !
decodebin !
videoconvert !
video/x-raw,format=RGBA !
appsink name=sink''',
),
),
Expanded(
child: GstPlayer(
pipeline:
'''rtspsrc location=
rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4 !
rtph264depay !
h264parse !
decodebin !
videoconvert !
video/x-raw,format=RGBA !
appsink name=sink''',
),
),
],
),
),
],
),
),
);
}
}
+1
View File
@@ -0,0 +1 @@
flutter/ephemeral
+116
View File
@@ -0,0 +1,116 @@
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
set(BINARY_NAME "flutter_gstreamer_player_example")
set(APPLICATION_ID "com.example.flutter_gstreamer_player")
cmake_policy(SET CMP0063 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)
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()
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
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})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Application build
add_executable(${BINARY_NAME}
"main.cc"
"my_application.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 PkgConfig::GTK)
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# 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)
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()
+87
View File
@@ -0,0 +1,87 @@
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)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# 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.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)
@@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <flutter_gstreamer_player/flutter_gstreamer_player_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_gstreamer_player_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterGstreamerPlayerPlugin");
flutter_gstreamer_player_plugin_register_with_registrar(flutter_gstreamer_player_registrar);
}
@@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_
@@ -0,0 +1,16 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
flutter_gstreamer_player
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux 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)
+6
View File
@@ -0,0 +1,6 @@
#include "my_application.h"
int main(int argc, char** argv) {
g_autoptr(MyApplication) app = my_application_new();
return g_application_run(G_APPLICATION(app), argc, argv);
}
+104
View File
@@ -0,0 +1,104 @@
#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include "flutter/generated_plugin_registrant.h"
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
GtkWindow* window =
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
// Use a header bar when running in GNOME as this is the common style used
// by applications and is the setup most users will be using (e.g. Ubuntu
// desktop).
// If running on X and not using GNOME then just use a traditional title bar
// in case the window manager does more exotic layout, e.g. tiling.
// If running on Wayland assume the header bar will work (may need changing
// if future cases occur).
gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
GdkScreen* screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
}
#endif
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "flutter_gstreamer_player_example");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "flutter_gstreamer_player_example");
}
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
g_autoptr(GError) error = nullptr;
if (!g_application_register(application, nullptr, &error)) {
g_warning("Failed to register: %s", error->message);
*exit_status = 1;
return TRUE;
}
g_application_activate(application);
*exit_status = 0;
return TRUE;
}
// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
static void my_application_init(MyApplication* self) {}
MyApplication* my_application_new() {
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_NON_UNIQUE,
nullptr));
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef FLUTTER_MY_APPLICATION_H_
#define FLUTTER_MY_APPLICATION_H_
#include <gtk/gtk.h>
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
GtkApplication)
/**
* my_application_new:
*
* Creates a new Flutter-based application.
*
* Returns: a new #MyApplication.
*/
MyApplication* my_application_new();
#endif // FLUTTER_MY_APPLICATION_H_
+182
View File
@@ -0,0 +1,182 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.2"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_gstreamer_player:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "0.0.1"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.8"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
sdks:
dart: ">=2.16.2 <3.0.0"
flutter: ">=2.5.0"
+84
View File
@@ -0,0 +1,84 @@
name: flutter_gstreamer_player_example
description: Demonstrates how to use the flutter_gstreamer_player plugin.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
sdk: ">=2.16.2 <3.0.0"
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
flutter_gstreamer_player:
# When depending on this package from a real application you should use:
# flutter_gstreamer_player: ^x.y.z
# See https://dart.dev/tools/pub/dependencies#version-constraints
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^1.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
+27
View File
@@ -0,0 +1,27 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_gstreamer_player_example/main.dart';
void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) => widget is Text &&
widget.data!.startsWith('Running on:'),
),
findsOneWidget,
);
});
}
+101
View File
@@ -0,0 +1,101 @@
#include "gst_player.h"
#include <gst/video/video.h>
#define GstPlayer_GstInit_ProgramName "gstreamer"
#define GstPlayer_GstInit_Arg1 "/home/1.ogg"
GstPlayer::GstPlayer(const std::vector<std::string>& cmd_arguments) {
if (cmd_arguments.empty()) {
char arg0[] = GstPlayer_GstInit_ProgramName;
char arg1[] = GstPlayer_GstInit_Arg1;
char* argv[] = { &arg0[0], &arg1[0], NULL };
int argc = (int)(sizeof(argv) / sizeof(argv[0])) - 1;
gst_init(&argc, (char ***)&argv);
} else {
// TODO handle this case, pass command line arguments to gstreamer
}
}
GstPlayer::~GstPlayer() {
// TODO Should free GStreamers stuff in destructor,
// but when implemented, flutter complains something about texture
// when closing application
// freeGst();
}
void GstPlayer::onVideo(VideoFrameCallback callback) {
video_callback_ = callback;
}
void GstPlayer::play(const gchar* pipelineString) {
pipelineString_ = pipelineString;
// Check and free previous playing GStreamers if any
if (sink_ != nullptr || pipeline != nullptr) {
freeGst();
}
pipeline = gst_parse_launch(
pipelineString_.c_str(),
nullptr);
sink_ = gst_bin_get_by_name(GST_BIN(pipeline), "sink");
gst_app_sink_set_emit_signals(GST_APP_SINK(sink_), TRUE);
g_signal_connect(sink_, "new-sample", G_CALLBACK(newSample), (gpointer)this);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
}
void GstPlayer::freeGst(void) {
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(sink_);
gst_object_unref(pipeline);
}
GstFlowReturn GstPlayer::newSample(GstAppSink *sink, gpointer gSelf) {
GstSample* sample = NULL;
GstMapInfo bufferInfo;
GstPlayer* self = static_cast<GstPlayer* >(gSelf);
sample = gst_app_sink_pull_sample(GST_APP_SINK(self->sink_));
if(sample != NULL) {
GstBuffer *buffer_ = gst_sample_get_buffer(sample);
if(buffer_ != NULL) {
gst_buffer_map(buffer_, &bufferInfo, GST_MAP_READ);
// Get video width and height
GstVideoFrame vframe;
GstVideoInfo video_info;
GstCaps* sampleCaps = gst_sample_get_caps(sample);
gst_video_info_from_caps(&video_info, sampleCaps);
gst_video_frame_map (&vframe, &video_info, buffer_, GST_MAP_READ);
self->video_callback_(
(uint8_t*)bufferInfo.data,
video_info.width,
video_info.height);
gst_buffer_unmap(buffer_, &bufferInfo);
gst_video_frame_unmap(&vframe);
}
gst_sample_unref(sample);
}
return GST_FLOW_OK;
}
GstPlayer* GstPlayers::Get(int32_t id, std::vector<std::string> cmd_arguments) {
std::lock_guard<std::mutex> lock(mutex_);
auto [it, added] = players_.try_emplace(id, nullptr);
if (added) {
it->second = std::make_unique<GstPlayer>(cmd_arguments);
}
return it->second.get();
}
void GstPlayers::Dispose(int32_t id) {
std::lock_guard<std::mutex> lock(mutex_);
players_.erase(id);
}
+50
View File
@@ -0,0 +1,50 @@
#ifndef GST_PLAYER_H_
#define GST_PLAYER_H_
#include <map>
#include <mutex>
#include <memory>
#include <string>
#include <functional>
#include <gst/gst.h>
#include <gst/app/gstappsink.h>
typedef std::function<void(uint8_t*, int32_t, int32_t)> VideoFrameCallback;
class GstPlayer {
public:
GstPlayer(const std::vector<std::string>& cmd_arguments);
~GstPlayer(void);
void onVideo(VideoFrameCallback callback);
void play(const gchar* pipelineString);
private:
std::string pipelineString_;
VideoFrameCallback video_callback_;
GstElement *pipeline = nullptr;
GstElement *sink_ = nullptr;
void freeGst(void);
static GstFlowReturn newSample(GstAppSink *sink, gpointer gSelf);
};
class GstPlayers {
public:
GstPlayer* Get(int32_t id, std::vector<std::string> cmd_arguments = {});
void Dispose(int32_t id);
private:
std::mutex mutex_;
std::map<int32_t, std::unique_ptr<GstPlayer>> players_;
};
static std::unique_ptr<GstPlayers> g_players = std::make_unique<GstPlayers>();
#endif
+74
View File
@@ -0,0 +1,74 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
class GstPlayerTextureController {
static const MethodChannel _channel = MethodChannel('flutter_gstreamer_player');
late int textureId;
static int _id = 0;
Future<int> initialize(String pipeline) async {
// No idea why, but you have to increase `_id` first before pass it to method channel,
// if not, receiver of method channel always received 0
GstPlayerTextureController._id = GstPlayerTextureController._id + 1;
textureId = await _channel.invokeMethod('PlayerRegisterTexture', {
'pipeline': pipeline,
'playerId': GstPlayerTextureController._id,
});
return textureId;
}
Future<Null> dispose() {
return _channel.invokeMethod('dispose', {'textureId': textureId});
}
bool get isInitialized => textureId != null;
}
class GstPlayer extends StatefulWidget {
String pipeline;
GstPlayer({Key? key, required this.pipeline}) : super(key: key);
@override
State<GstPlayer> createState() => _GstPlayerState();
}
class _GstPlayerState extends State<GstPlayer> {
final _controller = GstPlayerTextureController();
@override
void initState() {
initializeController();
super.initState();
}
@override
void didUpdateWidget(GstPlayer oldWidget) {
if (widget.pipeline != oldWidget.pipeline) {
initializeController();
}
super.didUpdateWidget(oldWidget);
}
Future<Null> initializeController() async {
await _controller.initialize(
widget.pipeline,
);
setState(() {});
}
@override
Widget build(BuildContext context) {
return Container(
child: _controller.isInitialized
? Texture(textureId: _controller.textureId)
: null,
);
}
}
+45
View File
@@ -0,0 +1,45 @@
cmake_minimum_required(VERSION 3.10)
set(PROJECT_NAME "flutter_gstreamer_player")
project(${PROJECT_NAME} LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
add_compile_options(-fPIC)
# This value is used when generating builds using this plugin, so it must
# not be changed
set(PLUGIN_NAME "flutter_gstreamer_player_plugin")
# GStreamer libraries
find_package(PkgConfig)
pkg_search_module(GLIB REQUIRED glib-2.0)
pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
pkg_check_modules(GST_APP REQUIRED gstreamer-app-1.0)
pkg_check_modules(GST_VIDEO REQUIRED gstreamer-video-1.0)
include_directories(${GSTREAMER_INCLUDE_DIRS} ${GLIB2_INCLUDE_DIRS})
# Custom GStreamer player
add_library(GST_PLAYER STATIC
"../gst_player/gst_player.cc"
)
include_directories(GST_PLAYER STATIC
"../gst_player"
)
add_library(${PLUGIN_NAME} SHARED
"flutter_gstreamer_player_plugin.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)
target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK)
target_link_libraries(${PLUGIN_NAME} PRIVATE ${GST_APP_LIBRARIES} ${GST_VIDEO_LIBRARIES})
target_link_libraries(${PLUGIN_NAME} PRIVATE GST_PLAYER)
# List of absolute paths to libraries that should be bundled with the plugin
set(flutter_gstreamer_player_bundled_libraries
""
PARENT_SCOPE
)
+124
View File
@@ -0,0 +1,124 @@
#include "gst_player.h"
#include "include/flutter_gstreamer_player/flutter_gstreamer_player_plugin.h"
#include "include/flutter_gstreamer_player/flutter_gstreamer_player_video_outlet.h"
#include <flutter_linux/flutter_linux.h>
#include <gtk/gtk.h>
#include <sys/utsname.h>
#include <cstring>
#define FLUTTER_GSTREAMER_PLAYER_PLUGIN(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj), flutter_gstreamer_player_plugin_get_type(), \
FlutterGstreamerPlayerPlugin))
struct _FlutterGstreamerPlayerPlugin {
GObject parent_instance;
FlMethodChannel* method_channel;
FlTextureRegistrar* texture_registrar;
};
std::unordered_map<int32_t, VideoOutlet*> g_video_outlets;
G_DEFINE_TYPE(FlutterGstreamerPlayerPlugin, flutter_gstreamer_player_plugin, g_object_get_type())
// Called when a method call is received from Flutter.
static void flutter_gstreamer_player_plugin_handle_method_call(
FlutterGstreamerPlayerPlugin* self,
FlMethodCall* method_call) {
g_autoptr(FlMethodResponse) response = nullptr;
const gchar* method = fl_method_call_get_name(method_call);
// TODO properly handle these method calls
if (strcmp(method, "getPlatformVersion") == 0) {
struct utsname uname_data = {};
uname(&uname_data);
g_autofree gchar *version = g_strdup_printf("Linux %s", uname_data.version);
g_autoptr(FlValue) result = fl_value_new_string(version);
response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
} else if (strcmp(method, "PlayerRegisterTexture") == 0) {
auto arguments = fl_method_call_get_args(method_call);
auto pipeline =
fl_value_get_string(fl_value_lookup_string(arguments, "pipeline"));
int32_t player_id =
fl_value_get_int(fl_value_lookup_string(arguments, "playerId"));
auto [it, added] = g_video_outlets.try_emplace(player_id, nullptr);
GstPlayer* gstPlayer = g_players->Get(player_id);
if (added) {
it->second = video_outlet_new();
FL_PIXEL_BUFFER_TEXTURE_GET_CLASS(it->second)->copy_pixels =
video_outlet_copy_pixels;
fl_texture_registrar_register_texture(self->texture_registrar,
FL_TEXTURE(it->second));
auto video_outlet_private = (VideoOutletPrivate*) video_outlet_get_instance_private(it->second);
video_outlet_private->texture_id = reinterpret_cast<int64_t>(FL_TEXTURE(it->second));
gstPlayer->onVideo([texture_registrar = self->texture_registrar,
video_outlet_ptr = it->second,
video_outlet_private = video_outlet_private]
(uint8_t* frame, int32_t width, int32_t height) -> void {
video_outlet_private->buffer = frame;
video_outlet_private->video_width = width;
video_outlet_private->video_height = height;
fl_texture_registrar_mark_texture_frame_available(
texture_registrar,
FL_TEXTURE(video_outlet_ptr));
});
}
gstPlayer->play(pipeline);
response =
FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_int(
((VideoOutletPrivate*) video_outlet_get_instance_private(it->second))->texture_id
)));
} else if (strcmp(method, "dispose") == 0) {
g_autoptr(FlValue) result = fl_value_new_bool(true);
response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
fl_method_call_respond(method_call, response, nullptr);
}
static void flutter_gstreamer_player_plugin_dispose(GObject* object) {
G_OBJECT_CLASS(flutter_gstreamer_player_plugin_parent_class)->dispose(object);
}
static void flutter_gstreamer_player_plugin_class_init(FlutterGstreamerPlayerPluginClass* klass) {
G_OBJECT_CLASS(klass)->dispose = flutter_gstreamer_player_plugin_dispose;
}
static void flutter_gstreamer_player_plugin_init(FlutterGstreamerPlayerPlugin* self) {}
static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call,
gpointer user_data) {
FlutterGstreamerPlayerPlugin* plugin = FLUTTER_GSTREAMER_PLAYER_PLUGIN(user_data);
flutter_gstreamer_player_plugin_handle_method_call(plugin, method_call);
}
void flutter_gstreamer_player_plugin_register_with_registrar(FlPluginRegistrar* registrar) {
FlutterGstreamerPlayerPlugin* plugin = FLUTTER_GSTREAMER_PLAYER_PLUGIN(
g_object_new(flutter_gstreamer_player_plugin_get_type(), nullptr));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
g_autoptr(FlMethodChannel) channel =
fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar),
"flutter_gstreamer_player",
FL_METHOD_CODEC(codec));
plugin->texture_registrar =
fl_plugin_registrar_get_texture_registrar(registrar);
fl_method_channel_set_method_call_handler(channel, method_call_cb,
g_object_ref(plugin),
g_object_unref);
g_object_unref(plugin);
}
@@ -0,0 +1,26 @@
#ifndef FLUTTER_PLUGIN_FLUTTER_GSTREAMER_PLAYER_PLUGIN_H_
#define FLUTTER_PLUGIN_FLUTTER_GSTREAMER_PLAYER_PLUGIN_H_
#include <flutter_linux/flutter_linux.h>
G_BEGIN_DECLS
#ifdef FLUTTER_PLUGIN_IMPL
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default")))
#else
#define FLUTTER_PLUGIN_EXPORT
#endif
typedef struct _FlutterGstreamerPlayerPlugin FlutterGstreamerPlayerPlugin;
typedef struct {
GObjectClass parent_class;
} FlutterGstreamerPlayerPluginClass;
FLUTTER_PLUGIN_EXPORT GType flutter_gstreamer_player_plugin_get_type();
FLUTTER_PLUGIN_EXPORT void flutter_gstreamer_player_plugin_register_with_registrar(
FlPluginRegistrar* registrar);
G_END_DECLS
#endif // FLUTTER_PLUGIN_FLUTTER_GSTREAMER_PLAYER_PLUGIN_H_
@@ -0,0 +1,43 @@
#ifndef VIDEO_OUTLET_H_
#define VIDEO_OUTLET_H_
#include <flutter_linux/flutter_linux.h>
#include <gtk/gtk.h>
struct _VideoOutletClass {
FlPixelBufferTextureClass parent_class;
};
struct VideoOutletPrivate {
int64_t texture_id = 0;
uint8_t* buffer = nullptr;
int32_t video_width = 0;
int32_t video_height = 0;
};
G_DECLARE_DERIVABLE_TYPE(VideoOutlet, video_outlet, MY_OPENGL, VIDEO_OUTLET,
FlPixelBufferTexture)
G_DEFINE_TYPE_WITH_CODE(VideoOutlet, video_outlet, fl_pixel_buffer_texture_get_type(), G_ADD_PRIVATE(VideoOutlet))
static VideoOutlet* video_outlet_new() {
return MY_OPENGL_VIDEO_OUTLET(g_object_new(video_outlet_get_type(), nullptr));
}
static gboolean video_outlet_copy_pixels(
FlPixelBufferTexture* texture, const uint8_t** out_buffer, uint32_t* width,
uint32_t* height, GError** error) {
auto video_outlet_private = (VideoOutletPrivate*) video_outlet_get_instance_private(MY_OPENGL_VIDEO_OUTLET(texture));
*out_buffer = video_outlet_private->buffer;
*width = video_outlet_private->video_width;
*height = video_outlet_private->video_height;
return TRUE;
}
static void video_outlet_class_init(VideoOutletClass* klass) {
FL_PIXEL_BUFFER_TEXTURE_CLASS(klass)->copy_pixels = video_outlet_copy_pixels;
}
static void video_outlet_init(VideoOutlet* self) {}
#endif
+62
View File
@@ -0,0 +1,62 @@
name: flutter_gstreamer_player
description: A new flutter plugin project.
version: 0.0.1
homepage:
environment:
sdk: ">=2.16.2 <3.0.0"
flutter: ">=2.5.0"
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# This section identifies this Flutter project as a plugin project.
# The 'pluginClass' and Android 'package' identifiers should not ordinarily
# be modified. They are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
platforms:
linux:
pluginClass: FlutterGstreamerPlayerPlugin
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages
+23
View File
@@ -0,0 +1,23 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_gstreamer_player/flutter_gstreamer_player.dart';
void main() {
const MethodChannel channel = MethodChannel('flutter_gstreamer_player');
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
channel.setMockMethodCallHandler((MethodCall methodCall) async {
return '42';
});
});
tearDown(() {
channel.setMockMethodCallHandler(null);
});
test('getPlatformVersion', () async {
expect(await FlutterGstreamerPlayer.platformVersion, '42');
});
}