我试图在FMX表单上渲染YUV图像。我一直在研究以下例子:https://github.com/grijjy/JustAddCode/tree/master/GpuProgramming
我已经设法呈现YUVNV12和YUV420图像(我用FFMPEG库从视频文件中解码),使用Delphi + Direct3D。在VCL的情况下,我可以在C/C++代码中找到足够的例子,可以翻译成Delphi (VCL)。
我尝试了以下方法来渲染(YUV)图像:
最终要实现的目标是渲染YUV图像,而不是通过FMX表单/组件上的CPU将其转换为RGB/BGR,这样它就可以在多个平台上使用。
在“03纺织”的例子中,我添加了两个纹理:一个用于Y平面,一个用于UV平面:
property TextureY : TTexture read FTextureY write SetTextureY;
property TextureUV: TTexture read FTextureUV write SetTextureUV;
我在过程“HandleImageChanged”中做了一些修改:
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
-我不知道这些是否是好的选择,只是为了尝试.
顶点着色器:
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:
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文件:
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调用的/任意相等项?
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库。
下面的任务是在使用不同的YUV类型(YUV 601,709,JPEG,NV12 601,.)之后,实现Android/OpenGL的相同功能。
这个临时解决方案看起来在windows上运行得很好,但在Android OpenGL上又太慢了,即使使用了与EDIT1相同的方法。Windows和android方法的唯一区别是,我必须复制两次数据,因为在Android上,我不能直接从线程中更新纹理数据,这在windows上是可能的。另一方面,两次复制在windows上并不是问题。
经过很多调整后,我正在考虑离开Delphi/FMX来创建一个视频播放器,至少对于移动设备是这样。
发布于 2022-04-08 22:18:18
我希望不要被投票赞成,但似乎没有一个好的/足够快的解决办法。
可以使用一些调整或库在FMX窗体或组件上呈现(视频) (YUV)图像。当图像大小大于FHD时,Android (平板电脑/手机)的速度太慢了。我不知道它在iOS或安卓电视上有多快/慢。
使用Exoplayer的TALVideoplayerSurface和其他方法一样慢/快。
在Android上播放外挂机视频时,用于Android的UHD示例已经足够快了。
目前看来,Android是创建视频播放器的最佳选择。
https://stackoverflow.com/questions/71453770
复制相似问题