多进程并行处理中FFMpeg子进程挂起导致整个任务阻塞问题解决

1 subprocess模块

1.1 subprocess介绍

在 Python 中,subprocess 模块为程序员提供了与操作系统命令进行交互的桥梁。无论是执行简单的 shell 命令,还是管理复杂的外部进程,subprocess 都能很好地完成任务。通过 subprocess,Python 脚本可以启动新的应用程序,与其输入/输出/错误管道建立连接,并获取其返回值,这对于实现自动化任务和系统集成至关重要。

subprocess 模块的出现,极大地扩展了 Python 的功能边界,使其不再仅仅局限于编写内部逻辑和数据处理,而是能够深入到操作系统的层面,与各种外部程序和命令进行交互。这对于那些需要调用外部工具或库来完成任务的 Python 开发者来说,无疑是一个巨大的福音。

subprocess 模块提供了一组函数和类,用于创建和管理子进程。这些子进程可以是外部应用程序、shell 命令,或者是其他任何可执行文件。通过 subprocess,我们可以控制子进程的输入和输出,获取其执行结果,甚至改变其行为。

在subprocess模块中的常用函数如下表所示:

subprocess模块常用参数说明:


1.1 subprocess.run() 函数

subprocess.run() 是 Python 3.5 引入的一个高级接口,用于替代旧的 subprocess.call() 和 subprocess.check_output() 等函数。它可以执行命令并等待其完成,然后返回一个 CompletedProcess 对象,包含执行结果的信息,如返回码、标准输出和标准错误等。
subprocess.run() 提供了更多的参数选项,如可以指定输入数据、设置超时时间、指定工作目录等。

参数说明:

  • args:这是命令和参数的列表,其中命令是列表的第一个元素。如果 shell 参数设置为 True,args必须是一个字符串
  • stdin:可以是 None、一个已打开的文件描述符、一个现有的文件对象,或者 subprocess.PIPE。用于指定子进程的标准输入。
  • input:如果 stdin 参数是 PIPE,这个参数可以被用来传递一个字符串到子进程的标准输入。
  • stdout 和 stderr:这些参数与 stdin 类似,但它们控制的是标准输出和标准错误输出。它们也可以被设置为 None、一个文件描述符、一个现有的文件对象,或者 subprocess.PIPE。
  • capture_output:如果设置为 True,stdout 和 stderr 将会被捕获。这相当于设置 stdout=subprocess.PIPE 和 stderr=subprocess.PIPE。
  • shell:如果为 True,指定的命令将通过 shell 执行。
  • cwd:如果指定,子进程的当前工作目录将被改变到 cwd。
  • timeout:如果指定,且执行时间超过了这个值(以秒计),将会抛出 subprocess.TimeoutExpired 异常。
  • check:如果设置为 True,并且进程以非零状态码退出,将会抛出 subprocess.CalledProcessError 异常。
  • encoding 和 errors:指定如何解码从子进程输出的字节。只有当 stdout 或 stderr 被捕获时才有效。
  • text:一个简写的标志,用于设置 encoding='utf-8' 和 universal_newlines=True,它会将 stdout 和 stderr 的输出作为字符串处理。
  • env:指定子进程的环境变量。如果为 None,使用父进程的环境变量。
  • universal_newlines:如果为 True,stdin、stdout 和 stderr 将会作为文本流处理,类似于 text=True。
  • **other_popen_kwargs:你可以提供其他的关键字参数,这些参数将直接传递给 Popen 构造函数。

subprocess.run() 的基本用法如下:

import subprocess

result = subprocess.run(['ls', '-l'], capture_output=True, text=True)
print(result.stdout)  # 输出命令执行结果
print(result.returncode)  # 输出命令返回值

在上面的例子中,我们执行了 ls -l 命令,并通过 capture_output=True 参数将标准输出捕获到变量 result.stdout 中。同时,text=True 参数确保输出以字符串形式而不是字节流形式返回。这样,我们就可以直接对输出进行字符串操作了。 

