当前位置: 首页 > news >正文

flutter开发实战-video_player插件播放抖音直播实现(仅限Android端)

flutter开发实战-video_player插件播放抖音直播实现(仅限Android端)

在之前的开发过程中,遇到video_player播放视频,通过查看video_player插件描述,可以看到video_player在Android端使用exoplayer,在iOS端使用的是AVPlayer。由于iOS的AVPlayer不支持flv、m3u8格式的直播,这里video_player播放抖音直播仅仅在Android有效,在iOS端,如果需要播放抖音直播,可以使用fijkplayer插件进行播放,由于fijkplayer使用的是ijkplayer,可以播放flv、m3u8格式的直播。

一、引入

在pubspec.yaml中引入video_player

  # 播放器video_player: ^2.7.0# fijkplayer: ^0.11.0

二、实现VideoPlayer的Widget

2.1 在iOS中的设置

在iOS工程中info.plist添加一下设置,以便支持Https,HTTP的视频地址

<key>NSAppTransportSecurity</key>
<dict><key>NSAllowsArbitraryLoads</key><true/>
</dict>

2.2 在Android中的设置

需要在/android/app/src/main/AndroidManifest.xml文件中添加网络权限

<uses-permission android:name="android.permission.INTERNET"/>

2.3 播放的VideoPlayer

使用video_player插件,需要使用VideoPlayerController来控制播放、暂停、添加监听

初始化后添加监听,来获取VideoPlayerController中的Value值,可以看到一些状态。例如

VideoPlayerValue(duration: 0:00:00.001000, size: Size(1280.0, 720.0), position: 0:32:14.877000, caption: Caption(number: 0, start: 0:00:00.000000, end: 0:00:00.000000, text: ), captionOffset: 0:00:00.000000, buffered: [DurationRange(start: 0:00:00.000000, end: 0:32:17.868000)], isInitialized: true, isPlaying: true, isLooping: false, isBuffering: false, volume: 1.0, playbackSpeed: 1.0, errorDescription: null, isCompleted: false)

添加监听

// 添加监听void addListener() {if (_controller != null) {_controller!.addListener(videoListenerCallback);}}

移除监听

// 移除监听void removeListener() {if (_controller != null) {_controller!.removeListener(videoListenerCallback);}}

监听的callback回调

