神器 ffmpeg :操作視頻,極度舒適
最近有了一個新任務,需要將賽事視頻,拆分成兩分鐘以內的小段,用于發(fā)布到短視頻平臺上。
本以為是個一次性的工作,結果賽事視頻數據巨大,視頻文件長短不一,完全沒法手工處理,于是 Python 又一次拯救了我。
還等什么,開始干吧!
最重要的事
無論做什么事情,都要去分析一下最重要的是什么,然后集中精力攻克,再繼續(xù)找最重要的事。
對我們這個任務來說,不算是個大項目,不過呢,還是要找最重要的事開始,步步為營,最終將整個問題解決了。
整體來來看,我們需要從一個目錄中讀取視頻文件,然后,對每個視頻文件進行裁剪,最后將處理好的文件保存好。
在這個過程中,最重要的是什么呢?我覺得,是視頻裁剪,如果不能方便的裁剪視頻,其他的一切工作都是白費的,是吧。
裁剪視頻
現在短視頻很流行,有很多視頻編輯軟件,功能豐富,而我們需要的只是裁剪功能,而且需要用編程的方式調用,那么最合適的莫過于 ffmpeg[1] 了。
ffmpeg 是一個命令行工具,功能強大,可以編程調用。
從 ffmpeg 官網上下載對應操作系統(tǒng)的版本,我下的是 Windows 版[2]。
下載后解壓到一個目錄,然后將目錄下的 bin,配置到環(huán)境變量里。然后打開一個命令行,輸入:
- > ffmpeg -version
- ffmpeg version 2021-10-07-git-b6aeee2d8b-full_build- ...
測試一下,能顯示出版本信息,說明配置好了。
現在讀一下文檔,發(fā)現拆分視頻文件的命令是:
- ffmpeg -i [filename] -ss [starttime] -t [length] -c copy [newfilename]
- i 為需要裁剪的文件
- ss 為裁剪開始時間
- t 為裁剪結束時間或者長度
- c 為裁剪好的文件存放
好了,用 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 需要的信息
- 將裁剪命令寫成一個字符串模板,將參數替換到其中
- 用 subprocess 的 Popen 執(zhí)行命令,其中參數 shell=True 表示將命令作為一個整體執(zhí)行
- p.wait() 很重要,因為裁剪需要一會兒,而且是另起進程執(zhí)行的,所以需要等執(zhí)行完成再做后續(xù)工作,否則可能找不到裁剪好的文件
這樣視頻裁剪工作就完成了,然后再看看什么是最重要的。
計算分段
視頻裁剪時,需要一些參數,特別是開始時間,如何確定呢?如果這件事做不好,裁剪工作就很麻煩。
所以看看如何計算裁剪分段。
我需要將視頻裁剪成一分半的小段,那么將需要知道目標視頻文件的時間長度。
獲取視頻長度
如何獲得長度呢?ffmpeg 提供了另一個命令 —— ffprobe。
找了一下,可以合成一個命令來獲取:
- > ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=11: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
- 函數只有一個參數,就是視頻文件路徑
- 合成命令語句,將視頻文件路徑替換進去
- 用 subprocess 來執(zhí)行,注意這里需要設置一下命令執(zhí)行后的輸出
- 用 wait 等待命令執(zhí)行完成
- 通過 communicate 提取輸出結果
- 從結果中提取視頻文件的長度,返回
分段
得到了視頻長度,確定好每個分段的長度,就可以計算出需要多少分段了。
代碼很簡單:
- import math
- duration = math.floor(float(get_video_duration(filename)))
- part = math.ceil(duration / length)
注意,計算分段時,需要進行向上取整,即用 ceil,以包含最后的一點尾巴。
得到了需要的分段數,用一個循環(huán)就可以計算出每一段的起始時間了。
獲取文件
因為處理的文件很多,所以需要自動獲取需要處理的文件。
方法很簡單,也很常用,一般可以用 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 獲取目錄中的文件,然后,合成文件的絕對路徑,因為調用裁剪命令時需要絕對路徑比較方便。
獲取文件名,是為了在后續(xù)對裁剪好的文件進行命名。
代碼集成
現在每個部分都寫好了,可以將代碼集成起來了:
- 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)
- main 方法是集成后的方法
- 先創(chuàng)建一個裁剪好的存儲目錄,放在視頻文件目錄中的 output 目錄里
- 通過 listdir 獲取到文件后,對每個文件進行處理,其中判斷了一下是否為文件
- 調用 split_video 方法開始對一個視頻文件進行裁剪
總結
總體而言,這是個很簡單的應用,核心功能就是調用了一個 ffmpeg 命令。
相對于技術,更重要的是如何對一個項目進行分析和分解,以及從什么地方開始。
這里的方式起始時,不斷地找最重要地事情,以最重要的事情為線索不斷地推進,最終以自下而上地方式解決整個問題。
期望這篇文章對你有所啟發(fā),比心。