草庐IT

uniapp 使用 mui-player 插件播放 m3u8/flv 视频流

翘翘红 2023-06-08 原文

背景:uniapp 开发的h5项目,需要播放m3u8/flv后缀的视频,网上有很多视频插件,但是样式和效果不尽如人意,博主最后选择mui-player插件,定制化稍微强一点以及有官方文档可以阅读,官网文档https://muiplayer.js.org/zh/guide/

tips:建议先阅读官方文档,再在页面进行引入

博主最后实现的效果如下,pc端和移动端为两种展示样式,pc可以设置声音、播放速度、分辨率、全屏、画中画等功能,具体还有其他的功能自定义可以参照官网,官网的说明很详细以及有示例进行参考;移动端和pc端的功能大差不差,只是展现形式略有差别。


1、安装mui-player插件

npm i mui-player --save

2、页面引入,可选择在需要展示视频的页面直接引入,也可以放入一个公共组件,这样方便多个页面都会使用播放器的情况,博主这里将播放器作为一个公共组件,在组件里面引入

// 播放器样式文件
import 'mui-player/dist/mui-player.min.css'
// npm安装方式引入mui-player
import MuiPlayer from 'mui-player'
// 要播放m3u8的视频就必须要引入hls.js
import Hls from 'hls.js'
// 要播放flv的视频就必须要引入flv.js
import Flv from 'flv.js'
// 要设置pc端视频的清晰度需要引入pc端扩展
import MuiPlayerDesktopPlugin from 'mui-player-desktop-plugin'

3、template模板

<template>
	<view id="mui-player">
		<!-- 可在这里添加你想要覆盖在视频上面的内容,这里我加了一个关闭按钮,层级最高,不会影响视频的播放 -->
		<image v-if="showCloseIcon" src="@/sub-live/static/close.png" class="pos-a full-close" @click.stop="videoClose">
	</view>
</template>

4、data定一个空的mp对象

data() {
	return {
		mp: {}
	}
},

5、需要向使用的页面传递的参数

props: {
	// 视频流地址,必传
	src: {
		type: String,
		default: ''
	},
	// 视频封面图,可选
	poster: {
		type: String,
		default: ''
	},
	// 是否要展示关闭视频图标
	showCloseIcon: {
		type: Boolean,
		default: false
	},
	// 当前视频是否是直播模式
	live: {
		type: Boolean,
		default: false
	},
	// 兼容音频m3u8(有些音频地址也是m3u8,但是音频不需要播放样式,所以需要兼容)
	isZero: {
		type: Boolean,
		default: false
	},
	// 设置pc/移动端清晰度选择
	childConfig: {
		type: Array,
		default: () => [{
				functions: '高清',
				selected: true
			},
			{
				functions: '标清'
			},
			{
				functions: '流畅'
			},
		]
	}
}

6、mounted生命周期初始化

