一、前言
Hi,大家好,我是承香墨影!
開門見山,開篇名義。今天來聊聊如何將多段視頻,拼接成一個完整而連續的視頻,然后無縫進行播放。
這樣的需求應該不算偏門吧?
最簡單的就是一些視頻 App,會將大段的視頻切割成小段的視頻進行播放,還有一些在播放視頻之前,會插播一段廣告,這些需求都可以被本文的內容覆蓋到。
說到多個視頻拼接來說,如果你了解過 Google 出的 ExoPlayer 的話,它其內正好有一個 ConcatenatingMediaSource
可以來完成多個視頻源的拼接工作,並且 Api 很好用,基本上算是無縫拼接。
不過呢,ExoPlayer 是依賴 MediaCodec
的,除了 Api Level 的限制之外(Api Level 16+),它對設備硬件也是有要求的,在一些低端機上,你會碰到一些莫名其妙的卡頓、馬賽克、花屏等問題。而正是因為封裝的太好了,如果你想自行添加軟解,很遺憾,不太簡單。而 Github 上 extensions 中,對 ffmpeg 的支持,也只是僅限於 Audio,對 Video 沒有這個支持。
本文是基於另外一個很火的開源播放器:IJKPlayer,來看看如何去拼接視頻。
二、IJKPlayer Concat
IJKPlayer 是一個基於 FFmpeg 的輕量級 Android/iOS 視頻播放器,被 Bilibili 開源出來,算是國內很火的一款開源播放器了,很多 App 都在用。
因為背靠 FFmpeg,所以你在視頻編碼解碼上碰到的大部分問題,IJKPlayer 都可以幫你解決,是一款非常好用的播放器。
IJKPlayer 本身已經很好用了,你如果想播放多段視頻源的話,想要挨個的順序播放,在要求不高或者本身有轉場效果的前提條件下,也不是不可以。可如果是需要那種無縫的銜接,使用這種方式你會發現會有短暫的黑屏,因為加載新的視頻源需要經歷一小段時間,這種黑屏的現象在越差的設備上,越明顯。
對此,我這里推薦的解決方案,就是使用 FFmpeg 的 concat 協議。
2.1 什么是 concat
concat 是 FFmpeg 提供的一個虛級聯腳本分解器(以下簡稱 concat 協議),它是以一段有規則的腳本文件的形式存在的。可以使用 concat 定義一個視頻播放列表,FFmpeg 在播放的時候,會根據你定義的順序,一個接一個的解析進行播放,就好像他們本身就是一個視頻源一樣。
這么解釋可能有點不清晰,不過如果你了解 .m3u8
的格式,你對 concat 的理解應該就不難,它們都是定義了一個視頻列表,交由播放器的解碼器去順序播放。
具體的信息,可以去 FFmpeg 的官方文檔中,查閱對應的內容。
FFmpeg Doc:
2.2 concat 文件
想要使用 concat 協議,首先需要定義一個待解析的文件。它必須是以 .ffcat
或者 .ffconcat
后綴結尾,並且文件的內容頭,必須標記當前 concat 的版本號。
其內有兩個可配置的選項:
- file:用於指定一個待解析的視頻源,它可以指定一個本地的文件路徑,或者一個在線的 Uri,都是合法的。
- duration:標記前一個 file 指定的視頻源的長度,根據官方文檔的介紹,它是和 seek 相關的,當你調整進度的時候,它可以精准的定位到文件。不過它是一個輔助參數,如果你拼接的視頻碼率什么的參數都一致,是可以不需要它的,所以 duration 是一個非必須的參數。
下面舉個官方的例子來看看一個完整的 .ffcat
文件,應該是什么樣子的。
ffconcat version 1.0
# my first filename
file /mnt/share/file-1.wav
duration 20.0
# my second filename including whitespace
file '/mnt/share/file 2.wav'
# my third filename including whitespace plus single quote
file '/mnt/share/file 3'\''.wav'
2.3 IJKPlayer 對 concat 的 Options 配置
在使用 IJKPlayer 的時候,會有一些設置是通過 setOption()
方法進行設置的,如果需要支持 concat 協議,同時也需要有對應的設置。
這里主要關注兩點:
- protocol_whitelist : 協議白名單。
- safe:安全路徑。
為了讓 IJKPlayer 能支持 concat 協議,你需要將 concat 配置到它的白名單協議里,主要是為了添加 ffconcat 和 concat 兩個。
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "protocol_whitelist", "rtmp,concat,ffconcat,file,subfile,http,https,tls,rtp,tcp,udp,crypto");
而 safe 主要是為了指定允許一些不安全的路徑,默認值是 1 ,會拒絕一些不安全的文件路徑。當然,什么是安全路徑?你可以自行測試或者查閱文檔,這里直接將它設置為 0 ,就可以將其安全監測關掉。
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT,"safe",0);
到此,基本上就說清楚關鍵點了,直接將這個 .ffcat
文件,丟給 IJKPlayer 就可以正常的順序播放了。
concat 相關的內容非常的少,如果你在實際操作過程中,碰到問題,還是建議關注一下 Logcat 的輸出信息,看看出錯的原因。當然,通讀一遍 FFmpeg 的 concat 文檔,也是有幫助的。
三、Concat 查缺補漏
3.1 什么是安全路徑
前面提到的,concat 文件中,file
后面跟隨的文件路徑,必須是一個安全路徑,那什么是安全路徑?
根據 concat 的文檔描述,安全路徑必須是一個相對路徑,並且只不包含特殊符號,只包含(字母。數字、句點、下划線)等字符,並且路徑開始的時候,不包含句點“.”,則認為是一個安全路徑。
例如 :
file a.mp4
則認為是當前 .ffcat 文件所在目錄下的 a.mp4
文件,這是一個安全路徑。
相反的,例如 :"https://"、"file://"、"./" 這種視頻源路徑,均會視為不安全路徑。
3.2 ffcat 文件是否一定要在本地
FFmpeg 只是接受一個 concat 協議格式的數據流,具體它是在本地還是在遠端的服務器上,其實是不影響的。
3.3 file 是否可以混編
file 后面跟隨的視頻源的地址,concat 並不強制要求需要都在同一個地方。
ffconcat version 1.0
file /sdcard/a.mp4
file http://down4.xxx.com/hash/c644d9e118417e56d91cba3dc467ab9b.mp4
例如這樣一個 .ffcat
文件,它是合法可播放的。
3.4 視頻拼接的地方有黑屏閃動
concat 要求拼接的視頻必須具有相同的流(相同的碼率和時間基准等),所以如果前后兩個視頻源這些參數不一致,是可能導致閃一下黑屏的。
這個問題,我在非常差的電視盒子上做過測試,如果文件流保持一致,是可以做到無縫銜接。所以如果你也碰到這樣的情況,不要懷疑 FFmpeg 的 concat 的問題,重新用 FFmpeg 轉碼一下你的視頻文件再試試吧。
https://github.com/alwaystest/Blog/issues/58
https://www.jianshu.com/p/ea794a357b48
今天在公眾號后台回復成長『成長』,將會得到我整理的一些學習資料,也能回復『加群』,一起學習進步。
推薦閱讀:
- 漫畫:程序員,你能“管理”好你的產品經理嗎?
- 利用 Kotlin 的特性,優化 Intent 的數據傳遞!
- 解決 Lottie 動畫包含圖片的問題!
- Google 的 Flutter 學習資料!
- 遠程控制智能電視,方案已開源!