转载请标明是引用于 http://blog.csdn.net/chenyujing1234
欢迎大家提出意见,一起讨论!
DirectShow对avi视频按帧设置慢放功能需要视频源支持帧操作。
可以通过IMediaSeeking::IsFormatSupported(&TIME_FORMAT_FRAME);判断是否支持;
如果支持然后通过IMediaSeeking;:SetTimeFormat(&TIME_FORMAT_FRAME); 设置时间格式为帧格式。
#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 ); |
REFERENCE_TIME rtNow = 10 * ONE_SECOND; hr = pSeek->SetPositions( &rtNow, AM_SEEKING_RelativePositioning, NULL, AM_SEEKING_NoPositioning ); |
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); } } |
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; }