7

I'm using flutter video_player package to play a list of video.

List sourceList;

sourceList = [
  {
    "size": 69742504,
    "name": "lucky-roulette.mp4",
    "mimetype": "video/mp4",
  },
  {
    "size": 69742504,
    "name": "BigBuckBunny.mp4",
    "mimetype": "video/mp4",
  }
];

I've checked out this issue, and did some custom codes upon it.

void play() {
  log.fine("Now playing: $_nowPlayingUrl");
  _adController = VideoPlayerController.network(_nowPlayingUrl);
  _adController.initialize().then((_) => setState(() {}));
  _adController.play();
  _adController.addListener(checkIfVideoFinished);
}

void checkIfVideoFinished() {
  if (_adController == null ||
      _adController.value == null ||
      _adController.value.position == null ||
      _adController.value.duration == null) return;
  if (_adController.value.position.inSeconds ==
      _adController.value.duration.inSeconds) {
    _adController.removeListener(checkIfVideoFinished);
    _adController.dispose();
    // Change _nowPlayingIndex
    setState(() {
      _nowPlayingIndex = (_nowPlayingIndex + 1) % _totalIndex;
    });
    play();
  }
}

But use this code snippet would send out an exception Another exception was thrown: A VideoPlayerController was used after being disposed.

Is there a better way to play and loop a list of video in Flutter?

Bobson Lin
  • 75
  • 1
  • 2
  • 5
  • The Best Answer Should Be, which is not mine but used in 2 flutter projects and working well https://stackoverflow.com/a/58959097/10329023 Special thanks to @igor-kharakhordin Help Others and Happy Coding :) – Kamlesh Nov 08 '20 at 13:30

4 Answers4

8

Recently I tested video list example. please check the source in github FlutterVideoListSample. I think the video widget must be disposed.

In my case, I clear the old VideoPlayerController before initialize it. And I don't use chewie plugin that make new page in entering fullscreen so cannot handle the next video widget.

dependencies
video_player: '>=0.10.11+1 <2.0.0'
some code in FlutterVideoListSample
VideoPlayerController _controller;

void _initializeAndPlay(int index) async {
  print("_initializeAndPlay ---------> $index");
  final clip = _clips[index];
  final controller = VideoPlayerController.asset(clip.videoPath());
  final old = _controller;
  if (old != null) {
    old.removeListener(_onControllerUpdated);
    old.pause(); // mute instantly
  }
  _controller = controller;
  setState(() {
    debugPrint("---- controller changed");
  });

  controller
    ..initialize().then((_) {
      debugPrint("---- controller initialized");
      old?.dispose();
      _playingIndex = index;
      controller.addListener(_onControllerUpdated);
      controller.play();
      setState(() {});
    });
}
Brownsoo Han
  • 4,549
  • 3
  • 20
  • 20
  • How long does it take to initialize the video? I'm using 'videoPlayerController.value.initialized' to check if the video is initialized...sometimes it takes up to 10 seconds to initialize the video. – Soropromo Mar 07 '20 at 20:03
  • I took under 1 sec. I don't know why your video take long time to initialize. But,, I used 720x480 short videos under 4min. And that videos rendered with 1 second interval key frame. @Soropromo – Brownsoo Han Mar 08 '20 at 07:31
  • 1
    Hi @BrownsooHan. I copied and pasted your code and after 5 times playing videos, throw this **exception: com.google.android.exoplayer2.ExoPlaybackException: com.google.android.exoplayer2.audio.AudioSink$InitializationException: AudioTrack init failed: 0, Config(48000, 12, 49248)** I'm using a Pixel C – Sami Issa Apr 01 '20 at 17:03
  • Hi, @SamiIssa , I used this code only in iOS. try `_controller?.dispose();` in _clearPrevious(). I gonna try this in android. If I also encount or resolve the issue, updated this post. If you find a solution, please reply the resolution. – Brownsoo Han Apr 03 '20 at 03:01
  • @SamiIssa : I agree.. I am facing this problem with the app that has already been pushed to prod now. – rohan koshti Jul 03 '20 at 11:56
  • In my case, I have video controller in separate widget and list videos in separate widget. I want when user click a video from list of video, then previously playing video should stop and new video should play. How can I do this with provider or scoped model. – Wikki Sep 29 '20 at 21:40
  • I think you need to use some property tracking play status in app level. – Brownsoo Han Sep 30 '20 at 23:39
  • @SamiIssa I was facing same issue but the best solution is at https://stackoverflow.com/a/64738624/10329023 – Kamlesh Nov 08 '20 at 13:30
  • How to add Video Quality option (720p, 480p, etc.) like Youtube to this video_player to play m3u8 format videos ??? any one knows? – Gulshan Yadav Dec 09 '20 at 10:43
3

You must call the video controller dispose method in Override dispose method. Need not call dispose method when removevideo.

Akio Alex
  • 316
  • 1
  • 9
2

This is how I played a list of videos, I am showing this example from the flutter web application where most of the video players are not supported that is why I am using the default one.

But on Android and IOS, this task is very easy to do with help of chewie or better_player packages. These packages provides very rich configuration step on the go.

Hope it will help in your case.

ListView.separated(
              shrinkWrap: true,
              itemCount: 2,
              itemBuilder: (context, index) {
                VideoPlayerController controller =
                    VideoPlayerController.network(
                        'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8')
                      ..initialize().then((_) {});

                controller.play();

                return AspectRatio(
                  aspectRatio: 16 / 9,
                  child: VideoPlayer(controller),
                );
              },
              separatorBuilder: (context, index) {
                return const SizedBox(height: 16.0);
              },
            ),
Ali Murtaza
  • 414
  • 4
  • 9
0

For anyone still having issues with this, I was able to find a way around it.

late VideoPlayerController _controller;
ImagePicker _picker = ImagePicker();

String _videoPath = '';

@override
void initState() {
  super.initState();
  _controller = VideoPlayerController.file(
    File(_videoPath)
  );

  _controller.addListener(() {
    setState(() {});
  });
  _controller.initialize().then((_) => setState(() {}));
  _controller.pause();
}

Now you should have a function that selects the video from your device:::

_chooseFromGallery(){
   _picker.pickVideo(source: ImageSource.gallery).then(((value) {
     if(value != null) {
        _videoPath = value.path;
        _controller.dispose(); // To remove an already existing playing video
        _controller = VideoPlayerController.file(File(_videoPath)); // Update the controller with a new file
        _controller.initialize().then((_) => setState(() {})); // Re-initialize the controller again.
      }
   }));
}

Then on the UI section:

SizedBox(
  child: _videoPath.isEmpty()
       ? Container()
       : AspectRatio(
           aspectRatio: _controller.value.aspectRatio,
           child: Stack(
           alignment: Alignment.bottomCenter,
           children: <Widget>[
              VideoPlayer(_controller),
           ],
          ),
        ),
  ),

NB: You can hook the '_chooseFromGallery' function to any widget you want to use for it, could be a button.