2017-03-27 2 views
1

J'essaie de rendre des textures NV12 à partir d'images décodées avec ffmpeg 2.8.11 en utilisant DirectX 11.1 mais quand je les rends la texture est cassée et la couleur est toujours éteinte.Les textures NV12 ne fonctionnent pas dans DirectX 11.1

Le résultat est: http://imgur.com/a/YIVQk

code

ci-dessous comment je reçois le cadre décodé par ffmpeg qui est au format YUV420P puis je convertir (pas sûr) au format NV12 en intercalant les plans U et V.

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; 
} 

Voici comment je crée le Texture2D avec les données que je viens de recevoir.

// 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(); 
} 

Puis je passe à la fois des ressources Shader Voir Pixel Shader

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

C'est le pixel shader:

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; 
} 

quelqu'un peut me aider? Qu'est-ce que je fais mal?

EDIT # 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++; 
} 

EDIT # 2

Mise à jour de la texture au lieu de créer de nouvelles

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); 

Répondre

1

le format NV12 ne sépare pas U et V valeurs. Ils sont entrelacés NV12 format. Vous devez utiliser le format de texture IMC4. Ou vous pouvez modifier le code de copie pour entrelacer les valeurs U et V.

+0

Mais j'intercalise les valeurs U et V, dans la première citation de code. avFrame est le cadre que ffmpeg me donne au format YUV420P et je copie ses trucs en pixelsPtr_ au format NV12. –

+1

@ AndréVitor Vous les entrelacez ligne par ligne, mais pour 'NV12' ils doivent être entrelacés valeur par valeur. Intercalaire ligne par ligne utilisé dans 'IMC4' –

+0

J'ai fait les changements nécessaires, le nouveau code pour les valeurs U et V est dans la publication. Maintenant, parfois l'image est parfaite, parfois cassée comme avant http://prnt.sc/epc19j –