在最近项目中需要实现一个前端拍摄短视频并上传后端的功能。
最初考虑的实现方式是使用拍摄短视频的安卓sdk并且改成cordova插件。
考虑目前做的比较成熟的sdk有七牛云的短视频拍摄sdk,功能强大。
此sdk实现了类似与微信的按住拍摄松开停止的功能,并且可以自动转码并且上传七牛云服务器。
但是缺点如下:
1.sdk只提供函数接口,即使改成cordova插件页面也需要再重写一个类似于上图的vue页面,不如调用系统原生摄像功能方便。
2.改造为cordova插件需要考虑ios版本的问题。
3.拍摄的视频会打上七牛云水印并且上传七牛云的服务器。
4.sdk使用收费。
故最终采用了cordova提供的现有插件实现。
效果如下:
用到了三个插件:
实现思路:
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 = nullvuetmp.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 =resultvar vuetmp = this
//resolveLocalFileSystemURL需要传入'file:///'前缀的地址,故加上
var resulttmp = 'file:///' +result
debugHelper.log(resulttmp)
resolveLocalFileSystemURL(resulttmp,function(entry) {
entry.file(function(file) {var reader = newFileReader()
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}
)
},