音视频-色域
1. 色域相关
1.1 python测试视频生成
import os
import subprocess
import argparse
import json
COLOR_PROFILES = {
"bt601": {
"ffmpeg": {
"colorspace": "5",
"color_primaries": "5",
"color_trc": "6",
},
"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",
},
"x265-params": "colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc",
},
}
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"
},
}
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:
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)
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 测试视频生成
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p -colorspace 5 -color_primaries 5 -color_trc 2 -color_range 1 -c:v libx264 -x264-params colorprim=bt601:transfer=bt709:colormatrix=bt601 -crf 23 -preset medium bt601_tv_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p -colorspace 5 -color_primaries 5 -color_trc 2 -color_range 2 -c:v libx264 -x264-params colorprim=bt601:transfer=bt709:colormatrix=bt601 -crf 23 -preset medium bt601_pc_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p -colorspace 1 -color_primaries 1 -color_trc 1 -color_range 1 -c:v libx264 -x264-params colorprim=bt709:transfer=bt709:colormatrix=bt709 -crf 23 -preset medium bt709_tv_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p -colorspace 1 -color_primaries 1 -color_trc 1 -color_range 2 -c:v libx264 -x264-params colorprim=bt709:transfer=bt709:colormatrix=bt709 -crf 23 -preset medium bt709_pc_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p -colorspace 9 -color_primaries 9 -color_trc 16 -color_range 1 -c:v libx265 -x265-params colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc -crf 23 -preset medium bt2020_tv_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p -colorspace 9 -color_primaries 9 -color_trc 16 -color_range 2 -c:v libx265 -x265-params colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc -crf 23 -preset medium bt2020_pc_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p10le -colorspace 5 -color_primaries 5 -color_trc 2 -color_range 1 -c:v libx265 -x265-params colorprim=bt601:transfer=bt709:colormatrix=bt601 -crf 23 -preset medium bt601_tv_10bit_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p10le -colorspace 5 -color_primaries 5 -color_trc 2 -color_range 2 -c:v libx265 -x265-params colorprim=bt601:transfer=bt709:colormatrix=bt601 -crf 23 -preset medium bt601_pc_10bit_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p10le -colorspace 1 -color_primaries 1 -color_trc 1 -color_range 1 -c:v libx265 -x265-params colorprim=bt709:transfer=bt709:colormatrix=bt709 -crf 23 -preset medium bt709_tv_10bit_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p10le -colorspace 1 -color_primaries 1 -color_trc 1 -color_range 2 -c:v libx265 -x265-params colorprim=bt709:transfer=bt709:colormatrix=bt709 -crf 23 -preset medium bt709_pc_10bit_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p10le -colorspace 9 -color_primaries 9 -color_trc 16 -color_range 1 -c:v libx265 -x265-params colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc -crf 23 -preset medium bt2020_tv_10bit_1080p.mp4
ffmpeg -f lavfi -i testsrc=duration=10:size=1920x1080:rate=25 -pix_fmt yuv420p10le -colorspace 9 -color_primaries 9 -color_trc 16 -color_range 2 -c:v libx265 -x265-params colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc -crf 23 -preset medium bt2020_pc_10bit_1080p.mp4