




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】怎么在android中使用FFmpeg實(shí)現(xiàn)音頻混合
怎么在android中使用FFmpeg實(shí)現(xiàn)音頻混合?針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。采用androidstudio進(jìn)行開(kāi)發(fā),配置build.gradle文件:defaultConfig
{
externalNativeBuild
{
cmake
{
cppFlags
""
}
}
ndk
{
abiFilters
"armeabi-v7a"
}
}另外指定cmake文件路徑:externalNativeBuild
{
cmake
{
path
"CMakeLists.txt"
}
}
sourceSets
{
main
{
jniLibs.srcDirs
=
['libs']
jni.srcDirs
=
[]
}
}從FFmpeg官網(wǎng)下載源碼,編譯成ffmpeg.so動(dòng)態(tài)庫(kù),并且導(dǎo)入相關(guān)源文件與頭文件:然后配置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.c
src/main/cpp/cmdutils.c
src/main/cpp/ffmpeg.c
src/main/cpp/ffmpeg_filter.c
src/main/cpp/ffmpeg_opt.c)
add_library(
ffmpeg
SHARED
IMPORTED
)
set_target_properties(
ffmpeg
PROPERTIES
IMPORTED_LOCATION
../../../../libs/armeabi-v7a/libffmpeg.so
)
include_directories(src/main/cpp/include)
find_library(
log-lib
log
)
target_link_libraries(
audio-handle
ffmpeg
${log-lib}
)
調(diào)用FFmpeg命令行進(jìn)行音頻處理:
/**
*
調(diào)用ffmpeg處理音頻
*
@param
handleType
handleType
*/
private
void
doHandleAudio(int
handleType){
String[]
commandLine
=
null;
switch
(handleType){
case
0://轉(zhuǎn)碼
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);
}其中,音頻混音、合并、剪切和轉(zhuǎn)碼的FFmpeg命令行的拼接如下:/**
*
使用ffmpeg命令行進(jìn)行音頻轉(zhuǎn)碼
*
@param
srcFile
源文件
*
@param
targetFile
目標(biāo)文件(后綴指定轉(zhuǎn)碼格式)
*
@return
轉(zhuǎn)碼后的文件
*/
public
static
String[]
transformAudio(String
srcFile,
String
targetFile){
String
transformAudioCmd
=
"ffmpeg
-i
%s
%s";
transformAudioCmd
=
String.format(transformAudioCmd,
srcFile,
targetFile);
return
transformAudioCmd.split("
");//以空格分割為字符串?dāng)?shù)組
}
/**
*
使用ffmpeg命令行進(jìn)行音頻剪切
*
@param
srcFile
源文件
*
@param
startTime
剪切的開(kāi)始時(shí)間(單位為秒)
*
@param
duration
剪切時(shí)長(zhǎng)(單位為秒)
*
@param
targetFile
目標(biāo)文件
*
@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("
");//以空格分割為字符串?dāng)?shù)組
}
/**
*
使用ffmpeg命令行進(jìn)行音頻合并
*
@param
srcFile
源文件
*
@param
appendFile
待追加的文件
*
@param
targetFile
目標(biāo)文件
*
@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("
");//以空格分割為字符串?dāng)?shù)組
}
/**
*
使用ffmpeg命令行進(jìn)行音頻混合
*
@param
srcFile
源文件
*
@param
mixFile
待混合文件
*
@param
targetFile
目標(biāo)文件
*
@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("
");//以空格分割為字符串?dāng)?shù)組
}FFmpeg處理混音的公式如下,其中sample1為源文件采樣率、sample2為待混合文件采樣率:混音公式:value=sample1+sample2-(sample1*sample2/(pow(2,16-1)-1))開(kāi)啟子線程,調(diào)用native方法進(jìn)行音頻處理:public
static
void
execute(final
String[]
commands,
final
OnHandleListener
onHandleListener){
new
Thread(new
Runnable()
{
@Override
public
void
run()
{
if(onHandleListener
!=
null){
onHandleListener.onBegin();
}
//調(diào)用ffmpeg進(jìn)行處理
int
result
=
handle(commands);
if(onHandleListener
!=
null){
onHandleListener.onEnd(result);
}
}
}).start();
}
private
native
static
int
handle(String[]
commands);關(guān)鍵的native方法,是把java傳入的字符串?dāng)?shù)組轉(zhuǎn)成二級(jí)指針數(shù)組,然后調(diào)用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);
}
//執(zhí)行ffmpeg命令
result
=
run(argc,
argv);
//釋放內(nèi)存
for
(i
=
0;
i
<
argc;
i++)
{
free(argv[i]);
}
free(argv);
return
result;
}關(guān)于FFmpeg的run方法的源碼如下,中間有部分省略:int
run(int
argc,
char
**argv)
{
/****************省略********************/
//注冊(cè)各個(gè)模塊
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();
/****************省略********************/
//解析命令選項(xiàng)與打開(kāi)輸入輸出文件
int
ret
=
ffmpeg_parse_options(argc,
argv);
if
(ret
<
0)
exit_program(1);
/****************省略********************/
//文件轉(zhuǎn)換
if
(transcode()
<
0)
exit_program(1);
/****************省略********************/
//退出程序操作:關(guān)閉文件、釋放內(nèi)存
exit_program(received_nb_signals
?
255
:
main_return_code);
ffmpeg_cleanup(0);
}其中,最關(guān)鍵的是文件轉(zhuǎn)換部分,源碼如下:static
int
transcode(void)
{
int
ret,
i;
AVFormatContext
*os;
OutputStream
*ost;
InputStream
*ist;
int64_t
timer_start;
int64_t
total_packets_written
=
0;
//轉(zhuǎn)碼方法初始化
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_PTHREADS
if
((ret
=
init_input_threads())
<
0)
goto
fail;
#endif
//transcode循環(huán)處理
while
(!received_sigterm)
{
int64_t
cur_time=
av_gettime_relative();
//如果遇到"q"命令,則退出循環(huán)
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_PTHREADS
free_input_threads();
#endif
//文件末尾最后一個(gè)stream,刷新解碼器buffer
for
(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();
//寫(xiě)文件尾,關(guān)閉文件
for
(i
=
0;
i
<
nb_output_files;
i++)
{
os
=
output_files[i]->ctx;
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年中國(guó)玻璃鋼防護(hù)罩市場(chǎng)調(diào)查研究報(bào)告
- 2025年中國(guó)普通聚氯乙烯護(hù)套電源線市場(chǎng)調(diào)查研究報(bào)告
- Unit 3 Charity Reading A Grammar in use 教學(xué)設(shè)計(jì) -2024-2025學(xué)年高中英語(yǔ)上外版(2020)選擇性必修第二冊(cè)
- 2025年中國(guó)嬰兒護(hù)膚濕巾市場(chǎng)調(diào)查研究報(bào)告
- 2025年中國(guó)發(fā)電機(jī)保護(hù)斷路器市場(chǎng)調(diào)查研究報(bào)告
- 2025年中國(guó)公路施工機(jī)械市場(chǎng)調(diào)查研究報(bào)告
- 2025年中國(guó)不銹鋼潔具市場(chǎng)調(diào)查研究報(bào)告
- 第十四章第1節(jié)《熱機(jī)》教學(xué)設(shè)計(jì)-2024-2025學(xué)年人教版物理九年級(jí)上學(xué)期
- 2024-2025年高中化學(xué)課時(shí)分層作業(yè)5攝取人體必需的化學(xué)元素含解析蘇教版選修1
- 2024-2025學(xué)年高中數(shù)學(xué)第二章數(shù)列2.3等差數(shù)列的前n項(xiàng)和課時(shí)作業(yè)新人教A版必修5
- 高中主題班會(huì) 悟哪吒精神做英雄少年-下學(xué)期開(kāi)學(xué)第一課主題班會(huì)課件-高中主題班會(huì)課件
- 2025版大學(xué)食堂冷鏈?zhǔn)巢呐渌头?wù)合同模板3篇
- 2024年青島港灣職業(yè)技術(shù)學(xué)院高職單招語(yǔ)文歷年參考題庫(kù)含答案解析
- 廣西壯族自治區(qū)公路發(fā)展中心2025年面向社會(huì)公開(kāi)招聘657名工作人員高頻重點(diǎn)提升(共500題)附帶答案詳解
- 《中國(guó)的宗教》課件
- 2025年山東魯商集團(tuán)有限公司招聘筆試參考題庫(kù)含答案解析
- 大學(xué)轉(zhuǎn)專業(yè)高等數(shù)學(xué)試卷
- 大型活動(dòng)中的風(fēng)險(xiǎn)管理與安全保障
- 公司廠區(qū)保潔培訓(xùn)
- 課題申報(bào)書(shū):個(gè)體衰老差異視角下社區(qū)交往空間特征識(shí)別與優(yōu)化
- 2024年防盜門(mén)銷售合同范本
評(píng)論
0/150
提交評(píng)論