DirectShow的截图方案简述

本文介绍了使用DirectShow进行视频截图的三种方法:通过IMediaDet接口的GetBitmaps方法快速截图,利用IBasicVideo接口的GetCurrentImage方法兼容更多视频格式,以及采用IVMRWindowlessControl9接口实现高效且全面兼容的截图方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://blog.youkuaiyun.com/Macapplelove/archive/2010/10/23/5961173.aspx

 

DirectShow的截图方案简述
用directshow可以有多种实现的方案,下面介绍三种:

第一种采用IMediaDet接口的GetBitmaps方法,速度很快,但是Video的类型有限制,对于WMF类型、RMVB等都无法正常截图,下面是例子代码:
BOOL Grabber1()
{
if(!m_pMediaDet) // IMediaDet接口,它除了用于截图还可以用于判断媒体类型
   return FALSE;
BOOL bResult = FALSE;
HRESULT thr;
long width = 0, height = 0;
AM_MEDIA_TYPE mt;
m_pMediaDet->put_Filename(m_strMediaFile.AllocSysString());
thr = m_pMediaDet->get_StreamMediaType(&mt);

if (1/*mt.formattype != FORMAT_VideoInfo*/) //在此有类型限制
{
   VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
   width = pVih->bmiHeader.biWidth;
   height = pVih->bmiHeader.biHeight;
   if (height < 0)
    height *= -1;
}
else
{
   return VFW_E_INVALIDMEDIATYPE;     
     }
FreeMediaType(mt);

long size;
char * pBuffer;
int gt; // grabber time
gt = m_curpos * m_Duration / m_filelength;
thr = m_pMediaDet->GetBitmapBits(gt,&size,NULL,width,height);
if(SUCCEEDED(thr))
{
   pBuffer = new char[size];
   if(!pBuffer)
    return E_OUTOFMEMORY;
}
try
{
   thr = m_pMediaDet->GetBitmapBits(gt,0,pBuffer,width,height);
}
catch(...)
{
   delete [] pBuffer;
   throw;
}
if(SUCCEEDED(thr))
{
   BITMAPINFOHEADER * bmih = (BITMAPINFOHEADER*)pBuffer;
   HDC hdcDest = ::GetDC(0);
   void * pData = pBuffer + sizeof(BITMAPINFOHEADER);
   // Note: In general a BITMAPINFOHEADER can include extra color
   // information at the end, so calculating the offset to the image
   // data is not generally correct. However, the IMediaDet interface
   // always returns an RGB-24 image with no extra color information
   BITMAPINFO bmi;
   ZeroMemory(&bmi, sizeof(BITMAPINFO));
   CopyMemory(&(bmi.bmiHeader),bmih,sizeof(BITMAPINFOHEADER));
   HBITMAP hB = CreateDIBitmap(hdcDest,
    bmih,
    CBM_INIT,
    pData,
    &bmi,
    DIB_RGB_COLORS);
   // write to bitmap
    BITMAPFILEHEADER   hdr;     //Bitmap的头信息
    LPBITMAPINFOHEADER    lpbi; // Bitmap的文件信息(包括数据)
    lpbi = (LPBITMAPINFOHEADER)pBuffer;
    int nColors = 1 << lpbi->biBitCount;
    if (nColors > 256)
     nColors = 0;
    hdr.bfType     = ((WORD) ('M' << 8) | 'B');     //always is "BM"
    hdr.bfSize     = size + sizeof( hdr );
    hdr.bfReserved1    = 0;
    hdr.bfReserved2    = 0;
    hdr.bfOffBits = (DWORD) (sizeof(BITMAPFILEHEADER) + lpbi->biSize);

// 保存图 
CString bmpPath(m_grbDirectory);
    bmpPath += "\\temp.bmp";
    CFile bitmapFile(bmpPath, CFile::modeNoTruncate|
    CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);
    bitmapFile.Write(&hdr, sizeof(BITMAPFILEHEADER));
    bitmapFile.Write(pBuffer, size);
    bitmapFile.Close();
    bResult = TRUE;
}
bResult = FALSE;
delete [] pBuffer;
return bResult;
}

第二种方案采用的是IBasicVideo中的GetCurrentImage方法,这种方案几乎可以处理所有的视频类型,但是每一次截图都有一个短暂的暂停,也是就停顿,下面是例子:

BOOL Grabber2()
{
if(m_pGraph == NULL)
    return FALSE;
else
    m_pGraph->QueryInterface(IID_IBasicVideo,(void **)&m_pBasicVideo);

if(m_pBasicVideo == NULL)
    return FALSE;
long bitmapSize = 0;
BOOL bResult = FALSE;
if (SUCCEEDED(m_pBasicVideo->GetCurrentImage(&bitmapSize, 0)))
{
    unsigned char * buffer = new unsigned char[bitmapSize];
    if (SUCCEEDED(m_pBasicVideo->GetCurrentImage(&bitmapSize, (long *)buffer)))
    {
     BITMAPFILEHEADER hdr;
     LPBITMAPINFOHEADER lpbi;
     lpbi = (LPBITMAPINFOHEADER)buffer;
     int nColors = 0;
     if( lpbi->biBitCount <=8)
     nColors = 1 << lpbi->biBitCount;
     hdr.bfType = ((WORD) ('M' << 8) | 'B'); //always is "BM"
     hdr.bfSize = bitmapSize + sizeof( hdr );
     hdr.bfReserved1 = 0;
     hdr.bfReserved2 = 0;
     hdr.bfOffBits = (DWORD) (sizeof(BITMAPFILEHEADER) + lpbi->biSize +
     nColors * sizeof(RGBQUAD));
 
     // write to file 
     CString bmpPath(m_grbDirectory);
     bmpPath += "\\";// to save something
     bmpPath += RandString(16);
     bmpPath += ".bmp";
     CFile bitmapFile(bmpPath, CFile::modeNoTruncate|
     CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);
     bitmapFile.Write(&hdr, sizeof(BITMAPFILEHEADER));
     bitmapFile.Write(buffer,bitmapSize);
     bitmapFile.Close();
     bResult = TRUE;
    // MessageBox(bmpPath,"成功保存提示");
     CRect rcTmp;
     GetWindowRect(&rcTmp);
     m_windowmove = -m_windowmove;
     rcTmp.OffsetRect(CPoint(0,m_windowmove));
     MoveWindow(rcTmp);
    }
    delete [] buffer;
    bResult = FALSE;
}
if(m_pBasicVideo != NULL)
{
    m_pBasicVideo->Release();
    m_pBasicVideo = NULL;
}

return bResult;
}


为了寻求既快而且能处理所有类型的方案,找到了IVMRWindowlessControl9接口。它是VRM9中才有的,所以必须引入d3d9.h和vrm9.h头文件。

但是在之前用的IMediaDet接口中包含了Qedit.h,而这个文件和上述两个头文件是不相容的。所以如果同在一个.h中#include他们会出现类似这样的报错:
syntax      error      :      identifier      'IDirect3DSurface9'  
所以,我尝试着把qedit.h放置到.cpp中去,错误虽然少了,但变成了这些:
error C2061: syntax error : identifier 'LPDIRECT3D'
error C2061: syntax error : identifier 'LPDIRECT3DDEVICE'
error C2061: syntax error : identifier 'LPDIRECT3DDEVICE2'

为了解决问题,可以把qedit.h去掉,但是那样就没法用IMediaDet接口了。所以,必须建立一个小点的头文件,把包含ImediaDet那部分copy进去,然后使用它就可以了,我把用到的置于了mediadet.h中,主要包含了SampleGrabberCB和IMediaDet以及CLSID的定义。这样问题就解决了。

例子代码如下:

      if(m_pVMRWC && m_fStyle == VIDEO_STYLE)
      {
          BYTE* lpCurrImage = NULL;

          // Read the current video frame into a byte buffer.    The information
          // will be returned in a packed Windows DIB and will be allocated
          // by the VMR.
          if(m_pVMRWC->GetCurrentImage(&lpCurrImage) == S_OK)
          {
              BITMAPFILEHEADER      hdr;
              DWORD                 dwSize, dwWritten;
              LPBITMAPINFOHEADER    pdib = (LPBITMAPINFOHEADER) lpCurrImage;

              // Create a new file to store the bitmap data 
     CString bmpPath(m_grbDirectory);
     bmpPath += "\\";// to save something
     bmpPath += RandString(16);
     bmpPath += ".bmp";
              HANDLE hFile = CreateFile(TEXT(bmpPath), GENERIC_WRITE, FILE_SHARE_READ, NULL,
                                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

              if (hFile == INVALID_HANDLE_VALUE)
                  return FALSE;

              // Initialize the bitmap header
              dwSize = DibSize(pdib);
              hdr.bfType            = BFT_BITMAP;
              hdr.bfSize            = dwSize + sizeof(BITMAPFILEHEADER);
              hdr.bfReserved1       = 0;
              hdr.bfReserved2       = 0;
              hdr.bfOffBits         = (DWORD)sizeof(BITMAPFILEHEADER) + pdib->biSize +
                  DibPaletteSize(pdib);

              // Write the bitmap header and bitmap bits to the file
              WriteFile(hFile, (LPCVOID) &hdr, sizeof(BITMAPFILEHEADER), &dwWritten, 0);
              WriteFile(hFile, (LPCVOID) pdib, dwSize, &dwWritten, 0);

              // Close the file
              CloseHandle(hFile);

              // The app must free the image data returned from GetCurrentImage()
              CoTaskMemFree(lpCurrImage);

              // Give user feedback that the write has completed
              // MessageBox(bmpPath);
              return TRUE;
          }
      }
      return FALSE;

 

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/Macapplelove/archive/2010/10/23/5961173.aspx

转载于:https://www.cnblogs.com/carl2380/archive/2011/01/13/1934372.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值