下载安装
提供了可执行文件和动态库,纯命令行使用Architecture版本。
查看当前dshow可用的录像设备和录音设备
使用如下命令可以查看设备列表
ffmpeg -list_devices true -f dshow -i dummy
可以从输出中提取到设备名,进行视频录制时会使用到这个设备名
[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="{录像设备名}"
可以从输出中得到摄像头的可用分辨率
[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
可以从输出中提取出桌面分辨率
[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或者对应的窗口名
采集摄像头和桌面和麦克风
使用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 # 输出到文件
语法解析
-f gdigrab -i desktop
获取桌面画面,-f
设置输入格式,-i
设置输入源-f dshow -i video="{设备名}" -s {分辨率设置}
获取摄像头画面, 设备名是从上面提取到的,分辨率也是查看设备配置得到的,这里使用的是我的摄像头的支持的最高分辨率,格式是{宽}*{高}
-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
末尾没有中括号和分号,表示输出滤镜的最后结果。-f dshow -i audio="麦克风 (Realtek High Definition Audio)"
获取麦克风数据-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
语法解析
[0:v]scale=-1:{max_height}[left];
,scale调整分辨率大小,将高度设置为两者的最高值,第一个参数为-1,表示按比例变化。[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
语法解析
[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)正好就可以将浮动画面完美的放置到右下角。