更新记录
1.0.2(2020-08-23)
新增IOS端FFmpeg支持。目前已经完整支持IOS端和Android端
1.0.1(2020-08-10)
1.0.1 第一个版本暂时支持android端 后期会更新IOS版本 欢迎大家使用
平台兼容性
Android | Android CPU类型 | iOS |
---|---|---|
适用版本区间:7.0 - 11.0 | armeabi-v7a:支持,arm64-v8a:支持,x86:未测试 | 适用版本区间:12 - 15 |
原生插件通用使用流程:
- 购买插件,选择该插件绑定的项目。
- 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
- 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
- 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
- 开发完毕后正式云打包
付费原生插件目前不支持离线打包。
Android 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android
iOS 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/ios
注意事项:使用HBuilderX2.7.14以下版本,如果同一插件且同一appid下购买并绑定了多个包名,提交云打包界面提示包名绑定不一致时,需要在HBuilderX项目中manifest.json->“App原生插件配置”->”云端插件“列表中删除该插件重新选择
欢迎使用UniAPP FFmpeg原生插件(完整版)支持进度显示 有详细DEMO
此插件完整集成了FFmpeg FFprobe 可以用于处理绝大部分视频编辑问题,比如视频裁剪,视频格式转换,截取视频中部分片段为gif动图,视频转音频,图片转视频,加水印,获取视频封面截图,录制视频然后进行编码转换,某些时候还支持下载m3u8文件为mp4等格式文件存储本地。还有很多功能等待您去发现,FFmpeg已完整集成在Android端,IOS版本会在后面版本中发布,敬请期待,IOS FFmpeg版本的插件已经提上日程,插件都是我平时抽空完成的。更细不及时的时候敬请见谅,可以给我发邮箱924462390@qq.com
插件使用说明
//先引入原生插件
// #ifdef APP-PLUS
const FFmpeg = uni.requireNativePlugin('Wind-FFmpeg')
// #endif
//调用API
FFmpeg.getMediaInfo({path},(res)=>{
if(res.data){
//如果有数据表示存在目录
plus.runtime.openFile(file);
}else{
//这里表示没有找到文件
uni.showModal({
content:"没有找到这个视频文件,请先进行转换!"
})
}
});
API文档
接口 | 接口说明 |
---|---|
init(options:Object,callback:function,progress:function) | 初始化FFmpeg插件,callback初始化成功后出发的回掉函数,progress为进度监控函数 |
FFmpegExcuteSync(options:Object,callback:function) | 同步执行FFmpeg命令 |
FFmpegExcuteAsync(options:Object,callback:function) | 异步执行FFmpeg命令 |
FFprobeExcute(options:Object,callback:function) | 同步执行FFprobe命令 |
stop(options:Object,callback:function) | 停止执行FFmpeg命令 |
listExecutions(options:Object,callback:function) | 列出执行FFmpeg命令 |
getMediaInfo(options:Object,callback:function) | 获取媒体信息 |
其他使用方式
// #ifdef APP-PLUS
const FFmpeg = uni.requireNativePlugin('Wind-FFmpeg')
// #endif
export default {
data() {
return {
inputFile:"",
//常见图片格式
imgExtArrs:['jpg','jpeg','gif','bmp','png','webp'] ,
radio: 'A',
curSize:0,//current size
size:0,//记录视频转换的大小用来显示进度
modalName: null,//modal for convert progress ...
frame:0,//记录转换时间
videoExts:['mov','mp4','m4a','3gp','3g2','mj2'],
outExt:'mp4'
};
},
computed:{
cmd(){
return `-i ${this.inputFile} -c:v mpeg4 ${this.outFile}`;
},
cmd2(){
return `-ss 25 -t 10 -i ${this.inputFile} -s 320x240 -f gif -r 15 ${plus.io.convertLocalFileSystemURL('_www')}OUTPUT.gif`
},
loadingPercent(){
return `${parseInt(this.curSize/this.size*100)}%`;
},
progressTime(){
return `${Math.floor(this.frame/100)}S`;
},
outFile(){
let Path = "_doc";
switch (this.radio){
case "A":
Path = "_www";
break;
case "B":
Path = "_doc";
break;
case "C":
Path = "_documents";
break;
case "D":
Path = "_downloads";
break;
default:
Path = "_www";
break;
}
return plus.io.convertLocalFileSystemURL(Path)+new Date().getTime()+"."+this.outExt
}
},
mounted() {
//转换为绝对路径
// #ifdef APP-PLUS
//this.inputFile = plus.io.convertLocalFileSystemURL("_www")+"static/video/TOD.mp4";
console.log('FFmpeg',FFmpeg);
//this.outFile = plus.io.convertLocalFileSystemURL('_www')+new Date().getTime()+"."+this.outExt
FFmpeg.init({
cmd:""
},()=>{
//表示启动成功
uni.showToast({
title:"FFmpeg 启动成功....",
icon:"none",
duration:2000
});
},(res)=>{
this.curSize = parseInt(res.time);
this.frame = parseInt(res.frame);
//这里进行进度监控
console.log(res);
})
// #endif
},
methods:{
//跳转到生成截图
generatePoster(){
uni.navigateTo({
url:"/pages/index/ffmpeg/poster"
})
},
//preview
priviewWatch(file){
console.log(file);
//先进行判断是否存在这个文件
FFmpeg.getMediaInfo({path:file},(res)=>{
if(res.data){
//如果有数据表示存在目录
plus.runtime.openFile(file);
}else{
//这里表示没有找到文件
uni.showModal({
content:"没有找到这个视频文件,请先进行转换!"
})
}
});
},
selectExt(){
this.showModal('RadioModal')
},
SetActive(e) {
this.active = e.detail.value
},
showModal(name) {
this.modalName = name;
},
hideModal(e) {
this.modalName = null
},
RadioChange2(e){
console.log(e);
this.outExt = e.detail.value;
},
RadioChange(e) {
this.radio = e.detail.value;
},
chooseVideo(){
//选择文件
// #ifdef APP-PLUS
uni.chooseVideo({
success:(video) =>{
this.inputFile = plus.io.convertLocalFileSystemURL(video.tempFilePath);
console.log(this.inputFile);
},
fail(err) {
console.log(err);
}
})
// #endif
},
chooseVideoOutDir(){
//选择输出文件夹目录
//调用plus 接口选择相册里面的图片或者视频
plus.gallery.pick((res)=>{
console.log(res);
//先取出后缀进行判断
let regxp = /(.*)\.(.*)/ig;
let regArr = regxp.exec(res);
if(this.imgExtArrs.findIndex(item=>item === regArr[2])!= -1){
uni.showToast({
title:'你选择的是图片文件,请选择视频文件!',
icon:"none",
duration:2000
})
return;
}
this.outFile = res;
},(err)=>{
})
},
startRecord(){
//先打开摄像头然后才能进行录制操作
let cm = plus.camera.getCamera();
cm.startVideoCapture((capturedFile)=>{
this.inputFile = plus.io.convertLocalFileSystemURL(capturedFile);
console.log(this.outFile);
uni.showToast({
title:"录制成功....",
icon:"none",
duration:2000
})
},(err)=>{
uni.showToast({
title:"录制失败....",
icon:"none",
duration:2000
})
})
},
startConvert(){
if(!this.inputFile){
uni.showModal({
content:"请选择输入文件或者点击录制视频!"
})
return;
}
this.showModal('Modal');
//在这之前我们要获取下输入文件的信息,主要是获取基本信息比如时长等
FFmpeg.getMediaInfo({path:this.inputFile},(res)=>{
console.log(res);
this.size = parseInt(res.data.duration*1000);//转换为毫秒
//开始转换....
console.log('开始转换....',this.cmd);
let timeout = null;
timeout = setTimeout(()=>{
clearTimeout(timeout);
//注意使用异步和同步是不一样的,同步会卡死页面 FFmpegExcuteAsync
FFmpeg.FFmpegExcuteAsync({
cmd:this.cmd
},(data)=>{
uni.showToast({
title:"FFmpeg 命令执行成功....",
icon:"none",
duration:2000
})
console.log(data);
})
},1000)
})
},
inputChange(event){
this.cmd = event.detail.value;
},
showInfo(){
//显示视频信息
}
}
}