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>

### 实现带有美颜功能的录像机 在uni-app中实现具有美颜效果的视频录制功能主要依赖于`live-pusher`组件以及特定API的支持。对于不同平台,实现方式有所区别。 #### nvue 页面 在nvue页面下可以利用`<live-pusher>`标签配合`uni.createLivePusherContext` API 来构建直播推流控件,在此过程中可以通过设置属性开启美颜效果[^1]: ```html <template> <view> <!-- 开启美颜 --> <live-pusher :beauty="2" :whiteness="2"></live-pusher> </view> </template> <script> export default { mounted(){ let context = uni.createLivePusherContext(&#39;pusher&#39;) } } </script> ``` 此处`beauty`参数用于控制磨皮强度,取值范围为0~9;`whiteness`则用来调整美白程度同样是在0到9之间变化。 #### vue 页面 (app) 针对app平台上的vue页面,则需借助plus系列接口完成相同的功能开发工作。通过调用`plus.video.createLivePusher()`函数创建实时推送器对象的同时指定美化选项: ```javascript const currentWebview = this.$mp.page.$getAppWebview() var pusherOptions = { url: &#39;your_rtmp_url&#39;, beauty: true, // 启动美颜模式 whiteningLevel: 7, // 设置美白级别 smoothLevel: 5 // 设定柔肤等级 }; var pusher = plus.video.createLivePusher(&#39;&#39;, pusherOptions); currentWebview.append(pusher); ``` 值得注意的是,以上代码片段仅适用于原生应用环境下的Vue页面配置,并且可能需要额外安装相应SDK才能正常使用这些特性。 为了进一步增强用户体验并满足更多个性化需求,还可以考虑集成第三方音视频解决方案提供商(如Agora)所提供的SDK和服务。这类服务通常会提供更加丰富的图像处理能力,比如滤镜、贴纸等特效支持[^3]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值