mounted() {
	// 防止this的改变
	const _this = this;
	// 根据视频路径后缀判断当前为m3u8还是flv的视频流
	var flieArr = _this.src.split('.');
	var suffix = flieArr[flieArr.length - 1];
	// m3u8格式
	var a = suffix.indexOf('m3u8') !== -1
	// flv格式
	var b = suffix.indexOf('flv') !== -1
	var c = {}
	// m3u8格式的视频配置
	if (a) {
	c = {
		type: 'hls',
		loader: Hls,
		config: {
			debug: false,
		}
	}
	}
	// flv格式的视频配置
	if (b) {
	c = {
		type: 'flv',
		loader: Flv,
		config: {
			cors: true
		},
	}
	}
	// 设置宽高,兼容音频,音频时高度为1,必须设置高度,不然音频没发播放,初始化会失败
	var sWidth = uni.getSystemInfoSync().screenWidth; // 获取屏幕宽度
	var width = 1;
	if (!_this.isZero) { // 不为音频
	if (_this.$util.isMobile()) { // 移动端动态获取
		width = sWidth;
	} else {
		width = 640; // pc端固定宽度为640
	}
	}
	var height = 1;
	if (!_this.isZero) {
	height = parseInt(width * 9 / 16) // 可改成你想设置的视频的高度,博主这里设置为宽高比为16:9的视频
	}
	_this.mp = new MuiPlayer({
	// 指定播放器容器
	container: '#mui-player',
	// 视频播放的资源地址
	src: _this.src,
	// 是否自动播放,亲测在ios某些机型上自动播放失效
	autoplay: false,
	// 是否静音播放
	muted: false,
	// 初始化播放器宽度
	width: width,
	// 初始化播放器高度
	height: height,
	// 播放器容器是否自适应视频高度
	autoFit: false,
	// 是否循环播放
	loop: false,
	// 视频封面的资源地址
	poster: _this.poster,
	// 是否开启直播模式,直播模式默认菜单配置不允许控制播放速度以及循环播放
	live: _this.live,
	// 配置声明启用同层播放
	videoAttribute: [{
			attrKey: 'webkit-playsinline',
			attrValue: 'webkit-playsinline'
		},
		{
			attrKey: 'playsinline',
			attrValue: 'playsinline'
		},
		{
			attrKey: 'x5-video-player-type',
			attrValue: 'h5-page'
		},
	],
	// flv以及m3u8视频资源的配置
	parse: c,
	// 自定义主题颜色
	themeColor: _this.$config.INFO.THEME_COLOR,
	// 非全屏模式下,是否显示播放器头部操作控件,具体可参考官方文档
	pageHead: false,
	plugins: [
		// pc端清晰度设置
		new MuiPlayerDesktopPlugin({
			customSetting: _this.childConfig.length > 0 ? [{
				functions: '清晰度',
				model: 'select',
				show: true,
				zIndex: 0,
				childConfig: _this.childConfig,
				onToggle: function(data, selected) {
					let onToggleLoad = function(state) {
						_this.mp.once('ready', function() {
							let _video = _this.mp.video();
							let _execute = function() {
								_video.currentTime = state
									.currentTime;
								state.paused ? _video.pause() :
									_video.play();
							}
	
							if (_video.readyState == 0) {
								_video.addEventListener(
									'durationchange',
									function(e) {
										_execute();
									}, {
										once: true
									})
							} else {
								_execute();
							}
						})
					}
					// 选择清晰度后重载视频
					selected(function() {
						let _video = _this.mp.video();
						onToggleLoad({
							currentTime: _video.currentTime,
							paused: _video.paused
						});
						// 将当前选择的清晰度传递给父组件
						_this.$emit('onToggleFn', data.functions)
					});
				}
			}] : []
		})
	]
	});
	// 必须放在nextTick里面,等待dom渲染完成再监听视频的播放事件等,视频的其他事件也可在此处进行监听
	_this.$nextTick(() => {
	// 监听播放器已创建完成
	_this.mp.on('ready', function(event) {
		let _video = _this.mp.video();
		_video.addEventListener("play",function(e){
			//播放事件
			_this.$emit('onPlayFn')
		});
		_video.addEventListener("ended",function(e){
			//播放完成事件
			_this.$emit('onEndedFn')
		});
	});
	// 播放发生错误
	_this.mp.on('error', function(event) {
		console.log('error', event);
	});
	})
}

7、组件销毁,视频播放器也要销毁

destroyed() {
	this.mp.destroy();
},

8、可在组件内定义一些播放/暂停的事件供父组件调用(按需写入)

// 关闭视频,返回上一页
videoClose() {
	uni.navigateBack();
},
// 播放视频
playVideo() {
	let _video = this.mp.video(); // 获取视频示例
	_video.paused ?_video.play(): _video.pause(); // 和原生video事件一致
},
// 暂停视频
pauseVideo(){
	let _video = this.mp.video();
	_video.pause();
}

9、播放视频组件完整代码,可按需进行增删改

<template>
	<view id="mui-player">
		<image v-if="showCloseIcon" src="@/sub-live/static/close.png" class="pos-a full-close" @click.stop="videoClose">
	</view>
</template>

