Cordova+vue混合开发app中实现短视频拍摄功能
在最近项目中需要实现一个前端拍摄短视频并上传后端的功能。
最初考虑的实现方式是使用拍摄短视频的安卓sdk并且改成cordova插件。
考虑目前做的比较成熟的sdk有七牛云的短视频拍摄sdk,功能强大。
此sdk实现了类似与微信的按住拍摄松开停止的功能,并且可以自动转码并且上传七牛云服务器。
但是缺点如下:
1.sdk只提供函数接口,即使改成cordova插件页面也需要再重写一个类似于上图的vue页面,不如调用系统原生摄像功能方便。
2.改造为cordova插件需要考虑ios版本的问题。
3.拍摄的视频会打上七牛云水印并且上传七牛云的服务器。
4.sdk使用收费。
故最终采用了cordova提供的现有插件实现。
效果如下:
用到了三个插件:
-
cordova-plugin-media-capture(https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-media-capture/index.html)
-
cordova-plugin-video-editor(https://github.com/jbavari/cordova-plugin-video-editor)
-
cordova-plugin-file(https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/index.html)
实现思路:
1.使用media-capture打开摄像头拍摄视频,控制时间在10秒以内。
2.此插件拍摄成功以后返回一个file类型的文件,对此文件调用video-editor插件转码成1M左右文件并且生成缩略图。此方法调用返回一个文件地址。
3.用cordvoa的file插件读取此地址,转码成base64格式二进制流。
4.在vue页面使用video.js组件展示预览视频,用户可以选择删除或者上传。
具体代码:
//调用video-capture插件拍摄 obtainVideo() { var vuetmp = this //先申请读取拌机文件权限 var permissions = cordova.plugins.permissions permissions.checkPermission( permissions.WRITE_EXTERNAL_STORAGE, function(s) { //hasPermission 验证是否成功 if (!s.hasPermission) { //没有权限 //app申请写入权限 permissions.requestPermission( permissions.WRITE_EXTERNAL_STORAGE, function(s) { if (s.hasPermission) { //申请成功 } else { msgbus.vm.setSnackBar({ value: { color: \'error\', text: `写入权限申请失败`, visible: true } }) } }, function(error) {} ) } else { //拥有权限 vuetmp.videoPath = null vuetmp.videoImg = null var options = { limit: 1, duration: 10 } //插件提供的拍视函数 navigator.device.capture.captureVideo( vuetmp.videoCaptureSuccess, error => {}, options ) } }, function(error) {} ) },
拍摄成功执行转码:
videoCaptureSuccess(mediaFiles) { this.dialog = true var file = mediaFiles[0] var vuetmp = this //resolveLocalFileSystemURL()方法将接受device-absolute-path,并返回Entry //js无法读取安卓绝对路径,需要使用toURL()函数转换成url resolveLocalFileSystemURL(file.fullPath, function(entry) { debugHelper.log(\'resolveLocalFileSystemURL\') var fileurl = entry.toURL()//如果此步转换失败检查app文件读取权限 debugHelper.log(fileurl) debugHelper.log(\'cdvfile URI: \' + fileurl) vuetmp.videoPath = fileurl //调用转码插件 VideoEditor.transcodeVideo( vuetmp.videoTranscodeSuccess, // success cb vuetmp.videoTranscodeError, // error cb { fileUri: fileurl, // the path to the video on the device outputFileName: \'ReportVideo\', // the file name for the transcoded video outputFileType: VideoEditorOptions.OutputFileType.mp4, // android is always mp4 saveToLibrary: true, // optional, defaults to true maintainAspectRatio: true, deleteInputFile: false, // optional (android only), defaults to false width: 640, // optional, see note below on width and height height: 640, // optional, see notes below on width and height videoBitrate: 1000000, // optional, bitrate in bits, defaults to 1 megabit (1000000) fps: 24, // optional (android only), defaults to 24 audioChannels: 2, // optional (ios only), number of audio channels, defaults to 2 progress: function(info) { console.log(\'transcodeVideo progress callback, info: \' + info) } // info will be a number from 0 to 100 } ) }) },
转码成功生成Base64格式二进制流;
//转码成功函数 videoTranscodeSuccess(result) { this.videoPath = result var vuetmp = this //resolveLocalFileSystemURL需要传入\'file:///\'前缀的地址,故加上 var resulttmp = \'file:///\' + result debugHelper.log(resulttmp) resolveLocalFileSystemURL(resulttmp, function(entry) { entry.file(function(file) { var reader = new FileReader() reader.onloadend = function() { vuetmp.videoBase64 = this.result//readAsDataURL函数执行成功返回result vuetmp.$emit(\'putFile\', vuetmp.videoBase64) vuetmp.dialog = false } reader.readAsDataURL(file)//此函数把文件读取为Base64二进制流 }, vuetmp.onErrorReadFile) }) //生成视频缩略图 VideoEditor.createThumbnail( this.createThumbnailSuccess, this.createThumbnailError, { fileUri: result, outputFileName: \'ReportVideoThumbnail\', atTime: 2, width: 320, height: 480, quality: 100 } ) },