神器 ffmpeg —— 操作視頻,極度舒適

最近有了一個新任務,需要將賽事視頻,拆分成兩分鐘以內的小段,用於發佈到短視頻平臺上。

本以爲是個一次性的工作,結果賽事視頻數據巨大,視頻文件長短不一,完全沒法手工處理,於是 Python 又一次拯救了我。

還等什麼,開始幹吧!

最重要的事

無論做什麼事情,都要去分析一下最重要的是什麼,然後集中精力攻克,再繼續找最重要的事。

對我們這個任務來說,不算是個大項目,不過呢,還是要找最重要的事開始,步步爲營,最終將整個問題解決了。

整體來來看,我們需要從一個目錄中讀取視頻文件,然後,對每個視頻文件進行裁剪,最後將處理好的文件保存好。

在這個過程中,最重要的是什麼呢?我覺得,是視頻裁剪,如果不能方便的裁剪視頻,其他的一切工作都是白費的,是吧。

裁剪視頻

現在短視頻很流行,有很多視頻編輯軟件,功能豐富,而我們需要的只是裁剪功能,而且需要用編程的方式調用,那麼最合適的莫過於 ffmpeg[1] 了。

ffmpeg 是一個命令行工具,功能強大,可以編程調用。

從 ffmpeg 官網上下載對應操作系統的版本,我下的是 Windows 版 [2]。

下載後解壓到一個目錄,然後將目錄下的 bin,配置到環境變量裏。然後打開一個命令行,輸入:

> ffmpeg -version
ffmpeg version 2021-10-07-git-b6aeee2d8b-full_build- ...

測試一下,能顯示出版本信息,說明配置好了。

現在讀一下文檔,發現拆分視頻文件的命令是:

ffmpeg -i [filename] -ss [starttime] -t [length] -c copy [newfilename]

好了,用 Python 寫一個調用:

import subprocess as sp

def cut_video(filename, outfile, start, length=90):
    cmd = "ffmpeg -i %s -ss %d -t %d -c copy %s" % (filename, start, length, outfile)
    p = sp.Popen(cmd, shell=True)
    p.wait()
    return

這樣視頻裁剪工作就完成了,然後再看看什麼是最重要的。

計算分段

視頻裁剪時,需要一些參數,特別是開始時間,如何確定呢?如果這件事做不好,裁剪工作就很麻煩。

所以看看如何計算裁剪分段。

我需要將視頻裁剪成一分半的小段,那麼將需要知道目標視頻文件的時間長度。

獲取視頻長度

如何獲得長度呢?ffmpeg 提供了另一個命令 —— ffprobe

找了一下,可以合成一個命令來獲取:

> ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 -i a.flv

920.667

命令比較複雜哈,可以先不用管其他參數,只要將要分析的視頻文件傳入就好了。命令的結果是顯示一行視頻文件的長度。

於是可以編寫一個函數:

import subprocess as sp

def get_video_duration(filename):
    cmd = "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 -i %s" % filename
    p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE)
    p.wait()
    strout, strerr = p.communicate() # 去掉最後的回車
    ret = strout.decode("utf-8").split("\n")[0]
    return ret

分段

得到了視頻長度,確定好每個分段的長度,就可以計算出需要多少分段了。

代碼很簡單:

import math
duration = math.floor(float(get_video_duration(filename)))
part = math.ceil(duration / length)

注意,計算分段時,需要進行向上取整,即用 ceil,以包含最後的一點尾巴。

得到了需要的分段數,用一個循環就可以計算出每一段的起始時間了。

獲取文件

因爲處理的文件很多,所以需要自動獲取需要處理的文件。

方法很簡單,也很常用,一般可以用 os.walk 遞歸獲取文件,還可以自己寫,具體根據實際情況。

for fname in os.listdir(dir):
    fname = os.path.join(dir, os.path.join(dir, fname))
    basenames = os.path.basename(fname).split('.')
    mainname = basenames[0].split("_")[0]
    ...

提供視頻文件所在的目錄,通過 os.listdir 獲取目錄中的文件,然後,合成文件的絕對路徑,因爲調用裁剪命令時需要絕對路徑比較方便。

獲取文件名,是爲了在後續對裁剪好的文件進行命名。

代碼集成

現在每個部分都寫好了,可以將代碼集成起來了:

def main(dir):
    outdir = os.path.join(dir, "output")
    if not os.path.exists(outdir):
        os.mkdir(outdir)

    for fname in os.listdir(dir):
        fname = os.path.join(dir, os.path.join(dir, fname))
        if os.path.isfile(fname):
            split_video(fname, outdir)

總結

總體而言,這是個很簡單的應用,核心功能就是調用了一個 ffmpeg 命令。

相對於技術,更重要的是如何對一個項目進行分析和分解,以及從什麼地方開始。

這裏的方式起始時,不斷地找最重要地事情,以最重要的事情爲線索不斷地推進,最終以自下而上地方式解決整個問題。

期望這篇文章對你有所啓發,比心。

參考資料

[1]

ffmpeg: http://ffmpeg.org/

[2]

ffmpeg Window 版下載: https://www.gyan.dev/ffmpeg/builds/packages/ffmpeg-2021-10-14-git-c336c7a9d7-full_build.7z

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Ts3isK0qLU5xHfYhVOtBPg