NV12 纹理在 DirectX 11.1 中不起作用

NV12 textures not working in DirectX 11.1

本文关键字:不起作用 DirectX 纹理 NV12      更新时间:2023-10-16

我正在尝试使用 DirectX 11.1 从使用 ffmpeg 2.8.11 解码的帧渲染 NV12 纹理,但当我渲染它们时,纹理被破坏并且颜色总是关闭。

结果是 :https://i.stack.imgur.com/8bnhT.jpg

下面的代码是我如何通过 ffmpeg 解码YUV420P格式的帧,然后通过交错 U 和 V 平面将(不确定)转换为 NV12 格式。

static uint8_t *pixelsPtr_ = nullptr;
UINT rowPitch = ((width + 1) >> 1) * 2;
UINT imageSize = (rowPitch * height) + ((rowPitch * height + 1) >> 1);
if (!pixelsPtr_)
{
pixelsPtr_ = new uint8_t[imageSize];
}
int j, position = 0;
uint32_t pitchY = avFrame.linesize[0];
uint32_t pitchU = avFrame.linesize[1];
uint32_t pitchV = avFrame.linesize[2];
uint8_t *avY = avFrame.data[0];
uint8_t *avU = avFrame.data[1];
uint8_t *avV = avFrame.data[2];
::SecureZeroMemory(pixelsPtr_, imageSize);
for (j = 0; j < height; j++)
{               
::CopyMemory(pixelsPtr_ + position, avY, (width));
position += (width);
avY += pitchY;
}
for (j = 0; j < height >> 1; j++)
{
::CopyMemory(pixelsPtr_ + position, avU, (width >> 1));
position += (width >> 1);
avU += pitchU;
::CopyMemory(pixelsPtr_ + position, avV, (width >> 1));
position += (width >> 1);
avV += pitchV;
}

这就是我用刚刚获得的数据创建Texture2D的方式。

// Create texture
D3D11_TEXTURE2D_DESC desc;
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_NV12;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA initData;
initData.pSysMem = pixelsPtr_;
initData.SysMemPitch = rowPitch;
ID3D11Texture2D* tex = nullptr;
hr = d3dDevice->CreateTexture2D(&desc, &initData, &tex);
if (SUCCEEDED(hr) && tex != 0)
{
D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc;
memset(&SRVDesc, 0, sizeof(SRVDesc));
SRVDesc.Format = DXGI_FORMAT_R8_UNORM;
SRVDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
SRVDesc.Texture2D.MipLevels = 1;
hr = d3dDevice->CreateShaderResourceView(tex, &SRVDesc, &textureViewYUV[0]);
if (FAILED(hr))
{
tex->Release();
return hr;
}
SRVDesc.Format = DXGI_FORMAT_R8G8_UNORM;
hr = d3dDevice->CreateShaderResourceView(tex, &SRVDesc, &textureViewYUV[1]);
if (FAILED(hr))
{
tex->Release();
return hr;
}
tex->Release();
}

然后我将两个着色器资源视图传递给像素着色器

graphics->Context()->PSSetShaderResources(0, 2, textureViewYUV);

这是像素着色器:

struct PixelShaderInput
{
float4 pos         : SV_POSITION;
float4 Color       : COLOR;
float2 texCoord    : TEXCOORD;
};
static const float3x3 YUVtoRGBCoeffMatrix =
{
1.164383f,  1.164383f, 1.164383f,
0.000000f, -0.391762f, 2.017232f,
1.596027f, -0.812968f, 0.000000f
};
Texture2D<float>  luminanceChannel;
Texture2D<float2> chrominanceChannel;
SamplerState linearfilter
{
Filter = MIN_MAG_MIP_LINEAR;
};
float3 ConvertYUVtoRGB(float3 yuv)
{
// Derived from https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx
// Section: Converting 8-bit YUV to RGB888
// These values are calculated from (16 / 255) and (128 / 255)
yuv -= float3(0.062745f, 0.501960f, 0.501960f);
yuv = mul(yuv, YUVtoRGBCoeffMatrix);
return saturate(yuv);
}
float4 main(PixelShaderInput input) : SV_TARGET
{
float y = luminanceChannel.Sample(linearfilter, input.texCoord);
float2 uv = chrominanceChannel.Sample(linearfilter, input.texCoord);
float3 YUV = float3(y, uv.x, uv.y);
float4 YUV4 = float4(YUV.x, YUV.y, YUV.z, 1);
float3 RGB = ConvertYUVtoRGB(YUV);
float4 RGB4 = float4(RGB.x, RGB.y, RGB.z, 1);
return RGB4;
}

有人可以帮助我吗?我做错了什么?

编辑 #1

int skipLineArea = 0;
int uvCount = (height >> 1) * (width >> 1);
for (j = 0, k = 0; j < uvCount; j++, k++)
{
if (skipLineArea == (width >> 1))
{
k += pitchU - (width >> 1);
skipLineArea = 0;
}
pixelsPtr_[position++] = avU[k];
pixelsPtr_[position++] = avV[k];
skipLineArea++;
}

编辑#2

更新纹理而不是创建新纹理

D3D11_MAPPED_SUBRESOURCE mappedResource;
d3dContext->Map(tex, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
uint8_t* mappedData = reinterpret_cast<uint8_t*>(mappedResource.pData);
for (UINT i = 0; i < height * 1.5; ++i)
{
memcpy(mappedData, frameData, rowPitch);
mappedData += mappedResource.RowPitch;
frameData += rowPitch;
}
d3dContext->Unmap(tex, 0);

NV12格式不会分隔U值和V值。它们是交错的 NV12 格式。您应该使用IMC4纹理格式。或者,可以将复制代码更改为交错UV值。