uniapp视频压缩踩坑记录

Posted ㄏ、Forgetˊ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了uniapp视频压缩踩坑记录相关的知识,希望对你有一定的参考价值。

最近在聊天APP中实现了一个视频上传发送的功能,在此记录一下所踩的坑。

视频格式背景

目前市面上主流的androidios机,其所拍摄的视频大体分为两种格式,分别为H.264H.265格式,这两种格式对于我们开发而言只需要理解:
H.264为兼容性高的视频格式,可以在绝大部分设备播放
H.265为高效存储的视频格式,可节约存储空间,缺点是兼容性差,其他设备可能无法播放
参考链接: H.264_百度百科H.265_百度百科

相关实现API

(1)uni.chooseVideo选取视频
参数名说明
sourceTypealbum 从相册选视频,camera 使用相机拍摄,默认为:[‘album’, ‘camera’]
extension根据文件拓展名过滤,每一项都不能是空字符串。默认不过滤。
compressed是否压缩所选的视频源文件,默认值为 true,需要压缩。
maxDuration拍摄视频最长拍摄时间,单位秒。最长支持 60 秒。
camera‘front’、‘back’,默认’back’
success接口调用成功,返回视频文件的临时文件路径,详见返回参数说明。
fail接口调用失败的回调函数
complete接口调用结束的回调函数(调用成功、失败都会执行)
(2)uni.compressVideo压缩视频
参数名说明
src视频文件路径,可以是临时文件路径也可以是永久文件路径
quality压缩质量
bitrate码率,单位 kbps
fps帧率
resolution相对于原视频的分辨率比例,取值范围(0, 1]
success接口调用成功的回调函数
fail接口调用失败的回调函数
complete接口调用结束的回调函数(调用成功、失败都会执行)
(3)uni.getVideoInfo获取视频详细信息
参数名说明
src视频文件路径,可以是临时文件路径也可以是永久文件路径(不支持网络地址)
success接口调用成功的回调函数
fail接口调用失败的回调函数
complete接口调用结束的回调函数(调用成功、失败都会执行)

实现细节

chooseVideoAPI中集成了compressed压缩功能,但在实际操作过程中,还存在有权限及压缩效果不可控的问题。以鸿蒙系统为例,不管你是否开启压缩,其前后选取回调中的视频大小均为原视频大小(即该参数无效)。但实际上,并非该参数没有效果,而是其准备压缩该视频时被拒绝了,原因是无权限,相关代码截图如下:

// 鸿蒙系统真机调试下
// demo1 使用uni.chooseVideo并开启compressed
uni.chooseVideo(
	compressed: true,
	success: (res) => 
		// res.size获取的视频大小为原视频大小,即压缩无效
	
);

// demo2 chooseVideo不开启压缩,改用compressVideo走压缩
uni.chooseVideo(
	compressed: false,
	success: (res) => 
		uni.compressVideo(
			src: res.tempFilePath,
			quality: 'low',
			success: () =>  // 压缩成功 
			,
			fail: (err) =>  
				// 压缩失败
				console.log('err:', err);
			
		);
	
);

运行结果:在鸿蒙系统中demo1不管是否开启了compressed,其获取到的视频大小均为原视频大小;demo2中则在compressVideo中走失败回调,报错如下图:

所以基本上可以判定,在鸿蒙系统中是无法直接选取后压缩视频的。

解决方案

一番查阅文档后,有了一个大致的方向,既然无法对回调所返回的临时文件进行压缩,那么我把该临时文件拷贝一份放到APP目录下的话,对拷贝文件做压缩就可以了吧?事不宜迟,博主直接上手实践

封装一个拷贝文件的方法,file-copy.js

该方法将本地文件拷贝指定的目标文件夹下,并可指定文件名,返回值为retry则需要再调用一次

export default 
	/**
	 * 复制系统文件到APP存储中
	 * @param String localPath 文件本地路径
	 * @param String targetFolder 复制到的目标文件夹
	 * @param String fileName 文件名
	 * @returns 拷贝成功返回路径
	 */
	copySysFileToAPP(localPath, targetFolder, fileName) 
		return new Promise((resolve) => 
			// #ifdef APP-PLUS
			plus.io.resolveLocalFileSystemURL(
				localPath,
				(fileEntry) => 
					console.log('获取文件成功', fileEntry);
					plus.io.resolveLocalFileSystemURL(
						`_doc/$targetFolder`,
						(directoryEntry) => 
							console.log('获取目录成功', directoryEntry);
							fileEntry.copyTo(
								directoryEntry,
								fileName,
								(res) => 
									console.log('拷贝成功', res);
									resolve(`_doc/$targetFolder/$fileName`); // 拷贝成功返回路径
								,
								(err) => 
									console.log('拷贝失败', err);
									resolve(false);
								
							);
						,
						(directoryErr) => 
							console.log('获取目录失败', directoryErr);
							// 获取不到目标目录,需要手动创建该目录后,再重新获取目录拷贝
							plus.io.resolveLocalFileSystemURL(
								'_doc',
								(docEntry) => 
									console.log('获取_doc成功', docEntry);
									docEntry.getDirectory(
										targetFolder,
										
											create: true,
											exclusive: false
										,
										(createSuccessCB) => 
											console.log('创建目录成功', createSuccessCB);
											resolve('retry'); // 创建后返回重试
										,
										(createFailCB) => 
											console.log('创建目录失败', createFailCB);
											resolve(false);
										
									);
								,
								(docErr) => 
									console.log('获取_doc失败', docErr);
									resolve(false);
								
							);
						
					);
				,
				(fileErr) => 
					console.log('获取文件失败', fileErr);
					resolve(false);
				
			);
			// #endif
		);
	
;
在业务逻辑页面使用
<script>
import fileCopy from './file-copy.js';
export default 
	methods: 
		chooseVideo() 
			uni.chooseVideo(
				compressed: false,
				maxDuration: 60,
				success: async(res) => 
					let videoName = 'dafeizhu';
					let videoPath = await fileCopy.copySysFileToAPP(res.tempFilePath, 'video', videoName);
					if (videoPath === 'retry') 
						videoPath = await fileCopy.copySysFileToAPP(res.tempFilePath, 'video', videoName);
					
					// 拷贝完之后,用新的文件路径走压缩
					uni.compressVideo(
						src: videoPath,
						quality: 'low',
						success: (compressRes) => 
							console.log('压缩成功', compressRes);
						,
						fail: (err) => 
							console.log('压缩失败', err);
						
					)
				
			);
		
	

</script>

真机测试,压缩成功!

结束了吗?还没!

前文提到的视频格式分为两种,这两种格式均可以通过compressVideo压缩,压缩之后的视频格式为H.264,故不需要再考虑视频可播放的问题。
这个作为一个拓展,讲一下如何在压缩视频的同时,又最大限度的保持视频的质量。
compressVideo接口中,除了quality之外,还有bitrate(码率),fps(帧率),resolution(相对于原视频的分辨率比例)这三个参数,官方文档上说明了当需要更精细的控制时,可指定 bitrate、fps、和 resolution,当 quality 传入时,这三个参数将被忽略。
博主在经过实际的上手操作后,发现直接指定quality的话,压缩4K视频会导致失败,故只能使用bitratefpsresolution来控制压缩效果,那么如何获取视频的这三个参数呢?getVideoInfo接口派上用场。
在实际操作中,博主还发现了Android端获取本机拍摄的视频时,会存在视频的宽高颠倒的情况(图片也有类似的情况),故需要做特定的处理

let video = await new Promise((resolve) => 
	uni.getVideoInfo(
		src: res.tempFilePath,
		success: (result) => 
			console.log('视频信息', result, res);
			if (this.$store.state.platform === 'android' && (result.orientation === 'right' || result.orientation === 'left')) 
				// Android端在HBuilderX版本为3.6.2以下,会出现获取纵向视频宽高值相反的BUG
				// HBx3.6.2版本已修复,详见https://ask.dcloud.net.cn/question/151205
				let width = res.height;
				res.height = res.width;
				res.width = width;
			
			res.fps = Math.ceil(result.fps);
			res.bitrate = result.bitrate;
			resolve(res);
		,
		fail: () => 
			resolve(false);
		
	);
);

压缩规则制定如下:
限制视频分辨率上限为1080P(1920×1080),不足1080P则默认为原视频分辨率,超过则换算成比例;码率超过4096的,按4096算;保持原视频帧率

// 修改一下compressVideo参数
let ratio = Math.sqrt((1920 * 1080) / (video.width * video.height));
let resolution = ratio >= 1 ? 1 : ratio;
let bitrate = video.bitrate > 4 * 1024 ? 4 * 1024 : video.bitrate;
uni.compressVideo(
	src: videoPath,
	bitrate,
	fps: video.fps,
	resolution,
	...
)

大功告成!

总结

关于本文中提及的直接用chooseVideo压缩视频失败的问题,在前段时间已反馈给官方,而关于文末的压缩规则制定,是根据博主自身的需求出发考虑的,各位有需要可以参照着修修改改(不过这里面也有!如码率过高也会导致压缩失败)
每踩一个坑,都是一个成长的脚印,在官方对这些BUG问题无作为无反馈的情况下,只能硬着头皮找出路,明知山有虎,偏向虎山行!
Keep learning…

以上是关于uniapp视频压缩踩坑记录的主要内容,如果未能解决你的问题,请参考以下文章

uniapp视频压缩踩坑记录

uniapp 视频压缩插件 Ba-VideoCompressor

uniapp 视频压缩插件 Ba-VideoCompressor

Uniapp 图片选择插件(支持视频音频) Ba-MediaPicker

uniapp文件管理 文件列表 获取媒体文件 图片视频音频文档压缩包文件并实现可删除文件 图片文件列表 视频文件列表 音频文件列表 获取内存卡图片视频音频pdf xlxs docx txt

uniapp,保存视频到相册