'Flutter video player - Playback speed couldn't be set after rebuilding with new VideoPlayerController
I have a problem with the video player. The playback speed is unable to be set. Steps:
- Create a player with a VideoPlayerController
- Change the playback speed to 2.0.
- Re-create a new
VideoPlayerController
, and set playback speed as the old playback speed (2.0). - Re-build the player -> playback speed is reset to 1.0. For more details, please refer to my code sample. Thank you!
// 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/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 Scaffold(
key: const ValueKey<String>('home_page'),
appBar: AppBar(
title: const Text('Video player example'),
),
body: _BumbleBeeRemoteVideo(),
);
}
}
class _BumbleBeeRemoteVideo extends StatefulWidget {
@override
_BumbleBeeRemoteVideoState createState() => _BumbleBeeRemoteVideoState();
}
class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> {
late VideoPlayerController _controller;
M3u8 _currentM3u8 = _ControlsOverlay._exampleM3u8s.first;
@override
void initState() {
super.initState();
_controller = VideoPlayerController.network(
'https://multiplatform-f.akamaihd.net/i/multi/will/bunny/big_buck_bunny_,640x360_400,640x360_700,640x360_1000,950x540_1500,.f4v.csmil/master.m3u8',
formatHint: VideoFormat.hls,
);
_controller.addListener(() {
setState(() {});
});
_controller.setLooping(true);
_controller.initialize();
}
_resetControllerWithM3u8(M3u8 m3u8) {
final speed = _controller.value.playbackSpeed;
final position = _controller.value.position;
_controller.pause();
_controller = VideoPlayerController.network(
m3u8.url,
formatHint: VideoFormat.hls,
);
_controller.addListener(() {
setState(() {});
});
// THE PLAYBACK SPEED IS NOT CHANGED.
_controller.setPlaybackSpeed(speed);
_controller.setLooping(true);
_controller.initialize().then((_) {
_controller.play();
_controller.seekTo(position);
});
}
@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 m3u8'),
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,
onChangeQuality: (m3u8) {
_currentM3u8 = m3u8;
_resetControllerWithM3u8(m3u8);
},
currentM3u8: _currentM3u8,
),
VideoProgressIndicator(_controller, allowScrubbing: true),
],
),
),
),
],
),
);
}
}
class _ControlsOverlay extends StatelessWidget {
const _ControlsOverlay(
{Key? key,
required this.controller,
required this.onChangeQuality,
required this.currentM3u8})
: super(key: key);
static const List<Duration> _exampleCaptionOffsets = <Duration>[
Duration(seconds: -10),
Duration(seconds: -3),
Duration(seconds: -1, milliseconds: -500),
Duration(milliseconds: -250),
Duration(milliseconds: 0),
Duration(milliseconds: 250),
Duration(seconds: 1, milliseconds: 500),
Duration(seconds: 3),
Duration(seconds: 10),
];
static const List<double> _examplePlaybackRates = <double>[
0.25,
0.5,
1.0,
1.5,
2.0,
3.0,
5.0,
10.0,
];
static const List<M3u8> _exampleM3u8s = [
M3u8('Auto',
'https://multiplatform-f.akamaihd.net/i/multi/will/bunny/big_buck_bunny_,640x360_400,640x360_700,640x360_1000,950x540_1500,.f4v.csmil/master.m3u8'),
M3u8('640x360',
'https://multiplatform-f.akamaihd.net/i/multi/will/bunny/big_buck_bunny_,640x360_400,640x360_700,640x360_1000,950x540_1500,.f4v.csmil/index_0_av.m3u8'),
M3u8('960x540',
'https://multiplatform-f.akamaihd.net/i/multi/will/bunny/big_buck_bunny_,640x360_400,640x360_700,640x360_1000,950x540_1500,.f4v.csmil/index_3_av.m3u8'),
];
final VideoPlayerController controller;
final Function(M3u8) onChangeQuality;
final M3u8 currentM3u8;
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
AnimatedSwitcher(
duration: const Duration(milliseconds: 50),
reverseDuration: const Duration(milliseconds: 200),
child: controller.value.isPlaying
? const SizedBox.shrink()
: Container(
color: Colors.black26,
child: const Center(
child: Icon(
Icons.play_arrow,
color: Colors.white,
size: 100.0,
semanticLabel: 'Play',
),
),
),
),
GestureDetector(
onTap: () {
controller.value.isPlaying ? controller.pause() : controller.play();
},
),
Align(
alignment: Alignment.topLeft,
child: PopupMenuButton<Duration>(
initialValue: controller.value.captionOffset,
tooltip: 'Caption Offset',
onSelected: (Duration delay) {
controller.setCaptionOffset(delay);
},
itemBuilder: (BuildContext context) {
return <PopupMenuItem<Duration>>[
for (final Duration offsetDuration
in _ControlsOverlay._exampleCaptionOffsets)
PopupMenuItem<Duration>(
value: offsetDuration,
child: Text('${offsetDuration.inMilliseconds}ms'),
)
];
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
child: Text('${controller.value.captionOffset.inMilliseconds}ms'),
),
),
),
Align(
alignment: Alignment.topRight,
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [
PopupMenuButton<M3u8>(
initialValue: currentM3u8,
tooltip: 'Video Quality',
onSelected: (m3u8) {
onChangeQuality(m3u8);
},
itemBuilder: (BuildContext context) {
return <PopupMenuItem<M3u8>>[
for (final m3u8 in _ControlsOverlay._exampleM3u8s)
PopupMenuItem<M3u8>(
value: m3u8,
child: Text(m3u8.quality),
)
];
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
child: Text(currentM3u8.quality),
),
),
PopupMenuButton<double>(
initialValue: controller.value.playbackSpeed,
tooltip: 'Playback speed',
onSelected: (double speed) {
controller.setPlaybackSpeed(speed);
},
itemBuilder: (BuildContext context) {
return <PopupMenuItem<double>>[
for (final double speed
in _ControlsOverlay._examplePlaybackRates)
PopupMenuItem<double>(
value: speed,
child: Text('${speed}x'),
)
];
},
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 16,
),
child: Text('${controller.value.playbackSpeed}x'),
),
),
],
),
),
],
);
}
}
class M3u8 {
final String quality;
final String url;
const M3u8(this.quality, this.url);
}
flutter doctor:
[✓] Flutter (Channel stable, 2.10.3, on macOS 12.3 21E230 darwin-arm, locale en-VN)
[✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
[✓] Xcode - develop for iOS and macOS (Xcode 13.2.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.1)
[✓] VS Code (version 1.65.2)
[✓] Connected device (2 available)
[✓] HTTP Host Availability
• No issues found!
I run the app on the simulator iPhone 13 iOS 15.2. To reproduce the issue, run the code sample -> change the playback speed to 2.0 -> change the quality of the video -> the playback speed is reset to 1.0.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|