Skip to content

[video_player] Seek to live edge #9181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
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
64 changes: 63 additions & 1 deletion packages/video_player/video_player/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class _App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
length: 4,
child: Scaffold(
key: const ValueKey<String>('home_page'),
appBar: AppBar(
Expand Down Expand Up @@ -51,6 +51,7 @@ class _App extends StatelessWidget {
),
Tab(icon: Icon(Icons.insert_drive_file), text: 'Asset'),
Tab(icon: Icon(Icons.list), text: 'List example'),
Tab(icon: Icon(Icons.stream), text: 'HLS live'),
],
),
),
Expand All @@ -59,6 +60,7 @@ class _App extends StatelessWidget {
_BumbleBeeRemoteVideo(),
_ButterFlyAssetVideo(),
_ButterFlyAssetVideoInList(),
_HLSVideo(),
],
),
),
Expand Down Expand Up @@ -271,6 +273,66 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> {
}
}

class _HLSVideo extends StatefulWidget {
@override
_HLSVideoState createState() => _HLSVideoState();
}

class _HLSVideoState extends State<_HLSVideo> {
late VideoPlayerController _controller;

@override
void initState() {
super.initState();
_controller = VideoPlayerController.networkUrl(
Uri.parse('https://ireplay.tv/test/blender.m3u8'),
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 HLS live stream'),
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),
],
),
),
),
TextButton(
onPressed: () => _controller.seekToDefaultPosition(),
child: const Text('Seek to default pos')),
],
),
);
}
}

