【Flutter】双路视频播放方案

发布于:2025-08-02 ⋅ 阅读:(20) ⋅ 点赞:(0)

最近在做双路视频播放,就是在一个页面播放两个视频。我遇到的问题就是音频焦点冲突问题,在下面说明。

什么是双路视频播放(来自AI)

双路视频播放(Dual-Video Playback),从字面上理解,就是指在一个屏幕或应用界面上,同时解码和渲染两个独立的视频流

可以把它想象成有两列火车在两条并行的轨道上同时行驶,你可以同时看到两列火车的状态。这两个视频流可以有多种关系和布局方式。

核心特征

  1. 两个独立的视频源: 这不是将一个视频画面分割成两半,而是实实在在地处理两个不同的视频文件或网络流。例如,一个是 video_A.mp4,另一个是 video_B.mp4

  2. 同时播放: 两个视频在时间上是同步或并行播放的。播放/暂停/快进等操作可能联动控制两个视频,也可能允许独立控制。

  3. 精确同步(关键特性): 在很多场景下,双路视频播放的难点和核心在于保持两个视频画面的精确时间同步。例如,如果视频A的第5秒100毫秒对应的是视频B的第5秒100毫秒,那么在播放时必须保证它们始终对齐,不能出现一个快一个慢的情况。

  4. 灵活的布局: 两个视频的画面可以有多种呈现方式:

    • 画中画 (Picture-in-Picture, PiP):一个小视频窗口悬浮在一个大视频窗口之上。
    • 左右分屏 (Side-by-Side):屏幕一分为二,左边播放一个视频,右边播放另一个。
    • 上下分屏 (Top-and-Bottom):屏幕一分为二,上边播放一个视频,下边播放另一个。
    • 叠加融合 (Overlay/Blending):一个视频作为背景,另一个半透明地叠加在上面。

常见的应用场景

双路视频播放技术非常有价值,因为它能提供单一视频无法实现的信息维度和交互体验。

应用领域 具体场景描述 示例
在线教育/网络课程 一路视频播放老师的讲课画面,另一路同步播放课件PPT或屏幕操作的录像。 网易云课堂、Coursera等平台,老师头像在角落,主屏幕是课件。
视频剪辑/后期制作 在调色或添加特效时,一路播放原始素材,另一路实时播放处理后的效果,方便进行对比。 Adobe Premiere Pro, Final Cut Pro 中的对比视图功能。
体育赛事直播/分析 一路播放赛场的全景视角,另一路播放某个运动员的特写视角或精彩回放。 足球比赛中,同时展示球场全景和某个球星的个人镜头。
安防监控 在一个屏幕上同时显示来自不同摄像头的实时监控画面。 大楼的安保中心,监控墙上显示着多个区域的实时视频。
无障碍功能 (Accessibility) 一路播放主视频内容,另一路在角落同步播放手语翻译的视频。 很多新闻发布会或官方视频会提供手语翻译版本。
3D/VR/360°视频 这是一种特殊形式的双路视频,系统为左眼和右眼分别渲染一个视频流,从而创造出立体感和沉浸感。 YouTube VR, Oculus TV 等 VR 应用。
互动视频/游戏 允许用户在不同视角或故事线之间切换,系统在后台可能需要同时准备好多个视频流以实现无缝切换。 一些互动电影或真人游戏。

技术挑战

实现稳定流畅的双路视频播放并非易事,尤其是在移动端。

  1. 性能消耗巨大: 解码两个视频流(尤其是高清视频)需要双倍的CPU/GPU计算资源。这会显著增加设备的功耗、发热量,并可能导致卡顿或掉帧。这也是为什么你之前会遇到内存溢出等问题的原因之一,处理双路视频对内存管理的要求更高。

  2. 同步难题: 保持两个视频流的精确同步是一个巨大的挑战。网络延迟的抖动(Jitter)、解码速度的差异都可能导致音画或两个画面之间出现偏移。需要复杂的时钟同步机制来校准。

  3. 音轨处理: 两个视频通常都有自己的音轨。如何处理音频?

    • 只播放其中一个视频的音频?
    • 将两个音轨混合(Mix)在一起?
    • 让用户自己选择听哪一个?
      这都需要在产品设计和技术实现上仔细考虑。
  4. 复杂的播放器控制逻辑: 一个播放器现在需要管理两个视频源的状态,播放/暂停、快进/快退、缓冲状态、错误处理等逻辑都变得复杂了一倍。

