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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/130290.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

防止SQL注入攻击的综合解决方案

文章目录 摘要背景和危害性防御措施示例代码&#xff08;Java&#xff09;示例代码&#xff08;PHP&#xff09;示例MySQL命令示例代码&#xff08;Python&#xff09;示例代码&#xff08;C#&#xff0c;使用Entity Framework&#xff09; 进一步防御SQL注入攻击的措施使用ORM…

链表(2)——带头双向循环链表

&#x1f341;一、链表的分类 &#x1f315;1.单向或者双向 &#x1f315;2.带头或者不带头&#xff08;有无哨兵&#xff09; &#x1f315;3.循环或者不循环 &#x1f315;4.无头单向非循环链表&#xff08;常用&#xff09; &#x1f315;5.带头双向循环链表&#xff08;常用…

粘性文本整页滚动效果

效果展示 CSS 知识点 background 相关属性综合运用position 属性的 sticky 值运用scroll-behavior 属性运用scroll-snap-type 属性运用scroll-snap-align 属性运用 整体页面效果实现 <div class"container"><!-- 第一屏 --><div class"sec&qu…

Flink之Watermark源码解析

1. WaterMark源码分析 在Flink官网中介绍watermark和数据是异步处理的,通过分析源码得知这个说法不够准确或者说不够详细,这个异步处理要分为两种情况: watermark源头watermark下游 这两种情况的处理方式并不相同,在watermark的源头确实是异步处理的,但是在下游只是做的判断,这…

TypeScript 笔记:基础类型

1 any类型&#xff08;任意值类型&#xff09; 声明为 any 的变量可以赋予任意类型的值。 any类型是Typescript 针对编程时类型不明确的变量使用的一种数据类型&#xff0c;常用于: 变量的值会动态改变 ——>任意值类型可以让这些变量跳过编译阶段的类型检查 let x: any …

12.2 实现键盘模拟按键

本节将向读者介绍如何使用键盘鼠标操控模拟技术&#xff0c;键盘鼠标操控模拟技术是一种非常实用的技术&#xff0c;可以自动化执行一些重复性的任务&#xff0c;提高工作效率&#xff0c;在Windows系统下&#xff0c;通过使用各种键盘鼠标控制函数实现动态捕捉和模拟特定功能的…

爬取微博热榜并将其存储为csv文件

&#x1f64c;秋名山码民的主页 &#x1f602;oi退役选手&#xff0c;Java、大数据、单片机、IoT均有所涉猎&#xff0c;热爱技术&#xff0c;技术无罪 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; 获取源码&#xff0c;添加WX 目录 前言1.…

景联文科技:AI大模型强势赋能,助力自动驾驶迭代升级

我国一直以来都将自动驾驶作为新兴产业发展的重点领域之一&#xff0c;工信部等相关部委出台了一系列自动驾驶发展战略、规划和标准&#xff0c;一些地方政府也在积极开展关于自动驾驶的地方立法&#xff0c;为自动驾驶技术的研发和应用提供更加具体的法律保障。例如&#xff0…

决策树算法——C4.5算法

目录 1.ID3算法 2.C4.5算法 3.信息增益率 &#xff08;1&#xff09;信息增益率 &#xff08;2&#xff09;案例 4.决策树的剪枝 5.总结 &#xff08;1&#xff09;优点与改进 &#xff08;2&#xff09;缺点 &#xff08;3&#xff09; 总结及展望 近年来决策树方…

v-on事件处理指令;简写@事件名

一、v-on 给元素&#xff08;标签&#xff09;绑定事件监听器 oninput、onclick、onchange、onblur等 1、 完整方式v-on:事件名“函数/方法” 2、简写方式事件名“函数/方法”&#xff0c;注意符号不能加冒号“&#xff1a;” input /click/change/blur ..... 代码如下&#xf…

【Qt】三种方式实现抽奖小游戏

简介 本文章是基本Qt与C实现一个抽奖小游戏&#xff0c;用到的知识点在此前发布的几篇文章。 下面是跳转链接&#xff1a; 【Qt控件之QLabel】用法及技巧链接&#xff1a; https://blog.csdn.net/MrHHHHHH/article/details/133691441?spm1001.2014.3001.5501 【Qt控件之QPus…

【ElasticSearch】基于 Java 客户端 RestClient 实现对 ElasticSearch 索引库、文档的增删改查操作,以及文档的批量导入

文章目录 前言一、对 Java RestClient 的认识1.1 什么是 RestClient1.2 RestClient 核心类&#xff1a;RestHighLevelClient 二、使用 Java RestClient 操作索引库2.1 根据数据库表编写创建 ES 索引的 DSL 语句2.2 初始化 Java RestClient2.2.1 在 Spring Boot 项目中引入 Rest…