class _ControlsOverlay extends StatelessWidget {
const _ControlsOverlay({required this.controller});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
D341BFD5EA4254315E3CCE6E /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
Expand Down Expand Up @@ -306,6 +307,23 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
D341BFD5EA4254315E3CCE6E /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
D3E396DFBCC51886820113AA /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand Down
10 changes: 10 additions & 0 deletions packages/video_player/video_player/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ environment:
sdk: ^3.4.0
flutter: ">=3.22.0"

dependency_overrides:
video_player_android:
path: ../../video_player_android
video_player_avfoundation:
path: ../../video_player_avfoundation
video_player_web:
path: ../../video_player_web
video_player_platform_interface:
path: ../../video_player_platform_interface

dependencies:
flutter:
sdk: flutter
Expand Down
14 changes: 14 additions & 0 deletions packages/video_player/video_player/lib/video_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,20 @@ class VideoPlayerController extends ValueNotifier<VideoPlayerValue> {
_updatePosition(position);
}

/// Seeks to the default position associated with the current MediaItem.
///
/// The position can depend on the type of media being played.
/// For live streams it will typically be the live edge.
/// For other streams it will typically be the start.
Future<void> seekToDefaultPosition() async {
if (_isDisposedOrNotInitialized) {
return;
}
await _videoPlayerPlatform.seekToDefaultPosition(_textureId);
final position = await _videoPlayerPlatform.getPosition(_textureId);
_updatePosition(position);
}

/// Sets the audio volume of [this].
///
/// [volume] indicates a value between 0.0 (silent) and 1.0 (full volume) on a
Expand Down
10 changes: 10 additions & 0 deletions packages/video_player/video_player/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ flutter:
web:
default_package: video_player_web

dependency_overrides:
video_player_android:
path: ../video_player_android
video_player_avfoundation:
path: ../video_player_avfoundation
video_player_web:
path: ../video_player_web
video_player_platform_interface:
path: ../video_player_platform_interface

dependencies:
flutter:
sdk: flutter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class FakeController extends ValueNotifier<VideoPlayerValue>
@override
Future<void> seekTo(Duration moment) async {}

@override
Future<void> seekToDefaultPosition() async {}

@override
Future<void> setVolume(double volume) async {}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// 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.
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon

package io.flutter.plugins.videoplayer;
Expand Down Expand Up @@ -393,6 +393,8 @@ public interface AndroidVideoPlayerApi {

void seekTo(@NonNull Long playerId, @NonNull Long position);

void seekToDefaultPosition(@NonNull Long playerId);

void pause(@NonNull Long playerId);

void setMixWithOthers(@NonNull Boolean mixWithOthers);
Expand All @@ -401,7 +403,6 @@ public interface AndroidVideoPlayerApi {
static @NonNull MessageCodec<Object> getCodec() {
return PigeonCodec.INSTANCE;
}

/**
* Sets up an instance of `AndroidVideoPlayerApi` to handle messages through the
* `binaryMessenger`.
Expand Down Expand Up @@ -643,6 +644,31 @@ static void setUp(
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekToDefaultPosition"
+ messageChannelSuffix,
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<>();
ArrayList<Object> args = (ArrayList<Object>) message;
Long playerIdArg = (Long) args.get(0);
try {
api.seekToDefaultPosition(playerIdArg);
wrapped.add(0, null);
} catch (Throwable exception) {
wrapped = wrapError(exception);
}
reply.reply(wrapped);
});
} else {
channel.setMessageHandler(null);
}
}
{
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ void seekTo(int location) {
exoPlayer.seekTo(location);
}

void seekToDefaultPosition() {
exoPlayer.seekToDefaultPosition();
}

long getPosition() {
return exoPlayer.getCurrentPosition();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ public void seekTo(@NonNull Long playerId, @NonNull Long position) {
player.seekTo(position.intValue());
}

@Override
public void seekToDefaultPosition(@NonNull Long playerId) {
VideoPlayer player = getPlayer(playerId);
player.seekToDefaultPosition();
}

@Override
public void pause(@NonNull Long playerId) {
VideoPlayer player = getPlayer(playerId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ environment:
sdk: ^3.5.0
flutter: ">=3.24.0"

dependency_overrides:
video_player_android:
path: ../../video_player_android
video_player_platform_interface:
path: ../../video_player_platform_interface

dependencies:
flutter:
sdk: flutter
Expand All @@ -18,7 +24,8 @@ dependencies:
# 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: ../
video_player_platform_interface: ^6.3.0
video_player_platform_interface:
path: ../../video_player_platform_interface

dev_dependencies:
espresso: ^0.4.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ class AndroidVideoPlayer extends VideoPlayerPlatform {
return _api.seekTo(playerId, position.inMilliseconds);
}

@override
Future<void> seekToDefaultPosition(int playerId) {
return _api.seekToDefaultPosition(playerId);
}

@override
Future<Duration> getPosition(int playerId) async {
final int position = await _api.position(playerId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// 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.
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers

Expand Down Expand Up @@ -382,6 +382,30 @@ class AndroidVideoPlayerApi {
}
}

Future<void> seekToDefaultPosition(int playerId) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.seekToDefaultPosition$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[playerId]) as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}

Future<void> pause(int playerId) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.video_player_android.AndroidVideoPlayerApi.pause$pigeonVar_messageChannelSuffix';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ abstract class AndroidVideoPlayerApi {
void play(int playerId);
int position(int playerId);
void seekTo(int playerId, int position);
void seekToDefaultPosition(int playerId);
void pause(int playerId);
void setMixWithOthers(bool mixWithOthers);
}
4 changes: 4 additions & 0 deletions packages/video_player/video_player_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ flutter:
package: io.flutter.plugins.videoplayer
pluginClass: VideoPlayerPlugin

dependency_overrides:
video_player_platform_interface:
path: ../video_player_platform_interface

dependencies:
flutter:
sdk: flutter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ class _ApiLogger implements TestHostVideoPlayerApi {
passedPosition = position;
}

@override
void seekToDefaultPosition(int playerId) {
log.add('seekToDefaultPosition');
passedPlayerId = playerId;
}

@override
void setLooping(int playerId, bool looping) {
log.add('setLooping');
Expand Down
Loading