多媒體開發(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:
record的workflows

script的設置如下:
workflow的設置

找到這個workflow的目錄,增加一個cmd.sh文件,這個文件用來初始化python環境,並執行上面的mian.py文件:
cmd.sh

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.

Tags: