I create a NV12 texture with cpu access, and use shaderResource to map Luma and Chroma, then copy decoded frame with NV12 format to it, since 1080p is 1920x1088 in buffer actually, so i dropped the bottom 8px data.
// Create NV12Texture
bool Texture::CreateNV12Texture(uint32_t width, uint32_t height)
{
HRESULT hr;
xres = width;
yres = height;
real_yres = yres;
if (yres == 1088)
{
real_yres = 1080;
}
D3D11_TEXTURE2D_DESC const texDesc = CD3D11_TEXTURE2D_DESC(
DXGI_FORMAT_NV12, // HoloLens PV camera format, common for video sources
xres, // Width of the video frames
real_yres, // Height of the video frames
1, // Number of textures in the array
1, // Number of miplevels in each texture
D3D11_BIND_SHADER_RESOURCE, // We read from this texture in the shader
D3D11_USAGE_DYNAMIC, // Because we'll be copying from CPU memory
D3D11_CPU_ACCESS_WRITE // We only need to write into the texture
);
hr = device->CreateTexture2D(&texDesc, nullptr, &nv12_texture);
if (FAILED(hr))
return false;
D3D11_SHADER_RESOURCE_VIEW_DESC const luminancePlaneDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(
nv12_texture,
D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8_UNORM
);
hr = device->CreateShaderResourceView(
nv12_texture,
&luminancePlaneDesc,
&luminanceView
);
if (FAILED(hr))
return false;
D3D11_SHADER_RESOURCE_VIEW_DESC const chrominancePlaneDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(
nv12_texture,
D3D11_SRV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_R8G8_UNORM
);
hr = device->CreateShaderResourceView(
nv12_texture,
&chrominancePlaneDesc,
&chrominanceView
);
if (FAILED(hr))
return false;
D3D11_TEXTURE2D_DESC desc = { 0 };
nv12_texture->GetDesc(&desc);
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
desc.CPUAccessFlags = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
hr = device->CreateTexture2D(&desc, 0, &d3d_texture);
if (FAILED(hr))
return false;
return true;
}
// copy frame to texture
bool Texture::updateNV12(const uint8_t* data, size_t data_size)
{
assert(data);
assert(data_size == xres * yres * 6 / 4);
HRESULT hr = S_OK;
// NV12 Copy
int stride_y = xres;
uint8_t* src_y = (uint8_t*)data;
uint8_t* src_uv = src_y + stride_y * yres;
// COPY
D3D11_MAPPED_SUBRESOURCE ms;
hr = ctx->Map(nv12_texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
if (FAILED(hr))
return false;
uint8_t* dst = (uint8_t*)ms.pData;
ms.RowPitch = xres;
uint8_t* dst_y = dst;
uint8_t* dst_uv = dst_y + stride_y * real_yres;
for (int i = 0; i < real_yres; i++)
{
//memcpy(dst + stride_y * i, dst_y + stride_y * i, stride_y);
memcpy(dst_y, src_y, stride_y);
dst_y += stride_y;
src_y += stride_y;
}
for (int i = 0; i < real_yres / 2; i++)
{
//memcpy(dst + (real_yres + i) * stride_y, dst_uv + stride_y * i, stride_y);
memcpy(dst_uv, src_uv, stride_y);
dst_uv += stride_y;
src_uv += stride_y;
}
ctx->Unmap(nv12_texture, 0);
ctx->CopyResource(d3d_texture, nv12_texture);
hr = ProcessNV12ToBmpFile("data/nv12.bmp", dst, xres, xres, real_yres);
if (FAILED(hr))
return false;
return true;
}
Treat d3d_texture as input_view, dxgi_backbuffer(BGRA format) as output_view of VideoProcessor, then call VideoProcessorBlt() and Present() method.
I tested 720p and 4k videos, decode and render normal, but not work for 1080p videos, the rendered image like this 1080p frame render result, i convert this NV12 buffer to bmp file, the bmp file seems normal, so the NV12 buffer of texture maybe no problem NV12 to bmp file saved.
Update
I upload my test solution to Github, including one 1080p video example, you can test it, here is dx11_video_player
Update Again
I tesetd Microsoft DX11VideoRenderer
sample, which use hard decoded texture2D for render, and i also copy the texture2D data from GPU to CPU then converted to bmp file. Under this condition, the render result is normal, but converted bmp file seems like green screen, similiar with 1080p frame render result.
Update Last
By accident, i solved this problem. If i use AMD Radeon 520 grahpic card to render 1080p nv12 format video, problem occured; but if i change to Intel UHD 630 graphic card with latest driver, problem disappered. IF you met same problem, i recommend you to change you graphic card or update driver, good luck for you