更新记录

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

原生插件通用使用流程:

  1. 购买插件,选择该插件绑定的项目。
  2. 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
  3. 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
  4. 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
  5. 开发完毕后正式云打包

付费原生插件目前不支持离线打包。
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(){
                //显示视频信息
            }
        }
    }

敬请发挥你们的想象力使用此插件做出更多更好的应用,推荐去官方看文档,学习命令,如果在插件使用过过程中遇到什么问题,欢迎发送邮件到924462390@qq.com 空了会一一回复消息。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问