ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SMPTE 274M标准解析:1080p视频时序、SAV/EAV与YCbCr采样

SMPTE 274M标准解析:1080p视频时序、SAV/EAV与YCbCr采样 简介SMPTE 274M-2008是电视行业针对1920×1080高清图像格式的正式标准面向广播工程、视频设备研发与信号处理相关技术人员解决多帧率下图像样本结构、数字表示及定时参考序列的规范化问题。PDF共35页单文件509KB内容包含标准的完整正文及附录涵盖图像结构、系统色度、栅格结构、数字图像表示、SAV/EAV数字定时参考序列、模拟同步与接口规范并延伸至渐进分段帧接口、辅助数据、位并行接口及前后滤波特性等关键技术细节。目前已有110人学习下载适合需要查阅原始规范、确保设备互操作性或开展高清视频系统设计的工程师作为案头参考。通过这份原始英文标准可准确核对1080i/1080p等格式的采样结构、色彩参数和同步信号定义避免二手资料的不确定性。1. SMPTE 274M-2008 到底在定什么从 1920x1080 到“能交换的比特流”一台采集卡标称支持 1080p60但把裸 YUV 流送进 FPGA 或软件栈时第一件事不是算像素而是找行消隐在哪结束、SAV 在哪出现、黑电平码字是 64 还是 16。1920x1080 只是 Image Sample Structure 里“有效样本”这一项SMPTE 274M-2008 真正回答的是三件事样本在行场结构里怎么摆放active line / blanking、每个样本在 8bit 和 10bit 下怎么编码Digital Representation、以及每行从哪里开始和结束Digital Timing Reference Sequences。这份标准是 1125 行系统里 1080 有效行格式的基准HDMI 的 video timing、SDI 的 payload、甚至部分屏幕驱动里的 timing generator 都能回溯到它。适合做视频采集、显示驱动、FPGA 监视器和编解码测试的工程师也适合刚接触视频格式、想搞懂 1080p 时序表为什么长成那样的人。2. 1920x1080 的样本结构与色度采样有效区、消隐区和 4:2:2 怎么对齐2.1 先看 1125 行里的有效区与消隐区SMPTE 274M 定义的系统不是 1080 行而是 1125 行一帧其中 1080 行是有效视频其余 45 行用于垂直消隐。有效行编号在逐行和隔行模式下略有差异但常规做法是把第 1 行到第 41 行左右作为垂直消隐区间之后进入有效区直到帧末。你拿到的 1080p 信号如果显示出来画面上下有黑边经常就是这 45 行被错误地当成有效像素处理了。水平方向上每行不是 1920 个样本到头。标准按不同帧率给每行分配了不同的总样本数有效样本固定 1920剩下的部分就是水平消隐。水平消隐里除了承载 SAV/EAV 定时参考码还可能有辅助数据ANC这部分在驱动开发和采集测试里最容易出问题把消隐样本灌进帧缓冲画面会出现右侧垂直条纹把消隐直接裁掉又可能把 ANC 里的 HDR 或音频元数据丢掉。2.2 4:2:2 色度采样亮度和色度的位置关系样本结构不只是亮度。SMPTE 274M 支持 4:2:2 和 4:4:4 两种色度采样电视制作链路里默认是 4:2:2。4:2:2 的含义是每两个亮度样本对应一个 Cb 一个 Cr所以一条 1920 样本宽的亮度行对应的是 960 个 Cb 和 960 个 Cr。放到内存布局里10bit 4:2:2 常见的排布是 Cb Y Cr Y 交错也就是每个像素至少有一个 Y但色度是每两像素共享一次。表格列出不同帧率对应的行结构这是做 timing generator 时直接要查的参数帧率族每行总样本有效样本采样率 (MHz)行频 (kHz)1080i/60, 1080p/302200192074.2533.751080i/59.94, 1080p/29.972200192074.25 / 1.00133.75 / 1.0011080p/6022001920148.567.51080p/5026401920148.556.251080i/50, 1080p/252640192074.2528.1251080p/242750192074.25271080p/23.982750192074.25 / 1.00127 / 1.001看到 2200、2640、2750 这三个数不要背错。它们的来源是采样率固定后行频由帧率和 1125 总行数决定再用采样率除以行频得到每行总样本。1080p/60 的采样率是 148.5 MHz行频 67.5 kHz所以每行 2200 个样本1080p/24 采样率 74.25 MHz行频 27 kHz每行就是 2750 个样本。行频和总样本数会直接影响你对流做行同步时的步进长度。2.3 用 ffprobe 核对容器里的有效区元数据拿到一个视频文件先用 ffprobe 看它的解析度和色度信息是否和 274M 的约定一致ffprobe -v error -select_streams v:0 \ -show_entries streamwidth,height,pix_fmt,color_range,color_space,field_order \ -of defaultnoprint_wrappers1 input.mov输出里重点看两项color_range 为 tv 或 pc以及 field_order。274M 电视应用默认是 tv range有限范围也就是前面表格里说的黑电平不是 0 而是 16/64。如果 color_range 显示 pc说明文件可能已经做过了 range 转换后面做 YCbCr 转 RGB 时不能直接套电视标准系数。这个命令只验证“文件头声称的格式”不验证流内容是否真的符合 274M 的时序。真正的行结构校验要靠解析 SAV/EAV 完成这在第 4 章会展开。先把格式参数固定下来后面所有转换和时序判断才有基准。3. 数字表示码字范围、黑电平与防过冲的量化边界3.1 8bit 和 10bit 的码字映射SMPTE 274M 的 Digital Representation 规定样本值不是 0~255 或 0~1023 全范围使用的。以亮度 Y 为例8bit 下黑电平是 16参考白是 23510bit 下对应的是 64 和 940。色度 Cb/Cr 以消隐电平为中心8bit 中心值是 12810bit 是 512。表格里这几个数字值得记牢信号分量8bit 范围10bit 范围参考电平Y 黑电平1664有效范围下限Y 参考白235940有效范围上限Cb/Cr 中心128512色差零值预留保护区0~15, 236~2550~63, 941~1023同步和过冲预留上下两端留出的保护区不是浪费。0 和 2558bit在 SDI 和并行接口里要留给定时参考序列的同步字使用如果视频内容把像素填到全范围同步字就可能会被误判成图像数据。另外模拟前端过冲、滤波振铃都可能让像素值越过正常边界预留区给这部分失真留了缓冲。3.2 为什么坚持用有限范围而不是全范围消费级视频设备经常把 8bit 用满 0~255这叫 full range也叫 pc range。274M 面向电视链路默认是 limited range。两者的转换关系不是简单的线性缩放那么简单因为 black level 和 white level 的坐标都变了。做 10bit 到 8bit 转换时常规做法是右移两位v8 (v10 2) 2注意要先加 2 再移位相当于做四舍五入而不是直接截断。反过来 8bit 升 10bit 是左移两位低位补零即可不需要加数值。很多显示驱动里看到的“画面发灰”问题根因就是往 RGB 面板送数据时把 limited range 的 YCbCr 直接用 full range 系数做了矩阵运算黑电平被抬到了 16/255 的位置对比度一下就丢了。先确认 source 的 range再决定转换矩阵比调亮度和对比度参数有效得多。3.3 YCbCr 转 RGB 的一个可靠实现下面这段代码是 10bit limited range 到 8bit RGB 的完整转换系数组来自 BT.709 的常规数字实现。输入 y、cb、cr 是 10bit 值输出是 0~255 的 R、G、B。def ycbcr10_to_rgb8(y, cb, cr): # 10bit limited range - 归一化 y (y - 64) / (940 - 64) cb (cb - 512) / (960 - 64) # 10bit 色度标称范围 64~960 cr (cr - 512) / (960 - 64) # BT.709 系数矩阵y 范围已归一到 0~1 r y 1.5748 * cr g y - 0.1873 * cb - 0.4681 * cr b y 1.8556 * cb # 量化到 8bit 并钳位 r max(0, min(255, round(r * 255))) g max(0, min(255, round(g * 255))) b max(0, min(255, round(b * 255))) return r, g, b参数说明里要注意两点。第一色度的归一化范围用了 64~960 而不是 512 为中心再对称展开这是按 10bit 色度信号的标称范围做的跟亮度用 64~940 略有区别避免色差分量增益偏大。第二矩阵系数 1.5748、-0.1873、-0.4681、1.8556 是 BT.709 有限范围到 RGB 的常见常量你自己写硬件时这些系数可以固化成定点数注意符号位和精度够不够 10bit 输入。这段代码只解决“数值翻译”问题。它不处理 gamma 编码因为视频链路里的 YCbCr 本身是经过光电转换曲线编码的直接矩阵运算后得到的是非线性 RGB通常还要再接一条 EOTF 才能显示。显示器驱动里有很多“颜色不对”的 bug 就是少了这一步扯远了先记住先 range再矩阵最后 gamma。4. 数字定时参考序列SAV/EAV、P0-P3 保护位与多帧率换算4.1 定时参考码的四字结构每一行图像数据在传输时由三个段组成消隐前的 EAVEnd of Active Video、消隐后的 SAVStart of Active Video、中间的有效样本区。EAV 和 SAV 都是四个字长的定时参考序列。8bit 模式下是 FF 00 00 XY 四个字节10bit 模式是 3FF 000 000 XY 四个字。前三个字是固定同步字最后一个 XY 字携带行状态信息。XY 字的位定义是bit7 固定为 1bit6 是 F隔行模式下标识奇偶场逐行模式恒为 0bit5 是 V表示当前是否处于垂直消隐V1 在消隐区bit4 是 HEAV 里 H1SAV 里 H0。bit3 到 bit0 是保护位 P3~P0用来做误码检测。4.2 保护位 P0-P3 的生成和校验P0~P3 不是冗余添加它们由 F、V、H 的异或组合生成。公式如下P0 V XOR H P1 F XOR H P2 F XOR V P3 F XOR V XOR H生成 XY 字的 Python 实现def build_xy(f, v, h): p0 v ^ h p1 f ^ h p2 f ^ v p3 f ^ v ^ h return 0x80 | (f 6) | (v 5) | (h 4) | (p3 3) | (p2 2) | (p1 1) | p0以逐行有效行为例F0、V0、H0 时SAV 的 XY 是 0x80。EAV 在同样条件下 F0、V0、H1代入公式算出 P01、P11、P20、P31得到 0x9B。很多人把 EAV 记成 0x9D那是垂直消隐区 SAV 的值——F0、V1、H0 时 P01、P10、P21、P31才得到 0x9D。解析时不要背固定值直接用公式校验前四位和 XY 的后四位是否一致这样对误码的容忍度更高。4.3 多帧率下总样本数和行频的换算逻辑回到第 2 章的表格2200、2640、2750 这三个总样本数不是并列的三种格式族。它们对应的采样率族分别是60/59.94 系的 33.75 kHz 行频 2200 样本25/50 系的 28.125 kHz 行频 2640 样本24 系的 27 kHz 行频 2750 样本。从 SAV 到下一个 SAV 的距离就是每行总样本数。换成字节数还要乘以每个样本的字节数——4:2:2 10bit 每 4 个样本打包成 5 字节这个在硬件位流里常见软件里则多按 32bit 对齐处理。做软件解析时有个人人都踩的坑按帧率直接假设行长为 1920 的倍数。正确做法是先找 EAV数两个 SAV 之间的样本数用这个实测值反推行频和帧率。下面这段代码在裸 YUV 流里定位 SAV/EAV并统计每行样本数import sys def find_timing(data, bit_depth8): sync_size 4 if bit_depth 8 else 4 # 这里按 8bit 流处理 # 8bit: FF 00 00 XY results [] i 0 while i len(data) - 4: if data[i] 0xFF and data[i1] 0x00 and data[i2] 0x00: xy data[i3] f (xy 6) 0x01 v (xy 5) 0x01 h (xy 4) 0x01 p3 (xy 3) 0x01 p2 (xy 2) 0x01 p1 (xy 1) 0x01 p0 xy 0x01 if (p0 (v ^ h) and p1 (f ^ h) and p2 (f ^ v) and p3 (f ^ v ^ h)): results.append((i, f, v, h)) i 4 else: i 1 return results with open(sys.argv[1], rb) as fh: buf fh.read() res find_timing(buf) print(f找到 {len(res)} 个定时参考码)逻辑说明同步字是 FF 00 00 三个字节找到后取第四个字节做 XY 校验。校验通过才记录否则跳过。这样能滤掉视频内容里恰好出现的 FF 00 00。输出结果里 F、V、H 的值组合起来就能画出每一行的消隐状态。实际做行同步时更稳的办法是找 EAVH1作为行边界锚点再数固定偏移到 SAV。这个脚本的问题是不处理 10bit10bit 流里同步字是 3FF 000 000字节序跟 8bit 不同需要按 16bit 对齐扫描。4.4 多帧率混合场景下的判定优先级同一个采集卡可能收到 1080i/59.94 和 1080p/29.97 两种流它们行频近似、帧率相同区别只在 F bit 是否在每行切换。判定的优先级是这样的先统计 V1 的行数判断垂直消隐长度再看 F bit 是否随行号交替变化判断隔行还是逐行最后用两个 SAV 之间的距离推断总样本数反查帧率。不要把帧率当成第一判断依据时序结构比帧率更稳定因为锁定结构只需要几行数据锁帧率则需要跨越多帧统计。5. 用一份小脚本对 YUV 裸流做 274M 合规性自检最后给一个能直接跑的合规性检查脚本它不做完整解码只验证时序结构是否满足 SMPTE 274M 的基本要求。输入是一段 8bit 4:2:2 的 YUV 裸流输出每行的 SAV/EAV 位置、垂直消隐行数和推断出的帧率族。#!/usr/bin/env python3 import sys def parse_xy(xy): f (xy 6) 1 v (xy 5) 1 h (xy 4) 1 p [(xy 0) 1, (xy 1) 1, (xy 2) 1, (xy 3) 1] return f, v, h, p def check_protect(f, v, h, p): return (p[0] (v ^ h) and p[1] (f ^ h) and p[2] (f ^ v) and p[3] (f ^ v ^ h)) def main(): if len(sys.argv) 2: print(usage: check_274m.py input.yuv) return with open(sys.argv[1], rb) as fh: data fh.read() markers [] i 0 while i len(data) - 4: if data[i] 0xFF and data[i1] 0x00 and data[i2] 0x00: f, v, h, p parse_xy(data[i3]) if check_protect(f, v, h, p): markers.append((i, f, v, h)) i 4 else: i 1 eav_pos [m[0] for m in markers if m[3] 1] if len(eav_pos) 2: print(未找到足够多的 EAV无法判定格式) return line_lens [eav_pos[k1] - eav_pos[k] for k in range(len(eav_pos)-1)] from collections import Counter common_len Counter(line_lens).most_common(1)[0][0] total_samples common_len # 8bit 4:2:2 下字节数即样本数 vbi_lines sum(1 for m in markers if m[3] 1 and m[2] 1) total_lines len(eav_pos) family {2200: 60/59.94 系, 2640: 50/25 系, 2750: 24 系}.get(total_samples, 未知) print(f总 EAV 数: {total_lines}) print(f垂直消隐行数: {vbi_lines}) print(f每行总样本: {total_samples}, 属于 {family}) print(fSAV/EAV 交替正常: {all(markers[k][3] ! markers[k1][3] for k in range(len(markers)-1))}) if __name__ __main__: main()运行方式python3 check_274m.py capture_1080p.yuv看输出时有个判断技巧总样本数落在 2200、2640、2750 三档之一基本可以认定行结构符合 274M如果出现 1920 或者是 2200 附近但不等于 2200 的数不要怀疑算法先查是不是把 10bit 流当成 8bit 读了或者源流本身经过了裁剪。垂直消隐行数如果跟预期帧率对不上再用 ffprobe 或其它工具确认场序很多采集驱动会在隔行转逐行时把 V 信号改掉标准合规的流不会出现这种情况。把这段脚本固化成一个通用工具函数以后每次拿到新格式的裸流都先跑一遍能省掉大量手动数行的时间。遇到奇偶场切换再加一个判断F bit 在隔行模式下行号奇偶变化是否同步就能顺带识别出隔行还是逐行。到此SMPTE 274M-2008 的三个核心部分——样本结构、数字表示、定时参考序列已经全部落到可执行代码上了。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进