当前位置: 首页 > article >正文

uniapp H5 对接 声网,截图

文章目录

  • 安装依赖
  • 创建容器
    • 容器样式
  • javascript代码
    • ImageDataToBlob 方法
  • 控制控制台LOG输出

安装依赖

  • 版本"agora-rtc-sdk-ng": "^4.22.0",

创建容器

<template>
	<view class="videoValue " id="videoValue">
		<u-toast ref="uToast"></u-toast>
		<view @click="screenshot()">
			截图
		</view>
	</view>
</template>

容器样式

Hbuilder X 默认支持 less 语法

<style lang="less" scoped>
	.videoValue {
		width: 100%;
		height: 100%;
	}
</style>

javascript代码

  • 导入 agora-rtc-sdk-ng
  • 创建声网视频实例 AgoraRTC.createClient
<script>
	import AgoraRTC from 'agora-rtc-sdk-ng';
	import {
		wuRenJiApi
	} from '@/api/wu-ren-ji.js'
	// 机场直播声网
	const client = AgoraRTC.createClient({
		codec: 'vp9',// codec 设置支持 "vp8" (VP8)、"h264"(H.264) 具体差别自行研究
		mode: 'live', // "rtc"(通信场景) 和 "live"(直播场景)
		mediaType: 'video',
	});
	
	let userClient;
	// 当远端用户成功发布音视频轨道之后,SDK 会触发 user-published 事件。
	// 这个事件携带两个参数:远端用户对象 (user) 和远端发布的媒体类型 (mediaType)。
	// 此时,你可以调用 AgoraRTCClient.subscribe 发起订阅。
	client.on('user-published', async (user, mediaType) => {
		await client.subscribe(user, mediaType)
		if (mediaType === 'video') {
			await user.videoTrack.play('videoValue');
			userClient = user.videoTrack // 获取当前渲染的视频帧数据
		}
	});

	export default {
		data() {
			return {
				snObj: null,
				videoUser: null,
				shootErrorCount: 0, // 拍摄错误次数
				screenShotCount: 30,
				screenShotTimer: null
			}
		},
		mounted() {
			let snObj = uni.getStorageSync('snObj')
			// snObj 主要包含1个关键参数 deviceSn
			/**
				{
				    "createName": null,
				    "createDatetime": "2024-10-30 10:57:36",
				    "updateName": null,
				    "deviceSn": "7CTxxxxxx1",
				}
			*/
			
			this.snObj = snObj
			this.playVideo()
		},
		methods: {
			playVideo() {
				this.openJiChangZhiBo()
			},

			// 机场
			async openJiChangZhiBo() {
				let option = uni.getStorageSync('option')
				// option 包含3个关键参数 deviceSn
			/**
				{
				    "token":"++/a1oP903NnBxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxACwS3Nn",
				    "appId": "fcb7ca994xxxxxxxxxxxxxxx08b",
				    "channel": "7Cxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0"
				}
			*/
				await this.startDockerLive(1) // 连接设备
				if (option.appId && option.channel && option.token) {
					// 这一步可有可无,(离开频道),一般用于在切换页面的时候,
					// 也就是路由改变离开频道不会导致视频一直在播放,从而减少消耗费用
					await this.leave(); 
					// // 获得token渲染直播画面 连接声网实例视频
					const uid = await client.join(
						option.appId,
						option.channel,
						option.token,
						null // 设置null 或者不设置 自动分配数字 UID
					);
				} else {
					console.error('option', option);
				}
			},
			// 控制机场直播 0断开 1连接
			async startDockerLive(op = 0) {
				try {
					const data = await api.startLive({
						dockSN: this.snObj.deviceSn,
						op: op,
					}).catch(err => {
						throw new Error('控制机场直播 断开/连接抛异常' + err)
					});
					if (op === 1) {
						return data;
					}

				} catch (error) {
					console.error(error);
				}
			},
			async level() {
				await client.leave();
			},
			// 截图
			async screenshot() {
				try {
					if (this.shootErrorCount >= 5) {
					// uview框架
						this.$refs.uToast.show({
							message: `拍摄错误超过5次,请等待${this.screenShotCount}s后重试`
						})
						this.createInteVal()
						return
					}
					this.$refs.uToast.show({
						loading: true,
						message: '拍摄中...',
						type: "loading",
						duration: 1000 * 10
					})
					const resImg = await userClient.getCurrentFrameData()
					const blobData = await ImageDataToBlob(resImg) // 自行写个js文件吧代码粘过去引入

					// 创建一个 FileReader 对象
					const reader = new FileReader();
					// 定义读取完成后的回调
					reader.onloadend = async () => {
						// 获取转换后的 Base64 编码数据 
						// 这里已经是base64了,在浏览器可以直接打开看,
						// 但是因为url限制,无法看全,可以直接存到服务器,
						// 然后范围服务器的图片地址,或者是转File文件流传输到服务器
						const base64String = reader.result;
						let fileUrl = await this.uploadFile(base64String) // 这里我是把文件上传到服务器
						const data = await api.screenshot({
							fileName: fileUrl,
							deviceSn: this.snObj.deviceSn,
						}).catch(err => {
							this.shootErrorCount += 1
							this.$refs.uToast.show({
								message: "拍摄上传抛异常,原因:" + JSON.stringify(err),
								duration: 1000 * 3
							})
							throw new Error('上传抛异常' + JSON.stringify(err));
						})
						if (data.code === 200) {
							this.$refs.uToast.show({
								message: "拍摄成功,请前往我的相册查看",
								position: "center",
								duration: 1000 * 1.5
							})
						} else {
							this.$refs.uToast.show({
								message: "拍摄失败,原因:" + JSON.stringify(data.msg),
								duration: 1000 * 3
							})
							this.shootErrorCount += 1
						}
					};
					reader.readAsDataURL(blobData)
				} catch (e) {
					this.shootErrorCount += 1
					this.$refs.uToast.show({
						message: "拍摄失败,原因:" + JSON.stringify(data.msg),
						duration: 1000 * 3
					})
					console.error(e)
				}
			},
			clearInterval() {
				if (this.screenShotTimer) {
					return
				}
				this.screenShotCount -= 1
				this.screenShotTimer = setInterval(() => {
					if (this.screenShotCount > 0) {
						this.screenShotCount -= 1
					} else {
						clearInterval(this.screenShotTimer)
						this.screenShotTimer = null
						this.shootErrorCount = 0
					}
				}, 1000)
			}
		},
	}
