vue3 livePusher对象 处理内嵌摄像头视频流(手写livePusher)

前提 需要做出这样的一个效果 (以下为代码效果):

过程:
        在uniapp官网内 处理内嵌视频流可以发现livePusher这样一个组件,通过这个组件 可以实现 在页面弹窗或者某个部位实时播放摄像头的画面。

        uniapp 官网文档抛出了以下事件,来控制处理组件。

        同时 需要在模块配置中勾选livePusher模块;

        以上为官网内的例子,但是在页面使用的时候 发现了 摄像头内嵌画面出现了,但是发现事件的回调不执行,拿不到参数。

        官网问答社区出现了相同问题 作者提出用nvue方案 但是我使用一样没有效果。

官网文档中有这样一段画 使用plus.video.LivePusher  同时查看uniapp livepusher 源码 看到了该方法;
        通过 https://www.html5plus.org/doc/zh_cn/video.html#plus.video.createLivePusher 找到该方法 ,博主用的是livePusher对象处理重新写了一个简陋的直播推流组件;

<template>
	<view>
		<web-view style="background-color: #fff;" :webview-styles="{height:0,width:0}" id='webViewsss' src="">
			<!-- <cover-image  v-if="data.isSlot"  class="photograp_content_coverView" src="/static/images/home/livePusherBg.png"></cover-image> -->
			<slot v-if="data.isSlot"></slot>
		</web-view>		
	</view>
</template>

livepusher对象 需要内嵌在webView内 因此 需要创建一个webView

const pages = getCurrentPages();
const page = pages[pages.length - 1];
data.currentWebview = page.$getAppWebview(); //页面栈最顶层就是当前webview  

  通过此代码 找到页面栈最顶层就是当前webview

// 获取dom距离窗口的位置 控制webView 位置
	const getElmentTopLeft = async (className) => {
		return new Promise(reslove => {
			const query = uni.createSelectorQuery().in(pageData._this)
			query.select(className).boundingClientRect(data => {
				if (data) {
					reslove(data)
				}
			}).exec()
		})
	}

该方法为传入的为需要放置的盒子的class 通过传入类名 获取距离当前窗口的位置,从而控制webView的位置;

 
 

data.pusher = await plus.video.createLivePusher("livePusher1", {
					url: 'rtmp://192.168.99.4:1935/live/client01',
					left: 15,
					top: 10,
					right: 15,
					bottom: 10,
					width: objDom.width - 30,
					height: objDom.height - 20,
					position: 'absolute',
					mode: 'FHD',
					muted: true
				});

通过plus Api创建一个livePusher对象;

await data.currentWebview.children().forEach((v, index) => {
				if (index > 0) {
					plus.webview.close(v);
				}
			})
data.currentWebview.children()[0].append(data.pusher)

在将livePusher添加到webView前需要将多余的webView关闭 避免livePusher添加到了看不见的webView;

const ws = data.currentWebview.children()[0];
			ws.setStyle({
				height: objDom.height,
				left: objDom.left,
				top: objDom.top,
				width: objDom.width,

			})

通过传入的位置 控制当前webView的方位;
 

// 快照
	const snapshotPusher =  () => {
		return new Promise( (resolve, reject) => {
			data.pusher.snapshot(async function(e) {
				const url = await getMinImage(e.tempImagePath)
				resolve(url)
			}, function(e) {
				console.log(e);
				reject(e)
			})
		})
	}
	const getMinImage =  (imgPath) => {
		return  new Promise((resolve, reject) => {
			plus.zip.compressImage({
					src: imgPath,
					dst: imgPath,
					overwrite: true,
					quality: 40
				},
				zipRes => {
					console.log(zipRes);
					resolve(zipRes.target)						
				},
				function(error) {
					console.log('出错了');
				}
			);
		})
		
	}

通过plus api 执行快照事件 拿到回调数据 ,同时通过压缩图片 并抛出图片进制地址 ;

// 抛出销毁事件
	const destoryLisvPusher = () => {
		data.pusher.stop()
		const pages = getCurrentPages();
		const page = pages[pages.length - 1];
		const currentWebview = page.$getAppWebview();
		currentWebview.children().forEach((v, index) => {
			plus.webview.close(v);
		})
		data.isSlot = false

	}

写一个 抛出销毁事件 因为livePusher 在app 抓拍多次 会出现app闪退问题 因为结合业务 每次抓拍后 则关闭销毁当前组件
 

onUnmounted(() => {
		const pages = getCurrentPages();
		const page = pages[pages.length - 1];
		const currentWebview = page.$getAppWebview();
		currentWebview.children().forEach((v, index) => {
			plus.webview.close(v);
		})
	})

在生命周期内 再次销毁  做一个保险;
 

<livePusher ref='facialRecofnLiveRusher'>
	<cover-image class="photograp_content_coverView "
			src="/static/images/home/livePusherBg.png"></cover-image>
</livePusher>

通过slot 将cover-image覆盖在livePusher 上 ;
通过slot问题 在组件销毁的时候 控制台会出现报错 不过不影响运行;uniapp官网上也会有这个问题;想要不报错,将slot内容直接写在webView内 即可;
uniapp:uni-app官网

html5+:HTML5+ API Reference

总结:通过livePusher创建对象 与webView 结合封装了一个livePusher组件;其实可以直接用html5+ 的直播推流控件对象来创建,更简单,不过我也懒得去尝试了;webView还可以动态生成,有兴趣的可以自己去尝试一下;以下为所有代码:

