guvcview-源码记录
guvcview源码记录
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编码并保存文件 |
编码流程:
- 初始化编码器上下文和量化表
- 将YU12格式转换为YUYV格式
- 按16x8 MCU单元遍历图像
- 对每个MCU执行:电平移位 → DCT变换 → 量化 → 哈夫曼编码
- 写入文件头和编码数据
关键数据结构:
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. 通用设计模式
- 公共API暴露:各库通过
gview*.h主头文件对外暴露统一接口 - 后端抽象:通过抽象层屏蔽不同后端差异(如SDL2/SFML、GTK3/Qt6)
- 模块解耦:各模块独立编译,通过头文件接口通信
- 回调机制:通过事件和回调实现模块间异步通信
六、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:第二行的色度采样点数
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 (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 三个分量分别存储在三个独立的连续内存块中。
特点:
- 优点:便于图像处理(如滤波、变换),Y/U/V 可独立操作
- 缺点:需要额外内存管理
项目内部格式:YU12 (I420),所有输入格式都转换为此格式进行处理。
3.3 半平面格式(Semi-Planar)
定义:Y 分量独立存储,U 和 V 分量交错存储。
特点:
- 兼顾打包和平面格式的优点
- 硬件编解码器(如 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 格式对比
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)。
色域对比:
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 采用统一的格式转换策略:
选择 YU12 作为内部格式的原因:
- 编码友好:H.264、VP8 等编码器原生支持 4:2:0 平面格式
- 处理高效:Y/U/V 分离便于图像处理算法实现
- 内存连续:平面格式有利于 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 优化版本。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)