diff --git a/packages/video_player_avplay/CHANGELOG.md b/packages/video_player_avplay/CHANGELOG.md index c2a649203..8419f55d7 100644 --- a/packages/video_player_avplay/CHANGELOG.md +++ b/packages/video_player_avplay/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.15 + +* Add a `playerEngine` option to `VideoPlayerController.network` to select which native playback engine drives a source: `PlayerEngine.adaptiveStreaming` (specialized for DASH/HLS/Smooth Streaming and DRM) or `PlayerEngine.general` (the general-purpose media engine). Defaults to `PlayerEngine.auto`, which keeps the existing URL-based selection, so existing behavior is unchanged. + ## 0.8.14 * Update plusplayer diff --git a/packages/video_player_avplay/README.md b/packages/video_player_avplay/README.md index eb6dc74b3..8b360487b 100644 --- a/packages/video_player_avplay/README.md +++ b/packages/video_player_avplay/README.md @@ -12,7 +12,7 @@ To use this package, add `video_player_avplay` as a dependency in your `pubspec. ```yaml dependencies: - video_player_avplay: ^0.8.14 + video_player_avplay: ^0.8.15 ``` Then you can import `video_player_avplay` in your Dart code: @@ -61,6 +61,25 @@ Note that if you play dash streams, please add dash format when creating the pla formatHint: VideoFormat.dash); ``` +### Selecting the playback engine + +The plugin drives playback with one of two native engines that differ in capability: + +| `PlayerEngine` | Specialized for | +| ------------------------- | -------------------------------------------------- | +| `adaptiveStreaming` | DASH / HLS / Smooth Streaming and DRM | +| `general` | general-purpose playback of a single media source | + +By default (`PlayerEngine.auto`) the engine is chosen from the URI: one beginning with `http` uses the adaptive-streaming engine, otherwise the general-purpose engine. If a source fails on the auto-selected engine — for example media served through a localhost reverse-proxy, which is an `http://` URI but plays only on the general-purpose engine — you can force one with `playerEngine`: + +```dart + VideoPlayerController.network( + 'http://127.0.0.1:PORT/stream', + playerEngine: PlayerEngine.general); // or PlayerEngine.adaptiveStreaming +``` + +This selects the engine, not the content format: the general-purpose engine can still play adaptive content such as a DASH manifest. `playerEngine` is only available on `VideoPlayerController.network`; other sources always use the general-purpose engine. The engine is fixed when the player is created and is not re-evaluated on restore. + ### Example ```dart diff --git a/packages/video_player_avplay/lib/src/video_player_tizen.dart b/packages/video_player_avplay/lib/src/video_player_tizen.dart index 9b68a50ff..f433e4b50 100644 --- a/packages/video_player_avplay/lib/src/video_player_tizen.dart +++ b/packages/video_player_avplay/lib/src/video_player_tizen.dart @@ -38,7 +38,10 @@ class VideoPlayerTizen extends VideoPlayerPlatform { message.formatHint = _videoFormatStringMap[dataSource.formatHint]; message.httpHeaders = dataSource.httpHeaders; message.drmConfigs = dataSource.drmConfigs?.toMap(); - message.playerOptions = dataSource.playerOptions; + message.playerOptions = _playerOptionsWithEngine( + dataSource.playerOptions, + dataSource.playerEngine, + ); message.streamingProperty = dataSource.streamingProperty == null ? null : { @@ -275,7 +278,10 @@ class VideoPlayerTizen extends VideoPlayerPlatform { message.formatHint = _videoFormatStringMap[dataSource.formatHint]; message.httpHeaders = dataSource.httpHeaders; message.drmConfigs = dataSource.drmConfigs?.toMap(); - message.playerOptions = dataSource.playerOptions; + message.playerOptions = _playerOptionsWithEngine( + dataSource.playerOptions, + dataSource.playerEngine, + ); message.streamingProperty = dataSource.streamingProperty == null ? null : { @@ -497,6 +503,20 @@ class VideoPlayerTizen extends VideoPlayerPlatform { return EventChannel('tizen/video_player/video_events_$playerId'); } + /// Carries the abstract [PlayerEngine] selection to the native side by adding + /// a `playerEngine` key to the playerOptions map (read in the plugin's + /// `Create`). Omitted for [PlayerEngine.auto] so the wire is unchanged for + /// existing callers. + static Map? _playerOptionsWithEngine( + Map? options, + PlayerEngine engine, + ) { + if (engine == PlayerEngine.auto) { + return options; + } + return {...?options, 'playerEngine': engine.name}; + } + static const Map _videoFormatStringMap = { VideoFormat.ss: 'ss', diff --git a/packages/video_player_avplay/lib/video_player.dart b/packages/video_player_avplay/lib/video_player.dart index 1a9d2e38b..932f94e74 100644 --- a/packages/video_player_avplay/lib/video_player.dart +++ b/packages/video_player_avplay/lib/video_player.dart @@ -355,6 +355,7 @@ class VideoPlayerController extends ValueNotifier { drmConfigs = null, playerOptions = const {}, streamingProperty = null, + playerEngine = PlayerEngine.auto, super( VideoPlayerValue( duration: DurationRange(Duration.zero, Duration.zero), @@ -379,6 +380,7 @@ class VideoPlayerController extends ValueNotifier { this.drmConfigs, this.playerOptions, this.streamingProperty, + this.playerEngine = PlayerEngine.auto, }) : dataSourceType = DataSourceType.network, package = null, super( @@ -403,6 +405,7 @@ class VideoPlayerController extends ValueNotifier { drmConfigs = null, playerOptions = const {}, streamingProperty = null, + playerEngine = PlayerEngine.auto, super( VideoPlayerValue( duration: DurationRange(Duration.zero, Duration.zero), @@ -429,6 +432,7 @@ class VideoPlayerController extends ValueNotifier { drmConfigs = null, playerOptions = const {}, streamingProperty = null, + playerEngine = PlayerEngine.auto, super( VideoPlayerValue( duration: DurationRange(Duration.zero, Duration.zero), @@ -457,6 +461,14 @@ class VideoPlayerController extends ValueNotifier { /// Only for [VideoPlayerController.network]. final Map? streamingProperty; + /// Selects which native playback engine drives the source. + /// + /// [PlayerEngine.auto] (the default) keeps the historical URI-based selection. + /// Use [PlayerEngine.adaptiveStreaming] or [PlayerEngine.general] to force an + /// engine when a source fails on the auto-selected one. Only for + /// [VideoPlayerController.network]. + final PlayerEngine playerEngine; + /// **Android only**. Will override the platform's generic file format /// detection with whatever is set here. final VideoFormat? formatHint; @@ -563,6 +575,7 @@ class VideoPlayerController extends ValueNotifier { drmConfigs: drmConfigs, playerOptions: playerOptions, streamingProperty: streamingProperty, + playerEngine: playerEngine, ); case DataSourceType.file: dataSourceDescription = DataSource( diff --git a/packages/video_player_avplay/lib/video_player_platform_interface.dart b/packages/video_player_avplay/lib/video_player_platform_interface.dart index d6a4594f9..e58e945c0 100644 --- a/packages/video_player_avplay/lib/video_player_platform_interface.dart +++ b/packages/video_player_avplay/lib/video_player_platform_interface.dart @@ -253,6 +253,7 @@ class DataSource { this.drmConfigs, this.playerOptions, this.streamingProperty, + this.playerEngine = PlayerEngine.auto, }); /// The way in which the video was originally loaded. @@ -291,6 +292,11 @@ class DataSource { /// Sets specific feature values for HTTP, MMS, or specific streaming engine Map? streamingProperty; + + /// Selects which native playback engine drives the source. + /// + /// Only for [DataSourceType.network]; defaults to [PlayerEngine.auto]. + final PlayerEngine playerEngine; } /// The way in which the video was originally loaded. @@ -311,6 +317,29 @@ enum DataSourceType { contentUri, } +/// The native playback engine used to play a source. +/// +/// The plugin has two native engines with different capabilities. This selects +/// which one drives playback, independently of the content format +/// ([VideoFormat]). +enum PlayerEngine { + /// Selects the engine automatically from the source (default; preserves the + /// historical behavior): `http(s)` URIs use the adaptive-streaming engine, + /// local/asset sources use the general-purpose engine. + auto, + + /// Requests the engine specialized for adaptive streaming (DASH/HLS/Smooth + /// Streaming), DRM, and the full streaming-property control surface. + adaptiveStreaming, + + /// Requests the general-purpose media engine. Use it when a source fails to + /// prepare on the adaptive-streaming engine — e.g. media served through a + /// localhost reverse-proxy. This selects the engine, not the content format: + /// the general-purpose engine can still play adaptive content such as a DASH + /// manifest. + general, +} + /// The file format of the given video. enum VideoFormat { /// Dynamic Adaptive Streaming over HTTP, also known as MPEG-DASH. diff --git a/packages/video_player_avplay/pubspec.yaml b/packages/video_player_avplay/pubspec.yaml index e26f05c5a..3d3c4bf2f 100644 --- a/packages/video_player_avplay/pubspec.yaml +++ b/packages/video_player_avplay/pubspec.yaml @@ -2,7 +2,7 @@ name: video_player_avplay description: Flutter plugin for displaying inline video on Tizen TV devices. homepage: https://github.com/flutter-tizen/plugins repository: https://github.com/flutter-tizen/plugins/tree/master/packages/video_player_avplay -version: 0.8.14 +version: 0.8.15 environment: sdk: ">=3.1.0 <4.0.0" diff --git a/packages/video_player_avplay/tizen/src/video_player_tizen_plugin.cc b/packages/video_player_avplay/tizen/src/video_player_tizen_plugin.cc index 15a228740..f7ff16bb3 100644 --- a/packages/video_player_avplay/tizen/src/video_player_tizen_plugin.cc +++ b/packages/video_player_avplay/tizen/src/video_player_tizen_plugin.cc @@ -15,9 +15,11 @@ #include #include +#include "log.h" #include "media_player.h" #include "messages.h" #include "plus_player.h" +#include "video_player.h" #include "video_player_options.h" namespace video_player_avplay_tizen { @@ -144,7 +146,25 @@ ErrorOr VideoPlayerTizenPlugin::Create( } std::unique_ptr player = nullptr; - if (uri.substr(0, 4) == "http") { + // The "playerEngine" option lets the app force a native playback engine: + // "adaptiveStreaming" (specialized for DASH/HLS/Smooth Streaming and DRM) or + // "general" (the general-purpose media engine). When it is unset or "auto", + // the historical heuristic is used: a URI beginning with "http" uses the + // adaptive-streaming engine, otherwise the general-purpose engine. + std::string player_engine = flutter_common::GetValue( + msg.player_options(), "playerEngine", std::string()); + bool use_plus_player = uri.substr(0, 4) == "http"; + if (player_engine == "adaptiveStreaming") { + use_plus_player = true; + } else if (player_engine == "general") { + use_plus_player = false; + } else if (!player_engine.empty() && player_engine != "auto") { + LOG_ERROR( + "[VideoPlayerTizenPlugin] Unknown playerEngine \"%s\"; using automatic " + "selection.", + player_engine.c_str()); + } + if (use_plus_player) { player = std::make_unique( plugin_registrar_->messenger(), FlutterDesktopPluginRegistrarGetView(registrar_ref_));