组件代码:
 

<template>
	<view>
		<web-view style="background-color: #fff;" :webview-styles="{height:0,width:0}" id='webViewsss' src="">
			<!-- <cover-image  v-if="data.isSlot"  class="photograp_content_coverView" src="/static/images/home/livePusherBg.png"></cover-image> -->
			<slot v-if="data.isSlot"></slot>
		</web-view>		
	</view>
</template>

<script setup>
	import blobVue from './blob.vue'; //可以忽略 没有用
	import {
		onMounted,
		reactive,
		ref,
		getCurrentInstance,
		onUnmounted
	} from 'vue';
	import {
		onReady
	} from '@dcloudio/uni-app'
	const data = reactive({
		_this: null,
		snapshotsrc: null, //快照  
		livePusher: null,
		currentWebview: null,
		isSlot: true
	})
	const pops = defineProps({
		isWidthAll: {
			type: Boolean,
			default: false
		}
	})


	onUnmounted(() => {
		const pages = getCurrentPages();
		const page = pages[pages.length - 1];
		const currentWebview = page.$getAppWebview();
		currentWebview.children().forEach((v, index) => {
			plus.webview.close(v);
		})
	})
	// 生成livePusher
	const pusherInit = async (objDom) => {
		const pages = getCurrentPages();
		const page = pages[pages.length - 1];
		// #ifdef APP-PLUS
		data.currentWebview = page.$getAppWebview(); //页面栈最顶层就是当前webview  
		setTimeout(async function() {
			// const objDom = await getElmentTopLeft('.photograp_content')
            // 此处为业务需要
			if (pops.isWidthAll) {
				data.pusher = await plus.video.createLivePusher("livePusher1", {
					url: 'rtmp://192.168.99.4:1935/live/client01',
					left: 15,
					top: 10,
					right: 15,
					bottom: 10,
					width: objDom.width - 30,
					height: objDom.height - 20,
					position: 'absolute',
					mode: 'FHD',
					muted: true
				});
			} else {
				data.pusher = await plus.video.createLivePusher("livePusher1", {
					url: 'rtmp://192.168.99.4:1935/live/client01',
					left: 10,
					top: 10,
					right: 10,
					bottom: 10,
					width: objDom.width - 20,
					height: objDom.height - 20,
					position: 'absolute',
					mode: 'FHD',
					muted: true
				});
			}
			// 反转相机
			// data.pusher.switchCamera()
			await data.currentWebview.children().forEach((v, index) => {
				if (index > 0) {
					plus.webview.close(v);
				}
			})
			data.currentWebview.children()[0].append(data.pusher)
			const ws = data.currentWebview.children()[0];
			ws.setStyle({
				height: objDom.height,
				left: objDom.left,
				top: objDom.top,
				width: objDom.width,

			})

			
			// 监听错误事件

			data.pusher.addEventListener('statechange', function(e) {
				console.log('statechange: ' + JSON.stringify(e));
				// console.log('statechange: '+e);
			}, false);

		}, 500)

		// #endif
	}
	// 获取dom位置
	const getElmentTopLeft = async (className) => {
		return new Promise(reslove => {
			const query = uni.createSelectorQuery().in(data._this)
			query.select(className).boundingClientRect(data => {
				if (data) {
					reslove(data)
				}
			}).exec()
		})
	}
	// 快照
	const snapshotPusher =  () => {
		return new Promise( (resolve, reject) => {
			data.pusher.snapshot(async function(e) {
				const url = await getMinImage(e.tempImagePath)
				resolve(url)
			}, function(e) {
				console.log(e);
				reject(e)
			})
		})
	}
	const getMinImage =  (imgPath) => {
		return  new Promise((resolve, reject) => {
			plus.zip.compressImage({
					src: imgPath,
					dst: imgPath,
					overwrite: true,
					quality: 40
				},
				zipRes => {
					console.log(zipRes);
					resolve(zipRes.target)						
				},
				function(error) {
					console.log('出错了');
				}
			);
		})
		
	}
	
	// 抛出销毁事件
	const destoryLisvPusher = () => {
		data.pusher.stop()
		const pages = getCurrentPages();
		const page = pages[pages.length - 1];
		const currentWebview = page.$getAppWebview();
		currentWebview.children().forEach((v, index) => {
			plus.webview.close(v);
		})
		data.isSlot = false

	}
	defineExpose({
		pusherInit,
		snapshotPusher,
		destoryLisvPusher
	})
</script>

<style>

</style>

事件调用:

<template>
    <view class="photograp_content">
        <livePusher ref='settleLiveRusher'>
			<cover-image class="photograp_content_coverView "
							src="/static/images/home/livePusherBg.png"></cover-image>
					
        </livePusher>
    </view>
</template>
<script setup>
import { ref } from 'vue'
const settleLiveRusher = ref('')
// 生成
setTimeout(async () => {
	const objDom = await getElmentTopLeft('.photograp_content')
    nextTick(() => {
						settleLiveRusher.value.pusherInit(objDom)
					})
					
}, 500)
// 销毁
const facialRecoModelClose = () => {
    facialRecofnLiveRusher.value.destoryLisvPusher()
			
}
// 获取dom距离窗口的位置 控制webView 位置
	const getElmentTopLeft = async (className) => {
		return new Promise(reslove => {
			const query = uni.createSelectorQuery().in(pageData._this)
			query.select(className).boundingClientRect(data => {
				if (data) {
					reslove(data)
				}
			}).exec()
		})
	}
</script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值