Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/video_player_avplay/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
21 changes: 20 additions & 1 deletion packages/video_player_avplay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
24 changes: 22 additions & 2 deletions packages/video_player_avplay/lib/src/video_player_tizen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
: <String, String>{
Expand Down Expand Up @@ -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
: <String, String>{
Expand Down Expand Up @@ -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<String, dynamic>? _playerOptionsWithEngine(
Map<String, dynamic>? options,
PlayerEngine engine,
) {
if (engine == PlayerEngine.auto) {
return options;
}
return <String, dynamic>{...?options, 'playerEngine': engine.name};
}

static const Map<VideoFormat, String> _videoFormatStringMap =
<VideoFormat, String>{
VideoFormat.ss: 'ss',
Expand Down
13 changes: 13 additions & 0 deletions packages/video_player_avplay/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
drmConfigs = null,
playerOptions = const <String, dynamic>{},
streamingProperty = null,
playerEngine = PlayerEngine.auto,
super(
VideoPlayerValue(
duration: DurationRange(Duration.zero, Duration.zero),
Expand All @@ -379,6 +380,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
this.drmConfigs,
this.playerOptions,
this.streamingProperty,
this.playerEngine = PlayerEngine.auto,
}) : dataSourceType = DataSourceType.network,
package = null,
super(
Expand All @@ -403,6 +405,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
drmConfigs = null,
playerOptions = const <String, dynamic>{},
streamingProperty = null,
playerEngine = PlayerEngine.auto,
super(
VideoPlayerValue(
duration: DurationRange(Duration.zero, Duration.zero),
Expand All @@ -429,6 +432,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
drmConfigs = null,
playerOptions = const <String, dynamic>{},
streamingProperty = null,
playerEngine = PlayerEngine.auto,
super(
VideoPlayerValue(
duration: DurationRange(Duration.zero, Duration.zero),
Expand Down Expand Up @@ -457,6 +461,14 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
/// Only for [VideoPlayerController.network].
final Map<StreamingPropertyType, String>? 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;
Comment on lines +464 to +470

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To make the new PlayerEngine enum easily accessible to users of the plugin, it should be exported from video_player.dart. Currently, video_player.dart does not export video_player_platform_interface.dart or its members, meaning users would have to explicitly import video_player_platform_interface.dart to use PlayerEngine.general or PlayerEngine.adaptiveStreaming.\n\nPlease add the following export at the top of packages/video_player_avplay/lib/video_player.dart:\ndart\nexport 'video_player_platform_interface.dart' show PlayerEngine;\n

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I left PlayerEngine in the platform interface to match the package's existing convention — the sibling enums used in the same VideoPlayerController.network call (e.g. VideoFormat, the formatHint type) live in video_player_platform_interface.dart too and aren't re-exported, and the plugin's own example/lib/main.dart imports both libraries:

import 'package:video_player_avplay/video_player.dart';
import 'package:video_player_avplay/video_player_platform_interface.dart';

Exporting only PlayerEngine would make it the one exception. If you'd prefer better ergonomics here, the consistent move would be to export 'video_player_platform_interface.dart'; so VideoFormat, DataSourceType, PlayerEngine, … are all reachable from video_player.dart — happy to add that to this PR or keep the current convention, whichever you'd prefer.


/// **Android only**. Will override the platform's generic file format
/// detection with whatever is set here.
final VideoFormat? formatHint;
Expand Down Expand Up @@ -563,6 +575,7 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
drmConfigs: drmConfigs,
playerOptions: playerOptions,
streamingProperty: streamingProperty,
playerEngine: playerEngine,
);
case DataSourceType.file:
dataSourceDescription = DataSource(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -291,6 +292,11 @@ class DataSource {

/// Sets specific feature values for HTTP, MMS, or specific streaming engine
Map<StreamingPropertyType, String>? 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.
Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion packages/video_player_avplay/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
#include <string>
#include <variant>

#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 {
Expand Down Expand Up @@ -144,7 +146,25 @@ ErrorOr<PlayerMessage> VideoPlayerTizenPlugin::Create(
}

std::unique_ptr<VideoPlayer> 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<PlusPlayer>(
plugin_registrar_->messenger(),
FlutterDesktopPluginRegistrarGetView(registrar_ref_));
Expand Down
Loading