<script>
	import 'mui-player/dist/mui-player.min.css'
	import MuiPlayer from 'mui-player'
	import Hls from 'hls.js'
	import Flv from 'flv.js'
	import MuiPlayerDesktopPlugin from 'mui-player-desktop-plugin'
	export default {
		props: {
			src: {
				type: String,
				default: ''
			},
			poster: {
				type: String,
				default: ''
			},
			showCloseIcon: {
				type: Boolean,
				default: false
			},
			live: {
				type: Boolean,
				default: false
			},
			// 兼容音频m3u8
			isZero: {
				type: Boolean,
				default: false
			},
			childConfig: {
				type: Array,
				default: () => [{
						functions: '高清',
						selected: true
					},
					{
						functions: '标清'
					},
					{
						functions: '流畅'
					},
				]
			}
		},
		data() {
			return {
				mp: {}
			}
		},
		watch: {
			src(newVal, oldVal) {
				this.mp.reloadUrl(newVal);
			}
		},
		mounted() {
			const _this = this;
			var flieArr = _this.src.split('.');
			var suffix = flieArr[flieArr.length - 1];
			// m3u8格式
			var a = suffix.indexOf('m3u8') !== -1
			// flv格式
			var b = suffix.indexOf('flv') !== -1
			var c = {}
			if (a) {
				c = {
					type: 'hls',
					loader: Hls,
					config: {
						debug: false,
					}
				}
			}
			if (b) {
				c = {
					type: 'flv',
					loader: Flv,
					config: {
						cors: true
					},
				}
			}
			var sWidth = uni.getSystemInfoSync().screenWidth;
			var width = 1;
			if (!_this.isZero) {
				if (_this.$util.isMobile()) {
					width = sWidth;
				} else {
					width = 640;
				}
			}
			var height = 1;
			if (!_this.isZero) {
				height = parseInt(width * 9 / 16)
			}
			_this.mp = new MuiPlayer({
				// 指定播放器容器
				container: '#mui-player',
				// 视频播放的资源地址
				src: _this.src,
				autoplay: false,
				muted: false,
				width: width,
				// 初始化播放器高度
				height: height,
				// 播放器容器是否自适应视频高度
				autoFit: false,
				// loop
				loop: false,
				// 视频封面的资源地址
				poster: _this.poster,
				// 是否开启直播模式,直播模式默认菜单配置不允许控制播放速度以及循环播放
				live: _this.live,
				// 配置声明启用同层播放
				videoAttribute: [{
						attrKey: 'webkit-playsinline',
						attrValue: 'webkit-playsinline'
					},
					{
						attrKey: 'playsinline',
						attrValue: 'playsinline'
					},
					{
						attrKey: 'x5-video-player-type',
						attrValue: 'h5-page'
					},
				],
				parse: c,
				// 自定义主题颜色
				themeColor: _this.$config.INFO.THEME_COLOR,
				// 非全屏模式下,是否显示播放器头部操作控件
				pageHead: false,
				plugins: [
					new MuiPlayerDesktopPlugin({
						customSetting: _this.childConfig.length > 0 ? [{
							functions: '清晰度',
							model: 'select',
							show: true,
							zIndex: 0,
							childConfig: _this.childConfig,
							onToggle: function(data, selected) {
								let onToggleLoad = function(state) {
									_this.mp.once('ready', function() {
										let _video = _this.mp.video();
										let _execute = function() {
											_video.currentTime = state
												.currentTime;
											state.paused ? _video.pause() :
												_video.play();
										}

										if (_video.readyState == 0) {
											_video.addEventListener(
												'durationchange',
												function(e) {
													_execute();
												}, {
													once: true
												})
										} else {
											_execute();
										}
									})
								}
								selected(function() {
									let _video = _this.mp.video();
									onToggleLoad({
										currentTime: _video.currentTime,
										paused: _video.paused
									});
									_this.$emit('onToggleFn', data.functions)
								});
							}
						}] : []
					})
				]
			});
			_this.$nextTick(() => {
				// 监听播放器已创建完成
				_this.mp.on('ready', function(event) {
					let _video = _this.mp.video();
					_video.addEventListener("play",function(e){
						//播放事件
						_this.$emit('onPlayFn')
					});
					_video.addEventListener("ended",function(e){
						//播放完成事件
						_this.$emit('onEndedFn')
					});
				});
				// 播放发生错误
				_this.mp.on('error', function(event) {
					console.log('error', event);
				});
			})
		},
		destroyed() {
			console.log('destroyed');
			this.mp.destroy();
		},
		methods: {
			videoClose() {
				uni.navigateBack();
			},
			playVideo() {
				let _video = this.mp.video();
				_video.paused ?_video.play(): _video.pause();
			},
			pauseVideo(){
				let _video = this.mp.video();
				_video.pause();
			}
		},
	}
