首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Delphi FMX:使用GPU编程在TForm3D/组件上绘制YUV图像

Delphi FMX:使用GPU编程在TForm3D/组件上绘制YUV图像
EN

Stack Overflow用户
提问于 2022-03-13 01:28:58
回答 1查看 236关注 0票数 0

我试图在FMX表单上渲染YUV图像。我一直在研究以下例子:https://github.com/grijjy/JustAddCode/tree/master/GpuProgramming

我已经设法呈现YUVNV12和YUV420图像(我用FFMPEG库从视频文件中解码),使用Delphi + Direct3D。在VCL的情况下,我可以在C/C++代码中找到足够的例子,可以翻译成Delphi (VCL)。

我尝试了以下方法来渲染(YUV)图像:

  1. SDL2库:在VCL上工作得很好。与Delphi一起无法在Android上使用。
  2. PXL库:在Windows和Android上运行良好(我无法在其他平台上进行测试: iOS、macOS.)除了我需要转换YUV -> RGB在渲染之前。这在Android (手机/平板电脑)上太慢了。
  3. Delphi项目使用DirectX API调用,转换了C/C++示例的源代码,这些例子可以很好地工作到HD (rec.709)图像。我还无法用HDR (rec.2020)图像渲染图像。

最终要实现的目标是渲染YUV图像,而不是通过FMX表单/组件上的CPU将其转换为RGB/BGR,这样它就可以在多个平台上使用。

在“03纺织”的例子中,我添加了两个纹理:一个用于Y平面,一个用于UV平面:

代码语言:javascript
运行
复制
property TextureY : TTexture read FTextureY   write SetTextureY;
property TextureUV: TTexture read FTextureUV  write SetTextureUV;

我在过程“HandleImageChanged”中做了一些修改:

代码语言:javascript
运行
复制
procedure TImageMaterialSource.HandleImageChanged(Sender: TObject);
begin
//TImageMaterial(Material).Texture := TTexture.Create;
//TImageMaterial(Material).Texture.PixelFormat := FMX.types.tpixelformat.a;
if n12 <> nil
then begin
  if TImageMaterial(Material).TextureY = nil
  then begin
    TImageMaterial(Material).TextureY := TTexture.Create;
    TImageMaterial(Material).TextureY.PixelFormat := FMX.types.tpixelformat.L;
    TImageMaterial(Material).TextureY.SetSize(n12.pitch, n12.height);
  end;
  if TImageMaterial(Material).TextureUV = nil
  then begin
    TImageMaterial(Material).TextureUV := TTexture.Create;
    TImageMaterial(Material).TextureUV.PixelFormat := FMX.types.tpixelformat.LA;
    TImageMaterial(Material).TextureUV.SetSize(n12.pitch, n12.height div 2);
  end;

  TImageMaterial(Material).TextureY.UpdateTexture(n12.Y, n12.width);
  TImageMaterial(Material).TextureUV.UpdateTexture(n12.UV, n12.width);
end;

正如你所看到的,我正在尝试更新Y和UV纹理,希望它们被发送到GPU。我在textureY中使用了“FMX.ypes.t像素格式1字节”,在textureY代码中使用了DXGI_FORMAT_R8_UNORM。对于textureUV,"FMX.types.tpixelformat.LA -2字节“<- DXGI_FORMAT_R8G8_UNORM -我不知道这些是否是好的选择,只是为了尝试.

顶点着色器:

代码语言:javascript
运行
复制
struct VS_INPUT                
{                              
    float4 Pos : POSITION;     
    float2 Tex : TEXCOORD;     
};                             
                           
struct VS_OUTPUT               
{                              
   float4 Pos : SV_POSITION;  
   float2 Tex : TEXCOORD;     
};                             
VS_OUTPUT main(VS_INPUT input) 
{                              
  return input;               
}

PixelShader:

代码语言:javascript
运行
复制
Texture2D Texture;                                      
Texture2D TextureY;                                     
Texture2D TextureUV;                                    
SamplerState theSampler;                                
                                                     
struct PixelShaderInput                                 
{                                                       
   float4 pos : SV_POSITION;                          
   float2 tex : TEXCOORD0;                            
   float4 color : COLOR0;                             
};                                                     
                                                     
float4 main(PixelShaderInput input) : SV_TARGET       
{                                                       
  const float3 offset = {0.0, -0.501960814, -0.501960814};
  const float3 Rcoeff = {1.0000,  0.0000,  1.4020};       
  const float3 Gcoeff = {1.0000, -0.3441, -0.7141};       
  const float3 Bcoeff = {1.0000,  1.7720,  0.0000};       
  float4 Output;                                          
  float3 yuv;                                             
  yuv.x = TextureY.Sample(theSampler, input.tex).x;       
  yuv.yz = TextureUV.Sample(theSampler, input.tex).yz;    
  yuv += offset;                                          
  Output.r = dot(yuv, Rcoeff);                            
  Output.g = dot(yuv, Gcoeff);                            
  Output.b = dot(yuv, Bcoeff);                            
  Output.a = 1.0f;                                        
  //return Output * input.color;                          
  return float4(Output);                                  
 //return Texture.Sample(Sampler, input.tex);          
}                                                       

函数,用于读取NV12文件:

代码语言:javascript
运行
复制
PNV12Frame = ^TNV12Frame;
TNV12Frame = record
  width,
  height,
  pitch:Cardinal;
  Y:PByte;
  UV:PByte;
end;

function ReadNV12FromFile(fn:TFileName):PNV12Frame;
var f:TFileStream;
    xsize,
    readBytes:Integer;
    nv12Frame:PNV12Frame;
begin
  f := TFileStream.Create(fn, fmOpenRead);
  //FILE *file = nullptr;
  //sprintf_s(buf, "content\\16.nv12");
  //fopen_s(&file, buf, "rb");

  xsize := sizeof(TNV12Frame);
  nv12Frame := GetMemory(xsize);
  FillChar(nv12Frame^, xsize, 0);
  //readBytes := fread(nv12Frame, size, 1, file);
  f.Position := 0;
  readBytes := f.Read(nv12frame^, xsize);

  xsize := nv12Frame.pitch * nv12Frame.height;
  nv12Frame.Y := GetMemory(xsize); //(BYTE *)malloc(size);
  readBytes := f.ReadData(nv12Frame.Y, xsize);

  xsize := nv12Frame.pitch * nv12Frame.height div 2;
  nv12Frame.UV := GetMemory(xsize);  //(BYTE *)malloc(size);
  readBytes := f.ReadData(nv12Frame.UV, xsize);
  f.Free;
//fclose(file);

  Result := nv12Frame;
end;

如何设置/是否需要设置/任何用于(VCL D3D) API调用的/任意相等项?

代码语言:javascript
运行
复制
const vertexDesc:array[0..2] of D3D11_INPUT_ELEMENT_DESC  =
(
    ( SemanticName:'POSITION' ;SemanticIndex: 0;Format: DXGI_FORMAT_R32G32B32_FLOAT   
;InputSlot: 0; AlignedByteOffset:  0; InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA; 
InstanceDataStepRate: 0 ),
    ( SemanticName:'TEXCOORD' ;SemanticIndex: 0;Format: DXGI_FORMAT_R32G32_FLOAT      
;InputSlot: 0; AlignedByteOffset: 12; InputSlotClass: D3D11_INPUT_PER_VERTEX_DATA; 
InstanceDataStepRate: 0 ),
    ( SemanticName:'COLOR'    ;SemanticIndex: 0;Format: 
DXGI_FORMAT_R32G32B32A32_FLOAT;InputSlot: 0; AlignedByteOffset: 20; InputSlotClass: 
D3D11_INPUT_PER_VERTEX_DATA; InstanceDataStepRate: 0 )
);

Vertices:array [0..NUMVERTICES-1] of TVERTEX =
(
  (Pos:(x:-1.0; y:-1.0; z:0); TexCoord:(x:0.0; y:1.0)),
  (Pos:(x:-1.0; y: 1.0; z:0); TexCoord:(x:0.0; y:0.0)),
  (Pos:(x: 1.0; y:-1.0; z:0); TexCoord:(x:1.0; y:1.0)),
  (Pos:(x: 1.0; y:-1.0; z:0); TexCoord:(x:1.0; y:1.0)),
  (Pos:(x:-1.0; y: 1.0; z:0); TexCoord:(x:0.0; y:0.0)),
  (Pos:(x: 1.0; y: 1.0; z:0); TexCoord:(x:1.0; y:0.0))
);

如你所见,我需要假人的例子/解释。我刚刚开始在GPU编程、DirectX渲染等方面进行实验。另一件事是,我正在尝试使用尽可能少的外部库。例如:我使用了SDL2,并尝试了低音库的音频,但后来我实现了播放音频(文件/流)使用WaveAudio在Windows和AudioTrack在安卓上,这似乎是完美的工作。

--编辑--

使用TPixelFormat.L创建TTexture没有意义,因为它没有被转换为DXGI格式。因此,目前我放弃了TFORM3D和TPlane的用法,比如在“03纺织”中。

临时解决方案如下(Windows DX11):

使用PXL库。

  1. 创建1 TTexture of PixelFormat L8,并将其转换为在PXL库中定义的DXGI_FORMAT_R8_UNORM。
  2. 将纹理的高度设置为图片的两倍。
  3. 将Y+U+V平面写入纹理。请参阅:纹理
  4. 使用上面(3)链接中的像素着色器对FEffectTexturedL进行PXL.Canvas.DX11声明

下面的任务是在使用不同的YUV类型(YUV 601,709,JPEG,NV12 601,.)之后,实现Android/OpenGL的相同功能。

这个临时解决方案看起来在windows上运行得很好,但在Android OpenGL上又太慢了,即使使用了与EDIT1相同的方法。Windows和android方法的唯一区别是,我必须复制两次数据,因为在Android上,我不能直接从线程中更新纹理数据,这在windows上是可能的。另一方面,两次复制在windows上并不是问题。

经过很多调整后,我正在考虑离开Delphi/FMX来创建一个视频播放器,至少对于移动设备是这样。

EN

回答 1

Stack Overflow用户

发布于 2022-04-08 22:18:18

我希望不要被投票赞成,但似乎没有一个好的/足够快的解决办法。

可以使用一些调整或库在FMX窗体或组件上呈现(视频) (YUV)图像。当图像大小大于FHD时,Android (平板电脑/手机)的速度太慢了。我不知道它在iOS或安卓电视上有多快/慢。

使用Exoplayer的TALVideoplayerSurface和其他方法一样慢/快。

在Android上播放外挂机视频时,用于Android的UHD示例已经足够快了。

目前看来,Android是创建视频播放器的最佳选择。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71453770

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档