ffmpeg-python跟踪转码过程
ffmpeg-python tracking transcoding process
我的问题是基于https://github.com/kkroening/ffmpeg-python/blob/master/examples/show_progress.py
理想情况下,我只想跟踪转码过程,例如 0 - 100,向我的 gRPC 客户端发出流式收益响应。从技术上讲,我不需要进度条。
如何向 ffmpeg 子进程提供我自己的套接字并处理写入事件?
您可以使用 -progress pipe:1
将“进度”重定向到 stdout
。
参见
困难的部分是实际获得进度百分比。
- 首先使用 FFprobe 计算总帧数,如 here 所述。
- 作为子进程执行 FFmpeg,重定向
-progress
到 stdout
。
- 启动一个从
stdout
.
读取文本行的线程
该线程查找 frame=xx
,获取框架,并将其放入列表(1 个元素的列表)中。
- 执行“主循环”以演示进度读数。
循环休眠 1 秒,从队列中读取最后一个元素,并打印进度。
代码首先构建合成视频文件 input.mp4
- 用作输入。
这是一个“自包含”代码示例:
import subprocess as sp
import shlex
import json
from threading import Thread
import time
def progress_reader(procs, q):
while True:
if procs.poll() is not None:
break # Break if FFmpeg sun-process is closed
progress_text = procs.stdout.readline() # Read line from the pipe
# Break the loop if progress_text is None (when pipe is closed).
if progress_text is None:
break
progress_text = progress_text.decode("utf-8") # Convert bytes array to strings
# Look for "frame=xx"
if progress_text.startswith("frame="):
frame = int(progress_text.partition('=')[-1]) # Get the frame number
q[0] = frame # Store the last sample
# Build synthetic video for testing:
################################################################################
sp.run(shlex.split('ffmpeg -y -f lavfi -i testsrc=size=320x240:rate=30 -f lavfi -i sine=frequency=400 -f lavfi -i sine=frequency=1000 -filter_complex amerge -vcodec libx265 -crf 17 -pix_fmt yuv420p -acodec aac -ar 22050 -t 30 input.mp4'))
################################################################################
# Use FFprobe for counting the total number of frames
################################################################################
# Execute ffprobe (to show streams), and get the output in JSON format
# Actually counts packets instead of frames but it is much faster
#
data = sp.run(shlex.split('ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 -of json input.mp4'), stdout=sp.PIPE).stdout
dict = json.loads(data) # Convert data from JSON string to dictionary
tot_n_frames = float(dict['streams'][0]['nb_read_packets']) # Get the total number of frames.
################################################################################
# Execute FFmpeg as sub-process with stdout as a pipe
# Redirect progress to stdout using -progress pipe:1 arguments
process = sp.Popen(shlex.split('ffmpeg -y -loglevel error -i input.mp4 -acodec libvorbis -vcodec libvpx-vp9 -crf 20 -pix_fmt yuv420p -progress pipe:1 output.webm'), stdout=sp.PIPE)
q = [0] # We don't really need to use a Queue - use a list of of size 1
progress_reader_thread = Thread(target=progress_reader, args=(process, q)) # Initialize progress reader thread
progress_reader_thread.start() # Start the thread
while True:
if process.poll() is not None:
break # Break if FFmpeg sun-process is closed
time.sleep(1) # Sleep 1 second (do some work...)
n_frame = q[0] # Read last element from progress_reader - current encoded frame
progress_percent = (n_frame/tot_n_frames)*100 # Convert to percentage.
print(f'Progress [%]: {progress_percent:.2f}') # Print the progress
process.stdout.close() # Close stdin pipe.
progress_reader_thread.join() # Join thread
process.wait() # Wait for FFmpeg sub-process to finish
注:
- 代码示例假定
ffmpeg
和 ffprobe
在可执行路径中。
示例输出:
Progress [%]: 7.33
Progress [%]: 16.00
Progress [%]: 24.67
Progress [%]: 33.33
Progress [%]: 42.13
Progress [%]: 50.40
Progress [%]: 58.80
Progress [%]: 67.20
Progress [%]: 75.60
Progress [%]: 84.00
Progress [%]: 92.40
Progress [%]: 100.00
我的问题是基于https://github.com/kkroening/ffmpeg-python/blob/master/examples/show_progress.py 理想情况下,我只想跟踪转码过程,例如 0 - 100,向我的 gRPC 客户端发出流式收益响应。从技术上讲,我不需要进度条。 如何向 ffmpeg 子进程提供我自己的套接字并处理写入事件?
您可以使用 -progress pipe:1
将“进度”重定向到 stdout
。
参见
困难的部分是实际获得进度百分比。
- 首先使用 FFprobe 计算总帧数,如 here 所述。
- 作为子进程执行 FFmpeg,重定向
-progress
到stdout
。 - 启动一个从
stdout
.
读取文本行的线程 该线程查找frame=xx
,获取框架,并将其放入列表(1 个元素的列表)中。 - 执行“主循环”以演示进度读数。
循环休眠 1 秒,从队列中读取最后一个元素,并打印进度。
代码首先构建合成视频文件 input.mp4
- 用作输入。
这是一个“自包含”代码示例:
import subprocess as sp
import shlex
import json
from threading import Thread
import time
def progress_reader(procs, q):
while True:
if procs.poll() is not None:
break # Break if FFmpeg sun-process is closed
progress_text = procs.stdout.readline() # Read line from the pipe
# Break the loop if progress_text is None (when pipe is closed).
if progress_text is None:
break
progress_text = progress_text.decode("utf-8") # Convert bytes array to strings
# Look for "frame=xx"
if progress_text.startswith("frame="):
frame = int(progress_text.partition('=')[-1]) # Get the frame number
q[0] = frame # Store the last sample
# Build synthetic video for testing:
################################################################################
sp.run(shlex.split('ffmpeg -y -f lavfi -i testsrc=size=320x240:rate=30 -f lavfi -i sine=frequency=400 -f lavfi -i sine=frequency=1000 -filter_complex amerge -vcodec libx265 -crf 17 -pix_fmt yuv420p -acodec aac -ar 22050 -t 30 input.mp4'))
################################################################################
# Use FFprobe for counting the total number of frames
################################################################################
# Execute ffprobe (to show streams), and get the output in JSON format
# Actually counts packets instead of frames but it is much faster
#
data = sp.run(shlex.split('ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 -of json input.mp4'), stdout=sp.PIPE).stdout
dict = json.loads(data) # Convert data from JSON string to dictionary
tot_n_frames = float(dict['streams'][0]['nb_read_packets']) # Get the total number of frames.
################################################################################
# Execute FFmpeg as sub-process with stdout as a pipe
# Redirect progress to stdout using -progress pipe:1 arguments
process = sp.Popen(shlex.split('ffmpeg -y -loglevel error -i input.mp4 -acodec libvorbis -vcodec libvpx-vp9 -crf 20 -pix_fmt yuv420p -progress pipe:1 output.webm'), stdout=sp.PIPE)
q = [0] # We don't really need to use a Queue - use a list of of size 1
progress_reader_thread = Thread(target=progress_reader, args=(process, q)) # Initialize progress reader thread
progress_reader_thread.start() # Start the thread
while True:
if process.poll() is not None:
break # Break if FFmpeg sun-process is closed
time.sleep(1) # Sleep 1 second (do some work...)
n_frame = q[0] # Read last element from progress_reader - current encoded frame
progress_percent = (n_frame/tot_n_frames)*100 # Convert to percentage.
print(f'Progress [%]: {progress_percent:.2f}') # Print the progress
process.stdout.close() # Close stdin pipe.
progress_reader_thread.join() # Join thread
process.wait() # Wait for FFmpeg sub-process to finish
注:
- 代码示例假定
ffmpeg
和ffprobe
在可执行路径中。
示例输出:
Progress [%]: 7.33
Progress [%]: 16.00
Progress [%]: 24.67
Progress [%]: 33.33
Progress [%]: 42.13
Progress [%]: 50.40
Progress [%]: 58.80
Progress [%]: 67.20
Progress [%]: 75.60
Progress [%]: 84.00
Progress [%]: 92.40
Progress [%]: 100.00