提示

返回博客列表

下载了 HDR 视频结果一片灰白?HDR 下载、播放与转 SDR 的完整指南

下载了 HDR 视频结果一片灰白?HDR 下载、播放与转 SDR 的完整指南

你在 B 站或 YouTube 下载了一个标着"4K HDR 真彩"的视频,满心期待地打开——画面灰蒙蒙的,颜色像褪了色,亮度忽明忽暗。这不是视频坏了,是你的播放器/显示器不支持 HDR。HDR(高动态范围)视频包含的亮度和色彩信息远超普通 SDR 视频,但如果播放链路中任何一环不支持 HDR,画面就会"崩"。这篇文章讲清楚:HDR10、Dolby Vision、HLG 三种格式的区别、下载时怎么选、怎么正确播放、以及怎么把 HDR 转成 SDR 在普通设备上看。

TL;DR:HDR 视频发灰的原因——播放器把 HDR 数据当成 SDR 显示了,没有做"色调映射"(Tone Mapping)。解决方案:① 用支持 HDR 的播放器(VLC/mpv/ PotPlayer 开 HDR 模式) + HDR 显示器;② 用 FFmpeg 把 HDR 转成 SDR(色调映射),在任何设备上都能正常看;③ 下载时就选 SDR 版本(如果平台提供了 SDR 选项)。

目录

一、HDR 视频为什么在你的屏幕上发灰

1.1 根本原因:缺少色调映射

HDR 视频的亮度范围:0.0001 nit → 1000+ nit
SDR 显示器的亮度范围:0.1 nit → 100 nit(典型值)

如果直接把 HDR 数据喂给 SDR 显示器:
  → 超出 100 nit 的亮度被"截断"
  → 暗部细节丢失(0.0001 nit 在 SDR 上就是全黑)
  → 颜色空间不匹配(BT.2020 → BT.709 没有转换)
  → 结果:灰蒙蒙、褪色、发白

色调映射(Tone Mapping)就是把 HDR 的大范围亮度"压缩"到 SDR 显示器能显示的范围内,同时尽量保留明暗对比和色彩信息。

1.2 一个直观的类比

HDR 视频 = 一张 RAW 格式的照片(包含大量动态范围信息)
SDR 显示器 = 普通打印机

直接把 RAW 发给打印机 → 黑乎乎一团
先用 Lightroom 处理 RAW(色调映射)→ 打印机输出正常照片

FFmpeg 的色调映射滤镜就是视频领域的"Lightroom"。

二、HDR10、Dolby Vision、HLG 有什么区别

2.1 三种格式对比

特性 HDR10 HDR10+ Dolby Vision HLG
元数据类型 静态 动态 动态 无(基于伽马)
最大亮度 1000 nit 4000 nit 10000 nit 1000 nit
色深 10-bit 10-bit 12-bit 10-bit
授权费 免费 免费 收费 免费
兼容 SDR 需要转换 需要转换 需要转换 原生兼容
YouTube 使用
B 站使用 ✅(部分)
Netflix 使用
电视广播 ✅(BBC/NHK)

2.2 各自的特点

HDR10:最基础、最通用的 HDR 格式。所有 HDR 电视和播放器都支持。使用静态元数据——整个视频只有一套亮度和色彩参数。

HDR10+:三星主导的 HDR10 升级版。使用动态元数据——可以逐场景甚至逐帧调整亮度和色彩。免费授权。

Dolby Vision:杜比实验室的顶级 HDR 格式。使用动态元数据,支持 12-bit 色深。画质最好但需要授权费,所以支持的设备和平台较少。B 站的部分"杜比视界"视频就是这种格式。

HLG:由 BBC 和 NHK 联合开发,专为电视广播设计。最大的特点是原生兼容 SDR——在普通电视上也能正常显示(不会发灰),在 HDR 电视上效果更好。不需要元数据。

2.3 下载时怎么判断

# 用 ffprobe 查看视频的 HDR 信息
ffprobe -v error -show_entries stream=color_space,color_transfer,color_primaries \
  -of default=noprint_wrappers=1 input.mp4

输出解读:

HDR10 视频:
  color_space=bt2020nc
  color_transfer=smpte2084     ← PQ 曲线(HDR10 的标志)
  color_primaries=bt2020

HLG 视频:
  color_space=bt2020nc
  color_transfer=arib-std-b67  ← HLG 的标志
  color_primaries=bt2020

Dolby Vision:
  color_space=bt2020nc
  color_transfer=smpte2084
  color_primaries=bt2020
  还有额外的 Dolby Vision 元数据流

普通 SDR 视频:
  color_space=bt709
  color_transfer=bt709
  color_primaries=bt709

三、下载时怎么选:要 HDR 还是 SDR

3.1 选择决策树

你有 HDR 显示器(或 HDR 电视)吗?
  ├── 有 → 下载 HDR 版本 ✅
  │   └── 播放器推荐:mpv(自动色调映射)、VLC(手动开启 HDR)
  │
  └── 没有 → 平台提供了 SDR 版本吗?
      ├── 有 → 下载 SDR 版本(省心)
      └── 没有 → 下载 HDR 版本 + FFmpeg 转 SDR

