本文是一篇关于 FFmpeg 音频处理的文章,转载详情 可见文章末尾~~

接触FFmpeg有一段时间了,它是音视频开发的开源库,几乎其他所有播放器、直播平台都基于FFmpeg进行二次开发。

本篇文章来总结下采用FFmpeg进行音频处理:音频混合、音频剪切、音频拼接与音频转码。

采用android studio进行开发,配置build.gradle文件:

defaultConfig {......externalNativeBuild {cmake {cppFlags ""}}ndk {abiFilters "armeabi-v7a"}}

另外指定cmake文件路径:

    externalNativeBuild {cmake {path "CMakeLists.txt"}}sourceSets {main {jniLibs.srcDirs = ['libs']jni.srcDirs = []}

从FFmpeg官网下载源码,编译成ffmpeg.so动态库,并且导入相关源文件与头文件:

然后配置cMakeLists文件:

add_library( # Sets the name of the library.audio-handle# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).src/main/cpp/ffmpeg_cmd.csrc/main/cpp/cmdutils.csrc/main/cpp/ffmpeg.csrc/main/cpp/ffmpeg_filter.csrc/main/cpp/ffmpeg_opt.c)
add_library( ffmpegSHAREDIMPORTED )
set_target_properties( ffmpegPROPERTIES IMPORTED_LOCATION../../../../libs/armeabi-v7a/libffmpeg.so )
include_directories(src/main/cpp/include)
find_library( log-liblog )
target_link_libraries( audio-handleffmpeg${log-lib} )

调用FFmpeg命令行进行音频处理:

/*** 调用ffmpeg处理音频* @param handleType handleType*/private void doHandleAudio(int handleType){String[] commandLine = null;switch (handleType){case 0://转码String transformFile = PATH + File.separator + "transform.aac";commandLine = FFmpegUtil.transformAudio(srcFile, transformFile);break;case 1://剪切String cutFile = PATH + File.separator + "cut.mp3";commandLine = FFmpegUtil.cutAudio(srcFile, 10, 15, cutFile);break;case 2://合并String concatFile = PATH + File.separator + "concat.mp3";commandLine = FFmpegUtil.concatAudio(srcFile, appendFile, concatFile);break;case 3://混合String mixFile = PATH + File.separator + "mix.aac";commandLine = FFmpegUtil.mixAudio(srcFile, appendFile, mixFile);break;default:break;}executeFFmpegCmd(commandLine);}

其中,音频混音、合并、剪切和转码的FFmpeg命令行的拼接如下:

 /*** 使用ffmpeg命令行进行音频转码* @param srcFile 源文件* @param targetFile 目标文件(后缀指定转码格式)* @return 转码后的文件*/public static String[] transformAudio(String srcFile, String targetFile){String transformAudioCmd = "ffmpeg -i %s %s";transformAudioCmd = String.format(transformAudioCmd, srcFile, targetFile);return transformAudioCmd.split(" ");//以空格分割为字符串数组}/*** 使用ffmpeg命令行进行音频剪切* @param srcFile 源文件* @param startTime 剪切的开始时间(单位为秒)* @param duration 剪切时长(单位为秒)* @param targetFile 目标文件* @return 剪切后的文件*/@SuppressLint("DefaultLocale")public static  String[] cutAudio(String srcFile, int startTime, int duration, String targetFile){String cutAudioCmd = "ffmpeg -i %s -ss %d -t %d %s";cutAudioCmd = String.format(cutAudioCmd, srcFile, startTime, duration, targetFile);return cutAudioCmd.split(" ");//以空格分割为字符串数组}/*** 使用ffmpeg命令行进行音频合并* @param srcFile 源文件* @param appendFile 待追加的文件* @param targetFile 目标文件* @return 合并后的文件*/public static  String[] concatAudio(String srcFile, String appendFile, String targetFile){String concatAudioCmd = "ffmpeg -i concat:%s|%s -acodec copy %s";concatAudioCmd = String.format(concatAudioCmd, srcFile, appendFile, targetFile);return concatAudioCmd.split(" ");//以空格分割为字符串数组}/*** 使用ffmpeg命令行进行音频混合* @param srcFile 源文件* @param mixFile 待混合文件* @param targetFile 目标文件* @return 混合后的文件*/public static  String[] mixAudio(String srcFile, String mixFile, String targetFile){String mixAudioCmd = "ffmpeg -i %s -i %s -filter_complex amix=inputs=2:duration=first -strict -2 %s";mixAudioCmd = String.format(mixAudioCmd, srcFile, mixFile, targetFile);return mixAudioCmd.split(" ");//以空格分割为字符串数组}

FFmpeg处理混音的公式如下,其中sample1为源文件采样率、sample2为待混合文件采样率:

混音公式:value = sample1 + sample2 - (sample1 * sample2 / (pow(2, 16-1) - 1))

开启子线程,调用native方法进行音频处理:

public static void execute(final String[] commands, final OnHandleListener onHandleListener){new Thread(new Runnable() {@Overridepublic void run() {if(onHandleListener != null){onHandleListener.onBegin();}//调用ffmpeg进行处理int result = handle(commands);if(onHandleListener != null){onHandleListener.onEnd(result);}}}).start();}private native static int handle(String[] commands);

关键的native方法,是把java传入的字符串数组转成二级指针数组,然后调用FFmpeg源码中的run方法:

 JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_FFmpegCmd_handle
(JNIEnv *env, jclass obj, jobjectArray commands){int argc = (*env)->GetArrayLength(env, commands);char **argv = (char**)malloc(argc * sizeof(char*));int i;int result;for (i = 0; i < argc; i++) {jstring jstr = (jstring) (*env)->GetObjectArrayElement(env, commands, i);char* temp = (char*) (*env)->GetStringUTFChars(env, jstr, 0);argv[i] = malloc(1024);strcpy(argv[i], temp);(*env)->ReleaseStringUTFChars(env, jstr, temp);}//执行ffmpeg命令result =  run(argc, argv);//释放内存for (i = 0; i < argc; i++) {free(argv[i]);}free(argv);return result;
}

关于FFmpeg的run方法的源码如下,中间有部分省略:

int run(int argc, char **argv)
{/****************省略********************///注册各个模块avcodec_register_all();
#if CONFIG_AVDEVICEavdevice_register_all();
#endifavfilter_register_all();av_register_all();avformat_network_init();show_banner(argc, argv, options);term_init();/****************省略********************///解析命令选项与打开输入输出文件int ret = ffmpeg_parse_options(argc, argv);if (ret < 0)exit_program(1);/****************省略********************///文件转换if (transcode() < 0)exit_program(1);/****************省略********************///退出程序操作:关闭文件、释放内存exit_program(received_nb_signals ? 255 : main_return_code);ffmpeg_cleanup(0);
}

其中,最关键的是文件转换部分,源码如下:


static int transcode(void)
{int ret, i;AVFormatContext *os;OutputStream *ost;InputStream *ist;int64_t timer_start;int64_t total_packets_written = 0;//转码方法初始化ret = transcode_init();if (ret < 0)goto fail;if (stdin_interaction) {av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n");}timer_start = av_gettime_relative();#if HAVE_PTHREADSif ((ret = init_input_threads()) < 0)goto fail;
#endif//transcode循环处理while (!received_sigterm) {int64_t cur_time= av_gettime_relative();//如果遇到"q"命令,则退出循环if (stdin_interaction)if (check_keyboard_interaction(cur_time) < 0)break;//判断是否还有输出流if (!need_output()) {av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n");break;}ret = transcode_step();if (ret < 0 && ret != AVERROR_EOF) {char errbuf[128];av_strerror(ret, errbuf, sizeof(errbuf));av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", errbuf);break;}//打印音视频流信息print_report(0, timer_start, cur_time);}
#if HAVE_PTHREADSfree_input_threads();
#endif//文件末尾最后一个stream,刷新解码器bufferfor (i = 0; i < nb_input_streams; i++) {ist = input_streams[i];if (!input_files[ist->file_index]->eof_reached && ist->decoding_needed) {process_input_packet(ist, NULL, 0);}}flush_encoders();term_exit();//写文件尾,关闭文件for (i = 0; i < nb_output_files; i++) {os = output_files[i]->ctx;if ((ret = av_write_trailer(os)) < 0) {av_log(NULL, AV_LOG_ERROR, "Error writing trailer of %s: %s", os->filename, av_err2str(ret));if (exit_on_error)exit_program(1);}}//关闭所有编码器for (i = 0; i < nb_output_streams; i++) {ost = output_streams[i];if (ost->encoding_needed) {av_freep(&ost->enc_ctx->stats_in);}total_packets_written += ost->packets_written;}if (!total_packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT)) {av_log(NULL, AV_LOG_FATAL, "Empty output\n");exit_program(1);}//关闭所有解码器for (i = 0; i < nb_input_streams; i++) {ist = input_streams[i];if (ist->decoding_needed) {avcodec_close(ist->dec_ctx);if (ist->hwaccel_uninit)ist->hwaccel_uninit(ist->dec_ctx);}}//省略最后的释放内存return ret;
}

好了,使用FFmpeg进行音频剪切、混音、拼接与转码介绍完毕。

如果各位有什么问题或者建议,欢迎交流。

转载自:https://blog.csdn.net/u011686167/article/details/79135240

文章源码地址为:https://github.com/xufuji456/FFmpegAndroid

技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

推荐阅读:

音视频面试基础题

OpenGL ES 学习资源分享

开通专辑 | 细数那些年写过的技术文章专辑

NDK 学习进阶免费视频来了

推荐几个堪称教科书级别的 Android 音视频入门项目

觉得不错,点个在看呗~

FFmpeg音频处理——音频混合、拼接、剪切、转码相关推荐

  1. android端采用FFmpeg进行音频混合与拼接剪切

    接触FFmpeg有一段时间了,它是音视频开发的开源库,几乎其他所有播放器.直播平台都基于FFmpeg进行二次开发.本篇文章来总结下采用FFmpeg进行音频处理:音频混合.音频剪切.音频拼接与音频转码. ...

  2. android 混音 源码,FFmpegAndroid android 端基于 FFmpeg 实现音频剪切、拼接、转码、混音、编解码;视频剪切、水印、截图、转码、编 @codeKK c开源站...

    android 端基于 FFmpeg 库的使用 添加编译 ffmpeg.shine.mp3lame.x264 源码的参考脚本 目前音视频相关处理: 音频剪切.拼接 音频混音 音频转码 音视频合成 音频 ...

  3. iOS -录音-音频的拼接剪切以及边录边压缩转码

    总体内容 1.录音实现 2.录音的编辑 (拼接音频:可以设置多段,音频的剪切:按照时间段剪切) 3.lame静态库进行压缩转码 一.录音实现 1.1.导入 AVFoundation 框架,多媒体的处理 ...

  4. macOS实现视频转音频以及音频拼接

    macOS实现视频转音频以及音频拼接 ffmpeg 的安装 终端输入以下指令: brew install ffmpeg 视频转音频 终端输入以下指令: ffmpeg -i 视频名称.flv -vn - ...

  5. iOS 音视频开发:Audio Unit播放FFmpeg解码的音频

    本文档描述了iOS播放经FFmpeg解码的音频数据的编程步骤,具体基于Audio Toolbox框架的Audio Session和Audio Unit框架提供的接口实现.在iOS 7及以上平台Audi ...

  6. 音视频系列--音频基本操作(音频裁剪,音频和音频混合,音频和视频混合)

    前面介绍了音频的基本原理,这篇文章继续来总结下音频的基本操作,包括裁剪,混音和音频和视频的混合操作. 一.裁剪 下面Demo将一段输入mp3文件,根据startTime和endTime,进行裁剪,先解 ...

  7. python ffmpeg 视频转图片 视频转音频 播放音频 多张图片+音频转视频 多个视频合成一个视频 改变视频播放速度

    文章目录 视频转图片 视频转音频 播放音频 图片+音频 转 视频 多个视频合成一个视频 改变视频播放速度 视频转图片 #!/usr/bin/env python # -*- encoding: utf ...

  8. spring-boot ffmpeg 搭建一个音频转码服务

    2019独角兽企业重金招聘Python工程师标准>>> 利用FFMPEG实现一个音频转码服务 提供一个音频转码服务,主要是利用ffmpeg实现转码,利用java web对外提供htt ...

  9. 在Linux环境下使用ffmpeg将PCM音频数据编码成aac数据

    在Linux环境下使用ffmpeg将PCM音频数据编码成aac数据 程序框图 代码演示 程序框图 将pcm数据经过aac编码器编码成aac数据,我是将从设备上采集的数据经过重采样送入aac编码器进行编 ...

最新文章

  1. Linux学习(七)---组管理和权限管理
  2. iOS架构-静态库.a编译时自动导出.h头文件(24)
  3. DL之GANDCGNNcGAN:GANDCGNNcGAN算法思路、关键步骤的相关配图和论文集合
  4. 二叉树的操作(二叉树的创建、先序遍历---先根、中序遍历----先左、后续遍历---后根)
  5. aws ec2 选择可用区_AWS Messaging Services:选择合适的服务
  6. 安卓自定义View进阶-分类与流程
  7. 陈潇冰php,webpack4.x入门到进阶
  8. Socket开发探秘--基于Json格式的数据协议收发
  9. python高性能写法_python高性能编程方法一
  10. python 计算器功能实现
  11. java物流项目功能介绍_JAVAEE——BOS物流项目13:Quartz概述、创建定时任务、使用JavaMail发送邮件、HighCharts概述、实现区域分区分布图...
  12. 『HDU 5855』Less Time, More profit
  13. 使用Vant组件库,van-filed配置手机号验证规则
  14. 38. OP-TEE中secure stroage------安全文件数据格式和操作过程
  15. 4个万兆光口+8千兆combo光电复用口+16千兆电口万兆三层网管型工业以太网交换机HY5700-854XG8GC16GT
  16. 绝地求生手游qq和微信不在一个服务器,绝地求生刺激战场微信和qq好友能一起玩吗 qq和微信服务器互通吗...
  17. modbus4j.jar实现modbus urt模式
  18. 麻将公式一定要背下来「大全」
  19. Eclipse下Tomcat项目自动部署路径问题(.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps)
  20. ADS8685/ADS8681/ADS8689的stm32驱动程序

热门文章

  1. 微博爬虫及简单数据分析
  2. Java艺术字图片生成
  3. 服务器维护灵魂兽刷新吗,抓灵魂兽的各种辛酸,魔兽世界猎人当年抓灵魂兽用过的黑科技漫谈...
  4. [VCS]后仿真中的几个基本概念
  5. 提示程序需要Windows 7 Service Pack 1或更高版本问题如何解决?
  6. Dataframe中na.fill的用法
  7. 一文了解 NextJS 并对性能优化做出最佳实践
  8. 特征级融合_自动驾驶系统入门(七)- 多传感器信息融合
  9. 红外和可见光图像融合算法研究
  10. c语言坐标画折线,如何用C语言画折线图