guvcview 项目结构与模块说明

一、概述

guvcview 是一个 Linux 下的 USB 摄像头视频采集工具,支持实时预览、视频录制、截图等功能。项目采用模块化设计,将不同功能分离到独立的子库中。

地址: http://guvcview.sourceforge.net

git clone https://git.code.sf.net/p/guvcview/git-master guvcview-git-master

二、目录结构

guvcview/
├── guvcview/          # 主应用模块(GUI和主程序)
├── gview_render/      # 渲染模块(视频显示、OSD、特效)
├── gview_audio/       # 音频模块(设备管理、特效)
├── gview_encoder/     # 编码模块(音视频编码、文件封装)
├── gview_v4l2core/    # V4L2核心模块(设备访问、图像采集)
├── includes/          # 公共头文件
├── data/              # 资源文件(desktop、图标、appdata等)
├── po/                # 国际化翻译
├── build/             # 构建输出目录
├── CMakeLists.txt     # CMake构建配置
└── README.md          # 项目说明
.
├── 1.txt
├── AUTHORS
├── ChangeLog
├── CMakeLists.txt
├── COPYING
├── data
│   ├── CMakeLists.txt
│   ├── guvcview.1
│   ├── guvcview.appdata.xml.in
│   ├── guvcview.desktop.in
│   ├── guvcview.in
│   └── icons
│       └── guvcview.png
├── doc
│   ├── guvcview-源码记录.md
│   └── multica-desktop-0.3.11-linux-amd64.deb
├── guvcview
│   ├── CMakeLists.txt
│   ├── config.c
│   ├── config.h
│   ├── core_io.c
│   ├── core_io.h
│   ├── gui.c
│   ├── gui_gtk3_audioctrls.c
│   ├── gui_gtk3.c
│   ├── gui_gtk3_callbacks.c
│   ├── gui_gtk3_callbacks.h
│   ├── gui_gtk3.h
│   ├── gui_gtk3_h264ctrls.c
│   ├── gui_gtk3_meet4kctrls.c
│   ├── gui_gtk3_menu.c
│   ├── gui_gtk3_v4l2ctrls.c
│   ├── gui_gtk3_videoctrls.c
│   ├── gui.h
│   ├── gui_qt6_audioctrls.cpp
│   ├── gui_qt6_callbacks.cpp
│   ├── gui_qt6.cpp
│   ├── gui_qt6_h264ctrls.cpp
│   ├── gui_qt6.hpp
│   ├── gui_qt6_meet4kctrls.cpp
│   ├── gui_qt6_menu.cpp
│   ├── gui_qt6_v4l2ctrls.cpp
│   ├── gui_qt6_videoctrls.cpp
│   ├── gui_qt.h
│   ├── guvcview.c
│   ├── options.c
│   ├── options.h
│   ├── video_capture.c
│   └── video_capture.h
├── gview_audio
│   ├── audio.c
│   ├── audio_fx.c
│   ├── audio.h
│   ├── audio_portaudio.c
│   ├── audio_portaudio.h
│   ├── audio_pulseaudio.c
│   ├── audio_pulseaudio.h
│   ├── CMakeLists.txt
│   ├── core_time.c
│   ├── core_time.h
│   ├── gviewaudio.h
│   └── libgviewaudio.pc.in
├── gview_encoder
│   ├── audio_codecs.c
│   ├── avi.c
│   ├── avi.h
│   ├── CMakeLists.txt
│   ├── encoder.c
│   ├── encoder.h
│   ├── file_io.c
│   ├── file_io.h
│   ├── gviewencoder.h
│   ├── libav_encoder.c
│   ├── libgviewencoder.pc.in
│   ├── matroska.c
│   ├── matroska.h
│   ├── muxer.c
│   ├── packet.c
│   ├── packet.h
│   ├── stream_io.c
│   ├── stream_io.h
│   └── video_codecs.c
├── gview_render
│   ├── CMakeLists.txt
│   ├── gviewrender.h
│   ├── libgviewrender.pc.in
│   ├── render.c
│   ├── render_fx.c
│   ├── render.h
│   ├── render_osd_crosshair.c
│   ├── render_osd_vu_meter.c
│   ├── render_sdl2.c
│   ├── render_sdl2.h
│   ├── render_sfml.cpp
│   ├── render_sfml.h
│   └── render_sfml.hpp
├── gview_v4l2core
│   ├── CMakeLists.txt
│   ├── colorspaces.c
│   ├── colorspaces.h
│   ├── control_profile.c
│   ├── control_profile.h
│   ├── core_time.c
│   ├── core_time.h
│   ├── dct.c
│   ├── dct.h
│   ├── frame_decoder.c
│   ├── frame_decoder.h
│   ├── gviewv4l2core.h
│   ├── jpeg_decoder.c
│   ├── jpeg_decoder.h
│   ├── libgviewv4l2core.pc.in
│   ├── save_image_bmp.c
│   ├── save_image.c
│   ├── save_image.h
│   ├── save_image_jpeg.c
│   ├── save_image_png.c
│   ├── soft_autofocus.c
│   ├── soft_autofocus.h
│   ├── uvc_h264.c
│   ├── uvc_h264.h
│   ├── uvc_meet4k.c
│   ├── uvc_meet4k.ch
│   ├── uvc_meet4k.chp
│   ├── uvc_meet4k.h
│   ├── v4l2_controls.c
│   ├── v4l2_controls.h
│   ├── v4l2_core.c
│   ├── v4l2_core.h
│   ├── v4l2_devices.c
│   ├── v4l2_devices.h
│   ├── v4l2_formats.c
│   ├── v4l2_formats.h
│   ├── v4l2_xu_ctrls.c
│   └── v4l2_xu_ctrls.h
├── includes
│   └── gview.h
├── INSTALL
├── NEWS
├── po
└── README.md

