多媒體開發(2):錄製影片
上一節介紹了用ffplay來播放文件(或url),這裡有一個概念,如果是播放已經存在的文件,那叫「回放」,也就是Playback(從流媒體的角度也叫點播),如果播放的是正在錄製的數據(邊錄邊播),那叫直播。
不管是回放還是直播,都需要有媒體數據,那這個媒體數據是怎麼來的呢?從已有的文件編輯而來是一個辦法,但更直接更原始的辦法是錄製。
錄製,就是通過硬體設備,把聲音或者影像保存到文件(或者推到文件)。
在FFmpeg程式集中,有一個程式叫作ffmpeg(小寫),這個程式提供了錄製的功能。在上一節小程介紹了ffplay的安裝,而實際上ffplay依賴於FFmpeg,所以當ffplay安裝後,那FFmpeg程式集也就安裝上了,也就是已經可以使用ffmpeg程式。
本文介紹如何通過ffmpeg程式來實現聲音、影像以及螢幕的錄製。
(一)錄製命令
對於影像,可以通過攝影機或者螢幕來錄入,而對於聲音則通過麥克風來錄入。
因為我使用的是mac電腦,所以,有必要先查看一下mac電腦有沒有錄製的設備,也就是輸入設備。
因為查看輸入設備需要指定輸入設備的格式類型,所以,先查看輸入設備的格式類型:
ffmpeg -devices
在我的電腦上,有這樣的輸出:
avfoundation AVFoundation input device
lavfi Libavfilter virtual input device
qtkit QTKit input device
第一個類型,即avfoundation,為本機輸入設備的格式類型,有了這個類型,就可以進一步查看輸入設備了:
ffmpeg -list_devices true -f avfoundation -i “”
可以看到這樣的資訊:
[AVFoundation input device @ 0x7f97326002e0] AVFoundation video devices:
[AVFoundation input device @ 0x7f97326002e0] [0] FaceTime HD Camera
[AVFoundation input device @ 0x7f97326002e0] [1] Capture screen 0
[AVFoundation input device @ 0x7f97326002e0] AVFoundation audio devices:
[AVFoundation input device @ 0x7f97326002e0] [0] Built-in Microphone
上面的資訊可以知道,影片輸入(video devices)有兩個設備,設備0為攝影機,設備1為螢幕,而聲音輸入(audio devices)則只有設備0,也就是麥克風。
在知道設備號之後,就可以啟用這個設備來錄製,比如只打開攝影機進行錄製(按’q’結束錄製):
ffmpeg -f avfoundation -framerate 30 -i “0” -s 320×240 -vsync 2 -y cam.mp4
framerate是錄製幀率,也就是一秒取多少幀,這些概念以後小程再詳細介紹。-i “0”就是0號設備即攝影機,-s指定解析度,比如可以設置一個在手機上較佳的使用解析度。最後面的文件名,表示保存數據的文件。
但需要注意,參數並非可以隨意改動的,因為參數的值跟輸入設備有關,比如我的mac只支援幀率30,而且解析度也只有幾個可以選擇,所以,參數不能亂寫。如果出錯了,那應該根據出錯資訊作出修正。而且,參數的順序也不能亂寫,否則是可能出錯的!
另外,不同的ffmpeg的版本,對參數的要求也不一樣,比如ffmpeg3.x需要指定framerate跟解析度,而ffmpeg2.x則不需要,但出錯後都有相應的資訊提醒,你可以按指引來修正。
如果想同時錄製到聲音,那就同時打開攝影機跟麥克風,進行錄製:
ffmpeg -f avfoundation -framerate 30 -i “0:0” -s 320×240 -vsync 2 -y cam2.mp4
0:0,前面為影像設備,後面為聲音設備。
如果只錄製聲音,則:
ffmpeg -f avfoundation -i ":0" sound.aac
或者:
ffmpeg -f avfoundation -i ":0" -acodec libmp3lame sound2.mp3
aac是音頻的一種編碼格式,我用的FFmpeg3.1.2會編碼成aac格式(FFmpeg3.x內置aac編碼),當然也可以指定編碼成mp3,但要藉助libmp3lame這個組件。你可以先忽略這些編碼細節,只需要知道有這樣一個功能就好。
我在這裡給出一個只用攝影機錄製的效果,那是小程家裡的燈,通過mac電腦錄製到的影片:
如果只想錄製螢幕,那隻需要把-i “0”換成-i “1”,也就是指定從螢幕錄製。
現在錄製的命令有了,但是,作為一個追求便利的程式設計師,怎麼才能快速而方便地進行啟動錄製呢?分明,我要再次使用之前介紹過的alfred跟python介面了。
(二)快速啟動錄製
先來寫一個Python的介面,用來設置錄製的各個參數,程式碼如下:
from tkinter import *
import tkinter.messagebox as msgbox
import os
import threading
audiovalue = 0
widthvalue = ""
heightvalue = ""
recordsourcevalue = ""
def recordThreadFunc():
global audiovalue, widthvalue, heightvalue, recordsourcevalue
parms = recordsourcevalue.get()
if (audiovalue.get()==1):
parms = parms + ":0"
recordtemppath = "'%s'/recordtemp.mp4" % os.getcwd()
recordpath = "'%s'/record.mp4" % os.getcwd()
ffstr = "ffmpeg -f avfoundation"
if (recordsourcevalue.get()=="0"):
ffstr = ffstr + " -framerate 30"
ffstr = ffstr + " -i '%s' -s %sx%s -vsync 2 -y %s" % (parms, widthvalue.get(), heightvalue.get(), recordtemppath)
print(ffstr)
os.system("touch tempsh.sh")
with open("tempsh.sh", "w") as f:
f.write(ffstr)
f.write("\nffmpeg -i %s -pix_fmt yuv420p -y %s" % (recordtemppath, recordpath))
f.write("\nrm %s" % recordtemppath)
os.system("chmod +x tempsh.sh")
os.system("open -a Terminal.app tempsh.sh")
print("%d, %s, %s, %s" % (audiovalue.get(), widthvalue.get(), heightvalue.get(), recordsourcevalue.get()))
os.system("open '%s'" % os.getcwd())
pass
def recordFunc():
t1 = threading.Thread(target=recordThreadFunc)
t1.start()
def main():
global audiovalue, widthvalue, heightvalue, recordsourcevalue
window = Tk()
window.title("錄製控制台")
window.geometry("320x320+100+100")
audiovalue = IntVar()
audioCheckBtn = Checkbutton(window, text="錄入聲音", variable=audiovalue, onvalue=1, offvalue=0)
audioCheckBtn.place(x=10, y=10)
label1 = Label(window, text="影片解析度:")
label1.place(x=10, y=50)
widthlabel = Label(window, text="寬:")
heightlabel = Label(window, text="高:")
widthvalue = StringVar()
heightvalue = StringVar()
widthEntry = Entry(window, textvariable=widthvalue, width=10)
heightEntry = Entry(window, textvariable=heightvalue, width=10)
widthlabel.place(x=20, y=70)
widthEntry.place(x=50, y=70)
heightlabel.place(x=20, y=100)
heightEntry.place(x=50, y=100)
tiplabel = Label(window, text="如:320/1920/1280等")
tiplabel.place(x=140, y=70)
tiplabel = Label(window, text="如:240/1080/720等")
tiplabel.place(x=140, y=100)
recordsourcevalue = StringVar()
rsourceRadioBtn1 = Radiobutton(window, text="從攝影機錄製", value="0", variable=recordsourcevalue)
rsourceRadioBtn2 = Radiobutton(window, text="從螢幕錄製", value="1", variable=recordsourcevalue)
rsourceRadioBtn1.place(x=20, y=170)
rsourceRadioBtn2.place(x=140, y=170)
tiplabel = Label(window, text="從哪裡錄製:")
tiplabel.place(x=10, y=150)
recordBtn = Button(window, text="開啟錄製", command=recordFunc)
recordBtn.place(x=20, y=200)
window.mainloop()
if __name__ == '__main__':
main()
程式碼所在的文件叫main.py,運行這段程式碼可以看到這樣的介面:
在介面上設置好參數,點擊「開啟錄製」就可能看到你的攝影機打開了,但是,每次都要找到這個python腳本來執行是不完美的,所以,alfred出場了。
同樣,創建一個script filter:
script的設置如下:
找到這個workflow的目錄,增加一個cmd.sh文件,這個文件用來初始化python環境,並執行上面的mian.py文件:
cmd.sh的內容如下:
cd '/Users/freejet/Library/Application Support/Alfred/Alfred.alfredpreferences/workflows/user.workflow.0490D960-6C02-4F29-8265-51890350F2B4'
source ~/Desktop/pyenv/bin/activate
python3 main.py
再把上面的main.py拷貝到這個目錄,大功告成,用alfred來打下「record」,就可以來錄製了,效果如下:
gif看不太清楚?沒法子,影片轉過來又要兼容大小,是這樣的啦,這裡又不能上傳影片。
好了,總結一下,本文主要介紹通過ffmpeg來錄製聲音或者影片。錄製往往只是某件事情的一個環節,比如:在拿影片測試樣本時可以自己錄製、在直播時需要錄製、在截屏時可以用錄屏的方式來實現,等等。所以,理解這個環節的一個實現是有價值的。有緣再見,see you.