对wine下DirectShow学习
目标
- 实现wine下读取yuy2视频流
开发调研准备
- yuyv/yuy2/yuv/rgb24是图像/视频像素格式(Pixel Format)。
- rgb24:RGB(红、绿、蓝),每个像素 24 位(3 字节),即 R、G、B 各占 8 位(0~255),通常叫做无损画质。
- yuv:是广义叫法,用Y:亮度(Luma),决定灰度。
- yuy2/yuyv:是同一种格式的不同叫法(YUY2 是 Windows 命名,YUYV 是 Linux/V4L2 命名),是一种压缩格式
- rgb24和yuy2细节差距:
- 一个像素的颜色,既可以用 (R=100, G=150, B=200) 表示,也可以通过数学公式转换成 (Y=160, U=-20, V=30) 这样的 YUV 值。
- rgb:每个像素 3 字节(R、G、B),2 个像素 = 6 字节,每个像素内容是直接描述颜色的三原色成分
- yuy2/yuyv:把颜色拆成 亮度(Y) + 色度(U/V),每 2 个像素 = 4 字节(Y₀, U, Y₁, V),其中 U 和 V 被两个像素共享。
- 总结:原本两个像素用6个字节,原色用rgb显示,yuv是每两个像素用两个字节共享的是色度,rgb跟yuv像素内容不就是不一样的,rgb里面是红绿蓝,yuv里面是亮度色度,两者可以互相转换,但是yuv是压缩格式会出现颜色模糊和失真。
常见显示错误
- 直接用 YUV 数据当 RGB 显示:
- 图像整体呈青绿色或粉紫色
- 有明显的条纹、噪点、色块
- 人物或物体轮廓可能隐约可见(因为 Y 亮度信息还在),但颜色完全错误
- 水平方向有2像素周期的重复图案(因为 YUY2 是每2像素打包)
- 用 RGB 当 YUV 显示
- 视频播放器可能崩溃、花屏
- 或显示高对比度黑白+彩色噪点
- 为什么yuv内容和rgb内容差距很大但是 YUV 数据当 RGB 显示可以隐约查看到内容:Y 亮度保留,U/V 被误用,Y被rgb中的红色截取,红色和亮度强关联,所以能隐约看到内容。
- 直接用 YUV 数据当 RGB 显示:
相互转换
- 数学公式
- RGB → YCbCr(编码)
1
2
3
4
5
6假设 R、G、B 的取值范围是 [0, 255](8-bit)
Y = 0.299·R + 0.587·G + 0.114·B
Cb = -0.168736·R - 0.331264·G + 0.5·B + 128
Cr = 0.5·R - 0.418688·G - 0.081312·B + 128
Y 范围:[0, 255]
Cb/Cr 范围:[0, 255](中心在 128,128 表示无色) - YCbCr → RGB(解码)
1
2
3R = Y + 1.402·(Cr - 128)
G = Y - 0.344136·(Cb - 128) - 0.714136·(Cr - 128)
B = Y + 1.772·(Cb - 128)
- RGB → YCbCr(编码)
- 数学公式
wine下通过linux采集视频数据流程
- wine下通过linux采集的代码是在
dlls/qcap/v4l.c,在初始化的时候会检测是否存在v4l2,若存在则通过v4l2库进行设备打开创建/关闭/ioctl/read
- 设备创建
- window函数:CoCreateInstance
- v4l_device_create:打开 /dev/videoN,枚举所有支持的格式(仅限 YUYV 和 BGR24),构建 caps 列表。
- 调用if (xioctl(fd, VIDIOC_QUERYCAP, &caps) == -1)查看当前设备是否支持视频采集和 read()
- 循环调用获取:VIDIOC_ENUM_FMT(像素格式),VIDIOC_ENUM_FRAMESIZES(分辨率),VIDIOC_ENUM_FRAMEINTERVALS(帧率)
- 对于第二步获取的数据进行组合调用 fill_caps() 填充 struct caps。
- 尝试 VIDIOC_TRY_FMT 验证第一个格式是否可用
- fill_caps():将 V4L2 的 (pixelformat, w, h, fps) 转换为 Windows 的 AM_MEDIA_TYPE + VIDEOINFOHEADER
- 格式设置
- window函数:IAMStreamConfig::SetFormat
- v4l_device_set_format:选择某个 caps 并通过 VIDIOC_S_FMT 应用到设备。
- 这里需要进行格式转换,将传入的window下的格式转换成v4l实际设置格式
- 读取一帧//window函数:
- v4l_device_read_frame:从设备读取原始帧,并做 垂直翻转(因 Windows DIB 是 bottom-up)
- Windows 的 BITMAPINFO 默认是 bottom-up(第0行在底部),V4L2 输出通常是 top-down(第0行在顶部),所以必须 逐行倒序拷贝,否则图像上下颠倒。
- v4l_device_read_frame:从设备读取原始帧,并做 垂直翻转(因 Windows DIB 是 bottom-up)
- 正式采集:
- window函数:IMediaControl::Run()
- v4l_device_start:正式应用格式(之前 create 只是 TRY_FMT),当 DirectShow 图表运行(IMediaControl::Run())时,会调用
- 格式读取:
- window函数:IAMStreamConfig::GetStreamCaps
- v4l_device_get_caps:格式获取,由于window下的格式读取参数较少,所以这里不需要转换
window下用DirectShowShow采集视频数据
- DirectShow 通过 Filter Graph 把摄像头(Capture Filter)和显示器(Renderer)连起来,调用 Run() 就开始采集。
- Filter Graph创建
- CoCreateInstance(CLSID_FilterGraph, ……, (void**)&pGraph):创建一个 空的 Filter Graph(过滤器图),就是里面的
pGraph参数。
- CoCreateInstance(CLSID_FilterGraph, ……, (void**)&pGraph):创建一个 空的 Filter Graph(过滤器图),就是里面的
- 摄像头(Capture Filter)创建
- CoCreateInstance(CLSID_VideoCapture,……, (void**)&pCamera):创建摄像头 Capture Filter
- pGraph->AddFilter(pCamera, L”Camera”):将摄像头添加到第一步创建的Filter Graph中
- 显示器(Renderer)创建
- CoCreateInstance(CLSID_VideoRendererDefault, ……,(void**)&pRenderer);创建显示器的Renderer
- pGraph->AddFilter(pRenderer, L”Renderer”);将显示器添加到第一步创建的Filter Graph中
- 显示器和摄像头连接
- IPin *pOut = GetOutPin(pCamera); // 摄像头输出
- IPin *pIn = GetInPin(pRenderer); // 渲染器输入
- pGraph->Connect(pOut, pIn); //手动连接
- 开始采集播放 // 6. 启动 Graph
- pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl):获取pControl控制播放关闭
- pControl->Run():开始运行
- Filter Graph创建
- 目前大部分情况会使用
CaptureGraphBuilder2来替代显示器和摄像头连接这一步,因为在连接的时候会出现数据源和显示不匹配问题,例如采集的是yuyv但是显示是rbg24,此时就会在CaptureGraphBuilder2内部调用pCapture->RenderStream这个函数进行格式转换。
wine下整体渲染流程
初始化
关键知识介绍
- wine下的Filter都是通过com来进行操作的,Filter Graph Manager (FGM) 也是一种filter,也是一个com接口
- com是什么:’’’’’’’’
- Filter Graph Manager是视频处理的核心,他通过Filter Mapper,Media Control,Media Event,Media Seeking这几个接口来协调视频显示
- Filter Graph Manager核心:整体协调
- Filter Mapper接口:查找过滤器
- Media Control接口:播放控制
- Media Event接口:事件处理
- Media Seeking接口:定位管理
- Media Foundation(MF),新一代的视频流处理方案,DirectShow的Filter Graph Manager是老一代,wine下主要是依赖gstream实现这个方案,该方案是提供了色彩转换接口。
- wine下的Filter都是通过com来进行操作的,Filter Graph Manager (FGM) 也是一种filter,也是一个com接口
容器初始化
- 创建Filter Mapper实例,通过ilter_mapper_create函数实现
- 012c:trace:quartz:filter_mapper_create Created filter mapper 01343DE0.
- 注册视频渲染器过滤器,AVI解压缩器过滤器,MPEG音频解码器过滤器,通过FilterMapper3_RegisterFilter函数实现
- 012c:trace:quartz:FilterMapper3_RegisterFilter iface 01343DE4, clsid {70e102b0-5556-11ce-97c0-00aa0055595a}, name L”Video Renderer”, ret_moniker 00000000, category {083863f1-70de-11d0-bd40-00a0c911ce86}, instance (null), prf2 7BDC3E78.
- 012c:trace:quartz:FilterMapper3_RegisterFilter iface 01343DE4, clsid {feb50740-7bef-11ce-9bd9-0000e202599c}, name L”MPEG Video Decoder”, ret_moniker 00000000, category (null), instance (null), prf2 7BDC3DD8.
- 创建过滤器的类别,把上面的过滤器进行分类包含音频压缩器,音频渲染器,设备控制过滤器,通过这个函数实现FilterMapper3_CreateCategory
- 012c:trace:quartz:FilterMapper3_CreateCategory iface 013441FC, category {cc7bfb41-f175-11d1-a392-00e0291f3959}, merit 0x200000, description L”External Renderers”.
- 012c:trace:quartz:FilterMapper3_CreateCategory iface 013441FC, category {860bb310-5d01-11d0-bd3b-00a0c911ce86}, merit 0x200000, description L”Video Capture Sources”.
- 012c:trace:quartz:FilterMapper3_CreateCategory iface 013441FC, category {33d9a760-90c8-11d0-bd43-00a0c911ce86}, merit 0x200000, description L”Video Compressors”.
- 上面三步都是在 Quartz.dll模块中实现, Quartz.dll是基础模块,需要提前注册
- 注册MP3音频解码器L3codec.acm
- 注册qcap.dll视频捕获相关模块,包含AVI复用器(AVI Mux),文件写入器(File writer),智能分配器(Smart Tee )
- 012c:trace:quartz:filter_mapper_create Created filter mapper 0135CAF8.
- 012c:trace:quartz:FilterMapper3_RegisterFilter iface 0135CAFC, clsid {e2510970-f137-11ce-8b67-00aa00a3f1a6}, name L”AVI Mux”, ret_moniker 00000000, category (null), instance (null), prf2 7A7821BC.
- 012c:trace:quartz:FilterMapper3_RegisterFilter iface 0135CAFC, clsid {8596e5f0-0da5-11d0-bd21-00a0c911ce86}, name L”File writer”, ret_moniker 00000000, category (null), instance (null), prf2 7A782110.
- 012c:trace:quartz:FilterMapper3_RegisterFilter iface 0135CAFC, clsid {cc58e280-8aa1-11d1-b3f1-00aa003761c5}, name L”Smart Tee”, ret_moniker 00000000, category (null), instance (null), prf2 7A782144.
- 注册qedit.dll编辑相关相关模块注册,采样抓取器(SampleGrabber ),空渲染器(Null Renderer)
- 012c:trace:quartz:FilterMapper3_RegisterFilter iface 0135CB9C, clsid {c1f400a0-3f08-11d3-9f0b-006008039e37}, name L”SampleGrabber”, ret_moniker 00000000, category (null), instance (null), prf2 7A730178.
- 012c:trace:quartz:FilterMapper3_RegisterFilter iface 0135CB9C, clsid {c1f400a4-3f08-11d3-9f0b-006008039e37}, name L”Null Renderer”, ret_moniker 00000000, category (null), instance (null), prf2 7A73014C.
- 注册winegstreamer.dll,把GStreamer集成进来,用于处理wine下的视频,比如解码,色彩转换,gstreamer是wine下的后端,大部分filter的底层都和这个有关联,视频的Decoder都是通过GStreamer底层实现,还有较为复杂的视频操作也是通过GStreamer,例如MPEG-I Stream Splitter,Wave Parser等
- 创建Filter Mapper实例,通过ilter_mapper_create函数实现
模块加载
- todo
数据捕获
- todo
数据处理
- todo
实际渲染
- todo
实现directshow的色彩转换filter
- todo