Android NDK开发之如何实现FFmpeg视频添加水印
发表于:2025-11-16 作者:千家信息网编辑
千家信息网最后更新 2025年11月16日,这篇文章主要介绍Android NDK开发之如何实现FFmpeg视频添加水印,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!前言Android也是利用FFmpeg命令行的形式(混
千家信息网最后更新 2025年11月16日Android NDK开发之如何实现FFmpeg视频添加水印
这篇文章主要介绍Android NDK开发之如何实现FFmpeg视频添加水印,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
前言
Android也是利用FFmpeg命令行的形式(混编),进行视频转码压缩。
1.FFmpeg添加水印命令
1.1.水印命令
ffmpeg -iWildlife.wmv-vf "movie=panda.png[watermark];[in][watermark] overlay=10:10[out]"Marked.wmv
原始视频文件路径:Wildlife.wmv
水印图片路径:panda.png
水印位置:(x,y)=(10,10)<=(left,top)距离左侧、顶部各10像素;
输出文件路径:Marked.wmv
注意事项
问题:FFmpeg 3.0之前-vf "movie=绝对路径存在问题
从3.0开始就支持了
解决的方案:过滤器
ffmpeg -i /Users/yangshaohong/Desktop/Test.mov -i /Desktop/watermark.png -filter_complex overlay=480:10 /Desktop/Test_out.mp4
1.2.命令补充说明
水印位置参数的第一个数值是水印图片的左上角的x轴像素值,第二个数值是水印图片的左上角的y轴像素值。
水印位置参数除了使用数值外,还可以使用以下几个参数:

对应地可以将overlay参数设置成如下值来改变水印图片的位置:

2.Android 核心代码
2.1.jni Java声明
//添加水印 public native void addWatermark(int argc,String[] argv);
2.2.核心代码ffmpeg.c
#include "jni.h"#include "ffmpeg.h"//视频转码压缩主函数入口//SDL(main)//ffmpeg_mod.c有一个FFmpeg视频转码主函数入口//标记(声明有一个这样的函数提供给我调用)//参数含义分析//首先分析:String str = "ffmpeg -i input.mov -b:v 640k output.mp4"// argc = str.split(" ").length()// argv = str.split(" ") 字符串数组//参数一:命令行字符串命令个数//参数二:命令行字符串数组int ffmpegmain(int argc, char **argv);JNIEXPORT void JNICALL Java_com_haocai_ffmpegtest_util_VideoPlayer_transcodingCompress (JNIEnv *env, jobject jobj,jint jlen,jobjectArray jobjArray){ //转码 //将java的字符串数组转成C字符串 int argc = jlen; //开辟内存空间 char **argv = (char**)malloc(sizeof(char*) * argc); //填充内容 for (int i = 0; i < argc; ++i) { jstring str = (*env)->GetObjectArrayElement(env,jobjArray,i); const char* tem = (*env)->GetStringUTFChars(env,str,0); argv[i] = (char*)malloc(sizeof(char)*1024); strcpy(argv[i],tem); } //开始转码(底层实现就是只需命令) ffmpegmain(argc,argv); //释放内存空间 for (int i = 0; i < argc; ++i) { free(argv[i]); } //释放数组 free(argv);}调用ffmpeg_mod.c中ffmpegmain函数入口
//ffmpeg主函数入口int ffmpegmain(int argc, char **argv){ int ret; int64_t ti; //av_log_set_callback(av_log_callback); register_exit(ffmpeg_cleanup); setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */ av_log_set_flags(AV_LOG_SKIP_REPEATED); parse_loglevel(argc, argv, options); if(argc>1 && !strcmp(argv[1], "-d")){ run_as_daemon=1; av_log_set_callback(log_callback_null); argc--; argv++; } avcodec_register_all();#if CONFIG_AVDEVICE avdevice_register_all();#endif avfilter_register_all(); av_register_all(); avformat_network_init(); show_banner(argc, argv, options); term_init(); /* parse options and open all input/output files */ ret = ffmpeg_parse_options(argc, argv); if (ret < 0) { ffmpeg_cleanup(1); return 1;} if (nb_output_files <= 0 && nb_input_files == 0) { show_usage(); av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name); ffmpeg_cleanup(1); return 1; } /* file converter / grab */ if (nb_output_files <= 0) { av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n"); ffmpeg_cleanup(1); return 1; }// if (nb_input_files == 0) {// av_log(NULL, AV_LOG_FATAL, "At least one input file must be specified\n");// exit_program(1);// } current_time = ti = getutime(); if (transcode() < 0) { ffmpeg_cleanup(1); return 1;} ti = getutime() - ti; av_log(NULL, AV_LOG_FATAL, "Transcode has Finished\n"); // if (do_benchmark) { // printf("bench: utime=%0.3fs\n", ti / 1000000.0); // } // av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n", // decode_error_stat[0], decode_error_stat[1]); //if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1]) // { exit_program(69); return 69;} ///exit_program(received_nb_signals ? 255 : main_return_code); ffmpeg_cleanup(0); return main_return_code;}2.3调用程序
public void addWatermark(){ File ipFile = new File(Environment.getExternalStorageDirectory(),"告白气球.avi"); File opFile = new File(Environment.getExternalStorageDirectory(),"告白气球_out.mp4"); File wmFile = new File(Environment.getExternalStorageDirectory(),"watermark.png"); String str = "ffmpeg -i "+ipFile.getAbsolutePath()+" -i "+wmFile.getAbsolutePath()+" -filter_complex overlay=480:10 "+opFile.getAbsolutePath(); final String[] argv = str.split(" "); final int argc = argv.length; new Thread(){ public void run() { player.ffmpegCmdUtil(argc,argv); Log.i("main","------加水印完成-------"); } }.start(); }3.运行结果
3.1原视频播放
3.2添加水印
注意:
视频质量 下降是因为没有设置-b bitrate 比特率,缺省200kb/s
所以质量会下降,在参数中添加设置 如:-b 1024k 会提高视频质量
以上是"Android NDK开发之如何实现FFmpeg视频添加水印"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!
水印
视频
参数
命令
函数
字符
字符串
位置
入口
图片
数组
路径
像素
内容
数值
质量
开发
代码
内存
告白
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
国外期刊数据库
数据库系统与网络安全技术
灾备服务器
软件开发需求取证更新
Samba服务器端软件的名称是
作文网络安全靠大家500
医药软件开发研究生
网络安全自查表范文
王者转移可以选服务器吗
项目申报书范例网络安全
数据库查找姓名=和like
rdf图数据库
省司法厅网络安全
开放式数据库互联技术简称
华为业务群网络安全答案
电商平台app软件开发机构
如何连接我的世界快照版服务器
山东巨量引擎网络技术有限公司
戴尔服务器安装linux
naicat重置数据库密码
mysql查询存在数据库
政企业的网络安全
省司法厅网络安全
mfc文本文件读取数据库
沪深300网络安全
工业企业数据库营业状态
联想3550服务器管理口
亿码通科技共享软件开发
安帆网络技术工作室官方帐号
畅想一下理想的软件开发模式