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

时间:2019-05-31 18:40:16

标签: android flutter dart

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

我已经建立了一个带有开关的底页,当用户选择所需的质量时,我运行changeQuality()。它应该做的就是简单地将一个新的源文件提供给旧播放器,并在视频离开的地方继续播放。

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

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"]),
      )
);

还有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();
}

我还尝试过处置旧的视频播放器,然后设置新值,但是我收到一个错误,指出处置后无法使用变量。

切换器可以工作一点,因为它会改变质量约4到5倍,然后出现错误,无法播放任何内容。

1 个答案:

答案 0 :(得分:2)

我在this solution for video_player上进行扩展,并将其扩展为也包括chewie。

此解决方案的关键部分

  • 您需要两个小部件。封装了video_player和chewie的MyVideoPlayer以及一个外部小部件,您可以在其中响应用户输入或状态更改,并用新的交换出MyVideoPlayer。
  • 此解决方案以一种方式绕过整个问题。我没有解决如何更改video_player或chewie的视频。取而代之的是,它遵循文档化的原则,即如何在主机小部件(MyVideoPlayer)的整个生命周期中使用chewie并将其交换以更改视频URL。
  • 如果不想仅将其专用于包含MyVideoPlayer,则可以根据需要在外部窗口小部件中填充更多内容。就是如果您要根据应用状态在其附近添加描述文字。

外部小部件

我用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),
            )
      /* ... */
    );
  }
}

MyVideoPlayer

我用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);
  }
}