Archive for March, 2010

Build DirectShow Source Filter with Media Foundation

2010-3-29 20:27 | by 2ndboy

  Microsoft 在 Windows 7 里增加了对 H.264 和 AAC 这两种日渐流行的编码格式的 native support。对于 H.264,在 DirectShow 里你可以找到一个名字叫“Microsoft DTV-DVD Video Decoder”的 filter。相应的,在 Media Foundation 里有一个叫“Microsoft H264 Video Decoder”的 MFT;对于 AAC,在 DirectShow 里你可以找到一个名字叫“Microsoft DTV-DVD Audio Decoder”的 filter。在 Media Foundation 里与之对应的 MFT 叫做“Microsoft AAC Audio Decoder”。

  虽然 Windows 7 提供了对上述格式的支持,但是基于 DirectShow 构建的应用程序如果想 playback MP4 视频文件的话却不能直接从这个改进里获得好处,原因是 Microsoft 并没有随 H.264/AAC decoder 一起发布 MP4 的 source filter。所以基于 DirectShow 架构的程序想要回放 MP4 文件的话,有两个解决方案:第一种方法是自己实现一个 MP4 文件的 parser,输出合适的 media type 来配接系统自带的 H.264/AAC decoder,当然也可以用现成的。第二种方法是用 Media Foundation 来实现一个特殊的 DirectShow source filter,这个 source filter 调用 MF 来解码 MP4 数据,拿到原始数据以后直接推送给 A/V renderer。

  第一种方法需要熟悉 MP4 容器的结构,我没有动手实现过,所以这里不详述。现成的解决方案有 Haali Media SplitterGabest MP4 Splitter

  Haali Media Splitter 其实不是一个单纯的 MP4 parser,它还支持其它几种容器格式,具体可以看它官网上的介绍。Gabest MP4 Splitter 作为 guliverkli(MPC) 的一部分发行,没发现它有独立的官方网站。据两者都用过的用户说 Gabest MP4 Splitter 相对来说更稳定一些。我只尝试了 Gabest MP4 Splitter,注册过它的 DLL/ax 以后系统里多了两个新的 filter(MP4 source 和 MP4 splitter),这时如果用 GraphEdit render 一个 MP4 文件的话,会发现 MP4 source 连接了 MP4 splitter,MP4 splitter 分别连接了 Microsoft DTV-DVD Audio Decoder 和 Microsoft DTV-DVD Video Decoder,最后连接至 Audio Renderer 和 Video Renderer。

  第二种解决方案,如果只看过 Media Session 的文档的话,会感觉这是个不可能的任务,幸好 Microsoft 在 Windows 7 里对 Media Foundation 做了改进,新增的一些新组件和接口使得这个任务变得简单起来。在 Windows 7 里,我们可以用 Source Reader 来实现一个可以直接输出 uncompressed data 的 source filter。

  Source Reader 可以在某些场合下替代 Media Session 来处理多媒体音视频数据,好处是接口非常简单,还可以选择是读取 compressed data 还是 uncompressed data。你可以简单的把 Source Reader 看作是没有接 renderer 的 Media Session。具体的实现就不多说了,感兴趣的可以自行参考 Source Reader 的使用文档例子。下面侃侃 Source Reader 和 DXVA。

  Source Reader 的使用文档里其实是简单的提过一下如何配合 DXVA 做硬件加速的,但是我的实做结果是——这要看你把 Source Reader 用在什么地方了。如果在创建 Source Reader 实例的模块里你同时也做 render,那么这个硬件加速和可能实现的(这里之所以用“可能”这两个字是因为我没有去做这个尝试,因为这里提到的方案属于下面要说的情况);如果 render 是交给其它模块做的(像我这里提到的把 Source Reader 包装成一个 DS 的 source filter,rendering 是在 renderer 里做的),那 Source Reader 跟 renderer 之间的数据交换和 D3D 设施共享就不容易完成。

  把 Source Reader 的硬件加速打开可以简化成如下代码:

  1. IDirect3D9 *m_pD3D9 = Direct3DCreate9( D3D_SDK_VERSION );
  2.  
  3.  IDirect3DDevice9 *m_pD3D9Device = NULL;
  4.  HRESULT hResult = m_pD3D9->CreateDevice( ..., &m_pD3D9Device );
  5.  
  6.  UINT uResetToken = 0;
  7.  IDirect3DDeviceManager9 *m_pD3D9DeviceManager = NULL;
  8.  hResult = DXVA2CreateDirect3DDeviceManager9( &uResetToken, &m_pD3D9DeviceManager );
  9.  
  10.  hResult = m_pD3D9DeviceManager->ResetDevice( m_pD3D9Device, uResetToken );
  11.  
  12.  IMFAttributes *pAttributes = NULL;
  13.  hResult = MFCreateAttributes( &pAttributes, 1 );
  14.  hResult = pAttributes->SetUnknown( MF_SOURCE_READER_D3D_MANAGER, m_pD3D9DeviceManager );
  15.  hResult = pAttributes->SetUINT32( MF_SOURCE_READER_DISABLE_DXVA, FALSE );

  Source Reader 开启 DXVA 硬件加速要用到 Direct3D device manager,D3D device manager 其实是用来在 decoder 和 renderer 之间共享 D3D device 的。如果作为 Source Reader 使用者的我们没有直接负责 render 的话,已经用 GPU 解压到显存的 uncompressed data 要被复制出来,这样做的性能损耗据说比较大。所以在这种情况下,没什么好的方法来开启 DXVA,而且开了也没用。

