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

Post date: 2015/5/5 上午 06:26:32

转载请标明是引用于 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代码贴出来,如果需要加入界面的,请与我联系。

[cpp] view plaincopy

  1. // CDXGraph.h: interface for the CDXGraph class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #if !defined(AFX_CDXGRAPH_H__56139478_B3F8_45B1_BA74_BE016AF14C77__INCLUDED_)
  5. #define AFX_CDXGRAPH_H__56139478_B3F8_45B1_BA74_BE016AF14C77__INCLUDED_
  6. #if _MSC_VER > 10000
  7. #pragma once
  8. #endif // _MSC_VER > 1000
  9. #define WM_GRAPHNOTIFY (WM_USER+20)/////////////注意
  10. class CDXGraph
  11. {
  12. public:
  13. // 获得帧率
  14. int GetFPS(void);
  15. // 设置每秒播放的帧数
  16. BOOL SetRate(double iFrameps = 1.0);
  17. // 判断是否支持帧操作
  18. BOOL IsFormatFrame(void);
  19. BOOL GrabBitmap(const char *outFile);
  20. CDXGraph();
  21. virtual ~CDXGraph();
  22. virtual void Release(void);
  23. virtual bool Create(void);
  24. // 设置窗口的函数
  25. bool SetDisplayWindow(HWND inWindow);
  26. bool SetNotifyWindow(HWND inWindow);
  27. // 基本功能函数
  28. bool Run(void);
  29. bool Stop(void);
  30. bool Pause(void);
  31. bool IsRunning(void);
  32. bool IsStopped(void);
  33. bool IsPaused(void);
  34. // 设置全屏
  35. bool SetFullScreen(BOOL inEnabled);
  36. bool GetFullScreen(void);
  37. // 设置滑块
  38. bool GetCurrentPosition(double * outPosition);
  39. bool GetStopPosition(double * outPosition);
  40. bool SetCurrentPosition(double inPosition);
  41. bool GetDuration(double * outDuration);
  42. bool RenderFile(const char * inFile);
  43. bool SnapshotBitmap(const char * outFile);
  44. IBasicVideo * mBasicVideo;
  45. private:
  46. IVideoWindow * mVideoWindow;
  47. IMediaSeeking * mSeeking;
  48. IMediaControl * mMediaControl;
  49. IGraphBuilder * mGraph;
  50. IMediaEventEx * mEvent;
  51. IBasicAudio * mBasicAudio;
  52. DWORD mObjectTableEntry;
  53. void RemoveFromObjectTable(void);
  54. bool QueryInterfaces(void);
  55. void AddToObjectTable(void);
  56. };
  57. #endif // !defined(AFX_CDXGRAPH_H__56139478_B3F8_45B1_BA74_BE016AF14C77__INCLUDED_)