12 directories, 188 files

三、模块依赖关系

┌─────────────────────────────────────────────────────────┐
│                     guvcview (主程序)                    │
│              ┌─────────────────────────────┐            │
│              │       GUI (GTK3/Qt6)        │            │
│              └─────────────────────────────┘            │
└─────────────────────────────────────────────────────────┘
                           │
        ┌──────────────────┼──────────────────┐
        │                  │                  │
        ▼                  ▼                  ▼
┌──────────────┐   ┌──────────────┐   ┌──────────────────┐
│ gview_render │   │ gview_audio  │   │   gview_encoder  │
│  (视频显示)  │   │  (音频采集)  │   │  (编码/封装)     │
└──────────────┘   └──────────────┘   └──────────────────┘
        │                  │                  │
        └──────────────────┴──────────────────┘
                           │
                           ▼
                ┌──────────────────────┐
                │   gview_v4l2core     │
                │  (V4L2设备核心)      │
                └──────────────────────┘

四、模块说明

1. guvcview(主应用模块)

负责用户界面、主程序入口和视频捕获调度。支持 GTK3 和 Qt6 两种 GUI 后端。

guvcview模块为完整的应用,包括主窗口的显示,参数配置窗口。
在这里插入图片描述

文件 说明
guvcview.c 主程序入口,初始化各模块,启动视频捕获线程
gui.c GUI抽象层,统一管理GTK3和Qt6界面切换
gui.h GUI公共接口定义
gui_gtk3.c GTK3界面实现,主窗口创建和显示
gui_gtk3.h GTK3界面头文件
gui_gtk3_callbacks.c GTK3事件回调处理(按钮、菜单等)
gui_gtk3_callbacks.h GTK3回调头文件
gui_gtk3_v4l2ctrls.c GTK3中V4L2控制面板
gui_gtk3_videoctrls.c GTK3中视频控制面板
gui_gtk3_audioctrls.c GTK3中音频控制面板
gui_gtk3_h264ctrls.c GTK3中H.264硬件编码控制
gui_gtk3_meet4kctrls.c GTK3中OBSBOT Meet 4K专用控制
gui_gtk3_menu.c GTK3菜单栏实现
gui_qt6.cpp Qt6界面实现
gui_qt6.hpp Qt6 C++类定义
gui_qt6.h Qt6 C语言包装头文件
gui_qt6_callbacks.cpp Qt6事件回调处理
gui_qt6_v4l2ctrls.cpp Qt6中V4L2控制面板
gui_qt6_videoctrls.cpp Qt6中视频控制面板
gui_qt6_audioctrls.cpp Qt6中音频控制面板
gui_qt6_h264ctrls.cpp Qt6中H.264硬件编码控制
gui_qt6_meet4kctrls.cpp Qt6中OBSBOT Meet 4K专用控制
gui_qt6_menu.cpp Qt6菜单栏实现
video_capture.c 视频捕获调度,协调采集、渲染、编码流程
video_capture.h 视频捕获头文件
options.c 命令行参数解析
options.h 命令行参数头文件
config.c 配置文件读写
config.h 配置管理头文件
core_io.c 设备IO操作(GPIO、I2C等)
core_io.h 设备IO头文件

2. gview_v4l2core(V4L2核心模块)

负责与Linux V4L2子系统交互,管理USB摄像头设备、视频格式转换、图像保存等功能。

文件 说明
v4l2_core.c V4L2核心功能,设备打开、流开启、帧捕获
v4l2_core.h V4L2核心头文件
v4l2_controls.c V4L2控制接口实现(亮度、对比度等)
v4l2_controls.h V4L2控制头文件
v4l2_devices.c 设备枚举和设备列表管理
v4l2_devices.h 设备管理头文件
v4l2_formats.c 视频格式处理(YUV、RGB等转换)
v4l2_formats.h 格式处理头文件
v4l2_xu_ctrls.c 扩展控制(UVC扩展单元)实现
v4l2_xu_ctrls.h 扩展控制头文件
colorspaces.c 色彩空间转换(YUV<->RGB)
colorspaces.h 色彩空间头文件
frame_decoder.c 通用帧解码器(处理MJPG、H.264等)
frame_decoder.h 帧解码头文件
jpeg_decoder.c JPEG解码实现
jpeg_decoder.h JPEG解码头文件
uvc_h264.c UVC H.264硬件编码支持
uvc_h264.h H.264支持头文件
uvc_meet4k.c OBSBOT Meet 4K摄像头专用控制
uvc_meet4k.h Meet4K控制头文件
uvc_meet4k.ch Meet4K控制参数宏定义
uvc_meet4k.chp Meet4K控制宏处理器
soft_autofocus.c 软件自动对焦实现
soft_autofocus.h 自动对焦头文件
save_image.c 图像保存统一接口
save_image.h 图像保存头文件
save_image_bmp.c BMP格式保存
save_image_jpeg.c JPEG格式保存
save_image_png.c PNG格式保存
control_profile.c 控制配置文件管理
control_profile.h 控制配置头文件
dct.c DCT变换辅助函数
dct.h DCT头文件
core_time.c 高精度时间获取
core_time.h 时间工具头文件
gviewv4l2core.h 对外主头文件,定义V4L2核心公共API