-=-= 我是很普通的分割线 =-=-

WinMEnc 之后,又发现了一个可以压 iPod Touch 视频的好工具,而且同样开源——Handbrake。WinMEnc 什么都好,就是在处理 WMV 和某些 AVI 的时候,压完的 M4V 视频有问题,而且 iPT 不认,Handbrake 就没这个问题,推荐 iPT 机主使用,双剑合璧,基本上无敌;-)

About YUV Video

2010-3-10 21:49 | by 2ndboy

  今天在看 Media Foundation 文档的时候翻到一篇叫“About YUV Video”文章,感觉对理解 YUV color space 很有帮助,一激动就翻译了;),晾在这里分享一下。

注 1:为了严格区分 luma、luminance 和 brightness,故保留 luma 和 luminance 未译。
注 2:在表示单独的色彩饱和度时,将 chroma 译作饱和度;在表示 U、V 这两个量时,将 chroma 译作色度。

关于 YUV 视频

数字视频经常被编码成 YUV 格式。这篇文章解释了 YUV 视频的一般概念和一些术语,而不会深入到 YUV 视频处理的数学理论中去。

如果你曾经接触过 CG,你大概比较熟悉 RGB 颜色。一个 RGB 色彩由三个值构成:红、绿和蓝。这些值直接对应于可见光谱的一部分。这三个 RGB 值构成的数学坐标系统称作颜色空间(color space)。红色分量定义了这个坐标系统中的一个轴,蓝色分量定义了第二个,绿色定义了第三个。任何一个 RGB 颜色都会落在这个颜色空间的某处。例如纯品红是 100% 的蓝色,100% 的红色和 0% 的绿色。

尽管 RGB 是一种常见的颜色表示方法,但是用其它坐标系统来表示颜色也是可行的。术语 YUV 代表了一族颜色空间,它们的共同点是把亮度信息和色彩信息分开来编码。跟 RGB 一样,YUV 也用三个值来表示所有颜色。这些值被称为 Y’、U 和 V(事实上,“YUV”这种用法严格来讲是不正确的。在计算机视频里,YUV 几乎总是代表一个我们稍后要进行讨论的叫 Y’CbCr 的颜色空间。尽管如此 YUV 经常被当作一个通用术语来表示那些跟 Y’CbCr 有着相同原来的颜色空间)。

Y’(也被称作 luma)用来表示颜色的亮度值。撇号(’)用来把 luma 跟另外一个意义接近的值加以区分区分,这个值就是 luminance(这个值用 Y 做标记)。Luminance 源自线性 RGB 值,而 luma 源自非线性(gamma-corrected)RGB 值。Luminance 是一个跟真实亮度接近的值,但是由于技术原因,实践中 luma 更常用一些。撇号经常被省略,但是 YUV 颜色空间总是使用 luma 而不是 luminance。

Luma 源自 RGB 颜色,但是要在红、绿、蓝分量上做个加权平均。对于标清(standard-definition)视频, 使用如下公式:

Y’ = 0.299R + 0.587G + 0.114B

这个公式反应了人眼对某些特定波长的光更加敏感这一事实,这关系到我们对颜色中亮度的感知。蓝光看上去最暗淡,绿光看上去最亮,红光则介乎其间。这个公式也反映了被用在早期电视中的磷的特性。一个重新考虑了现代电视技术的新公式被用在高清(high-definition)电视上:

Y’ = 0.2125R + 0.7154G + 0.0721B

跟标清电视等价的 luma 定义在一份名为 ITU-R BT.601 的规范中。对于高清电视,相关的规范是 ITU-R BT.709。

U 分量和 V 分量(也被称作饱和度(chroma)和色差(color difference)值),可以通过把原始 RGB 中的红绿分量从 Y 值中减去而得到:

U = Y’ – B

V = Y’ – R

这些值所包含的信息足够把 YUV 值再转换至原始 RGB 值。

