更新记录
1.7.0(2025-10-23)
1.andorid 针对arm64-v8a架构,适配支持16KB对齐
1.6.0(2025-07-02)
andorid 插件 修改 minSdkVersion 为 24,解决不能打包问题
1.5.0(2025-06-28)
android 更换 ffmpeg 库下载地址
查看更多平台兼容性
| Android | Android CPU类型 | iOS |
|---|---|---|
| 适用版本区间:7.0 - 16.0 | armeabi-v7a:支持,arm64-v8a:支持,x86:未测试 | 适用版本区间:10 - 18 |
原生插件通用使用流程:
- 购买插件,选择该插件绑定的项目。
- 在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原生插件配置”->”云端插件“列表中删除该插件重新选择
KJ-FFmpegV2
FFmpeg升级增强完整版、跟官方video不冲突、视频编辑 音频编辑 视频压缩 合成 裁剪 图片 水印 gif 转码...
16KB对齐说明,主要是针对arm64-v8a架构,其他架构无需搞,也搞不了
本插件的SO文件为
arm64-v8a:
libavcodec.so
libavdevice.so
libavfilter.so
libavformat.so
libavutil.so
libffmpegkit abidetect.so
libffmpegkit.so
libswresample.so
libswscale.so
check_elf_alignment.sh测试结果
xxx ~ % sh /Users/xxx/Desktop/check_elf_alignment.sh /Users/xxx/Desktop/2.apk
Recursively analyzing /Users/xxx/Desktop/2.apk
=== APK zip-alignment ===
6598773 lib/arm64-v8a/lib39285EFA.so (OK - compressed)
6660799 lib/arm64-v8a/libavcodec.so (OK - compressed)
10880966 lib/arm64-v8a/libavdevice.so (OK - compressed)
10903312 lib/arm64-v8a/libavfilter.so (OK - compressed)
12051298 lib/arm64-v8a/libavformat.so (OK - compressed)
13014671 lib/arm64-v8a/libavutil.so (OK - compressed)
13229470 lib/arm64-v8a/libbreakpad-core.so (OK - compressed)
13379295 lib/arm64-v8a/libdcblur.so (OK - compressed)
13383140 lib/arm64-v8a/libffmpegkit.so (OK - compressed)
13571480 lib/arm64-v8a/libffmpegkit_abidetect.so (OK - compressed)
13582120 lib/arm64-v8a/libgifimage.so (OK - compressed)
13688795 lib/arm64-v8a/libimagepipeline.so (OK - compressed)
13692027 lib/arm64-v8a/liblamemp3.so (OK - compressed)
13808649 lib/arm64-v8a/libnative-filters.so (OK - compressed)
13821489 lib/arm64-v8a/libnative-imagetranscoder.so (OK - compressed)
14046399 lib/arm64-v8a/libpl_droidsonroids_gif.so (OK - compressed)
14066009 lib/arm64-v8a/libstatic-webp.so (OK - compressed)
14272969 lib/arm64-v8a/libswresample.so (OK - compressed)
14308386 lib/arm64-v8a/libswscale.so (OK - compressed)
14445782 lib/arm64-v8a/libuts-runtime.so (OK - compressed)
14597832 lib/arm64-v8a/libweexcore.so (OK - compressed)
15433970 lib/arm64-v8a/libweexjsb.so (OK - compressed)
15435647 lib/arm64-v8a/libweexjss.so (OK - compressed)
19795454 lib/arm64-v8a/libweexjst.so (OK - compressed)
Verification successful
=========================
=== ELF alignment ===
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libweexcore.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libgifimage.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libavutil.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libffmpegkit_abidetect.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libnative-filters.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libavfilter.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libweexjss.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/liblamemp3.so: \e[32mALIGNED\e[0m (2**16)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libnative-imagetranscoder.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libuts-runtime.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libpl_droidsonroids_gif.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libstatic-webp.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libimagepipeline.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libffmpegkit.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libweexjsb.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libbreakpad-core.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libswscale.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/lib39285EFA.so: \e[32mALIGNED\e[0m (2**16)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libdcblur.so: \e[32mALIGNED\e[0m (2**16)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libweexjst.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libswresample.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libavdevice.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libavcodec.so: \e[32mALIGNED\e[0m (2**14)
-e /var/folders/ww/xcthf5jn7gq5_p_v4hkmrm580000gn/T/2_out_XXXXX.EEWmqLw3KN/lib/arm64-v8a/libavformat.so: \e[32mALIGNED\e[0m (2**14)
-e ELF Verification Successful
check_elf_alignment.sh
#!/bin/bash
progname="${0##*/}"
progname="${progname%.sh}"
# usage: check_elf_alignment.sh [path to *.so files|path to *.apk]
cleanup_trap() {
if [ -n "${tmp}" -a -d "${tmp}" ]; then
rm -rf ${tmp}
fi
exit $1
}
usage() {
echo "Host side script to check the ELF alignment of shared libraries."
echo "Shared libraries are reported ALIGNED when their ELF regions are"
echo "16 KB or 64 KB aligned. Otherwise they are reported as UNALIGNED."
echo
echo "Usage: ${progname} [input-path|input-APK|input-APEX]"
}
if [ ${#} -ne 1 ]; then
usage
exit
fi
case ${1} in
--help | -h | -\?)
usage
exit
;;
*)
dir="${1}"
;;
esac
if ! [ -f "${dir}" -o -d "${dir}" ]; then
echo "Invalid file: ${dir}" >&2
exit 1
fi
if [[ "${dir}" == *.apk ]]; then
trap 'cleanup_trap' EXIT
echo
echo "Recursively analyzing $dir"
echo
if { zipalign --help 2>&1 | grep -q "\-P <pagesize_kb>"; }; then
echo "=== APK zip-alignment ==="
zipalign -v -c -P 16 4 "${dir}" | egrep 'lib/arm64-v8a|lib/x86_64|Verification'
echo "========================="
else
echo "NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher."
echo " You can install the latest build-tools by running the below command"
echo " and updating your \$PATH:"
echo
echo " sdkmanager \"build-tools;35.0.0-rc3\""
fi
dir_filename=$(basename "${dir}")
tmp=$(mktemp -d -t "${dir_filename%.apk}_out_XXXXX")
unzip "${dir}" lib/* -d "${tmp}" >/dev/null 2>&1
dir="${tmp}"
fi
if [[ "${dir}" == *.apex ]]; then
trap 'cleanup_trap' EXIT
echo
echo "Recursively analyzing $dir"
echo
dir_filename=$(basename "${dir}")
tmp=$(mktemp -d -t "${dir_filename%.apex}_out_XXXXX")
deapexer extract "${dir}" "${tmp}" || { echo "Failed to deapex." && exit 1; }
dir="${tmp}"
fi
RED="\e[31m"
GREEN="\e[32m"
ENDCOLOR="\e[0m"
unaligned_libs=()
echo
echo "=== ELF alignment ==="
matches="$(find "${dir}" -type f)"
IFS=$'\n'
for match in $matches; do
# We could recursively call this script or rewrite it to though.
[[ "${match}" == *".apk" ]] && echo "WARNING: doesn't recursively inspect .apk file: ${match}"
[[ "${match}" == *".apex" ]] && echo "WARNING: doesn't recursively inspect .apex file: ${match}"
[[ $(file "${match}") == *"ELF"* ]] || continue
res="$(objdump -p "${match}" | grep LOAD | awk '{ print $NF }' | head -1)"
if [[ $res =~ 2\*\*(1[4-9]|[2-9][0-9]|[1-9][0-9]{2,}) ]]; then
echo -e "${match}: ${GREEN}ALIGNED${ENDCOLOR} ($res)"
else
echo -e "${match}: ${RED}UNALIGNED${ENDCOLOR} ($res)"
unaligned_libs+=("${match}")
fi
done
if [ ${#unaligned_libs[@]} -gt 0 ]; then
echo -e "${RED}Found ${#unaligned_libs[@]} unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).${ENDCOLOR}"
elif [ -n "${dir_filename}" ]; then
echo -e "ELF Verification Successful"
fi
echo "====================="
使用
<template>
<view class="content">
<video :src="src" style="width: 100%;"></video>
<view class="title">命令例子</view>
<view class="btns">
<button type="primary" @click="audioMixing">音频混合</button>
<button type="primary" @click="audioMerge">音频合并</button>
<button type="primary" @click="audioCropping">音频裁剪</button>
<button type="primary" @click="audioTranscoding">音频转码</button>
<button type="primary" @click="movTomp4">mov转mp4</button>
<button type="primary" @click="changeResolution">改变分辨率</button>
<button type="primary" @click="startVideoRate">改变视频速率</button>
<button type="primary" @click="concatVideo">视频合并</button>
<button type="primary" @click="videoThumbnail">视频获取所有帧</button>
<button type="primary" @click="addWatermark">视频加水印</button>
<button type="primary" @click="imageMirror">图片上下左右镜像</button>
<button type="primary" @click="videoMirror">视频上下左右镜像</button>
</view>
<view class="title">取消任务相关方法</view>
<view class="btns">
<button type="primary" @click="cancelAll">取消所有任务</button>
<button type="primary" @click="cancelSessionId">根据任务ID取消任务</button>
<button type="primary" @click="getListSessions">获取所有已完成的任务</button>
</view>
<view class="title">获取媒体信息相关方法</view>
<view class="btns">
<button type="primary" @click="ffprobe_getMediaInformation">获取媒体信息</button>
<button type="primary" @click="ffprobe_startCommand">命令获取媒体信息</button>
</view>
</view>
</template>
<script>
const KJFFmpeg = uni.requireNativePlugin('KJ-FFmpeg');
export default {
data() {
return {
src: plus.io.convertLocalFileSystemURL("static/dad%23d.mp4"),
sessionId: null
}
},
onLoad() {
},
methods: {
startCommand(command, savePath) {
plus.nativeUI.showWaiting("开始...", {
back: 'none'
});
var dic = {
"command": command
}
console.log(JSON.stringify(dic))
KJFFmpeg.startCommand(dic, (res) => {
console.log("完成回调:" + JSON.stringify(res));
/**
* 返回字段说明:{"createTime":"2022-07-05 15:35:52","startTime":"2022-07-05 15:35:52","error":null,
* "endTime":"2022-07-05 15:36:02","sessionId":1,"duration":9691,"returnCode":0,"state":3,"command":""}
* sessionId:任务ID
* state:0(创建) 1(执行) 2(失败) 3(完成)
* returnCode: 0(成功) 255(取消) 其它(失败)
* command:执行的命令
* duration:任务执行时长
* createTime:任务创建时间
* startTime:任务开始时间
* endTime:任务结束时间
* */
plus.nativeUI.closeWaiting();
if (res.returnCode == 0) {
uni.showToast({
title: '成功了',
duration: 2000
});
this.src = savePath;
uni.saveImageToPhotosAlbum({
filePath: savePath
})
} else {
uni.showToast({
title: '失败了',
duration: 2000
});
}
}, (res) => {
console.log("统计信息回调:" + JSON.stringify(res));
/**
* 返回字段说明:{"videoQuality":0,"speed":4.732010117327661,"videoFps":115.13915252685547,"bitrate":723.1251261083256,"time":6041,"videoFrameNumber":147,"sessionId":2,"size":546089}
* videoQuality:视频质量
* speed:速度
* videoFps: 视频fps
* bitrate:比特率
* time:时长 进度=time/源文件时长
* videoFrameNumber:视频帧号
* sessionId:任务ID
* size:大小
**/
}, (res) => {
console.log("开始任务回调:" + JSON.stringify(res));
this.sessionId = res.sessionId;
}, (res) => {
console.log("log回调:" + JSON.stringify(res));
/**
* 返回字段说明:{"sessionId":1,"meessage":"ffmpeg version v4.5-dev-3393-g30322ebe3c","level":32}
* meessage:日志描述
* level:日志等级 -16(标准差) -8(不打印输出) 0(出了点问题,我们现在会崩溃) 8(出了点问题,无法恢复)
* 16(出了点问题,无法无损恢复) 24(有些东西看起来不正确) 32(标准信息) 40(详细资料) 48(仅对 libav* 开发人员有用的东西)
* 56(极其冗长的调试,对 libav* 开发很有用)
* */
})
},
audioMixing() {
var filePath = plus.io.convertLocalFileSystemURL("static/pre.mp3");
var filePath2 = plus.io.convertLocalFileSystemURL("static/yuan.mp3");
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".mp3");
var command = "-i " + filePath + " -i " + filePath2 +
" -filter_complex amix=inputs=2:duration=first -strict -2 " + savePath;
this.startCommand(command, savePath);
},
audioMerge() {
var filePath = plus.io.convertLocalFileSystemURL("static/pre.mp3");
var filePath2 = plus.io.convertLocalFileSystemURL("static/yuan.mp3");
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".mp3");
var command = "-i concat:" + filePath + "|" + filePath2 + " -acodec copy " + savePath;
this.startCommand(command, savePath);
},
audioCropping() {
var filePath = plus.io.convertLocalFileSystemURL("static/m4a.m4a");
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".mp3");
var command = "-i " + filePath + " -ss 00:00:00 -t 1 " + savePath; //-ss 裁剪时间,后跟裁剪开始时间,以及 -t 裁剪时间
this.startCommand(command, savePath);
},
audioTranscoding() {
var filePath = plus.io.convertLocalFileSystemURL("static/pre.mp3");
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".m4a");
var command = "-i " + filePath + " " + savePath;
this.startCommand(command, savePath);
},
changeResolution() {
uni.chooseVideo({
sourceType: ['camera', 'album'],
compressed: false,
success: (res) => {
var filePath = plus.io.convertLocalFileSystemURL(res.tempFilePath);
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".mp4");
var width = 1080
var command = "-i " + filePath + " -vf scale=" + width + ":-1 -y " + savePath;
this.startCommand(command, savePath);
},
});
},
movTomp4() {
var filePath = plus.io.convertLocalFileSystemURL("static/1.mp4");
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".mov");
var command = "-i " + filePath +
" -c:v libx264 -vf scale=1080:-1 -crf 23 -preset medium -movflags +faststart -c:a aac " + savePath;
this.startCommand(command, savePath);
},
startVideoRate() {
uni.chooseVideo({
sourceType: ['camera', 'album'],
compressed: false,
success: (res) => {
var filePath = plus.io.convertLocalFileSystemURL(res.tempFilePath);
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".mp4");
var command = "-i " + filePath +
" -an -filter_complex \"[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]\" -map \"[v]\" -map \"[a]\" " +
savePath
this.startCommand(command, savePath);
}
});
},
concatVideo() {
var filePath = plus.io.convertLocalFileSystemURL("static/inputs.text");
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".mp4");
var command = "-f concat -i " + filePath + " -c copy " + savePath;
this.startCommand(command, savePath);
},
videoThumbnail() {
uni.chooseVideo({
sourceType: ['camera', 'album'],
compressed: false,
success: (res) => {
var filePath = plus.io.convertLocalFileSystemURL(res.tempFilePath);
uni.getVideoInfo({
src: filePath,
success: (image) => {
var savePath = plus.io.convertLocalFileSystemURL("_doc");
var command = "-i " + filePath + " -vsync 0 -s " + image.width +
"*" + image.height + " " + savePath +
"/%d.jpeg";
this.startCommand(command, savePath);
}
});
}
});
},
addWatermark() {
uni.chooseVideo({
sourceType: ['camera', 'album'],
compressed: true,
success: (res) => {
var filePath = plus.io.convertLocalFileSystemURL(res.tempFilePath);
var logoPath = plus.io.convertLocalFileSystemURL("static/logo.png");
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".mp4");
var command = "-i " + filePath + " -vf \"movie=" + logoPath +
",scale=64:48[watermask];[in][watermask] overlay=30:10 [out]\" " + savePath;
this.startCommand(command, savePath);
}
});
},
imageMirror() {
uni.chooseImage({
sourceType: ['camera', 'album'],
compressed: true,
success: (res) => {
console.log(JSON.stringify(res));
var filePath = plus.io.convertLocalFileSystemURL(res.tempFilePaths[0]);
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".jpg");
//hflip 左右镜像 vflip 上下镜像 //https://blog.csdn.net/u010164190/article/details/112689804
var command = "-i " + filePath + " -vf 'hflip' -y " + savePath;
this.startCommand(command, savePath);
}
});
},
videoMirror() {
uni.chooseVideo({
sourceType: ['camera', 'album'],
compressed: true,
success: (res) => {
var filePath = plus.io.convertLocalFileSystemURL(res.tempFilePath);
var savePath = plus.io.convertLocalFileSystemURL("_doc/" + new Date().getTime() +
".mp4");
//hflip 左右镜像 vflip 上下镜像 //https://blog.csdn.net/u010164190/article/details/112689804
var command = "-i " + filePath + " -vf 'hflip' " + savePath;
this.startCommand(command, savePath);
}
});
},
cancelAll() {
KJFFmpeg.cancelAll((res) => {
console.log("cancelAll: " + JSON.stringify(res));
})
},
cancelSessionId() {
var dic = {
"sessionId": this.sessionId
}
KJFFmpeg.cancelAll((res) => {
console.log("cancelSessionId: " + JSON.stringify(res));
})
},
getListSessions() {
KJFFmpeg.getListSessions((res) => {
console.log("getListSessions: " + JSON.stringify(res));
})
},
ffprobe_getMediaInformation() {
console.log("ssss")
var dic = {
"filePath": plus.io.convertLocalFileSystemURL("static/pre.mp3"),
"timeout": 60000 //超时时间
}
KJFFmpeg.ffprobe_getMediaInformation(dic, (res) => {
/**
* allProperties:媒体相关信息
* */
console.log("完成回调:" + JSON.stringify(res));
}, (res) => {
console.log("log回调:" + JSON.stringify(res));
})
},
ffprobe_startCommand() {
var dic = {
"command": "-i " + plus.io.convertLocalFileSystemURL("static/test.mp4") + " -hide_banner"
}
KJFFmpeg.ffprobe_startCommand(dic, (res) => {
/**
* 根据log来查看相关信息
* */
console.log("完成回调:" + JSON.stringify(res));
}, (res) => {
//console.log("log回调:" + JSON.stringify(res));
})
}
}
}
</script>
<style>
.title {
text-align: center;
margin-top: 8px;
margin-bottom: 8px;
}
button {
font-size: 12px;
margin-top: 8px;
}
.btns {
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
justify-content: start;
}
</style>
命令大全,注意:文件要传绝对路径
FFmpeg文档汇总:https://ffmpeg.org/documentation.html
FFmpeg filters文档:https://ffmpeg.org/ffmpeg-filters.html
分解、复用
抽取音频流:-i input.mp4 -acodec copy -vn out.aac
抽取视频流:-i input.mp4 -vcodec copy -an out.h264
转格式:-i input.mp4 -vcodec copy -acodec copy out.flv
音视频合并:-i input.h264 -i input.aac -vcodec copy -acodec copy out.mp4
处理原始数据
抽取yuv数据:-i input.mp4 -an -c:v rawvideo -pixel_format yuv420p out.yuv
视频中提取图片:-i input.mp4 -r 30 -ss 00:00:10 -t 2 image-%3d.jpg
-r:每秒提取30帧
-t:取t秒时间的帧
图片文件转成YUV文件:-i image.png -pix_fmt yuv420p out.yuv
YUV转H264:-f rawvideo -pix_fmt yuv420p -s 320x240 -r 30 -i input.yuv -c:v libx264 -f rawvideo out.h264
提取PCM数据:-i out.mp4 -vn -ar 44100 -ac 2 -f s16le out.pcm
PCM转WAV:-f s16be -ar 8000 -ac 2 -acodec pcm_s16be -i input.pcm output.wav
滤镜
添加水印:-i input.mp4 -vf "movie=logo.png,scale=64:48[watermask];[in][watermask] overlay=30:10 [out]" out.mp4
PCM转WAV:-f s16be -ar 8000 -ac 2 -acodec pcm_s16be -i input.pcm output.wav
拼接与裁剪
裁剪:-i input.mp4 -ss 00:00:00 -t 10 out.mp4
ss:指定开始时间
-t:被裁剪后的时长
视频合并:-f concat -i inputs.txt -c copy out.flv
inputs.txt内容如下:
file '1.flv'
file '2.flv'
file '3.flv'
音频合并:-i input1.mp3 -i input2.mp3 -filter_complex '[0:0] [1:0] concat=n=2:v=0:a=1 [a]' -map [a] out.mp3
其它
视频转JPEG:-i input.flv -r 1 -f image2 image-%3d.jpeg
视频转gif:-i input.mp4 -ss 00:00:00 -t 10 out.gif
图片转视频:-f image2 -i image-%3d.jpeg out.mp4

收藏人数:
购买(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 1314
赞赏 10
下载 12267
赞赏 1
赞赏
京公网安备:11010802035340号