一、前言
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:
https://ffmpeg.org/ffmpeg-formats.html#concat
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
今天在公眾號後臺回覆成長『成長』,將會得到我整理的一些學習資料,也能回覆『加群』,一起學習進步。
推薦閱讀: