ffmpeg使用總結

下載安裝

下載地址 輸入圖片說明html

提供了可執行文件和動態庫,純命令行使用Architecture版本。python

查看當前dshow可用的錄像設備和錄音設備

使用以下命令能夠查看設備列表

ffmpeg -list_devices true -f dshow -i dummy

能夠從輸出中提取到設備名,進行視頻錄製時會使用到這個設備名shell

[dshow @ 0000023a33b3a680] DirectShow video devices (some may be both video and audio devices)
[dshow @ 0000023a33b3a680]  "USB 2.0 Webcam Device"
[dshow @ 0000023a33b3a680]     Alternative name "@device_pnp_\\?xxxx}\global"
[dshow @ 0000023a33b3a680]  "screen-capture-recorder"
[dshow @ 0000023a33b3a680]     Alternative name "@device_sw_{xxx}"
[dshow @ 0000023a33b3a680] DirectShow audio devices
[dshow @ 0000023a33b3a680]  "麥克風 (Realtek High Definition Audio)"
[dshow @ 0000023a33b3a680]     Alternative name "@device_cm_{xxx}"
[dshow @ 0000023a33b3a680]  "virtual-audio-capturer"
[dshow @ 0000023a33b3a680]     Alternative name "@device_sw_{xxxx}"

其中的 "screen-capture-recorder"和"virtual-audio-capturer"兩個是虛擬設備,用來輔助錄製屏幕和聲卡, (下載地址)[https://sourceforge.net/projects/screencapturer/files/]。編程

查看單個設備的可用配置能夠用以下命令

ffmpeg -list_options true -f dshow -i video="{錄像設備名}"

能夠從輸出中獲得攝像頭的可用分辨率app

[dshow @ 000001d59c14a680] DirectShow video device options (from video devices)
[dshow @ 000001d59c14a680]  Pin "捕獲" (alternative pin name "0")
[dshow @ 000001d59c14a680]   pixel_format=yuyv422  min s=640x480 fps=5 max s=640x480 fps=30
[dshow @ 000001d59c14a680]   pixel_format=yuyv422  min s=640x480 fps=5 max s=640x480 fps=30
...
[dshow @ 000001d59c14a680]   pixel_format=yuyv422  min s=1280x720 fps=5 max s=1280x720 fps=10
[dshow @ 000001d59c14a680]   vcodec=mjpeg  min s=640x480 fps=5 max s=640x480 fps=30
[dshow @ 000001d59c14a680]   vcodec=mjpeg  min s=640x480 fps=5 max s=640x480 fps=30
...
[dshow @ 000001d59c14a680]   vcodec=mjpeg  min s=1280x720 fps=5 max s=1280x720 fps=30
2018-03-19 13:39:48,178 - video=USB 2.0 Webcam Device: Immediate exit requested

獲取桌面信息

ffmpeg -list_options true -f gdigrab -i desktop

能夠從輸出中提取出桌面分辨率ide

[gdigrab @ 000001f1b84da680] Capturing whole desktop as 1920x1080x32 at (0,0)

python代碼提取設備列表和配置信息

本身封裝的一個函數來獲取設備可用分辨率列表函數式編程

__author__ = "Dalton Xiong"
__license__ = "GPL"
__version__ = "0.1"
__email__ = "daltonxiong@gmail.com"
'''
使用ffmpeg讀取錄像設備列表及其支持的分辨率
'''


import subprocess
import re
import logging
from collections import namedtuple

import parse

logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(message)s')

# 讀取設備列表, 忽略ffmpeg的錄屏和錄聲卡的插件程序
def device_list():
    cmd = 'ffmpeg -list_devices true -f dshow -i dummy'
    logging.info('讀取設備列表>>>>>>')
    logging.info(cmd)

    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    output = process.stdout.read()
    lines = str(output, encoding='utf-8').split('\n')

    video_device_list = []
    audio_device_list = []
    video_device_name_flag = False
    audio_device_name_flag = False
    for l in lines:
        logging.info(l)
        l = l.strip()
        if '[dshow' in l:
            if 'DirectShow video devices' in l:
                video_device_name_flag = True
                audio_device_name_flag = False
            elif 'DirectShow audio devices' in l:
                video_device_name_flag = False
                audio_device_name_flag = True
            elif video_device_name_flag and 'Alternative name' not in l:
                video_device_name = re.search(r'\"(.*)\"', l) or None
                video_device_name = video_device_name and video_device_name.groups()[0]
                if video_device_name != 'screen-capture-recorder': # 忽略錄屏程序
                    video_device_list.append(video_device_name)
            elif audio_device_name_flag and 'Alternative name' not in l:
                audio_device_name = re.search(r'\"(.*)\"', l) or None
                audio_device_name = audio_device_name and audio_device_name.groups()[0]
                if audio_device_name != 'virtual-audio-capturer': # 忽略聲卡錄製程序
                    audio_device_list.append(audio_device_name)

    return video_device_list, audio_device_list

# 影像採集設備信息
def video_device_info(device_name):
    cmd = 'ffmpeg -list_options true -f dshow -i video="{}"'.format(device_name)
    logging.info('讀取影像採集設備({})的信息>>>>>>'.format(device_name))
    logging.info(cmd)

    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    output = process.stdout.read()
    lines = str(output, encoding='utf-8').split('\n')

    pixel_format_set = set()
    vcodec_set = set()
    for l in lines:
        logging.info(l)
        l = l.strip()
        if '[dshow' not in l:
            continue

        if 'pixel_format' in l:
            result = parse.parse('{}   pixel_format={}  min s={} fps={} max s={}x{} fps={}', l)
            if result:
                pixel_format_set.add( (result[1], result[4], result[5], result[6]) )
        elif 'vcodec' in l:
            result = parse.parse('{}   vcodec={}  min s={} fps={} max s={}x{} fps={}', l)
            if result:
                vcodec_set.add( (result[1], result[4], result[5], result[6]) )
        else:
            continue

    return pixel_format_set, vcodec_set

# 聲音採集設備信息
def audio_device_info(device_name):
    cmd = 'ffmpeg -list_options true -f dshow -i audio="{}"'.format(device_name)
    logging.info('讀取聲音採集設備({})的信息>>>>>>'.format(device_name))
    logging.info(cmd)

    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    output = process.stdout.read()
    lines = str(output, encoding='utf-8').split('\n')

    format_set = set()
    for l in lines:
        logging.info(l)
        l = l.strip()
        if '[dshow' not in l:
            continue

        result = parse.parse('{}   min ch={} bits={} rate= {} max ch={} bits={} rate= {}', l)
        if result:
            format_set.add((result[4], result[5], result[6]))

    return format_set

# 桌面分辨率大小
def desktop_device_info():
    cmd = 'ffmpeg -list_options true -f gdigrab -i desktop'
    logging.info('讀取桌面影像信息>>>>>>')
    logging.info(cmd)

    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    output = process.stdout.read()
    lines = str(output, encoding='utf-8').split('\n')

    format_set = set()
    for l in lines:
        logging.info(l)
        l = l.strip()

        if '[gdigrab' not in l:
            continue

        result = parse.parse('{} Capturing whole desktop as {}x{}x{} at (0,0)', l)
        if result:
            format_set.add( (result[1], result[2]) )

    return format_set

# 獲取全部設備列表和可用配置信息
def device_config_list():
    video_device_list, audio_device_list = device_list()

    # 錄像設備
    VideoDeviceInfo = namedtuple('VideoDeviceInfo', ['width', 'height', 'fps'])
    vdevice_list = []
    for device_name in video_device_list:
        pixel_format_set, vcodec_set = video_device_info(device_name)
        format_set = vcodec_set or pixel_format_set
        format_list = set()
        for (format, width, height, fps) in format_set:
            format_list.add( VideoDeviceInfo(width=int(width), height=int(height), fps=int(fps)) )

        format_list = list(format_list)
        format_list.sort(key=lambda x: (x.fps, x.width), reverse=True)

        device_info = {'name': device_name, 'format_list': format_list}
        vdevice_list.append(device_info)

    # 錄音設備
    AudioDeviceInfo = namedtuple('AudioDeviceInfo', ['channel', 'bits', 'rate'])
    adevice_list = []
    for device_name in audio_device_list:
        format_set = audio_device_info(device_name)
        format_list = set()
        for (channel, bits, rate) in format_set:
            format_list.add( AudioDeviceInfo(channel=int(channel), bits=int(bits), rate=int(rate)) )

        format_list = list(format_list)
        format_list.sort(key=lambda x: (x.channel, x.bits), reverse=True)

        device_info = {'name': device_name, 'format_list': format_list}
        adevice_list.append(device_info)

    # 計算機桌面
    desktop_format_set =  desktop_device_info()
    if len(desktop_format_set) != 1:
        raise ValueError('屏幕分辨率讀取失敗')

    desktop_format_set = list(desktop_format_set)[0]
    DesktopDeviceInfo = namedtuple('DesktopDeviceInfo', ['width', 'height'])
    desktop_info = DesktopDeviceInfo(width=int(desktop_format_set[0]), height=int(desktop_format_set[1]))

    logging.info('錄像設備列表和可用配置以下:')
    logging.info('{}'.format(vdevice_list))

    logging.info('錄音設備列表和可用配置以下:')
    logging.info('{}'.format(adevice_list))

    logging.info('桌面分辨率爲:{}x{}'.format(desktop_info.width, desktop_info.height))

    return vdevice_list,adevice_list,desktop_info

if __name__ == '__main__':
    vdevice_list, adevice_list, desktop_info = device_config_list()

封裝了一個函數device_config_list返回了, 錄像設備列表,錄音設備列表, 桌面分辨率函數

錄像設備列表和可用配置以下:
[{'name': 'USB 2.0 Webcam Device', 'format_list': [VideoDeviceInfo(width=1280, height=720, fps=30), VideoDeviceInfo(width=960, height=540, fps=30), VideoDeviceInfo(width=848, height=480, fps=30), VideoDeviceInfo(width=640, height=360, fps=30), VideoDeviceInfo(width=640, height=480, fps=30), VideoDeviceInfo(width=424, height=240, fps=30), VideoDeviceInfo(width=352, height=288, fps=30), VideoDeviceInfo(width=320, height=240, fps=30), VideoDeviceInfo(width=320, height=180, fps=30), VideoDeviceInfo(width=176, height=144, fps=30), VideoDeviceInfo(width=160, height=120, fps=30)]}]
錄音設備列表和可用配置以下:
[{'name': '麥克風 (Realtek High Definition Audio)', 'format_list': [AudioDeviceInfo(channel=2, bits=16, rate=44100)]}]
桌面分辨率爲:1920x1080

其餘

ffmpeg -list_devices true -f gdigrab -i dummy動畫

把dshow換成gdigrab。。。能夠看到提示讓你使用desktop或者對應的窗口名ui

採集攝像頭和桌面和麥克風

使用python啓動和中止錄製進程

啓動ffmpeg子進程

ffmpeg_process = subprocess.Popen('ffmpeg -f gdigrab -i desktop -y out.mp4', shell=True, stdin=subprocess.PIPE)

中止子進程,ffmpeg命令行收到'q'就會自動退出

ffmpeg_process.communicate(b'q')

效果1

輸入圖片說明

在不變更桌面和攝像頭的分辨率狀況下,讓桌面畫面和攝像頭並排顯示,hstack可讓兩個畫面並排顯示,hstack函數要求兩邊的畫面的高度必須保持一致,所以加上pad函數來對高度短的畫面填充純色。

代碼分段加註釋以後以下

fmpeg 
-f gdigrab -i desktop # 輸入桌面畫面
-f dshow -i video="USB 2.0 Webcam Device" -s 1280*720 # 輸入攝像頭畫面
-filter_complex "[0:v]pad=1920:1080:0:0:black[left];[1:v]pad=1280:1080:0:0:black[right];[left][right]hstack"  #濾鏡操做
-f dshow -i audio="麥克風 (Realtek High Definition Audio)"  # 輸入麥克風
-y out.mp4  # 輸出到文件

語法解析

  1. -f gdigrab -i desktop 獲取桌面畫面, -f設置輸入格式, -i設置輸入源
  2. -f dshow -i video="{設備名}" -s {分辨率設置} 獲取攝像頭畫面, 設備名是從上面提取到的,分辨率也是查看設備配置獲得的,這裏使用的是個人攝像頭的支持的最高分辨率,格式是{寬}*{高}
  3. -filter_complex, 濾鏡處理,處理流程用雙引號包起來了,裏面的語法是相似函數式編程, 單純的輸入數據處理而後輸出數據,[0:v][1:v] 分別表示前面兩個用-i引入的第一個和第二個畫面源。第一個分號語句[0:v]pad=1920:1080:0:0:black[left];第一個中括號表示輸入,末尾中括號表示輸出,中間是調用pad函數,參數列表用冒號隔開,每一個位置的參數意義能夠查看文檔。第二個分號語句[1:v]pad=1280:1080:0:0:black[right];同上,最後一段是[left][right]hstack末尾沒有中括號和分號,表示輸出濾鏡的最後結果。
  4. -f dshow -i audio="麥克風 (Realtek High Definition Audio)" 獲取麥克風數據
  5. -y out.mp4 將濾鏡的畫面結果和麥克風的聲音數據合併,輸出到文件out.mp4, -y表示若是文件存在就替換。

濾鏡函數文檔

效果2

輸入圖片說明

仍是使用hstack來並排顯示兩個畫面,仍然須要將兩個畫面的高度調成一致,這邊使用scale函數來修改畫面的分辨率,將兩個畫面的高度調爲二者中的高值。

python組裝命令行代碼

cmd = '''
    ffmpeg
    -f gdigrab -i desktop
    -f dshow -i video="{vname}" -s {vwidth}*{vheight}
    -filter_complex "[0:v]scale=-1:{max_height}[left];[1:v]scale=-1:{max_height}[right];[left][right]hstack,scale=1980:-1"
    -f dshow -i audio="{aname}"
    -y out.mp4
'''.format(
    vname=vdevice['name'],
    vwidth=vdevice['format_list'][0].width,
    vheight=vdevice['format_list'][0].height,
    max_height = max(desktop_info.height, vdevice['format_list'][0].height),
    aname=adevice['name']
)

cmd = cmd.replace('\n', ' ').replace('   ', ' ').replace('   ', ' ').replace('   ', ' ')   #刪除富餘的空格

個人機器上最後執行的命令是

ffmpeg -f gdigrab -i desktop -f dshow -i video="USB 2.0 Webcam Device" -s 1280*720 -filter_complex "[0:v]scale=-1:1080[left];[1:v]scale=-1:1080[right];[left][right]hstack,scale=1980:-1" -f dshow -i audio="麥克風 (Realtek High Definition Audio)" -y out.mp4

語法解析

  1. [0:v]scale=-1:{max_height}[left];,scale調整分辨率大小,將高度設置爲二者的最高值,第一個參數爲-1,表示按比例變化。
  2. [left][right]hstack,scale=1980:-1, 最後合併的畫面將長度改爲了1980高度成比例變化,逗號語法將hstack的輸出直接做爲scale的輸入,省去了中間變量的命名。

效果3

輸入圖片說明

讓攝像頭畫面浮動到桌面的右下角可使用overlay方法

python組裝命令行代碼

cmd = '''
    ffmpeg
    -f gdigrab -i desktop
    -f dshow -i video="{vname}" -s {vwidth}*{vheight}
    -filter_complex "[0:v][1:v]overlay=main_w-overlay_w:main_h-overlay_h"
    -f dshow -i audio="{aname}"
    -y out.mp4
'''.format(
    vname=vdevice['name'],
    vwidth=vdevice['format_list'][0].width,
    vheight=vdevice['format_list'][0].height,
    max_height = max(desktop_info.height, vdevice['format_list'][0].height),
    aname=adevice['name']
)

cmd = cmd.replace('\n', ' ').replace('   ', ' ').replace('   ', ' ').replace('   ', ' ')   #刪除富餘的空格

個人機器上最後執行的命令是

ffmpeg -f gdigrab -i desktop -f dshow -i video="USB 2.0 Webcam Device" -s 1280*720 -filter_complex "[0:v][1:v]overlay=main_w-overlay_w:main_h-overlay_h" -f dshow -i audio="麥克風 (Realtek High Definition Audio)"  -y o.mp4

語法解析

  1. [0:v][1:v]overlay=main_w-overlay_w:main_h-overlay_h, overlay方法接收兩個輸入參數,第一個是[0:v]底層畫面,這邊使用的是桌面, 第二個是[1:v]浮動畫面, 這邊使用的是攝像頭畫面, overlay設置浮動畫面相對於底層畫面的偏移位置座標,main_w,overlay_w,main_h,overlay_h是經過兩個輸入參數的附加參數,其餘附加參數能夠看文檔, main_w和main_h是底層畫面的寬度和高度,overlay_w和overlay_h是浮動畫面的寬度和高度, 座標(main_w-overlay_w, main_h-overlay_h)正好就能夠將浮動畫面完美的放置到右下角。
相關文章
相關標籤/搜索