DirectShow对avi视频按帧设置慢放功能

转载请标明是引用于 http://blog.csdn.net/chenyujing1234

欢迎大家提出意见,一起讨论!

 DirectShow对avi视频按帧设置慢放功能需要视频源支持帧操作。

可以通过IMediaSeeking::IsFormatSupported(&TIME_FORMAT_FRAME);判断是否支持;

如果支持然后通过IMediaSeeking;:SetTimeFormat(&TIME_FORMAT_FRAME); 设置时间格式为帧格式。

 

一. 技术要点

1、Setting and Retrieving the Position

  Filter graph包含两个位置,当前位置和停止位置,定义如下:

  1) 当前位置,当一个graph正处于运行的时候,当前位置就是当前的回放位置,相对于开始的位置而言。如果graph处于停止或者暂停状态的时候,当前位置就是数据流下次开始播放的位置点。

  2) 停止位置,停止位置就是数据流将要停止的位置,当一个graph到达一个停止位置时,将没有数据流,filter graph管理器将会发送一个EC_COMPLETE事件。

  可以通过IMediaSeeking::GetPositions方法可以获取这些位置值。返回值都是相对于原始的开始位置。
通过IMediaSeeking::SetPositions方法可以seek一个新的位置,见下面:

#define ONE_SECOND 10000000
REFERENCE_TIME rtNow = 2 * ONE_SECOND,
rtStop = 5 * ONE_SECOND;
hr = pSeek->SetPositions(
&rtNow, AM_SEEKING_AbsolutePositioning,
&rtStop, AM_SEEKING_AbsolutePositioning
);

  注:1秒是10,000,000参考时间单位。为了方便,这个例子将这个值定义为ONE_SECOND,如果你使用的dshow的基类,常量CUITS的值和这个值相等。

 RtNow参数指定新的当前位置,第二个参数用来标示如何来定位rtNow参数。在这个例子中,AM_SEEKING_AbsolutePositioning 标志表示rtNow指定的位置是一个绝对的位置。RtStop参数指定了停止时间,最后一个参数也指定了绝对位置。

  如果想指定一个相对的位置,可以指定一个AM_SEEKING_RelativePositioning参数,为了设置这个位置不能改变,可以指定一个AM_SEEKING_NoPositioning参数。此时,参考时间应该设置为NULL。下面的例子将位置向前seek 10秒,然后停止位置不变。

REFERENCE_TIME rtNow = 10 * ONE_SECOND;
hr = pSeek->SetPositions(
 &rtNow, AM_SEEKING_RelativePositioning,
 NULL, AM_SEEKING_NoPositioning
);


   以上讲到的都是时间格式为TIME_FORMAT_NONE的情况,在时间格式为帧的情况下,上面设置得到的double都是指帧

 2、Setting the Playback Rate

  调用IMediaSeeking::SetRate方法可以改变回放的速率。通过将新的速率设置成原来速率的倍数就可以设置新的速率,例如,pSeek->SetRate(2.0),将新的速率设置为原来速率的两倍。比率大于1说明回放的速度比原来的大,如果介于0和1之间,就比正常的速度慢。

  如果我们不考虑回放速率,当前位置和停止位置相对于开始位置都是不变的。举个例子,如果我们有一个可以播放20秒的文件,将当前时间设置为10秒就会将播放位置设置到中间,如果播放的速率提高要原来的2倍,如果停止时间是20秒,你将播放位置设置到原来的10秒处,结果现在只能播放5秒了,因为速度提高了两倍。

 

3、Time Formats For Seek Commands

  IMediaSeeking接口中的许多函数的参数都要求指定一个位置值,比如当前位置,或者停止位置,缺省的情况下这些参数是以of 100 nanoseconds为时间单位的,称为参考时间,任何支持seek的filter必须支持按参考时间来进行定位。一些filter也支持采取其他时间单位进行定位。例如,根据指定的桢的数量,或在数据流偏移的字节数进行定位。

  这种用来定位的时间单位称为时间格式,采用一个GUID来标示。Directshow定义了一系列的时间格式,详细地可以参考SDK。第三方也可以定义自己的时间格式。

 为了确定graph中的当前的filter是否支持特定的时间格式,可以调用
IMediaSeeking::IsFormatSupported方法,如果filter支持该时间格式,该函数返回ok否则返回false或者一个错误码。如果filter支持某种指定的时间格式,可以调用IMediaSeeking::SetTimeFormat方法切换到其他的时间格式。如果SetTimeFormat方法成功,下面的seek命令就要使用新的时间格式。

  下面的代码检查graph是否支持用桢的数量进行定位,如果支持,定位到第20桢。

