uniapp视频压缩踩坑记录 您所在的位置:网站首页 js上传视频压缩 uniapp视频压缩踩坑记录

uniapp视频压缩踩坑记录

2024-06-13 04:40| 来源: 网络整理| 查看: 265

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

视频格式背景

目前市面上主流的Android及iOS机,其所拍摄的视频大体分为两种格式,分别为H.264和H.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码率,单位 kbpsfps帧率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 }); } }; 在业务逻辑页面使用 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); } }) } }); } } }

真机测试,压缩成功!

结束了吗?还没!

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



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有