1. save_image.h

图像保存模块的头文件,定义了统一的图像保存接口。

主要函数:

函数 说明
save_frame_image 图像保存统一入口,根据格式参数调用对应的保存函数
save_image_jpeg 将帧数据保存为JPEG格式文件
save_image_bmp 将帧数据保存为BMP格式文件
save_image_png 将帧数据保存为PNG格式文件

结构关系:

图像保存模块提供将视频帧保存为不同格式图片文件的功能,支持 RAW、JPEG、BMP、PNG 四种格式。

save_image.h (公共接口)
    │
├── save_image.c (统一调度入口)
│       ├── save_image_jpeg.c (JPEG编码保存)
│       ├── save_image_bmp.c  (BMP格式保存)
│       └── save_image_png.c  (PNG格式保存)

2. save_image.c

图像保存的统一接口实现,提供格式分发和数据写入功能。

主要函数:

函数 说明
v4l2core_save_data_to_file 将原始数据写入文件,使用全缓冲模式提高写入效率
save_frame_image 根据格式类型(RAW/JPG/BMP/PNG)分发到对应的保存函数

格式支持:

  • IMG_FMT_RAW - 直接保存原始帧数据
  • IMG_FMT_JPG - 调用JPEG编码保存
  • IMG_FMT_BMP - 转换为BMP格式保存
  • IMG_FMT_PNG - 转换为PNG格式保存

3. save_image_bmp.c

BMP格式图像保存实现,将YUV帧数据转换为24位BMP文件。

主要函数:

函数 说明
save_bmp 内部函数,构造BMP文件头和信息头,写入RGB像素数据
save_image_bmp 分配RGB缓冲区,调用 yu12_to_dib24 将YUV转换为DIB格式,保存为BMP文件

数据结构:

  • bmp_file_header_t - BMP文件头结构(文件类型、大小、数据偏移)
  • bmp_info_header_t - BMP信息头结构(宽度、高度、位深度、压缩方式等)

注意事项:

  • BMP使用DIB(设备无关位图)格式,像素按从下到上顺序存储
  • 固定使用24位色深(RGB888)

4. save_image_jpeg.c

JPEG格式图像保存实现,包含完整的JPEG编码器,无需外部库支持。

主要函数:

函数 说明
initialization 初始化JPEG编码器上下文,设置图像尺寸和MCU参数
initialize_quantization_tables 初始化亮度和色度量化表
read_422_format 将YUYV格式数据分离为Y、Cb、Cr三个平面
encode_MCU 编码单个MCU(最小编码单元),包含DCT变换、量化、哈夫曼编码
huffman 哈夫曼编码处理,对DC和AC系数进行编码
write_markers 写入JPEG文件标记(SOI、APP0、量化表、SOF、SOS等)
close_bitstream 关闭比特流,添加EOI结束标记
encode_jpeg JPEG编码主函数,遍历所有MCU进行编码
save_image_jpeg 分配编码缓冲区,执行JPEG编码并保存文件

编码流程:

  1. 初始化编码器上下文和量化表
  2. 将YU12格式转换为YUYV格式
  3. 按16x8 MCU单元遍历图像
  4. 对每个MCU执行:电平移位 → DCT变换 → 量化 → 哈夫曼编码
  5. 写入文件头和编码数据

关键数据结构:

  • jpeg_encoder_ctx_t - JPEG编码器上下文,包含图像参数、量化表、MCU缓冲区等
  • jpeg_file_header_t - JFIF文件头结构

5. save_image_png.c

PNG格式图像保存实现,使用 libpng 库进行编码。

主要函数:

函数 说明
save_png 使用libpng库将RGB数据保存为PNG文件,设置图像元信息
save_image_png 分配RGB缓冲区,调用 yu12_to_rgb24 将YUV转换为RGB,保存为PNG文件

功能特点:

  • 使用libpng库进行标准PNG编码
  • 自动添加文本元数据(标题、软件名称、描述)
  • 支持错误处理和资源清理

3. gview_render(渲染模块)

负责视频帧的显示渲染、OSD叠加层绘制和视频特效处理。支持 SDL2 和 SFML 两种渲染后端。

文件 说明
render.c 渲染核心实现,提供渲染接口抽象层,管理SDL2/SFML后端切换、OSD叠加层和事件回调
render.h 内部头文件,声明OSD渲染功能接口(VU表、十字准星、特效滤镜)
gviewrender.h 对外主头文件,定义渲染API类型、事件ID、特效标志、OSD标志及所有公共接口和数据结构
render_sdl2.c SDL2渲染后端实现,负责SDL2窗口初始化、纹理处理、帧显示和键盘事件分发
render_sdl2.h SDL2后端头文件,声明初始化、帧渲染、标题设置、事件分发和清理函数接口
render_sfml.cpp SFML渲染后端C++实现,提供窗口创建、YUV到RGB转换(支持shader加速)、帧渲染和事件处理
render_sfml.h SFML后端的C语言包装头文件,声明与SFML交互的C接口函数
render_sfml.hpp SFML后端C++类定义头文件,定义 SFMLRender 类及其成员变量和方法
render_osd_crosshair.c OSD十字准星渲染,在YUV帧数据上直接绘制可配置颜色和大小的十字准星标记
render_osd_vu_meter.c OSD音量电平表渲染,绘制双声道VU表显示音频信号强度,支持峰值保持和逐渐衰减效果
render_fx.c 视频特效滤镜实现,提供镜像、翻转、黑白、模糊、镜头畸变、粒子效果等多种图像处理效果

