android采用FFmpeg实现音视频合成与分离
作者:徐福记456 发布时间:2022-03-05 09:18:16
标签:android,FFmpeg,音视频
上一篇文章谈到音频剪切、混音、拼接与转码,也详细介绍cMake配置与涉及FFmpeg文件的导入: android端采用FFmpeg进行音频混合与拼接剪切 。现在接着探讨音视频的合成与分离。
1、音频提取
从多媒体文件中提取音频,关键命令为“-acodec copy -vn”,其中“-acodec copy”是采用音频编码器拷贝音频流,“-vn”是去掉video视频流:
/**
* 使用ffmpeg命令行进行抽取音频
* @param srcFile 原文件
* @param targetFile 目标文件
* @return 抽取后的音频文件
*/
public static String[] extractAudio(String srcFile, String targetFile){
//-vn:video not
String mixAudioCmd = "ffmpeg -i %s -acodec copy -vn %s";
mixAudioCmd = String.format(mixAudioCmd, srcFile, targetFile);
return mixAudioCmd.split(" ");//以空格分割为字符串数组
}
2、视频提取
从多媒体文件中提取视频,关键命令为“-vcodec copy -an”,其中“-vcodec copy”是采用视频编码器拷贝视频流,“-an”是去掉audio音频流:
/**
* 使用ffmpeg命令行进行抽取视频
* @param srcFile 原文件
* @param targetFile 目标文件
* @return 抽取后的视频文件
*/
public static String[] extractVideo(String srcFile, String targetFile){
//-an audio not
String mixAudioCmd = "ffmpeg -i %s -vcodec copy -an %s";
mixAudioCmd = String.format(mixAudioCmd, srcFile, targetFile);
return mixAudioCmd.split(" ");//以空格分割为字符串数组
}
3、音视频合成
把音频和视频文件合成多媒体文件,关键命令是“-i %s -i %s -t”,分别代表输入音频、视频和文件时长。需要注意的是,如果原视频文件包含有音频,先把单独视频流抽取出来,然后再使用独立音频和视频进行合成:
/**
* 使用ffmpeg命令行进行音视频合成
* @param videoFile 视频文件
* @param audioFile 音频文件
* @param duration 视频时长
* @param muxFile 目标文件
* @return 合成后的文件
*/
@SuppressLint("DefaultLocale")
public static String[] mediaMux(String videoFile, String audioFile, int duration, String muxFile){
//-t:时长 如果忽略音视频时长,则把"-t %d"去掉
String mixAudioCmd = "ffmpeg -i %s -i %s -t %d %s";
mixAudioCmd = String.format(mixAudioCmd, videoFile, audioFile, duration, muxFile);
return mixAudioCmd.split(" ");//以空格分割为字符串数组
}
单独的视频提取出来后,进行音视频合成:
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == 100){
String audioFile = PATH + File.separator + "tiger.mp3";//tiger.mp3
String muxFile = PATH + File.separator + "media-mux.mp4";
try {
//使用MediaPlayer获取视频时长
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(videoFile);
mediaPlayer.prepare();
//单位为ms
int videoDuration = mediaPlayer.getDuration()/1000;
Log.i(TAG, "videoDuration=" + videoDuration);
mediaPlayer.release();
//使用MediaMetadataRetriever获取音频时长
MediaMetadataRetriever mediaRetriever = new MediaMetadataRetriever();
mediaRetriever.setDataSource(audioFile);
//单位为ms
String duration = mediaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int audioDuration = (int)(Long.parseLong(duration)/1000);
Log.i(TAG, "audioDuration=" + audioDuration);
mediaRetriever.release();
//如果视频时长比音频长,采用音频时长,否则用视频时长
int mDuration = Math.min(audioDuration, videoDuration);
//使用纯视频与音频进行合成
String[] commandLine = FFmpegUtil.mediaMux(temp, audioFile, mDuration, muxFile);
executeFFmpegCmd(commandLine);
isMux = false;
} catch (Exception e) {
e.printStackTrace();
}
}
}
拼接好FFmpeg命令后,调用native方法去执行:
/**
* 调用ffmpeg处理音视频
* @param handleType handleType
*/
private void doHandleMedia(int handleType){
String[] commandLine = null;
switch (handleType){
case 0://音视频合成
try {
//视频文件有音频,先把纯视频文件抽取出来
commandLine = FFmpegUtil.extractVideo(videoFile, temp);
isMux = true;
} catch (Exception e) {
e.printStackTrace();
}
break;
case 1://提取音频
String extractAudio = PATH + File.separator + "extractAudio.aac";
commandLine = FFmpegUtil.extractAudio(srcFile, extractAudio);
break;
case 2://提取视频
String extractVideo = PATH + File.separator + "extractVideo.mp4";
commandLine = FFmpegUtil.extractVideo(srcFile, extractVideo);
break;
default:
break;
}
executeFFmpegCmd(commandLine);
}
FFmpeg执行的回调:
/**
* 执行ffmpeg命令行
* @param commandLine commandLine
*/
private void executeFFmpegCmd(final String[] commandLine){
if(commandLine == null){
return;
}
FFmpegCmd.execute(commandLine, new FFmpegCmd.OnHandleListener() {
@Override
public void onBegin() {
Log.i(TAG, "handle media onBegin...");
}
@Override
public void onEnd(int result) {
Log.i(TAG, "handle media onEnd...");
if(isMux){
mHandler.obtainMessage(100).sendToTarget();
}else {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MediaHandleActivity.this, "handle media finish...", Toast.LENGTH_SHORT).show();
}
});
}
}
});
}
来源:https://blog.csdn.net/u011686167/article/details/79146373