1.2 subprocess.Popen() 函数

虽然 subprocess.run() 函数非常方便,但它只适用于执行一次命令并等待其完成的情况。如果需要与子进程进行更复杂的交互,比如读取其输出、向其发送输入,或者同时管理多个子进程,那么就需要使用 subprocess.Popen() 类了。

参数说明:

  • args: 和 run 函数相同。
  • bufsize:缓冲区大小,-1 表示使用系统默认缓冲区大小,0 表示不使用缓冲区,1 表示行缓冲。
  • executable:如果指定,将使用这个可执行文件来替换要执行的程序。
  • stdin, stdout, stderr, shell, cwd:与 run 函数相同。
  • preexec_fn:仅在 Unix 系统上有效)是一个可调用对象,它将在子进程运行之前被调用。
  • close_fds:如果为 True(默认值),在子进程中除了 0、1 和 2 之外所有的文件描述符都将被关闭。
  • env:用于指定环境变量。
  • shell:如果这个参数为 True,指定的命令将通过 shell 执行。
  • cwd:如果指定,子进程的工作目录将改变到 cwd。
  • env:用于指定子进程的环境变量。如果为 None,子进程会继承父进程的环境变量。
  • universal_newlines:(现在推荐使用 text 参数)如果为 True,stdin、stdout 和 stderr 将作为文本流(字符串)而不是字节流。
  • startupinfo 和 creationflags:这两个参数仅在 Windows 系统上有效,用于控制子进程的创建方式。
  • restore_signals:(仅在 Unix 系统上有效)如果为 True,在执行新程序前,将会恢复 Python 的信号处理为默认值。
  • start_new_session:(仅在 Unix 系统上有效)如果为 True,子进程将会在新的会话中启动。
  • pass_fds:(仅在 Unix 系统上有效)要传递给子进程的文件描述符。
  • encoding 和 errors:这些参数用于设置在解码和编码文本数据时使用的编码和错误处理方式。
  • text:一个用于设置编码为 "utf-8" 且 universal_newlines=True 的简化参数,这将会让 stdout 和 stderr 的输出作为字符串处理。

subprocess.Popen() 类提供了更多的选项和参数,使得我们可以更精细地控制子进程的行为。下面是一个简单的例子:

import subprocess

# 创建一个子进程,但不等待它完成
process = subprocess.Popen(['ping', 'www.google.com'], stdout=subprocess.PIPE)

# 读取子进程的输出
output, _ = process.communicate()

# 打印输出
print(output.decode('utf-8'))

# 检查返回值
if process.returncode == 0:
    print("Ping 成功")
else:
    print("Ping 失败")

在这个例子中,我们创建了一个 Popen 对象来执行 ping 命令,并通过 stdout=subprocess.PIPE 将标准输出重定向到一个管道中。然后,我们使用 communicate() 方法读取输出,并等待进程结束。communicate() 方法返回的是一个包含标准输出和标准错误的元组,我们可以通过索引来访问它们。

需要注意的是,Popen 对象的 returncode 属性是在进程结束后才可用的,所以在调用 communicate() 方法之前无法获取它。如果需要在进程结束前获取其输出或错误,可以通过读取 Popen 对象的 stdout 和 stderr 属性来实现。

1.3 subprocess.call()函数

这个函数用于执行指定的命令,等待命令执行完成,并返回命令的返回码。

参数 基本上与 subprocess.run() 相同。

import subprocess
 
return_code = subprocess.call(["echo", "Hello, World!"])
print("Command returned:", return_code)

 2 subprocess执行ffmpeg命令

import subprocess

# 定义FFmpeg命令
ffmpeg_command = 'ffmpeg -i input.mp3 -ss 00:00:10 -to 00:00:20 output.mp3'

# 使用subprocess.run()方法执行FFmpeg命令
code = subprocess.run(ffmpeg_command, shell=True)

3 ffmpeg执行导致子进程挂起

当output.mp3文件存在时,会导致subprocess.run执行一直等待,通过增加ffmpeg命令参数解决:

import subprocess

# 定义FFmpeg命令
ffmpeg_command = 'ffmpeg -y -i input.mp3 -ss 00:00:10 -to 00:00:20 output.mp3'

# 使用subprocess.run()方法执行FFmpeg命令
code = subprocess.run(ffmpeg_command, shell=True)

增加-y参数,强制写入数据。

 4 FFmpeg介绍

FFmpeg项目由 Fabrice Bellard在2000年创立。到目前为止,FFmpeg项目的开发者仍然与VLC、MPV、dav1d、x264等多媒体开源项目有着广泛的重叠。Ffmpeg(FastForward Mpeg)是一款遵循GPL的开源软件,在音视频处理方面表现十分优秀,几乎囊括了现存所有的视音频格式的编码,解码、转码、混合、过滤及播放。作为最受欢迎的视频和图像处理软件,它被来自各行各业的不同公司所广泛使用。同时也是一款跨平台的软件,完美兼容Linux、Windows、Mac OSX等平台。其实它由3大部件组成,号称音视频处理工具三剑客:

  • Ffmpeg:由命令行组成,用于多媒体格式转换
  • Ffplay:基于ffmpeg开源代码库libraries做的多媒体播放器
  • Ffprobe:基于ffmpeg做的多媒体流分析器

Ffmpeg 应该是 FFmpeg 工具集中最核心的利器,支持多种多样的编码器、解码器、封装格式、滤镜功能。FFmpeg框架的基本组成包含AVFormat、AVCodec、AVFilter、AVDevice、AVUtil等模块库,结构图如下:

  •  AVFormat–FFmpeg的封装模块

AVFormat中实现了目前多媒体领域中的绝大多数媒体封装格式,包括封装和解封装,如MP4、FLV、KV、TS等文件封装格式,RTMP、RTSP、MMS、HLS等网络协议封装格式。FFmpeg是否支持某种媒体封装格式,取决于编译时是否包含了该格式的封装库。根据实际需求,可进行媒体封装格式的扩展,增加自己定制的封装格式,即在AVFormat中增加自己的封装处理模块。

  • AVCodec–FFmpeg的编解码模块

AVCodec中实现了目前多媒体领域绝大多数常用的编解码格式,即支持编码,也支持解码。AVCodec除了支持MPEG4、AAC、MJPEG等自带的媒体编解码格式之外,还支持第三方的编解码器,如H.264(AVC)编码,需要使用x264编码器;H.265(HEVC)编码,需要使用x264编码器;MP3(mp3lame)编码,需要使用libmp3lame编码器。如果希望增加自己的编码格式,或者硬件编解码,则需要在AVCodec中增加相应的编解码模块。

  • AVFilter–FFmpeg的滤镜模块

AVFilter库提供了一个通用的音频、视频、字幕等滤镜处理框架。在AVFilter中,滤镜框架可以有多个输入和多个输出。

  • swresample–FFmpeg的音频转换计算模块

swresample模块提供了高级别的音频重采样API。例如允许操作音频采样、音频通道布局转换与布局调整。

  • swscale–FFmpeg的视频图像转换计算模块

swscale模块提供了高级别的图像转换API,例如它允许进行图像缩放和像素格式转换,常见于将图像从1080p转换成720p或者480p等的缩放,或者将图像数据从YUV420p转换成YUYV,或者YUV转RGB等图像格式转换。

5 FFmpeg常用参数

5.1 能力集列表

  • -formats:列出支持的文件格式。
  • -codecs:列出支持的编解码器。
  • -decoders:列出支持的解码器。
  • -encoders:列出支持的编码器。
  • -protocols:列出支持的协议。
  • -bsfs:列出支持的比特流过滤器。
  • -filters:列出支持的滤镜。
  • -pix_fmts:列出支持的图像采样格式。
  • -sample_fmts:列出支持的声音采样格式。