总而言之,双路视频播放是一项强大的功能,它通过在同一时间呈现更多维度的视觉信息,极大地丰富了应用的功能和用户体验,但同时也对应用的性能优化和技术实现提出了更高的要求。

 

初方案

`video_player` 插件

创建两个独立的 `VideoPlayerController` 实例,分别控制两个视频的播放。

代码

写在开头,重点就是被注释掉的那句,其实没必要往下看。

    VideoPlayerController controller1 = VideoPlayerController.networkUrl(
      Uri.parse(videoUrl),
      //videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true), //是的,一开始没加这句!!!这就是问题所在
    );
    VideoPlayerController controller2 = VideoPlayerController.networkUrl(
      Uri.parse(videoUrl),
      //videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
    );

 组件代码,只是简单的模拟

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: Row(
        children: <Widget>[
          Expanded(
            child: _controller1.value.isInitialized
                ? AspectRatio(
                    aspectRatio: _controller1.value.aspectRatio,
                    child: VideoPlayer(_controller1), //这里
                  )
                : Container(),
          ),
          Expanded(
            child: _controller2.value.isInitialized
                ? AspectRatio(
                    aspectRatio: _controller2.value.aspectRatio,
                    child: VideoPlayer(_controller2),//这里
                  )
                : Container(),
          ),
        ],
      ),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: () {
        togglePlayPauseBoth();
      },
      child: Icon(
        _controller1.value.isPlaying ? Icons.pause : Icons.play_arrow,
      ),
    ),
  );
}

  void setVolumeBoth(double volume) {
    controller1?.setVolume(volume);  //注意这里少了一个
  }

  void togglePlayPauseBoth() {
    final bool isPlaying = controller1?.value.isPlaying ?? false;
    if (isPlaying) {
      pauseBoth();
    } else {
      playBoth();
    }
  }

  void playBoth() {
    controller1?.play();
    controller2?.play();
  }

  void pauseBoth() {
    controller1?.pause();
    controller2?.pause();
  }

  void seekBoth(Duration position) {
    controller1?.seekTo(position);
    controller2?.seekTo(position);
  }

  

问题描述

点击播放只有第一个视频播放,而且播放按钮play_arrow -> pause -> play_arrow。

原因分析

经过了一些不必要的弯路,我终于看到了日志

D/AudioManager(31189): dispatching onAudioFocusChange(-1) ...

 这一行日志就是问题的“确凿证据”。它告诉我们,系统正在向您的应用发送一个“音频焦点丢失” (AUDIOFOCUS_LOSS) 的通知。

问题根源:音频焦点冲突

  1. 什么是音频焦点? 在Android系统中,为了避免多个应用同时播放声音造成混乱,只有一个应用可以在同一时间“持有”音频焦点。
  2. 发生了什么? 当您调用 playBoth() 时,视频播放器1(controller1)和视频播放器2(controller2同时向系统请求音频焦点。
  3. 系统如何反应? Android系统看到来自同一个应用的第二个音频请求,会认为第一个请求应该被放弃。因此,它会立即给第一个请求者发送一个AUDIOFOCUS_LOSS通知。
  4. video_player插件的默认行为video_player插件在收到“音频焦点丢失”的通知后,会非常“懂事”地自动暂停播放。

其他方案

1、使用 `multi_video_player` 插件
2、使用 `video_player_mux` 插件

 怎么使用我也只看了官网例子,没有实践,有空补上。

一些废话,记录bug解决过程

没有发日志之前AI越走越弯 

问题1:

 一开始这里出现的问题是点击播放只有第一个视频播放,而且播放按钮play_arrow -> pause -> play_arrow。

一开始没给AI日志信息,所以分析是两个控制器状态不统一,给出了解决方案1。

解决方案1:

核心是将播放/暂停的命令统一处理,并建立一个单向的同步机制:始终以后置视频的状态为准,强制前置视频跟随。

问题2:

由于上诉修改产生了问题2。因为强制两个视频状态同步,主打同生共死,所以两个视频他们一起停了,按钮play_arrow -> pause -> play_arrow。

解决方案2:

撤回解决方法1。

后面还在ai的指导下尝试了

1、将一个视频静音---->没用。

2、只播放一个视频永远只让一个播放器(主播放器)真正执行play()pause()操作,另一个播放器(从播放器)只通过seekTo()来被动地同步画面,从而绝不请求音频焦点。---->卡顿非常明显,pass。

 


网站公告

今日签到

点亮在社区的每一天
去签到