void videoListenerCallback() {// 监听结果if (_controller != null) {if (_controller!.value.hasError) {// 出现错误setState(() {});}if (_controller!.value.isCompleted) {// 直播完成setState(() {});}if (_controller!.value.isBuffering) {// 正在buffer}if (_controller!.value.hasError || _controller!.value.isCompleted) {// 是否处于错误状态 或者 播放完成if (widget.liveController.onOutLinkPlayerCompleted != null) {widget.liveController.onOutLinkPlayerCompleted!();}}if (_controller!.value.hasError == false) {// 可播放,隐藏封面if (widget.liveController.onOutLinkPlayerCanPlay != null) {widget.liveController.onOutLinkPlayerCanPlay!();}}}}

播放

Future<void> play() async {
if (_controller != null) {await _controller?.play();}
}

暂停

Future<void> play() async {
if (_controller != null) {await _controller?.pause();}
}

完整代码如下

//  视频播放测试
class VideoPlayerSkeleton extends StatefulWidget {const VideoPlayerSkeleton({Key? key,required this.videoUrl,required this.isLooping,this.autoPlay = true,required this.width,required this.height,}) : super(key: key);final String videoUrl;final bool isLooping;final bool autoPlay;final double width;final double height;State<VideoPlayerSkeleton> createState() => _VideoPlayerSkeletonState();
}class _VideoPlayerSkeletonState extends State<VideoPlayerSkeleton> {VideoPlayerController? _controller;void initState() {super.initState();videoPlay();print("_VideoPlayerSkeletonState videoUrl:${widget.videoUrl}");}// 添加监听void addListener() {if (_controller != null) {_controller!.addListener(videoListenerCallback);}}void videoListenerCallback() {// 监听结果if (_controller != null) {if (_controller!.value.hasError) {// 出现错误setState(() {});}if (_controller!.value.isCompleted) {// 直播完成setState(() {});}if (_controller!.value.isBuffering) {// 正在buffer}}}// 移除监听void removeListener() {if (_controller != null) {_controller!.removeListener(videoListenerCallback);}}// 播放视频Future<void> videoPlay() async {_controller?.dispose();_controller = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl),videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true,allowBackgroundPlayback: false,),);addListener();await _controller?.initialize().then((_) {// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.setState(() {});}).catchError((error) {// 是否处于错误状态 或者 播放完成if (widget.liveController.onOutLinkPlayerCompleted != null) {widget.liveController.onOutLinkPlayerCompleted!();}}).whenComplete(() {// print('checkAnimationTimeout whenComplete');});await _controller!.setLooping(widget.isLooping);if (widget.autoPlay) {await _controller?.play();} else {await _controller?.pause();}}Widget build(BuildContext context) {return Container(width: widget.width,height: widget.height,color: Colors.black87,child: Stack(alignment: Alignment.center,children: [buildVideoPlayer(context),buildStateIntro(context),],),);}// 播放视频Widget buildVideoPlayer(BuildContext context) {if (_controller != null && _controller!.value.isInitialized) {return AspectRatio(aspectRatio: _controller!.value.aspectRatio,child: VideoPlayer(_controller!),);}return Container();}// 播放过程中出现errorWidget buildStateIntro(BuildContext context) {if (_controller != null) {String title = "";String message = "";bool showIntro = false;if (_controller!.value.hasError) {showIntro = true;title = "播放出现错误";message = _controller!.value.errorDescription ?? "";} else {if (_controller!.value.isCompleted) {showIntro = true;title = "播放结束";}}if (showIntro) {return Container(padding: EdgeInsets.symmetric(vertical: 50.r, horizontal: 50.r),color: Colors.transparent,child: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [Expanded(child: Container()),Text(title,textAlign: TextAlign.center,softWrap: true,style: TextStyle(fontSize: 28.r,fontWeight: FontWeight.w500,fontStyle: FontStyle.normal,color: Colors.white,decoration: TextDecoration.none,),),SizedBox(height: 25.r,),Text(message,textAlign: TextAlign.center,softWrap: true,style: TextStyle(fontSize: 22.r,fontWeight: FontWeight.w500,fontStyle: FontStyle.normal,color: Colors.white,decoration: TextDecoration.none,),),Expanded(child: Container()),],),);}}return Container();}void dispose() {// TODO: implement disposeremoveListener();_controller?.dispose();super.dispose();}
}

三、从抖音网站上找到直播地址

由于使用抖音播放地址,这里简单描述一下从抖音网站上找到直播的flv地址。

进入抖音直播间,在网页点击鼠标右键,看到检查。
https://live.douyin.com/567752440034
在这里插入图片描述

找到网络,刷新页面,可以看到stream的一条,
在这里插入图片描述

复制地址即可,使用该地址播放直播
在这里插入图片描述

https://pull-hs-spe-f5.douyincdn.com/fantasy/stream-728687306789918920718_sd.flv?_neptune_token=MIGlBAxGexWdmRAYAAGs67QEgYIZi9nqbdY3bbfeK9dCVFBnlFTJNF1WNGRZ3AVrQ1ixrE_54JzkGsfuBjGER_2RhP5Qy_GzELSQuct4bK5aktJ2P2xnNznJG87KKhybkeCuefBAkOCI9Tx8eA1mz2GcmfcfqFNeR8DFPDcbzFp_sKyyJRnytmILegqrqjcjxgW04GYwBBDMFIKjhmF1jpi96O53wH7v&expire=1696731973&sign=38f51d46dcd5828fdbc212372bbb3522&volcSecret=38f51d46dcd5828fdbc212372bbb3522&volcTime=1696731973

四、查看直播结果

之后,我们将地址复制到VideoPlayerSkeleton中,运行后,可以看到播放的效果

在这里插入图片描述

注意:直接在Container上设置大小后,child是AspectRatio(
aspectRatio: _controller!.value.aspectRatio,
child: VideoPlayer(_controller!),
);
会出现画面变形,可以使用Stack嵌套一下。

五、小结

flutter开发实战-video_player插件播放抖音直播实现(仅限Android端)。描述可能不是特别准确,请见谅。

https://blog.csdn.net/gloryFlow/article/details/133634186

学习记录,每天不停进步。

相关文章:

flutter开发实战-video_player插件播放抖音直播实现(仅限Android端)

flutter开发实战-video_player插件播放抖音直播实现&#xff08;仅限Android端&#xff09; 在之前的开发过程中&#xff0c;遇到video_player播放视频&#xff0c;通过查看video_player插件描述&#xff0c;可以看到video_player在Android端使用exoplayer&#xff0c;在iOS端…...

React组件

一、React组件 函数组件 // 函数组件 // 组件的名称必须首字母大写 // 函数组件必须有返回值 如果不需要渲染任何内容&#xff0c;则返回 null function HelloFn () {return <div>这是我的第一个函数组件!</div> }// 定义类组件 function App () {return (<di…...

[动手学深度学习]注意力机制Transformer学习笔记

动手学深度学习&#xff08;视频&#xff09;&#xff1a;68 Transformer【动手学深度学习v2】_哔哩哔哩_bilibili 动手学深度学习&#xff08;pdf&#xff09;&#xff1a;10.7. Transformer — 动手学深度学习 2.0.0 documentation (d2l.ai) 李沐Transformer论文逐段精读&a…...

hadoop集群安装并配置

文章目录 1.安装JDK 环境2.系统配置2.1修改本地hosts文件2.2创建hadoop 用户2.2 设置ssh免密&#xff08;使用hadoop 用户生成&#xff09; 3.安装 hadoop 3.2.43.1 安装hadoop3.1.1 配置Hadoop 环境变量 3.2配置 HDFS3.2.1 配置 workers 文件3.2.2 配置hadoop-env.sh3.2.3 配置…...

Quarto 入门教程 (3):代码框、图形、数据框设置

简介 本文是《手把手教你使用 Quarto 构建文档》第三期&#xff0c;前两期分别介绍了&#xff1a; 第一期 介绍了Quarto 构建文档的原理&#xff1b;可创建的文档类型&#xff1b;对应的参考资源分享。 第二期 介绍了如何使用 Quarto&#xff0c;并编译出文档&#xff08;PDF…...

虚拟机Ubuntu18.04安装对应ROS版本详细教程!(含错误提示解决)

参考链接&#xff1a; Ubuntu18.04安装Ros(最新最详细亲测)_向日葵骑士Faraday的博客-CSDN博客 1.4 ROS的安装与配置_哔哩哔哩_bilibili ROS官网&#xff1a;http://wiki.ros.org/melodic/Installation/Ubuntu 一、检查cmake 安装ROS时会自动安装旧版的Cmake3.10.2。所以在…...

#力扣:14. 最长公共前缀@FDDLC

14. 最长公共前缀 一、Java class Solution {public String longestCommonPrefix(String[] strs) {for (int l 0; ; l) {for (int i 0; i < strs.length; i) {if (l > strs[i].length() || strs[i].charAt(l) ! strs[0].charAt(l)) return strs[0].substring(0, l);}…...

Android 13.0 解锁状态下禁止下拉状态栏功能实现

1.前言 在13.0的系统定制化开发中,在关于一些systemui下拉状态栏的定制化功能开发中,对于关于systemui的下拉状态栏 是否可以下拉做了定制,用系统属性来判断是否可以在解锁的情况下可以下拉状态栏布局,虽然11.0 12.0和13.0的关于 下拉状态栏相关分析有区别,可以通过分析相…...

chromium线程模型(1)-普通线程实现(ui和io线程)

通过chromium 官方文档&#xff0c;线程和任务一节我们可以知道 &#xff0c;chromium有两类线程&#xff0c;一类是普通线程&#xff0c;最典型的就是io线程和ui线程。 另一类是 线程池线程。 今天我们先分析普通线程的实现&#xff0c;下一篇文章分析线程池的实现。&#xff…...

uniapp uni.showToast 一闪而过的问题

问题&#xff1a;在页面跳转uni.navigateBack()等操作的前或后&#xff0c;执行uni.showToast&#xff0c;即使代码中设置2000ms的显示时间&#xff0c;也会一闪而过。 解决&#xff1a;用setTimeout延后navigateBack的执行。...

代理模式介绍及具体实现(设计模式 三)

代理模式是一种结构型设计模式&#xff0c;它允许通过创建一个代理对象来控制对另一个对象的访问 实例介绍和实现过程 假设我们正在开发一个电子商务网站&#xff0c;其中有一个商品库存管理系统。我们希望在每次查询商品库存之前&#xff0c;先进行权限验证&#xff0c;以确…...

【18】c++设计模式——>适配器模式

c的适配器模式是一种结构型设计模式&#xff0c;他允许将一个类的接口转换成另一个客户端所期望的接口。适配器模式常用于已存在的&#xff0c;但不符合新需求或者规范的类的适配。 在c中实现适配器模式时&#xff0c;通常需要一下几个组件&#xff1a; 1.目标接口&#xff08;…...

mariadb 错误日志中报错:Incorrect definition of table mysql.column_stats:

数据库错误日志出现此错误原因是因为系统表中字段类型或者数据结构有变动导致&#xff0c;一般是因为升级数据库版本后未同步升级系统表结构。 解决方法&#xff1a; 1.如果错误日志过大&#xff0c;直接删除。 2.执行 mysql_upgrade -u[用户名] -p[密码];&#xff0c;这一步…...

【SpringBoot】多环境配置和启动

环境分类&#xff0c;可以分为 本地环境、测试环境、生产环境等&#xff0c;通过对不同环境配置内容&#xff0c;来实现对不同环境做不同的事情。 SpringBoot 项目&#xff0c;通过 application-xxx.yml 添加不同的后缀来区分配置文件&#xff0c;启动时候通过后缀启动即可。 …...

跨qml通信

****Commet.qml //加载其他文件中的组件 不需要声明称Component //1.用loader.item.属性 访问属性 //2.loader.item.方法 访问方法 //3.用loader.item.方法.connect(槽)连接信号 Item { Loader{ id:loader; width: 200; …...

力扣-404.左叶子之和

Idea attention&#xff1a;先看清楚题目&#xff0c;题目说的是左叶子结点&#xff0c;不是左结点【泣不成声】 遇到像这种二叉树类型的题目呢&#xff0c;我们一般还是选择dfs&#xff0c;然后类似于前序遍历的方式加上判断条件即可 AC Code class Solution { public:void d…...

如何搭建一个 websocket

环境: NodeJssocket.io 4.7.2 安装依赖 yarn add socket.io创建服务器 引入文件 特别注意: 涉及到 colors 的代码&#xff0c;请采取 console.log() 打印 // 基础老三样 import http from "http"; import fs from "fs"; import { Server } from &quo…...

pip常用命令

TOC(pip常用命令) 1.pip 2.where pip 3.pip install --upgrade pip 4.安装 这里暂用flask库举例&#xff0c;安装flask库&#xff0c;默认安装最新版&#xff1a; pip install flask指定要安装flask库的版本&#xff1a; pip install flask版本号我们在安装第三方库时可…...

[QT编程系列-43]: Windows + QT软件内存泄露的检测方法

目录 一、如何查找Windows程序是否有内存泄露 二、如何定位Windows程序内存泄露的原因 二、Windows环境下内存监控工具的使用 2.1 内存监测工具 - Valgrind 2.2.1 Valgrind for Linux 2.2.2 Valgrind for Windows 2.2 内存监测工具 - Dr. Memory 2.2.1 特点 2.2.2 安装…...

【Java-LangChain:使用 ChatGPT API 搭建系统-5】处理输入-思维链推理

第五章&#xff0c;处理输入-思维链推理 在本章中&#xff0c;我们将专注于处理输入&#xff0c;即通过一系列步骤生成有用地输出。 有时&#xff0c;模型在回答特定问题之前需要进行详细地推理。如果您参加过我们之前的课程&#xff0c;您将看到许多这样的例子。有时&#xf…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...