I started a quick app to try to organize a group of photos, which I intend to turn into a video. The problem is that I have a bunch of videos in the batch as well that I need to review. I thought I could get the video to work inside my app, but long story short, I wasn’t able to figure it out. I don’t think video is supported on the Linux platform. Maybe if I was working on Android, it would be a different story.
Anyway, maybe some of my notes will be helpful if you are trying things similar.
I started by installing the “video_player” package:
flutter pub add video_player
Then, I found a player that would take less work building a UI:
flutter pub add chewie
I declared a couple of variables for my controllers up at the top of my class:
late VideoPlayerController _controller;
late Future<void> _initializeVideoPlayerFuture;
late Future<void> _controllersFuture;
late ChewieController _chewieController;
Then, in the initialization method, I setup the controllers. I had planned to use the file method and pull the path for the video on my drive, but I never got the sample to work.
@override
void initState() {
// Create an store the VideoPlayerController. The VideoPlayerController
// offers several different constructors to play videos from assets, files,
// or the internet.
// _controller = VideoPlayerController.file(File(widget.videoPath));
_controller = VideoPlayerController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4');
_controllersFuture = initControllers();
super.initState();
}
My initControllers() method sets up both controllers at the same time:
Future<void> initControllers() async {
_initializeVideoPlayerFuture = _controller.initialize();
await _initializeVideoPlayerFuture;
_chewieController = ChewieController(videoPlayerController: _controller, autoPlay: true, looping: true);
}
Here’s the build method that creates the UI. It uses a FutureBuilder to show when it is ready:
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _controllersFuture,
builder: (context, snapshot) {
print("Built ... connectionState -- ${snapshot.connectionState}, hasData ${snapshot.hasError}");
if (snapshot.connectionState == ConnectionState.done) {
// If the VideoPlayerController has finished initialization, use
// the data it provides to limit the aspect ratio of the video.
return AspectRatio(
aspectRatio: _controller.value.aspectRatio,
// Use the VideoPlayer widget to display the video.
child: //VideoPlayer(_controller),
Chewie(controller: _chewieController,),
);
} else {
// If the VideoPlayerController is still initializing, show a
// loading spinner.
return Center(child: CircularProgressIndicator());
}
},
);
}
Don’t forget to dispose the controllers:
@override
void dispose() {
// Ensure disposing of the VideoPlayerController to free up resources.
_controller.dispose();
_chewieController.dispose();
super.dispose();
}
Linux Desktop not supported
Originally, I thought my problem was that Apple .mov files were not supported. So, I thought I could simply change to a supported format such as .mp4 or .webm. Maybe that would have been the case had I been running on Android. Anyway, here is what I went through.
I was getting this error:
PlatformException(channel-error, Unable to establish connection on channel., null, null)
A quick search shows this command can convert the video:
ffmpeg -i input.mov -vcodec h264 -acodec mp2 output.mp4
or for webm:
ffmpeg -i IMG_3548.mov -c:v libvpx -crf 10 -b:v 1M -c:a libvorbis IMG_3548.webm
For the command, I need the new file name with an mp4 on it. I wrote this little procedure to swap out the last 3 characters on the file name:
String getMP4Path() {
if(fileName.length > 0) {
if(fileName.substring(fileName.length - 3, fileName.length).toLowerCase() == "mov") {
return "${_backend.path}/${fileName.substring(0, fileName.length - 3)}mp4";
} else {
return "";
}
} else {
return "";
}
}
So, I found I could use the Process class to call ffmpeg directly from Flutter:
var result = await Process.run("ffmpeg", [
"-i",
picture.path,
"-vcodec",
"h264",
"-acodec",
"mp2",
picture.getMP4Path()
]);
print("converted: ${result.exitCode} -- ${result.stdout}");
Cheating
So, since I can’t get it to work, I just added a button to open the video with the xdg-open command. Here’s the code:
ElevatedButton(
onPressed: () {
var result = Process.run("xdg-open", [picture.path]);
},
child: Text("Open"),
),
Resources
- Cookbook: Play and Pause Video
- StackOverflow: Video widget doesn’t play “Unable to establish connection on channel”
- AskUbuntu: Converting .mov files to .mp4 using FFmpeg
- DWB: Convert Videos (MP4, MPEG, or MOV) to WEBM
- Chewie Package
- Github Flutter Issue: Unhandled Exception: PlatformException(channel-error, Unable to establish connection on channel., null) #61096