模块架构概览:

gviewrender.h (公共API)
    │
    ├── render.c  (抽象调度层)
    │       ├── render_sdl2.c   (SDL2后端)
    │       └── render_sfml.cpp (SFML后端)
    │
    └── OSD 叠加层(直接操作YUV帧数据,与渲染后端无关)
            ├── render_osd_crosshair.c  (十字准星)
            ├── render_osd_vu_meter.c   (音量表)
            └── render_fx.c             (视频特效滤镜)

1. render_osd_crosshair.c

这个函数是界面中OSD配置的功能
在这里插入图片描述
就是相机中的这个十字准星
在这里插入图片描述
函数解读:
render_osd_crosshair 调用 plot_crosshair_yu12函数,通过对传入的YUV图像进行绘制。

render_osd_crosshair 函数主要是处理一下十字准星的颜色,将RGB转为YUV格式,具体实现在plot_crosshair_yu12。

/*
 * 渲染十字准星
 * args:
 *   frame - 传入 yuyv 数据的指针
 *   width - 图像宽度
 *   height - 图像高度
 *
 * asserts:
 *   none
 *
 * returns: none
 */
void render_osd_crosshair(uint8_t *frame, int width, int height)
{
	yuv_color_t color;
	color.y = 0;
	color.u = 0;
	color.v = 0;

	uint32_t rgb_color = render_get_crosshair_color();
	int size = render_get_crosshair_size();

	uint8_t r = (uint8_t) ((rgb_color & 0x00FF0000) >> 16);
	uint8_t g = (uint8_t) ((rgb_color & 0x0000FF00) >> 8);
	uint8_t b = (uint8_t) (rgb_color & 0x000000FF);

	color.y = CLIP(0.299*(r-128) + 0.587*(g-128) + 0.114*(b-128) + 128) ;
	color.u = CLIP(-0.147*(r-128) - 0.289*(g-128) + 0.436*(b-128) + 128);
	color.v = CLIP(0.615*(r-128) - 0.515*(g-128) - 0.100*(b-128) + 128);

	plot_crosshair_yu12(frame, size, width, height, &color);
}

准星由四条线构成,需要对Y和UV分量单独覆盖

/*
 * plot a crosshair in a yu12 frame (planar)
 * args:
 *   frame - pointer to yu12 frame data
 *   size  - frame line size in pixels (width)十字准星短线的长度
 *   width - width,图像宽度
 *   height - height,图像高度
 *   color - line color
 *
 * asserts:
 *   none
 *
 * returns: none
 */
static void plot_crosshair_yu12(uint8_t *frame, int size, int width, int height, yuv_color_t *color)
{
	//找到YUV数据的地址
	uint8_t *py = frame;
	uint8_t *pu = frame + (width * height);
	uint8_t *pv = pu + ((width * height) / 4); 

	/*y - 1st vertical line*/
	int h = (height-size)/2;
	for(h = (height-size)/2; h < height/2 - 2; h++)
	{
		py = frame + (h * width) + width/2;
		*py = color->y;
	}
	/*y - 1st horizontal line*/
	int w = (width-size)/2;
	for(w = (width-size)/2; w < width/2 - 2; w++)
	{
		py = frame + ((height/2) * width) + w;
		*py = color->y;
	}
	/*y - 2nd horizontal line*/
	for(w = width/2 + 2; w < (width+size)/2; w++)
	{
		py = frame + ((height/2) * width) + w;
		*py = color->y;
	}
	/*y - 2nd vertical line*/
	for(h = height/2 + 2; h < (height+size)/2; h++)
	{
		py = frame + (h * width) + width/2;
		*py = color->y;
	}
				
	/*u v - 1st vertical line*/
	for(h = (height-size)/4; h < height/4 - 1; h++) /*every two rows*/
	{
		pu = frame + (width * height) + (h * width/2) + width/4;
		*pu = color->u;
		pv = pu + (width * height)/4;
		*pv = color->v;
	}
	/*u v - 1st horizontal line*/
	for(w = (width-size)/4; w < width/4 - 1; w++) /*every two rows*/
	{
		pu = frame + (width * height) + ((height/4) * width/2) + w;
		*pu = color->u;
		pv = pu + (width * height)/4;
		*pv = color->v;
	}
	/*u v - 2nd horizontal line*/
	for(w = width/4 + 1; w < (width+size)/4; w++) /*every two rows*/
	{
		pu = frame + (width * height) + ((height/4) * width/2) + w;
		*pu = color->u;
		pv = pu + (width * height)/4;
		*pv = color->v;
	}
	/*u v - 2nd vertical line*/
	for(h = height/4 + 1; h < (height+size)/4; h++) /*every two rows*/
	{
		pu = frame + (width * height) + (h * width/2) + width/4;
		*pu = color->u;
		pv = pu + (width * height)/4;
		*pv = color->v;
	}
}

2. render_osd_vu_meter.c

这个文件是绘制音量键区域
在这里插入图片描述
激活就在录制视频时候激活
在这里插入图片描述

这个文件有三个函数

  • plot_box_yu12:音量块儿
  • plot_line_yu12:短线
  • render_osd_vu_meter:组合上述音量块和短线组成的控件

在这里插入图片描述

3. render_fx.c

这个文件中是特殊的滤镜特效实现,具体内容比较多。
在这里插入图片描述
实现的函数具体如下
在这里插入图片描述


4. gview_audio(音频模块)

负责音频设备管理、音频流捕获和音频特效处理。支持 PulseAudio 和 PortAudio 两种音频后端。

文件 说明
audio.c 音频核心实现,提供音频缓冲管理、设备选择、流控制以及与不同音频API的集成
audio.h 内部头文件,定义音频上下文结构体和内部函数声明
gviewaudio.h 对外主头文件,定义音频库的数据结构、常量、枚举和公共API接口
audio_fx.c 音频特效实现,包括回声、混响、失真、哇音、变调等效果及其滤波器和信号处理算法
audio_portaudio.c PortAudio后端实现,提供设备枚举、流捕获和回调处理
audio_portaudio.h PortAudio后端头文件,声明初始化、设备设置、启动停止和清理函数
audio_pulseaudio.c PulseAudio后端实现,支持异步流捕获和设备管理
audio_pulseaudio.h PulseAudio后端头文件,声明初始化、设备设置、启动停止和清理函数
core_time.c 高精度时间工具,提供纳秒级单调时间获取,用于音频时间戳同步
core_time.h 时间工具头文件,声明单调时间获取接口

模块架构概览:

gviewaudio.h (公共API)
    │
    ├── audio.c  (核心调度层)
    │       ├── audio_pulseaudio.c  (PulseAudio后端)
    │       └── audio_portaudio.c   (PortAudio后端)
    │
    ├── audio_fx.c  (音频特效处理)
    │
    └── core_time.c (时间工具)

5. gview_encoder(编码模块)

负责音视频数据的编码和文件封装输出。使用 libavcodec 进行编解码,支持 AVI、Matroska(MKV)、WebM 等容器格式。

文件 说明
encoder.c 编码器核心实现,使用libavcodec进行软件编码,管理环形缓冲区和编码线程,处理音视频数据的编码和写入
encoder.h 内部头文件,定义编解码器数据结构、音视频格式常量、版本检查宏及编解码器相关函数接口
gviewencoder.h 对外主头文件,定义编码库的公共接口、数据结构、常量宏和所有对外暴露的函数
audio_codecs.c 音频编解码器管理,定义支持的音频编码格式(PCM、MP2、MP3、AAC、Vorbis等),提供编解码器配置
video_codecs.c 视频编解码器管理,定义支持的视频编码格式(MJPEG、MPEG4、H.264、VP8、VP9等),提供编解码器配置
avi.c AVI容器封装实现,负责AVI文件创建、流头写入、音视频数据包写入和索引生成,支持OpenDML扩展
avi.h AVI封装头文件,定义RIFF头、索引条目等数据结构和函数接口
matroska.c Matroska/WebM容器封装实现,负责EBML元素写入、流头构建、集群管理和数据包缓存,支持seek表和提示点
matroska.h Matroska封装头文件,定义EBML和Matroska元素ID常量、上下文数据结构和函数接口
muxer.c 复用器统一接口,根据指定格式(AVI/MKV/WEBM)初始化对应封装器,协调音视频数据写入
packet.c 数据包管理工具,提供数据包克隆、释放、链表操作,用于编码后数据包的排序和缓冲
packet.h 数据包头文件,定义简化数据包结构、链表项和操作函数接口
file_io.c 文件I/O实现,提供缓冲写入、文件定位、字节序转换等功能
file_io.h 文件I/O头文件,定义写入器数据结构和各种写入操作函数接口
stream_io.c 流管理实现,负责流链表的创建、添加、获取和销毁
stream_io.h 流管理头文件,定义流类型枚举、流数据结构和流管理函数接口
libav_encoder.c libavcodec底层封装,提供视频帧准备、Xiph头部分割等辅助函数,直接操作libavcodec底层结构

模块架构概览:

gviewencoder.h (公共API)
    │
    ├── encoder.c ─── libav_encoder.c  (编码引擎)
    │       │
    │       ├── audio_codecs.c  (音频编解码器列表)
    │       └── video_codecs.c  (视频编解码器列表)
    │
    ├── muxer.c  (复用器调度)
    │       ├── avi.c      (AVI封装)
    │       └── matroska.c  (MKV/WebM封装)
    │
    ├── packet.c    (数据包管理)
    ├── stream_io.c (流管理)
    └── file_io.c   (文件I/O)


五、设计模式总结

1. 各模块特点

模块 公共API 抽象后端 功能
gview_render gviewrender.h SDL2 / SFML 视频显示、OSD、特效
gview_audio gviewaudio.h PulseAudio / PortAudio 音频采集、特效
gview_encoder gviewencoder.h AVI / MKV / WebM 音视频编码、文件封装
gview_v4l2core gviewv4l2core.h - V4L2设备访问、格式转换
guvcview - GTK3 / Qt6 GUI界面、主程序调度

