最近在项目中遇到了交错视频,视频显示的时候会出现横纹的现象。如下图:

如果对交错视频的知识不是很了解,可以参考下面两篇博文:

交错、反交错与IVTC —— 从入门到放弃

去交错原理介绍

为了让交错视频正常显示,可以通过ffmpeg的滤镜,dxva 等技术来解决。由于我们的项目对性能要求比较高,所以我这里是通过dxva来处理的(dxva是调用GPU处理,ffmpeg的滤镜是通过CPU来处理)。

由于国内关于dxva的知识讲解的特别少,为了解决这个问题,花费了不少时间来研究英文文档,为了能让dxva的新手不走更多的弯路,这里总结一下这段时间对dxva的研究,希望能对大家有所帮助。

标题:DXVA视频处理器(DXVA Video Processing)

DXVA视频处理器封装了图像硬件的一些功能集,目的是为了对解码出来的视频画面做处理,比如对视频画面的反交错和视频融合处理。

这篇文章包含下面各节:

1.概览

2.创建视频处理器对象

  • 获取 IDirectXVideoProcessorService 对象指针
  • 枚举 视频处理器对象
  • 枚举 渲染目标格式对象
  • 查询 视频处理器设备能力集
  • 创建视频处理器对象

3.处理视频画面(video Process Blit)

  • Blit 变量
  • 输入源(Input Samples)

4.图像融合(Image Composition)

  • 例1:黑边处理
  • 例2:拉伸子流视频画面
  • 例3 :不匹配的流高度
  • 例4:目标矩形小于目标surface
  • 例5:源矩形
  • 例6: 相交的目标矩形
  • 例7: 拉伸和裁剪视频

5.输入源序列

下文为对上面各节的详细介绍:

1.概览:

图形硬件能使用GPU处理解码完成的视频画面。一个视频处理器(video processing device)是一个封装了一定功能的软件组件,应用程序能是用它处理功能如下:

  • 反交错(Deinterlacing)和 Inverse Telecine
  • 将视频子流图像合并到视频主流图像上面
  • 颜色调整和图像过滤
  • 图像缩放
  • 颜色空间转换
  • 透明通道融合

下面的图表展示了视频处理管线( video processing pipeline)的各个阶段。这个图表展示的并不是真正的代码实现。例如硬件驱动可能会合并其中的几个阶段到一个操作里面。所有的这些操作能合并到一个单个的操作里面。这里显示的一些操作可能硬件驱动不支持,例如noise和detail过滤器。

视频处理管线总是包含一个主视频流( a primary video stream),它包含主要的视频图像数据。主视频流决定了输出视频的帧率,它总是不透明的,每一个像素都没有透明通道数据。主视频流有可能是逐行扫描视频,也可能是交错视频。

视频处理管线可以最多接收15个视频子数据流。一个子数据流包含一个辅助的图像数据,例如闭合字幕和DVD字幕。这些辅助图像一般覆盖到主视频流上面,和主视频流做融合处理。子视频流图片一般包含透明通道数据并且总是逐行扫描帧。视频处理器(video processing device)从主视频流里面取出反交错的视频帧,然后将子视频流通过透明通道融合(alpha-blends)进主视频流的帧里面。

在以后各节,术语“picture”被认为是输入到视频处理器的输入数据。一个picture可以由一个逐行帧,单场,或者两个隔行的场组成。输出图片总是反交错的帧。

一个硬件驱动能实现多个视频处理器(video processing device),每个视频处理器能提供不同的功能。每个视频处理器通过唯一GUID来标识。下面的GUID被预先定义好了:

  • DXVA2_VideoProcBobDevice. 这个视频处理器执行 bob 反交错(bob deinterlacing)。
  • DXVA2_VideoProcProgressiveDevice。如果视频只包含逐行的帧没有交错帧,这个视频处理器可以被使用

每个支持DXVA的硬件驱动至少要实现这两个视频处理器。硬件驱动也能支持其他的视频处理器,并且指定它们的GUID。例如,硬件驱动可能会实现一些专有的反交错算法,去获取比bob反交错更好的图像质量。一些反交错算法可能需要从主视频流里面获取前后的参考帧。像这样,调用者必须提供正确的图片序列给硬件驱动,稍后我们将更详细的描述。

参考软件处理器(reference software device)也被提供。软件处理器优化了质量处理,但是速度很慢,因此不能使用在实时视频处理里面。参考软件处理器的GUIDDXVA2_VideoProcSoftwareDevice

2.创建视频处理器

在用视频处理器处理视频之前,应用程序必须先创建一个视频处理器对象。这里是视频处理步骤的简要概述,后面各节将会有更详细的描述。

1.获取IDirectXVideoProcessorService接口

2.创建主视频流的视频格式描述。使用这个描述去获取到支持这个视频格式的视频处理器列表。视频处理器用GUID来定义。

3.拿到一个视频处理器以后,获取它的渲染目标格式列表(render-target formats)。这个格式用类型 D3DFORMAT 标识。如果你计划去融合子数据流,你也可以获取支持子流格式的列表。

