音视频-色域¶
1. 色域相关¶
1.1 python测试视频生成¶
import os
import subprocess
import argparse
import json
# 色域配置
COLOR_PROFILES = {
"bt601": {
"ffmpeg": {
"colorspace": "5",
"color_primaries": "5",
"color_trc": "6", # smpte170m
},
"x264-params": "colorprim=bt601:transfer=smpte170m:colormatrix=bt601",
"x265-params": "colorprim=bt601:transfer=smpte170m:colormatrix=bt601",
},
"bt709": {
"ffmpeg": {
"colorspace": "1",
"color_primaries": "1",
"color_trc": "1",
},
"x264-params": "colorprim=bt709:transfer=bt709:colormatrix=bt709",
"x265-params": "colorprim=bt709:transfer=bt709:colormatrix=bt709",
},
"bt2020": {
"ffmpeg": {
"colorspace": "9",
"color_primaries": "9",
"color_trc": "16", # smpte2084 (PQ)
},
"x265-params": "colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc",
},
}
# HDR 配置
HDR_PROFILES = {
"hdr10": {
"x265": (
":master-display="
"G(13250,34500)B(7500,3000)R(34000,16000)"
"WP(15635,16450)L(10000000,1)"
":max-cll=1000,400"
)
},
"hlg": {
"x265": ":transfer=arib-std-b67" # Hybrid Log-Gamma
},
# Dolby Vision 需要二次封装
}
def probe_video(video_path):
"""使用 ffprobe 获取视频流信息"""
if not os.path.exists(video_path):
return {"error": "文件不存在"}
cmd = [
"ffprobe",
"-v", "quiet",
"-print_format", "json",
"-show_streams",
"-show_format",
video_path
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
return {"error": result.stderr}
info = json.loads(result.stdout)
video_stream = next((s for s in info.get("streams", []) if s.get("codec_type") == "video"), None)
if not video_stream:
return {"error": "未找到视频流"}
fps = eval(video_stream.get("r_frame_rate", "0/1"))
return {
"codec": video_stream.get("codec_name"),
"pix_fmt": video_stream.get("pix_fmt"),
"width": video_stream.get("width"),
"height": video_stream.get("height"),
"color_range": video_stream.get("color_range", "unknown"),
"color_space": video_stream.get("color_space", "unknown"),
"color_primaries": video_stream.get("color_primaries", "unknown"),
"color_trc": video_stream.get("color_transfer", "unknown"),
"fps": fps,
}
def generate_videos(profile, color_range, bit_depth, out_dir, hdr="off"):
os.makedirs(out_dir, exist_ok=True)
profiles = [profile] if profile != "all" else list(COLOR_PROFILES.keys())
ranges = [color_range] if color_range != "all" else ["tv", "pc"]
bits = [bit_depth] if bit_depth != "all" else ["8bit", "10bit"]
if hdr == "all":
hdrs = ["off"] + list(HDR_PROFILES.keys())
else:
hdrs = [hdr]
results = []
for p in profiles:
for r in ranges:
for b in bits:
for h in hdrs:
ffmpeg_args = []
# 选择像素格式与编码器
if b == "8bit":
pix_fmt = "yuv420p"
encoder = "libx264" if p != "bt2020" else "libx265"
else: # 10bit
pix_fmt = "yuv420p10le"
encoder = "libx265"
# 输出文件名
filename = f"{p}_{r}_{b}"
if h != "off":
filename += f"_{h}"
filename += "_1080p.mp4"
filepath = os.path.join(out_dir, filename)
# 色彩参数
ffmpeg_args += [
"-pix_fmt", pix_fmt,
"-colorspace", COLOR_PROFILES[p]["ffmpeg"]["colorspace"],
"-color_primaries", COLOR_PROFILES[p]["ffmpeg"]["color_primaries"],
"-color_trc", COLOR_PROFILES[p]["ffmpeg"]["color_trc"],
"-color_range", "1" if r == "tv" else "2",
]
# 编码器私有参数
if encoder == "libx264":
ffmpeg_args += ["-x264-params", COLOR_PROFILES[p]["x264-params"]]
else:
x265_params = COLOR_PROFILES[p]["x265-params"]
if h in HDR_PROFILES and "x265" in HDR_PROFILES[h]:
x265_params += HDR_PROFILES[h]["x265"]
ffmpeg_args += ["-x265-params", x265_params]
# 构建命令
ffmpeg_cmd = [
"ffmpeg",
"-f", "lavfi",
"-i", "testsrc=duration=10:size=1920x1080:rate=25",
"-c:v", encoder,
"-crf", "23",
"-preset", "medium",
*ffmpeg_args,
filepath,
]
print(f"\n生成视频: {filepath}")
try:
subprocess.run(ffmpeg_cmd, check=True, capture_output=True)
# probe 生成的视频
info = probe_video(filepath)
results.append((True, filepath, info))
print("视频流信息:", info)
except subprocess.CalledProcessError as e:
results.append((False, filepath, {"error": e.stderr.decode()}))
print(f"生成失败: {filepath}\n", e.stderr.decode())
return results
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="生成不同色域/位深/HDR 的视频测试文件")
parser.add_argument("--profile", default="all", choices=["all"] + list(COLOR_PROFILES.keys()),
help="色域 (bt601, bt709, bt2020, all)")
parser.add_argument("--range", default="all", choices=["all", "tv", "pc"],
help="范围 (tv, pc, all)")
parser.add_argument("--bit", default="all", choices=["all", "8bit", "10bit"],
help="位深 (8bit, 10bit, all)")
parser.add_argument("--hdr", default="off", choices=["off", "all"] + list(HDR_PROFILES.keys()),
help="HDR 设置 (off, hdr10, hlg, all)")
parser.add_argument("--out", default="output_videos", help="输出目录")
args = parser.parse_args()
results = generate_videos(args.profile, args.range, args.bit, args.out, args.hdr)
print("\n=== 生成结果汇总 ===")
for success, filepath, info in results:
status = "成功" if success else "失败"
print(f"{status}: {filepath}")
print(f" 信息: {info}")
1.2 测试视频生成¶
-
BT.601 TV 范围
-
BT.601 PC 范围
-
BT.709 TV 范围
-
BT.709 PC 范围
-
BT.2020 TV 范围
-
BT.2020 PC 范围
-
BT.601 TV 范围(10-bit)
-
BT.601 PC 范围(10-bit)
-
BT.709 TV 范围(10-bit)
-
BT.709 PC 范围(10-bit)
-
BT.2020 TV 范围(10-bit)
-
BT.2020 PC 范围(10-bit)