YUV 的好处

由于部分历史原因,模拟电视使用 YUV。模拟彩色电视信号被设计成可以向前兼容黑白电视。彩色电视信号在 luma 信号之上叠加了色度信息(U 和 V)。黑白电视会忽略掉色度,把叠加的信号显示成灰度图像(刻意设计过的信号使得色度信号不会干扰到 luma 信号)。彩色电视机能够提取出色度信息从而把信号还原为 RGB。

YUV 还有另外一个跟今天主题更加相关的优点。比起色调的改变,人眼对亮度的改变更加敏感。所以,一幅图像可以携带比 luma 信息更少的色度信息而又不会以牺牲图像质量为代价。例如,比较通用的做法是把水平扫描线上的色度样本数降为 luma 样本数的一半。换句话说,对于一行像素上的每两个 luma 样本,只有一个 U 样本和一个 V 样本与之对应。假定每个值用 8 位来编码,那么每两个像素就需要 4 个字节的空间(两个 Y’,一个 U 和一个 V),这比同等的 24 位 RGB 编码要节约 30% 的空间或者说节约 16 位每像素。

YUV 并不是天生就比 RGB 更加密实。除非色度值被降低采样率,否则一个 YUV 像素跟一个 RGB 像素的尺寸是一样的。从 RGB 到 YUV 的转换是无损的。如果没有降低采样率,一个 YUV 像素可以被无损的转换回 RGB。降低采样率可以让 YUV 图像小一些,同时也会损失一些颜色信息。但是如果转换正确的话,这种损失是难以察觉的。

计算机视频中的 YUV

前面列出来用于 YUV 的公式并不能用来精确的转换数字视频。数字视频通常使用一种叫做 Y’CbCr 的 YUV。本质上,Y’CbCr 要把 YUV 分量缩放到如下取值范围内:

Component Range
Y’ 16-235
Cb/Cr 16-240,128 代表零

这些取值范围假定 Y’CbCr 分量的精度是 8 位。下面是 Y’CbCr 的准确来历,使用 BT.601 中定义的 luma:

  1. 我们把 RGB 的取值范围设定为 [0…1]。换句话说,纯黑是 0,纯白是 1。注意,这里说的是非线性(gamma corrected)RGB 值。

  2. 计算 luma。对于 BT.601,Y’ = 0.299R + 0.587G + 0.114B,参见先前的描述。

  3. 计算中间色度差值(B – Y’)和(R – Y’)。(B – Y’)的取值范围是 +/- 0.886,(R – Y’)是 +/- 0.701。

  4. 按如下方法缩放色度差值:

    Pb = (0.5 / (1 – 0.114)) × (B – Y’)

    Pr = (0.5 / (1 – 0.299)) × (R – Y’)

    这些缩放系数会赋予两个值相同的数值范围,+/- 0.5。它们定义的 YUV 颜色空间叫作 Y’PbPr,这个颜色空间被用在模拟视频分量上。

  5. 缩放 Y’PbPr 值以得到最终的 Y’CbCr值:

    Y’ = 16 + 219 × Y’

    Cb = 128 + 224 × Pb

    Cr = 128 + 224 × Pr

最后的这几个缩放系数用来把数值处理到之前的那个表格里列出的范围内。当然,你也可以不用存储中间结果而把 RGB 直接转换成 Y’CbCr。列在这里的步骤是为了展示 Y’CbCr 是如何从文章一开头的原始 YUV 演化而来的。

下面这个表格展示了不同颜色的 RGB 和 YCbCr 值,同样,这里使用的也是 BT.601 里定义的 luma。

Color R G B Y’ Cb Cr
Black 0 0 0 16 128 128
Red 255 0 0 81 90 240
Green 0 255 0 145 54 34
Blue 0 0 255 41 240 110
Cyan 0 255 255 170 166 16
Magenta 255 0 255 106 202 222
Yellow 255 255 0 210 16 146
White 255 255 255 235 128 128

在上面这张表中,Cb 和 Cr 没有在直觉上反映相应的颜色。例如纯白和纯黑都包含了一个中间值的 Cb 和 Cr(128)。Cb 的最高和最低值分别是蓝色和黄色。Cb 的最高和最低值分别是红色和青色。

报名学车啦

2010-3-8 22:32 | by 2ndboy

  本来去年的这个时候就想学的,但是太忙只好作罢,最近手上的项目进入稳定期,于是赶紧趁这个窗口期报了个名,接下来一两周要抽 5 个晚上的时间去上理论课了。不爽的是城西的上课地点居然只有西溪路上的公路技师学院一个,而且交通不怎么方便。

  一直以来感觉自己对机械操作还挺有感觉的,这是个检验的机会:D