HwCodecDetect 0.1.9__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hwcodecdetect-0.1.9/src/HwCodecDetect.egg-info → hwcodecdetect-0.2.0}/PKG-INFO +21 -1
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/README.md +20 -0
- hwcodecdetect-0.2.0/VERSION +1 -0
- hwcodecdetect-0.2.0/src/HwCodecDetect/bitdepth_chroma_detect.py +473 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/src/HwCodecDetect/run_tests.py +20 -0
- hwcodecdetect-0.2.0/src/HwCodecDetect/version.py +1 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0/src/HwCodecDetect.egg-info}/PKG-INFO +21 -1
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/src/HwCodecDetect.egg-info/SOURCES.txt +1 -0
- hwcodecdetect-0.1.9/VERSION +0 -1
- hwcodecdetect-0.1.9/src/HwCodecDetect/version.py +0 -1
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/LICENSE +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/MANIFEST.in +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/pyproject.toml +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/requirements.txt +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/setup.cfg +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/setup.py +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/src/HwCodecDetect/__init__.py +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/src/HwCodecDetect/install_ffmpeg_if_needed.py +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/src/HwCodecDetect.egg-info/dependency_links.txt +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/src/HwCodecDetect.egg-info/entry_points.txt +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/src/HwCodecDetect.egg-info/requires.txt +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/src/HwCodecDetect.egg-info/top_level.txt +0 -0
- {hwcodecdetect-0.1.9 → hwcodecdetect-0.2.0}/src/HwCodecDetect.egg-info/zip-safe +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: HwCodecDetect
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A cross-platform tool to automatically detect and test hardware video decoders/encoders using FFmpeg.
|
|
5
5
|
Home-page: https://github.com/whyb/HwCodecDetect
|
|
6
6
|
Author: whyb
|
|
@@ -74,6 +74,26 @@ The script automatically detect and reports on the following major hardware deco
|
|
|
74
74
|
| Vulkan | H.264、H.265、AV1 |
|
|
75
75
|
| Apple VideoToolbox | H.264、H.265、MPEG-2、MPEG-4 |
|
|
76
76
|
|
|
77
|
+
### Bit-depth and Chroma Subsampling Detection
|
|
78
|
+
In addition to resolution-based testing, the tool now includes comprehensive bit-depth and chroma subsampling detection. This feature tests hardware codec support for different pixel formats, helping you understand the full capabilities of your hardware encoders and decoders.
|
|
79
|
+
|
|
80
|
+
The detection covers the following pixel formats:
|
|
81
|
+
| Bit-depth | Chroma Subsampling | Pixel Format | Description |
|
|
82
|
+
|-----------|-------------------|--------------|-------------|
|
|
83
|
+
| 8-bit | YUV 4:2:0 | yuv420p | Standard 8-bit 4:2:0 |
|
|
84
|
+
| 8-bit | YUV 4:2:2 | yuv422p | 8-bit 4:2:2 |
|
|
85
|
+
| 8-bit | YUV 4:4:4 | yuv444p | 8-bit 4:4:4 |
|
|
86
|
+
| 10-bit | YUV 4:2:0 | yuv420p10le, p010le | 10-bit 4:2:0 |
|
|
87
|
+
| 10-bit | YUV 4:2:2 | yuv422p10le | 10-bit 4:2:2 |
|
|
88
|
+
| 10-bit | YUV 4:4:4 | yuv444p10le | 10-bit 4:4:4 |
|
|
89
|
+
| 12-bit | YUV 4:2:0 | yuv420p12le | 12-bit 4:2:0 |
|
|
90
|
+
| 12-bit | YUV 4:2:2 | yuv422p12le | 12-bit 4:2:2 |
|
|
91
|
+
| 12-bit | YUV 4:4:4 | yuv444p12le | 12-bit 4:4:4 |
|
|
92
|
+
|
|
93
|
+
This feature uses a fixed resolution of 1280x720 for all tests and follows the encode-then-decode workflow. If hardware encoding fails, the tool automatically falls back to software encoding to ensure decoder tests can still be performed.
|
|
94
|
+
|
|
95
|
+
**Note:** This feature is enabled by default. You can disable it using the `--no-bitdepth-chroma` command-line parameter.
|
|
96
|
+
|
|
77
97
|
|
|
78
98
|
## How to Use
|
|
79
99
|
You can install and use HwCodecDetect in two ways.
|
|
@@ -48,6 +48,26 @@ The script automatically detect and reports on the following major hardware deco
|
|
|
48
48
|
| Vulkan | H.264、H.265、AV1 |
|
|
49
49
|
| Apple VideoToolbox | H.264、H.265、MPEG-2、MPEG-4 |
|
|
50
50
|
|
|
51
|
+
### Bit-depth and Chroma Subsampling Detection
|
|
52
|
+
In addition to resolution-based testing, the tool now includes comprehensive bit-depth and chroma subsampling detection. This feature tests hardware codec support for different pixel formats, helping you understand the full capabilities of your hardware encoders and decoders.
|
|
53
|
+
|
|
54
|
+
The detection covers the following pixel formats:
|
|
55
|
+
| Bit-depth | Chroma Subsampling | Pixel Format | Description |
|
|
56
|
+
|-----------|-------------------|--------------|-------------|
|
|
57
|
+
| 8-bit | YUV 4:2:0 | yuv420p | Standard 8-bit 4:2:0 |
|
|
58
|
+
| 8-bit | YUV 4:2:2 | yuv422p | 8-bit 4:2:2 |
|
|
59
|
+
| 8-bit | YUV 4:4:4 | yuv444p | 8-bit 4:4:4 |
|
|
60
|
+
| 10-bit | YUV 4:2:0 | yuv420p10le, p010le | 10-bit 4:2:0 |
|
|
61
|
+
| 10-bit | YUV 4:2:2 | yuv422p10le | 10-bit 4:2:2 |
|
|
62
|
+
| 10-bit | YUV 4:4:4 | yuv444p10le | 10-bit 4:4:4 |
|
|
63
|
+
| 12-bit | YUV 4:2:0 | yuv420p12le | 12-bit 4:2:0 |
|
|
64
|
+
| 12-bit | YUV 4:2:2 | yuv422p12le | 12-bit 4:2:2 |
|
|
65
|
+
| 12-bit | YUV 4:4:4 | yuv444p12le | 12-bit 4:4:4 |
|
|
66
|
+
|
|
67
|
+
This feature uses a fixed resolution of 1280x720 for all tests and follows the encode-then-decode workflow. If hardware encoding fails, the tool automatically falls back to software encoding to ensure decoder tests can still be performed.
|
|
68
|
+
|
|
69
|
+
**Note:** This feature is enabled by default. You can disable it using the `--no-bitdepth-chroma` command-line parameter.
|
|
70
|
+
|
|
51
71
|
|
|
52
72
|
## How to Use
|
|
53
73
|
You can install and use HwCodecDetect in two ways.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.2.0
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bit-depth and Chroma Subsampling Detection Module
|
|
3
|
+
This module tests hardware codec support for different pixel formats.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
import shlex
|
|
9
|
+
import subprocess
|
|
10
|
+
import tempfile
|
|
11
|
+
from collections import defaultdict
|
|
12
|
+
from colorama import init, Fore, Style
|
|
13
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
14
|
+
from tqdm import tqdm
|
|
15
|
+
|
|
16
|
+
init(autoreset=True)
|
|
17
|
+
|
|
18
|
+
# Fixed resolution for bit-depth and chroma tests
|
|
19
|
+
BITDEPTH_CHROMA_RESOLUTION = "1280x720"
|
|
20
|
+
|
|
21
|
+
# Pixel format definitions: (pix_fmt_name, bit_depth, chroma_sampling, description)
|
|
22
|
+
PIXEL_FORMATS = [
|
|
23
|
+
("yuv420p", 8, "4:2:0", "8-bit YUV 4:2:0"),
|
|
24
|
+
("yuv422p", 8, "4:2:2", "8-bit YUV 4:2:2"),
|
|
25
|
+
("yuv444p", 8, "4:4:4", "8-bit YUV 4:4:4"),
|
|
26
|
+
("yuv420p10le", 10, "4:2:0", "10-bit YUV 4:2:0"),
|
|
27
|
+
("yuv422p10le", 10, "4:2:2", "10-bit YUV 4:2:2"),
|
|
28
|
+
("yuv444p10le", 10, "4:4:4", "10-bit YUV 4:4:4"),
|
|
29
|
+
("yuv420p12le", 12, "4:2:0", "12-bit YUV 4:2:0"),
|
|
30
|
+
("yuv422p12le", 12, "4:2:2", "12-bit YUV 4:2:2"),
|
|
31
|
+
("yuv444p12le", 12, "4:4:4", "12-bit YUV 4:4:4"),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
# Encoder titles (same as main module)
|
|
35
|
+
ENCODER_TITLES = {
|
|
36
|
+
("h264_nvenc", "h264"): "NVIDIA Hardware H264 Encoder(NVEnc)",
|
|
37
|
+
("hevc_nvenc", "h265"): "NVIDIA Hardware H265 Encoder(NVEnc)",
|
|
38
|
+
("av1_nvenc", "av1"): "NVIDIA Hardware AV1 Encoder(NVEnc)",
|
|
39
|
+
("h264_qsv", "h264"): "Intel Hardware H264 Encoder(QSV)",
|
|
40
|
+
("hevc_qsv", "h265"): "Intel Hardware H265 Encoder(QSV)",
|
|
41
|
+
("av1_qsv", "av1"): "Intel Hardware AV1 Encoder(QSV)",
|
|
42
|
+
("mpeg2_qsv", "mpeg2"): "Intel Hardware MPEG-2 Encoder(QSV)",
|
|
43
|
+
("vp9_qsv", "vp9"): "Intel Hardware VP9 Encoder(QSV)",
|
|
44
|
+
("h264_amf", "h264"): "AMD Hardware H264 Encoder(AMF)",
|
|
45
|
+
("hevc_amf", "h265"): "AMD Hardware H265 Encoder(AMF)",
|
|
46
|
+
("av1_amf", "av1"): "AMD Hardware AV1 Encoder(AMF)",
|
|
47
|
+
("h264_mf", "h264"): "Microsoft Hardware H264 Encoder(MediaFoundation)",
|
|
48
|
+
("hevc_mf", "h265"): "Microsoft Hardware H265 Encoder(MediaFoundation)",
|
|
49
|
+
("hevc_d3d12va", "h265"): "Microsoft Direct3D 12 Video Acceleration H265 Encoder(D3D12VA)",
|
|
50
|
+
("h264_vaapi", "h264"): "Video Acceleration H264 Encoder(VAAPI)",
|
|
51
|
+
("hevc_vaapi", "h265"): "Video Acceleration H265 Encoder(VAAPI)",
|
|
52
|
+
("av1_vaapi", "av1"): "Video Acceleration AV1 Encoder(VAAPI)",
|
|
53
|
+
("mpeg2_vaapi", "mpeg2"): "Video Acceleration MPEG-2 Encoder(VAAPI)",
|
|
54
|
+
("vp8_vaapi", "vp8"): "Video Acceleration VP8 Encoder(VAAPI)",
|
|
55
|
+
("vp9_vaapi", "vp9"): "Video Acceleration VP9 Encoder(VAAPI)",
|
|
56
|
+
("h264_vulkan", "h264"): "Vulkan Hardware H264 Encoder(Vulkan)",
|
|
57
|
+
("hevc_vulkan", "h265"): "Vulkan Hardware H265 Encoder(Vulkan)",
|
|
58
|
+
("h264_videotoolbox", "h264"): "MacOS Hardware H264 Encoder(VideoToolbox)",
|
|
59
|
+
("hevc_videotoolbox", "h265"): "MacOS Hardware H265 Encoder(VideoToolbox)",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Decoder titles (same as main module)
|
|
63
|
+
DECODER_TITLES = {
|
|
64
|
+
("h264_cuvid", "h264"): "NVIDIA CUDA H264 Decoder(NVDEC)",
|
|
65
|
+
("h264_qsv", "h264"): "Intel Quick Sync Video H264 Decoder(QSV)",
|
|
66
|
+
("hevc_cuvid", "h265"): "NVIDIA CUDA H265 Decoder(NVDEC)",
|
|
67
|
+
("hevc_qsv", "h265"): "Intel Quick Sync Video H265 Decoder(QSV)",
|
|
68
|
+
("av1_cuvid", "av1"): "NVIDIA CUDA AV1 Decoder(NVDEC)",
|
|
69
|
+
("av1_qsv", "av1"): "Intel Quick Sync Video AV1 Decoder(QSV)",
|
|
70
|
+
("mpeg1_cuvid", "mpeg1"): "NVIDIA CUDA MPEG-1 Decoder(NVDEC)",
|
|
71
|
+
("mpeg2_cuvid", "mpeg2"): "NVIDIA CUDA MPEG-2 Decoder(NVDEC)",
|
|
72
|
+
("mpeg2_qsv", "mpeg2"): "Intel Quick Sync Video MPEG-2 Decoder(QSV)",
|
|
73
|
+
("mpeg4_cuvid", "mpeg4"): "NVIDIA CUDA MPEG-4 Decoder(NVDEC)",
|
|
74
|
+
("vp8_cuvid", "vp8"): "NVIDIA CUDA VP8 Decoder(NVDEC)",
|
|
75
|
+
("vp8_qsv", "vp8"): "Intel Quick Sync Video VP8 Decoder(QSV)",
|
|
76
|
+
("vp9_cuvid", "vp9"): "NVIDIA CUDA VP9 Decoder(NVDEC)",
|
|
77
|
+
("vp9_qsv", "vp9"): "Intel Quick Sync Video VP9 Decoder(QSV)",
|
|
78
|
+
("dxva2", "h264"): "Microsoft DirectX Video Acceleration H264 Decoder(DXVA2)",
|
|
79
|
+
("dxva2", "h265"): "Microsoft DirectX Video Acceleration H265 Decoder(DXVA2)",
|
|
80
|
+
("dxva2", "av1"): "Microsoft DirectX Video Acceleration AV1 Decoder(DXVA2)",
|
|
81
|
+
("dxva2", "mpeg1"): "Microsoft DirectX Video Acceleration MPEG-1 Decoder(DXVA2)",
|
|
82
|
+
("dxva2", "mpeg2"): "Microsoft DirectX Video Acceleration MPEG-2 Decoder(DXVA2)",
|
|
83
|
+
("dxva2", "mpeg4"): "Microsoft DirectX Video Acceleration MPEG-4 Decoder(DXVA2)",
|
|
84
|
+
("dxva2", "vp8"): "Microsoft DirectX Video Acceleration VP8 Decoder(DXVA2)",
|
|
85
|
+
("dxva2", "vp9"): "Microsoft DirectX Video Acceleration VP9 Decoder(DXVA2)",
|
|
86
|
+
("d3d11va", "h264"): "Microsoft Direct3D 11 Video Acceleration H264 Decoder(D3D11VA)",
|
|
87
|
+
("d3d11va", "h265"): "Microsoft Direct3D 11 Video Acceleration H265 Decoder(D3D11VA)",
|
|
88
|
+
("d3d11va", "av1"): "Microsoft Direct3D 11 Video Acceleration AV1 Decoder(D3D11VA)",
|
|
89
|
+
("d3d11va", "mpeg1"): "Microsoft Direct3D 11 Video Acceleration MPEG-1 Decoder(D3D11VA)",
|
|
90
|
+
("d3d11va", "mpeg2"): "Microsoft Direct3D 11 Video Acceleration MPEG-2 Decoder(D3D11VA)",
|
|
91
|
+
("d3d11va", "mpeg4"): "Microsoft Direct3D 11 Video Acceleration MPEG-4 Decoder(D3D11VA)",
|
|
92
|
+
("d3d11va", "vp8"): "Microsoft Direct3D 11 Video Acceleration VP8 Decoder(D3D11VA)",
|
|
93
|
+
("d3d11va", "vp9"): "Microsoft Direct3D 11 Video Acceleration VP9 Decoder(D3D11VA)",
|
|
94
|
+
("vulkan", "h264"): "Vulkan Hardware H264 Decoder(Vulkan)",
|
|
95
|
+
("vulkan", "h265"): "Vulkan Hardware H265 Decoder(Vulkan)",
|
|
96
|
+
("vulkan", "av1"): "Vulkan Hardware AV1 Decoder(Vulkan)",
|
|
97
|
+
("videotoolbox", "h264"): "MacOS Hardware H264 Decoder(VideoToolbox)",
|
|
98
|
+
("videotoolbox", "h265"): "MacOS Hardware H265 Decoder(VideoToolbox)",
|
|
99
|
+
("videotoolbox", "mpeg2"): "MacOS Hardware MPEG-2 Decoder(VideoToolbox)",
|
|
100
|
+
("videotoolbox", "mpeg4"): "MacOS Hardware MPEG-4 Decoder(VideoToolbox)",
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Encoder definitions (same as main module)
|
|
104
|
+
ENCODERS = {
|
|
105
|
+
"h264": {"lib": "libx264", "hw_encoders": ["h264_nvenc", "h264_qsv", "h264_amf", "h264_mf", "h264_vaapi", "h264_vulkan", "h264_videotoolbox"]},
|
|
106
|
+
"h265": {"lib": "libx265", "hw_encoders": ["hevc_nvenc", "hevc_qsv", "hevc_amf", "hevc_mf", "hevc_d3d12va", "hevc_vaapi", "hevc_vulkan", "hevc_videotoolbox"]},
|
|
107
|
+
"av1": {"lib": "librav1e", "hw_encoders": ["av1_nvenc", "av1_qsv", "av1_amf", "av1_vaapi"]},
|
|
108
|
+
"mpeg2": {"lib": "mpeg2video", "hw_encoders": ["mpeg2_qsv", "mpeg2_vaapi"]},
|
|
109
|
+
"vp8": {"lib": "libvpx", "hw_encoders": ["vp8_vaapi"]},
|
|
110
|
+
"vp9": {"lib": "libvpx-vp9", "hw_encoders": ["vp9_qsv", "vp9_vaapi"]},
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Decoder definitions (same as main module)
|
|
114
|
+
DECODERS = {
|
|
115
|
+
"h264": {"lib": "libx264", "hw_decoders": ["h264_cuvid", "h264_qsv", "dxva2", "d3d11va", "vulkan", "videotoolbox"]},
|
|
116
|
+
"h265": {"lib": "libx265", "hw_decoders": ["hevc_cuvid", "hevc_qsv", "d3d11va", "vulkan", "videotoolbox"]},
|
|
117
|
+
"av1": {"lib": "librav1e", "hw_decoders": ["av1_cuvid", "av1_qsv", "dxva2", "d3d11va", "vulkan"]},
|
|
118
|
+
"mpeg1": {"lib": "mpeg1video", "hw_decoders": ["mpeg1_cuvid", "dxva2", "d3d11va"]},
|
|
119
|
+
"mpeg2": {"lib": "mpeg2video", "hw_decoders": ["mpeg2_cuvid", "mpeg2_qsv", "dxva2", "d3d11va", "videotoolbox"]},
|
|
120
|
+
"mpeg4": {"lib": "mpeg4", "hw_decoders": ["mpeg4_cuvid", "dxva2", "d3d11va", "videotoolbox"]},
|
|
121
|
+
"vp8": {"lib": "libvpx", "hw_decoders": ["vp8_cuvid", "vp8_qsv", "dxva2", "d3d11va"]},
|
|
122
|
+
"vp9": {"lib": "libvpx-vp9", "hw_decoders": ["vp9_cuvid", "vp9_qsv", "dxva2", "d3d11va"]},
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _run_ffmpeg_command(command, verbose):
|
|
127
|
+
"""Executes an FFmpeg command and returns True on success, False on failure."""
|
|
128
|
+
try:
|
|
129
|
+
stdout = subprocess.PIPE if verbose else subprocess.DEVNULL
|
|
130
|
+
stderr = subprocess.PIPE if verbose else subprocess.DEVNULL
|
|
131
|
+
result = subprocess.run(
|
|
132
|
+
command,
|
|
133
|
+
check=True,
|
|
134
|
+
stdout=stdout,
|
|
135
|
+
stderr=stderr,
|
|
136
|
+
text=True
|
|
137
|
+
)
|
|
138
|
+
return (result.returncode == 0, result.stdout, result.stderr)
|
|
139
|
+
except subprocess.CalledProcessError as e:
|
|
140
|
+
return (False, e.stdout, e.stderr)
|
|
141
|
+
except FileNotFoundError:
|
|
142
|
+
return (False, "", "FFmpeg executable not found")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _run_encoder_bitdepth_test(test_data):
|
|
146
|
+
"""Tests encoder support for a specific pixel format."""
|
|
147
|
+
codec, encoder, pix_fmt, bit_depth, chroma, test_dir, verbose = test_data
|
|
148
|
+
|
|
149
|
+
file_ext = ".webm" if codec in ["vp8", "vp9"] else ".mp4"
|
|
150
|
+
output_file = os.path.join(test_dir, f"{encoder}_{pix_fmt}{file_ext}")
|
|
151
|
+
|
|
152
|
+
# Determine pixel format for output based on input format
|
|
153
|
+
if bit_depth == 8:
|
|
154
|
+
if chroma == "4:2:0":
|
|
155
|
+
out_pix_fmt = "yuv420p"
|
|
156
|
+
elif chroma == "4:2:2":
|
|
157
|
+
out_pix_fmt = "yuv422p"
|
|
158
|
+
else: # 4:4:4
|
|
159
|
+
out_pix_fmt = "yuv444p"
|
|
160
|
+
elif bit_depth == 10:
|
|
161
|
+
if chroma == "4:2:0":
|
|
162
|
+
out_pix_fmt = "p010le"
|
|
163
|
+
elif chroma == "4:2:2":
|
|
164
|
+
out_pix_fmt = "yuv422p10le"
|
|
165
|
+
else: # 4:4:4
|
|
166
|
+
out_pix_fmt = "yuv444p10le"
|
|
167
|
+
else: # 12-bit
|
|
168
|
+
if chroma == "4:2:0":
|
|
169
|
+
out_pix_fmt = "yuv420p12le"
|
|
170
|
+
elif chroma == "4:2:2":
|
|
171
|
+
out_pix_fmt = "yuv422p12le"
|
|
172
|
+
else: # 4:4:4
|
|
173
|
+
out_pix_fmt = "yuv444p12le"
|
|
174
|
+
|
|
175
|
+
if "vulkan" in encoder:
|
|
176
|
+
command = [
|
|
177
|
+
"ffmpeg",
|
|
178
|
+
"-loglevel", "quiet",
|
|
179
|
+
"-hide_banner",
|
|
180
|
+
"-y",
|
|
181
|
+
"-init_hw_device", "vulkan=vk:0",
|
|
182
|
+
"-f", "lavfi",
|
|
183
|
+
"-i", f"color=white:s={BITDEPTH_CHROMA_RESOLUTION}:d=1",
|
|
184
|
+
"-frames:v", "1",
|
|
185
|
+
"-vf", f"format={pix_fmt},hwupload,format=vulkan",
|
|
186
|
+
"-c:v", encoder,
|
|
187
|
+
output_file,
|
|
188
|
+
]
|
|
189
|
+
elif "d3d12va" in encoder:
|
|
190
|
+
command = [
|
|
191
|
+
"ffmpeg",
|
|
192
|
+
"-loglevel", "quiet",
|
|
193
|
+
"-hide_banner",
|
|
194
|
+
"-y",
|
|
195
|
+
"-init_hw_device", "d3d12va:0",
|
|
196
|
+
"-f", "lavfi",
|
|
197
|
+
"-i", f"color=white:s={BITDEPTH_CHROMA_RESOLUTION}:d=1",
|
|
198
|
+
"-frames:v", "1",
|
|
199
|
+
"-vf", f"format={pix_fmt},hwupload",
|
|
200
|
+
"-c:v", encoder,
|
|
201
|
+
output_file,
|
|
202
|
+
]
|
|
203
|
+
else:
|
|
204
|
+
command = [
|
|
205
|
+
"ffmpeg",
|
|
206
|
+
"-loglevel", "quiet",
|
|
207
|
+
"-hide_banner",
|
|
208
|
+
"-y",
|
|
209
|
+
"-f", "lavfi",
|
|
210
|
+
"-i", f"color=white:s={BITDEPTH_CHROMA_RESOLUTION}:d=1",
|
|
211
|
+
"-frames:v", "1",
|
|
212
|
+
"-c:v", encoder,
|
|
213
|
+
"-pix_fmt", pix_fmt,
|
|
214
|
+
output_file,
|
|
215
|
+
]
|
|
216
|
+
|
|
217
|
+
if "qsv" in encoder:
|
|
218
|
+
command.insert(9, "-dual_gfx")
|
|
219
|
+
command.insert(10, "0")
|
|
220
|
+
|
|
221
|
+
if verbose:
|
|
222
|
+
command[2] = "error"
|
|
223
|
+
|
|
224
|
+
success, stdout, stderr = _run_ffmpeg_command(command, verbose)
|
|
225
|
+
status = "succeeded" if success else "failed"
|
|
226
|
+
|
|
227
|
+
# Clean up on failure
|
|
228
|
+
if not success and os.path.exists(output_file):
|
|
229
|
+
try:
|
|
230
|
+
os.remove(output_file)
|
|
231
|
+
except OSError:
|
|
232
|
+
pass
|
|
233
|
+
|
|
234
|
+
if verbose:
|
|
235
|
+
info_str = f"codec: {codec}, encoder: {encoder}, format: {pix_fmt}, status: {status}"
|
|
236
|
+
command_str = " ".join(shlex.quote(arg) for arg in command)
|
|
237
|
+
if stdout.strip() and stderr.strip():
|
|
238
|
+
command_log = f"{stdout.strip()}\n{stderr.strip()}"
|
|
239
|
+
elif stdout.strip():
|
|
240
|
+
command_log = stdout.strip()
|
|
241
|
+
elif stderr.strip():
|
|
242
|
+
command_log = stderr.strip()
|
|
243
|
+
else:
|
|
244
|
+
command_log = "(none)"
|
|
245
|
+
log_message = f"""
|
|
246
|
+
==================================================
|
|
247
|
+
[Bit-depth/Chroma Encoder Test]
|
|
248
|
+
{info_str}
|
|
249
|
+
|
|
250
|
+
[FFmpeg Command]
|
|
251
|
+
{command_str}
|
|
252
|
+
|
|
253
|
+
[Command Log]
|
|
254
|
+
{command_log}
|
|
255
|
+
|
|
256
|
+
""".strip()
|
|
257
|
+
print(log_message)
|
|
258
|
+
|
|
259
|
+
title = ENCODER_TITLES.get((encoder, codec), f"{encoder.upper()} Encoder:")
|
|
260
|
+
return title, pix_fmt, bit_depth, chroma, status
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _run_encoder_bitdepth_tests(test_dir, max_workers, verbose):
|
|
264
|
+
"""Tests encoder support for various pixel formats."""
|
|
265
|
+
results = defaultdict(dict)
|
|
266
|
+
|
|
267
|
+
print("\n--- Running Bit-depth/Chroma Encoder Tests ---")
|
|
268
|
+
|
|
269
|
+
tasks = []
|
|
270
|
+
for codec, info in ENCODERS.items():
|
|
271
|
+
for encoder in info['hw_encoders']:
|
|
272
|
+
for pix_fmt, bit_depth, chroma, desc in PIXEL_FORMATS:
|
|
273
|
+
tasks.append((codec, encoder, pix_fmt, bit_depth, chroma, test_dir, verbose))
|
|
274
|
+
|
|
275
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
276
|
+
futures = [executor.submit(_run_encoder_bitdepth_test, task) for task in tasks]
|
|
277
|
+
|
|
278
|
+
for future in tqdm(as_completed(futures), total=len(tasks), desc="Running encoder bit-depth tests"):
|
|
279
|
+
title, pix_fmt, bit_depth, chroma, status = future.result()
|
|
280
|
+
key = f"{bit_depth}-bit {chroma}"
|
|
281
|
+
if title not in results:
|
|
282
|
+
results[title] = {}
|
|
283
|
+
results[title][key] = status
|
|
284
|
+
|
|
285
|
+
return results
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _run_decoder_bitdepth_test(test_data):
|
|
289
|
+
"""Tests decoder support for a specific pixel format."""
|
|
290
|
+
codec, hw_decoder, pix_fmt, bit_depth, chroma, test_dir, verbose = test_data
|
|
291
|
+
|
|
292
|
+
file_ext = ".webm" if codec in ["vp8", "vp9"] else ".mp4"
|
|
293
|
+
test_file = os.path.join(test_dir, f"{codec}_{pix_fmt}{file_ext}")
|
|
294
|
+
|
|
295
|
+
# Create test file with specific pixel format if it doesn't exist
|
|
296
|
+
if not os.path.exists(test_file) or os.path.getsize(test_file) == 0:
|
|
297
|
+
cpu_lib = DECODERS[codec]["lib"]
|
|
298
|
+
command = [
|
|
299
|
+
"ffmpeg", "-loglevel", "quiet", "-hide_banner", "-y",
|
|
300
|
+
"-f", "lavfi", "-i", f"color=white:s={BITDEPTH_CHROMA_RESOLUTION}:d=1",
|
|
301
|
+
"-frames:v", "1", "-c:v", cpu_lib, "-pix_fmt", pix_fmt,
|
|
302
|
+
test_file,
|
|
303
|
+
]
|
|
304
|
+
if not _run_ffmpeg_command(command, verbose)[0]:
|
|
305
|
+
title = DECODER_TITLES.get((hw_decoder, codec), f"{hw_decoder.upper()} Decoder:")
|
|
306
|
+
return title, pix_fmt, bit_depth, chroma, "skipped"
|
|
307
|
+
|
|
308
|
+
if "vulkan" in hw_decoder:
|
|
309
|
+
command = [
|
|
310
|
+
"ffmpeg", "-loglevel", "quiet", "-hide_banner", "-y",
|
|
311
|
+
"-init_hw_device", "vulkan=vk:0",
|
|
312
|
+
"-hwaccel", "vulkan",
|
|
313
|
+
"-hwaccel_output_format", "vulkan",
|
|
314
|
+
"-i", test_file,
|
|
315
|
+
"-f", "null", "null",
|
|
316
|
+
]
|
|
317
|
+
elif "videotoolbox" in hw_decoder:
|
|
318
|
+
command = [
|
|
319
|
+
"ffmpeg", "-loglevel", "quiet", "-hide_banner", "-y",
|
|
320
|
+
"-hwaccel", "videotoolbox",
|
|
321
|
+
"-i", test_file,
|
|
322
|
+
"-f", "null", "null",
|
|
323
|
+
]
|
|
324
|
+
elif hw_decoder in ["dxva2", "d3d11va"]:
|
|
325
|
+
command = [
|
|
326
|
+
"ffmpeg", "-loglevel", "quiet", "-hide_banner", "-y",
|
|
327
|
+
"-hwaccel", hw_decoder, "-i", test_file,
|
|
328
|
+
"-c:v", "libx264", "-preset", "ultrafast",
|
|
329
|
+
"-f", "null", "null",
|
|
330
|
+
]
|
|
331
|
+
else:
|
|
332
|
+
command = [
|
|
333
|
+
"ffmpeg", "-loglevel", "quiet", "-hide_banner", "-y",
|
|
334
|
+
"-c:v", hw_decoder, "-i", test_file,
|
|
335
|
+
"-c:v", "libx264", "-preset", "ultrafast",
|
|
336
|
+
"-f", "null", "null",
|
|
337
|
+
]
|
|
338
|
+
|
|
339
|
+
if verbose:
|
|
340
|
+
command[2] = "error"
|
|
341
|
+
|
|
342
|
+
success, stdout, stderr = _run_ffmpeg_command(command, verbose)
|
|
343
|
+
status = "succeeded" if success else "failed"
|
|
344
|
+
|
|
345
|
+
if verbose:
|
|
346
|
+
info_str = f"codec: {codec}, decoder: {hw_decoder}, format: {pix_fmt}, status: {status}"
|
|
347
|
+
command_str = " ".join(shlex.quote(arg) for arg in command)
|
|
348
|
+
if stdout.strip() and stderr.strip():
|
|
349
|
+
command_log = f"{stdout.strip()}\n{stderr.strip()}"
|
|
350
|
+
elif stdout.strip():
|
|
351
|
+
command_log = stdout.strip()
|
|
352
|
+
elif stderr.strip():
|
|
353
|
+
command_log = stderr.strip()
|
|
354
|
+
else:
|
|
355
|
+
command_log = "(none)"
|
|
356
|
+
log_message = f"""
|
|
357
|
+
==================================================
|
|
358
|
+
[Bit-depth/Chroma Decoder Test]
|
|
359
|
+
{info_str}
|
|
360
|
+
|
|
361
|
+
[FFmpeg Command]
|
|
362
|
+
{command_str}
|
|
363
|
+
|
|
364
|
+
[Command Log]
|
|
365
|
+
{command_log}
|
|
366
|
+
|
|
367
|
+
""".strip()
|
|
368
|
+
print(log_message)
|
|
369
|
+
|
|
370
|
+
title = DECODER_TITLES.get((hw_decoder, codec), f"{hw_decoder.upper()} Decoder:")
|
|
371
|
+
return title, pix_fmt, bit_depth, chroma, status
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def _run_decoder_bitdepth_tests(test_dir, max_workers, verbose):
|
|
375
|
+
"""Tests decoder support for various pixel formats."""
|
|
376
|
+
results = defaultdict(dict)
|
|
377
|
+
|
|
378
|
+
print("\n--- Running Bit-depth/Chroma Decoder Tests ---")
|
|
379
|
+
|
|
380
|
+
tasks = []
|
|
381
|
+
for codec, info in DECODERS.items():
|
|
382
|
+
for hw_decoder in info['hw_decoders']:
|
|
383
|
+
for pix_fmt, bit_depth, chroma, desc in PIXEL_FORMATS:
|
|
384
|
+
tasks.append((codec, hw_decoder, pix_fmt, bit_depth, chroma, test_dir, verbose))
|
|
385
|
+
|
|
386
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
387
|
+
futures = [executor.submit(_run_decoder_bitdepth_test, task) for task in tasks]
|
|
388
|
+
for future in tqdm(as_completed(futures), total=len(tasks), desc="Running decoder bit-depth tests"):
|
|
389
|
+
title, pix_fmt, bit_depth, chroma, status = future.result()
|
|
390
|
+
key = f"{bit_depth}-bit {chroma}"
|
|
391
|
+
if title not in results:
|
|
392
|
+
results[title] = {}
|
|
393
|
+
results[title][key] = status
|
|
394
|
+
return results
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _get_display_width(s):
|
|
398
|
+
"""Calculates the display width of a string, ignoring ANSI escape codes."""
|
|
399
|
+
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
|
400
|
+
return len(ansi_escape.sub('', s))
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def _print_bitdepth_chroma_table(results, table_type="Encoder"):
|
|
404
|
+
"""Prints a formatted summary table for bit-depth/chroma results."""
|
|
405
|
+
GREEN_CHECK = Fore.GREEN + "✓" + Style.RESET_ALL
|
|
406
|
+
RED_X = Fore.RED + "×" + Style.RESET_ALL
|
|
407
|
+
GRAY_DASH = Fore.LIGHTBLACK_EX + "—" + Style.RESET_ALL
|
|
408
|
+
|
|
409
|
+
# Define columns for the table
|
|
410
|
+
format_columns = [
|
|
411
|
+
"8-bit 4:2:0",
|
|
412
|
+
"8-bit 4:2:2",
|
|
413
|
+
"8-bit 4:4:4",
|
|
414
|
+
"10-bit 4:2:0",
|
|
415
|
+
"10-bit 4:2:2",
|
|
416
|
+
"10-bit 4:4:4",
|
|
417
|
+
"12-bit 4:2:0",
|
|
418
|
+
"12-bit 4:2:2",
|
|
419
|
+
"12-bit 4:4:4",
|
|
420
|
+
]
|
|
421
|
+
|
|
422
|
+
titles = sorted(results.keys())
|
|
423
|
+
|
|
424
|
+
col_width = max(len(col) for col in format_columns)
|
|
425
|
+
row_header_width = max([_get_display_width(t) for t in titles] + [20, _get_display_width(table_type)])
|
|
426
|
+
|
|
427
|
+
print("\n" + "=" * (row_header_width + 3 + (col_width + 3) * len(format_columns)))
|
|
428
|
+
header_text = f"Bit-depth/Chroma {table_type} Support"
|
|
429
|
+
padding_left = (row_header_width - _get_display_width(header_text)) // 2
|
|
430
|
+
padding_right = row_header_width - _get_display_width(header_text) - padding_left
|
|
431
|
+
header_row = f"| {' ' * padding_left}{header_text}{' ' * padding_right} |"
|
|
432
|
+
for col in format_columns:
|
|
433
|
+
header_row += f" {col.center(col_width)} |"
|
|
434
|
+
print(header_row)
|
|
435
|
+
print("-" * (row_header_width + 3 + (col_width + 3) * len(format_columns)))
|
|
436
|
+
|
|
437
|
+
for title in titles:
|
|
438
|
+
padding_needed = row_header_width - _get_display_width(title)
|
|
439
|
+
row_string = f"| {title}{' ' * padding_needed} |"
|
|
440
|
+
for col in format_columns:
|
|
441
|
+
status = results.get(title, {}).get(col, "skipped")
|
|
442
|
+
symbol = GREEN_CHECK if status == "succeeded" else RED_X if status == "failed" else GRAY_DASH
|
|
443
|
+
symbol_width = _get_display_width(symbol)
|
|
444
|
+
padding_left = (col_width - symbol_width) // 2
|
|
445
|
+
padding_right = col_width - symbol_width - padding_left
|
|
446
|
+
row_string += f" {' ' * padding_left}{symbol}{' ' * padding_right} |"
|
|
447
|
+
print(row_string)
|
|
448
|
+
print("=" * (row_header_width + 3 + (col_width + 3) * len(format_columns)))
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def run_bitdepth_chroma_tests(encoder_count, decoder_count, verbose):
|
|
452
|
+
"""Run all bit-depth and chroma tests and return results."""
|
|
453
|
+
import shutil
|
|
454
|
+
temp_dir = os.path.join(tempfile.gettempdir(), "HwCodecDetect_BitDepth")
|
|
455
|
+
if os.path.exists(temp_dir):
|
|
456
|
+
shutil.rmtree(temp_dir)
|
|
457
|
+
os.makedirs(temp_dir)
|
|
458
|
+
|
|
459
|
+
encoder_results = _run_encoder_bitdepth_tests(temp_dir, encoder_count, verbose)
|
|
460
|
+
decoder_results = _run_decoder_bitdepth_tests(temp_dir, decoder_count, verbose)
|
|
461
|
+
|
|
462
|
+
# Clean up
|
|
463
|
+
shutil.rmtree(temp_dir)
|
|
464
|
+
|
|
465
|
+
return encoder_results, decoder_results
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def print_bitdepth_chroma_results(encoder_results, decoder_results):
|
|
469
|
+
"""Print bit-depth and chroma test results."""
|
|
470
|
+
if decoder_results:
|
|
471
|
+
_print_bitdepth_chroma_table(decoder_results, "Decoder")
|
|
472
|
+
if encoder_results:
|
|
473
|
+
_print_bitdepth_chroma_table(encoder_results, "Encoder")
|
|
@@ -9,6 +9,7 @@ import tempfile
|
|
|
9
9
|
import argparse
|
|
10
10
|
from collections import defaultdict
|
|
11
11
|
from .install_ffmpeg_if_needed import install_ffmpeg_if_needed
|
|
12
|
+
from .bitdepth_chroma_detect import run_bitdepth_chroma_tests, print_bitdepth_chroma_results
|
|
12
13
|
from colorama import init, Fore, Style
|
|
13
14
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
14
15
|
from tqdm import tqdm
|
|
@@ -575,6 +576,16 @@ def run_all_tests(args):
|
|
|
575
576
|
|
|
576
577
|
_print_summary_table(all_results)
|
|
577
578
|
|
|
579
|
+
# Run bit-depth and chroma tests if enabled
|
|
580
|
+
if getattr(args, 'bitdepth_chroma', True):
|
|
581
|
+
print("\n" + "=" * 60)
|
|
582
|
+
print("Starting Bit-depth and Chroma Subsampling Detection...")
|
|
583
|
+
print("=" * 60)
|
|
584
|
+
bd_encoder_results, bd_decoder_results = run_bitdepth_chroma_tests(
|
|
585
|
+
args.encoder_count, args.decoder_count, args.verbose
|
|
586
|
+
)
|
|
587
|
+
print_bitdepth_chroma_results(bd_encoder_results, bd_decoder_results)
|
|
588
|
+
|
|
578
589
|
print("\nCleaning up temporary files...")
|
|
579
590
|
shutil.rmtree(temp_dir)
|
|
580
591
|
print("Cleanup complete.")
|
|
@@ -645,7 +656,16 @@ def main():
|
|
|
645
656
|
help='Print detailed information for each test'
|
|
646
657
|
)
|
|
647
658
|
|
|
659
|
+
parser.add_argument(
|
|
660
|
+
'--no-bitdepth-chroma',
|
|
661
|
+
action='store_true',
|
|
662
|
+
dest='no_bitdepth_chroma',
|
|
663
|
+
help='Disable bit-depth and chroma subsampling detection (enabled by default)'
|
|
664
|
+
)
|
|
665
|
+
|
|
648
666
|
args = parser.parse_args()
|
|
667
|
+
# Set bitdepth_chroma to True unless --no-bitdepth-chroma is specified
|
|
668
|
+
args.bitdepth_chroma = not args.no_bitdepth_chroma
|
|
649
669
|
run_all_tests(args)
|
|
650
670
|
|
|
651
671
|
if __name__ == "__main__":
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: HwCodecDetect
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A cross-platform tool to automatically detect and test hardware video decoders/encoders using FFmpeg.
|
|
5
5
|
Home-page: https://github.com/whyb/HwCodecDetect
|
|
6
6
|
Author: whyb
|
|
@@ -74,6 +74,26 @@ The script automatically detect and reports on the following major hardware deco
|
|
|
74
74
|
| Vulkan | H.264、H.265、AV1 |
|
|
75
75
|
| Apple VideoToolbox | H.264、H.265、MPEG-2、MPEG-4 |
|
|
76
76
|
|
|
77
|
+
### Bit-depth and Chroma Subsampling Detection
|
|
78
|
+
In addition to resolution-based testing, the tool now includes comprehensive bit-depth and chroma subsampling detection. This feature tests hardware codec support for different pixel formats, helping you understand the full capabilities of your hardware encoders and decoders.
|
|
79
|
+
|
|
80
|
+
The detection covers the following pixel formats:
|
|
81
|
+
| Bit-depth | Chroma Subsampling | Pixel Format | Description |
|
|
82
|
+
|-----------|-------------------|--------------|-------------|
|
|
83
|
+
| 8-bit | YUV 4:2:0 | yuv420p | Standard 8-bit 4:2:0 |
|
|
84
|
+
| 8-bit | YUV 4:2:2 | yuv422p | 8-bit 4:2:2 |
|
|
85
|
+
| 8-bit | YUV 4:4:4 | yuv444p | 8-bit 4:4:4 |
|
|
86
|
+
| 10-bit | YUV 4:2:0 | yuv420p10le, p010le | 10-bit 4:2:0 |
|
|
87
|
+
| 10-bit | YUV 4:2:2 | yuv422p10le | 10-bit 4:2:2 |
|
|
88
|
+
| 10-bit | YUV 4:4:4 | yuv444p10le | 10-bit 4:4:4 |
|
|
89
|
+
| 12-bit | YUV 4:2:0 | yuv420p12le | 12-bit 4:2:0 |
|
|
90
|
+
| 12-bit | YUV 4:2:2 | yuv422p12le | 12-bit 4:2:2 |
|
|
91
|
+
| 12-bit | YUV 4:4:4 | yuv444p12le | 12-bit 4:4:4 |
|
|
92
|
+
|
|
93
|
+
This feature uses a fixed resolution of 1280x720 for all tests and follows the encode-then-decode workflow. If hardware encoding fails, the tool automatically falls back to software encoding to ensure decoder tests can still be performed.
|
|
94
|
+
|
|
95
|
+
**Note:** This feature is enabled by default. You can disable it using the `--no-bitdepth-chroma` command-line parameter.
|
|
96
|
+
|
|
77
97
|
|
|
78
98
|
## How to Use
|
|
79
99
|
You can install and use HwCodecDetect in two ways.
|
hwcodecdetect-0.1.9/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.1.9
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|