2. 通用设计模式

  1. 公共API暴露:各库通过 gview*.h 主头文件对外暴露统一接口
  2. 后端抽象:通过抽象层屏蔽不同后端差异(如SDL2/SFML、GTK3/Qt6)
  3. 模块解耦:各模块独立编译,通过头文件接口通信
  4. 回调机制:通过事件和回调实现模块间异步通信

六、YUV 色彩空间基础知识

YUV 是视频处理领域最核心的色彩空间表示方式。本章将深入讲解 YUV 的原理、格式分类以及项目中的具体实现。

1. YUV 概述

1.1 历史背景

YUV 色彩空间起源于模拟电视时代。1953 年,NTSC(美国国家电视系统委员会)制定了彩色电视标准,为了兼容现有的黑白电视接收机,工程师们将色彩信息与亮度信息分离,形成了 YUV 色彩模型:

  • Y (Luma):亮度分量,单独传输即可显示黑白图像
  • U (Cb):蓝色色差分量(Blue-difference Chroma)
  • V (Cr):红色色差分量(Red-difference Chroma)

这种设计使得黑白电视机只需接收 Y 分量即可正常显示,彩色电视机则解码完整的 YUV 信号。

1.2 分量含义详解

Y(亮度)

Y = 0.299R + 0.587G + 0.114B

Y 分量包含了人眼最敏感的亮度信息,其权重反映了人眼对绿光最敏感、对蓝光最不敏感的视觉特性。

U/Cb(蓝色色差)

U = 0.5 × (B - Y) = B - Y / 2.03

表示蓝色分量与亮度的差值,范围通常为 -128 ~ +127(或 0 ~ 255,偏移 128)。

V/Cr(红色色差)

V = 0.713 × (R - Y) = R - Y / 1.14

表示红色分量与亮度的差值。

1.3 为什么视频编码选择 YUV

人眼视觉特性

人眼对亮度变化极其敏感(可分辨约 500 级灰度),但对色度变化相对迟钝。利用这一特性,可以在不明显降低画质的前提下大幅压缩色度信息:

┌─────────────────────────────────────────────────────────┐
│                    人眼视觉敏感度                         │
├─────────────────────────────────────────────────────────┤
│  亮度 (Y)     ████████████████████████████████  极高    │
│  蓝色色差 (U)  ████████░░░░░░░░░░░░░░░░░░░░░░░░  较低    │
│  红色色差 (V)  ████████░░░░░░░░░░░░░░░░░░░░░░░░  较低    │
└─────────────────────────────────────────────────────────┘

带宽优势

以 4:2:0 采样为例:

  • RGB 存储:每像素 3 字节
  • YUV420 存储:每像素 1.5 字节
  • 压缩比:50%,画质损失几乎不可见

兼容性

YUV 格式天然支持向下兼容,黑白设备只需处理 Y 分量。

2. 色度子采样原理

2.1 采样比例定义

色度子采样(Chroma Subsampling)使用 J:a:b 表示法描述采样模式:

  • J:水平基准采样点数(通常为 4)
  • a:第一行的色度采样点数
  • b:第二行的色度采样点数

4:2:0 水平垂直各减半

Y

Y

Y

Y

Y

Y

Y

Y

Cb

4:2:2 水平减半

Y

Y

Y

Y

Cb

Cb

4:4:4 无压缩

Y

Y

Y

Y

Cb

Cb

Cb

Cb

2.2 常见采样格式对比

格式 色度采样 数据量比例 典型应用场景
4:4:4 无压缩 100% 专业视频制作、PNG 图像
4:2:2 水平减半 67% 广播级视频、专业摄像机
4:2:0 水平垂直各减半 50% 消费级视频、网络流媒体、H.264
4:1:1 水平 1/4 50% DV 数字视频

2.3 数据量计算示例

以 1920×1080 分辨率为例:

格式 Y 数据量 UV 数据量 总数据量 每帧大小
RGB24 - - 1920×1080×3 6,220,800 字节
4:4:4 2,073,600 2,073,600×2 6,220,800 字节 100%
4:2:2 2,073,600 1,036,800×2 4,147,200 字节 67%
4:2:0 2,073,600 518,400×2 3,110,400 字节 50%

3. 存储格式分类

YUV 数据在内存中的存储方式分为三大类:打包格式、平面格式和半平面格式。

3.1 打包格式(Packed/Interleaved)

定义:每个像素的 Y、U、V 分量交错存储在连续内存中。

YUYV 打包格式

Y0

U0

Y1

V0

Y2

U2

Y3

V2

特点

  • 优点:像素数据连续,便于直接显示和硬件采集
  • 缺点:图像处理时需要解包,效率较低

项目支持的打包格式

格式 排列方式 每像素字节数
YUYV (YUY2) Y0 U0 Y1 V0 2 字节(平均)
UYVY U0 Y0 V0 Y1 2 字节(平均)
YVYU Y0 V0 Y1 U0 2 字节(平均)
VYUY V0 Y0 U0 Y1 2 字节(平均)

3.2 平面格式(Planar)

定义:Y、U、V 三个分量分别存储在三个独立的连续内存块中。

YU12 平面格式内存布局

V 平面

V

V

V

V

U 平面

U

U

U

U

Y 平面

Y

Y

Y

Y

Y

Y

Y

Y

特点

  • 优点:便于图像处理(如滤波、变换),Y/U/V 可独立操作
  • 缺点:需要额外内存管理