3.2 yt-dlp 选择格式

# 查看可用格式
yt-dlp -F "URL"

# YouTube 的 HDR 格式通常标记为:
# 337  webm 1920x1080 HDR
# 336  webm 2560x1440 HDR

# 选择 HDR 视频 + 最佳音频
yt-dlp -f "bestvideo[height<=2160][dynamic_range=HDR]+bestaudio" URL

# 选择 SDR 视频(避免 HDR)
yt-dlp -f "bestvideo[height<=1080][dynamic_range=SDR]+bestaudio" URL

四、正确播放 HDR 视频

4.1 各播放器的 HDR 支持

播放器 HDR 支持 配置方式 推荐度
mpv ✅ 自动 无需配置,开箱即用 ⭐⭐⭐⭐⭐
VLC ✅ 手动 设置 → 视频 → 输出模块 → OpenGL ⭐⭐⭐⭐
PotPlayer ✅ 手动 视频 → 像素着色 → HDR 自动校正 ⭐⭐⭐⭐
IINA (Mac) ✅ 自动 开箱即用 ⭐⭐⭐⭐⭐
Windows Media Player ❌ 不支持
浏览器 (Chrome/Edge) ⚠️ 部分 YouTube 支持,本地文件不行 ⭐⭐

4.2 mpv 配置(推荐)

mpv 是目前 HDR 播放最省心的播放器——自动检测 HDR 并做色调映射:

# 安装 mpv
# Windows: https://mpv.io/installation/
# Mac: brew install mpv
# Linux: sudo apt install mpv

# 直接播放
mpv video_hdr.mp4

如果需要手动控制色调映射:

# ~/.config/mpv/mpv.conf
vo=gpu-next
target-colorspace-hint=yes
tone-mapping=bt.2390         # 色调映射算法
hdr-compute-peak=yes         # 自动计算峰值亮度
target-peak=200              # 你的显示器峰值亮度(nit

4.3 VLC 配置

1. 工具 → 偏好设置 → 视频
2. 输出模块 → OpenGL 视频输出
3. 开启 "HDR 色调映射"
4. 重启 VLC

五、HDR 转 SDR:在普通设备上正常观看

5.1 FFmpeg 色调映射命令

# HDR10 → SDR(推荐)
ffmpeg -i input_hdr.mp4 \
  -vf "zscale=t=linear:npl=100,format=gbrpf32le,\
       zscale=p=bt709,tonemap=tonemap=hable:desat=0,\
       zscale=t=bt709:m=bt709:r=tv,format=yuv420p" \
  -c:v libx264 -preset medium -crf 20 \
  -c:a aac -b:a 192k \
  output_sdr.mp4

这个命令看起来很复杂,拆解一下每一步:

步骤 1: zscale=t=linear:npl=100
  → 把 HDR 的 PQ 曲线转成线性光(npl=你的显示器峰值亮度,单位 nit)

步骤 2: format=gbrpf32le
  → 转为 32 位浮点格式(保留精度)

步骤 3: zscale=p=bt709
  → 把 BT.2020 色域转成 SDR 的 BT.709

步骤 4: tonemap=tonemap=hable:desat=0
  → 色调映射(Hable 算法,自然效果好)
  → desat=0 表示不降低饱和度

步骤 5: zscale=t=bt709:m=bt709:r=tv,format=yuv420p
  → 转回标准 SDR 格式

5.2 不同色调映射算法对比

算法 效果 速度 适用场景
hable 自然,不过曝 推荐,日常使用
mobius 平滑过渡 夜景/暗部细节多的视频
reinhard 亮部有压缩感 快速预览
bt.2390 标准算法 严格按 ITU 标准
clip 简单截断 极快 不推荐(丢细节)
# 尝试不同算法
ffmpeg -i input.mp4 \
  -vf "zscale=t=linear,format=gbrpf32le,\
       zscale=p=bt709,tonemap=tonemap=hable:desat=2,\
       zscale=t=bt709:m=bt709,format=yuv420p" \
  output_hable.mp4

5.3 HLG 转 SDR(更简单)

HLG 的转换比 HDR10 简单,因为它本身就是兼容 SDR 的设计:

# HLG → SDR
ffmpeg -i input_hlg.mp4 \
  -vf "zscale=t=linear,format=gbrpf32le,\
       zscale=p=bt709,tonemap=tonemap=mobius,\
       zscale=t=bt709:m=bt709,format=yuv420p" \
  -c:v libx264 -crf 22 -c:a copy \
  output_sdr.mp4

5.4 保留 HDR 同时改变编码

如果只是想改变编码但保留 HDR:

# HDR VP9 → HDR H.265(保留 HDR 元数据)
ffmpeg -i input_hdr.webm \
  -c:v libx265 -preset medium -crf 18 \
  -x265-params "colorprim=bt2020:transfer=smpte2084:colormatrix=bt2020nc" \
  -c:a aac -b:a 192k \
  output_hdr.mp4

六、检测视频是否是 HDR

import subprocess
import json

