Microsoft Media Foundation官方文档翻译(14)《Image Stride》

官方英文文档链接:https://docs.microsoft.com/en-us/windows/desktop/medfound/image-stride

基于05/31/2018

 

当视频图像存储在内存中时,内存缓冲区可能在每行像素后面有额外的填充字节,填充字节会影响图像在内存中的存储方式,但不会影响图像的显示方式。

stride 是从内存中一行像素开头到内存中的下一行像素间隔的字节数。Stride 也可以叫做 pitch。如果存在填充字节,则 stride 就会大于图像宽度,如下图所示:

diagram showing an image plus padding.

两个包含相同尺寸图像的 buffer 可能有不同 stride,所以在处理视频图像时必须考虑 stride。

另外,图像在内存中的排列方式有两种。top-down 图像的首行第一个像素先储存在内存中。bottom-up 图像的最后一行像素先存储在内存中。下图显示了两者的区别:

diagram showing top-down and bottom-up images.

bottom-up 图像具有负的 stride,因为 stride 被定义为从第一行像素到第二行像素需要向后移动的距离。YUV 图像应该始终是top-down,Direct3D surface 则必须是 top-down。而内存中的 RGB 图像则通常是 bottom-up。

做视频转换时尤其需要处理 stride 不匹配的 buffer,(剩下的废话。。), because the input buffer might not match the output buffer. For example, suppose that you want to convert a source image and write the result to a destination image. Assume that both images have the same width and height, but might not have the same pixel format or the same image stride.

下面的代码展示了编写此类函数的一般写法。这不是一个完整的示例,因为这里抽象了很多实现细节。

 1 void ProcessVideoImage(
 2     BYTE*       pDestScanLine0,     
 3     LONG        lDestStride,        
 4     const BYTE* pSrcScanLine0,      
 5     LONG        lSrcStride,         
 6     DWORD       dwWidthInPixels,     
 7     DWORD       dwHeightInPixels
 8     )
 9 {
10     for (DWORD y = 0; y < dwHeightInPixels; y++)
11     {
12         SOURCE_PIXEL_TYPE *pSrcPixel = (SOURCE_PIXEL_TYPE*)pDestScanLine0;
13         DEST_PIXEL_TYPE *pDestPixel = (DEST_PIXEL_TYPE*)pSrcScanLine0;
14 
15         for (DWORD x = 0; x < dwWidthInPixels; x +=2)
16         {
17             pDestPixel[x] = TransformPixelValue(pSrcPixel[x]);
18         }
19         pDestScanLine0 += lDestStride;
20         pSrcScanLine0 += lSrcStride;
21     }
22 }

 

 

这个函数需要 6 个参数:

  • 指向目标图像首行像素开头位置(scan line 0)的指针
  • 目标图像的 stride
  • 指向源图像首行像素开头位置(scan line 0)的指针
  • 源图像的 stride
  • 图像宽度(像素)
  • 图像高度(像素)

通常情况下每次处理一行,每次遍历行中的每个像素。假设 SOURCE_PIXEL_TYPE 和 DEST_PIXEL_TYPE 分别是表示源图像和目标图像像素的结构体(例如 32-bit RGB 使用 RGBQUAD structure。不一定每种像素格式都有一个定义好的结构体),则可以将数组指针转换为该结构体的指针,以访问每个像素的 RGB 或 YUV 分量。每行结束时,指针按照 stride 递增,使指针指向下一行。

本例中为每个像素都调用了一个假设的名为 TransformPixelValue 的转换函数。这个可以是任何能把源像素转换为目标像素的函数,当然具体细节取决于需求。例如对于 planar YUV 格式,你必须分别独立处理 luma plane 和 chroma plane;对于视频,可能需要分别处理 field,等等。

