0%

【Linux技术分享】wine下DirectShow学习

对wine下DirectShow学习

目标

  1. 实现wine下读取yuy2视频流

开发调研准备

  1. 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 命名),是一种压缩格式
  1. 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是压缩格式会出现颜色模糊和失真。
  1. 常见显示错误

    • 直接用 YUV 数据当 RGB 显示:
      1. 图像整体呈青绿色或粉紫色
      2. 有明显的条纹、噪点、色块
      3. 人物或物体轮廓可能隐约可见(因为 Y 亮度信息还在),但颜色完全错误
      4. 水平方向有2像素周期的重复图案(因为 YUY2 是每2像素打包)
    • 用 RGB 当 YUV 显示
      1. 视频播放器可能崩溃、花屏
      2. 或显示高对比度黑白+彩色噪点
    • 为什么yuv内容和rgb内容差距很大但是 YUV 数据当 RGB 显示可以隐约查看到内容:Y 亮度保留,U/V 被误用,Y被rgb中的红色截取,红色和亮度强关联,所以能隐约看到内容。
  2. 相互转换

    • 数学公式
      1. 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](中心在 128128 表示无色)
      2. YCbCr → RGB(解码)
        1
        2
        3
        R = Y + 1.402·(Cr - 128)  
        G = Y - 0.344136·(Cb - 128) - 0.714136·(Cr - 128)
        B = Y + 1.772·(Cb - 128)

wine下通过linux采集视频数据流程

  • wine下通过linux采集的代码是在dlls/qcap/v4l.c,在初始化的时候会检测是否存在v4l2,若存在则通过v4l2库进行设备打开创建/关闭/ioctl/read
  1. 设备创建
    • window函数:CoCreateInstance
    • v4l_device_create:打开 /dev/videoN,枚举所有支持的格式(仅限 YUYV 和 BGR24),构建 caps 列表。
      1. 调用if (xioctl(fd, VIDIOC_QUERYCAP, &caps) == -1)查看当前设备是否支持视频采集和 read()
      2. 循环调用获取:VIDIOC_ENUM_FMT(像素格式),VIDIOC_ENUM_FRAMESIZES(分辨率),VIDIOC_ENUM_FRAMEINTERVALS(帧率)
      3. 对于第二步获取的数据进行组合调用 fill_caps() 填充 struct caps。
      4. 尝试 VIDIOC_TRY_FMT 验证第一个格式是否可用
    • fill_caps():将 V4L2 的 (pixelformat, w, h, fps) 转换为 Windows 的 AM_MEDIA_TYPE + VIDEOINFOHEADER
  2. 格式设置
    • window函数:IAMStreamConfig::SetFormat
    • v4l_device_set_format:选择某个 caps 并通过 VIDIOC_S_FMT 应用到设备。
      1. 这里需要进行格式转换,将传入的window下的格式转换成v4l实际设置格式
  3. 读取一帧//window函数:
    • v4l_device_read_frame:从设备读取原始帧,并做 垂直翻转(因 Windows DIB 是 bottom-up)
      1. Windows 的 BITMAPINFO 默认是 bottom-up(第0行在底部),V4L2 输出通常是 top-down(第0行在顶部),所以必须 逐行倒序拷贝,否则图像上下颠倒。
  4. 正式采集:
    • window函数:IMediaControl::Run()
    • v4l_device_start:正式应用格式(之前 create 只是 TRY_FMT),当 DirectShow 图表运行(IMediaControl::Run())时,会调用
  5. 格式读取:
    • window函数:IAMStreamConfig::GetStreamCaps
    • v4l_device_get_caps:格式获取,由于window下的格式读取参数较少,所以这里不需要转换

window下用DirectShowShow采集视频数据

  • DirectShow 通过 Filter Graph 把摄像头(Capture Filter)和显示器(Renderer)连起来,调用 Run() 就开始采集。
    1. Filter Graph创建
      • CoCreateInstance(CLSID_FilterGraph, ……, (void**)&pGraph):创建一个 空的 Filter Graph(过滤器图),就是里面的pGraph参数。
    2. 摄像头(Capture Filter)创建
      • CoCreateInstance(CLSID_VideoCapture,……, (void**)&pCamera):创建摄像头 Capture Filter
      • pGraph->AddFilter(pCamera, L”Camera”):将摄像头添加到第一步创建的Filter Graph中
    3. 显示器(Renderer)创建
      • CoCreateInstance(CLSID_VideoRendererDefault, ……,(void**)&pRenderer);创建显示器的Renderer
      • pGraph->AddFilter(pRenderer, L”Renderer”);将显示器添加到第一步创建的Filter Graph中
    4. 显示器和摄像头连接
      • IPin *pOut = GetOutPin(pCamera); // 摄像头输出
      • IPin *pIn = GetInPin(pRenderer); // 渲染器输入
      • pGraph->Connect(pOut, pIn); //手动连接
    5. 开始采集播放 // 6. 启动 Graph
      • pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl):获取pControl控制播放关闭
      • pControl->Run():开始运行
  • 目前大部分情况会使用CaptureGraphBuilder2来替代显示器和摄像头连接这一步,因为在连接的时候会出现数据源和显示不匹配问题,例如采集的是yuyv但是显示是rbg24,此时就会在CaptureGraphBuilder2内部调用pCapture->RenderStream这个函数进行格式转换。

wine下整体渲染流程

  • 初始化

    • 关键知识介绍

      1. wine下的Filter都是通过com来进行操作的,Filter Graph Manager (FGM) 也是一种filter,也是一个com接口
        • com是什么:’’’’’’’’
      2. Filter Graph Manager是视频处理的核心,他通过Filter Mapper,Media Control,Media Event,Media Seeking这几个接口来协调视频显示
        • Filter Graph Manager核心:整体协调
        • Filter Mapper接口:查找过滤器
        • Media Control接口:播放控制
        • Media Event接口:事件处理
        • Media Seeking接口:定位管理
      3. Media Foundation(MF),新一代的视频流处理方案,DirectShow的Filter Graph Manager是老一代,wine下主要是依赖gstream实现这个方案,该方案是提供了色彩转换接口。
    • 容器初始化

      1. 创建Filter Mapper实例,通过ilter_mapper_create函数实现
        • 012c:trace:quartz:filter_mapper_create Created filter mapper 01343DE0.
      2. 注册视频渲染器过滤器,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.
      3. 创建过滤器的类别,把上面的过滤器进行分类包含音频压缩器,音频渲染器,设备控制过滤器,通过这个函数实现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”.
      4. 上面三步都是在 Quartz.dll模块中实现, Quartz.dll是基础模块,需要提前注册
      5. 注册MP3音频解码器L3codec.acm
      6. 注册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.
      7. 注册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.
      8. 注册winegstreamer.dll,把GStreamer集成进来,用于处理wine下的视频,比如解码,色彩转换,gstreamer是wine下的后端,大部分filter的底层都和这个有关联,视频的Decoder都是通过GStreamer底层实现,还有较为复杂的视频操作也是通过GStreamer,例如MPEG-I Stream Splitter,Wave Parser等
    • 模块加载

      • todo
    • 数据捕获

      • todo
    • 数据处理

      • todo
    • 实际渲染

      • todo

实现directshow的色彩转换filter

  • todo