更新记录

1.0.1(2024-04-27)

增加对图片的编辑

1.0.0(2024-04-26)

初始化


平台兼容性

Vue2 Vue3
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
HBuilderX 4.12,Android:7.0,iOS:不支持 × × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序
× × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
× × × × × × × × ×

xwq-ffmpeg

文档说明

插件集成ffmpeg实现安卓端对视频、音频、图片的编辑功能

  • 功能支持
    • 视频裁剪
    • 视频合并 (注:同种类型文件合并)
    • 视频格式转换
    • 音频合并 (注:同种类型文件合并)
    • 音频格式转换
    • 获取媒体信息
    • 图片裁剪
    • 图片格式转换
    • 图片旋转
    • ....更多功能可查阅官网

页面使用方式

<template>
    <view>
        <video :src="path" :autoplay="autoplay" :controls="true" :style="{height:'250px',width:'100%'}" :object-fit="objectFit" class="`video_1`" >
        </video>
    </view>
    <view class="btn-wrap" :style="{marginBottom:'15px'}">
        <button class="btn" @click='executeFFmpeg'>{{title}}</button>
        <button class="btn" @click='videoMerge'>视频合并</button>
        <button class="btn" @click='changeVideoType'>视频格式转换</button>
        <button class="btn" @click='audioMerge'>音频合并</button>
        <button class="btn" @click='changeAudioType'>音频格式转换</button>
        <button class="btn" @click='getVideoInfo'>获取媒体信息</button>
        <button class="btn" @click='imageRota'>图片旋转</button>
        <button class="btn" @click='imageCrop'>图片裁剪</button>
        <button class="btn" @click='getVideoInfo'>获取媒体信息</button>
    </view>
    <view class="content-wrap">
        <text :style="{color:'red'}">媒体信息输出:</text>
        <view class="content">
            <textarea :value="content"></textarea>
        </view>
    </view>
    <image :src="imagePath" :style="{width:'100%',height:'auto'}"></image>

</template>