下面给出了一个具体的例子,将 32-bit RGB 图像转换成 AYUV 图像。每个 RGB 像素可以通过 RGBQUAD 结构访问,AYUV 像素可以通过 DXVA2_AYUVSample8 结构访问。

 1 //-------------------------------------------------------------------
 2 // Name: RGB32_To_AYUV
 3 // Description: Converts an image from RGB32 to AYUV
 4 //-------------------------------------------------------------------
 5 void RGB32_To_AYUV(
 6     BYTE*       pDest,
 7     LONG        lDestStride,
 8     const BYTE* pSrc,
 9     LONG        lSrcStride,
10     DWORD       dwWidthInPixels,
11     DWORD       dwHeightInPixels
12     )
13 {
14     for (DWORD y = 0; y < dwHeightInPixels; y++)
15     {
16         RGBQUAD             *pSrcPixel = (RGBQUAD*)pSrc;
17         DXVA2_AYUVSample8   *pDestPixel = (DXVA2_AYUVSample8*)pDest;
18         
19         for (DWORD x = 0; x < dwWidthInPixels; x++)
20         {
21             pDestPixel[x].Alpha = 0x80;
22             pDestPixel[x].Y = RGBtoY(pSrcPixel[x]);   
23             pDestPixel[x].Cb = RGBtoU(pSrcPixel[x]);   
24             pDestPixel[x].Cr = RGBtoV(pSrcPixel[x]);   
25         }
26         pDest += lDestStride;
27         pSrc += lSrcStride;
28     }
29 }

 

 

下面的例子将 32-bit RGB 图像转换为 YV12 图像。这里展示了如何处理 planar YUV 格式(YV12 是 planar 4:2:0 格式,planar后面会讲《Recommended 8-Bit YUV Formats for Video Rendering》)。本例中的函数中,分别为三个 plane 维护了三个单独的指针,但基本方法与前面的示例相同。

 1 void RGB32_To_YV12(
 2     BYTE*       pDest,
 3     LONG        lDestStride,
 4     const BYTE* pSrc,
 5     LONG        lSrcStride,
 6     DWORD       dwWidthInPixels,
 7     DWORD       dwHeightInPixels
 8     )
 9 {
10     assert(dwWidthInPixels % 2 == 0);
11     assert(dwHeightInPixels % 2 == 0);
12 
13     const BYTE *pSrcRow = pSrc;
14     
15     BYTE *pDestY = pDest;
16 
17     // Calculate the offsets for the V and U planes.
18 
19     // In YV12, each chroma plane has half the stride and half the height  
20     // as the Y plane.
21     BYTE *pDestV = pDest + (lDestStride * dwHeightInPixels);
22     BYTE *pDestU = pDest + 
23                    (lDestStride * dwHeightInPixels) + 
24                    ((lDestStride * dwHeightInPixels) / 4);
25 
26     // Convert the Y plane.
27     for (DWORD y = 0; y < dwHeightInPixels; y++)
28     {
29         RGBQUAD *pSrcPixel = (RGBQUAD*)pSrcRow;
30         
31         for (DWORD x = 0; x < dwWidthInPixels; x++)
32         {
33             pDestY[x] = RGBtoY(pSrcPixel[x]);    // Y0
34         }
35         pDestY += lDestStride;
36         pSrcRow += lSrcStride;
37     }
38 
39     // Convert the V and U planes.
40 
41     // YV12 is a 4:2:0 format, so each chroma sample is derived from four 
42     // RGB pixels.
43     pSrcRow = pSrc;
44     for (DWORD y = 0; y < dwHeightInPixels; y += 2)
45     {
46         RGBQUAD *pSrcPixel = (RGBQUAD*)pSrcRow;
47         RGBQUAD *pNextSrcRow = (RGBQUAD*)(pSrcRow + lSrcStride);
48 
49         BYTE *pbV = pDestV;
50         BYTE *pbU = pDestU;
51 
52         for (DWORD x = 0; x < dwWidthInPixels; x += 2)
53         {
54             // Use a simple average to downsample the chroma.
55 
56             *pbV++ = ( RGBtoV(pSrcPixel[x]) +
57                        RGBtoV(pSrcPixel[x + 1]) +       
58                        RGBtoV(pNextSrcRow[x]) +         
59                        RGBtoV(pNextSrcRow[x + 1]) ) / 4;        
60 
61             *pbU++ = ( RGBtoU(pSrcPixel[x]) +
62                        RGBtoU(pSrcPixel[x + 1]) +       
63                        RGBtoU(pNextSrcRow[x]) +         
64                        RGBtoU(pNextSrcRow[x + 1]) ) / 4;    
65         }
66         pDestV += lDestStride / 2;
67         pDestU += lDestStride / 2;
68         
69         // Skip two lines on the source image.
70         pSrcRow += (lSrcStride * 2);
71     }
72 }

 

 

在这篇的所有示例中,都假设应用程序已经确定了 stride。有时可以从 media buffer 中得到这些信息,否则必须根据视频格式进行计算。有关图像 stride 的计算和如何使用 media buffer,参考 Uncompressed Video Buffers

posted @ 2019-04-22 12:00  CaliforniaCHAO  阅读(422)  评论(0编辑  收藏  举报