FFmpeg 音視頻倍速控制
網(wǎng)上關(guān)于FFmpeg音視頻倍速控制的資料不多,大部分都是講通過(guò)FFmpeg命令去做音視頻文件的倍速處理,通過(guò)FFmpeg api去 處理倍速的資料少之又少。
本文除了會(huì)講到通過(guò)命令行處理倍速,還會(huì)講到通過(guò)FFmpeg api的方式去處理音頻倍速和視頻倍速,進(jìn)而合并成支持倍速的音視頻發(fā)布成rtmp或者存成flv文件。
介紹FFmpeg的filter工具
音視頻的倍速處理少不了filter這個(gè)工具,filter可以翻譯成過(guò)濾器和濾波器。
按照處理數(shù)據(jù)的類型還可分為音頻filter、視頻filter、字幕filter,F(xiàn)Fmpeg可以通過(guò)filter將音視頻實(shí)現(xiàn)出非常多不同的filter效果,視頻可以實(shí)現(xiàn)縮放、合并、裁剪、旋轉(zhuǎn)、水印添加、倍速等效果,音頻可以實(shí)現(xiàn)回聲、延遲、去噪、混音、音量調(diào)節(jié)、變速等效果。
我們可以通過(guò)filter不同方式的組合去定制出我們想要的音視頻特效
介紹FFmpeg命令行倍速
1、視頻的倍速主要是通過(guò)控制filter中的setpts來(lái)實(shí)現(xiàn),setpts是視頻濾波器通過(guò)改變每一個(gè)pts時(shí)間戳來(lái)實(shí)現(xiàn)倍速的效果,如下只要把PTS縮小一半就可以實(shí)現(xiàn)2倍速,相反的是PTS增加一倍就達(dá)到2倍慢放的效果。
ffmpeg?-i?in.mp4?-filter:v?"setpts=0.5*PTS"?out.mp4
ffmpeg?-i?in.mp4?-filter:v?"setpts=2.0*PTS"?out.mp4
2、音頻的倍速則是通過(guò)控制filter的atempo來(lái)實(shí)現(xiàn),atempo的配置區(qū)間在0.5和2.0之間,如果需要更高倍速,則需要多個(gè)atempo串一起,下面是2、4倍速的實(shí)現(xiàn)命令。
ffmpeg?-i?in.mp4?-filter:"atempo=2.0"?-vn?out.mp4
ffmpeg?-i?in.mp4?-filter:"atempo=2.0,atempo=2.0"?-vn?out.mp4
3、同時(shí)對(duì)音視頻進(jìn)行2倍速。
ffmpeg?-i?in.mp4?-filter_complex?"[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]"?-map?"[v]"?-map?"[a]"?out.mp4
視頻倍速
視頻倍速可以通過(guò)filter的setpts來(lái)更改視頻的倍速,但是filter只對(duì)未編碼的原始視頻數(shù)據(jù)進(jìn)行倍速處理。
我這邊對(duì)于視頻的處理是將H264進(jìn)行格式封裝,如果使用filter的話,我還需要先對(duì)H264解碼,再做倍速轉(zhuǎn)換,再重新編碼成H264,這樣將會(huì)消耗相當(dāng)多的資源。
我這邊對(duì)視頻倍速就沒必要用filter進(jìn)行處理。其實(shí)視頻倍速只需要改變封裝格式時(shí)設(shè)置的時(shí)間戳就可以,所以我這邊只要改變video_tempo這個(gè)參數(shù)的值即可,1000是正常倍速、2000是2倍速,依次疊加。
if?(tempo?!=?video_tempo?&&?(tempo?>=?1000?&&?tempo?<=?16000))?{
??video_tempo?=?tempo;
??Logger::Info("video_tempo?=?%d",?video_tempo);
}
?AVRational?time_base?=?{?1,?video_tempo};
?pkt.pts?=?av_rescale_q((ptsInc++)?*?2,?time_base,?ost->st->time_base);
?pkt.dts?=?av_rescale_q_rnd(pkt.dts,?ost->st->time_base,?ost->st->time_base,?(AVRounding)(AV_ROUND_NEAR_INF?|?AV_ROUND_PASS_MINMAX));
?pkt.duration?=?av_rescale_q(pkt.duration,?ost->st->time_base,?ost->st->time_base);
音頻倍速
音頻倍速就需要用到filter進(jìn)行處理了,我這邊對(duì)音頻的處理是,先解碼音頻,然后用filter進(jìn)行倍速處理,對(duì)處理后的音頻數(shù)據(jù)進(jìn)行重采樣再編碼成AAC的音頻格式。
如果只是對(duì)音頻做處理,不做音視頻封裝,可以直接把filter處理后的數(shù)據(jù)存成PCM,即可驗(yàn)證音頻的倍速。
初始化filter的abuffer,aformat,abuffersink濾波器,abuffer用于接收輸入frame,形成待處理的數(shù)據(jù)緩存,abuffersink用于傳出輸出Frame,aformat過(guò)濾器約束最終的輸出格式(采樣率,聲道數(shù),存儲(chǔ)位數(shù)等),而中間的其他過(guò)濾器可以串聯(lián)多個(gè)filter,如volume(音量調(diào)節(jié)),atempo(變速)等。
int?init_atempo_filter(AVFilterGraph?**pGraph,?AVFilterContext?**src,?AVFilterContext?**out,?char?*value)
{
?//初始化AVFilterGraph
?AVFilterGraph?*graph?=?avfilter_graph_alloc();
?//獲取abuffer用于接收輸入端
?const?AVFilter?*abuffer?=?avfilter_get_by_name("abuffer");
?AVFilterContext?*abuffer_ctx?=?avfilter_graph_alloc_filter(graph,?abuffer,?"src");
?//設(shè)置參數(shù),這里需要匹配原始音頻采樣率8000、數(shù)據(jù)格式(位數(shù):16)、單聲道
?if?(avfilter_init_str(abuffer_ctx,?"sample_rate=8000:sample_fmt=s16:channel_layout=mono")?0)?{
??Logger::Error("error?init?abuffer?filter");
??return?-1;
?}
?//初始化atempo?filter
?const?AVFilter?*atempo?=?avfilter_get_by_name("atempo");
?AVFilterContext?*atempo_ctx?=?avfilter_graph_alloc_filter(graph,?atempo,?"atempo");
?//這里采用av_dict_set設(shè)置參數(shù)
?AVDictionary?*args?=?NULL;
?av_dict_set(&args,?"tempo",?value,?0);//這里傳入外部參數(shù),可以動(dòng)態(tài)修改
?if?(avfilter_init_dict(atempo_ctx,?&args)?0)?{
??Logger::Error("error?init?atempo?filter");
??return?-1;
?}
?const?AVFilter?*aformat?=?avfilter_get_by_name("aformat");
?AVFilterContext?*aformat_ctx?=?avfilter_graph_alloc_filter(graph,?aformat,?"aformat");
?if?(avfilter_init_str(aformat_ctx,?"sample_rates=8000:sample_fmts=s16:channel_layouts=mono")?0)?{
??Logger::Error("error?init?aformat?filter");
??return?-1;
?}
?//初始化sink用于輸出
?const?AVFilter?*sink?=?avfilter_get_by_name("abuffersink");
?AVFilterContext?*sink_ctx?=?avfilter_graph_alloc_filter(graph,?sink,?"sink");
?if?(avfilter_init_str(sink_ctx,?NULL)?0)?{//無(wú)需參數(shù)
??Logger::Error("error?init?sink?filter");
??return?-1;
?}
?//鏈接各個(gè)filter上下文
?if?(avfilter_link(abuffer_ctx,?0,?atempo_ctx,?0)?!=?0)?{
??Logger::Error("error?link?to?atempo?filter");
??return?-1;
?}
?if?(avfilter_link(atempo_ctx,?0,?aformat_ctx,?0)?!=?0)?{
??Logger::Error("error?link?to?aformat?filter");
??return?-1;
?}
?if?(avfilter_link(aformat_ctx,?0,?sink_ctx,?0)?!=?0)?{
??Logger::Error("error?link?to?sink?filter");
??return?-1;
?}
?if?(avfilter_graph_config(graph,?NULL)?0)?{
??Logger::Error("error?config?filter?graph");
??return?-1;
?}
?*pGraph?=?graph;
?*src?=?abuffer_ctx;
?*out?=?sink_ctx;
?Logger::Info("init?filter?success...");
?return?0;
}
注冊(cè)所有過(guò)濾器
????avfilter_register_all();
?if?(init_atempo_filter(&s_graph,?&in_ctx,?&out_ctx,?"1.0")?!=?0)?{
??Logger::Error("Codec?not?init?audio?filter");
??return?-1;
?}
使用過(guò)濾器 將解碼后的AVFrame通過(guò)av_buffersrc_add_frame(in_ctx, pFrameAudio)加入到輸入過(guò)濾器上下文in_ctx中,通過(guò)av_buffersink_get_frame(out_ctx, pFrameAudio)獲取處理完成的pFrameAudio。pFrameAudio就可以直接存成PCM或者再編碼封裝音視頻格式。
????//將pFrameAudio放入輸入filter上下文
?if?(av_buffersrc_add_frame(in_ctx,?pFrameAudio)?0)?{
??Logger::Error("error?add?frame");
?}
?//從輸出filter上下文中獲取pFrameAudio
?while?(av_buffersink_get_frame(out_ctx,?pFrameAudio)?>=?0)?{
??break;
?}
動(dòng)態(tài)修改倍速說(shuō)明
本文由于封裝音視頻倍速的時(shí)候,視頻倍速處理是沒有通過(guò)filter去做的,只是改變視頻幀封裝的時(shí)間戳達(dá)到倍速效果.如果最開始是以正常速度的倍速推流,過(guò)程中再去修改倍速參數(shù),就會(huì)導(dǎo)致要寫進(jìn)的視頻時(shí)間戳大于之前的時(shí)間戳,進(jìn)而出錯(cuò)。
所以本文講的ffmpeg的API控制音視頻倍速是沒辦法通過(guò)動(dòng)態(tài)調(diào)整倍速的。必須一開始就設(shè)置好倍速,后續(xù)就不能再改動(dòng)。如果單獨(dú)對(duì)音頻進(jìn)行動(dòng)態(tài)修改倍速,則是沒問(wèn)題的。
作者:RzzZ
來(lái)源:https://blog.csdn.net/qq_22633333/article/details/104967304

推薦閱讀:
音視頻開發(fā)工作經(jīng)驗(yàn)分享 || 視頻版
開通專輯 | 細(xì)數(shù)那些年寫過(guò)的技術(shù)文章專輯
Android NDK 免費(fèi)視頻在線學(xué)習(xí)?。?!
推薦幾個(gè)堪稱教科書級(jí)別的 Android 音視頻入門項(xiàng)目
覺得不錯(cuò),點(diǎn)個(gè)在看唄~

