在最近项目中需要实现一个前端拍摄短视频并上传后端的功能。
最初考虑的实现方式是使用拍摄短视频的安卓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
}
)
},