4.查询对应视频处理器支持的能力集。

5.创建视频处理器对象。

有时你可以省略某些步骤。例如,你能用预先指定的渲染目标格式创建视频处理器对象,看它是否成功。这样就能省掉获取渲染目标格式的过程。一个公用的格式 D3DFMT_X8R8G8B8 通常都能成功。

下面各节将详细描述各个步骤。

获取IDirectXVideoProcessorService接口

IDirectXVideoProcessorService接口从Direct3D device对象中获取。有两种方式去获取这个接口。

  • 从Direct3D device中获取
  • 从 Direct3D Device Manager中获取。

如果你有一个Direct3D device对象指针,你能通过调用DXVA2CreateVideoService函数获取一个IDirectXVideoProcessorService接口指针。次函数声明如下:

HRESULT DXVA2CreateVideoService( IDirect3DDevice9 *pDD, REFIID riid, void **ppService );

调用代码如下:

// Create the DXVA-2 Video Processor service.hr = DXVA2CreateVideoService(g_pD3DD9, IID_PPV_ARGS(&g_pDXVAVPS));

有时一个对象创建了Direct3D device对象,然后其他对象能通过Direct3D Device Manager对象共享这个Direct3D device对象。

这个时候你能通过调用Direct3D Device Manager对象的IDirect3DDeviceManager9::GetVideoService 函数获取 IDirectXVideoProcessorService接口指针。代码如下:

HRESULT GetVideoProcessorService(IDirect3DDeviceManager9 *pDeviceManager,IDirectXVideoProcessorService **ppVPService)
{*ppVPService = NULL;HANDLE hDevice;HRESULT hr = pDeviceManager->OpenDeviceHandle(&hDevice);if (SUCCEEDED(hr)){// Get the video processor service HRESULT hr2 = pDeviceManager->GetVideoService(hDevice, IID_PPV_ARGS(ppVPService));// Close the device handle.hr = pDeviceManager->CloseDeviceHandle(hDevice);if (FAILED(hr2)){hr = hr2;}}if (FAILED(hr)){SafeRelease(ppVPService);}return hr;
}

枚举视频处理器对象

用主视频流的格式信息填充DXVA2_VideoDesc 结构体,然后将此结构体对象传递给 IDirectXVideoProcessorService::GetVideoProcessorDeviceGuids 函数去获取到视频处理器对象列表。这个函数返回一个GUID数组。其中每一个GUID所代表的视频处理器对象都能处理这个主视频流。

我们现在假设一个应用程序渲染一个视频流的信息为:YUY2格式,BT.709 YUV 颜色空间,帧率为29.97,视频由逐行帧组成。下面的代码展示了怎样去填充DXVA2_VideoDesc结构体对象并获取GUIDs:

// Initialize the video descriptor.g_VideoDesc.SampleWidth                         = VIDEO_MAIN_WIDTH;g_VideoDesc.SampleHeight                        = VIDEO_MAIN_HEIGHT;g_VideoDesc.SampleFormat.VideoChromaSubsampling = DXVA2_VideoChromaSubsampling_MPEG2;g_VideoDesc.SampleFormat.NominalRange           = DXVA2_NominalRange_16_235;g_VideoDesc.SampleFormat.VideoTransferMatrix    = EX_COLOR_INFO[g_ExColorInfo][0];g_VideoDesc.SampleFormat.VideoLighting          = DXVA2_VideoLighting_dim;g_VideoDesc.SampleFormat.VideoPrimaries         = DXVA2_VideoPrimaries_BT709;g_VideoDesc.SampleFormat.VideoTransferFunction  = DXVA2_VideoTransFunc_709;g_VideoDesc.SampleFormat.SampleFormat           = DXVA2_SampleProgressiveFrame;g_VideoDesc.Format                              = VIDEO_MAIN_FORMAT;g_VideoDesc.InputSampleFreq.Numerator           = VIDEO_FPS;g_VideoDesc.InputSampleFreq.Denominator         = 1;g_VideoDesc.OutputFrameFreq.Numerator           = VIDEO_FPS;g_VideoDesc.OutputFrameFreq.Denominator         = 1;// Query the video processor GUID.UINT count;GUID* guids = NULL;hr = g_pDXVAVPS->GetVideoProcessorDeviceGuids(&g_VideoDesc, &count, &guids);

在这个例子中,GUID 的数组是通过 GetVideoProcessorDeviceGuids 函数获取的,因此应用程序必须调用CoTaskMemFree 函数释放这个数组。接下来的步骤可以使用这个GUID数组中的任何一个。

枚举渲染目标格式(Render-Target Formats)

通过传参数GUID 和 DXVA2_VideoDesc 结构体给IDirectXVideoProcessorService::GetVideoProcessorRenderTargets 函数来获取GUID所标识的视频处理器支持的渲染目标格式(Render-Target Formats)列表。代码如下:

// Query the supported render-target formats.UINT i, count;D3DFORMAT* formats = NULL;HRESULT hr = g_pDXVAVPS->GetVideoProcessorRenderTargets(guid, &g_VideoDesc, &count, &formats);if (FAILED(hr)){DBGMSG((L"GetVideoProcessorRenderTargets failed: 0x%x.\n", hr));return FALSE;}for (i = 0; i < count; i++){if (formats[i] == VIDEO_RENDER_TARGET_FORMAT){break;}}CoTaskMemFree(formats);if (i >= count){DBGMSG((L"The device does not support the render-target format.\n"));return FALSE;}

这个方法返回一个 D3DFORMAT 数组。在个例子中,输入类型为YUY2,获取到的输出列表可能是D3DFMT_X8R8G8B8 (32-bit RGB) 和 D3DMFT_YUY2。但是,真实的列表还是依赖于硬件驱动。

子流的渲染目标格式格式列表依赖于主流渲染目标格式和输入格式。要获取一个子流的渲染目标格式列表,可以通过传递GUID, DXVA2_VideoDesc 结构体和主流渲染目标格式给IDirectXVideoProcessorService::GetVideoProcessorSubStreamFormats  函数来获取。代码如下:

// Query the supported substream formats.formats = NULL;hr = g_pDXVAVPS->GetVideoProcessorSubStreamFormats(guid, &g_VideoDesc, VIDEO_RENDER_TARGET_FORMAT, &count, &formats);if (FAILED(hr)){DBGMSG((L"GetVideoProcessorSubStreamFormats failed: 0x%x.\n", hr));return FALSE;}for (i = 0; i < count; i++){if (formats[i] == VIDEO_SUB_FORMAT){break;}}CoTaskMemFree(formats);if (i >= count){DBGMSG((L"The device does not support the substream format.\n"));return FALSE;}

这个函数返回另外一个D3DFORMAT 数组。通常子流的渲染目标格式为AYUV 和 AI44。

查询设备内管理集

通过传递GUID,DXVA2_VideoDesc结构体,渲染目标格式给IDirectXVideoProcessorService::GetVideoProcessorCaps  函数来获取特定设备的能力集。这个方法通过结构体 DXVA2_VideoProcessorCaps 来返回对应设备的能力集。代码如下:

// Query video processor capabilities.hr = g_pDXVAVPS->GetVideoProcessorCaps(guid, &g_VideoDesc, VIDEO_RENDER_TARGET_FORMAT, &g_VPCaps);if (FAILED(hr)){DBGMSG((L"GetVideoProcessorCaps failed: 0x%x.\n", hr));return FALSE;}

创建设备

最后通过调用IDirectXVideoProcessorService::CreateVideoProcessor 函数来创建视频处理器。输入参数为GUID,DXVA2_VideoDesc结构体,渲染目标格式、你打算去混合的最大子流数量。这个方法返回一个指向 IDirectXVideoProcessor 接口的对象 。它代表一个视频处理器对象。代码如下:

// Finally create a video processor device.hr = g_pDXVAVPS->CreateVideoProcessor(guid,&g_VideoDesc,VIDEO_RENDER_TARGET_FORMAT,SUB_STREAM_COUNT,&g_pDXVAVPD);

3.处理视频画面(video Process Blit)

主要的视频处理操作是video processing blit (一个blit 联合2个或者更多的位图为一个位图),通过调用函数IDirectXVideoProcessor::VideoProcessBlt 来执行一个video processing blit。这个函数传递 video samples 的一个集合给视频处理器对象。视频处理器处理Video Samples里面的位图来得到一个输出帧。处理包括反交错,颜色空间转换,子流混合。输出被绘制到一个目标表面(IDirect3DSurface9 对象,并且目标是一个Render Target对象)。

VideoProcessBlt 函数拥有下面的参数:

  • pRT 是一个指向IDirect3DSurface9 渲染目标对象的指针,处理完的视频画面将绘制到这个目标表面。
  • pBltParams指向一个  DXVA2_VideoProcessBltParams 结构体对象指针。这个结构体的参数控制了blit的一部分行为。
  • pSamples 指向一个 DXVA2_VideoSample 结构体数组的地址。这个结构体数组包含blit行为的 input samples。
  • NumSamples标识了pSamples指向数组的大小。
  • Reserved 为保留参数,设置为NULL.

在pSamples数组中,调用者必须提供下面  input samples:

  • 主视频流的当前位图。
  • 如果反交错算法需要,需要包含参考前后帧。
  • 最多15路的子流位图。

硬件驱动希望这个数组是指定的序列。具体描述参考后面。

DXVA2_VideoProcessBltParams 参数介绍

重要成员如下所示:

  • TargetFrame 标识输出帧的显示时间。针对逐行帧,这个时间必须和主视频流当前帧的开始时间相等。这个时间包含在 input sample中的DXVA2_VideoSample 结构体的Start 成员变量中。对于交错视频,一帧是由两个交错的场组成,可以生成两个不同的输出帧。第一个输出帧的时间和主视频流中的DXVA2_VideoSample结构体的Start 相等,第二个输出帧的时间是当前帧和下一帧时间的中间值。例如,如果输入视频 25帧每秒(50 场每秒),输出帧将有下面图表中显示的时间戳。这个图表的时间单位为 100 纳秒。

如果交错的视频是由单个的场组成,输出帧的时间总是和输入帧的时间相同。同逐行视频一样。

  • TargetRect 定义了一个目标表面的矩形区域。blit 操作只会到这个矩形区域输出。这个矩形定义了所有输入视频流的绘制边界。每一路流在TargetRect 矩形中的绘制区域在VideoProcessBlt函数的pSamples 变量中定义。
  • BackgroundColor 指定没有视频位图绘制地方的背景色。例如,一个16:9的视频显示在一个4:3的区域里面,会产生一个黑边,这个黑边将用这个颜色绘制。背景色仅仅会在TargetRect 变量指定的区域显示。TargetRect 区域外的像素将不做任何修改。
  • DestFormat 变量将描述输出视频的颜色空间。例如,是否 ITU-R BT.709 或者 BT.601颜色空间被使用。这个参数能影响位图显示的效果。想要知道更多的信息,参考Extended Color Information

其他变量的描述请参考 DXVA2_VideoProcessBltParams 结构体。

Input Samples参数介绍

函数 IDirectXVideoProcessor::VideoProcessBlt 的 pSamples 变量指向 DXVA2_VideoSample结构体数组。每个这样的结构体包含一个 input sample 信息和 一个 IDirect3DSurface 对象。每个Sample 是下面中的一个:

  • 主视频流中的当前位图。
  • 反交错视频的时候,主视频流中的前后参考帧。
  • 一个子流位图。

pSamples 指向的数组里面Sample 的序列稍后将有详细的描述。

虽然大部分时候视频应用程序仅仅需要一个子视频流,但是我们能增加子视频流到15个。每次调用函数VideoProcessBlt 都可以改变子流的数量。通过设置 DXVA2_VideoSample 结构的SampleFormat.SampleFormat 成员变量为DXVA2_SampleSubStream来标识这一个Sample为子流位图。对于主视频流,这个变量标识了交错视频的信息。想要查看更多的知识,请查看DXVA2_SampleFormat 结构体信息。

针对主视频流, DXVA2_VideoSample 结构体的 Start 和End 变量标识了此 input sample的开始和结束时间。针对子视频流,设置这些值为0。因为显示时间总是从主视频流当中获取。应用程序要负责在合适的时间将子视频流的位图提交给 VideoProcessBlt 函数。

下面两个矩形定义了每一路视频流在目标显示区域的呈现方式:

  • DXVA2_VideoSample 结构体的 SrcRect 变量指定了 输入帧的哪一部分要显示出来。可以用这个参数来对输入帧做裁剪。
  • DXVA2_VideoSample 结构体的 DstRect 变量指定了在目标Surface上面的显示区域。

硬件驱动将输入帧中SrcRect 区域指定的像素数据复制到目标Surface的DstRect 区域中。两个区域可能大小和纵横比例不同(驱动会更根据需要缩放对应位图)。此外,每个子数据流可以有不同的缩放因子。一般使用的子流格式为 AYUV 和AI44。

4.图像融合(Image Composition)

每次图像处理操作都包含下面几个矩形:

  • TargetRect 矩形变量标识了目标Surface 显示的区域。输出图像通过这个矩形来裁剪。
  • DstRect 矩形变量标识了子流位图在合成图像中显示的位置。
  • SrcRect 矩形变量标识了子流位图中那一部分要显示出来。

详细描述可以参考下图:

视频处理器做 透明通道融合操作时,使用下面的 alpha 数据:

  • 子流当中每个像素数据的 alpha 数据。
  • 每个子流中一个平面的alpha 数据。  在DXVA2_VideoSample结构体的PlanarAlpha 变量中指定。
  • 合成后的图片与背景色融合的alpha数据。在 DXVA2_VideoProcessBltParams 结构体的Alpha变量中指定。

这一节将通过一系列的例子展示如何对图像融合。

例1:黑边处理

这个例子展示怎么对输入源图像做黑边处理,通过设置DstRect小于TargetRect可以达到这个效果。在这个例子中,主视频流的输入源大小为720*480,它 的纵横比例为16:9,但是目标Surface为640*480,纵横比为4:3,为了让视频的纵横比保持不变,因此应该将DstRect的大小设置为640*360。为了简单起见,这个例子不包含子数据流,下图展示了输入源和目标Surface大小:

上图显示的矩形大小为:

  • Target rectangle: { 0, 0, 640, 480 }

  • Primary video:

    • Source rectangle: { 0, 0, 720, 480 }
    • Destination rectangle: { 0, 60, 640, 420 }

硬件驱动将对视频进行反交错处理,缩小反交错帧到640 × 360的大小。然后将数据填充到DstRect标识目标区域内。TargetRect比DstRect矩形大,因此驱动将使用指定背景色填充其他区域。背景色在 DXVA2_VideoProcessBltParams 结构体中指定。

例2:拉伸子流视频画面

The preceding diagram shows the following rectangles:

  • Target rectangle: { 0, 0, 854, 480 }

  • Primary video:

    • Source rectangle: { 0, 0, 720, 480 }
    • Destination rectangle: { 0, 107, 474, 480 }
  • Substream:

    • Source rectangle: { 0, 0, 720, 480 }
    • Destination rectangle: { 0, 0, 854, 480 }

例3 :不匹配的流高度

The preceding diagram shows the following rectangles:

  • Target rectangle: { 0, 0, 150, 85 }
  • Primary video:
    • Source rectangle: { 0, 0, 150, 50 }
    • Destination rectangle: { 0, 17, 150, 67 }
  • Substream:
    • Source rectangle: { 0, 0, 100, 85 }
    • Destination rectangle: { 25, 0, 125, 85 }

例4:目标矩形小于目标surface

The preceding diagram shows the following rectangles:

  • Destination surface: { 0, 0, 300, 200 }
  • Target rectangle: { 0, 0, 150, 85 }
  • Primary video:
    • Source rectangle: { 0, 0, 150, 50 }
    • Destination rectangle: { 0, 17, 150, 67 }
  • Substream:
    • Source rectangle: { 0, 0, 100, 85 }
    • Destination rectangle: { 25, 0, 125, 85 }

例5:源矩形

The preceding diagram shows the following rectangles:

  • Target rectangle: { 0, 0, 720, 576 }
  • Primary video:
    • Source surface size: { 0, 0, 720, 480 }
    • Source rectangle: { 360, 240, 720, 480 }
    • Destination rectangle: { 0, 0, 360, 240 }
  • Substream:
    • Source surface size: { 0, 0, 640, 576 }
    • Source rectangle: { 0, 288, 320, 576 }
    • Destination rectangle: { 400, 0, 720, 288 }

例6: 相交的目标矩形

The preceding diagram shows the following rectangles:

  • Target rectangle: { 0, 0, 720, 576 }
  • Primary video:
    • Source surface size: { 0, 0, 720, 480 }
    • Source rectangle: { 260, 92, 720, 480 }
    • Destination rectangle: { 0, 0, 460, 388 }
  • Substream:
    • Source surface size: { 0, 0, 640, 576 }
    • Source rectangle: { 0, 0, 460, 388 }
    • Destination rectangle: { 260, 188, 720, 576 }

例7: 拉伸和裁剪视频

The preceding diagram shows the following rectangles:

  • Target rectangle: { 0, 0, 720, 480 }
  • Primary video:
    • Source surface size: { 0, 0, 360, 240 }
    • Source rectangle: { 180, 120, 360, 240 }
    • Destination rectangle: { 0, 0, 360, 240 }
  • Substream:
    • Source surface size: { 0, 0, 360, 240 }
    • Source rectangle: { 0, 0, 180, 120 }
    • Destination rectangle: { 360, 240, 720, 480 }

5.输入源序列

VideoProcessBlt 函数的pSamples 变量指定了一个 input samples数组。主视频流的Samples 最先显示。下面是子视频流位图序列:

  • 按时间顺序,主视频流的Samples 最先显示。为了实现反交错处理,驱动可能需要从主视频流当中获取更多的参考 samples。 在结构体 DXVA2_VideoProcessorCaps 中的NumForwardRefSamples 和变量NumBackwardRefSamples 指定了向前和向后参考帧的数量。调用者必须提供参考的samples ,即使输入视频帧为逐行帧。列如,当一个输入源同时包含逐行帧和交错帧。
  • 在主视频流的samples后面, 数组能包含15个子流samples ,顺序为从低到上。子流总是逐行帧,并且没有参考帧。

任何时候,主视频流总是能在逐行和交错帧之间切换。子流的数量也能改变。

DXVA2_VideoSample结构体的SampleFormat.SampleFormat变量标识了图片的类型。针对子流类型,这个变量为DXVA2_SampleSubStream。针对逐行帧,这个变量为DXVA2_SampleProgressiveFrame,针对为交错帧,这个变量依赖具体的场类型。

如果驱动要求前后参考帧,全部的samples可能无效在视频开始序列,在这种情况下,丢掉DXVA2_SampleUnknown类型的Samples.

DXVA2_VideoSample结构体的Start 和End变量给定了每个sample显示的时间。这个仅仅在主视频流中有用,对于子视频流,设置这两个变量为0.

下面的例子将帮助我们弄清楚这个序列。

例子1:

最简单的情况是:没有子数据流,并且反交错的算法不要求前后参考帧。Bob 反交错算法就是这样的。pSamples 数组包含一个输入数据流。如下图:

这个时间值T被假设为当前视频帧的开始显示时间。

例子2:

这个例子将2个子数据流与一个主视频流融合。反交错算法不请求参考samples,下表显示了samples 在pSamples 数组中显示的顺序:

例子3:

这个例子有2个子视频流,一个主视频流,还请求一个向前参考帧,一个向后参考帧。下表显示了samples 在pSamples 数组中显示的顺序:

时间T-1为当前帧的前一帧的开始时间,T+1为当前帧的后一帧的开始时间。

如果当前视频流切换到逐行帧,使用相同的反交错模式。应用程序必须提供相同的samples序列。序列显示如下表:

例子4:

在刚开始播放视频的时候,向前或者向后的参考帧可能是无效的。这个时候无效的Sample用类型DXVA2_SampleUnknown标识。

假设一个交错算法需要一个先前和一个向后参考帧,前3次调用VideoProcessBlt 函数 Samples 的序列为:

写了一天多终于把这篇博客写完了,希望自己的劳动成果能对大家有所帮助,也感谢我女朋友(吕文静)对我写博客的支持。

这里贴出我在项目中使用dxva进行反交错视频的部分代码:

//获取IDirectXVideoProcessorService接口对象
DXVA2CreateVideoService(m_pd3dDevice, IID_PPV_ARGS(&m_pDXVAVPS));
....//创建IDirectXVideoProcessor接口对象
bool CD3DRenderEx::CreateVideoProcessor(AVFrame * pFrame, void ** ppVideoProcessor)
{if (!m_pDXVAVPS || !ppVideoProcessor)return false;IDirectXVideoProcessor *pDXVAVPD = NULL;*ppVideoProcessor = NULL;DXVA2_VideoDesc desc = { 0 };desc.SampleWidth = pFrame->width;desc.SampleHeight = pFrame->height;if (pFrame->format == AV_PIX_FMT_DXVA2_VLD || pFrame->format== AV_PIX_FMT_NV12){desc.Format = (D3DFORMAT)MAKEFOURCC('N', 'V', '1', '2');}else if (pFrame->format == AV_PIX_FMT_YUV420P){desc.Format = (D3DFORMAT)MAKEFOURCC('Y', 'V', '1', '2');}else{return false;}HR(m_pDXVAVPS->CreateVideoProcessor(DXVA2_VideoProcBobDevice, &desc, D3DFMT_X8R8G8B8, 0, &pDXVAVPD));*ppVideoProcessor = pDXVAVPD;return true;
}
...//调用VideoProcessBlt函数来处理输入的surface,得到处理好的Surface
int CD3DRenderEx::PutFrameToVideoTextureByDeinterlaced(AVFrame * pFrame, LONGLONG pts, LONGLONG duration, void * pTexture, void* pVideoProcessor)
{if (!m_pd3dDevice || !pFrame || !pTexture || !pVideoProcessor){return -1;}IDirectXVideoProcessor *pDXVAVPD = (IDirectXVideoProcessor*)pVideoProcessor;LPD3DVIDEOTEXTURE pD3DVideoTexuture = (LPD3DVIDEOTEXTURE)pTexture;DXVA2_VideoProcessBltParams blitParam = { 0 };blitParam.TargetFrame = pts * 10000;blitParam.TargetRect.right = pD3DVideoTexuture->iTexWidth;blitParam.TargetRect.bottom = pD3DVideoTexuture->iTexHeight;blitParam.BackgroundColor.Alpha = 0xFFFF;blitParam.DestFormat.NominalRange = DXVA2_NominalRange_Unknown;blitParam.DestFormat.SampleFormat = DXVA2_SampleFieldInterleavedOddFirst;//blitParam.DestData = DXVA2_DestData_RFF;DXVA2_ValueRange valueRange;pDXVAVPD->GetProcAmpRange(DXVA2_ProcAmp_Brightness, &valueRange);blitParam.ProcAmpValues.Brightness = valueRange.DefaultValue;pDXVAVPD->GetProcAmpRange(DXVA2_ProcAmp_Contrast, &valueRange);blitParam.ProcAmpValues.Contrast = valueRange.DefaultValue;pDXVAVPD->GetProcAmpRange(DXVA2_ProcAmp_Hue, &valueRange);blitParam.ProcAmpValues.Hue = valueRange.DefaultValue;pDXVAVPD->GetProcAmpRange(DXVA2_ProcAmp_Saturation, &valueRange);blitParam.ProcAmpValues.Saturation = valueRange.DefaultValue;blitParam.Alpha = DXVA2_Fixed32OpaqueAlpha();DXVA2_VideoSample videoSample = { 0 };videoSample.Start = pts * 10000;videoSample.End = (pts + duration) * 10000;videoSample.SampleFormat.NominalRange = DXVA2_NominalRange_Unknown;videoSample.SampleFormat.SampleFormat = DXVA2_SampleFieldInterleavedOddFirst;videoSample.SrcRect.right = pFrame->width;videoSample.SrcRect.bottom = pFrame->height;videoSample.DstRect.right = pD3DVideoTexuture->iTexWidth;videoSample.DstRect.bottom = pD3DVideoTexuture->iTexHeight;//videoSample.SampleData = DXVA2_DestData_RFF;do{CAutoExclLocker lock(&pD3DVideoTexuture->srwLock);//SUN test硬解if (pFrame->format == AV_PIX_FMT_DXVA2_VLD){pD3DVideoTexuture->dataFormat = DATA_FMT_BGRA;if (!pD3DVideoTexuture->pDisplayTexture){if (!CreateDisplayTexture(pD3DVideoTexuture->iTexWidth, pD3DVideoTexuture->iTexHeight, &pD3DVideoTexuture->pDisplayTexture)){return -1;}}IDirect3DSurface9 * pSurface = NULL;pD3DVideoTexuture->pDisplayTexture->GetSurfaceLevel(0, &pSurface);IDirect3DSurface9* pDXVASurface = (IDirect3DSurface9*)pFrame->data[3];videoSample.SrcSurface = pDXVASurface;if (FAILED(pDXVAVPD->VideoProcessBlt(pSurface, &blitParam, &videoSample, 1, NULL))){RELEASE(pSurface);return -1;}RELEASE(pSurface);return 0;}else if (pFrame->format == AV_PIX_FMT_NV12){int iFrameWidth = pFrame->width & 0xFFFFFFFE;int iFrameHeight = pFrame->height & 0xFFFFFFFE;if (pD3DVideoTexuture->pOffScreenSurface == NULL){if (FAILED(m_pd3dDevice->CreateOffscreenPlainSurface(iFrameWidth,iFrameHeight,(D3DFORMAT)MAKEFOURCC('N', 'V', '1', '2'),D3DPOOL_DEFAULT,&pD3DVideoTexuture->pOffScreenSurface,NULL))){return -1;}}D3DLOCKED_RECT d3d_rect;if (FAILED(pD3DVideoTexuture->pOffScreenSurface->LockRect(&d3d_rect, NULL, D3DLOCK_DISCARD))){return -1;}byte * pDest = (BYTE *)d3d_rect.pBits;int stride = d3d_rect.Pitch;pD3DVideoTexuture->dataFormat = DATA_FMT_BGRA;for (int i = 0; i < iFrameHeight; i++) {memcpy(pDest + i * stride, pFrame->data[0] + i * pFrame->linesize[0], iFrameWidth);}for (int i = 0; i < iFrameHeight / 2; i++){memcpy(pDest + stride * iFrameHeight + i*stride, pFrame->data[1] + i * pFrame->linesize[1], iFrameWidth);}pD3DVideoTexuture->pOffScreenSurface->UnlockRect();if (!pD3DVideoTexuture->pDisplayTexture){if (!CreateDisplayTexture(pD3DVideoTexuture->iTexWidth, pD3DVideoTexuture->iTexHeight, &pD3DVideoTexuture->pDisplayTexture, false)){return -1;}}IDirect3DSurface9 * pSurface = NULL;pD3DVideoTexuture->pDisplayTexture->GetSurfaceLevel(0, &pSurface);videoSample.SrcSurface = pD3DVideoTexuture->pOffScreenSurface;if (FAILED(pDXVAVPD->VideoProcessBlt(pSurface, &blitParam, &videoSample, 1, NULL))){RELEASE(pSurface);return -1;}RELEASE(pSurface);return 0;    }else if(pFrame->format == AV_PIX_FMT_YUV420P){int iFrameWidth = pFrame->width & 0xFFFFFFFE;int iFrameHeight = pFrame->height & 0xFFFFFFFE;if (pD3DVideoTexuture->pOffScreenSurface == NULL){if (FAILED(m_pd3dDevice->CreateOffscreenPlainSurface(iFrameWidth,iFrameHeight,(D3DFORMAT)MAKEFOURCC('Y', 'V', '1', '2'),D3DPOOL_DEFAULT,&pD3DVideoTexuture->pOffScreenSurface,NULL))){return -1;}}D3DLOCKED_RECT d3d_rect;if (FAILED(pD3DVideoTexuture->pOffScreenSurface->LockRect(&d3d_rect, NULL, D3DLOCK_DISCARD))){return -1;}byte * pDest = (BYTE *)d3d_rect.pBits;int stride = d3d_rect.Pitch;pD3DVideoTexuture->dataFormat = DATA_FMT_BGRA;for (int i = 0; i < iFrameHeight; i++) {memcpy(pDest + i * stride, pFrame->data[0] + i * pFrame->linesize[0], iFrameWidth);}for (int i = 0; i < iFrameHeight / 2; i++) {memcpy(pDest + stride * iFrameHeight + i * stride / 2, pFrame->data[2] + i * pFrame->linesize[1], iFrameWidth / 2);}for (int i = 0; i < iFrameHeight / 2; i++) {memcpy(pDest + stride * iFrameHeight + stride * iFrameHeight / 4 + i * stride / 2, pFrame->data[1] + i * pFrame->linesize[1], iFrameWidth / 2);}pD3DVideoTexuture->pOffScreenSurface->UnlockRect();if (!pD3DVideoTexuture->pDisplayTexture){if (!CreateDisplayTexture(pD3DVideoTexuture->iTexWidth, pD3DVideoTexuture->iTexHeight, &pD3DVideoTexuture->pDisplayTexture, false)){return -1;}}IDirect3DSurface9 * pSurface = NULL;pD3DVideoTexuture->pDisplayTexture->GetSurfaceLevel(0, &pSurface);videoSample.SrcSurface = pD3DVideoTexuture->pOffScreenSurface;if (FAILED(pDXVAVPD->VideoProcessBlt(pSurface, &blitParam, &videoSample, 1, NULL))){RELEASE(pSurface);return -1;}RELEASE(pSurface);return 0;}else{return 1;}} while (0);
}

DXVA 处理交错视频相关推荐

  1. android 播放器 反交错,反交错- 视频横纹处理

    以下名词解释摘自维基百科 反交错"(deinterlacing)是将交错式(即隔行扫描)(interlace)影像讯号转换为渐进式(逐行扫描)(progressive)影像讯号的一种方法. ...

  2. Revvel如何将视频转码速度提升几十倍?

    来自Revvel的团队的Greg Femec在re:Invent 2017大会上分享了他在构建视频转码服务中遇到的挑战,以及构建基于Serverless架构的视频转码平台的经验.Revvel团队将视频 ...

  3. H.264码流解析 一个SPS的nalu及获取视频的分辨率

    00 00 00 01 67 42 00 28 E9 00   A0 0B 77 FE 00 02 00 03 C4 80   00 00 03 00 80 00 00 1A 4D 88   10 9 ...

  4. FFmpeg 工具:音视频开发都用它,快@你兄弟来看丨音视频工具

    (本文基本逻辑:ffmpeg 常用命令介绍 → ffplay 常用命令介绍 → ffprobe 常用命令介绍) 从事音视频开发的程序员几乎都应该知道或使用过 FFmpeg.FFmpeg 是一个开源软件 ...

  5. 【H.264/AVC视频编解码技术详解】三. H.264简介

    <H.264/AVC视频编解码技术详解>视频教程已经在"CSDN学院"上线,视频中详述了H.264的背景.标准协议和实现,并通过一个实战工程的形式对H.264的标准进行 ...

  6. 【视频编解码·学习笔记】2. H.264简介

    一.H.264视频编码标准 H.264视频编码标准是ITU-T与MPEG合作产生的又一巨大成果,自颁布之日起就在业界产生了巨大影响.严格地讲,H.264标准是属于MPEG-4家族的一部分,即MPEG- ...

  7. 详解Camtasia的视频导出功能

    使用Camtasia录制和剪辑的视频格式为trec,这种格式只能用Camtasia进行查看,若想用其他软件查看,我们就必须转换视频格式.下面我将使用Camtasia 2020(Win版)来为大家介绍如 ...

  8. 视频编解码及H264技术

    [H.264/AVC视频编解码技术详解]一. 视频信息与压缩编码 2016年07月31日 21:30:02 取次花丛懒回顾 阅读数 7460更多 分类专栏: 视频编解码 先进视频压缩编码(Advanc ...

  9. 【音视频】H264编码基础

    H264编码基础 0x1 基本介绍 视频是由一帧帧图像组成,视频为了不卡顿,一秒钟至少要16帧画面,但是图片内容太大,传输不现实.因此需要对他们编码. 官方文档:http://www.itu.int/ ...

最新文章

  1. ASP.NET Core 2.1带来SignalR、Razor类库
  2. python爬虫 django搜索修改更新数据_django_数据库操作—增、删、改、查
  3. 头秃元凶「真面目」首次被揭穿,鹅厂程序员立功了 | Nature子刊
  4. [置顶] 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
  5. matlab读取/播放视频的函数(2)
  6. HLS-搭建Nginx流媒体服务器
  7. H5——while循环,for循环
  8. 预告|CVPR 2021 论文分享会日程公布!与计算机视觉领域学者一起收获“立体”参会体验...
  9. VUE自学日志04-Data Property 和方法
  10. controller 用 map 接收值_一文弄懂apply、map和applymap三种函数的区别
  11. 软件测试思维导图大全
  12. 群晖存储服务器虚拟机,安装黑群晖DSM6.2.1完整教程(虚拟机VMWARE15)
  13. nvidia卸载程序失败_卸载删除NVIDIA驱动程序的方法步骤教程 - 系统家园
  14. PS使模糊图片变清晰
  15. 电脑恶意软件删除方法
  16. [乐意黎原创] 2018年度CSDN博客排名前一百五十强
  17. 大咖说*计算讲谈社|不止能上路,更能做好服务:自动驾驶产品规模化的问题定义
  18. 详叙 @vue/cli 和 vue-cli
  19. 【学习笔记】正则语言的可检验性(性质检验)
  20. Snowflake Snow Snowflakes(哈希表的应用)

热门文章

  1. 【Vue】 错误:Already included file name ‘××ב differs from file name ‘××ב only in casing的解决方法
  2. 数据结构与算法_02_局部最优的贪心算法
  3. 条件判断————8.判断闰年
  4. RuntimeError: non-empty 3D or 4D input tensor expected but got ndim: 4
  5. java开源验证框架OVAL
  6. # 如何在Git上更改本地分支名称和远程分支名称
  7. 中职学校计算机专业精品课,建设中职学校计算机应用基础精品课程之我见
  8. Androidnbsp;滑动开关控件
  9. [生而为人-思考] Knowledge Cooking 分享会记录 -1
  10. 三维基础建模知识,看完就会