<script setup>
    import {useExecuteFFmpeg,
            FfmpegType,
            getMediaInfo,
            getLocalPath,
            TaskStartCallbackOpt,
            StatisticsCallBackOpt,
            ffmpegVideoMerge,
            MergeType,
            convertTypeV
            } from '../../uni_modules/xwq-ffmpeg';

    const title=ref('执行命令')
    const objectFit='cover'
    const autoplay=ref(false)
    const content=ref('')
    const imagePath=ref('')
    const path=ref('/static/test002.mp4')

    const executeFFmpeg=async ()=>{
        //  -vf "scale=1080x1080" 
        // libx265 mpeg4
        let result=await getLocalPath('/static/test.mp4');
        let outPath=`/storage/emulated/0/DCIM/Camera/video_${Date.now()}.mp4`
        let cmd=`-i ${result} -c:v libx265 -s 540x280 -y ${outPath}`;

        useExecuteFFmpeg({
            cmd,
            success:()=>{
                console.log('成功回调====')
                path.value=outPath
                autoplay.value=true
            },
            cancel:()=>{
                console.log('取消执行回调===')
            },
            fail:()=>{
                console.log('失败回调====')
            },
            taskStart:(val:TaskStartCallbackOpt)=>{
                console.log('任务开始回调===',val)
            },
            statistics:(val:StatisticsCallBackOpt)=>{
                // console.log('统计信息开始回调===',val)
            }
        } as FfmpegType)
    }

    /**
     *视频合并 
    */
    const videoMerge=async()=>{
        let filePath=['/static/test.mp4','/static/test002.mp4']
        let outPath=`/storage/emulated/0/DCIM/Camera/merge_video${Date.now()}.mp4`
        ffmpegVideoMerge({
            filePath,
            outPath,
            success:()=>{
                path.value=outPath
                autoplay.value=true
            },
            fail:()=>{
                console.log('失败回调====')
            }
        } as MergeType)
    }

    //视频格式转换
    const changeVideoType=()=>{
        let inputPath='/static/test.mp4';
        let videoType='flv';
        let outPath=`/storage/emulated/0/DCIM/Camera/convertType${Date.now()}.${videoType}`;
        convertTypeV(inputPath,videoType,outPath).then((filePath:string)=>{
            path.value=filePath
            autoplay.value=true
        });

    }

    //音频合并
    const audioMerge=()=>{
        let filePath=['/static/test001.mp3','/static/test002.mp3']
        // let filePath=['https://www.wanguoqiche.com/files/web/video/test001.mp4','https://www.wanguoqiche.com/files/web/video/test002.mp4']
        let outPath=`/storage/emulated/0/Android/data/uni.UNI43B527F/files/merge_Audio${Date.now()}.mp3`
        ffmpegVideoMerge({
            type:2,
            filePath,
            outPath,
            success:()=>{
                // path.value=outPath
                // autoplay.value=true
            },
            fail:()=>{
                console.log('失败回调====')
            }
        } as MergeType)
    }

    //音频格式转换
    const changeAudioType=()=>{
        let inputPath='/static/test001.mp3';
        let videoType='FLAC';
        let outPath=`/storage/emulated/0/Android/data/uni.UNI43B527F/files/convertType${Date.now()}.${videoType}`;
        convertTypeV(inputPath,videoType,outPath).then((filePath:string)=>{
            console.log('音频filePath====',filePath)
        });
    }

    //获取视频信息
    const getVideoInfo=()=>{
        let inputPath='/static/test.mp4'
        // let inputPath='/storage/emulated/0/DCIM/Camera/video_1714100294515.mp4'
        getMediaInfo(inputPath,(val:string)=>{
            content.value=val
        })
    }

    //图片格式转换
    const changeImageType=async ()=>{
        let inputPath=await getLocalPath('/static/logo.png');
        let outPath=`/storage/emulated/0/DCIM/Camera/logo_${Date.now()}.jpg`
        let cmd=`-i ${inputPath} ${outPath}`;

        useExecuteFFmpeg({
            cmd,
            success:()=>{
                console.log('成功回调====')
                imagePath.value=outPath
            }
        } as FfmpegType)
    }

    //图片旋转
    const imageRota=async ()=>{
        let inputPath=await getLocalPath('/static/crop.png');
        let outPath=`/storage/emulated/0/DCIM/Camera/logo_${Date.now()}.png`
        let cmd=`-i ${inputPath} -vf "transpose=0" ${outPath}`;
        // transpose=0:逆时针旋转 90 度。
        // transpose=1:顺时针旋转 90 度。
        // transpose=4,vflip:垂直翻转。
        useExecuteFFmpeg({
            cmd,
            success:()=>{
                console.log('成功回调====')
                imagePath.value=outPath
            }
        } as FfmpegType)
    }
    //图片裁剪
    const imageCrop=async ()=>{
        let inputPath=await getLocalPath('/static/crop.png');
        let outPath=`/storage/emulated/0/DCIM/Camera/crop_${Date.now()}.png`
        let cmd=`-i ${inputPath} -vf "crop=300:200:0:0" ${outPath}`;

        // crop=w:h:x:y
        // w裁剪后的宽度
        // h裁剪后的高
        //x y 裁剪起点坐标
        useExecuteFFmpeg({
            cmd,
            success:()=>{
                console.log('成功回调====')
                imagePath.value=outPath
            }
        } as FfmpegType)
    }
</script>

<style>

.btn-wrap{
    flex-direction: row;
    flex-wrap: wrap;
    padding: 10px;
    justify-content: flex-start;
}
.btn{
    margin: 0 10px 15px 0;
}

.content-wrap{
    padding: 10px;
    width: 100%;
    min-height:100px;
}
.content{
    width: 100%;
    height: 100%;
    border: 1px solid #ccc;
    background-color: #f2f2f2;
}
</style>

调用方法说明

  • 裁剪
    • useExecuteFFmpeg

方法参数说明

属性 类型 描述
cmd string FFmpeg命令,可参照官网例子
success function 成功回调
cancel function 取消回调
fail function 失败回调
taskStart function 任务开始回调
statistics function 统计信息回调
  • 合并
    • ffmpegVideoMerge

方法参数说明

属性 类型 描述
type number 合并的文件类型 默认1 视频格式 ,可选2 音频格式
filePath string 文件路径 只支持同种类型文件
outPath string 文件输出路径
success function 成功回调
fail function 失败回调
  • 转换
    • convertTypeV

方法参数说明

属性 类型 描述
inputPath string 输入地址
outPath string 输出地址
vType string 要转换的视频类型
  • 媒体信息
    • getMediaInfo
属性 类型 描述
filePath string 文件地址
  • 取消
    • ffmpegCancel
属性 类型 描述
sessionId number null 会话ID,取消某个任务时需要传

隐私、权限声明

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

android.permission.READ_EXTERNAL_STORAGE android.permission.WRITE_EXTERNAL_STORAGE

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

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

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