uniapp uses the mui-player plugin to play m3u8/flv video streams

Background : The h5 project developed by uniapp needs to play videos with m3u8/flv suffixes. There are many video plug-ins on the Internet, but the styles and effects are not satisfactory. The blogger finally chooses the mui-player plug-in, which is slightly more customized and has official documents. Can be read, official website documentation https://muiplayer.js.org/zh/guide/

tips: It is recommended to read the official document first, and then introduce it on the page

The final effect achieved by the blogger is as follows. There are two display styles on PC and mobile. PC can set functions such as sound, playback speed, resolution, full screen, picture-in-picture, etc. For specific functions, please refer to the official website. The description on the official website is very detailed and there are examples for reference; the functions of the mobile terminal and the PC terminal are almost the same, but the display form is slightly different.
pc side
insert image description here
1. Install the mui-player plugin

npm i mui-player --save

2. Page introduction, you can choose to import it directly on the page where the video needs to be displayed, or you can put it in a public component, which is convenient for multiple pages to use the player. Here, the blogger uses the player as a public component in the component introduce

// 播放器样式文件
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

<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 sets an empty mp object

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

5. Parameters that need to be passed to the used page

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 life cycle initialization

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. When the component is destroyed, the video player should also be destroyed

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

8. Some play/pause events can be defined in the component for the parent component to call (write on demand)

// 关闭视频,返回上一页
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. The complete code of the video playback component can be added, deleted or modified as needed

<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. The parent component calls the player and modifies it as needed

<!-- #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 -->

Summary : This player still uses the open source mui-player, so try to read the documentation first, the documentation is very detailed, but it needs a large area of ​​additions, deletions, and modifications, and finally customize it to what you want.

Guess you like

Origin blog.csdn.net/qiaoqiaohong/article/details/129554288