博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用DirectDraw直接显示YUV视频数据 分类: windows...
阅读量:4948 次
发布时间:2019-06-11

本文共 3030 字,大约阅读时间需要 10 分钟。

最近在编写一个进行视频播放的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;
    }

    // Done 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占用率降低了一半。

版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://www.cnblogs.com/mao0504/p/4706847.html

你可能感兴趣的文章
电源防反接保护电路
查看>>
stm32 堆和栈(stm32 Heap & Stack)
查看>>
SpringMVC从入门到精通之第三章
查看>>
JS基础-dom操作
查看>>
【转】Android详细的对话框AlertDialog.Builder使用方法
查看>>
Unite Beijing 2015大型活动
查看>>
loading加载的代码
查看>>
PHP框架CI CodeIgniter 的log_message开启日志记录方法
查看>>
arraylist
查看>>
关于poi导出excel三种方式HSSFWorkbook,SXSSFWorkbook,csv的总结
查看>>
zoj 1649 Rescue (BFS)(转载)
查看>>
371. Sum of Two Integers java solutions
查看>>
2124: 等差子序列 - BZOJ
查看>>
3529: [Sdoi2014]数表 - BZOJ
查看>>
自我介绍
查看>>
字符串匹配算法综述
查看>>
Linux centosVMware shell 管道符和作业控制、shell变量、环境变量配置文件
查看>>
在程序被送入后台时,向 iOS 借点时间,来完成一个长期任务
查看>>
【设计模式】工厂模式
查看>>
两个表格中数据不用是一一对应关系--来筛选不同数据,或者相同数据
查看>>