[cpp] view plaincopy

  1. // CDXGraph.cpp: implementation of the CDXGraph class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "stdafx.h"
  5. //#include "ppz.h"////////////////注意
  6. #include <streams.h>//////////////注意
  7. #include "CDXGraph.h"
  8. #ifdef _DEBUG
  9. #undef THIS_FILE
  10. static char THIS_FILE[]=__FILE__;
  11. #define new DEBUG_NEW
  12. #endif
  13. extern int i;
  14. //////////////////////////////////////////////////////////////////////
  15. // Construction/Destruction
  16. //////////////////////////////////////////////////////////////////////
  17. CDXGraph::CDXGraph()
  18. {
  19. mGraph = NULL;
  20. mMediaControl = NULL;
  21. mEvent = NULL;
  22. mBasicVideo = NULL;
  23. mBasicAudio = NULL;
  24. mVideoWindow = NULL;
  25. mSeeking = NULL;
  26. mObjectTableEntry = 0;
  27. }
  28. CDXGraph::~CDXGraph()
  29. {
  30. Release();
  31. }
  32. void CDXGraph::AddToObjectTable(void)
  33. {
  34. IMoniker * pMoniker = 0;
  35. IRunningObjectTable * objectTable = 0;
  36. if (SUCCEEDED(GetRunningObjectTable(0, &objectTable)))
  37. {
  38. WCHAR wsz[256];
  39. // 将数值按照指定的格式转化为字符串:
  40. wsprintfW(wsz, L"FilterGraph %08p pid %08x", (DWORD_PTR)mGraph, GetCurrentProcessId());
  41. HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
  42. if (SUCCEEDED(hr))
  43. {
  44. hr = objectTable->Register(0, mGraph, pMoniker, &mObjectTableEntry);
  45. pMoniker->Release();
  46. }
  47. objectTable->Release();
  48. }
  49. }
  50. bool CDXGraph::Create(void)
  51. { // 如果没有创建mGraph指针,创建一个,并初始化
  52. if (!mGraph)
  53. {
  54. if (SUCCEEDED(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
  55. IID_IGraphBuilder, (void **)&mGraph))) // 创建未经初始化的mGraph
  56. {
  57. AddToObjectTable(); // 添加进去
  58. return QueryInterfaces();
  59. }
  60. mGraph = 0;
  61. }
  62. return false;
  63. }
  64. //得到当前的position(换成s)给*outPosition
  65. bool CDXGraph::GetCurrentPosition(double *outPosition)
  66. {
  67. if (mSeeking)//类IMediaSeeking
  68. {
  69. __int64 position = 0;
  70. if (SUCCEEDED(mSeeking->GetCurrentPosition(&position)))
  71. {
  72. GUID Format;
  73. if (TRUE == IsFormatFrame())
  74. *outPosition = position;
  75. else
  76. *outPosition = ((double)position) / 10000000.;
  77. return true;
  78. }
  79. }
  80. return false;
  81. }
  82. //得到持续时间Duration(换成s)给*outDuration
  83. bool CDXGraph::GetDuration(double *outDuration)
  84. {
  85. if (mSeeking)
  86. {
  87. __int64 length = 0;
  88. if (SUCCEEDED(mSeeking->GetDuration(&length)))
  89. {
  90. if (TRUE == IsFormatFrame())
  91. *outDuration = length;
  92. else
  93. *outDuration = ((double)length) / 10000000.;
  94. return true;
  95. }
  96. }
  97. return false;
  98. }
  99. bool CDXGraph::GetFullScreen(void)
  100. {
  101. if (mVideoWindow)
  102. {
  103. long fullScreenMode = OAFALSE;
  104. mVideoWindow->get_FullScreenMode(&fullScreenMode);
  105. return (fullScreenMode == OATRUE);
  106. }
  107. return false;
  108. }
  109. bool CDXGraph::GetStopPosition(double *outPosition)
  110. {
  111. if (mSeeking)
  112. {
  113. __int64 position = 0;
  114. if (SUCCEEDED(mSeeking->GetStopPosition(&position)))
  115. {
  116. *outPosition = ((double)position) / 10000000.;
  117. return true;
  118. }
  119. }
  120. return false;
  121. }
  122. bool CDXGraph::IsPaused(void)
  123. {
  124. if (mGraph && mMediaControl)
  125. {
  126. OAFilterState state = State_Stopped;
  127. if (SUCCEEDED(mMediaControl->GetState(10, &state)))
  128. {
  129. return state == State_Paused;
  130. }
  131. }
  132. return false;
  133. }
  134. bool CDXGraph::IsRunning(void)
  135. {
  136. if (mGraph && mMediaControl)//检验这2个成员变量所指向的类是否为空
  137. {
  138. OAFilterState state = State_Stopped;//声明state为long并赋值(State_Stopped=0)
  139. if (SUCCEEDED(mMediaControl->GetState(10, &state)))
  140. {
  141. return state == State_Running;//State_Running=2,返回是否在State_Running状态
  142. }
  143. }
  144. return false;
  145. }
  146. /*
  147. HRESULT GetState( //typedef LONG HRESULT;here is the result
  148. LONG msTimeout, //设置timeout时间,毫秒单位
  149. OAFilterState *pfs//获取state:State_Running,State_Stopped,State_Paused
  150. );
  151. //这个函数是用来得到filter graph的状态的,因为state转变频繁,调用函数和调用结束state可能有所变化
  152. //所以,设置msTimeout,以保证所获得的state是单前的state
  153. */
  154. bool CDXGraph::IsStopped(void)
  155. {
  156. if (mGraph && mMediaControl)
  157. {
  158. OAFilterState state = State_Stopped;
  159. if (SUCCEEDED(mMediaControl->GetState(10, &state)))
  160. {
  161. return state == State_Stopped;//返回当前是否在e_Stopped状态
  162. }
  163. }
  164. return false;
  165. }
  166. //调用IMediaControl里的PAUSE()函数,方法与调用RUN()大致
  167. bool CDXGraph::Pause(void)
  168. {
  169. if (mGraph && mMediaControl)
  170. {
  171. if (!IsPaused())//检查是否暂停
  172. {
  173. if (SUCCEEDED(mMediaControl->Pause()))//如果没有暂停,重新mMediaControl->Pause()并返回结果
  174. {
  175. return true;
  176. }
  177. }
  178. else
  179. {
  180. return true;
  181. }
  182. }
  183. return false;
  184. }
  185. bool CDXGraph::QueryInterfaces(void)
  186. {
  187. if (mGraph)
  188. {
  189. HRESULT hr = NOERROR;
  190. hr |= mGraph->QueryInterface(IID_IMediaControl, (void **)&mMediaControl);
  191. hr |= mGraph->QueryInterface(IID_IMediaEventEx, (void **)&mEvent);
  192. hr |= mGraph->QueryInterface(IID_IBasicVideo, (void **)&mBasicVideo);
  193. hr |= mGraph->QueryInterface(IID_IBasicAudio, (void **)&mBasicAudio);
  194. hr |= mGraph->QueryInterface(IID_IVideoWindow, (void **)&mVideoWindow);
  195. hr |= mGraph->QueryInterface(IID_IMediaSeeking, (void **)&mSeeking);
  196. if (mSeeking)
  197. {
  198. mSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME);
  199. }
  200. return SUCCEEDED(hr);
  201. }
  202. return false;
  203. }
  204. void CDXGraph::Release(void)
  205. {
  206. if (mSeeking)
  207. {
  208. mSeeking->Release();
  209. mSeeking = NULL;
  210. }
  211. if (mMediaControl)
  212. {
  213. mMediaControl->Release();
  214. mMediaControl = NULL;
  215. }
  216. if (mEvent)
  217. {
  218. mEvent->Release();
  219. mEvent = NULL;
  220. }
  221. if (mBasicVideo)
  222. {
  223. mBasicVideo->Release();
  224. mBasicVideo = NULL;
  225. }
  226. if (mBasicAudio)
  227. {
  228. mBasicAudio->Release();
  229. mBasicAudio = NULL;
  230. }
  231. if (mVideoWindow)
  232. {
  233. mVideoWindow->put_Visible(OAFALSE);
  234. mVideoWindow->put_MessageDrain((OAHWND)NULL);
  235. mVideoWindow->put_Owner(OAHWND(0));
  236. mVideoWindow->Release();
  237. mVideoWindow = NULL;
  238. }
  239. RemoveFromObjectTable();
  240. if (mGraph)
  241. {
  242. mGraph->Release();
  243. mGraph = NULL;
  244. }
  245. }
  246. void CDXGraph::RemoveFromObjectTable(void)
  247. {
  248. IRunningObjectTable * objectTable = 0;
  249. if (SUCCEEDED(GetRunningObjectTable(0, &objectTable)))
  250. {
  251. objectTable->Revoke(mObjectTableEntry);
  252. objectTable->Release();
  253. mObjectTableEntry = 0;
  254. }
  255. }
  256. bool CDXGraph::RenderFile(const char *inFile)
  257. {
  258. if (mGraph)//类IGraphBuilder
  259. {
  260. WCHAR szFilePath[MAX_PATH];//wide characer(16-bit);MAX_PATH=260
  261. MultiByteToWideChar(CP_ACP, 0, inFile, -1, szFilePath, MAX_PATH);//字符转换mFilterGraph=>szFilePath
  262. if (SUCCEEDED(mGraph->RenderFile(szFilePath, NULL)))//szFilePath路径和文件名;建立一个filtergraph
  263. {
  264. return true;
  265. }
  266. }
  267. return false;
  268. }
  269. //run()和isrunning()组合,调用IMediaControl里的run()函数
  270. bool CDXGraph::Run()
  271. {
  272. HRESULT hr = NULL;
  273. if (mGraph && mMediaControl && mSeeking)//mGraph为IGraphBuilder型的指针;mMediaControl为IMediaControl型指针
  274. {
  275. if (!IsRunning())//检查是否在播放
  276. {
  277. if (SUCCEEDED(mMediaControl->Run()))//检验mMediaControl->Run()是否连接成功并调用
  278. //SUCCEEDED()检验是否非负(检查是否mMediaControl->Run()的原因造成没有播放)
  279. {
  280. hr = mSeeking->IsFormatSupported(&TIME_FORMAT_FRAME);
  281. if (SUCCEEDED(hr))
  282. {
  283. hr = mSeeking->SetTimeFormat(&TIME_FORMAT_FRAME);
  284. if (FAILED(hr))
  285. {
  286. printf("SetTimeFormat TIME_FORMAT_FRAME failed\n");
  287. }
  288. }
  289. else
  290. printf("IsFormatSupported TIME_FORMAT_FRAME failed\n");
  291. // if(FAILED(mSeeking->SetRate(0.01)))
  292. // {
  293. // printf("SetRate failed\r\n");
  294. // }
  295. return true;//mMediaControl->Run()正常,返回
  296. }
  297. }
  298. else
  299. {
  300. return true;//正在播放,返回
  301. }
  302. }
  303. return false;//类IGraphBuilder或IMediaControl为空类
  304. }
  305. bool CDXGraph::SetCurrentPosition(double inPosition)
  306. {
  307. if (mSeeking)
  308. {
  309. __int64 one = 10000000;
  310. __int64 position = (TRUE == IsFormatFrame()) ? inPosition : (__int64)(one * inPosition);
  311. HRESULT hr = mSeeking->SetPositions(&position, AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame,
  312. 0, AM_SEEKING_NoPositioning);
  313. return SUCCEEDED(hr);
  314. }
  315. return false;
  316. }
  317. bool CDXGraph::SetDisplayWindow(HWND inWindow)
  318. {
  319. if (mVideoWindow)//类IVideoWindow
  320. {
  321. // 先隐藏video window
  322. mVideoWindow->put_Visible(OAFALSE);//隐藏video window
  323. mVideoWindow->put_Owner((OAHWND)inWindow);//为video window指定一个父窗口
  324. // 设置播放窗口和video window相同
  325. RECT windowRect;
  326. ::GetClientRect(inWindow, &windowRect); // 得到video window的数据(长宽等数据)
  327. mVideoWindow->put_Left(0);
  328. mVideoWindow->put_Top(0);
  329. mVideoWindow->put_Width(windowRect.right - windowRect.left);
  330. mVideoWindow->put_Height(windowRect.bottom - windowRect.top);
  331. mVideoWindow->put_WindowStyle(WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
  332. mVideoWindow->put_MessageDrain((OAHWND) inWindow);//响应键盘和鼠标
  333. // Restore the video window
  334. if (inWindow != NULL)
  335. {
  336. mVideoWindow->put_Visible(OATRUE);//显示video window
  337. //mVideoWindow->put_Visible(OAFALSE);
  338. }
  339. else
  340. {
  341. mVideoWindow->put_Visible(OAFALSE);//隐藏video window
  342. //mVideoWindow->put_Visible(OATRUE);
  343. }
  344. return true;
  345. }
  346. return false;
  347. }
  348. bool CDXGraph::SetFullScreen(BOOL inEnabled)
  349. {
  350. if (mVideoWindow) // 类IVideoWindow
  351. { // 设置全屏或者从全屏返回
  352. HRESULT hr = mVideoWindow->put_FullScreenMode(inEnabled ? OATRUE : OAFALSE);
  353. return SUCCEEDED(hr);
  354. }
  355. return false;
  356. }
  357. bool CDXGraph::SetNotifyWindow(HWND inWindow)
  358. {
  359. if (mEvent)//类IMediaEventEx
  360. {
  361. mEvent->SetNotifyWindow((OAHWND)inWindow, WM_GRAPHNOTIFY, 0);
  362. return true;//WM_GRAPHNOTIFY:(WM_USER+20)
  363. }
  364. return false;
  365. }
  366. /*
  367. IMediaEventEx::SetNotifyWindow(
  368. OAHWND hwnd, // 如果是NULL停止接受消息事件
  369. long lMsg, // 窗口消息作为通知来传递
  370. long lInstanceData // 数值转化为lMsg,然后再作为通知来传递
  371. ) //当窗口接收到消息会调用IMediaEvent::GetEvent
  372. IMediaEvent::GetEvent(
  373. long *lEventCode, //(指针)接收事件代码
  374. long *lParam1, //(指针)接收事件的第一个参数
  375. long *lParam2, //(指针)接收事件的第二个参数
  376. long msTimeout //设置timeout间隔
  377. );
  378. */
  379. bool CDXGraph::SnapshotBitmap(const char *outFile)
  380. {
  381. if (mBasicVideo) // 类IBasicVideo
  382. {
  383. long bitmapSize = 0;
  384. if (SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, 0)))
  385. {
  386. bool pass = false;
  387. unsigned char * buffer = new unsigned char[bitmapSize];
  388. // 得到当前画面的大小bitmapSize和数据buffer
  389. if (SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, (long *)buffer)))
  390. {
  391. BITMAPFILEHEADER hdr;
  392. LPBITMAPINFOHEADER lpbi;
  393. lpbi = (LPBITMAPINFOHEADER)buffer;
  394. int nColors = 0; //调色板中的颜色个数.(RGB格式在8位以下的,需要用调色板。
  395. //调色板实际上就是定义一些颜色的数组)
  396. if (lpbi->biBitCount <= 8) //biBitCount:每个像素的位数.PS:RGB32每个像素用32位表示,也就是4个字节
  397. nColors = 1 << lpbi->biBitCount;
  398. hdr.bfType = ((WORD) ('M' << 8) | 'B'); //always is "BM"
  399. hdr.bfSize = bitmapSize + sizeof( hdr );
  400. hdr.bfReserved1 = 0;
  401. hdr.bfReserved2 = 0;
  402. hdr.bfOffBits = (DWORD) (sizeof(BITMAPFILEHEADER) + lpbi->biSize +
  403. nColors * sizeof(RGBQUAD));
  404. CString FileName;//建立文件
  405. FileName.Format("Frame-%05d.bmp",i);
  406. //CString strtemp = strBmpDir;
  407. //strtemp += "\\";
  408. //strtemp += FileName;
  409. FILE* fp=_tfopen(FileName ,_T("wb"));
  410. fwrite(&hdr,1,sizeof(BITMAPFILEHEADER),fp);//写入文件头
  411. fwrite(lpbi,1,sizeof(BITMAPINFOHEADER),fp);//写入信息头
  412. int ff = fwrite(buffer,1,lpbi->biSizeImage,fp);
  413. int e = GetLastError();
  414. pass=true;
  415. fclose(fp);
  416. // szTemp[] = "C:\\mysnapshot.bmp"临时的.bmp文件(outFile指针所指)
  417. /* CFile bitmapFile(outFile, CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);
  418. bitmapFile.Write(&hdr, sizeof(BITMAPFILEHEADER)); // 设置.bmp格式
  419. bitmapFile.Write(buffer, bitmapSize); // 存入数据(通俗的说就是画图)
  420. bitmapFile.Close(); // Closes the bitmapFile and deletes the object
  421. pass = true;*/
  422. }
  423. delete [] buffer;
  424. return pass;
  425. }
  426. }
  427. return false;
  428. }
  429. /*
  430. typedef struct tagBITMAPFILEHEADER {
  431. WORD bfType;
  432. DWORD bfSize;
  433. WORD bfReserved1;
  434. WORD bfReserved2;
  435. DWORD bfOffBits;
  436. } BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
  437. */
  438. bool CDXGraph::Stop(void)
  439. {
  440. if (mGraph && mMediaControl)
  441. {
  442. if (!IsStopped())
  443. {
  444. if (SUCCEEDED(mMediaControl->Stop()))
  445. {
  446. return true;
  447. }
  448. }
  449. else
  450. {
  451. return true;
  452. }
  453. }
  454. return false;
  455. }
  456. BOOL CDXGraph::GrabBitmap(const char *outFile)
  457. {
  458. if (mBasicVideo) // 类IBasicVideo
  459. {
  460. long bitmapSize = 0;
  461. if (SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, 0)))
  462. {
  463. bool pass = false;
  464. unsigned char * buffer = new unsigned char[bitmapSize];
  465. // 得到当前画面的大小bitmapSize和数据buffer
  466. if (SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, (long *)buffer)))
  467. {
  468. BITMAPFILEHEADER hdr;
  469. LPBITMAPINFOHEADER lpbi;
  470. lpbi = (LPBITMAPINFOHEADER)buffer;
  471. int nColors = 0; //调色板中的颜色个数.(RGB格式在8位以下的,需要用调色板。
  472. //调色板实际上就是定义一些颜色的数组)
  473. if (lpbi->biBitCount <= 8) //biBitCount:每个像素的位数.PS:RGB32每个像素用32位表示,也就是4个字节
  474. nColors = 1 << lpbi->biBitCount;
  475. hdr.bfType = ((WORD) ('M' << 8) | 'B'); //always is "BM"
  476. hdr.bfSize = bitmapSize + sizeof( hdr );
  477. hdr.bfReserved1 = 0;
  478. hdr.bfReserved2 = 0;
  479. hdr.bfOffBits = (DWORD) (sizeof(BITMAPFILEHEADER) + lpbi->biSize +
  480. nColors * sizeof(RGBQUAD));
  481. // szTemp[] = "C:\\mysnapshot.bmp"临时的.bmp文件(outFile指针所指)
  482. CFile bitmapFile(outFile, CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);
  483. bitmapFile.Write(&hdr, sizeof(BITMAPFILEHEADER)); // 设置.bmp格式
  484. bitmapFile.Write(buffer, bitmapSize); // 存入数据(通俗的说就是画图)
  485. bitmapFile.Close(); // Closes the bitmapFile and deletes the object
  486. pass = true;
  487. }
  488. delete [] buffer;
  489. return pass;
  490. }
  491. }
  492. return false;
  493. }
  494. BOOL CDXGraph::IsFormatFrame(void)
  495. {
  496. if (SUCCEEDED(mSeeking->IsFormatSupported(&TIME_FORMAT_FRAME)))
  497. return TRUE;
  498. else
  499. return FALSE;
  500. }
  501. BOOL CDXGraph::SetRate(double iFrameps)
  502. {
  503. if(FAILED(mSeeking->SetRate(iFrameps)))
  504. {
  505. printf("SetRate failed\r\n");
  506. return FALSE;
  507. }
  508. return TRUE;
  509. }
  510. int CDXGraph::GetFPS(void)
  511. {
  512. int nCount = 0;
  513. int nSize = 0;
  514. HRESULT hr = NULL;
  515. int nFrame = 0;
  516. double dAvgTimePerFrame = 0;
  517. double dRate = 0;
  518. if (NULL == mBasicVideo || NULL == mSeeking) return 0;
  519. mBasicVideo->get_AvgTimePerFrame(&dAvgTimePerFrame);
  520. mSeeking->GetRate(&dRate);
  521. nFrame = (int)(1.0/dAvgTimePerFrame)*dRate;
  522. // hr = mSeeking->SetTimeFormat(&TIME_FORMAT_NONE);
  523. // if (FAILED(hr))
  524. // {
  525. // printf("SetTimeFormat TIME_FORMAT_NONE failed\n");
  526. // }
  527. // if (SUCCEEDED(mSeeking->GetDuration(&length)))
  528. // {
  529. //
  530. return nFrame;
  531. }