一、需求
用戶針對(duì)一個(gè)PPT的每一頁圖片,進(jìn)行語音錄制,輸出多段音頻文件,將用戶每段音頻和對(duì)應(yīng)的PPT圖片拼接起來,最后輸出成一整段MP4視頻,作為教學(xué)視頻播放
二、方案選擇
針對(duì)需求,最開始提出了幾個(gè)主要的方案
方案 | 優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|---|
方案一:直播推流錄制 | 使用現(xiàn)成直播方案,上手成本小 | 業(yè)務(wù)邏輯要和直播業(yè)務(wù)切割隔離,重新弄一套,不合適,而且感覺殺雞用牛刀 |
方案二:客戶端處理圖片、音頻合成,視頻拼接等多媒體操作 | 1、后端業(yè)務(wù)簡單; 2、大多數(shù)視頻處理類APP都是如此,方案成熟 |
1、前端要新嵌入七牛多媒體處理SDK,對(duì)包穩(wěn)定性有影響 2、APP處理視頻,可能比較耗費(fèi)手機(jī)性能,如果APP受眾用戶是中老年用戶,可能手機(jī)性能扛不住 |
方案三:服務(wù)端統(tǒng)一處理圖片、音頻合成,視頻拼接等多媒體操作 | 1、客戶端無需再嵌入SDK 2、對(duì)用戶手機(jī)性能的要求降到最低 |
服務(wù)端交互邏輯變復(fù)雜,并且要處理耗時(shí)的多媒體合成任務(wù) |
最終定了方案三,原因是該功能的受眾是老年用戶,手機(jī)性能可能很差,耗時(shí)的操作交給服務(wù)端來比較合適
三、方案執(zhí)行
3.1 初版方案
查詢了一下,對(duì)應(yīng)圖片+音頻合成視頻,這樣的音畫合成的操作,七牛并沒有提供API~
所以只能服務(wù)端采用萬能的多媒體處理工具:ffmpeg 了,整體方案如下
可以看到上述方案,有兩個(gè)關(guān)鍵操作:
關(guān)鍵操作 | 描述 | 如何觸發(fā) |
---|---|---|
音畫合成 | 圖片+音頻合成視頻 | 客戶端接口觸發(fā),用戶每錄一段語音,則服務(wù)端立馬調(diào)異步任務(wù)進(jìn)行音畫合成 |
視頻mp4拼接 | 不同的視頻片段拼接成一整段視頻 | 客戶端接口觸發(fā),用戶點(diǎn)擊預(yù)覽或提交審核,服務(wù)端檢查所有語音片段是否音畫合成完畢,條件符合則進(jìn)行視頻mp4拼接 |
注意,七牛提供了視頻mp4拼接的接口,但是經(jīng)過實(shí)踐,用ffmpeg進(jìn)行本地視頻mp4拼接沒有任何問題,并且速度很快,所以這里所有操作都用 本地 ffmpeg 來進(jìn)行
ffmpeg 不具體介紹,詳情可自行g(shù)oogle:
官網(wǎng):https://ffmpeg.org/
參數(shù)詳解:https://zhuanlan.zhihu.com/p/31674583
具體ffmpeg的命令執(zhí)行操作,第一版的執(zhí)行如下:
關(guān)鍵操作 | 描述 | ffmpeg操作和參考 |
---|---|---|
音畫合成 | 圖片+音頻合成視頻 |
ffmpeg -i 1976.aac -i mulan.jpg -acodec aac -strict -2 -vcodec libx264 -ar 22050 -ab 128k -ac 2 -pix_fmt yuvj420p -y conf_liutao_test1.mp4 參考來源:https://blog.51cto.com/cjxkaka/1569109 |
視頻mp4拼接 | 不同的視頻片段拼接成一整段視頻 | 如下 |
$ cat mylist.txt
file '/path/to/file1'
file '/path/to/file2'
file '/path/to/file3'
$ ffmpeg -f concat
-i mylist.txt
-c copy output
不同的視頻片段拼接成一整段視頻
參考來源:我是Stack Overflow鏈接
參考上面 Stack Overflow回答中”Jack Miller“ 的回答
3.2 遇到的問題和優(yōu)化
問題1. 音畫合成的視頻,在有些瀏覽器中無法拖動(dòng)進(jìn)度條
咨詢了人森導(dǎo)師手哥,他給我介紹了一個(gè)工具:mediainfo,該工具可以查看視頻詳情,如音軌(Audio)和畫面(Video)的時(shí)長,通過該工具可以看到通過第一版操作音畫合成的視頻,畫面時(shí)長只有40ms,然而音軌時(shí)長卻有7s,這里存在嚴(yán)重的不同步,因此在有些瀏覽器(safari)中并不能正常拖動(dòng)進(jìn)度條播放:
問題1的解決辦法
參考:Combine one image + one audio file to make one video using FFmpeg
中"community wiki"的回答,使用如下ffmpeg命令可以正常生成Video_Duration和Audio_Duration接近的視頻
ffmpeg -loop 1 -i xuanwu.jpg
-i 1.aac
-c:v libx264 -tune stillimage
-c:a aac -b:a 192k -pix_fmt yuvj420p
-shortest liutao_test_2.mp4
問題2:將不同的音畫合成后的視頻片段拼接起來后生成的 最終課程錄制視頻,會(huì)有音畫不同步的問題
現(xiàn)象是明明是第一個(gè)PPT的錄音,畫面已經(jīng)翻到PPT第二頁了,錄音還在播放第一頁P(yáng)PT尾段的錄制語音
原因:通過 mediainfo 查看最后生成的 最終拼接視頻,發(fā)現(xiàn)還是存在 Video_Duration和Audio_Duration 不一致的問題
應(yīng)該是第一步音畫合成的視頻片段本身就有 Video_Duration和Audio_Duration 不完全一致,將他們拼接起來后,是音軌和畫面軌道分別拼接,最后兩條軸出現(xiàn)了不一致的問題。
因此,我們需要在第一步音畫合成的時(shí)候做處理,讓 Video_Duration和Audio_Duration 保持嚴(yán)格一致或盡量接近
問題2的解決辦法
在音畫合成后,多一步操作,對(duì)合成的視頻片段,進(jìn)行人為剪裁~讓視頻的 Video_Duration和Audio_Duration 保持一致:
ffmpeg -i input.mp4
-ss 00:00:00
-t 00:00:11.72
-acodec aac -vcodec h264
-strict -2 cut_output.mp4
如此生成的視頻 Video_Duration和Audio_Duration 不會(huì)有太大差距。
問題3:安卓端的播放器,播放合成的課程視頻,依然無法拖動(dòng)視頻的進(jìn)度條
和安卓端同學(xué)溝通后,定位問題是視頻缺少關(guān)鍵幀,需要為視頻加入關(guān)鍵幀
問題3的解決辦法
參考:https://codeday.me/bug/20180927/259812.html
在音畫合成截?cái)啵歪槍?duì)視頻插入關(guān)鍵幀,關(guān)鍵命令:
ffmpeg -x264-params keyint=1:scenecut=0
上面的keyint=1表示每隔1幀插入設(shè)置一個(gè)關(guān)鍵幀
問題4:音畫合成的速度特別慢,音畫合成生成的文件也特別的大
首先觀察現(xiàn)象,發(fā)現(xiàn) 圖片大小為 212k,音頻 .aac 文件大小為 132k,生成的視頻文件居然會(huì)是540k
懷疑是幀率問題,google了一下,ffmpeg指令如果不人為設(shè)定幀率,默認(rèn)幀率為25,而我們音畫合成的視頻就是一張圖片,并不需要太高的幀率,這個(gè)地方應(yīng)該可以優(yōu)化下
問題4的解決辦法
參考:https://zhuanlan.zhihu.com/p/31674583
經(jīng)過人為設(shè)置幀率為1,生成文件大小優(yōu)化為356k
人為設(shè)置幀率為1的關(guān)鍵指令如下:
ffmpeg -r 1
同時(shí),寫了個(gè)小腳本,做了下實(shí)驗(yàn)驗(yàn)證,人為設(shè)置幀率,也大大降低了處理速度:
實(shí)驗(yàn):對(duì)比使用 -r 2 設(shè)置幀率(fps) 來對(duì)靜態(tài)圖的mp4處理速度和大小進(jìn)行優(yōu)化
第一組:幀率使用默認(rèn)值為25的處理:
Array
(
[command] => ffmpeg -loop 1 -i mulan.jpg -i 1_min.aac -c:v libx264 -c:a aac -b:a 64k -pix_fmt yuvj420p -shortest liutao_test_1min_64k.mp4
[spend] => 46401.793956757ms
)
第二組:幀率認(rèn)為設(shè)定為2的處理(使用 命令參數(shù) -r 2 認(rèn)為指定幀率為2):
Array
(
[command] => ffmpeg -loop 1 -i mulan.jpg -i 1_min.aac -r 2 -c:v libx264 -c:a aac -b:a 64k -pix_fmt yuvj420p -shortest liutao_test_1min_64k_r2.mp4
[spend] => 21741.201877594ms
)
生成文件大小的對(duì)比
[med@qa liutao]$ du -ak liutao_test_1min_64k.mp4 liutao_test_1min_64k_r2.mp4
1404 liutao_test_1min_64k.mp4
548 liutao_test_1min_64k_r2.mp4
從上面的實(shí)驗(yàn)看起來,針對(duì)1分鐘的音頻,人為設(shè)置幀率為2使得處理耗時(shí)降低了至少50%,生成文件大小降低了近60%
問題5:音畫合成后的視頻,截?cái)嗪笥謥G失了關(guān)鍵幀
音畫合成后的視頻,是帶有關(guān)鍵幀信息的,為何截?cái)嗪笥謥G失了關(guān)鍵幀?
經(jīng)過仔細(xì)對(duì)比,發(fā)現(xiàn)音畫合成和截?cái)嗟拿睿兄?xì)微差距
1,音畫合成:
ffmpeg -loop 1
-i mulan.jpg
-i 2191.aac
-r 1
-c:v libx264 -x264-params keyint=1:scenecut=0
-c:a aac
-b:a 32k -pix_fmt yuvj420p
-shortest
liutao_test_2191_mulan_r1_key1.mp4
2,截?cái)啵?ffmpeg -i output1.mp4
-ss 00:00:00
-t 00:00:06.80
-acodec aac
-vcodec h264
-strict -2 output1_cut.mp4
仔細(xì)觀察上面兩個(gè)命令,經(jīng)過google,發(fā)現(xiàn) 【-c:a】和【-acodec】是一個(gè)意思,表示音頻編碼方式,【-c:v】和【-vcodec】是一個(gè)意思,表示視頻編碼方式
這里兩個(gè)指令的 視頻編碼方式,一個(gè)指定的使用 libx264,一個(gè)使用h264, 懷疑是這里的不一致導(dǎo)致關(guān)鍵幀丟失
經(jīng)過試驗(yàn),發(fā)現(xiàn)猜測正確。
問題5的解決辦法:
將音畫合成和視頻截?cái)嗟囊纛l解碼方式統(tǒng)一為 libx264,就能保證截?cái)嗪笠曨l的關(guān)鍵幀不丟失:
1,音畫合成:
ffmpeg -loop 1
-i mulan.jpg
-i 2191.aac
-r 1
-c:v libx264 -x264-params keyint=1:scenecut=0
-c:a aac
-b:a 32k -pix_fmt yuvj420p
-shortest
liutao_test_2191_mulan_r1_key1.mp4
2,截?cái)啵?ffmpeg -i output1.mp4
-ss 00:00:00
-t 00:00:06.80
-acodec aac
-vcodec libx264 -x264-params keyint=1:scenecut=0
-strict -2 output1_cut.mp4
3.3 最終的視頻處理命令
三個(gè)步驟:
- 音畫合成,圖片+音頻合成視頻
ffmpeg -loop 1
-i mulan.jpg
-i 2191.aac
-r 1
-c:v libx264 -x264-params keyint=1:scenecut=0
-c:a aac
-b:a 32k
-pix_fmt yuvj420p
-shortest liutao_test_2191_mulan_r1_key1.mp4
該指令人為設(shè)置合成幀率為1,降低處理耗時(shí)和生成文件大小,
人為設(shè)置關(guān)鍵幀間隔為每間隔1幀設(shè)置一個(gè),解決安卓RN播放無法拉動(dòng)進(jìn)度條的問題
- 對(duì)音畫合成后的視頻片段進(jìn)行截?cái)?/li>
ffmpeg
-ss 00:00:00
-t 00:00:20.096
-accurate_seek
-i liutao_test_pre_2191.mp4
-acodec aac
-vcodec libx264 -x264-params keyint=1:scenecut=0
-strict -2
liutao_test_final_2191.mp4
參考:我是CSDN博客鏈接
截?cái)嗍菫榱吮WC音軌長度和畫面軌道長度
盡量保持一致,杜絕拼接后的音畫不同步問題
- 視頻mp4拼接,不同的視頻片段拼接成一整段視頻
$ cat mylist.txt
file '/path/to/file1'
file '/path/to/file2'
file '/path/to/file3'
$ ffmpeg -f concat
-i mylist.txt
-c copy output
參考來源:我是Stack Overflow鏈接
參考上面 Stack Overflow回答中”Jack Miller“ 的回答