项目内部格式:YU12 (I420),所有输入格式都转换为此格式进行处理。

3.3 半平面格式(Semi-Planar)

定义:Y 分量独立存储,U 和 V 分量交错存储。

NV12 半平面格式

UV 交错平面

U

V

U

V

Y 平面

Y

Y

Y

Y

特点

  • 兼顾打包和平面格式的优点
  • 硬件编解码器(如 VideoToolbox、MediaCodec)常用格式
格式 UV 排列 应用平台
NV12 UVUVUV… Android、iOS
NV21 VUVUVU… Android Camera

4. 常见格式详解

4.1 YU12/I420(项目内部格式)

YU12 是 guvcview 项目的内部处理格式,属于 4:2:0 平面格式。

FourCC 代码I420

内存大小计算

总大小 = width × height × 3 / 2
       = Y平面 + U平面 + V平面
       = (width × height) + (width/2 × height/2) + (width/2 × height/2)

内存布局(以 4×4 像素为例):

地址偏移    内容
────────────────────────────────
0x00-0x0F   Y 平面 (16 字节)
────────────────────────────────
0x10-0x13   U 平面 (4 字节)
────────────────────────────────
0x14-0x17   V 平面 (4 字节)
────────────────────────────────
总大小: 24 字节 = 16 + 4 + 4

4.2 YV12

与 YU12 的唯一区别是 U/V 平面位置互换:

YU12: | Y 平面 | U 平面 | V 平面 |
YV12: | Y 平面 | V 平面 | U 平面 |

4.3 NV12/NV21

NV12

| Y 平面 (width × height) | UV 交错 (width/2 × height) |
排列: U0 V0 U1 V1 U2 V2 ...

NV21(Android Camera 默认输出):

| Y 平面 (width × height) | VU 交错 (width/2 × height) |
排列: V0 U0 V1 U1 V2 U2 ...

4.4 YUYV/YUY2

V4L2 摄像头最常用的输出格式,属于 4:2:2 打包格式。

内存排列

像素:     P0        P1        P2        P3
数据:   Y0 U0    Y1 V0    Y2 U2    Y3 V2
         │  │    │  │     │  │    │  │
         │  └────┼──┘     │  └────┼──┘
         │   共享 U/V     │   共享 U/V

内存大小width × height × 2 字节

5. 内存布局图示

5.1 4:2:0 格式对比

NV12 - 半平面格式

Y 平面
width × height

UV 交错平面
width × height/2

YU12 (I420) - 平面格式

Y 平面
width × height

U 平面
width/2 × height/2

V 平面
width/2 × height/2

5.2 实际数据量示例(640×480 图像)

格式 计算 数据量
YU12 640×480 + 320×240×2 460,800 字节
NV12 640×480 + 640×240 460,800 字节
YUYV 640×480×2 614,400 字节
RGB24 640×480×3 921,600 字节

6. YUV 与 RGB 转换公式

6.1 BT.601 标准转换(SD 视频)

BT.601 是标清视频(480i/576i)使用的色彩空间标准。

RGB → YUV(Full Range)

Y  =  0.299  × R + 0.587  × G + 0.114  × B
Cb = -0.169  × R - 0.331  × G + 0.500  × B + 128
Cr =  0.500  × R - 0.419  × G - 0.081  × B + 128

YUV → RGB

R = Y                    + 1.402   × (Cr - 128)
G = Y - 0.344 × (Cb - 128) - 0.714 × (Cr - 128)
B = Y + 1.772 × (Cb - 128)

6.2 BT.709 标准转换(HD 视频)

BT.709 是高清视频(720p/1080p)使用的色彩空间标准。

RGB → YUV

Y  =  0.2126 × R + 0.7152 × G + 0.0722 × B
Cb = -0.1146 × R - 0.3854 × G + 0.5000 × B + 128
Cr =  0.5000 × R - 0.4542 × G - 0.0458 × B + 128

YUV → RGB

R = Y                     + 1.5748 × (Cr - 128)
G = Y - 0.1873 × (Cb - 128) - 0.4681 × (Cr - 128)
B = Y + 1.8556 × (Cb - 128)

6.3 BT.2020 标准转换(UHD 视频)

BT.2020 是超高清视频(4K/8K)使用的色彩空间标准,支持更广的色域(Wide Color Gamut, WCG)。

色域对比

色域覆盖范围

BT.601
SD 标清

BT.709
HD 高清

BT.2020
UHD 超高清

DCI-P3
数字影院

RGB → YUV(Full Range)

Y  =  0.2627 × R + 0.6780 × G + 0.0593 × B
Cb = -0.1396 × R - 0.3604 × G + 0.5000 × B + 128
Cr =  0.5000 × R - 0.4598 × G - 0.0402 × B + 128

YUV → RGB

R = Y                     + 1.4746 × (Cr - 128)
G = Y - 0.1646 × (Cb - 128) - 0.5714 × (Cr - 128)
B = Y + 1.8814 × (Cb - 128)

与 BT.709 的主要区别

参数 BT.709 BT.2020 说明
绿色权重 0.7152 0.6780 BT.2020 绿色权重降低
蓝色权重 0.0722 0.0593 BT.2020 蓝色权重更低
红色权重 0.2126 0.2627 BT.2020 红色权重提高
色域 ~35.9% ~75.8% BT.2020 覆盖更广色域
位深 8/10 bit 10/12 bit BT.2020 支持更高位深

