在最近項目中需要實現一個前端拍攝短視頻並上傳后端的功能。
最初考慮的實現方式是使用拍攝短視頻的安卓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 } ) },