FFmpeg:Android利用Filter進行音訊資料預處理
前言
這篇檔案簡單介紹下移動端Android系統下利用FFmpeg的Filter進行音訊資料預處理的方法。
按照慣例先上一份原始碼ofollow,noindex">AndroidFFmpegFilter 。
專案實現了:
- 音量調節功能volume_filter.cpp
- 混音amix_filter.cpp
FFmepg編譯須知
由於需要用到Filter模組,
所以在FFmpeg編譯指令碼中需要開啟相關編譯選項。
否則會出現avfilter_get_by_name(filter_name)
找不到對應的處理器。
--enable-filters #or --enable-filter=name #name 指定需要使用到的filter name
如果不想自己編譯,可以使用專案編譯好的動態庫 。
使用FFmpeg相關動態庫
接下來要將ffmpeg的動態庫連結到我們的工程上面。
Filter相關只需要使用到libavfilter.so、libavformat.so、libavutil.so這三個動態庫。
參考程式碼如下(提供CMake實現,Andorid.mk請自己轉換):
set(LIB_DIR ${PROJECT_SOURCE_DIR}/libs) #設定ffmpeg的標頭檔案目錄位置 include_directories(${LIB_DIR}/include/ffmpeg) #匯入avfilter動態庫 add_library(avfilter SHARED IMPORTED ) set_target_properties(avfilter PROPERTIES IMPORTED_LOCATION ${LIB_DIR}/${ANDROID_ABI}/libavfilter-6.so ) #匯入avformat動態庫 add_library(avformat SHARED IMPORTED ) set_target_properties(avformat PROPERTIES IMPORTED_LOCATION ${LIB_DIR}/${ANDROID_ABI}/libavformat-57.so ) #匯入avutil動態庫 add_library(avutil SHARED IMPORTED ) set_target_properties(avutil PROPERTIES IMPORTED_LOCATION ${LIB_DIR}/${ANDROID_ABI}/libavutil-55.so ) #連線動態庫 target_link_libraries( your-lib avfilter avutil avformat )
FFmpeg Filter初始化流程
匯入標頭檔案
extern "C" { #include <libavfilter/buffersink.h> #include <libavfilter/buffersrc.h> #include <libavutil/channel_layout.h> };
註冊相關filter
avfilter_register_all();
獲取一個AVFilterGraph
利用這個Graph可以對後續的AVFilter進行管理。
AVFilterGraph *graph = avfilter_graph_alloc();
對於AVFilter的處理
一般步驟都是:
1、通過filter_name獲取到需要使用的AVFilter。
AVFilter filter = avfilter_get_by_name(filter_name);
2、利用AVFilter從AVFilterGraph獲取到相應的上下文環境。
AVFilterContext filter_ctx = avfilter_graph_alloc_filter(graph, filter, NULL);
3、構造初始化引數配置(多種方式)
- 方式一
char options_str[1024]; snprintf(options_str, sizeof(options_str), "sample_fmt=%s:sample_rate=%d:channel_layout=0x%" PRIx64 , av_get_sample_fmt_name(sample_format), sample_rate, sample_channel); avfilter_init_str(filter_ctx, options_str);
- 方式二
char ch_layout[64]; av_get_channel_layout_string(ch_layout, sizeof(ch_layout), 0, sample_channel); av_opt_set(filter_ctx, "channel_layout", ch_layout, AV_OPT_SEARCH_CHILDREN); av_opt_set(filter_ctx, "sample_fmt", av_get_sample_fmt_name(sample_format), AV_OPT_SEARCH_CHILDREN); av_opt_set_int(filter_ctx, "sample_rate", sample_rate, AV_OPT_SEARCH_CHILDREN); avfilter_init_str(filter_ctx, NULL);
- 方式三
AVDictionary *options_dict = NULL; char ch_layout[64]; av_get_channel_layout_string(ch_layout, sizeof(ch_layout), 0, sample_channel); av_dict_set(&options_dict, "channel_layout", ch_layout, AV_OPT_SEARCH_CHILDREN); av_dict_set(&options_dict, "sample_fmt", av_get_sample_fmt_name(sample_format), AV_OPT_SEARCH_CHILDREN); av_dict_set(&options_dict, "sample_rate", sample_rate, AV_OPT_SEARCH_CHILDREN); avfilter_init_dict(volume_ctx, &options_dict);
PS:以上三種方式的實現效果是一致的。
對各個Filter進行連結
- 連線情況一(例如音量調節):
//abuffersrc_ctx -> volume_ctx -> abuffersink_ctx avfilter_link(abuffersrc_ctx, 0, volume_ctx, 0); avfilter_link(volume_ctx, 0, abuffersink_ctx, 0);
- 連線情況二 (例如混音):
//abuffersrc1_ctx //-> amix_ctx -> abuffersink_ctx //abuffersrc2_ctx avfilter_link(abuffersrc1_ctx, 0, amix_ctx, 0); avfilter_link(abuffersrc2_ctx, 0, amix_ctx, 1); avfilter_link(amix_ctx, 0, abuffersink_ctx, 0);
初始化整個filters鏈
avfilter_graph_config(graph, NULL);
以上的流程就是整個FFmpeg Filter的初始化過程。
FFmpeg Filter使用流程
源音訊資料輸入
1、構造一個AVFrame:
//獲取一個AVFrame例項 AVFrame *avframe = av_frame_alloc(); //配置輸入音訊的格式、取樣率、聲道和取樣數 avframe->sample_rate = sample_rate; avframe->format = sample_format; avframe->channel_layout = sample_channel; avframe->nb_samples = nb_sample; //根據上面設定的情況,申請音訊資料緩衝區 av_frame_get_buffer(avframe, 1);
2、將源音訊輸入送入Filter鏈中:
av_buffersrc_add_frame(abuffersrc_ctx, avframe);
3、銷燬AVFrame相關資源
av_frame_free(&avframe);
處理後音訊資料輸出
1、申請一個AVFrame例項,值得提醒的是我們不需要對這個AVFrame做任何配置
AVFrame *avframe = av_frame_alloc();
2、從Filters鏈中獲取處理後的資料包
av_buffersink_get_frame(abuffersink_ctx, avframe);
3、提取完畢AVFrame的資料後,我們需要將其銷燬
av_frame_free(&avframe);
最後說幾句
對於FFmpeg Filter的使用,基本都是遵循上述流程。
- 註冊Filters
- 獲取一個AVFilterGraph
- 獲取多個AVFilter和AVFilterContext並進行引數配置
- 連線各個AVFilterContext
- 初始化整個Filters鏈
- 將源資料AVFrame輸入Filters連結收端
- 從Filters鏈輸出端獲取處理後資料AVFrame
對於音量調節,我們需要獲取如下幾個filter:
- abuffer:提供了音訊資料的輸入端。
- volume:提供了音訊資料音量調節的模組。
- aformat:提供了轉換成我們期望輸出格式的模組,是因為Graph會在abuffer和volume之間自動做了格式轉換。
- abuffersink:提供了音訊資料的輸出端。
對於混音,我們需要獲取如下幾個filter:
- abuffer:提供了音訊資料的輸入端,我們需要獲取兩個,因為有兩路輸入。
- amix:提供了多路音訊資料混合的模組。
- aformat:提供了轉換成我們期望輸出格式的模組,是因為Graph會在abuffer和amix之間自動做了格式轉換。
- abuffersink:提供了音訊資料的輸出端。
播放PCM檔案可以利用Audacity這個工具可以匯入pcm原始檔案,並且提供了波形圖檢視和播放功能。
本文同步釋出於簡書 、CSDN 。
End!