=====================================================
最简单的基于DirectShow的示例文章列表:
最简单的基于DirectShow的示例:视频播放器
最简单的基于DirectShow的示例:视频播放器图形界面版
最简单的基于DirectShow的示例:视频播放器自定义版
最简单的基于DirectShow的示例:获取Filter信息
=====================================================
本文记录一个最简单的基于DirectShow的图形界面的视频播放器。基于DirectShow的图形界面的播放器的例子还是比较多的,但是大部分都是“层层封装”的例子。“层层封装”的例子相对来说更加稳定,但是却不是很容易理解。因为DirectShow本身的接口函数的数量就比较多,如果再加上封装DirectShow的函数,合起来的函数数量是非常大的,很容易让人搞不清哪些才是真正的DirectShow接口函数。本播放器剥去了DirectShow例子中的“层层封装”,直接调用DirectShow的接口完成视频的播放工作,更加适合DirectShow入门使用。
void CplayerGUIDlg::OnBnClickedStart() { CStringA cstr_urla; CStringW cstr_urlw; HRESULT hr; //Render #ifdef _UNICODE m_url.GetWindowText(cstr_urlw); #else USES_CONVERSION; m_url.GetWindowText(cstr_urla); cstr_urlw.Format(L"%s",A2W(cstr_urla)); #endif if(cstr_urlw.IsEmpty()){ AfxMessageBox(_T("Input URL is NULL!")); return; } hr = pGraph->RenderFile(cstr_urlw, NULL); if(FAILED(hr)){ AfxMessageBox(_T("Can't open input file!")); return; } //Set Window HWND screen_hwnd=NULL; RECT windowRect; screen_hwnd = this->GetDlgItem(IDC_SCREEN)->GetSafeHwnd(); ::GetClientRect(screen_hwnd, &windowRect); pWindow->put_Visible(OAFALSE); pWindow->put_Owner((OAHWND)screen_hwnd); pWindow->put_Left(0); pWindow->put_Top(0); pWindow->put_Width(windowRect.right - windowRect.left); pWindow->put_Height(windowRect.bottom - windowRect.top); pWindow->put_WindowStyle(WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_THICKFRAME); pWindow->put_MessageDrain((OAHWND) screen_hwnd);//Receive Message pWindow->put_Visible(OATRUE); pEvent->SetNotifyWindow((OAHWND)screen_hwnd, WM_GRAPHNOTIFY, 0); // Run hr = pControl->Run(); playerstate=STATE_PLAY; SetBtn(STATE_PLAY); SetTimer(1,1000,NULL); }
void CplayerGUIDlg::OnBnClickedPause() { HRESULT hr; if(playerstate==STATE_PLAY){ hr=pControl->Pause(); playerstate=STATE_PAUSE; GetDlgItem(ID_PAUSE)->SetWindowText(_T("Resume")); }else if(playerstate==STATE_PAUSE){ hr=pControl->Run(); playerstate=STATE_PLAY; GetDlgItem(ID_PAUSE)->SetWindowText(_T("Pause")); } }
void CplayerGUIDlg::OnBnClickedStop() { long long position = 0; HRESULT hr; hr = pSeeking->SetPositions(&position, AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame, 0, AM_SEEKING_NoPositioning); KillTimer(1); hr=pControl->Stop(); // Enumerate the filters And remove them IEnumFilters *pEnum = NULL; hr = pGraph->EnumFilters(&pEnum); if (SUCCEEDED(hr)) { IBaseFilter *pFilter = NULL; while (S_OK == pEnum->Next(1, &pFilter, NULL)) { // Remove the filter. pGraph->RemoveFilter(pFilter); // Reset the enumerator. pEnum->Reset(); pFilter->Release(); } pEnum->Release(); } SystemClear(); }
SetTimer(1,1000,NULL);在视频停止播放的时候,调用KillTimer()结束定时器。
KillTimer(1);在定时器的消息响应函数中,调用了IMediaSeeking的GetCurrentPosition()获取视频当前播放到的时间,调用了IMediaSeeking的GetDuration ()获取视频的时长。根据以上函数得到的数值,计算后把结果设置到相应的控件上。这部分的代码如下所示。
void CplayerGUIDlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1){ CString curtimestr,durationstr; long long curtime; long long duration; int tns, thh, tmm, tss; int progress; //ms pSeeking->GetCurrentPosition(&curtime); if(curtime!=0){ //change to second tns = curtime/10000000; thh = tns / 3600; tmm = (tns % 3600) / 60; tss = (tns % 60); curtimestr.Format(_T("%02d:%02d:%02d"),thh,tmm,tss); m_curtime.SetWindowText(curtimestr); } pSeeking->GetDuration(&duration); if(duration!=0){ tns = duration/10000000; thh = tns / 3600; tmm = (tns % 3600) / 60; tss = (tns % 60); durationstr.Format(_T("%02d:%02d:%02d"),thh,tmm,tss); m_duration.SetWindowText(durationstr); progress=curtime*100/duration; m_progress.SetPos(progress); } } CDialogEx::OnTimer(nIDEvent); }
void CplayerGUIDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if (pScrollBar->GetSafeHwnd() == m_progress.GetSafeHwnd()){ float pos_bar=0.0; long long duration=0.0; long long pos_time=0.0; if(nSBCode==SB_THUMBPOSITION){ pos_bar=(float)nPos/100.0; pSeeking->GetDuration(&duration); pos_time=pos_bar*duration; long long position = (long long)(pos_time); HRESULT hr = pSeeking->SetPositions(&position, AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame, 0, AM_SEEKING_NoPositioning); } } CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar); }
void CplayerGUIDlg::OnBnClickedFullscreen() { pWindow->put_FullScreenMode(OATRUE); }
//Exit Full Screen mode when push "ESC" BOOL CplayerGUIDlg::PreTranslateMessage(MSG* pMsg) { if (pMsg->message == WM_KEYDOWN){ if (pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE){ // Restore form fullscreen mode pWindow->put_FullScreenMode(OAFALSE); return 1; } } return CDialogEx::PreTranslateMessage(pMsg); }
pWindow->put_WindowStyle(WS_CHILD|WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_THICKFRAME);如果没有指定WS_THICKFRAME样式的话,在退出“全屏”模式之后,视频就显示不出来了,取而代之的是一片黑色。
但是设定WS_THICKFRAME样式之后,视频窗口的外围会有一层“白边”,会影响到视频显示的美观。因此我们如果希望正常使用全屏的话,可能需要找一种更好的方法,在这里我就没有深入研究了。
这是使用DirectShow基于MFC开发的一个示例播放器。实现了一个播放器的基本功能:播放,暂停/继续,停止,播放时间轴的显示,以及从任一点开始播放媒体。并且支持将媒体文件拖拽至播放器进行播放。播放前将媒体文件的路径输入到“URL”栏中,然后单击“Start”即可开始播放。在软件下方包含了“start”,“Pause”,“Stop”等按钮用于控制媒体的播放。
播放时候的效果截图如下所示。
单击“Full Screen”可以全屏播放。单击“Info”可以显示正在播放媒体的信息,包括以下两种信息:
项目主页
SourceForge:https://sourceforge.net/projects/simplestdirectshowexample/
Github:https://github.com/leixiaohua1020/simplest_directshow_example
开源中国:http://git.oschina.net/leixiaohua1020/simplest_directshow_example