android采用FFmpeg實(shí)現(xiàn)音視頻合成與分離
上一篇文章談到音頻剪切、混音、拼接與轉(zhuǎn)碼,也詳細(xì)介紹cMake配置與涉及FFmpeg文件的導(dǎo)入: android端采用FFmpeg進(jìn)行音頻混合與拼接剪切 ?,F(xiàn)在接著探討音視頻的合成與分離。
1、音頻提取
從多媒體文件中提取音頻,關(guān)鍵命令為“-acodec copy -vn”,其中“-acodec copy”是采用音頻編碼器拷貝音頻流,“-vn”是去掉video視頻流:
/**
* 使用ffmpeg命令行進(jìn)行抽取音頻
* @param srcFile 原文件
* @param targetFile 目標(biāo)文件
* @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(" ");//以空格分割為字符串?dāng)?shù)組
}
2、視頻提取
從多媒體文件中提取視頻,關(guān)鍵命令為“-vcodec copy -an”,其中“-vcodec copy”是采用視頻編碼器拷貝視頻流,“-an”是去掉audio音頻流:
/**
* 使用ffmpeg命令行進(jìn)行抽取視頻
* @param srcFile 原文件
* @param targetFile 目標(biāo)文件
* @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(" ");//以空格分割為字符串?dāng)?shù)組
}
3、音視頻合成
把音頻和視頻文件合成多媒體文件,關(guān)鍵命令是“-i %s -i %s -t”,分別代表輸入音頻、視頻和文件時(shí)長(zhǎng)。需要注意的是,如果原視頻文件包含有音頻,先把單獨(dú)視頻流抽取出來(lái),然后再使用獨(dú)立音頻和視頻進(jìn)行合成:
/**
* 使用ffmpeg命令行進(jìn)行音視頻合成
* @param videoFile 視頻文件
* @param audioFile 音頻文件
* @param duration 視頻時(shí)長(zhǎng)
* @param muxFile 目標(biāo)文件
* @return 合成后的文件
*/
@SuppressLint("DefaultLocale")
public static String[] mediaMux(String videoFile, String audioFile, int duration, String muxFile){
//-t:時(shí)長(zhǎng) 如果忽略音視頻時(shí)長(zhǎng),則把"-t %d"去掉
String mixAudioCmd = "ffmpeg -i %s -i %s -t %d %s";
mixAudioCmd = String.format(mixAudioCmd, videoFile, audioFile, duration, muxFile);
return mixAudioCmd.split(" ");//以空格分割為字符串?dāng)?shù)組
}
單獨(dú)的視頻提取出來(lái)后,進(jìn)行音視頻合成:
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獲取視頻時(shí)長(zhǎng)
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(videoFile);
mediaPlayer.prepare();
//單位為ms
int videoDuration = mediaPlayer.getDuration()/1000;
Log.i(TAG, "videoDuration=" + videoDuration);
mediaPlayer.release();
//使用MediaMetadataRetriever獲取音頻時(shí)長(zhǎng)
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();
//如果視頻時(shí)長(zhǎng)比音頻長(zhǎng),采用音頻時(shí)長(zhǎng),否則用視頻時(shí)長(zhǎng)
int mDuration = Math.min(audioDuration, videoDuration);
//使用純視頻與音頻進(jìn)行合成
String[] commandLine = FFmpegUtil.mediaMux(temp, audioFile, mDuration, muxFile);
executeFFmpegCmd(commandLine);
isMux = false;
} catch (Exception e) {
e.printStackTrace();
}
}
}
拼接好FFmpeg命令后,調(diào)用native方法去執(zhí)行:
/**
* 調(diào)用ffmpeg處理音視頻
* @param handleType handleType
*/
private void doHandleMedia(int handleType){
String[] commandLine = null;
switch (handleType){
case 0://音視頻合成
try {
//視頻文件有音頻,先把純視頻文件抽取出來(lái)
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執(zhí)行的回調(diào):
/**
* 執(zhí)行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();
}
});
}
}
});
}
好了,使用FFmpeg進(jìn)行音視頻合成與分離介紹完畢。如果各位有什么問(wèn)題或者建議,歡迎交流。
源碼:鏈接地址。如果對(duì)您有幫助,麻煩fork和star。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android中button點(diǎn)擊后字體的變色效果
button的點(diǎn)擊效果無(wú)疑是非常簡(jiǎn)單的,接下來(lái)通過(guò)本文給大家介紹下如何添加button點(diǎn)擊的字體顏色變化效果,感興趣的朋友一起看看吧2016-10-10
Android studio 如何刪除項(xiàng)目 module
本篇文章主要介紹了Android studio 如何刪除項(xiàng)目module的相關(guān)知識(shí),具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-05-05
Android編程實(shí)現(xiàn)XML解析與保存的三種方法詳解
這篇文章主要介紹了Android編程實(shí)現(xiàn)XML解析與保存的三種方法,結(jié)合實(shí)例形式詳細(xì)分析了Android實(shí)現(xiàn)xml解析的SAX、DOM、PULL三種方法的相關(guān)操作技巧,需要的朋友可以參考下2017-08-08
Android應(yīng)用中炫酷的橫向和環(huán)形進(jìn)度條的實(shí)例分享
這篇文章主要介紹了Android應(yīng)用中炫酷的橫向和圓形進(jìn)度條的實(shí)例分享,文中利用了一些GitHub上的插件進(jìn)行改寫(xiě),也是一片很好的二次開(kāi)發(fā)教學(xué),需要的朋友可以參考下2016-04-04
Android實(shí)現(xiàn)一個(gè)簡(jiǎn)易的帶邊輸入框
如今市面上APP的輸入框可以說(shuō)是千奇百怪,不搞點(diǎn)花樣出來(lái)貌似代表格局沒(méi)打開(kāi),還在使用系統(tǒng)自帶的輸入框的兄弟可以停下腳步,哥帶你實(shí)現(xiàn)一個(gè)簡(jiǎn)易的帶邊框輸入框,感興趣的同學(xué)可以自己動(dòng)手試一下2023-08-08