hr = pSeek->IsFormatSupported(&TIME_FORMAT_FRAME);
if (hr == S_OK)
{
 hr = pSeek->SetTimeFormat(&TIME_FORMAT_FRAME);
 if (SUCCEEDED(hr))
 {
  // Seek to frame number 20.
  LONGLONG rtNow = 20;
  hr = pSeek->SetPositions(&rtNow, AM_SEEKING_AbsolutePositioning,0, AM_SEEKING_NoPositioning);
 }
}



4、如何设置Graph时钟(Setting Graph Clock)

  当你构建了一个graph后,graph管理器会自动地给你的graph选择一个参考时钟的。Graph中的所有filter都同步于时钟。特别的,Renderer filter还要根据参考时钟的时间来决定每一个sample的Presentation 时间。

  通常的情况下,应用程序是没有必要重新设置graph管理器选择好的参考时钟的。但是,如果你想修改参考时钟,你可以通过graph管理器提供的IMediaFilter::SetSyncSource方法来重新设置参考时钟。这个方法的参数是一个时钟的IReferenceClock接口指针。可以在graph停止的时候调用这个函数,下面的例子演示了如何指定一个时钟

IGraphBuilder *pGraph = 0;
IReferenceClock *pClock = 0;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGraph);
// Build the graph.
pGraph->RenderFile(L"C:\Example.avi", 0);
// Create your clock.
hr = CreateMyPrivateClock(&pClock);
if (SUCCEEDED(hr))
{
 // Set the graph clock.
 IMediaFilter *pMediaFilter = 0;
 pGraph->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter);
 pMediaFilter->SetSyncSource(pClock);
 pClock->Release();
 pMediaFilter->Release();
}


  这段代码假定CreateMyPrivateClock 是应用程序定义的一个函数,用来创建一个时钟,然后返回一个IReferenceClock接口。

  你也可以在graph没有设置时钟的情况下运行graph。当SetSyncSource 函数的参数为NULL的时候就给graph设置了一个空的参考时钟。如果graph没有时钟,graph将运行的快许多。因为renderer 不用再按照sample的presentation 时间了,只要sample到达了renderer filter,就可以立即被提交。所以,当你想处理数据尽可能快,而不是还要考虑预览的实际时间,你就可以给graph设置一个空的时间。

 

二、实现过程

这里把实现类CDXGraph的h与cpp代码贴出来,如果需要加入界面的,请与我联系。

// CDXGraph.h: interface for the CDXGraph class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_CDXGRAPH_H__56139478_B3F8_45B1_BA74_BE016AF14C77__INCLUDED_)
#define AFX_CDXGRAPH_H__56139478_B3F8_45B1_BA74_BE016AF14C77__INCLUDED_

#if _MSC_VER > 10000
#pragma once
#endif // _MSC_VER > 1000

#define WM_GRAPHNOTIFY  (WM_USER+20)/////////////注意

class CDXGraph  
{
public:
	// 获得帧率
	int GetFPS(void);
	// 设置每秒播放的帧数
	BOOL SetRate(double iFrameps = 1.0);
	// 判断是否支持帧操作
	BOOL IsFormatFrame(void);
	BOOL GrabBitmap(const char *outFile);
	CDXGraph();
	virtual ~CDXGraph();

	virtual void Release(void);
	virtual bool Create(void);

	// 设置窗口的函数
	bool SetDisplayWindow(HWND inWindow);
	bool SetNotifyWindow(HWND inWindow);
	// 基本功能函数
	bool Run(void);
	bool Stop(void);
	bool Pause(void);
	bool IsRunning(void);
	bool IsStopped(void);
	bool IsPaused(void);
	// 设置全屏
	bool SetFullScreen(BOOL inEnabled);
	bool GetFullScreen(void);
	// 设置滑块
	bool GetCurrentPosition(double * outPosition);
	bool GetStopPosition(double * outPosition);
	bool SetCurrentPosition(double inPosition);
	bool GetDuration(double * outDuration);

	bool RenderFile(const char * inFile);
	bool SnapshotBitmap(const char * outFile);

	IBasicVideo *		mBasicVideo;
private:
	IVideoWindow  *		mVideoWindow;
	IMediaSeeking *		mSeeking;
	IMediaControl *		mMediaControl;
	IGraphBuilder *		mGraph;
	IMediaEventEx *		mEvent;