猜你喜欢
- Android WebView 1.首先修改activity.xml中的代码:2.然后MainActivity中的代码:3.最后设置权限:&
- 前言表单提交是最常见的数据提交方式,我们经常会填写表单信息,比如用户名,身份证,手机号等等,因此就会产生身份证是否合法,用户名是否为空,虽然
- 本文实例讲述了C#实现发送邮件的三种方法。分享给大家供大家参考。具体方法分析如下:一、问题:最近公司由于一个R&I项目的需要,用户要
- 实例如下:static void testLock1(){final AtomicInteger waitCount = new Atomi
- 目录Bitmap类BitmapData类参考:Bitmap类Bitmap对象封装了GDI+中的一个位图,此位图由图形图像及其属性的像素数据组
- 员工管理系统要求如下:通过面向对象的编程思想,实现员工信息的增删改查,存储结构为数组。限定数组长度为100。实现页面:添加员工查找员工修改员
- 提出问题我做的是一个通讯录,用到了选项菜单,每一个菜单项左边我都添加了一个小图标,运行后发现没有显示出来。解决方案利用反射机制,根据对象来寻
- 1.介绍在SpringBoot的Web项目中,默认采用的是内置Tomcat,当然也可以配置支持内置的jetty,内置有什么好处呢? 1. 方
- 上一篇 主要介绍了如何通过蓝牙连接到打印机。这一篇,我们就介绍如何向打印机发送打印指令,来打印字符和图片。1. 构造输出流首先要明确一点,就
- 在日常开发的过程中应该不可避免的会发生 crash,无论你的程序写的多么完美,都不可能完全避免 crash 的发生,可能是由于 Androi
- 本人是从事互联网金融行业的,所以会接触到一些金融类的问题,常见的一种就是数字转汉字大小写的问题。所以抽空就写了一个小小的工具类,实现了数字转
- 问题描述最近IDEA抽风了,不管是新建SpringBoot工程,还是导入项目。IDEA代码里面都会飘红~Build项目时,会提示错误:错误:
- mkdir函数用于创建目录。格式如下:#include<sys/types.h>#include<sys/stat.h&g
- 提到java里的注解,和我们平时的注释还是有很大的区别,主要是作为java特性来使用的,跟我们常见的类是同一个使用的层面。关于java注解的
- 本文实例讲述了Android编程将Activity背景设置为墙纸的简单实现方法。分享给大家供大家参考,具体如下:1)代码方式Drawable
- 1.创建动画控制器,双击打开动画控制器,创建 状态并添加动画片段,并且状态与状态之间进行连线,往返的都要有,在Animator的左上角–Pa
- Java 开发中,需要将一些易变的配置参数放置再 XML 配置文件或者 properties 配置文件中。然而 XML 配置文件需要通过 D
- Service概念及用途:Android中的服务,它与Activity不同,它是不能与用户交互的,不能自己启动的,运行在后台的程序,如果我们
- 目录一、使用BeanFactoryPostProcessor注入Bean:第一步:创建实现SpringUtils 接口工具(组件)来获取sp
- 前言: 最近一直在看Launcher模块,经过差不多两个月学习,终于摸透了Launcher的一些主要功能实现,目前继续还处于摸索状态。未看L