几个很简单的问题 望各FW不吝赐教(100分)

  • 主题发起人 主题发起人 63226526yuan
  • 开始时间 开始时间
6

63226526yuan

Unregistered / Unconfirmed
GUEST, unregistred user!
1.在摄像头中,镜头获取的是不是一帧帧的图片
2.如果是自己进行视频解码(异地),解码后是一帧帧图片,可不可以通过显示图片的方式来达到进行视频监控的效果,会不会太慢?
 
刚好我现在做的是视频监控.
1.是的.是一帧帧图片.但有可能经过各种编码器编过码的.
2.我们现在就是这样处理的.给用户可选项.GDI或者DirectDraw画出图片.不会太慢.
GDI的好处是和设备无关.但是当图片较大,帧速率较高的时候比较吃CPU.特别是全屏的时候.
DirectDraw的方式就没有这些问题.非常快.缩放都是由显卡负责的.CPU占用率比较低.
 
传输差分图像,这样速度可以快些
 
wr960204,你能说的详细一点嘛
我是需要自己写解码程序,因为一般的解码控件可能无法达到我的要求
打算解码后通过图片显示的方式实现视频播放
 
还要怎么详细都告诉你画图的方式不会太慢.
 
我看了下 解码后的每一帧应该是YUV格式的
但是这样的图片DELPHI要怎样才能显示到界面上呢
疑惑中
 
如何使用DirectDraw直接显示YUV视频数据


最近在编写一个进行视频播放的ActiveX控件,工作已经接近尾声,现将其中显示YUV数据的使用DirectDraw的一些经验总结如下:(解码部分不是我编写的,我负责从网络接收数据,将数据传给解码器,并将解码得到的YUV数据进行显示,最初在显示部分我是先将YUV数据转换为RGB数据,再以位图的形式显示到屏幕上,但发现CPU占用率比较高,后来改用DirectDraw直接显示YUV数据)

1.在DirectDraw中创建YUV表面

与一般表面不同的是,创建YUV表面时需要指定象素格式,并指定YUV数据的FourCC码,关于FourCC码可以参考微软MSDN站点上的说明,下面是具体的创建方法:(以YUV4:2:0格式为例,其中drawwidth和drawheight是欲显示图像的宽度和高度,以象素为单位)

LPDIRECTDRAW7 lpDD;
// DirectDraw 对象指针
LPDIRECTDRAWSURFACE7 lpDDSPrimary;
// DirectDraw 主表面指针
LPDIRECTDRAWSURFACE7 lpDDSOffScr;
// DirectDraw 离屏表面指针
DDSURFACEDESC2 ddsd;
// DirectDraw 表面描述

// 创建DirectCraw对象
if (DirectDrawCreateEx(NULL, (VOID**)&lpDD, IID_IDirectDraw7, NULL) != DD_OK)
{
//MessageBox("Error Create DDraw.");
return FALSE;
}

// 设置协作层
if (lpDD->SetCooperativeLevel(hWnd,
DDSCL_NORMAL | DDSCL_NOWINDOWCHANGES) != DD_OK)
{
//MessageBox("Error Create Level.", s);
return FALSE;
}

// 创建主表面
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
if (lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL) != DD_OK)
{
//MessageBox("Error Create Primary Surface.", s);
return FALSE;
}

LPDIRECTDRAWCLIPPER pcClipper;
// Cliper
if( lpDD->CreateClipper( 0, &pcClipper, NULL ) != DD_OK )
return FALSE;

if( pcClipper->SetHWnd( 0, hWnd ) != DD_OK )
{
pcClipper->Release();
return FALSE;
}

if( lpDDSPrimary->SetClipper( pcClipper ) != DD_OK )
{
pcClipper->Release();
return FALSE;
}

//do
ne with clipper
pcClipper->Release();


// 创建YUV表面
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
ddsd.dwWidth = drawwidth;
ddsd.dwHeight = drawheight;
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC | DDPF_YUV ;
ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('Y','V', '1', '2');
ddsd.ddpfPixelFormat.dwYUVBitCount = 8;
if (lpDD->CreateSurface(&ddsd, &lpDDSOffScr, NULL) != DD_OK)
{
//MessageBox("Error Create Off Surface.", s);
return FALSE;
}

2.将解码得到的YUV数据拷贝到YUV表面

设解码得到的YUV数据的指针分别是Y,U,V, 每行数据长度为BPS,具体拷贝代码如下,这里需要特别注意每拷一行都要对写指针加ddsd.lPitch(对于Y)或ddsd.lPitch/2(对于UV):