	IBasicAudio *		mBasicAudio;

	DWORD				mObjectTableEntry;

	void RemoveFromObjectTable(void);
	bool QueryInterfaces(void);
	void AddToObjectTable(void);
};

#endif // !defined(AFX_CDXGRAPH_H__56139478_B3F8_45B1_BA74_BE016AF14C77__INCLUDED_)


 

 

// CDXGraph.cpp: implementation of the CDXGraph class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
//#include "ppz.h"////////////////注意
#include <streams.h>//////////////注意
#include "CDXGraph.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

extern int i;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CDXGraph::CDXGraph()
{
 mGraph        = NULL;
 mMediaControl = NULL;
 mEvent        = NULL;
 mBasicVideo   = NULL;
 mBasicAudio   = NULL;
 mVideoWindow  = NULL;
 mSeeking      = NULL;
 

 mObjectTableEntry = 0;
}

CDXGraph::~CDXGraph()
{
 Release();
}

void CDXGraph::AddToObjectTable(void)
{
 IMoniker * pMoniker = 0;
    IRunningObjectTable * objectTable = 0;

    if (SUCCEEDED(GetRunningObjectTable(0, &objectTable))) 
 {
  WCHAR wsz[256];
  // 将数值按照指定的格式转化为字符串:
  wsprintfW(wsz, L"FilterGraph %08p pid %08x", (DWORD_PTR)mGraph, GetCurrentProcessId());
  HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
  if (SUCCEEDED(hr)) 
  {
   hr = objectTable->Register(0, mGraph, pMoniker, &mObjectTableEntry);
   pMoniker->Release();
  }
  objectTable->Release();
 }
}

bool CDXGraph::Create(void)
{ // 如果没有创建mGraph指针,创建一个,并初始化
 if (!mGraph)
 {
  if (SUCCEEDED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
   IID_IGraphBuilder, (void **)&mGraph))) // 创建未经初始化的mGraph
  {
   AddToObjectTable(); // 添加进去

   return QueryInterfaces();
  }
  mGraph = 0;
 }
 return false;
}

//得到当前的position(换成s)给*outPosition
bool CDXGraph::GetCurrentPosition(double *outPosition)
{
 if (mSeeking)//类IMediaSeeking
 {
  __int64 position = 0;
  if (SUCCEEDED(mSeeking->GetCurrentPosition(&position)))
  {
   GUID Format;
   if (TRUE == IsFormatFrame())
    *outPosition = position;
   else
    *outPosition = ((double)position) / 10000000.;
   return true;
  }
 }
 return false;
}
//得到持续时间Duration(换成s)给*outDuration
bool CDXGraph::GetDuration(double *outDuration)
{
 if (mSeeking)
 {
  __int64 length = 0;
  if (SUCCEEDED(mSeeking->GetDuration(&length)))
  {
   if (TRUE == IsFormatFrame())
    *outDuration = length;
   else
    *outDuration = ((double)length) / 10000000.;
   return true;
  }
 }
 return false;
}

bool CDXGraph::GetFullScreen(void)
{
 if (mVideoWindow)
 {
  long  fullScreenMode = OAFALSE;
  mVideoWindow->get_FullScreenMode(&fullScreenMode);
  return (fullScreenMode == OATRUE);
 }
 return false;
}

bool CDXGraph::GetStopPosition(double *outPosition)
{
 if (mSeeking)
 {
  __int64 position = 0;
  if (SUCCEEDED(mSeeking->GetStopPosition(&position)))
  {
   *outPosition = ((double)position) / 10000000.;
   return true;
  }
 }
 return false;
}

bool CDXGraph::IsPaused(void)
{
 if (mGraph && mMediaControl)
 {
  OAFilterState state = State_Stopped;
  if (SUCCEEDED(mMediaControl->GetState(10, &state)))
  {
   return state == State_Paused;
  }
 }
 return false;
}

bool CDXGraph::IsRunning(void)
{
 if (mGraph && mMediaControl)//检验这2个成员变量所指向的类是否为空
 {
  OAFilterState state = State_Stopped;//声明state为long并赋值(State_Stopped=0)
  if (SUCCEEDED(mMediaControl->GetState(10, &state)))
  {
   return state == State_Running;//State_Running=2,返回是否在State_Running状态
  }
 }
 return false;
}
/*
HRESULT GetState( //typedef LONG HRESULT;here is the result
  LONG msTimeout, //设置timeout时间,毫秒单位
  OAFilterState *pfs//获取state:State_Running,State_Stopped,State_Paused
);
//这个函数是用来得到filter graph的状态的,因为state转变频繁,调用函数和调用结束state可能有所变化
//所以,设置msTimeout,以保证所获得的state是单前的state
*/

