Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
tsoc
dcu-codec-pipeline-py
Commits
0cb40ca1
Commit
0cb40ca1
authored
Mar 10, 2026
by
zhyh2010
Browse files
Initial commit
parents
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
765 additions
and
0 deletions
+765
-0
codecbenchmark-py/__pycache__/benchmark_multi_inst.cpython-310.pyc
...hmark-py/__pycache__/benchmark_multi_inst.cpython-310.pyc
+0
-0
codecbenchmark-py/benchmark_multi_inst.py
codecbenchmark-py/benchmark_multi_inst.py
+82
-0
codecbenchmark-py/readme.md
codecbenchmark-py/readme.md
+210
-0
codecbenchmark-py/requirments.txt
codecbenchmark-py/requirments.txt
+2
-0
codecbenchmark-py/video_benchmark.py
codecbenchmark-py/video_benchmark.py
+467
-0
codecbenchmark-py/video_benchmark/sample_1920x1080_30fps_8bit.h264
...hmark-py/video_benchmark/sample_1920x1080_30fps_8bit.h264
+0
-0
codecbenchmark-py/video_cuda_decode_benchmark.csv
codecbenchmark-py/video_cuda_decode_benchmark.csv
+2
-0
codecbenchmark-py/video_cuda_encode_benchmark.csv
codecbenchmark-py/video_cuda_encode_benchmark.csv
+2
-0
No files found.
codecbenchmark-py/__pycache__/benchmark_multi_inst.cpython-310.pyc
0 → 100644
View file @
0cb40ca1
File added
codecbenchmark-py/benchmark_multi_inst.py
0 → 100644
View file @
0cb40ca1
#!/usr/bin/env python3
import
argparse
import
subprocess
import
ffmpeg
import
os
import
sys
V4L2_CODEC_NAMES
=
{
"h264"
:
"h264_v4l2m2m"
,
"hevc"
:
"hevc_v4l2m2m"
,
"vp9"
:
"vp9_v4l2m2m"
,
"avs2"
:
"avs2_v4l2m2m"
,
"mjpeg"
:
"mjpeg_v4l2m2m"
,
}
CUVID_CODEC_NAMES
=
{
"h264"
:
"h264_cuvid"
,
"hevc"
:
"hevc_cuvid"
,
"vp9"
:
"vp9_cuvid"
,
"avs2"
:
"avs2_cuvid"
,
"mjpeg"
:
"mjpeg_cuvid"
,
}
def
get_input_codec
(
input_file
):
"""获取输入文件的编码格式(小写)"""
try
:
probe
=
ffmpeg
.
probe
(
input_file
,
select_streams
=
'v:0'
,
show_entries
=
'stream=codec_name'
)
return
probe
[
'streams'
][
0
][
'codec_name'
].
lower
()
except
Exception
:
return
os
.
path
.
splitext
(
input_file
)[
1
].
replace
(
'.'
,
''
).
lower
()
def
run_multi_instance_benchmark
(
input_file
,
codec_type
,
threads
,
log_file_path
):
"""运行多实例基准测试"""
if
not
os
.
path
.
isfile
(
input_file
):
print
(
f
"Error: Input file '
{
input_file
}
' does not exist."
,
file
=
sys
.
stderr
)
sys
.
exit
(
1
)
input_codec
=
get_input_codec
(
input_file
)
if
codec_type
==
"cuda"
:
stream
=
ffmpeg
.
input
(
input_file
,
hwaccel
=
'cuda'
)
elif
codec_type
==
"v4l2"
:
decoder
=
V4L2_CODEC_NAMES
.
get
(
input_codec
,
input_codec
)
stream
=
ffmpeg
.
input
(
input_file
,
vcodec
=
decoder
)
elif
codec_type
==
"cuvid"
:
decoder
=
CUVID_CODEC_NAMES
.
get
(
input_codec
,
input_codec
)
stream
=
ffmpeg
.
input
(
input_file
,
vcodec
=
decoder
)
elif
codec_type
==
"vaapi"
:
stream
=
ffmpeg
.
input
(
input_file
,
hwaccel
=
'vaapi'
)
else
:
print
(
f
"Unknown codec type:
{
codec_type
}
"
,
file
=
sys
.
stderr
)
sys
.
exit
(
1
)
cmd
=
ffmpeg
.
output
(
stream
,
'pipe:'
,
format
=
'null'
).
global_args
(
'-benchmark'
,
'-y'
).
compile
()
processes
=
[]
print
(
f
"Starting
{
threads
}
concurrent ffmpeg processes..."
,
file
=
sys
.
stderr
)
with
open
(
log_file_path
,
'w'
)
as
log_f
:
log_f
.
write
(
f
"# Multi-instance benchmark log
\n
"
)
log_f
.
write
(
f
"# Command:
{
' '
.
join
(
cmd
)
}
\n
"
)
log_f
.
write
(
f
"# Threads:
{
threads
}
\n\n
"
)
for
i
in
range
(
threads
):
p
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
DEVNULL
,
stderr
=
log_f
)
processes
.
append
(
p
)
for
p
in
processes
:
p
.
wait
()
print
(
"All processes finished."
,
file
=
sys
.
stderr
)
if
__name__
==
"__main__"
:
parser
=
argparse
.
ArgumentParser
(
description
=
"Multi-instance FFmpeg Benchmark"
)
parser
.
add_argument
(
"-i"
,
"--input"
,
required
=
True
,
help
=
"Input video file"
)
parser
.
add_argument
(
"-c"
,
"--codec"
,
default
=
"cuda"
,
choices
=
[
"cuda"
,
"cuvid"
,
"v4l2"
,
"vaapi"
],
help
=
"Codec type"
)
parser
.
add_argument
(
"-n"
,
"--threads"
,
type
=
int
,
default
=
6
,
help
=
"Number of concurrent processes"
)
parser
.
add_argument
(
"-o"
,
"--output"
,
required
=
True
,
help
=
"Output log file"
)
args
=
parser
.
parse_args
()
run_multi_instance_benchmark
(
args
.
input
,
args
.
codec
,
args
.
threads
,
args
.
output
)
\ No newline at end of file
codecbenchmark-py/readme.md
0 → 100644
View file @
0cb40ca1
# DCU Codec Pipeline Benchmark
DCU编解码性能测试工具 - 用于测试和评估不同编解码器后端的视频处理性能。
## 项目简介
该项目是DCU性能压测工具codecbenchmark的python实现,可作为DCU上MacCodecSDK以及ffmpeg的python api参考
## 主要功能
### 核心特性
-
✅
**多后端支持**
:支持 CUDA、CUVID、V4L2、VAAPI 等多种编解码后端
-
✅
**编解码测试**
:支持 H264、HEVC、VP9、AVS2、MJPEG 等格式的编解码性能测试
-
✅
**多实例并发**
:支持多线程并发测试,评估系统并发处理能力
-
✅
**多分辨率支持**
:支持从 720p 到 8K 的多种分辨率测试
-
✅
**位深测试**
:支持 8-bit 和 10-bit 位深测试
-
✅
**详细报告**
:生成 CSV 格式的性能测试报告
### 支持的编解码器
**解码器:**
-
H264 (h264_cuvid/h264_v4l2m2m)
-
HEVC (hevc_cuvid/hevc_v4l2m2m)
-
VP9 (vp9_cuvid/vp9_v4l2m2m)
-
AVS2 (avs2_cuvid/avs2_v4l2m2m)
-
MJPEG (mjpeg_cuvid/mjpeg_v4l2m2m)
**编码器:**
-
H264_NVENC / H264_VAAPI
-
HEVC_NVENC / HEVC_VAAPI
-
VP9_NVENC / VP9_VAAPI
-
MJPEG_NVENC / MJPEG_VAAPI
## 环境要求
与MacCodec环境要求一致
### 依赖安装
```
bash
pip
install
-r
requirements.txt
```
主要依赖:
-
ffmpeg-python
-
MacCodecSDK
## 项目结构
其中测试视频文件为方便clone测试仅包含一个文件,若想参考codecBenchmark全面测试可将所有文件放入同目录下,并取消代码中的注释
```
dcu-codec-pipeline-py/
├── codecbenchmark-py/
│ ├── video_benchmark/ # 测试视频文件目录
│ │ └── sample_1920x1080_30fps_8bit.h264
│ ├── benchmark_multi_inst.py # 多实例测试模块
│ ├── video_benchmark.py # 主测试脚本
│ ├── readme.md # 项目说明
│ └── requirements.txt # 依赖列表
├── video_cuda_decode_benchmark.csv # 解码测试结果
└── video_cuda_encode_benchmark.csv # 编码测试结果
```
## 使用方法
### 基本用法
运行解码和编码性能测试:
```
bash
# 使用 CUDA 后端,默认 6 线程
python video_benchmark.py
# 指定后端和线程数
python video_benchmark.py
-c
cuda
-n
8
python video_benchmark.py
-c
vaapi
-n
4
python video_benchmark.py
-c
v4l2
-n
6
```
### 命令行参数
```
bash
usage: video_benchmark.py
[
-h
]
[
-c
{
cuda,cuvid,v4l2,vaapi
}]
[
-n
THREADS]
可选参数:
-h
,
--help
显示帮助信息
-c
,
--codec
编解码后端类型
可选值: cuda, cuvid, v4l2, vaapi
默认值: cuda
-n
,
--threads
并发进程数量
默认值: 6
```
## 测试配置
### 解码测试文件
项目预配置了以下测试文件:
-
`sample_1920x1080_30fps_8bit.h264`
- 1080p 30fps H264 8bit
### 编码测试配置
支持多种分辨率和格式的编码测试:
| 分辨率 | 格式 | 位深 | 帧数 |
|--------|------|------|------|
| 1280x720 | H264/HEVC | 8-bit | 677 |
| 1920x1080 | H264/HEVC/JPEG | 8-bit | 812-2174 |
| 3840x2160 | H264/HEVC/JPEG | 8-bit | 812 |
| 7680x4320 | H264/HEVC | 8-bit | 77 |
| 1280x720 | HEVC | 10-bit | 677 |
| 1920x1080 | HEVC | 10-bit | 812 |
| 3840x2160 | HEVC | 10-bit | 812 |
| 4096x2160 | JPEG | 8-bit | 1 |
| 8192x4320 | JPEG | 8-bit | 1 |
## 输出结果
### CSV 报告文件
测试完成后会生成两个 CSV 文件:
1.
**解码测试报告**
(
`video_{codec}_decode_benchmark.csv`
)
```
csv
Decode,Bit Depth,Resolution,Fps/(BMZ 1 Core),Fps/(BMZ 6 Processes)
H264,8,1920x1080,245.67,1234.56
```
2.
**编码测试报告**
(
`video_{codec}_encode_benchmark.csv`
)
```
csv
Encode,Bit Depth,Resolution,Fps/(BMZ 1 Core)
H264,8,1920x1080,189.34
HEVC,10,3840x2160,95.67
```
### 性能指标说明
-
**Fps/(BMZ 1 Core)**
: 单核心/单进程处理的帧率(FPS)
-
**Fps/(BMZ N Processes)**
: N 个并发进程总处理的帧率(FPS)
## 测试流程
1.
**解压测试文件**
:自动从
`video_benchmark.zip`
解压测试视频
2.
**解码测试**
:
-
单实例解码性能测试
-
多实例并发解码测试
3.
**编码测试**
:
-
将解码后的 YUV 数据重新编码
-
测试不同格式和分辨率的编码性能
4.
**结果聚合**
:
-
统计平均性能数据
-
生成 CSV 报告
5.
**清理临时文件**
:自动清理测试过程中生成的临时文件
## 示例输出
```
Processing: sample_1920x1080_30fps_8bit.h264 (Idx: 1)
-> Single instance decode: 245.67 FPS
-> Multi instance (6 processes): 1234.56 FPS
Encoding: sample_1920x1080_30fps_8bit.h264 -> H264
-> Encode performance: 189.34 FPS
Aggregating results...
Benchmark finished.
```
## 注意事项
1.
**文件准备**
:确保测试视频文件位于
`video_benchmark/`
目录或提供
`video_benchmark.zip`
2.
**权限要求**
:使用 VAAPI/V4L2 后端时可能需要访问
`/dev/dri/renderD128`
设备文件
3.
**性能影响**
:多实例测试会占用较多系统资源,建议根据硬件配置调整线程数
4.
**日志文件**
:测试过程会生成临时日志文件,测试完成后自动清理
## 故障排除
### 常见问题
**问题 1**
:
`FFmpeg command construction failed`
-
检查 FFmpeg 是否正确安装
-
确认选择的编解码后端是否受支持
**问题 2**
:
`Unknown encoder for XXX`
-
确认 FFmpeg 编译时启用了相应的编码器
-
检查硬件驱动是否正确安装
**问题 3**
: 多实例测试性能异常
-
检查系统资源(GPU、内存)是否充足
-
减少并发线程数
`-n`
参数
### 日志调试
测试过程中的详细日志会保存在:
-
解码日志:
`video_benchmark/{filename}.log`
-
多实例日志:
`video_benchmark/{filename}_multi.log`
-
编码日志:
`video_benchmark/{filename}_encode.log`
## 许可证
本项目仅供性能测试和评估使用。
## 联系方式
如有问题或建议,请提交 Issue 或联系项目维护者。
---
**版本**
: 1.0
**最后更新**
: 2026-02-26
\ No newline at end of file
codecbenchmark-py/requirments.txt
0 → 100644
View file @
0cb40ca1
ffmpeg-python
\ No newline at end of file
codecbenchmark-py/video_benchmark.py
0 → 100644
View file @
0cb40ca1
#!/usr/bin/env python3
import
argparse
import
subprocess
import
ffmpeg
import
os
import
re
import
csv
import
zipfile
import
sys
from
collections
import
defaultdict
from
benchmark_multi_inst
import
run_multi_instance_benchmark
# --- 配置数据 ---
DECODE_FILE_ARR
=
[
[
"sample_1920x1080_30fps_8bit.h264"
,
"1"
,
"812"
,
"H264"
,
"8"
,
"1920x1080"
],
]
ENCODE_FILE_ARR
=
[
[
"1280x720_nv12.yuv"
,
"677"
,
"H264"
,
"8"
,
"1280x720"
],
[
"1920x1080_nv12.yuv"
,
"812"
,
"H264"
,
"8"
,
"1920x1080"
],
[
"3840x2160_nv12.yuv"
,
"812"
,
"H264"
,
"8"
,
"3840x2160"
],
[
"7680x4320_nv12.yuv"
,
"77"
,
"H264"
,
"8"
,
"7680x4320"
],
[
"1280x720_nv12.yuv"
,
"677"
,
"HEVC"
,
"8"
,
"1280x720"
],
[
"1920x1080_nv12.yuv"
,
"812"
,
"HEVC"
,
"8"
,
"1920x1080"
],
[
"3840x2160_nv12.yuv"
,
"812"
,
"HEVC"
,
"8"
,
"3840x2160"
],
[
"7680x4320_nv12.yuv"
,
"77"
,
"HEVC"
,
"8"
,
"7680x4320"
],
[
"1280x720_p010le.yuv"
,
"677"
,
"HEVC"
,
"10"
,
"1280x720"
],
[
"1920x1080_p010le.yuv"
,
"812"
,
"HEVC"
,
"10"
,
"1920x1080"
],
[
"3840x2160_p010le.yuv"
,
"812"
,
"HEVC"
,
"10"
,
"3840x2160"
],
[
"1920x1080_nv12.yuv"
,
"1"
,
"JPEG_1"
,
"8"
,
"1920x1080"
],
[
"3840x2160_nv12.yuv"
,
"1"
,
"JPEG_1"
,
"8"
,
"3840x2160"
],
[
"4096x2160_nv12.yuv"
,
"1"
,
"JPEG_1"
,
"8"
,
"4096x2160"
],
[
"8192x4320_nv12.yuv"
,
"1"
,
"JPEG_1"
,
"8"
,
"8192x4320"
],
[
"1920x1080_nv12.yuv"
,
"2174"
,
"JPEG"
,
"8"
,
"1920x1080"
],
[
"1920x1080_nv12.yuv"
,
"1701"
,
"JPEG"
,
"8"
,
"1920x1080"
],
[
"1920x1080_nv12.yuv"
,
"2076"
,
"JPEG"
,
"8"
,
"1920x1080"
],
[
"1920x1080_nv12.yuv"
,
"1581"
,
"JPEG"
,
"8"
,
"1920x1080"
],
[
"1920x1080_nv12.yuv"
,
"2154"
,
"JPEG"
,
"8"
,
"1920x1080"
],
]
# 编解码器映射(键为小写)
NVENC_ENCODER_NAMES
=
{
"h264"
:
"h264_nvenc"
,
"hevc"
:
"hevc_nvenc"
,
"vp9"
:
"vp9_nvenc"
,
"mjpeg"
:
"mjpeg_nvenc"
,
}
VAAPI_ENCODER_NAMES
=
{
"h264"
:
"h264_vaapi"
,
"hevc"
:
"hevc_vaapi"
,
"vp9"
:
"vp9_vaapi"
,
"mjpeg"
:
"mjpeg_vaapi"
,
}
V4L2_CODEC_NAMES
=
{
"h264"
:
"h264_v4l2m2m"
,
"hevc"
:
"hevc_v4l2m2m"
,
"vp9"
:
"vp9_v4l2m2m"
,
"avs2"
:
"avs2_v4l2m2m"
,
"mjpeg"
:
"mjpeg_v4l2m2m"
,
}
CUVID_CODEC_NAMES
=
{
"h264"
:
"h264_cuvid"
,
"hevc"
:
"hevc_cuvid"
,
"vp9"
:
"vp9_cuvid"
,
"avs2"
:
"avs2_cuvid"
,
"mjpeg"
:
"mjpeg_cuvid"
,
}
# --- 辅助函数 ---
def
parse_ffmpeg_benchmark_times
(
log_content
):
"""解析 ffmpeg 日志获取所有 rtime 值(秒)"""
times
=
[]
# 匹配 rtime=0.123456 (benchmark 输出的浮点秒数)
time_matches
=
re
.
findall
(
r
'rtime=(\d+\.?\d*)'
,
log_content
)
for
time_str
in
time_matches
:
try
:
total_sec
=
float
(
time_str
)
times
.
append
(
total_sec
)
except
ValueError
:
continue
# 备选:匹配 time=00:00:00.00 (时间戳格式)
if
not
times
:
timestamp_matches
=
re
.
findall
(
r
'time=(\d+:\d+:\d+\.\d+)'
,
log_content
)
for
time_str
in
timestamp_matches
:
parts
=
time_str
.
split
(
':'
)
if
len
(
parts
)
==
3
:
h
,
m
,
s
=
parts
s_clean
=
s
.
rstrip
(
's'
)
try
:
total_sec
=
float
(
h
)
*
3600
+
float
(
m
)
*
60
+
float
(
s_clean
)
times
.
append
(
total_sec
)
except
ValueError
:
continue
return
times
def
parse_ffmpeg_log_frames
(
log_content
):
"""获取总帧数"""
frame_matches
=
re
.
findall
(
r
'frame=\s*(\d+)'
,
log_content
)
if
frame_matches
:
return
int
(
frame_matches
[
-
1
])
return
0
def
calculate_fps
(
frames
,
rtime
):
if
rtime
>
0
:
return
round
(
frames
/
rtime
,
2
)
return
0.0
def
run_ffmpeg_command
(
cmd
,
log_file_path
):
"""运行 ffmpeg 命令并将 stderr 保存到日志文件(即使失败也创建文件)"""
try
:
with
open
(
log_file_path
,
'w'
)
as
log_f
:
process
=
subprocess
.
run
(
cmd
,
stdout
=
subprocess
.
DEVNULL
,
stderr
=
log_f
,
text
=
True
)
return
process
.
returncode
except
Exception
as
e
:
# 即使出错也创建日志文件
with
open
(
log_file_path
,
'w'
)
as
log_f
:
log_f
.
write
(
f
"Error:
{
e
}
\n
"
)
return
-
1
def
extract_zip
():
video_benchmark_dir
=
"./video_benchmark"
zip_file
=
"video_benchmark.zip"
if
not
os
.
path
.
exists
(
video_benchmark_dir
):
if
os
.
path
.
exists
(
zip_file
):
print
(
f
"Extracting
{
zip_file
}
..."
)
with
zipfile
.
ZipFile
(
zip_file
,
'r'
)
as
zip_ref
:
zip_ref
.
extractall
(
video_benchmark_dir
)
else
:
print
(
f
"Warning:
{
zip_file
}
not found. Ensure test files exist in
{
video_benchmark_dir
}
"
)
else
:
print
(
"video_benchmark dir exist"
)
def
get_decoder_args
(
codec_type
,
file_ext
):
"""获取解码器参数"""
file_ext_lower
=
file_ext
.
lower
()
if
codec_type
==
"cuda"
:
return
{
"hwaccel"
:
"cuda"
}
elif
codec_type
==
"vaapi"
:
return
{
"hwaccel"
:
"vaapi"
}
elif
codec_type
==
"v4l2"
:
decoder
=
V4L2_CODEC_NAMES
.
get
(
file_ext_lower
,
file_ext_lower
)
return
{
"vcodec"
:
decoder
}
elif
codec_type
==
"cuvid"
:
decoder
=
CUVID_CODEC_NAMES
.
get
(
file_ext_lower
,
file_ext_lower
)
return
{
"vcodec"
:
decoder
}
return
{}
def
get_encoder_name
(
codec_type
,
file_ext
):
"""获取编码器名称(修复大小写问题)"""
# 转换为小写
file_ext_lower
=
file_ext
.
lower
()
# 特殊处理:H264 -> h264, HEVC -> hevc, JPEG -> mjpeg
if
file_ext_lower
in
[
"h264"
,
"hevc"
,
"vp9"
,
"avs2"
]:
codec_key
=
file_ext_lower
elif
file_ext_lower
in
[
"jpg"
,
"jpeg"
,
"avi"
,
"mjpeg"
]:
codec_key
=
"mjpeg"
else
:
codec_key
=
file_ext_lower
if
codec_type
==
"cuda"
:
return
NVENC_ENCODER_NAMES
.
get
(
codec_key
,
""
)
elif
codec_type
==
"vaapi"
:
return
VAAPI_ENCODER_NAMES
.
get
(
codec_key
,
""
)
elif
codec_type
==
"v4l2"
:
return
V4L2_CODEC_NAMES
.
get
(
codec_key
,
""
)
elif
codec_type
==
"cuvid"
:
return
CUVID_CODEC_NAMES
.
get
(
codec_key
,
""
)
return
""
def
run_single_decode_benchmark
(
input_file
,
codec_type
,
log_file
):
"""运行单次解码测试"""
file_ext
=
os
.
path
.
splitext
(
input_file
)[
1
].
replace
(
'.'
,
''
)
input_kwargs
=
get_decoder_args
(
codec_type
,
file_ext
)
try
:
stream
=
ffmpeg
.
input
(
input_file
,
**
input_kwargs
)
cmd
=
ffmpeg
.
output
(
stream
,
'pipe:'
,
format
=
'null'
).
global_args
(
'-benchmark'
,
'-y'
).
compile
()
run_ffmpeg_command
(
cmd
,
log_file
)
return
True
except
Exception
as
e
:
print
(
f
"FFmpeg command construction failed:
{
e
}
"
)
return
False
def
run_encode_benchmark
(
yuv_file
,
output_file
,
resolution
,
codec_type
,
file_ext
,
depth
,
log_file
):
"""运行编码测试"""
w
,
h
=
resolution
.
split
(
'x'
)
pix_fmt
=
"p010le"
if
"10"
in
str
(
depth
)
else
"nv12"
profile
=
"main10"
if
"10"
in
str
(
depth
)
else
None
# 修复:获取编码器名称(处理大小写)
encoder_name
=
get_encoder_name
(
codec_type
,
file_ext
)
if
not
encoder_name
:
print
(
f
"Unknown encoder for
{
file_ext
}
on
{
codec_type
}
"
)
# 即使失败也创建日志文件
with
open
(
log_file
,
'w'
)
as
f
:
f
.
write
(
f
"Error: Unknown encoder for
{
file_ext
}
on
{
codec_type
}
\n
"
)
return
False
try
:
input_kwargs
=
{
'format'
:
'rawvideo'
,
's'
:
f
"
{
w
}
x
{
h
}
"
,
'pix_fmt'
:
pix_fmt
}
global_args
=
[
'-benchmark'
,
'-y'
]
output_kwargs
=
{
'vcodec'
:
encoder_name
}
if
codec_type
==
"vaapi"
:
input_kwargs
[
'hwaccel'
]
=
'vaapi'
global_args
.
extend
([
'-vaapi_device'
,
'/dev/dri/renderD128'
])
output_kwargs
[
'vf'
]
=
'format=nv12,hwupload'
if
pix_fmt
==
'nv12'
else
'format=p010le,hwupload'
if
profile
:
output_kwargs
[
'profile'
]
=
profile
stream
=
ffmpeg
.
input
(
yuv_file
,
**
input_kwargs
)
cmd
=
ffmpeg
.
output
(
stream
,
output_file
,
**
output_kwargs
).
global_args
(
*
global_args
).
compile
()
run_ffmpeg_command
(
cmd
,
log_file
)
return
True
except
Exception
as
e
:
print
(
f
"Encode command failed:
{
e
}
"
)
# 即使失败也创建日志文件
with
open
(
log_file
,
'w'
)
as
f
:
f
.
write
(
f
"Error:
{
e
}
\n
"
)
return
False
def
aggregate_results
(
temp_csv
,
final_csv
,
is_encode
=
False
,
threads
=
6
):
"""聚合 CSV 结果"""
data
=
defaultdict
(
lambda
:
{
"sum_1core"
:
0.0
,
"sum_multi"
:
0.0
,
"count"
:
0
})
try
:
with
open
(
temp_csv
,
'r'
,
newline
=
''
)
as
f
:
reader
=
csv
.
reader
(
f
)
header
=
next
(
reader
)
for
row
in
reader
:
if
len
(
row
)
<
5
:
continue
fmt
=
row
[
2
].
strip
()
depth
=
row
[
3
].
strip
()
res
=
row
[
4
].
strip
()
try
:
fps_1
=
float
(
row
[
5
])
if
len
(
row
)
>
5
else
0.0
fps_multi
=
float
(
row
[
6
])
if
(
not
is_encode
and
len
(
row
)
>
6
)
else
0.0
except
ValueError
:
fps_1
=
0.0
fps_multi
=
0.0
key
=
f
"
{
fmt
}
,
{
depth
}
,
{
res
}
"
data
[
key
][
"sum_1core"
]
+=
fps_1
data
[
key
][
"sum_multi"
]
+=
fps_multi
data
[
key
][
"count"
]
+=
1
except
FileNotFoundError
:
print
(
f
"Warning:
{
temp_csv
}
not found."
)
return
with
open
(
final_csv
,
'w'
,
newline
=
''
)
as
f
:
writer
=
csv
.
writer
(
f
)
if
is_encode
:
writer
.
writerow
([
"Encode"
,
"Bit Depth"
,
"Resolution"
,
"Fps/(BMZ 1 Core)"
])
for
key
,
val
in
data
.
items
():
if
val
[
"count"
]
>
0
:
avg_1
=
val
[
"sum_1core"
]
/
val
[
"count"
]
parts
=
key
.
split
(
','
)
writer
.
writerow
([
parts
[
0
],
parts
[
1
],
parts
[
2
],
f
"
{
avg_1
:.
2
f
}
"
])
else
:
writer
.
writerow
([
"Decode"
,
"Bit Depth"
,
"Resolution"
,
"Fps/(BMZ 1 Core)"
,
f
"Fps/(BMZ
{
threads
}
Processes)"
])
for
key
,
val
in
data
.
items
():
if
val
[
"count"
]
>
0
:
avg_1
=
val
[
"sum_1core"
]
/
val
[
"count"
]
avg_multi
=
val
[
"sum_multi"
]
/
val
[
"count"
]
parts
=
key
.
split
(
','
)
writer
.
writerow
([
parts
[
0
],
parts
[
1
],
parts
[
2
],
f
"
{
avg_1
:.
2
f
}
"
,
f
"
{
avg_multi
:.
2
f
}
"
])
def
main
():
parser
=
argparse
.
ArgumentParser
(
description
=
"Video Benchmark Script"
)
parser
.
add_argument
(
"-c"
,
"--codec"
,
default
=
"cuda"
,
choices
=
[
"cuda"
,
"cuvid"
,
"v4l2"
,
"vaapi"
],
help
=
"Codec backend"
)
parser
.
add_argument
(
"-n"
,
"--threads"
,
type
=
int
,
default
=
6
,
help
=
"Number of concurrent processes"
)
args
=
parser
.
parse_args
()
CODEC
=
args
.
codec
THREADS
=
args
.
threads
video_decode_benchmark_temp
=
f
"video_
{
CODEC
}
_decode_benchmark_temp.csv"
video_decode_benchmark
=
f
"video_
{
CODEC
}
_decode_benchmark.csv"
video_encode_benchmark_temp
=
f
"video_
{
CODEC
}
_encode_benchmark_temp.csv"
video_encode_benchmark
=
f
"video_
{
CODEC
}
_encode_benchmark.csv"
for
f
in
[
video_decode_benchmark_temp
,
video_encode_benchmark_temp
]:
if
os
.
path
.
exists
(
f
):
os
.
remove
(
f
)
with
open
(
video_decode_benchmark_temp
,
'w'
,
newline
=
''
)
as
f
:
writer
=
csv
.
writer
(
f
)
writer
.
writerow
([
"Test File"
,
"Total Frames"
,
"Decode"
,
"Bit Depth"
,
"Resolution"
,
"Fps/(BMZ 1 Core)"
,
f
"Fps/(BMZ
{
THREADS
}
Processes)"
])
with
open
(
video_encode_benchmark_temp
,
'w'
,
newline
=
''
)
as
f
:
writer
=
csv
.
writer
(
f
)
writer
.
writerow
([
"Test File"
,
"Total Frames"
,
"Encode"
,
"Bit Depth"
,
"Resolution"
,
"Fps/(BMZ 1 Core)"
])
extract_zip
()
base_dir
=
"./video_benchmark"
if
not
DECODE_FILE_ARR
:
print
(
"Error: DECODE_FILE_ARR is empty. Please uncomment test files in script."
)
return
for
i
,
file_info
in
enumerate
(
DECODE_FILE_ARR
):
filename
,
encode_idx
,
frames
,
format_name
,
depth
,
resolution
=
file_info
filepath
=
os
.
path
.
join
(
base_dir
,
filename
)
if
not
os
.
path
.
exists
(
filepath
):
print
(
f
"Skip missing file:
{
filepath
}
"
)
continue
file_ext
=
os
.
path
.
splitext
(
filename
)[
1
].
replace
(
'.'
,
''
)
base_name
=
os
.
path
.
splitext
(
filename
)[
0
]
# 中间生成的 YUV 文件路径
yuv_filename
=
os
.
path
.
join
(
base_dir
,
base_name
+
".yuv"
)
decode_log
=
os
.
path
.
join
(
base_dir
,
base_name
+
".log"
)
multi_log
=
os
.
path
.
join
(
base_dir
,
base_name
+
"_multi.log"
)
encode_log
=
os
.
path
.
join
(
base_dir
,
base_name
+
"_encode.log"
)
# --- 修改点 1: 编码输出文件改为临时文件,避免覆盖原文件 ---
# 原代码:encode_result = os.path.join(base_dir, filename)
# 新代码:添加 _encoded_tmp 后缀,确保不与原文件冲突
encode_result
=
os
.
path
.
join
(
base_dir
,
f
"
{
base_name
}
_encoded_tmp.
{
file_ext
}
"
)
print
(
f
"Processing:
{
filename
}
(Idx:
{
encode_idx
}
)"
)
# --- 解码测试 ---
success
=
run_single_decode_benchmark
(
filepath
,
CODEC
,
decode_log
)
fps_single
=
0.0
fps_multi
=
0.0
error_occurred
=
False
if
not
success
or
not
os
.
path
.
exists
(
decode_log
)
or
os
.
path
.
getsize
(
decode_log
)
==
0
:
error_occurred
=
True
print
(
f
" -> Single instance failed or empty log"
)
else
:
with
open
(
decode_log
,
'r'
)
as
f
:
log_content
=
f
.
read
()
d_times
=
parse_ffmpeg_benchmark_times
(
log_content
)
d_frames
=
parse_ffmpeg_log_frames
(
log_content
)
d_rtime
=
d_times
[
0
]
if
d_times
else
0.0
if
d_rtime
>
0
:
fps_single
=
calculate_fps
(
d_frames
,
d_rtime
)
else
:
error_occurred
=
True
print
(
f
" -> Failed to parse rtime from single log"
)
# 多实例
if
not
error_occurred
:
run_multi_instance_benchmark
(
filepath
,
CODEC
,
THREADS
,
multi_log
)
with
open
(
multi_log
,
'r'
)
as
f
:
multi_log_content
=
f
.
read
()
m_times
=
parse_ffmpeg_benchmark_times
(
multi_log_content
)
if
m_times
:
avg_rtime
=
sum
(
m_times
)
/
len
(
m_times
)
fps_multi
=
calculate_fps
(
d_frames
,
avg_rtime
)
*
THREADS
else
:
fps_multi
=
0.0
print
(
f
" -> Failed to parse rtime from multi log"
)
if
error_occurred
:
fps_single
=
0.0
fps_multi
=
0.0
with
open
(
video_decode_benchmark_temp
,
'a'
,
newline
=
''
)
as
f
:
writer
=
csv
.
writer
(
f
)
writer
.
writerow
([
filename
,
frames
,
format_name
.
strip
(),
depth
.
strip
(),
resolution
.
strip
(),
fps_single
,
fps_multi
])
# --- 编码测试 ---
if
encode_idx
!=
"n/a"
and
not
error_occurred
:
try
:
idx
=
int
(
encode_idx
)
if
0
<=
idx
<
len
(
ENCODE_FILE_ARR
):
enc_info
=
ENCODE_FILE_ARR
[
idx
]
enc_yuv_name
=
enc_info
[
0
]
enc_frames
=
enc_info
[
1
]
enc_format
=
enc_info
[
2
]
enc_depth
=
enc_info
[
3
]
enc_resolution
=
enc_info
[
4
]
print
(
f
"Encoding:
{
filename
}
->
{
enc_format
}
"
)
# 生成 YUV(使用与解码测试相同的解码器参数)
if
not
os
.
path
.
exists
(
yuv_filename
):
try
:
input_kwargs
=
get_decoder_args
(
CODEC
,
file_ext
)
stream
=
ffmpeg
.
input
(
filepath
,
**
input_kwargs
)
cmd
=
ffmpeg
.
output
(
stream
,
yuv_filename
,
pix_fmt
=
'nv12'
).
global_args
(
'-y'
).
compile
()
subprocess
.
run
(
cmd
,
stdout
=
subprocess
.
DEVNULL
,
stderr
=
subprocess
.
DEVNULL
)
except
Exception
as
e
:
print
(
f
"YUV conversion failed:
{
e
}
"
)
enc_fps
=
0.0
with
open
(
video_encode_benchmark_temp
,
'a'
,
newline
=
''
)
as
f
:
csv
.
writer
(
f
).
writerow
([
enc_yuv_name
,
enc_frames
,
enc_format
.
strip
(),
enc_depth
.
strip
(),
enc_resolution
.
strip
(),
enc_fps
])
continue
# 编码 - 使用 Encode_File_Arr 中的格式
# 注意:输出文件现在是 encode_result (临时文件),不会覆盖 filepath (原文件)
run_encode_benchmark
(
yuv_filename
,
encode_result
,
enc_resolution
,
CODEC
,
enc_format
.
strip
(),
enc_depth
,
encode_log
)
# 检查日志文件是否存在
if
os
.
path
.
exists
(
encode_log
):
with
open
(
encode_log
,
'r'
)
as
f
:
enc_log_content
=
f
.
read
()
e_frames
=
parse_ffmpeg_log_frames
(
enc_log_content
)
e_times
=
parse_ffmpeg_benchmark_times
(
enc_log_content
)
e_rtime
=
e_times
[
0
]
if
e_times
else
0.0
enc_fps
=
calculate_fps
(
e_frames
,
e_rtime
)
else
:
enc_fps
=
0.0
print
(
f
" -> Encode log file not created"
)
with
open
(
video_encode_benchmark_temp
,
'a'
,
newline
=
''
)
as
f
:
writer
=
csv
.
writer
(
f
)
writer
.
writerow
([
enc_yuv_name
,
enc_frames
,
enc_format
.
strip
(),
enc_depth
.
strip
(),
enc_resolution
.
strip
(),
enc_fps
])
# --- 修改点 2: 清理临时文件 ---
# 现在 encode_result 是临时文件,可以安全删除,不会误删原文件
for
f_to_rm
in
[
yuv_filename
,
encode_result
,
encode_log
]:
if
os
.
path
.
exists
(
f_to_rm
):
os
.
remove
(
f_to_rm
)
except
ValueError
:
pass
# 清理日志
for
f_to_rm
in
[
decode_log
,
multi_log
]:
if
os
.
path
.
exists
(
f_to_rm
):
os
.
remove
(
f_to_rm
)
print
(
"Aggregating results..."
)
aggregate_results
(
video_decode_benchmark_temp
,
video_decode_benchmark
,
is_encode
=
False
,
threads
=
THREADS
)
aggregate_results
(
video_encode_benchmark_temp
,
video_encode_benchmark
,
is_encode
=
True
,
threads
=
THREADS
)
for
f
in
[
video_decode_benchmark_temp
,
video_encode_benchmark_temp
]:
if
os
.
path
.
exists
(
f
):
os
.
remove
(
f
)
print
(
"Benchmark finished."
)
if
__name__
==
"__main__"
:
main
()
\ No newline at end of file
codecbenchmark-py/video_benchmark/sample_1920x1080_30fps_8bit.h264
0 → 100644
View file @
0cb40ca1
File added
codecbenchmark-py/video_cuda_decode_benchmark.csv
0 → 100644
View file @
0cb40ca1
Decode,Bit Depth,Resolution,Fps/(BMZ 1 Core),Fps/(BMZ 6 Processes)
H264,8,1920x1080,506.87,2277.36
codecbenchmark-py/video_cuda_encode_benchmark.csv
0 → 100644
View file @
0cb40ca1
Encode,Bit Depth,Resolution,Fps/(BMZ 1 Core)
H264,8,1920x1080,251.78
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment