如何在运行时正确更改视频播放器源?

Kar*_*ski 7 android dart flutter

我正在构建一个基本上是 YouTube 克隆的应用程序。我使用官方的 video_player 插件进行播放,使用 Chewie 进行控制。我想实现一个质量切换器,以便用户可以决定他们希望视频以什么质量进行流式传输

我已经构建了一个带有开关的底部工作表,changeQuality()当用户选择所需的质量时我会运行。它应该做的只是向旧播放器提供新的源文件,并从视频离开的位置继续播放。

这是在 initState() 上运行的视频播放器和咀嚼播放器:

videoPlayer = VideoPlayerController.network(data == null
    ? dataAll[indexNo]["video"]["480"]
    : data[indexNo]["video"]["480"]);

chewieController = ChewieController(
    videoPlayerController: videoPlayer,
    aspectRatio: 16 / 9,
    autoPlay: true,
    allowedScreenSleep: false,
    placeholder: data == null
      ? Image(
      image: NetworkImage(dataAll[indexNo]["thumbnail"]),
      )
      : Image(
         image: NetworkImage(data[indexNo]["thumbnail"]),
      )
);
Run Code Online (Sandbox Code Playgroud)

changeQuality()功能:

changeQuality(String newQuality) {
  setState(() {
    position = videoPlayer.value.position;
    chewieController.pause();
    videoPlayer = new VideoPlayerController.network(data == null
      ? dataAll[indexNo]["video"]["$newQuality"]
      : data[indexNo]["video"]["$newQuality"]);
    chewieController = ChewieController(
      videoPlayerController: videoPlayer,
      aspectRatio: 16 / 9,
      autoPlay: true,
      allowedScreenSleep: false,
      startAt: position,
    );
  });
  Navigator.of(context).pop();
}
Run Code Online (Sandbox Code Playgroud)

我也尝试过处理旧的视频播放器,然后设置新值,但出现错误,表明变量在处理后无法使用。

切换器可以工作一点,因为它会改变质量大约 4 到 5 次,然后会遇到错误并且不会播放任何内容。

小智 5

我扩展了video_player 的这个解决方案,并将其扩展为也涵盖了chewie。

该解决方案的关键部分

  • 您需要两个小部件。MyVideoPlayer 封装了 video_player 和 Chewie 以及一个外部小部件,您可以在其中对用户输入或状态更改做出反应,并将 MyVideoPlayer 替换为新的。
  • 该解决方案以一种方式绕过了整个问题。我没有解决如何更改 video_player 或 Chewie 的视频。相反,它遵循记录的原则,即如何在主机小部件 (MyVideoPlayer) 的整个生命周期中使用 Chewie 并交换该小部件以更改视频 url。
  • 如果您不想仅将其专用于包含 MyVideoPlayer,则可以在外部小部件中填充更多您认为合适的内容。IE。如果您想要根据应用程序状态在其旁边添加描述文本。

外部部件

我用 with 编写this.,但在 Dart 代码中可以省略它。

class QuizVideoPlayer extends StatefulWidget {
  @override
  _QuizVideoPlayerState createState() => _QuizVideoPlayerState();
}

class _QuizVideoPlayerState extends State<QuizVideoPlayer> {
  Word _url;
  UniqueKey _urlKey;

  // Call this method from button or in reaction to model change etc.
  // I call it from Provider.of in didChangeDependencies, but I don't think it is
  // a necessary detail of the answer as it depends on how you do state management.
  // The key in this solution is that state management occur in the outer widget and
  // due to some trigger call _changeUrl() which changes _url and _urlKey which then
  // swaps out MyVideoPlayer.
  @override
  void _changeUrl(String newUrl) async {
    this.setState(() {
      // Rebuild MyVideoPlayer with a new instance => eventually dispose old controllers
      this._url = newUrl;
      this._urlKey = UniqueKey();
    });
  }

  @override
  Widget build(BuildContext context) {
    return 
      /* ... */
      this._url != null
          ? MyVideoPlayer(
              this._url,
              this._urlKey,
            )
          : AspectRatio(
              aspectRatio: 3 / 2,
              child: Container(color: Colors.black),
            )
      /* ... */
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

我的视频播放器

我用 with 编写this.,但在 Dart 代码中可以省略它。

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';

class MyVideoPlayer extends StatefulWidget {
  final String videoUrl;
  final UniqueKey newKey;

  MyVideoPlayer(this.videoUrl, this.newKey): super(key: newKey); // passing Unique key to dispose old class instance and create new with new data

  @override
  _MyVideoPlayerState createState() => _MyVideoPlayerState();
}

class _MyVideoPlayerState extends State<MyVideoPlayer> {
  VideoPlayerController _controller;
  ChewieController _chewie;

  @override
  void initState() {
    this._initControllers(this.widget.videoUrl);
    super.initState();
  }

  void _initControllers(String url) {
    this._controller = VideoPlayerController.network(url);
    this._chewie = ChewieController(
      videoPlayerController: this._controller,
      autoPlay: true,
    );
  }

  @override
  void dispose() {
    this._controller?.dispose();
    this._chewie?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Chewie(controller: this._chewie);
  }
}
Run Code Online (Sandbox Code Playgroud)