bool CDXGraph::IsStopped(void)
{
 if (mGraph && mMediaControl)
 {
  OAFilterState state = State_Stopped;
  if (SUCCEEDED(mMediaControl->GetState(10, &state)))
  {
   return state == State_Stopped;//返回当前是否在e_Stopped状态
  }
 }
 return false;
}
//调用IMediaControl里的PAUSE()函数,方法与调用RUN()大致
bool CDXGraph::Pause(void)
{
 if (mGraph && mMediaControl)
 {
  if (!IsPaused())//检查是否暂停
  { 
   if (SUCCEEDED(mMediaControl->Pause()))//如果没有暂停,重新mMediaControl->Pause()并返回结果
   {
    return true;
   }
  }
  else
  {
   return true;
  }
 }
 return false;
}

bool CDXGraph::QueryInterfaces(void)
{
 if (mGraph)
 {
  HRESULT hr = NOERROR;
  

  hr |= mGraph->QueryInterface(IID_IMediaControl, (void **)&mMediaControl);
  hr |= mGraph->QueryInterface(IID_IMediaEventEx, (void **)&mEvent);
  hr |= mGraph->QueryInterface(IID_IBasicVideo, (void **)&mBasicVideo);
  hr |= mGraph->QueryInterface(IID_IBasicAudio, (void **)&mBasicAudio);
  hr |= mGraph->QueryInterface(IID_IVideoWindow, (void **)&mVideoWindow);
  hr |= mGraph->QueryInterface(IID_IMediaSeeking, (void **)&mSeeking);

  if (mSeeking)
  {
   mSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
  }
  return SUCCEEDED(hr);
 }
 return false;
}

void CDXGraph::Release(void)
{
 if (mSeeking)
 {
  mSeeking->Release();
  mSeeking = NULL;
 }
 if (mMediaControl)
 {
  mMediaControl->Release();
  mMediaControl = NULL;
 }
 if (mEvent)
 {
  mEvent->Release();
  mEvent = NULL;
 }
 if (mBasicVideo)
 {
  mBasicVideo->Release();
  mBasicVideo = NULL;
 }
 if (mBasicAudio)
 {
  mBasicAudio->Release();
  mBasicAudio = NULL;
 }
 if (mVideoWindow)
 {
  mVideoWindow->put_Visible(OAFALSE);
  mVideoWindow->put_MessageDrain((OAHWND)NULL);
  mVideoWindow->put_Owner(OAHWND(0));
  mVideoWindow->Release();
  mVideoWindow = NULL;
 }
 RemoveFromObjectTable();
 if (mGraph) 
 {
  mGraph->Release(); 
  mGraph = NULL;
 }
}

void CDXGraph::RemoveFromObjectTable(void)
{
 IRunningObjectTable * objectTable = 0;
    if (SUCCEEDED(GetRunningObjectTable(0, &objectTable))) 
 {
        objectTable->Revoke(mObjectTableEntry);
        objectTable->Release();
  mObjectTableEntry = 0;
    }
}

bool CDXGraph::RenderFile(const char *inFile)
{
 if (mGraph)//类IGraphBuilder
 {
  WCHAR    szFilePath[MAX_PATH];//wide characer(16-bit);MAX_PATH=260
  MultiByteToWideChar(CP_ACP, 0, inFile, -1, szFilePath, MAX_PATH);//字符转换mFilterGraph=>szFilePath
  if (SUCCEEDED(mGraph->RenderFile(szFilePath, NULL)))//szFilePath路径和文件名;建立一个filtergraph
  {
   return true;
  }
 }
 return false;
}

//run()和isrunning()组合,调用IMediaControl里的run()函数
bool CDXGraph::Run()
{
 HRESULT hr = NULL;
 if (mGraph && mMediaControl && mSeeking)//mGraph为IGraphBuilder型的指针;mMediaControl为IMediaControl型指针
 {
  if (!IsRunning())//检查是否在播放
  {
   if (SUCCEEDED(mMediaControl->Run()))//检验mMediaControl->Run()是否连接成功并调用
    //SUCCEEDED()检验是否非负(检查是否mMediaControl->Run()的原因造成没有播放)
   {
    
    hr = mSeeking->IsFormatSupported(&TIME_FORMAT_FRAME);
    if (SUCCEEDED(hr))
    {
     hr = mSeeking->SetTimeFormat(&TIME_FORMAT_FRAME);
     if (FAILED(hr))
     {
      printf("SetTimeFormat TIME_FORMAT_FRAME failed\n");
     }
    }
    else
     printf("IsFormatSupported TIME_FORMAT_FRAME failed\n");
    

    

//     if(FAILED(mSeeking->SetRate(0.01)))
//     {
//      printf("SetRate failed\r\n");
//     }
    return true;//mMediaControl->Run()正常,返回
   }
  }
  else
  {
   return true;//正在播放,返回
  }
 }
 return false;//类IGraphBuilder或IMediaControl为空类
}

bool CDXGraph::SetCurrentPosition(double inPosition)
{
 if (mSeeking)
 {
  __int64 one = 10000000;
  __int64 position = (TRUE == IsFormatFrame()) ? inPosition : (__int64)(one * inPosition);
  HRESULT hr = mSeeking->SetPositions(&position, AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame,
   0, AM_SEEKING_NoPositioning);
  return SUCCEEDED(hr);
 }
 return false;
}

bool CDXGraph::SetDisplayWindow(HWND inWindow)
{
 if (mVideoWindow)//类IVideoWindow
 {
  // 先隐藏video window
  mVideoWindow->put_Visible(OAFALSE);//隐藏video window
  mVideoWindow->put_Owner((OAHWND)inWindow);//为video window指定一个父窗口
  // 设置播放窗口和video window相同
  RECT windowRect;
  ::GetClientRect(inWindow, &windowRect); // 得到video window的数据(长宽等数据)
  mVideoWindow->put_Left(0);
  mVideoWindow->put_Top(0);
  mVideoWindow->put_Width(windowRect.right - windowRect.left);
  mVideoWindow->put_Height(windowRect.bottom - windowRect.top);
  mVideoWindow->put_WindowStyle(WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);

  mVideoWindow->put_MessageDrain((OAHWND) inWindow);//响应键盘和鼠标
  // Restore the video window
  if (inWindow != NULL)
  {
   mVideoWindow->put_Visible(OATRUE);//显示video window
   //mVideoWindow->put_Visible(OAFALSE);
  }
  else
  {
   mVideoWindow->put_Visible(OAFALSE);//隐藏video window
   //mVideoWindow->put_Visible(OATRUE);
  }
  return true;
 }
 return false;
}

bool CDXGraph::SetFullScreen(BOOL inEnabled)
{
 if (mVideoWindow) // 类IVideoWindow
 { // 设置全屏或者从全屏返回
  HRESULT hr = mVideoWindow->put_FullScreenMode(inEnabled ? OATRUE : OAFALSE);
  return SUCCEEDED(hr);
 }
 return false;
}

bool CDXGraph::SetNotifyWindow(HWND inWindow)
{
 if (mEvent)//类IMediaEventEx
 {
  mEvent->SetNotifyWindow((OAHWND)inWindow, WM_GRAPHNOTIFY, 0);
  return true;//WM_GRAPHNOTIFY:(WM_USER+20)
 }
 return false;
}
/*
IMediaEventEx::SetNotifyWindow(  
 OAHWND hwnd,  // 如果是NULL停止接受消息事件
 long lMsg,   // 窗口消息作为通知来传递
 long lInstanceData // 数值转化为lMsg,然后再作为通知来传递
)  //当窗口接收到消息会调用IMediaEvent::GetEvent

IMediaEvent::GetEvent(
 long *lEventCode, //(指针)接收事件代码
 long *lParam1,  //(指针)接收事件的第一个参数
 long *lParam2,  //(指针)接收事件的第二个参数
 long msTimeout  //设置timeout间隔
);

*/

bool CDXGraph::SnapshotBitmap(const char *outFile)
{
 if (mBasicVideo) // 类IBasicVideo
 {
  long bitmapSize = 0;
  if (SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, 0)))
  {
   bool pass = false;
   unsigned char * buffer = new unsigned char[bitmapSize];
   // 得到当前画面的大小bitmapSize和数据buffer
   if (SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, (long *)buffer)))
   {
    BITMAPFILEHEADER hdr;
    LPBITMAPINFOHEADER lpbi;

    lpbi = (LPBITMAPINFOHEADER)buffer;

    int nColors = 0;   //调色板中的颜色个数.(RGB格式在8位以下的,需要用调色板。
           //调色板实际上就是定义一些颜色的数组)
    if (lpbi->biBitCount <= 8) //biBitCount:每个像素的位数.PS:RGB32每个像素用32位表示,也就是4个字节
     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));
              
                CString FileName;//建立文件
    FileName.Format("Frame-%05d.bmp",i);
    //CString strtemp = strBmpDir;
    //strtemp += "\\";
    //strtemp += FileName;
    FILE* fp=_tfopen(FileName ,_T("wb"));
    fwrite(&hdr,1,sizeof(BITMAPFILEHEADER),fp);//写入文件头
    
    fwrite(lpbi,1,sizeof(BITMAPINFOHEADER),fp);//写入信息头
    
    int ff = fwrite(buffer,1,lpbi->biSizeImage,fp);
    int e = GetLastError();
    pass=true;
    fclose(fp);
   

    // szTemp[] = "C:\\mysnapshot.bmp"临时的.bmp文件(outFile指针所指)
   /* CFile bitmapFile(outFile, CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);
    bitmapFile.Write(&hdr, sizeof(BITMAPFILEHEADER)); // 设置.bmp格式
    bitmapFile.Write(buffer, bitmapSize); // 存入数据(通俗的说就是画图)
    bitmapFile.Close(); // Closes the bitmapFile and deletes the object
    pass = true;*/
   }
   delete [] buffer;
   return pass;
  }
 }
 return false;
}
/*
typedef struct tagBITMAPFILEHEADER {
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
*/

bool CDXGraph::Stop(void)
{
 if (mGraph && mMediaControl)
 {
  if (!IsStopped())
  { 
   if (SUCCEEDED(mMediaControl->Stop()))
   {
    return true;
   }
  }
  else
  {
   return true;
  }
 }
 return false;
}

BOOL CDXGraph::GrabBitmap(const char *outFile)
{
 if (mBasicVideo) // 类IBasicVideo
 {
  long bitmapSize = 0;
  if (SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, 0)))
  {
   bool pass = false;
   unsigned char * buffer = new unsigned char[bitmapSize];
   // 得到当前画面的大小bitmapSize和数据buffer
   if (SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, (long *)buffer)))
   {
    BITMAPFILEHEADER hdr;
    LPBITMAPINFOHEADER lpbi;

    lpbi = (LPBITMAPINFOHEADER)buffer;

    int nColors = 0;   //调色板中的颜色个数.(RGB格式在8位以下的,需要用调色板。
           //调色板实际上就是定义一些颜色的数组)
    if (lpbi->biBitCount <= 8) //biBitCount:每个像素的位数.PS:RGB32每个像素用32位表示,也就是4个字节
     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));

    // szTemp[] = "C:\\mysnapshot.bmp"临时的.bmp文件(outFile指针所指)
    CFile bitmapFile(outFile, CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);
    bitmapFile.Write(&hdr, sizeof(BITMAPFILEHEADER)); // 设置.bmp格式
    bitmapFile.Write(buffer, bitmapSize); // 存入数据(通俗的说就是画图)
    bitmapFile.Close(); // Closes the bitmapFile and deletes the object
    pass = true;
   }
   delete [] buffer;
   return pass;
  }
 }
 return false;

}


BOOL CDXGraph::IsFormatFrame(void)
{
 if (SUCCEEDED(mSeeking->IsFormatSupported(&TIME_FORMAT_FRAME)))
  return TRUE;
 else 
  return FALSE;
}


BOOL CDXGraph::SetRate(double iFrameps)
{
 if(FAILED(mSeeking->SetRate(iFrameps)))
 {
  printf("SetRate failed\r\n");
  return FALSE;
 }
 return TRUE;
}


int CDXGraph::GetFPS(void)
{
 int nCount = 0;
 int nSize = 0;
 HRESULT hr = NULL;
 int nFrame = 0;
 double dAvgTimePerFrame = 0;
 double dRate = 0;

 if (NULL == mBasicVideo || NULL == mSeeking) return 0;

 mBasicVideo->get_AvgTimePerFrame(&dAvgTimePerFrame);
 mSeeking->GetRate(&dRate);
 nFrame = (int)(1.0/dAvgTimePerFrame)*dRate;
//  hr = mSeeking->SetTimeFormat(&TIME_FORMAT_NONE);
//  if (FAILED(hr))
//  {
//   printf("SetTimeFormat TIME_FORMAT_NONE failed\n");
//  }
//  if (SUCCEEDED(mSeeking->GetDuration(&length)))
//  {
//   

 return nFrame;


}


你可能感兴趣的:(filter,video,null,Graph,buffer,avi)