5.2 常用输入选项

  • -i filename:指定输入文件名。
  • -f fmt:强制设定文件格式,需使用能力集列表中的名称(缺省是根据扩展名选择的)。
  • -ss hh:mm:ss[.xxx]:设定输入文件的起始时间点,启动后将跳转到此时间点然后开始读取数据。

对于输入,以下选项通常是自动识别的,但也可以强制设定。

  • -c codec:指定解码器,需使用能力集列表中的名称。
  • -acodec codec:指定声音的解码器,需使用能力集列表中的名称。
  • -vcodec codec:指定视频的解码器,需使用能力集列表中的名称。
  • -b:v bitrate:设定视频流的比特率,整数,单位bps。
  • -r fps:设定视频流的帧率,整数,单位fps。
  • -s WxH : 设定视频的画面大小。也可以通过挂载画面缩放滤镜实现。
  • -pix_fmt format:设定视频流的图像格式(如RGB还是YUV)。
  • -ar sample rate:设定音频流的采样率,整数,单位Hz。
  • -ab bitrate:设定音频流的比特率,整数,单位bps。
  • -ac channels:设置音频流的声道数目。

5.3 常用输出选项

  • -f fmt:强制设定文件格式,需使用能力集列表中的名称(缺省是根据扩展名选择的)。
  • -c codec:指定编码器,需使用能力集列表中的名称(编码器设定为”copy“表示不进行编解码)。
  • -acodec codec:指定声音的编码器,需使用能力集列表中的名称(编码器设定为”copy“表示不进行编解码)。
  • -vcodec codec:指定视频的编码器,需使用能力集列表中的名称(编解码器设定为”copy“表示不进行编解码)。
  • -r fps:设定视频编码器的帧率,整数,单位fps。
  • -pix_fmt format:设置视频编码器使用的图像格式(如RGB还是YUV)。
  • -ar sample rate:设定音频编码器的采样率,整数,单位Hz。
  • -b bitrate:设定音视频编码器输出的比特率,整数,单位bps。
  • -ab bitrate:设定音频编码器输出的比特率,整数,单位bps。
  • -ac channels:设置音频编码器的声道数目。
  • -an 忽略任何音频流。
  • -vn 忽略任何视频流。
  • -t hh:mm:ss[.xxx]:设定输出文件的时间长度。
  • -to hh:mm:ss[.xxx]:如果没有设定输出文件的时间长度的画可以设定终止时间点。

5.4 ffmpeg流标识

FFMPEG的某些选项可以对一个特定的媒体流起作用,这种情况下需要在选项后面增加一个流标识。流标识允许以下几种格式:

  • 流序号。譬如“:1”表示第二个流。
  • 流类型。譬如“:a“表示音频流,流类型可以和流序号合并使用,譬如“:a:1”表示第二个音频流。
  • 节目。节目和流序号可以合并使用。
  • 流标识。流标识是一个内部标识号。

假如要设定第二个音频流为copy,则需要指定-codec:a:1 copy

5.5 ffmpeg音频选项

  • -aframes:等价于frames:a,输出选项,用于指定输出的音频帧数目。
  • -aq:等价于q:a,老版本为qscale:a,用于设定音频质量。
  • -atag:等价于tag:a,用于设定音频流的标签。
  • -af:等价于filter:a,用于设定一个声音的后处理过滤链,其参数为一个描述声音后处理链的字符串。

5.6 ffmpeg视频选项

  • -vframes:等价于frames:v,输出选项,用于指定输出的视频帧数目。
  • -aspect:设置宽高比,如4:3、16:9、1.3333、1.7777等。
  • -bits_per_raw_sample:设置每个像素点的比特数。
  • -vstats:产生video统计信息。
  • -vf:等价于filter:v,用于设定一个图像的后处理过滤链,其参数为一个描述图像后处理链的字符串。
  • -vtag:等价于tag:v,用于设定视频流的标签。
  • -force_fps:强制设定视频帧率。
  • -force_key_frames:显式控制关键帧的插入,参数为字符串,可以是一个时间戳,也可以是一个 “expr:”前缀的表达式。如“-force_key_frames 0:05:00”、“-force_key_frames expr:gte(t,n_forced*5)”

