当前位置: 首页 > 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…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

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

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

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...

提升移动端网页调试效率:WebDebugX 与常见工具组合实践

在日常移动端开发中&#xff0c;网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时&#xff0c;开发者迫切需要一套高效、可靠且跨平台的调试方案。过去&#xff0c;我们或多或少使用过 Chrome DevTools、Remote Debug…...

Pydantic + Function Calling的结合

1、Pydantic Pydantic 是一个 Python 库&#xff0c;用于数据验证和设置管理&#xff0c;通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发&#xff08;如 FastAPI&#xff09;、配置管理和数据解析&#xff0c;核心功能包括&#xff1a; 数据验证&#xff1a;通过…...

算法—栈系列

一&#xff1a;删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...