uni-app App和H5平台上传视频截取视频第一帧生成图片

Posted 啟大大

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了uni-app App和H5平台上传视频截取视频第一帧生成图片相关的知识,希望对你有一定的参考价值。

uni-app App和H5平台使用renderjs上传视频截取视频第一帧生成图片

提示:因为uni-app中renderjs仅支持App和H5平台,所以该方案仅支持当前这两个平台。 this.request为本人封装的接口请求方法,可以替换成个人的接口请求方法,如有需要可在下方留言


文章目录


前言

因为uni-app App端没有dom概念,不支持dom操作,并且uni-app的canvas不支持绘制video。renderjs完美解决了uni-app App端的基础dom操作。实现效果在最下方!!


一、renderjs简介

renderjs是一个运行在视图层的js。它比[WXS](https://uniapp.dcloud.io/tutorial/miniprogram-subject.html#wxs)更加强大。它只支持app-vue和h5。

renderjs的主要作用有2个:

  • 大幅降低逻辑层和视图层的通讯损耗,提供高性能视图交互能力
  • 在视图层操作dom,运行for web的js库

二、创建index.vue文件,下方代码均在index.vue中

1.HTML代码

代码如下(示例):

<template>
	<view class="content">
		// 逻辑层调用视图层方法,采用监听data中变量改变的方法
		<view id="canvas" class="canvas" :prop="newVal" :change:prop="canvas.create"></view>
		<button @click="choose">chooseVideo</button>
	</view>
</template>

2.逻辑层代码

代码如下(示例):

<!-- 逻辑层script -->
<script>
	export default 
		data() 
			return 
				newVal: null
			;
		,
		methods: 
			choose()
				// 选取视频文件,拿到本地地址
				uni.chooseVideo(
					sourceType: ['camera', 'album'],
					success:  (blod)=>
						// 获取视频信息,拿到宽高信息
						uni.getVideoInfo(
							src: blod.tempFilePath,
							success: (info) => 
								// 上传视频到网络地址,当然也可以使用本地地址。App、H5平台本人都测试过,都没问题!!!
								uni.uploadFile(
									url: 'http://替换成自己个上传文件接口/api/common/upload', //仅为示例,非真实的接口地址
									filePath: blod.tempFilePath,
									name: 'file',
									formData: 
										'token': uni.getStorageSync('userInfo').token
									,
									success: src => 
										// fullurl也可以使用本地地址,上传选择文件获取到的 => blod.tempFilePath
										this.newVal = fullurl: JSON.parse(src.data).data.fullurl, width: info.width, height: info.height
										// 这里当时想做个平台区分,但是后面发现H5平台这种调用方式,视图层create接受参数的时候,只能接收到newValue,但是不能接收到event, ownerInstance,所以还是统一使用上方操作
										// 下方方法仅展示,调用还是统一使用上方操作
										// // #ifdef APP-PLUS
										// this.newVal = fullurl: JSON.parse(src.data).data.fullurl, width: info.width, height: info.height
										// // #endif
										// // #ifdef H5
										// this.create(fullurl: JSON.parse(src.data).data.fullurl, width: info.width, height: info.height)
										// // #endif
									,
									complete: all => 
										console.log(JSON.parse(all.data))
									
								)
							
						)
					
				)
			,
			// 逻辑层拿到base64字符串,上传网络图片
			getBase64(options)
				this.request(
					url: 'common/base64', //仅为示例,非真实的接口地址
					data: 
						base64: options.base64
					
				).then(res=>
					// 拿到上传base64图片的网络图片
					console.log(res)
				)
			,
		
	
</script>

3.视图层代码

代码如下(示例):

<!-- 视图层script module对应HTML代码中view的id-->
<script module="canvas" lang="renderjs">
	export default 
		methods: 
			// 视图层创建base64图片
			create(newValue, oldValue, ownerInstance)
				// 第一次进入为空不操作
				if(newValue == null)
					return
				
				// 在缓存中创建video标签
				var video = document.createElement("VIDEO")
				// 通过setAttribute给video dom元素添加自动播放的属性,因为视频播放才能获取封面图    
				// 设置video自动播放属性
				video.autoplay = true
				// 该设置方法无效
				// video.setAttribute('autoplay', true)
				// 再添加一个静音的属性,否则自动播放会有声音
				// 该设置方法无效
				// video.setAttribute('muted', true)
				video.muted = true
				// 如果报错Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
				// 可以把下面两行代码加上,因为我用的线上video url,所以可能抛出了异常。大概意思就是跨域了toDataURL()使用了外域资源
				video.setAttribute('crossOrigin', 'anonymous')
				video.crossOrigin = '*'
				// 上面我们只是创建了video标签,视频播放需要内部的source的标签,scr为播放源
				video.innerHTML = '<source src=' + newValue.fullurl + ' type="audio/mp4">'
				// 再创建canvas画布标签
				var canvas = document.createElement('canvas');
				var ctx = canvas.getContext('2d');
				// video注册canplay自动播放事件
				// 防止video不播放,所以手动加个播放操作
				video.play()
				// video播放事件
				video.addEventListener('canplay', ()=>
					// 创建画布的宽高属性节点,就是图片的大小,单位PX
					var anw = document.createAttribute("width");
					anw.nodeValue = newValue.width;
					var anh = document.createAttribute("height");
					anh.nodeValue = newValue.height;
					canvas.setAttributeNode(anw);
					canvas.setAttributeNode(anh);
					// 画布渲染
					ctx.drawImage(video, 0, 0, newValue.width, newValue.height);
					// 生成base64图片,指定type为jpeg格式生成的图片base64编码会小很多
					var base64 = canvas.toDataURL('image/jpeg') // 这就是封面图片的base64编码
					// 传递数据给逻辑层
					ownerInstance.callMethod('getBase64',
						base64: base64
					)
					// 删除创建的video 、canvas dom,要不然重新选取视频生成图片不生效
					// ps:开始有这个问题,但是后面不知道为什么又没有了,如果发现生成第一次base64之后再选择不生效,可以尝试一下把下方注释打开
					// document.body.removeChild(video)
					// document.body.removeChild(canvas)
				)
			
		
	
</script>

提示:本文由本人原创,转载请注明出处!!! 如果本文对你有帮助,请点个赞吧!

实现效果

1.base64图片效果

2.线上图片效果


uni-app技术分享| uni-app转小程序_实时音视频

微信小程序 实现实时音视频与 uniapp 转码成微信小程序 实现实时音视频两者是一样的,区别仅仅是一个是原生小程序一个是 uniapp 转码成小程序

本文使用uniapp转码成小程序实现音视频通话

前提

  • 确保微信开发平台 =》开发 =》开发管理 =》接口设置实时播放音视频流实时录制音视频流开启

  • 线上版本配置 anyRTC 相关的服务器域名(本地调试可设置不校验域名)
    如图所示:小程序原生本地设置不校验域名

  • uniapp 转小程序可以在小程序编辑器中配置也可在 uniapp 的 manifest.json 中配置

代码逻辑

1. 引入anyRTC 小程序版的实时音视频 SDK

2. 初始化 SDK

3. 加入相同的频道并将自己的视频流发布出去

4. 根据相关事件回调处理相关逻辑

必须填写 appid (在 anyRTC 控制台的项目管理中获取)
用户 uid 类型必须为字符串并且不重复
只有加入同一频道房间(类型必须为字符串)才可进行通话

代码实现

1. npm 引入 ar-rtc-miniapp

   ```js
     npm i ar-rtc-miniapp
   ```

2. 封装 rtc.js

  ```javascript
    // 引入 RTC
    import ArRTC from "ar-rtc-miniapp";
    console.log("ArRTC 版本", ArRTC);
    // 定义
    let Store = 
        appId: '',
        // 本地用户uid
        userId: "",
        // 频道房间
        channelId: "",

        // RTC 客户端
        rtcClient: null,
        // 本地录制地址(小程序特有推流)
        livePusherUrl: "",
        // 远端播放(小程序特有拉流)
        livePlayerUrl: "",
    ;
    // 初始化 RTC
    const InItRTC = async (info) => 
       Store = Object.assign(Store, info)
       // 创建RTC客户端 
       Store.rtcClient = new ArRTC.client();
       // 初始化
       await Store.rtcClient.init(Store.appId);
       // 已添加远端音视频流
       Store.rtcClient.on('stream-added', rtcEvent.userPublished);
       // 已删除远端音视频流
       Store.rtcClient.on('stream-removed', rtcEvent.userUnpublished);
       // 通知应用程序发生错误
       Store.rtcClient.on('error', rtcEvent.error);
       // 更新 Url 地址
       Store.rtcClient.on('update-url', rtcEvent.updateUrl);
       // 远端视频已旋转
       Store.rtcClient.on('video-rotation', rtcEvent.videoRotation);
       // 远端用户已停止发送音频流
       Store.rtcClient.on('mute-audio', rtcEvent.muteAudio);
       // 远端用户已停止发送视频流
       Store.rtcClient.on('mute-video', rtcEvent.muteVideo);
       // 远端用户已恢复发送音频流
       Store.rtcClient.on('unmute-audio', rtcEvent.unmuteAudio);
       // 远端用户已恢复发送视频流
       Store.rtcClient.on('unmute-video', rtcEvent.unmuteAudio);
       return
   
  // RTC 监听事件处理
  const rtcEvent = 
      // RTC SDK 监听用户发布
      userPublished: (uid) => 
	    console.log("RTC SDK 监听用户发布", uid);
	    if (Store.Mode == 0) 
		    uni.showLoading(
			    title: '远端加载中',
			    mask: true,
		    )
	    

	   // 订阅远端用户发布音视频
	   Store.rtcClient.subscribe(uid, (url) => 
		   console.log("远端用户发布音视频", url);
		   // 向视频页面发送远端拉流地址
		   uni.$emit("livePusherUrlEvent", 
			   livePlayerUrl: url
		   );
	   , (err) => 
		console.log("订阅远端用户发布音视频失败", err);
	   )
   ,
       // RTC SDK 监听用户取消发布
       userUnpublished: (uid ) => 
	       console.log("RTC SDK 监听用户取消发布", uid);
       ,
     // 更新 Url 地址
     updateUrl: (
	      uid,
	      url
      ) => 
	      console.log("包含远端用户的 ID 和更新后的拉流地址", uid, url);
	      // 向视频页面发送远端拉流地址
	      uni.$emit("livePusherUrlEvent", 
		      livePlayerUrl: url
	      );
      ,
      // 视频的旋转信息以及远端用户的 ID
      videoRotation: (
	      uid,
	      rotation
      ) => 
	      console.log("视频的旋转信息以及远端用户的 ID", uid, rotation);
      ,
      // 远端用户已停止发送音频流
      muteAudio: (
	      uid
      ) => 
	      console.log("远端用户已停止发送音频流", uid);
      ,
      // 远端用户已停止发送视频流
      muteVideo: (
	      uid
      ) => 
	      console.log("远端用户已停止发送视频流", uid);
      ,
      // 远端用户已恢复发送音频流
      unmuteAudio: (
	      uid
      ) => 
	      console.log("远端用户已恢复发送音频流", uid);
      ,
      // 远端用户已恢复发送视频流
      unmuteAudio: (
	      uid
      ) => 
	      console.log("远端用户已恢复发送视频流", uid);
      ,
      // 通知应用程序发生错误。 该回调中会包含详细的错误码和错误信息
      error: (
	      code,
	      reason
      ) => 
	      console.log("错误码:" + code, "错误信息:" + reason);
      ,
     
 // RTC 内部逻辑
 const rtcInternal = 
     // 加入频道
     joinChannel: () => 
	     console.log("加入频道", Store.rtcClient);
	     return Store.rtcClient.join(undefined, Store.channelId,      Store.userId, () => 
		// uni.showModal(
		// 	title: '加入频道'
		// )
		console.log("加入频道成功", Store.rtcClient);
		// 发布视频
		rtcInternal.publishTrack();

	, (err) => 
		console.log("加入频道失败");
	);
,
// 离开频道
leaveChannel: (sendfase = true) => 
	console.log("RTC 离开频道", Store);

,
// 发布本地音视频
publishTrack: () => 
	Store.rtcClient.publish((url) => 
		console.log("发布本地音视频", url);
		// 本地录制地址(小程序特有推流)
		Store.livePusherUrl = url;
		// 向视频页面发送本地推流地址
		uni.$emit("livePusherUrlEvent", 
			livePusherUrl: url
		);
	, (
		code,
		reason
	) => 
		console.log("发布本地音视频失败", code, reason);
	)
   ,
  
     module.exports = 
        InItRTC,
        rtcInternal,
     
 ```

3. 页面调用

  • 页面
     <!-- 本地录制 -->
     <live-pusher v-if="livePusherUrl" :url="livePusherUrl" mode="RTC" autopush @statechange="statechange" @error="error" style="height: 100%;width: 100%;" />
     <!-- 远端播放 -->
     <live-player v-if="livePlayerUrl" :src="livePlayerUrl" mode="RTC" autoplay @statechange="statechange" style="height: 100%;width: 100%;position: absolute;z-index: -100;" />
  • 页面逻辑
  import RTC from "../rtc.js";
  export default 
  	data() 
  		return 
  			// 可用宽度
  			windowWidth: "",
  			// 本地录制地址(小程序特有推流)
  			livePusherUrl: "",
  			// 远端播放(小程序特有拉流)
  			livePlayerUrl: "",
  		
  	,
  	async onLoad() 
  	    // 初始化 RTC
  		await ArRTC.InItRTC(
  					appId: "------------anyRTC 控制台项目管理中获取"
  					userId: "----------------自定义",
  					channelId: "----------自定义"
  				);
  		// 加入频道
  		await ArRTC.rtcInternal.joinChannel();
  		
  	const _this = this;
  		// // 推拉流变更
  		uni.$on("livePusherUrlEvent", (data) => 
  			if (data.livePusherUrl) 
  			_this.livePusherUrl = data.livePusherUrl
  			
  
  			if (data.livePlayerUrl) 
  				_this.livePlayerUrl = data.livePlayerUrl
  			
  
  		);
  		// 获取页面宽度
  		try 
  			const res = uni.getSystemInfoSync();
  			this.windowWidth = res.windowWidth;
  
  		 catch (e) 
  			// error
  		
  	,
  	onUnload() 
  		// uni.$off()
  	,
  	methods: 
  		// 组件状态
  		statechange(e) 
  			console.log('组件状态:', e)
  		,
  		// 组件错误
  		error(e) 
  			console.log("组件错误", e);
  		
  	
  

以上是关于uni-app App和H5平台上传视频截取视频第一帧生成图片的主要内容,如果未能解决你的问题,请参考以下文章

uni-app webrtc 实现H5音视频通讯

如何在uni-app 平台快速实现一对一音视频通话应用

h5 canvas截取视频第一帧

跨平台应用开发进阶(三十):uni-app 实现集成火山视频直播服务

uni-app 158发布朋友圈-上传短视频

基于Uni-APP多端「h5+小程序+App」高仿抖音小视频|直播|聊天实例