</script>
<style lang="scss" scoped>
	#mui-player{
		z-index: 2;
	}
	.full-close {
		top: 22rpx;
		right: 22rpx;
		width: 44rpx;
		height: 44rpx;
		cursor: pointer;
		z-index: 8;
	}
</style>

10、父组件调用播放器,按需进行修改

<!-- #ifdef H5 -->
<common-player ref="muiplayer" :showCloseIcon="true" :poster="liveDetailInfo.thumb"
	:live="liveDetailInfo.start_time <= nowTime && nowTime <= liveDetailInfo.end_time ? true : false"
	:src="liveDetailInfo.rewriteVideoUrl" :childConfig="liveDetailInfo.qxdConfig"
	@onToggleFn="qxdToggle" @onEndedFn="ended" @onPlayFn="playFn">
</common-player>
<!-- #endif -->

总结:此播放器还是使用了开源的mui-player,所以尽量先去看文档,文档写的很详细,只是需要大面积的增删改操作,最后定制为自已想要的样子。

有关uniapp 使用 mui-player 插件播放 m3u8/flv 视频流的更多相关文章

  1. 停车系统源码-基于springboot+uniapp开源项目 - 2

    Iparking停车收费管理系统-可商用介绍Iparking是一款基于springBoot的停车收费管理系统,支持封闭车场和路边车场,支持微信支付宝多种支付渠道,支持多种硬件,涵盖了停车场管理系统的所有基础功能。技术栈Springboot,MybatisPlus,Beetl,Mysql,Redis,RabbitMQ,UniApp功能云端功能序号模块功能描述1系统管理菜单管理配置系统菜单2系统管理组织管理管理组织机构3系统管理角色管理配置系统角色,包含数据权限和功能权限配置4系统管理用户管理管理后台用户5系统管理租户管理多租户管理6系统管理公众号配置租户公众号配置7系统管理操作日志审计日志8系统

  2. 【uniapp】uni.request请求跨域问题解决方案 - 2

    例如,运行H5页面,请求一个地址资源,如果不是本站地址,浏览器就会报跨域错误,这样访问受限问题呈现例如,项目代码里是这样写的,运行H5测试uni.request({ url:'https://gitcode.net/zs1028/stat...ouces_2023/-/...', success(res){ console.log(res) }, fail(err){ console.error(err) }})因为https://gitcode.net不是本站地址,根据浏览器同源策略,是会报跨域错误,解决步骤打开项目的manifest.json文件,以源码视图查看,添加以下代码{ //.

  3. uniapp+uview开发微信小程序学习笔记(一) - 2

    文章目录前言一、注册小程序二、项目创建三、运行项目四、其他配置最后前言此次项目开发使用uniapp和uview进行开发,需要用到的开发工具为HBuilderX和微信开发者工具,具体的安装方式见官网,小程序注册见微信公众平台。一、注册小程序注册在微信公众平台注册小程序,按照提示注册完后会发配一个appid和密钥,需要复制保存好。完善信息设置=>基本设置,填写小程序基本信息,包括名称、头像、介绍及服务范围等。第三方设置根据开发需求添加插件授权。成员管理管理=>成员管理,点击编辑或下拉选择添加成员,输入微信号添加新的项目成员,只有成员可以进行真机测试。体验成员可以使用发布的体验版。开发设置开发=>开

  4. uniapp实现自定义相机 - 2

    自定义相机起因由于最近用uniapp调用原生相机容易出现闪退问题,找了很多教程又是压缩图片又是优化代码,我表示并没有太大作用!!实现自定义相机使用效果图拓展实现多种自定义相机水印相机身份证相机人像相机起因由于最近用uniapp调用原生相机容易出现闪退问题,找了很多教程又是压缩图片又是优化代码,我表示并没有太大作用!!于是开启了我的解决之路利用livePusher实现实现自定义相机拓展性挺强的,可以实现自定义水印、身份证拍摄、人像拍摄等这里我简单实现一个相机功能主要用于解决闪退Tip:这里需要创建nvue文件哦~创建camera.nvuetemplate> viewclass="pengke-c

  5. javascript - YouTube Player API 检测全屏退出 - 2

    所以基本上有一个按钮,点击它会打开并全屏播放视频。我想在用户退出全屏但无法捕获该事件时停止视频。现在我用它作为开始http://codepen.io/bfred-it/pen/GgOvLM谁能告诉我一个可能的解决方案。HTML:playfullscreenJS:varplayer,iframe;var$=document.querySelector.bind(document);//initplayerfunctiononYouTubeIframeAPIReady(){player=newYT.Player('player',{height:'200',width:'300',vide

  6. javascript - 具有 mui-datatables 的对象数组 - 2

    向使用mui-datatables的人提问。它以字符串数组形式处理数据,但无法加载对象数组并出现此错误:bundle.js:126379Uncaught(inpromise)TypeError:e.mapisnotafunctionimportMUIDataTablefrom"mui-datatables";classAppextendsReact.Component{render(){constcolumns=["Name","Title","Location","Age","Salary"];constdata=[{name:"GabbyGeorge",title:"Busines

  7. javascript - MUI抽屉设置背景颜色 - 2

    如何简单设置MUIDrawer的背景颜色?试过这个,但不起作用this.setState({menuOpened})}/>constlistStyle3={background:'#fafa00',backgroundColor:'red'} 最佳答案 编辑:(5月21日)-MaterialUIV4.11.1这可以在4.11.1版本和功能组件中以不同方式完成。不再需要使用HoC。这是它的完成方式:你应该使用makeStyles帮助程序使用类的定义创建钩子(Hook)并使用钩子(Hook)将它们拉出来。constuseStyles=m

  8. Jenkins发布uniapp开发的H5遇到的问题 - 2

    目录​编辑 前言:一、问题:     二、解决经历:    1、思路1(不成功):    2、思路2(成功):        三、原因分析:总结前言:    背景:由于历史原因,公司有个历史项目使用vue开发的公众号H5,原生开发的微信小程序。两端功能的完全一样,但是需要维护两个项目,最近客户提了需求需要修改部分功能,博主接到需求后,觉得维护两套代码不仅是重复开发,测试起来也麻烦,因为之前是两个人开发不同端的缘故,导致大部分的bug都是因为两端不一致产生的。为了节省时间和维护成本,提升开发测试效率,在反复对比调研,最终选择了uniapp技术框架融合两端进行重构。一、问题:           

  9. uniapp小程序微信支付流程 - 2

    微信支付(微信支付分为3大步) 1.创建订单         1.1组织订单的信息对象(包含三个参数)          价格order_price,地址consignee_addr,商品信息goods      1.2发起请求创建订单(调接口)      1.3得到服务器响应的“订单编号”  2.订单预支付      2.1发起请求获取订单的支付信息(调接口)       2.2预付订单生成失败      2.3得到订单支付相关的必要参数 3.发起微信支付      3.1调用uni.requestPayment()发起微信支付      3.2未完成支付      3.3完成了支付,进一步

  10. 全开源微信小程序商城系统源码:基于Java+uniapp框架开发 - 2

    基于Java+uniapp框架开发的全开源微信小程序商城源码源码免费分享 应用介绍基于Java+uniapp框架开发的全开源微信小程序商城系统源码,前端采用目前主流的uniapp框架开发,后端采用Java语言开发,前后端代码全部开源,减少重复造轮子,支持小程序商城秒杀、优惠券、多商户、直播卖货、分销等功能,帮助商家快速搭建一个属于自己的微信小程序商城。 主要功能:一:会员管理会员管理、会员等级、收货地址管理、会员优惠劵、会员收藏、会员足迹、搜索历史、购物车二:商城配置区域配置、商品属性种类、品牌制造商、商品规格、订单管理、商品类型、渠道管理、商品问答、反馈、关键词三:商品编辑所有商品、用户评论

随机推荐