def detect_hdr(video_path):
    """检测视频的 HDR 格式"""
    cmd = [
        'ffprobe', '-v', 'error',
        '-show_entries', 'stream=color_space,color_transfer,color_primaries,width,height,codec_name',
        '-of', 'json', video_path
    ]

    result = subprocess.run(cmd, capture_output=True, text=True)
    data = json.loads(result.stdout)

    for stream in data.get('streams', []):
        transfer = stream.get('color_transfer', '')

        hdr_type = 'SDR'
        if transfer == 'smpte2084':
            hdr_type = 'HDR10'
        elif transfer == 'arib-std-b67':
            hdr_type = 'HLG'
        elif 'dolby' in str(stream).lower():
            hdr_type = 'Dolby Vision'

        if hdr_type != 'SDR':
            print(f"[{hdr_type}] {video_path}")
            print(f"  分辨率: {stream.get('width')}x{stream.get('height')}")
            print(f"  编码: {stream.get('codec_name')}")
            print(f"  色域: {stream.get('color_primaries')}")
            print(f"  传输函数: {transfer}")
            return hdr_type

    print(f"[SDR] {video_path}")
    return 'SDR'

七、批量 HDR→SDR 转换脚本

#!/usr/bin/env python3
"""批量把目录中的 HDR 视频转成 SDR"""

import os
import subprocess
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor


def convert_hdr_to_sdr(input_path, output_path, tonemap='hable'):
    """单个视频 HDR→SDR 转换"""
    cmd = [
        'ffmpeg', '-i', str(input_path),
        '-vf',
        f'zscale=t=linear:npl=100,format=gbrpf32le,'
        f'zscale=p=bt709,tonemap=tonemap={tonemap}:desat=0,'
        f'zscale=t=bt709:m=bt709:r=tv,format=yuv420p',
        '-c:v', 'libx264', '-preset', 'medium', '-crf', '22',
        '-c:a', 'aac', '-b:a', '192k',
        str(output_path), '-y'
    ]

    subprocess.run(cmd, capture_output=True, check=True, timeout=3600)

    in_size = os.path.getsize(input_path) / 1024 / 1024
    out_size = os.path.getsize(output_path) / 1024 / 1024
    return in_size, out_size


def batch_convert_hdr(input_dir, output_dir, tonemap='hable', workers=2):
    """批量 HDR→SDR 转换"""
    os.makedirs(output_dir, exist_ok=True)
    video_exts = {'.mp4', '.mkv', '.webm', '.mov'}

    tasks = []
    for f in Path(input_dir).iterdir():
        if f.suffix.lower() not in video_exts:
            continue
        if detect_hdr(str(f)) == 'SDR':
            print(f"跳过(SDR): {f.name}")
            continue

        out = Path(output_dir) / f'{f.stem}_SDR.mp4'
        if out.exists():
            print(f"跳过(已存在): {out.name}")
            continue
        tasks.append((f, out))

    if not tasks:
        print("没有需要转换的 HDR 视频")
        return

    print(f"待转换: {len(tasks)} 个 HDR 视频")
    total_saved = 0

    with ThreadPoolExecutor(max_workers=workers) as executor:
        futures = {executor.submit(convert_hdr_to_sdr, *t, tonemap): t for t in tasks}
        for i, future in enumerate(futures):
            in_path, out_path = futures[future]
            try:
                in_size, out_size = future.result()
                saved = in_size - out_size
                total_saved += saved
                print(f"[{i+1}/{len(tasks)}] {in_path.name}: "
                      f"{in_size:.0f}MB → {out_size:.0f}MB")
            except Exception as e:
                print(f"[{i+1}/{len(tasks)}] {in_path.name}: 失败 - {e}")

    print(f"\n完成!总节省空间: {total_saved:.0f} MB")


if __name__ == '__main__':
    batch_convert_hdr('./hdr_videos/', './sdr_output/', tonemap='hable')

八、合规与温馨提示

  • HDR 转 SDR 属于视频格式转换,本身是合法的技术操作
  • 转换他人版权内容用于再分发属于侵权
  • 色调映射会损失部分画质——HDR 的高动态范围在 SDR 上不可能完全还原。如果需要最佳体验,建议升级到 HDR 显示器
  • 更多讨论见 下载视频算侵权吗?聊聊个人备份与版权的那条线

HDR 这件事,如果你有 HDR 显示器(包括大部分近几年的 MacBook 和 iPhone),直接下载 HDR 版本用 mpv 或 IINA 播放,体验确实比 SDR 好一个档次。如果你只有普通显示器,就别折腾了——下载时选 SDR 版本,或者用 FFmpeg 转一下。别想着"我下载 HDR 版本存着以后看",HDR 视频在不支持的设备上就是灰白一片,存了也看不了。

本文由 VidDown 技术博客原创发布。VidDown 下载器支持自动检测 HDR 格式,并提供一键 HDR→SDR 转换功能。访问 VidDown 了解更多。

想亲手试试?用 VidDown 一键解析下载

粘贴视频链接即可解析,多平台支持、网页端即用;下载桌面客户端解锁海外平台本地解析,开通会员更享不限次下载。

顶部