5.7 ffmpeg滤镜选项

-filter_simple 添加简单滤镜

-filter_complex FILTER 添加复杂滤镜

5.8 ffmpeg高级选项

  • -re:要求按照既定速率处理输入数据,这个速率即是输入文件的帧率。
  • -map:指定输出文件的流映射关系。例如 “-map 1:0 -map 1:1”要求将第二个输入文件的第一个流和第二个流写入到输出文件。如果没有-map选项,ffmpeg采用缺省的映射关系。

5.9 ffprobe参数

简单的说,ffprobe 是一个多媒体流分析工具。它从多媒体流中收集信息,并且以人类和机器可读的形式打印出来。它可以用来检测多媒体流的容器类型,以及每一个多媒体流的格式和类型。它可以作为一个独立的应用来使用,也可以结合文本过滤器执行更复杂的处理。

  • -f format 强制使用某种格式
  • -sexagesimal 时间单元格式化 HOURS:MM:SS.MICROSECONDS
  • -pretty 格式美化
  • -print_format format 格式化(可选值: default, compact, csv, flat, ini, json, xml)
  • -of format -print_format别名
  • -select_streams stream_specifier 选择指定流
  • -sections 打印节的结构和信息
  • -show_data 显示包数据
  • -show_data_hash 显示包数据哈希值
  • -show_error 显示文件探测/检测错误
  • -show_format 显示格式或者容器信息
  • -show_frames 显示帧信息
  • -show_format_entry entry 根据格式/容器信息显示指定entry
  • -show_packets 显示包信息
  • -show_programs 显示程序信息
  • -show_streams 显示流信息
  • -show_chapters 显示章节信息
  • -count_frames 统计每个流的帧数
  • -count_packets 统计每个流的包数
  • -show_program_version 显示ffprobe版本
  • -show_library_versions show library versions
  • -show_versions show program and library versions
  • -show_pixel_formats 显示像素格式
  • -show_private_data show private data
  • -private same as show_private_data
  • -bitexact force bitexact output
  • -read_intervals read_intervals set read intervals
  • -default generic catch all option

5.10 ffplayer参数

  • -x 强制设置视频显示窗口的宽度
  • -y 强制设置视频显示窗口的高度
  • -S 设置视频显示的宽高
  • -fs 强制全屏显示
  • -an 屏蔽音频
  • -vn 屏蔽视频
  • -Sn 屏蔽字幕
  • -ss 根据设置的秒进行定位拖动
  • -t 设置播放视频/音频长度
  • -Bytes 设置定位拖动的策略,0为不可拖动,1为可拖动,-1为自动
  • -Nodisp 关闭图形化显示窗口
  • -f 强制使用设置的格式进行解析
  • -window_title 设置显示窗口的标题
  • -af 设置音频的滤镜
  • -Codec 强制使用设置的codec进行解码
  • -autorotate 自动旋转视频
  • -ast 设置将要播放的音频流
  • -vst 设置将要播放的视频流
  • -sst 设置将要播放的字幕流
  • -Stats 输出多媒体播放状态
  • -Fast 非标准化规范的多媒体兼容优化
  • -sync 音视频同步设置可设置根据音频视频进行参考,视频时间参考,或者外部扩展时间进行参考
  • -autoexit 多媒体播放完毕自动退出ffplay,ffplay默认播放完毕不退出播放器
  • -exitonkeydown 当有按键按下事件产生时退出ffplay
  • -exitonmousedown 当有鼠标按键事件产生时退出ffplay
  • -loop 设置多媒体文件循环播放次数
  • -framedrop 当CPU资 源占用过高时,自动丢帧
  • -infbuf 设置无极限的播放器buffer,这个选项常见于实时流媒体播放场景
  • -vf 视频滤镜设置
  • -acodec 强制使用设置的音频解码器
  • -vcodec 强制使用设置的视频解码器
  • -scodec 强制使用设置的字幕解码器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值