LPBYTE lpSurf = (LPBYTE)ddsd.lpSurface;
LPBYTE PtrY = Y;
LPBYTE PtrU = U;
LPBYTE PtrV = V;

do
{
ddRval = lpDDSOffScr->Lock(NULL,&ddsd,DDLOCK_WAIT | DDLOCK_WRITEONLY,NULL);
} while(ddRval == DDERR_WASSTILLDRAWING);
if(ddRval != DD_OK)
return 1;

// 填充离屏表面
if(lpSurf)
{
for (int i=0;iHeight;i++)
{
memcpy(lpSurf, PtrY, ddsd.dwWidth);
PtrY += BpS;
lpSurf += ddsd.lPitch;
}

for (int i=0;iHeight/2;i++)
{
memcpy(lpSurf, PtrV, ddsd.dwWidth/2);
PtrV += BpS;

lpSurf += ddsd.lPitch/2;
}
for (int i=0;iHeight/2;i++)
{
memcpy(lpSurf, PtrU, ddsd.dwWidth/2);
PtrU += BpS;
lpSurf += ddsd.lPitch/2;
}

}

lpDDSOffScr->Unlock(NULL);

3.YUV表面的显示

现在就可以直接将YUV表面Blt到主表面或后备表面进行显示了:(设lpDDSBack为后备表面)

ddRval = lpDDSBack->Blt(NULL, lpDDSOffScr, NULL, DDBLT_WAIT, NULL);


这样就实现了YUV据的显示,对比发现使用DirectDraw直接进行YUV数据显示,CPU占用率降低了一半。
 
但是我想知道DELPHI下是怎样实现的啊
 
YUV数据有两种方式可以显示.
1.转换成RGB然后用GDI或者DirectDraw画出来.
2.直接创建DirectDraw的YUV表面.在上面画.我的实际经历是不是所有的显卡都支持创建YUV表面的.

方案1.你自己去找YUV和RGB的转换公式.我记得DFW上也有例子代码.你自己搜索一下.
方案2.给你一些我的代码片段.如果你有一点点DirectDraw的知识.这个很容易理解.
需要JEDI翻译的DirectX9的pas头文件.


var
lpclip : IDirectDrawClipper;
Ret : HRESULT;
begin


if m_lpDD = nil then

begin

if FScreen.HasGuid then

begin

if DirectDrawCreateEx(@FScreen.GUID, m_lpDD, IID_IDirectDraw7, nil) <> DD_OK then

Exit;
end
else

begin

if DirectDrawCreateEx(nil, m_lpDD, IID_IDirectDraw7, nil) <> DD_OK then

Exit;
end;

if m_lpDD.SetCooperativeLevel(Handle, DDSCL_NORMAL) <> DD_OK then

Exit;
//注意这里

//※主表面
ZeroMemory(@m_ddsd, sizeof(DDSURFACEDESC2));
m_ddsd.dwSize := sizeof(DDSURFACEDESC2);
m_ddsd.dwFlags := DDSD_CAPS;
m_ddsd.ddsCaps.dwCaps := DDSCAPS_PRIMARYSURFACE;

if m_lpDD.CreateSurface(m_ddsd, m_lpDDSPrimary, nil) <> DD_OK then

Exit;

if m_lpDD.CreateClipper(0, lpclip, nil) <> DD_OK then

Exit;
if m_lpDDSPrimary.SetClipper(lpclip) <> DD_OK then

Exit;
lpclip.SetHWnd(0, Handle);
//注意这里
lpclip := nil;
BackSurface();
end;

end;


//※次表面函数:

function TDDrawItem.BackSurface(): HRESULT;
var
Ret : HRESULT;
begin

ZeroMemory(@m_ddsd, sizeof(DDSURFACEDESC2));
m_ddsd.dwSize := sizeof(DDSURFACEDESC2);
{$IFDEF useformat}
m_ddsd.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_3DDEVICE;
// DDSCAPS_VIDEOMEMORY;
//DDSCAPS_OVERLAY DDSCAPS_OFFSCREENPLAIN;
m_ddsd.dwFlags := DDSD_CAPS or DDSD_HEIGHT or DDSD_WIDTH or DDSD_PIXELFORMAT;
m_ddsd.dwWidth := FFormatWidth;
m_ddsd.dwHeight := FFormatHeight;
m_ddsd.ddpfPixelFormat.dwSize := sizeof(DDPIXELFORMAT);
m_ddsd.ddpfPixelFormat.dwFlags := DDPF_FOURCC or DDPF_YUV;
m_ddsd.ddpfPixelFormat.dwFourCC := MAKEFOURCC('Y', 'V', '1', '2');
m_ddsd.ddpfPixelFormat.dwYUVBitCount := 8;
{$else
}
m_ddsd.dwFlags := DDSD_CAPS or DDSD_WIDTH or DDSD_HEIGHT;
m_ddsd.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN or DDSCAPS_VIDEOMEMORY or DDSCAPS_LOCALVIDMEM;
m_ddsd.dwWidth := Self.Width;
m_ddsd.dwHeight := Self.Height;
{$ENDIF}
m_lpDDSOffScr := nil;
Ret := m_lpDD.CreateSurface(m_ddsd, m_lpDDSOffScr, nil);
if Ret <> DD_OK then