</script>


ImageDataToBlob 方法

// Uint8ClampedArray 转blob
export function ImageDataToBlob(imageData) {
	let w = imageData.width;
	let h = imageData.height;
	let canvas = document.createElement('canvas');
	canvas.width = w;
	canvas.height = h;
	let ctx = canvas.getContext('2d');
	ctx.putImageData(imageData, 0, 0);
	return new Promise((resolve) => {
		canvas.toBlob(resolve);
	});
};

控制控制台LOG输出

AgoraRTC.setLogLevel(Number)
SDK 日志输出级别。按照输出日志最全到最少排列:

  • 0: DEBUG。输出所有的 SDK 日志。
  • 1: INFO。输出 INFO、WARNING 和 ERROR 级别的日志。
  • 2: WARNING。输出 WARNING 和 ERROR 级别的日志。
  • 3: ERROR。输出 ERROR 级别的日志。
  • 4: NONE。不输出日志。
    例如,如果你输入代码 AgoraRTC.setLogLevel(2);,就可以只看到WARNING 和 ERROR 级别的日志信息。

创建实例之前设置log

<script>
		AgoraRTC.setLogLevel(2)
		// 航线直播声网
		const client = AgoraRTC.createClient({
			codec: 'vp9',
			mode: 'live',
		});
</script>

http://www.kler.cn/a/464190.html

相关文章:

  • 友元和运算符重载
  • matlab时频分析库
  • 设计模式之桥接设计模式
  • 在Ubuntu 18.04.6 LTS安装OpenFace流程
  • 38. 考古学家
  • BP神经网络的反向传播算法
  • 【技术新浪潮】DeepSeek-V3:中国AI的开源巨浪,全球AI格局的破局者
  • C# 设计模式:装饰器模式与代理模式的区别
  • 力扣hot100——二叉树
  • 高效使用AI完成编程项目任务的指南:从需求分析到功能实现
  • 华为OD E卷(100分)45-喊7的次数重排
  • 【网站推荐】IP反查域名实战
  • leetcode 729. 我的日程安排表 I 中等
  • 小程序配置文件 —— 15 页面配置
  • 【2024美国数学建模AB题原文翻译】
  • 基于QT(C++)实现的坦克大战
  • 力扣刷题:栈和队列OJ篇(下)
  • 力扣-数据结构-10【算法学习day.81】
  • 浅谈Beam Search
  • “混合双打”二维数组展平的有效方案(Python)
  • 【SqlSugar雪花ID常见问题】.NET开源ORM框架 SqlSugar 系列
  • requests请求带cookie
  • 深入理解Java Map集合
  • 逻辑回归(Logistic Regression)深度解析
  • 在Swagger(现称为OpenAPI)中各类@api之间的区别
  • k8s系列--docker拉取镜像导入k8s的containerd中