6.4 BT.2100 标准(HDR 视频)

BT.2100 在 BT.2020 色域基础上增加了高动态范围(HDR)支持,定义了两种 HDR 传输曲线:

PQ (Perceptual Quantizer) 曲线

  • 也称 ST.2084 标准
  • 最大亮度可达 10,000 nits
  • 适合 HDR10、Dolby Vision

HLG (Hybrid Log-Gamma) 曲线

  • 兼容 SDR 显示
  • 最大亮度约 1,000 nits
  • 适合广播级 HDR

HDR 转换注意事项

SDR (BT.709)  ──────> HDR (BT.2100 PQ)
      │                     │
      │  色域映射           │  亮度映射
      │  (Gamut Mapping)   │  (Tone Mapping)
      ▼                     ▼
   色彩空间转换 + 动态范围扩展

6.5 标准对比总览

标准 年份 分辨率 色域 位深 动态范围
BT.601 1982 SD (480i/576i) 8 bit SDR
BT.709 1990 HD (720p/1080p) 标准 8/10 bit SDR
BT.2020 2012 UHD (4K/8K) 广色域 10/12 bit SDR
BT.2100 2016 UHD (4K/8K) 广色域 10/12 bit HDR (PQ/HLG)

6.6 项目代码实现

项目在 colorspaces.c 中实现了 YUV 与 RGB 的转换:

yu12_to_rgb24 函数(简化版):

void yu12_to_rgb24(uint8_t *out, uint8_t *in, int width, int height) {
    uint8_t *py = in;                          // Y 平面起始
    uint8_t *pu = in + width * height;         // U 平面起始
    uint8_t *pv = pu + (width * height) / 4;   // V 平面起始
    
    for (int i = 0; i < width * height; i++) {
        int y = py[i] - 16;
        int u = pu[i/4] - 128;
        int v = pv[i/4] - 128;
        
        // YUV → RGB 转换
        int r = (298 * y + 409 * v + 128) >> 8;
        int g = (298 * y - 100 * u - 208 * v + 128) >> 8;
        int b = (298 * y + 516 * u + 128) >> 8;
        
        // 限幅到 0-255
        out[i * 3 + 0] = CLIP(r);
        out[i * 3 + 1] = CLIP(g);
        out[i * 3 + 2] = CLIP(b);
    }
}

定点数优化

项目中使用定点数运算(>> 8 代替除法)提高性能,这是嵌入式系统常见的优化手段。

7. 项目中的格式转换实现

7.1 转换策略

guvcview 采用统一的格式转换策略:

摄像头输出
YUYV/UYVY/NV12...

转换为 YU12

内部处理
编码/特效/OSD

输出
JPEG/PNG/BMP

选择 YU12 作为内部格式的原因

  1. 编码友好:H.264、VP8 等编码器原生支持 4:2:0 平面格式
  2. 处理高效:Y/U/V 分离便于图像处理算法实现
  3. 内存连续:平面格式有利于 CPU 缓存命中

7.2 支持的输入格式

项目 colorspaces.h 定义了丰富的格式转换函数:

格式类型 函数 采样比 说明
4:2:2 打包 yuyv_to_yu12 4:2:2 最常用摄像头格式
uyvy_to_yu12 4:2:2 U 在前的变体
yvyu_to_yu12 4:2:2 V 在前的变体
vyuy_to_yu12 4:2:2 少见变体
4:2:0 平面 yv12_to_yu12 4:2:0 U/V 位置互换
4:2:0 半平面 nv12_to_yu12 4:2:0 Android/iOS 常用
nv21_to_yu12 4:2:0 Android Camera 默认
4:4:4 y444_to_yu12 4:4:4 无损色度
RGB rgb24_to_yu12 - RGB888 转 YUV
bgr24_to_yu12 - BGR888 转 YUV
灰度 grey_to_yu12 - 单通道灰度
Bayer bayer_to_rgb24 - 原始传感器数据

7.3 转换函数示例

yuyv_to_yu12 实现(简化版):

void yuyv_to_yu12(uint8_t *out, uint8_t *in, int width, int height) {
    uint8_t *y_plane = out;
    uint8_t *u_plane = out + width * height;
    uint8_t *v_plane = u_plane + (width * height) / 4;
    
    // YUYV: Y0 U0 Y1 V0 Y2 U2 Y3 V2 ...
    // 每 4 字节包含 2 个像素的 Y 和 1 个共享的 UV
    for (int i = 0; i < width * height; i += 2) {
        y_plane[i]   = in[i * 2];     // Y0
        y_plane[i+1] = in[i * 2 + 2]; // Y1
        
        // UV 分量水平方向 2:1 下采样
        u_plane[i/2] = in[i * 2 + 1]; // U0
        v_plane[i/2] = in[i * 2 + 3]; // V0
    }
}

7.4 性能考量

优化方向 方法 适用场景
减少内存拷贝 原地转换 源数据不再需要
SIMD 加速 SSE/NEON 指令 批量转换
多线程 分块并行处理 高分辨率图像
GPU 加速 Shader/OpenCL 实时视频处理

项目中 colorspaces.c 采用直接的 C 语言实现,保证了可移植性。对于性能敏感场景,可考虑添加 SIMD 优化版本。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