begin

ShowMessage(Format('创建离屏表面失败"%x"!', [Ret]));
Exit;
end;

result := DD_OK;
end;


procedure TDDrawItem.FillYUV(lpYUV: PChar);
var
Rect, RectBack : TRect;
lpY, lpU, lpV, lpSurf: PChar;
I : integer;

value1, value2, value3, value4: Integer;
begin


if (not Self.HandleAllocated) or (m_lpDDSOffScr = nil) then

Exit;

GetWindowRect(Handle, Rect);

m_lpDDSOffScr.Lock(nil, m_ddsd, DDLOCK_WAIT or DDLOCK_WRITEONLY, 0);
//------------------------------
lpY := lpYUV;
lpU := lpYUV + FFormatUOffset;
lpV := lpYUV + FFormatVOffset;
lpSurf := m_ddsd.lpSurface;
//ZeroMemory(lpSurf, (FFormatWidth * FFormatHeight) * 3 div 2);
//-------------------填充YUV-----------------------------
{
Inc(lpSurf, FFormatWidth*Self.FYUVTop + FYUVLeft);
Inc(lpY, FFormatWidth*Self.FYUVTop);
for I := Self.FYUVTop to Self.FYUVHeight - 1do

begin

Inc(lpY, FYUVLeft);
CopyMemory(lpSurf, lpY, FYUVWidth);
Inc(lpY, FFormatWidth);
Inc(lpSurf, m_ddsd.lPitch);
end;


value1 := m_ddsd.dwHeight div 2;
value2 := m_ddsd.dwWidth div 2;
value3 := FFormatWidth div 2;
value4 := m_ddsd.lPitch div 2;

for i := 0 to value1 - 1do

begin

CopyMemory(lpSurf, lpU, value2);
Inc(lpU, value3);
Inc(lpSurf, value4);
end;


for i := 0 to value1 - 1do

begin

CopyMemory(lpSurf, lpV, value2);
Inc(lpV, value3);
Inc(lpSurf, value4);
end;

}

for I := 0 to 480 - 1do

begin

CopyMemory(lpSurf, lpY, 640);
Inc(lpY, 640);
Inc(lpSurf, m_ddsd.lPitch);
end;



value1 := m_ddsd.dwHeight div 2;
value2 := m_ddsd.dwWidth div 2;
value3 := FFormatWidth div 2;
value4 := m_ddsd.lPitch div 2;

for i := 0 to value1 - 1do

begin

CopyMemory(lpSurf, lpU, value2);
Inc(lpU, value3);
Inc(lpSurf, value4);
end;


for i := 0 to value1 - 1do

begin

CopyMemory(lpSurf, lpV, value2);
Inc(lpV, value3);
Inc(lpSurf, value4);
end;


//RectBack := Classes.Rect(0, 0, m_ddsd.dwWidth, m_ddsd.dwHeight);
RectBack := Classes.Rect(FYUVLeft, FYUVTop, FYUVWidth + FYUVLeft, FYUVHeight + FYUVTop);
OffsetRect(Rect, -FScreen.Rect.Left, -FScreen.Rect.Top);
//------------------------------
m_lpDDSOffScr.Unlock(nil);
m_lpDDSPrimary.Blt(@Rect, m_lpDDSOffScr, @RectBack, DDBLT_WAIT, nil);

end;
 
谢谢 我看了下,觉得将YUV转换成RGB显示可能更简单点吧
先试试看
 
据说转换成RGB再显示会慢很多,更耗内存??
 
晕了,解码后的一帧图片格式是怎样的?
是纯粹的YUV转换成RGB就可以直接用了吗?
RGB又要怎样写入图片显示?
希望能得到帮助 分可以加 只要我有
 
请问wr960204,解码的方式是自己实现的嘛
有没有现成的插件可用 h.264
 
后退
顶部