Media Player Classic - Home Cinema (mpc-hc)播放器一个经典的影音播放器,免费软件,可播放CD、VCD、DVD、及MP3、MP4、AVI、AAC等多种影音格式。与此同时,它还是开源的。今天尝试着编译了一下它的源代码(还是第一次接触这么大的MFC工程)
第一步::准备
1. 安装 Visual C++ 2010(不能是Express版本)
2. 安装Visual Studio 2010 Service Pack 1 -> http://www.microsoft.com/downloads/en/details.aspx?FamilyID=75568aa6-8107-475d-948a-ef22627e57a5&displaylang=en
3. 安装DirectX SDK (June 2010) -> http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=3021d52b-514e-41d3-ad02-438a3ba730ba
第二步:准备GCC环境(这个主要是编译FFMPEG用的,FFMPEG只能用GCC编译)
1. 下载并解压 MSYS_MinGW-w64_GCC_481_x86-x64.7z 到 "C:\MSYS" 地址: http://xhmikosr.1f0.de/tools/MSYS_MinGW-w64_GCC_481_x86-x64.7z
(注意:就算自己电脑上有MSYS_MinGW,也建议下载这个)
2. 在 "C:\mpc-hc" 创建一个"build.user.bat",内容如下(有些参数根据自己系统确定):
@ECHO OFF SET "MPCHC_MSYS=C:\MSYS" SET "MPCHC_MINGW32=%MPCHC_MSYS%\mingw" SET "MPCHC_MINGW64=%MPCHC_MINGW32%" REM Git is optional to set if you chose to add it in PATH when installing it(下面这条可以不要) SET "MPCHC_GIT=C:\Program Files (x86)\Git"
第三步:编译
1.使用Git 下载 MPC-HC's 到 "C:\mpc-hc" (其他地方也行)
Git命令(俩都行):
git clone --recursive https://github.com/mpc-hc/mpc-hc.git
或:
git clone https://github.com/mpc-hc/mpc-hc.git
git submodule update --init --recursive
2. 打开sln文件 "C:\mpc-hc\mpc-hc.sln",编译之(注:如果Release编译不成功,可以试试编译Release-Lite)
3. 在"C:\mpc-hc\bin\mpc-hc_x86"下得到"mpc-hc.exe"
4. 打开sln文件 "C:\mpc-hc\mpciconlib.sln",编译之
5. 得到"mpciconlib.dll"
6. 打开sln文件 "C:\mpc-hc\mpcresources.sln",编译之
7. 得到 "mpcresources.XX.dll"
注:直接运行"build.bat" 可以编译所有文件
Media Player Classic - Home Cinema (mpc-hc)播放器一个经典的影音播放器,可播放CD、VCD、DVD、及MP3、MP4、AVI、AAC等多种影音格式。与此同时,它还是开源的。很多知名的视频播放器都是在它的基础上建立起来的,在这里就不例举了。本文将会对其源代码进行简要的分析。
之前一篇博客中曾经介绍了它的编译过程:VC2010 编译 Media Player Classic - Home Cinema (mpc-hc)
在这里就不再重复说明了,直入主题,看看它的工程是什么样子:
相信大部分人看到这个工程的第一反应就是:好大啊!确实,我看到这个工程的时候也是这个反应。mpc-hc总体上分为3个部分:Apps,Filters,Libraries。其中Apps是其主程序;Filters是其附带的一些directshow filter,比如说AVI分离器,FLV分离器等等;Libraries则是其依赖的一些库:像Zlib这类的。
来细看看mpc-hc都有什么directshow filter吧(截图都放不下了...)
Filters分为以下几种:
Muxer(封装),Parser(解封装,或称为分离器),Reader(读取),Renderer(显示),Source(源),Switcher(这个我不懂),Transform(处理)
在这里就不一一例举各种Filter了,因为数量实在太多,大部分Filter工程都可以通过名称来判断其功能。
再看看主程序Apps工程吧:
可见主程序包含了巨量的代码,截图也只能显示其中的一部分。因此在代码分析的时候,不可能做到面面俱到,只能选择其中的重点部分进行详细的分析。
mpc-hc的对话框数量也很惊人:
在这里就不再花篇幅形容mpc-hc工程的巨大了。赶紧说说如何来研究分析它的代码。本文主要分析它的主程序即在Apps目录下的工程。先介绍一下我总结出来的一些规则:
1.以PPage开头的.cpp或.h文件通常是一些属性选项卡的对话框对应的类。随后会详细介绍一个“视频信息”选项卡的代码(在这里用到了开源库MediaInfo)
2.主框架所在的位置是Mainfrm.cpp
目前只有这两条规则,以后会随着研究的不断深入,进一步完善这些规则。
mpc-hc最核心的类名字叫CMainFrame,它的定义位于MainFrm.h文件中
CMainFrame定义非常的长,包含了视频播放器的方方面面,一共900多行,在这里应该快放不下了。因此我删掉了很多代码,只保留了部分代码。关键的函数上面都写上了注释。
class CMainFrame : public CFrameWnd, public CDropTarget { ... // TODO: wrap these graph objects into a class to make it look cleaner //各种DirectShow接口 //CComPtr被称为智能指针,是ATL提供的一个模版类,能够从语法上自动完成COM的AddRef和Release。 CComPtr<IGraphBuilder2> m_pGB; CComQIPtr<IMediaControl> m_pMC; CComQIPtr<IMediaEventEx> m_pME; CComQIPtr<IVideoWindow> m_pVW; //这里也可以获得 //分辨率,比特率,帧率 //经过测试,貌似这里取不到值 = = CComQIPtr<IBasicVideo> m_pBV; //音量,均衡器等信息 CComQIPtr<IBasicAudio> m_pBA; CComQIPtr<IMediaSeeking> m_pMS; CComQIPtr<IVideoFrameStep> m_pFS; //接收端质量信息:抖动,抖动,视音频同步情况等。。。 CComQIPtr<IQualProp, &IID_IQualProp> m_pQP; //缓存信息 CComQIPtr<IBufferInfo> m_pBI; CComQIPtr<IAMOpenProgress> m_pAMOP; CComPtr<IVMRMixerControl9> m_pVMRMC; CComPtr<IMFVideoDisplayControl> m_pMFVDC; CComPtr<IMFVideoProcessor> m_pMFVP; CComPtr<IVMRWindowlessControl9> m_pVMRWC; ... void SetVolumeBoost(UINT nAudioBoost); void SetBalance(int balance); // subtitles CCritSec m_csSubLock; CList<SubtitleInput> m_pSubStreams; POSITION m_posFirstExtSub; ISubStream* m_pCurrentSubStream; SubtitleInput* GetSubtitleInput(int& i, bool bIsOffset = false); friend class CTextPassThruFilter; // windowing CRect m_lastWindowRect; CPoint m_lastMouseMove; void ShowControls(int nCS, bool fSave = false); void SetUIPreset(int iCaptionMenuMode, UINT nCS); void SetDefaultWindowRect(int iMonitor = 0); void SetDefaultFullscreenState(); void RestoreDefaultWindowRect(); void ZoomVideoWindow(bool snap = true, double scale = ZOOM_DEFAULT_LEVEL); double GetZoomAutoFitScale(bool bLargerOnly = false) const; void SetAlwaysOnTop(int iOnTop); // dynamic menus // 动态菜单 void SetupOpenCDSubMenu(); void SetupFiltersSubMenu(); void SetupAudioSwitcherSubMenu(); void SetupSubtitlesSubMenu(); ... CMenu m_popupmain, m_popup; CMenu m_opencds; CMenu m_filters, m_subtitles, m_audios; CMenu m_language; ... // chapters (file mode) CComPtr<IDSMChapterBag> m_pCB; void SetupChapters(); // chapters (dvd mode) void SetupDVDChapters(); void SetupIViAudReg(); void AddTextPassThruFilter(); int m_nLoops; UINT m_nLastSkipDirection; bool m_fCustomGraph; ... public: void StartWebServer(int nPort); void StopWebServer(); CString GetStatusMessage() const; int GetPlaybackMode() const { return m_iPlaybackMode; } void SetPlaybackMode(int iNewStatus); bool IsMuted() { return m_wndToolBar.GetVolume() == -10000; } int GetVolume() { return m_wndToolBar.m_volctrl.GetPos(); } public: CMainFrame(); DECLARE_DYNAMIC(CMainFrame) // Attributes public: bool m_fFullScreen; bool m_fFirstFSAfterLaunchOnFS; bool m_fStartInD3DFullscreen; bool m_fHideCursor; CMenu m_navaudio, m_navsubtitle; CComPtr<IBaseFilter> m_pRefClock; // Adjustable reference clock. GothSync CComPtr<ISyncClock> m_pSyncClock; ... CControlBar* m_pLastBar; protected: MPC_LOADSTATE m_iMediaLoadState; bool m_bFirstPlay; bool m_fAudioOnly; dispmode m_dmBeforeFullscreen; CString m_LastOpenFile, m_LastOpenBDPath; HMONITOR m_LastWindow_HM; DVD_DOMAIN m_iDVDDomain; DWORD m_iDVDTitle; double m_dSpeedRate; double m_ZoomX, m_ZoomY, m_PosX, m_PosY; int m_AngleX, m_AngleY, m_AngleZ; //操作 Operations //打开一个媒体 bool OpenMediaPrivate(CAutoPtr<OpenMediaData> pOMD); //关闭媒体 void CloseMediaPrivate(); void DoTunerScan(TunerScanData* pTSD); CWnd* GetModalParent(); void OpenCreateGraphObject(OpenMediaData* pOMD); //打开文件 void OpenFile(OpenFileData* pOFD); //打开DVD void OpenDVD(OpenDVDData* pODD); //打开摄像头 void OpenCapture(OpenDeviceData* pODD); HRESULT OpenBDAGraph(); void OpenCustomizeGraph(); //设置视频窗口 void OpenSetupVideo(); //设置音量 void OpenSetupAudio(); void OpenSetupInfoBar(); void UpdateChapterInInfoBar(); //打开统计工具条 void OpenSetupStatsBar(); //打开状态工具条 void OpenSetupStatusBar(); // void OpenSetupToolBar(); void OpenSetupCaptureBar(); //设置窗口标题 void OpenSetupWindowTitle(CString fn = _T("")); void AutoChangeMonitorMode(); bool GraphEventComplete(); friend class CGraphThread; CGraphThread* m_pGraphThread; bool m_bOpenedThruThread; CAtlArray<REFERENCE_TIME> m_kfs; bool m_fOpeningAborted; bool m_bWasSnapped; public: void OpenCurPlaylistItem(REFERENCE_TIME rtStart = 0); void OpenMedia(CAutoPtr<OpenMediaData> pOMD); void PlayFavoriteFile(CString fav); void PlayFavoriteDVD(CString fav); bool ResetDevice(); bool DisplayChange(); void CloseMedia(); void StartTunerScan(CAutoPtr<TunerScanData> pTSD); void StopTunerScan(); HRESULT SetChannel(int nChannel); void AddCurDevToPlaylist(); bool m_fTrayIcon; //设置系统托盘图标 void ShowTrayIcon(bool fShow); void SetTrayTip(CString str); CSize GetVideoSize() const; void ToggleFullscreen(bool fToNearest, bool fSwitchScreenResWhenHasTo); void ToggleD3DFullscreen(bool fSwitchScreenResWhenHasTo); void MoveVideoWindow(bool fShowStats = false); void RepaintVideo(); void HideVideoWindow(bool fHide); OAFilterState GetMediaState() const; REFERENCE_TIME GetPos() const; REFERENCE_TIME GetDur() const; void SeekTo(REFERENCE_TIME rt, bool fSeekToKeyFrame = false); //设置播放速率 void SetPlayingRate(double rate); DWORD SetupAudioStreams(); DWORD SetupSubtitleStreams(); //字幕 bool LoadSubtitle(CString fn, ISubStream** actualStream = nullptr, bool bAutoLoad = false); bool SetSubtitle(int i, bool bIsOffset = false, bool bDisplayMessage = false, bool bApplyDefStyle = false); void SetSubtitle(ISubStream* pSubStream, bool bApplyDefStyle = false); void ToggleSubtitleOnOff(bool bDisplayMessage = false); void ReplaceSubtitle(const ISubStream* pSubStreamOld, ISubStream* pSubStreamNew); void InvalidateSubtitle(DWORD_PTR nSubtitleId = -1, REFERENCE_TIME rtInvalidate = -1); void ReloadSubtitle(); HRESULT InsertTextPassThruFilter(IBaseFilter* pBF, IPin* pPin, IPin* pPinto); void SetAudioTrackIdx(int index); void SetSubtitleTrackIdx(int index); void AddFavorite(bool fDisplayMessage = false, bool fShowDialog = true); // shaders CAtlList<CString> m_shaderlabels; CAtlList<CString> m_shaderlabelsScreenSpace; void SetShaders(); void UpdateShaders(CString label); // capturing bool m_fCapturing; HRESULT BuildCapture(IPin* pPin, IBaseFilter* pBF[3], const GUID& majortype, AM_MEDIA_TYPE* pmt); // pBF: 0 buff, 1 enc, 2 mux, pmt is for 1 enc bool BuildToCapturePreviewPin(IBaseFilter* pVidCap, IPin** pVidCapPin, IPin** pVidPrevPin, IBaseFilter* pAudCap, IPin** pAudCapPin, IPin** pAudPrevPin); bool BuildGraphVideoAudio(int fVPreview, bool fVCapture, int fAPreview, bool fACapture); bool DoCapture(), StartCapture(), StopCapture(); bool DoAfterPlaybackEvent(); void ParseDirs(CAtlList<CString>& sl); bool SearchInDir(bool bDirForward, bool bLoop = false); virtual BOOL PreCreateWindow(CREATESTRUCT& cs); virtual BOOL PreTranslateMessage(MSG* pMsg); virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo); virtual void RecalcLayout(BOOL bNotify = TRUE); // DVB capture void ShowCurrentChannelInfo(bool fShowOSD = true, bool fShowInfoBar = false); // Implementation public: virtual ~CMainFrame(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // control bar embedded members CChildView m_wndView; UINT m_nCS; CPlayerSeekBar m_wndSeekBar; CPlayerToolBar m_wndToolBar; CPlayerInfoBar m_wndInfoBar; CPlayerInfoBar m_wndStatsBar; CPlayerStatusBar m_wndStatusBar; CList<CControlBar*> m_bars; CPlayerSubresyncBar m_wndSubresyncBar; CPlayerPlaylistBar m_wndPlaylistBar; CPlayerCaptureBar m_wndCaptureBar; CPlayerNavigationBar m_wndNavigationBar; CPlayerShaderEditorBar m_wndShaderEditorBar; CEditListEditor m_wndEditListEditor; CList<CSizingControlBar*> m_dockingbars; ... // Generated message map functions DECLARE_MESSAGE_MAP() public: //打开的时候加载 afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); //关闭的时候加载 afx_msg void OnDestroy(); afx_msg LRESULT OnTaskBarRestart(WPARAM, LPARAM); afx_msg LRESULT OnNotifyIcon(WPARAM, LPARAM); afx_msg LRESULT OnTaskBarThumbnailsCreate(WPARAM, LPARAM); afx_msg LRESULT OnSkypeAttach(WPARAM wParam, LPARAM lParam); afx_msg void OnSetFocus(CWnd* pOldWnd); afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI); afx_msg void OnMove(int x, int y); afx_msg void OnMoving(UINT fwSide, LPRECT pRect); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnSizing(UINT fwSide, LPRECT pRect); afx_msg void OnDisplayChange(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnActivateApp(BOOL bActive, DWORD dwThreadID); afx_msg LRESULT OnAppCommand(WPARAM wParam, LPARAM lParam); afx_msg void OnRawInput(UINT nInputcode, HRAWINPUT hRawInput); afx_msg LRESULT OnHotKey(WPARAM wParam, LPARAM lParam); afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg LRESULT OnGraphNotify(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnResetDevice(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnRepaintRenderLess(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnResumeFromState(WPARAM wParam, LPARAM lParam); ... // menu item handlers afx_msg void OnFileOpenQuick(); afx_msg void OnFileOpenmedia(); afx_msg void OnUpdateFileOpen(CCmdUI* pCmdUI); afx_msg BOOL OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct); afx_msg void OnFileOpendvd(); afx_msg void OnFileOpendevice(); afx_msg void OnFileOpenCD(UINT nID); afx_msg void OnFileReopen(); afx_msg void OnFileRecycle(); afx_msg void OnDropFiles(HDROP hDropInfo); // no menu item ... afx_msg void OnHelpHomepage(); afx_msg void OnHelpCheckForUpdate(); afx_msg void OnHelpToolbarImages(); afx_msg void OnHelpDonate(); //关闭的时候加载 afx_msg void OnClose(); afx_msg void OnLanguage(UINT nID); afx_msg void OnUpdateLanguage(CCmdUI* pCmdUI); CMPC_Lcd m_Lcd; // ==== Added by CASIMIR666 CWnd* m_pVideoWnd; // Current Video (main display screen or 2nd) SIZE m_fullWndSize; CFullscreenWnd* m_pFullscreenWnd; CVMROSD m_OSD; bool m_bRemainingTime; int m_nCurSubtitle; long m_lSubtitleShift; REFERENCE_TIME m_rtCurSubPos; CString m_strTitle; bool m_bToggleShader; bool m_bToggleShaderScreenSpace; bool m_bInOptions; bool m_bStopTunerScan; bool m_bLockedZoomVideoWindow; int m_nLockedZoomVideoWindow; bool m_fSetChannelActive; void SetLoadState(MPC_LOADSTATE iState); void SetPlayState(MPC_PLAYSTATE iState); bool CreateFullScreenWindow(); void SetupEVRColorControl(); void SetupVMR9ColorControl(); void SetColorControl(DWORD flags, int& brightness, int& contrast, int& hue, int& saturation); void SetClosedCaptions(bool enable); LPCTSTR GetDVDAudioFormatName(const DVD_AudioAttributes& ATR) const; void SetAudioDelay(REFERENCE_TIME rtShift); void SetSubtitleDelay(int delay_ms); //void AutoSelectTracks(); bool IsRealEngineCompatible(CString strFilename) const; void SetTimersPlay(); void KillTimersStop(); // MPC API functions void ProcessAPICommand(COPYDATASTRUCT* pCDS); void SendAPICommand(MPCAPI_COMMAND nCommand, LPCWSTR fmt, ...); void SendNowPlayingToApi(); void SendSubtitleTracksToApi(); void SendAudioTracksToApi(); void SendPlaylistToApi(); ... protected: // GDI+ ULONG_PTR m_gdiplusToken; virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); void WTSRegisterSessionNotification(); void WTSUnRegisterSessionNotification(); DWORD m_nMenuHideTick; UINT m_nSeekDirection; public: afx_msg UINT OnPowerBroadcast(UINT nPowerEvent, UINT nEventData); afx_msg void OnSessionChange(UINT nSessionState, UINT nId); void EnableShaders1(bool enable); void EnableShaders2(bool enable); CAtlList<CHdmvClipInfo::PlaylistItem> m_MPLSPlaylist; bool m_bIsBDPlay; bool OpenBD(CString Path); };
面对一个如此巨大的类,可能会让人感觉到无从下手。我开始研究的时候也不知道该从何学起(实际上找到CMainFrame这个类就花了我挺长时间的,开始的时候根本没找到哪个类才是mpc-hc的最核心的类)。经过一段时间的探索,我发现了打开一个媒体的函数OpenMedia(),这个函数应该是我们每次使用mpc-hc都一定会调用的函数。从这个函数开始学习源代码还是比较合适的。
在看OpenMedia()代码之前,先来看看有哪些函数调用它了。我们可以借助VC2010的“查看调用层次结构”功能来完成这个任务。发现有3个函数:
OnFileOpendevice()//打开一个设备(比如说摄像头) OnFileOpendvd()//打开一个DVD OpenCurPlaylistItem()//打开播放列表的一条记录(比如说一个文件)
这3个函数正好对应着mpc-hc的3个功能:打开设备(摄像头),打开DVD,打开文件。这3个函数在这里就不多讲了,以后有机会再进行分析。
下面我们来看看OpenMedia()函数:
//打开媒体(非private) void CMainFrame::OpenMedia(CAutoPtr<OpenMediaData> pOMD) { // shortcut if (OpenDeviceData* p = dynamic_cast<OpenDeviceData*>(pOMD.m_p)) { if (m_iMediaLoadState == MLS_LOADED && m_pAMTuner && m_VidDispName == p->DisplayName[0] && m_AudDispName == p->DisplayName[1]) { m_wndCaptureBar.m_capdlg.SetVideoInput(p->vinput); m_wndCaptureBar.m_capdlg.SetVideoChannel(p->vchannel); m_wndCaptureBar.m_capdlg.SetAudioInput(p->ainput); SendNowPlayingToSkype(); return; } } if (m_iMediaLoadState != MLS_CLOSED) { CloseMedia(); } //m_iMediaLoadState = MLS_LOADING; // HACK: hides the logo const CAppSettings& s = AfxGetAppSettings(); bool fUseThread = m_pGraphThread && s.fEnableWorkerThreadForOpening; if (OpenFileData* p = dynamic_cast<OpenFileData*>(pOMD.m_p)) { if (!p->fns.IsEmpty()) { engine_t e = s.m_Formats.GetEngine(p->fns.GetHead()); if (e != DirectShow /*&& e != RealMedia && e != QuickTime*/) { fUseThread = false; } } } else if (OpenDeviceData* p = dynamic_cast<OpenDeviceData*>(pOMD.m_p)) { fUseThread = false; } // Create D3DFullscreen window if launched in fullscreen if (s.IsD3DFullscreen() && m_fStartInD3DFullscreen) { if (s.AutoChangeFullscrRes.bEnabled) { AutoChangeMonitorMode(); } CreateFullScreenWindow(); m_pVideoWnd = m_pFullscreenWnd; m_fStartInD3DFullscreen = false; } else { m_pVideoWnd = &m_wndView; } if (fUseThread) { m_pGraphThread->PostThreadMessage(CGraphThread::TM_OPEN, 0, (LPARAM)pOMD.Detach()); m_bOpenedThruThread = true; } else { //打开媒体(private) OpenMediaPrivate(pOMD); m_bOpenedThruThread = false; } }
这里需要注意,OpenMedia()调用了函数OpenMediaPrivate()。文件的打开功能实际上是在OpenMediaPrivate()中完成的。
下面我们来看看OpenMediaPrivate()的代码,发现比OpenMedia()要复杂很多。
//打开一个媒体(private) bool CMainFrame::OpenMediaPrivate(CAutoPtr<OpenMediaData> pOMD) { //获得设置信息 CAppSettings& s = AfxGetAppSettings(); if (m_iMediaLoadState != MLS_CLOSED) { ASSERT(0); return false; } //OpenFileData //OpenDVDData //OpenDeviceData //里面包含了文件或者DVD信息(名称等) OpenFileData* pFileData = dynamic_cast<OpenFileData*>(pOMD.m_p); OpenDVDData* pDVDData = dynamic_cast<OpenDVDData*>(pOMD.m_p); OpenDeviceData* pDeviceData = dynamic_cast<OpenDeviceData*>(pOMD.m_p); if (!pFileData && !pDVDData && !pDeviceData) { ASSERT(0); return false; } // Clear DXVA state ... ClearDXVAState(); #ifdef _DEBUG // Debug trace code - Begin // Check for bad / buggy auto loading file code if (pFileData) { POSITION pos = pFileData->fns.GetHeadPosition(); UINT index = 0; while (pos != nullptr) { CString path = pFileData->fns.GetNext(pos); TRACE(_T("--> CMainFrame::OpenMediaPrivate - pFileData->fns[%d]:\n"), index); TRACE(_T("\t%ws\n"), path.GetString()); // %ws - wide character string always index++; } } // Debug trace code - End #endif CString mi_fn = _T(""); if (pFileData) { if (pFileData->fns.IsEmpty()) { return false; } CString fn = pFileData->fns.GetHead(); int i = fn.Find(_T(":\\")); if (i > 0) { CString drive = fn.Left(i + 2); UINT type = GetDriveType(drive); CAtlList<CString> sl; if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM && GetCDROMType(drive[0], sl) != CDROM_Audio) { int ret = IDRETRY; while (ret == IDRETRY) { WIN32_FIND_DATA findFileData; HANDLE h = FindFirstFile(fn, &findFileData); if (h != INVALID_HANDLE_VALUE) { FindClose(h); ret = IDOK; } else { CString msg; msg.Format(IDS_MAINFRM_114, fn); ret = AfxMessageBox(msg, MB_RETRYCANCEL); } } if (ret != IDOK) { return false; } } mi_fn = fn; } } SetLoadState(MLS_LOADING); // FIXME: Don't show "Closed" initially PostMessage(WM_KICKIDLE); CString err; m_fUpdateInfoBar = false; BeginWaitCursor(); try { CComPtr<IVMRMixerBitmap9> pVMB; CComPtr<IMFVideoMixerBitmap> pMFVMB; CComPtr<IMadVRTextOsd> pMVTO; if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } OpenCreateGraphObject(pOMD); if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } SetupIViAudReg(); if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } //按类型的不同打开不同的文件 if (pFileData) { //文件 OpenFile(pFileData); } else if (pDVDData) { //DVD OpenDVD(pDVDData); } else if (pDeviceData) { if (s.iDefaultCaptureDevice == 1) { HRESULT hr = OpenBDAGraph(); if (FAILED(hr)) { throw (UINT)IDS_CAPTURE_ERROR_DEVICE; } } else { OpenCapture(pDeviceData); } } else { throw (UINT)IDS_INVALID_PARAMS_ERROR; } m_pCAP2 = nullptr; m_pCAP = nullptr; //查找接口 m_pGB->FindInterface(__uuidof(ISubPicAllocatorPresenter), (void**)&m_pCAP, TRUE); m_pGB->FindInterface(__uuidof(ISubPicAllocatorPresenter2), (void**)&m_pCAP2, TRUE); m_pGB->FindInterface(__uuidof(IVMRWindowlessControl9), (void**)&m_pVMRWC, FALSE); // might have IVMRMixerBitmap9, but not IVMRWindowlessControl9 m_pGB->FindInterface(__uuidof(IVMRMixerControl9), (void**)&m_pVMRMC, TRUE); m_pGB->FindInterface(__uuidof(IVMRMixerBitmap9), (void**)&pVMB, TRUE); m_pGB->FindInterface(__uuidof(IMFVideoMixerBitmap), (void**)&pMFVMB, TRUE); pMVTO = m_pCAP; if (s.fShowOSD || s.fShowDebugInfo) { // Force OSD on when the debug switch is used if (pVMB) { m_OSD.Start(m_pVideoWnd, pVMB, IsD3DFullScreenMode()); } else if (pMFVMB) { m_OSD.Start(m_pVideoWnd, pMFVMB, IsD3DFullScreenMode()); } else if (pMVTO) { m_OSD.Start(m_pVideoWnd, pMVTO); } } //VMR9 SetupVMR9ColorControl(); // === EVR ! m_pGB->FindInterface(__uuidof(IMFVideoDisplayControl), (void**)&m_pMFVDC, TRUE); m_pGB->FindInterface(__uuidof(IMFVideoProcessor), (void**)&m_pMFVP, TRUE); if (m_pMFVDC) { m_pMFVDC->SetVideoWindow(m_pVideoWnd->m_hWnd); } //SetupEVRColorControl(); //does not work at this location //need to choose the correct mode (IMFVideoProcessor::SetVideoProcessorMode) BeginEnumFilters(m_pGB, pEF, pBF) { if (m_pLN21 = pBF) { m_pLN21->SetServiceState(s.fClosedCaptions ? AM_L21_CCSTATE_On : AM_L21_CCSTATE_Off); break; } } EndEnumFilters; if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } //打开自定义的Graph OpenCustomizeGraph(); if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } //设置视频窗口 OpenSetupVideo(); if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } //设置音量 OpenSetupAudio(); if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } if (m_pCAP && (!m_fAudioOnly || m_fRealMediaGraph)) { if (s.fDisableInternalSubtitles) { m_pSubStreams.RemoveAll(); // Needs to be replaced with code that checks for forced subtitles. } m_posFirstExtSub = nullptr; POSITION pos = pOMD->subs.GetHeadPosition(); while (pos) { LoadSubtitle(pOMD->subs.GetNext(pos), nullptr, true); } } if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } //设置视频窗口标题 OpenSetupWindowTitle(pOMD->title); if (s.fEnableEDLEditor) { m_wndEditListEditor.OpenFile(pOMD->title); } if (::GetCurrentThreadId() == AfxGetApp()->m_nThreadID) { OnFilePostOpenmedia(); } else { PostMessage(WM_COMMAND, ID_FILE_POST_OPENMEDIA); } while (m_iMediaLoadState != MLS_LOADED && m_iMediaLoadState != MLS_CLOSING // FIXME ) { Sleep(50); } //设置音频流 DWORD audstm = SetupAudioStreams(); //设置字幕流 DWORD substm = SetupSubtitleStreams(); if (audstm) { OnPlayAudio(ID_AUDIO_SUBITEM_START + audstm); } if (substm) { SetSubtitle(substm - 1); } // PostMessage instead of SendMessage because the user might call CloseMedia and then we would deadlock PostMessage(WM_COMMAND, ID_PLAY_PAUSE); m_bFirstPlay = true; if (!(s.nCLSwitches & CLSW_OPEN) && (s.nLoops > 0)) { PostMessage(WM_COMMAND, ID_PLAY_PLAY); } else { // If we don't start playing immediately, we need to initialize // the seekbar and the time counter. OnTimer(TIMER_STREAMPOSPOLLER); OnTimer(TIMER_STREAMPOSPOLLER2); } s.nCLSwitches &= ~CLSW_OPEN; if (pFileData) { if (pFileData->rtStart > 0) { PostMessage(WM_RESUMEFROMSTATE, (WPARAM)PM_FILE, (LPARAM)(pFileData->rtStart / 10000)); // REFERENCE_TIME doesn't fit in LPARAM under a 32bit env. } } else if (pDVDData) { if (pDVDData->pDvdState) { PostMessage(WM_RESUMEFROMSTATE, (WPARAM)PM_DVD, (LPARAM)(CComPtr<IDvdState>(pDVDData->pDvdState).Detach())); // must be released by the called message handler } } else if (pDeviceData) { m_wndCaptureBar.m_capdlg.SetVideoInput(pDeviceData->vinput); m_wndCaptureBar.m_capdlg.SetVideoChannel(pDeviceData->vchannel); m_wndCaptureBar.m_capdlg.SetAudioInput(pDeviceData->ainput); } } catch (LPCTSTR msg) { err = msg; } catch (CString& msg) { err = msg; } catch (UINT msg) { err.LoadString(msg); } EndWaitCursor(); if (!err.IsEmpty()) { //关闭 CloseMediaPrivate(); m_closingmsg = err; if (err != ResStr(IDS_AG_ABORTED)) { if (pFileData) { m_wndPlaylistBar.SetCurValid(false); if (m_wndPlaylistBar.IsAtEnd()) { m_nLoops++; } if (s.fLoopForever || m_nLoops < s.nLoops) { bool hasValidFile = false; if (m_nLastSkipDirection == ID_NAVIGATE_SKIPBACK) { hasValidFile = m_wndPlaylistBar.SetPrev(); } else { hasValidFile = m_wndPlaylistBar.SetNext(); } if (hasValidFile) { OpenCurPlaylistItem(); } } else if (m_wndPlaylistBar.GetCount() > 1) { DoAfterPlaybackEvent(); } } else { OnNavigateSkip(ID_NAVIGATE_SKIPFORWARD); } } } else { m_wndPlaylistBar.SetCurValid(true); // Apply command line audio shift if (s.rtShift != 0) { SetAudioDelay(s.rtShift); s.rtShift = 0; } } m_nLastSkipDirection = 0; if (s.AutoChangeFullscrRes.bEnabled && (m_fFullScreen || IsD3DFullScreenMode())) { AutoChangeMonitorMode(); } if (m_fFullScreen && s.fRememberZoomLevel) { m_fFirstFSAfterLaunchOnFS = true; } m_LastOpenFile = pOMD->title; PostMessage(WM_KICKIDLE); // calls main thread to update things if (!m_bIsBDPlay) { m_MPLSPlaylist.RemoveAll(); m_LastOpenBDPath = _T(""); } m_bIsBDPlay = false; return err.IsEmpty(); }
这里需要注意,根据打开方式的不同,OpenMediaPrivate()调用了不同的函数。
如果输入的类型为文件,则调用OpenFile()
如果输入的类型为DVD,则调用OpenDVD()
如果输入的类型为设备(例如摄像头),则调用OpenCapture()
在这里,我们假设输入的类型为文件(实际上这也是最普遍的情况)。
看看OpenFile()的源代码。
//打开文件 void CMainFrame::OpenFile(OpenFileData* pOFD) { if (pOFD->fns.IsEmpty()) { throw (UINT)IDS_MAINFRM_81; } //获取设置 CAppSettings& s = AfxGetAppSettings(); bool bMainFile = true; POSITION pos = pOFD->fns.GetHeadPosition(); while (pos) { CString fn = pOFD->fns.GetNext(pos); fn.Trim(); if (fn.IsEmpty() && !bMainFile) { break; } //使用DirectShow播放文件 HRESULT hr = m_pGB->RenderFile(CStringW(fn), nullptr); if (bMainFile) { // Don't try to save file position if source isn't seekable REFERENCE_TIME rtDur = 0; m_pMS->GetDuration(&rtDur); m_bRememberFilePos = s.fKeepHistory && s.fRememberFilePos && rtDur > 0; if (m_bRememberFilePos && !s.filePositions.AddEntry(fn)) { REFERENCE_TIME rtPos = s.filePositions.GetLatestEntry()->llPosition; if (m_pMS) { m_pMS->SetPositions(&rtPos, AM_SEEKING_AbsolutePositioning, nullptr, AM_SEEKING_NoPositioning); } } } QueryPerformanceCounter(&m_liLastSaveTime); if (FAILED(hr)) { if (bMainFile) { if (s.fReportFailedPins) { CComQIPtr<IGraphBuilderDeadEnd> pGBDE = m_pGB; if (pGBDE && pGBDE->GetCount()) { CMediaTypesDlg(pGBDE, GetModalParent()).DoModal(); } } UINT err; switch (hr) { case E_ABORT: case RFS_E_ABORT: err = IDS_MAINFRM_82; break; case E_FAIL: case E_POINTER: default: err = IDS_MAINFRM_83; break; case E_INVALIDARG: err = IDS_MAINFRM_84; break; case E_OUTOFMEMORY: err = IDS_AG_OUT_OF_MEMORY; break; case VFW_E_CANNOT_CONNECT: err = IDS_MAINFRM_86; break; case VFW_E_CANNOT_LOAD_SOURCE_FILTER: err = IDS_MAINFRM_87; break; case VFW_E_CANNOT_RENDER: err = IDS_MAINFRM_88; break; case VFW_E_INVALID_FILE_FORMAT: err = IDS_MAINFRM_89; break; case VFW_E_NOT_FOUND: err = IDS_MAINFRM_90; break; case VFW_E_UNKNOWN_FILE_TYPE: err = IDS_MAINFRM_91; break; case VFW_E_UNSUPPORTED_STREAM: err = IDS_MAINFRM_92; break; case RFS_E_NO_FILES: err = IDS_RFS_NO_FILES; break; case RFS_E_COMPRESSED: err = IDS_RFS_COMPRESSED; break; case RFS_E_ENCRYPTED: err = IDS_RFS_ENCRYPTED; break; case RFS_E_MISSING_VOLS: err = IDS_RFS_MISSING_VOLS; break; } throw err; } } // We don't keep track of the standard input since that hardly makes any sense if (s.fKeepHistory && fn != _T("pipe:0")) { CRecentFileList* pMRU = bMainFile ? &s.MRU : &s.MRUDub; pMRU->ReadList(); pMRU->Add(fn); pMRU->WriteList(); SHAddToRecentDocs(SHARD_PATH, fn); } if (bMainFile) { pOFD->title = fn; } bMainFile = false; if (m_fCustomGraph) { break; } } if (s.fReportFailedPins) { CComQIPtr<IGraphBuilderDeadEnd> pGBDE = m_pGB; if (pGBDE && pGBDE->GetCount()) { CMediaTypesDlg(pGBDE, GetModalParent()).DoModal(); } } if (!(m_pAMOP = m_pGB)) { BeginEnumFilters(m_pGB, pEF, pBF); if (m_pAMOP = pBF) { break; } EndEnumFilters; } if (FindFilter(CLSID_MPCShoutcastSource, m_pGB)) { m_fUpdateInfoBar = true; } SetupChapters(); CComQIPtr<IKeyFrameInfo> pKFI; BeginEnumFilters(m_pGB, pEF, pBF); if (pKFI = pBF) { break; } EndEnumFilters; UINT nKFs = 0; if (pKFI && S_OK == pKFI->GetKeyFrameCount(nKFs) && nKFs > 0) { UINT k = nKFs; if (!m_kfs.SetCount(k) || S_OK != pKFI->GetKeyFrames(&TIME_FORMAT_MEDIA_TIME, m_kfs.GetData(), k) || k != nKFs) { m_kfs.RemoveAll(); } } //设置播放模式 SetPlaybackMode(PM_FILE); }
从OpenFile()函数的源代码我们可以看出,mpc-hc调用了DirectShow的函数,打开相应的文件。比如说:
HRESULT hr = m_pGB->RenderFile(CStringW(fn), nullptr);
本文补充介绍CMainFrame类中的其他一些函数。
再回顾一下打开文件功能主要所在的函数OpenMediaPrivate():
//打开一个媒体(private) bool CMainFrame::OpenMediaPrivate(CAutoPtr<OpenMediaData> pOMD) { //获得设置信息 CAppSettings& s = AfxGetAppSettings(); if (m_iMediaLoadState != MLS_CLOSED) { ASSERT(0); return false; } //OpenFileData //OpenDVDData //OpenDeviceData //里面包含了文件或者DVD信息(名称等) OpenFileData* pFileData = dynamic_cast<OpenFileData*>(pOMD.m_p); OpenDVDData* pDVDData = dynamic_cast<OpenDVDData*>(pOMD.m_p); OpenDeviceData* pDeviceData = dynamic_cast<OpenDeviceData*>(pOMD.m_p); if (!pFileData && !pDVDData && !pDeviceData) { ASSERT(0); return false; } // Clear DXVA state ... ClearDXVAState(); #ifdef _DEBUG // Debug trace code - Begin // Check for bad / buggy auto loading file code if (pFileData) { POSITION pos = pFileData->fns.GetHeadPosition(); UINT index = 0; while (pos != nullptr) { CString path = pFileData->fns.GetNext(pos); TRACE(_T("--> CMainFrame::OpenMediaPrivate - pFileData->fns[%d]:\n"), index); TRACE(_T("\t%ws\n"), path.GetString()); // %ws - wide character string always index++; } } // Debug trace code - End #endif CString mi_fn = _T(""); if (pFileData) { if (pFileData->fns.IsEmpty()) { return false; } CString fn = pFileData->fns.GetHead(); int i = fn.Find(_T(":\\")); if (i > 0) { CString drive = fn.Left(i + 2); UINT type = GetDriveType(drive); CAtlList<CString> sl; if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM && GetCDROMType(drive[0], sl) != CDROM_Audio) { int ret = IDRETRY; while (ret == IDRETRY) { WIN32_FIND_DATA findFileData; HANDLE h = FindFirstFile(fn, &findFileData); if (h != INVALID_HANDLE_VALUE) { FindClose(h); ret = IDOK; } else { CString msg; msg.Format(IDS_MAINFRM_114, fn); ret = AfxMessageBox(msg, MB_RETRYCANCEL); } } if (ret != IDOK) { return false; } } mi_fn = fn; } } SetLoadState(MLS_LOADING); // FIXME: Don't show "Closed" initially PostMessage(WM_KICKIDLE); CString err; m_fUpdateInfoBar = false; BeginWaitCursor(); try { CComPtr<IVMRMixerBitmap9> pVMB; CComPtr<IMFVideoMixerBitmap> pMFVMB; CComPtr<IMadVRTextOsd> pMVTO; if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } OpenCreateGraphObject(pOMD); if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } SetupIViAudReg(); if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } //按类型的不同打开不同的文件 if (pFileData) { //文件 OpenFile(pFileData); } else if (pDVDData) { //DVD OpenDVD(pDVDData); } else if (pDeviceData) { if (s.iDefaultCaptureDevice == 1) { HRESULT hr = OpenBDAGraph(); if (FAILED(hr)) { throw (UINT)IDS_CAPTURE_ERROR_DEVICE; } } else { OpenCapture(pDeviceData); } } else { throw (UINT)IDS_INVALID_PARAMS_ERROR; } m_pCAP2 = nullptr; m_pCAP = nullptr; //查找接口 m_pGB->FindInterface(__uuidof(ISubPicAllocatorPresenter), (void**)&m_pCAP, TRUE); m_pGB->FindInterface(__uuidof(ISubPicAllocatorPresenter2), (void**)&m_pCAP2, TRUE); m_pGB->FindInterface(__uuidof(IVMRWindowlessControl9), (void**)&m_pVMRWC, FALSE); // might have IVMRMixerBitmap9, but not IVMRWindowlessControl9 m_pGB->FindInterface(__uuidof(IVMRMixerControl9), (void**)&m_pVMRMC, TRUE); m_pGB->FindInterface(__uuidof(IVMRMixerBitmap9), (void**)&pVMB, TRUE); m_pGB->FindInterface(__uuidof(IMFVideoMixerBitmap), (void**)&pMFVMB, TRUE); pMVTO = m_pCAP; if (s.fShowOSD || s.fShowDebugInfo) { // Force OSD on when the debug switch is used if (pVMB) { m_OSD.Start(m_pVideoWnd, pVMB, IsD3DFullScreenMode()); } else if (pMFVMB) { m_OSD.Start(m_pVideoWnd, pMFVMB, IsD3DFullScreenMode()); } else if (pMVTO) { m_OSD.Start(m_pVideoWnd, pMVTO); } } //VMR9 SetupVMR9ColorControl(); // === EVR ! m_pGB->FindInterface(__uuidof(IMFVideoDisplayControl), (void**)&m_pMFVDC, TRUE); m_pGB->FindInterface(__uuidof(IMFVideoProcessor), (void**)&m_pMFVP, TRUE); if (m_pMFVDC) { m_pMFVDC->SetVideoWindow(m_pVideoWnd->m_hWnd); } //SetupEVRColorControl(); //does not work at this location //need to choose the correct mode (IMFVideoProcessor::SetVideoProcessorMode) BeginEnumFilters(m_pGB, pEF, pBF) { if (m_pLN21 = pBF) { m_pLN21->SetServiceState(s.fClosedCaptions ? AM_L21_CCSTATE_On : AM_L21_CCSTATE_Off); break; } } EndEnumFilters; if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } //打开自定义的Graph OpenCustomizeGraph(); if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } //设置视频窗口 OpenSetupVideo(); if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } //设置音量 OpenSetupAudio(); if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } if (m_pCAP && (!m_fAudioOnly || m_fRealMediaGraph)) { if (s.fDisableInternalSubtitles) { m_pSubStreams.RemoveAll(); // Needs to be replaced with code that checks for forced subtitles. } m_posFirstExtSub = nullptr; POSITION pos = pOMD->subs.GetHeadPosition(); while (pos) { LoadSubtitle(pOMD->subs.GetNext(pos), nullptr, true); } } if (m_fOpeningAborted) { throw (UINT)IDS_AG_ABORTED; } //设置视频窗口标题 OpenSetupWindowTitle(pOMD->title); if (s.fEnableEDLEditor) { m_wndEditListEditor.OpenFile(pOMD->title); } if (::GetCurrentThreadId() == AfxGetApp()->m_nThreadID) { OnFilePostOpenmedia(); } else { PostMessage(WM_COMMAND, ID_FILE_POST_OPENMEDIA); } while (m_iMediaLoadState != MLS_LOADED && m_iMediaLoadState != MLS_CLOSING // FIXME ) { Sleep(50); } //设置音频流 DWORD audstm = SetupAudioStreams(); //设置字幕流 DWORD substm = SetupSubtitleStreams(); if (audstm) { OnPlayAudio(ID_AUDIO_SUBITEM_START + audstm); } if (substm) { SetSubtitle(substm - 1); } // PostMessage instead of SendMessage because the user might call CloseMedia and then we would deadlock PostMessage(WM_COMMAND, ID_PLAY_PAUSE); m_bFirstPlay = true; if (!(s.nCLSwitches & CLSW_OPEN) && (s.nLoops > 0)) { PostMessage(WM_COMMAND, ID_PLAY_PLAY); } else { // If we don't start playing immediately, we need to initialize // the seekbar and the time counter. OnTimer(TIMER_STREAMPOSPOLLER); OnTimer(TIMER_STREAMPOSPOLLER2); } s.nCLSwitches &= ~CLSW_OPEN; if (pFileData) { if (pFileData->rtStart > 0) { PostMessage(WM_RESUMEFROMSTATE, (WPARAM)PM_FILE, (LPARAM)(pFileData->rtStart / 10000)); // REFERENCE_TIME doesn't fit in LPARAM under a 32bit env. } } else if (pDVDData) { if (pDVDData->pDvdState) { PostMessage(WM_RESUMEFROMSTATE, (WPARAM)PM_DVD, (LPARAM)(CComPtr<IDvdState>(pDVDData->pDvdState).Detach())); // must be released by the called message handler } } else if (pDeviceData) { m_wndCaptureBar.m_capdlg.SetVideoInput(pDeviceData->vinput); m_wndCaptureBar.m_capdlg.SetVideoChannel(pDeviceData->vchannel); m_wndCaptureBar.m_capdlg.SetAudioInput(pDeviceData->ainput); } } catch (LPCTSTR msg) { err = msg; } catch (CString& msg) { err = msg; } catch (UINT msg) { err.LoadString(msg); } EndWaitCursor(); if (!err.IsEmpty()) { //关闭 CloseMediaPrivate(); m_closingmsg = err; if (err != ResStr(IDS_AG_ABORTED)) { if (pFileData) { m_wndPlaylistBar.SetCurValid(false); if (m_wndPlaylistBar.IsAtEnd()) { m_nLoops++; } if (s.fLoopForever || m_nLoops < s.nLoops) { bool hasValidFile = false; if (m_nLastSkipDirection == ID_NAVIGATE_SKIPBACK) { hasValidFile = m_wndPlaylistBar.SetPrev(); } else { hasValidFile = m_wndPlaylistBar.SetNext(); } if (hasValidFile) { OpenCurPlaylistItem(); } } else if (m_wndPlaylistBar.GetCount() > 1) { DoAfterPlaybackEvent(); } } else { OnNavigateSkip(ID_NAVIGATE_SKIPFORWARD); } } } else { m_wndPlaylistBar.SetCurValid(true); // Apply command line audio shift if (s.rtShift != 0) { SetAudioDelay(s.rtShift); s.rtShift = 0; } } m_nLastSkipDirection = 0; if (s.AutoChangeFullscrRes.bEnabled && (m_fFullScreen || IsD3DFullScreenMode())) { AutoChangeMonitorMode(); } if (m_fFullScreen && s.fRememberZoomLevel) { m_fFirstFSAfterLaunchOnFS = true; } m_LastOpenFile = pOMD->title; PostMessage(WM_KICKIDLE); // calls main thread to update things if (!m_bIsBDPlay) { m_MPLSPlaylist.RemoveAll(); m_LastOpenBDPath = _T(""); } m_bIsBDPlay = false; return err.IsEmpty(); }
来看一看OpenMediaPrivate()函数的细节:
1.开始的时候有这么一句
CAppSettings& s = AfxGetAppSettings();
在这里涉及到一个类CAppSettings,存储的是mpc-hc用到的各种设置信息。源代码如下:
//应用程序中的各种参数 class CAppSettings { bool fInitialized; class CRecentFileAndURLList : public CRecentFileList { public: CRecentFileAndURLList(UINT nStart, LPCTSTR lpszSection, LPCTSTR lpszEntryFormat, int nSize, int nMaxDispLen = AFX_ABBREV_FILENAME_LEN); virtual void Add(LPCTSTR lpszPathName); // we have to override CRecentFileList::Add because the original version can't handle URLs }; public: bool fShaderEditorWasOpened; // cmdline params UINT nCLSwitches; CAtlList<CString> slFiles, slDubs, slSubs, slFilters; // Initial position (used by command line flags) REFERENCE_TIME rtShift; REFERENCE_TIME rtStart; ULONG lDVDTitle; ULONG lDVDChapter; DVD_HMSF_TIMECODE DVDPosition; CSize sizeFixedWindow; bool HasFixedWindowSize() const { return sizeFixedWindow.cx > 0 || sizeFixedWindow.cy > 0; } //int iFixedWidth, iFixedHeight; int iMonitor; CString ParseFileName(CString const& param); void ParseCommandLine(CAtlList<CString>& cmdln); // Added a Debug display to the screen (/debug option) bool fShowDebugInfo; int iAdminOption; //播放器 Player bool fAllowMultipleInst; bool fTrayIcon; bool fShowOSD; bool fLimitWindowProportions; bool fSnapToDesktopEdges; bool fHideCDROMsSubMenu; DWORD dwPriority; int iTitleBarTextStyle; bool fTitleBarTextTitle; bool fKeepHistory; CRecentFileAndURLList MRU; CRecentFileAndURLList MRUDub; CFilePositionList filePositions; CDVDPositionList dvdPositions; bool fRememberDVDPos; bool fRememberFilePos; bool bRememberPlaylistItems; bool fRememberWindowPos; CRect rcLastWindowPos; bool fRememberWindowSize; bool fSavePnSZoom; double dZoomX; double dZoomY; // Formats CMediaFormats m_Formats; bool fAssociatedWithIcons; // Keys CList<wmcmd> wmcmds; HACCEL hAccel; bool fWinLirc; CString strWinLircAddr; CWinLircClient WinLircClient; bool fUIce; CString strUIceAddr; CUIceClient UIceClient; bool fGlobalMedia; //图标 Logo UINT nLogoId; bool fLogoExternal; CString strLogoFileName; //web界面? Web Inteface BOOL fEnableWebServer; int nWebServerPort; int nCmdlnWebServerPort; bool fWebServerUseCompression; bool fWebServerLocalhostOnly; bool fWebServerPrintDebugInfo; CString strWebRoot, strWebDefIndex; CString strWebServerCGI; //播放时候 Playback int nVolume; bool fMute; int nBalance; int nLoops; bool fLoopForever; bool fRewind; bool fRememberZoomLevel; int nAutoFitFactor; int iZoomLevel; CStringW strAudiosLanguageOrder; CStringW strSubtitlesLanguageOrder; bool fEnableWorkerThreadForOpening; bool fReportFailedPins; bool fAutoloadAudio; bool fAutoloadSubtitles; bool fBlockVSFilter; UINT nVolumeStep; UINT nSpeedStep; // DVD/OGM bool fUseDVDPath; CString strDVDPath; LCID idMenuLang, idAudioLang, idSubtitlesLang; bool fAutoSpeakerConf; bool fClosedCaptions; //输出 Output CRenderersSettings m_RenderersSettings; int iDSVideoRendererType; int iRMVideoRendererType; int iQTVideoRendererType; CStringW strAudioRendererDisplayName; bool fD3DFullscreen; //全屏 Fullscreen bool fLaunchfullscreen; bool fShowBarsWhenFullScreen; int nShowBarsWhenFullScreenTimeOut; bool fExitFullScreenAtTheEnd; CStringW strFullScreenMonitor; AChFR AutoChangeFullscrRes; bool fRestoreResAfterExit; // Sync Renderer Settings // Capture (BDA configuration) int iDefaultCaptureDevice; // Default capture device (analog=0, 1=digital) CString strAnalogVideo; CString strAnalogAudio; int iAnalogCountry; CString strBDANetworkProvider; CString strBDATuner; CString strBDAReceiver; //CString strBDAStandard; int iBDAScanFreqStart; int iBDAScanFreqEnd; int iBDABandwidth; bool fBDAUseOffset; int iBDAOffset; bool fBDAIgnoreEncryptedChannels; UINT nDVBLastChannel; CAtlList<CDVBChannel> m_DVBChannels; DVB_RebuildFilterGraph nDVBRebuildFilterGraph; DVB_StopFilterGraph nDVBStopFilterGraph; // Internal Filters bool SrcFilters[SRC_LAST + !SRC_LAST]; bool TraFilters[TRA_LAST + !TRA_LAST]; //音频 Audio Switcher bool fEnableAudioSwitcher; bool fAudioNormalize; UINT nAudioMaxNormFactor; bool fAudioNormalizeRecover; UINT nAudioBoost; bool fDownSampleTo441; bool fAudioTimeShift; int iAudioTimeShift; bool fCustomChannelMapping; int nSpeakerChannels; DWORD pSpeakerToChannelMap[AS_MAX_CHANNELS][AS_MAX_CHANNELS]; // External Filters CAutoPtrList<FilterOverride> m_filters; //字幕 Subtitles bool fOverridePlacement; int nHorPos, nVerPos; int nSubDelayInterval; // Default Style STSStyle subdefstyle; // Misc bool bPreferDefaultForcedSubtitles; bool fPrioritizeExternalSubtitles; bool fDisableInternalSubtitles; bool bAllowOverridingExternalSplitterChoice; CString strSubtitlePaths; CString strISDb; // Tweaks int nJumpDistS; int nJumpDistM; int nJumpDistL; bool fFastSeek; bool fShowChapters; bool bNotifySkype; bool fPreventMinimize; bool fUseWin7TaskBar; bool fLCDSupport; bool fUseSearchInFolder; bool fUseTimeTooltip; int nTimeTooltipPosition; CString strOSDFont; int nOSDSize; //亮度色度饱和度 Miscellaneous int iBrightness; int iContrast; int iHue; int iSaturation; int nUpdaterAutoCheck; int nUpdaterDelay; // MENUS // View int iCaptionMenuMode; // normal -> hidemenu -> frameonly -> borderless bool fHideNavigation; UINT nCS; // Control state for toolbars // Language LANGID language; // Subtitles menu bool fEnableSubtitles; bool fUseDefaultSubtitlesStyle; // Video Frame int iDefaultVideoSize; bool fKeepAspectRatio; CSize sizeAspectRatio; bool fCompMonDeskARDiff; // Pan&Scan CString strPnSPreset; CStringArray m_pnspresets; // On top menu int iOnTop; // After Playback bool fExitAfterPlayback; bool fNextInDirAfterPlayback; // WINDOWS // Add Favorite bool bFavRememberPos; bool bFavRelativeDrive; // Save Image... CString strSnapShotPath, strSnapShotExt; // Save Thumbnails... int iThumbRows, iThumbCols, iThumbWidth; // Shader Editor struct Shader { CString label; CString target; CString srcdata; }; CAtlList<Shader> m_shaders; // Shader Combiner bool fToggleShader; bool fToggleShaderScreenSpace; CString strShaderList; CString strShaderListScreenSpace; // Playlist (contex menu) bool bShufflePlaylistItems; bool bHidePlaylistFullScreen; // OTHER STATES CStringW strLastOpenDir; UINT nLastWindowType; UINT nLastUsedPage; bool fRemainingTime; bool fLastFullScreen; bool fIntRealMedia; //bool fRealMediaRenderless; //float dRealMediaQuickTimeFPS; //int iVideoRendererType; //int iQuickTimeRenderer; //bool fMonitorAutoRefreshRate; bool fEnableEDLEditor; HWND hMasterWnd; bool IsD3DFullscreen() const; CString SelectedAudioRenderer() const; bool IsISREnabled() const; private: CString SrcFiltersKeys[SRC_LAST + !SRC_LAST]; CString TraFiltersKeys[TRA_LAST + !TRA_LAST]; __int64 ConvertTimeToMSec(const CString& time) const; void ExtractDVDStartPos(CString& strParam); void CreateCommands(); void SaveExternalFilters(CAutoPtrList<FilterOverride>& filters, LPCTSTR baseKey = IDS_R_EXTERNAL_FILTERS); void LoadExternalFilters(CAutoPtrList<FilterOverride>& filters, LPCTSTR baseKey = IDS_R_EXTERNAL_FILTERS); void ConvertOldExternalFiltersList(); void UpdateRenderersData(bool fSave); friend void CRenderersSettings::UpdateData(bool bSave); public: CAppSettings(); virtual ~CAppSettings(); void SaveSettings(); void LoadSettings(); void SaveExternalFilters() { if (fInitialized) { SaveExternalFilters(m_filters); } }; void GetFav(favtype ft, CAtlList<CString>& sl) const; void SetFav(favtype ft, CAtlList<CString>& sl); void AddFav(favtype ft, CString s); CDVBChannel* FindChannelByPref(int nPrefNumber); bool GetAllowMultiInst() const; static bool IsVSFilterInstalled(); static bool HasEVR(); };
由代码可见,包含的参数信息很多。在mpc-hc中,任何需要获取设置信息的地方,都可以使用AfxGetAppSettings()获得CAppSettings的引用。
2.OpenSetupVideo()这个函数的作用是设置视频窗口,源代码如下:
//设置视频窗口 void CMainFrame::OpenSetupVideo() { //大部分都在确定:m_fAudioOnly是否为True m_fAudioOnly = true; //获得视频的宽和高,然后调整窗口大小 if (m_pMFVDC) { // EVR m_fAudioOnly = false; } else if (m_pCAP) { CSize vs = m_pCAP->GetVideoSize(); m_fAudioOnly = (vs.cx <= 0 || vs.cy <= 0); } else { { long w = 0, h = 0; if (CComQIPtr<IBasicVideo> pBV = m_pGB) { pBV->GetVideoSize(&w, &h); } if (w > 0 && h > 0) { m_fAudioOnly = false; } } //如果 m_fAudioOnly=true;再检查 if (m_fAudioOnly) { BeginEnumFilters(m_pGB, pEF, pBF) { long w = 0, h = 0; if (CComQIPtr<IVideoWindow> pVW = pBF) { long lVisible; if (FAILED(pVW->get_Visible(&lVisible))) { continue; } pVW->get_Width(&w); pVW->get_Height(&h); } if (w > 0 && h > 0) { m_fAudioOnly = false; break; } } EndEnumFilters; } } if (m_fShockwaveGraph) { m_fAudioOnly = false; } if (m_pCAP) { SetShaders(); } // else { // TESTME //设置所有者。。。 m_pVW->put_Owner((OAHWND)m_pVideoWnd->m_hWnd); m_pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); m_pVW->put_MessageDrain((OAHWND)m_hWnd); for (CWnd* pWnd = m_wndView.GetWindow(GW_CHILD); pWnd; pWnd = pWnd->GetNextWindow()) { pWnd->EnableWindow(FALSE); // little trick to let WM_SETCURSOR thru } } //如果只有音频,则消灭视频窗口! if (m_fAudioOnly && IsD3DFullScreenMode()) { m_pFullscreenWnd->DestroyWindow(); } }
3.OpenSetupAudio()这个函数的作用是设置音频,源代码如下:
//设置音量 void CMainFrame::OpenSetupAudio() { //设置音量 m_pBA->put_Volume(m_wndToolBar.Volume); // FIXME int balance = AfxGetAppSettings().nBalance; int sign = balance > 0 ? -1 : 1; // -1: invert sign for more right channel if (balance > -100 && balance < 100) { balance = sign * (int)(100 * 20 * log10(1 - abs(balance) / 100.0f)); } else { balance = sign * (-10000); // -10000: only left, 10000: only right } //设置均衡 m_pBA->put_Balance(balance); }
4.如果出现问题,则会调用CloseMediaPrivate(),关闭打开的媒体。
//关闭 void CMainFrame::CloseMediaPrivate() { SetLoadState(MLS_CLOSING); // why it before OnPlayStop()? // TODO: remake or add detailed comments OnPlayStop(); // SendMessage(WM_COMMAND, ID_PLAY_STOP); if (m_pMC) { m_pMC->Stop(); // needed for StreamBufferSource, because m_iMediaLoadState is always MLS_CLOSED // TODO: fix the opening for such media } SetPlaybackMode(PM_NONE); m_fLiveWM = false; m_fEndOfStream = false; m_rtDurationOverride = -1; m_kfs.RemoveAll(); m_pCB.Release(); { CAutoLock cAutoLock(&m_csSubLock); m_pSubStreams.RemoveAll(); } m_pSubClock.Release(); //if (m_pVW) m_pVW->put_Visible(OAFALSE); //if (m_pVW) m_pVW->put_MessageDrain((OAHWND)NULL), m_pVW->put_Owner((OAHWND)NULL); // IMPORTANT: IVMRSurfaceAllocatorNotify/IVMRSurfaceAllocatorNotify9 has to be released before the VMR/VMR9, otherwise it will crash in Release() //各种清空 m_OSD.Stop(); m_pCAP2.Release(); m_pCAP.Release(); m_pVMRWC.Release(); m_pVMRMC.Release(); m_pMFVP.Release(); m_pMFVDC.Release(); m_pLN21.Release(); m_pSyncClock.Release(); m_pAMXBar.Release(); m_pAMDF.Release(); m_pAMVCCap.Release(); m_pAMVCPrev.Release(); m_pAMVSCCap.Release(); m_pAMVSCPrev.Release(); m_pAMASC.Release(); m_pVidCap.Release(); m_pAudCap.Release(); m_pAMTuner.Release(); m_pCGB.Release(); m_pDVDC.Release(); m_pDVDI.Release(); m_pAMOP.Release(); m_pBI.Release(); m_pQP.Release(); m_pFS.Release(); m_pMS.Release(); m_pBA.Release(); m_pBV.Release(); m_pVW.Release(); m_pME.Release(); m_pMC.Release(); if (m_pGB) { m_pGB->RemoveFromROT(); m_pGB.Release(); } m_pProv.Release(); m_fRealMediaGraph = m_fShockwaveGraph = m_fQuicktimeGraph = false; m_VidDispName.Empty(); m_AudDispName.Empty(); m_closingmsg.LoadString(IDS_CONTROLS_CLOSED); AfxGetAppSettings().nCLSwitches &= CLSW_OPEN | CLSW_PLAY | CLSW_AFTERPLAYBACK_MASK | CLSW_NOFOCUS; //设置状态 SetLoadState(MLS_CLOSED); }
此前的文章一直都是围绕着OpenMedia()以及其调用的函数进行分析的。研究的都是和文件打开有关系的功能。在这里再介绍一些其它函数。
在mpc-hc开始运行的时候,会调用OnCreate():
//刚打开的时候加载 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (__super::OnCreate(lpCreateStruct) == -1) { return -1; } //加载菜单 m_popup.LoadMenu(IDR_POPUP); m_popupmain.LoadMenu(IDR_POPUPMAIN); // create a view to occupy the client area of the frame // 创建视频画面部分? if (!m_wndView.Create(nullptr, nullptr, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, nullptr)) { TRACE(_T("Failed to create view window\n")); return -1; } // Should never be RTLed m_wndView.ModifyStyleEx(WS_EX_LAYOUTRTL, WS_EX_NOINHERITLAYOUT); // static bars //各种状态栏 BOOL bResult = m_wndStatusBar.Create(this); if (bResult) { bResult = m_wndStatsBar.Create(this); } if (bResult) { bResult = m_wndInfoBar.Create(this); } if (bResult) { bResult = m_wndToolBar.Create(this); } if (bResult) { bResult = m_wndSeekBar.Create(this); } if (!bResult) { TRACE(_T("Failed to create all control bars\n")); return -1; // fail to create } // 各种Bar m_pFullscreenWnd = DEBUG_NEW CFullscreenWnd(this); m_bars.AddTail(&m_wndSeekBar); m_bars.AddTail(&m_wndToolBar); m_bars.AddTail(&m_wndInfoBar); m_bars.AddTail(&m_wndStatsBar); m_bars.AddTail(&m_wndStatusBar); m_wndSeekBar.Enable(false); // dockable bars // 可停靠的 EnableDocking(CBRS_ALIGN_ANY); m_dockingbars.RemoveAll(); m_wndSubresyncBar.Create(this, AFX_IDW_DOCKBAR_TOP, &m_csSubLock); m_wndSubresyncBar.SetBarStyle(m_wndSubresyncBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); m_wndSubresyncBar.EnableDocking(CBRS_ALIGN_ANY); m_wndSubresyncBar.SetHeight(200); m_dockingbars.AddTail(&m_wndSubresyncBar); m_wndPlaylistBar.Create(this, AFX_IDW_DOCKBAR_BOTTOM); m_wndPlaylistBar.SetBarStyle(m_wndPlaylistBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); m_wndPlaylistBar.EnableDocking(CBRS_ALIGN_ANY); m_wndPlaylistBar.SetHeight(100); m_dockingbars.AddTail(&m_wndPlaylistBar); m_wndPlaylistBar.LoadPlaylist(GetRecentFile()); m_wndEditListEditor.Create(this, AFX_IDW_DOCKBAR_RIGHT); m_wndEditListEditor.SetBarStyle(m_wndEditListEditor.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); m_wndEditListEditor.EnableDocking(CBRS_ALIGN_ANY); m_dockingbars.AddTail(&m_wndEditListEditor); m_wndEditListEditor.SetHeight(100); m_wndCaptureBar.Create(this, AFX_IDW_DOCKBAR_LEFT); m_wndCaptureBar.SetBarStyle(m_wndCaptureBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); m_wndCaptureBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT); m_dockingbars.AddTail(&m_wndCaptureBar); m_wndNavigationBar.Create(this, AFX_IDW_DOCKBAR_LEFT); m_wndNavigationBar.SetBarStyle(m_wndNavigationBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); m_wndNavigationBar.EnableDocking(CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT); m_dockingbars.AddTail(&m_wndNavigationBar); m_wndShaderEditorBar.Create(this, AFX_IDW_DOCKBAR_TOP); m_wndShaderEditorBar.SetBarStyle(m_wndShaderEditorBar.GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); m_wndShaderEditorBar.EnableDocking(CBRS_ALIGN_ANY); m_dockingbars.AddTail(&m_wndShaderEditorBar); // Hide all dockable bars by default POSITION pos = m_dockingbars.GetHeadPosition(); while (pos) { m_dockingbars.GetNext(pos)->ShowWindow(SW_HIDE); } m_fileDropTarget.Register(this); const CAppSettings& s = AfxGetAppSettings(); // Load the controls m_nCS = s.nCS; ShowControls(m_nCS); //是否在最前? SetAlwaysOnTop(s.iOnTop); //显示系统托盘图标 ShowTrayIcon(s.fTrayIcon); //焦点 SetFocus(); //创建CGraphThread类的线程 m_pGraphThread = (CGraphThread*)AfxBeginThread(RUNTIME_CLASS(CGraphThread)); //设置。。 if (m_pGraphThread) { m_pGraphThread->SetMainFrame(this); } if (s.nCmdlnWebServerPort != 0) { if (s.nCmdlnWebServerPort > 0) { StartWebServer(s.nCmdlnWebServerPort); } else if (s.fEnableWebServer) { StartWebServer(s.nWebServerPort); } } // Casimir666 : reload Shaders { CString strList = s.strShaderList; CString strRes; int curPos = 0; strRes = strList.Tokenize(_T("|"), curPos); while (!strRes.IsEmpty()) { m_shaderlabels.AddTail(strRes); strRes = strList.Tokenize(_T("|"), curPos); } } { CString strList = s.strShaderListScreenSpace; CString strRes; int curPos = 0; strRes = strList.Tokenize(_T("|"), curPos); while (!strRes.IsEmpty()) { m_shaderlabelsScreenSpace.AddTail(strRes); strRes = strList.Tokenize(_T("|"), curPos); } } m_bToggleShader = s.fToggleShader; m_bToggleShaderScreenSpace = s.fToggleShaderScreenSpace; //标题 m_strTitle.LoadString(IDR_MAINFRAME); #ifdef MPCHC_LITE m_strTitle += _T(" Lite"); #endif //设置窗口标题 SetWindowText(m_strTitle); m_Lcd.SetMediaTitle(LPCTSTR(m_strTitle)); WTSRegisterSessionNotification(); if (s.bNotifySkype) { m_pSkypeMoodMsgHandler.Attach(DEBUG_NEW SkypeMoodMsgHandler()); m_pSkypeMoodMsgHandler->Connect(m_hWnd); } return 0; }
在mpc-hc关闭的时候,会调用OnDestroy():
//关闭的时候加载 void CMainFrame::OnDestroy() { WTSUnRegisterSessionNotification(); //关闭系统托盘图标 ShowTrayIcon(false); m_fileDropTarget.Revoke(); //线程还在运行的话 if (m_pGraphThread) { CAMMsgEvent e; //退出 m_pGraphThread->PostThreadMessage(CGraphThread::TM_EXIT, 0, (LPARAM)&e); if (!e.Wait(5000)) { TRACE(_T("ERROR: Must call TerminateThread() on CMainFrame::m_pGraphThread->m_hThread\n")); TerminateThread(m_pGraphThread->m_hThread, (DWORD) - 1); } } if (m_pFullscreenWnd) { if (m_pFullscreenWnd->IsWindow()) { m_pFullscreenWnd->DestroyWindow(); } delete m_pFullscreenWnd; } __super::OnDestroy(); }
在关闭一个媒体的时候,会调用OnClose():
//关闭的时候加载 void CMainFrame::OnClose() { //获取设置 CAppSettings& s = AfxGetAppSettings(); // Casimir666 : save shaders list { POSITION pos; CString strList = ""; pos = m_shaderlabels.GetHeadPosition(); while (pos) { strList += m_shaderlabels.GetAt(pos) + "|"; m_dockingbars.GetNext(pos); } s.strShaderList = strList; } { POSITION pos; CString strList = ""; pos = m_shaderlabelsScreenSpace.GetHeadPosition(); while (pos) { strList += m_shaderlabelsScreenSpace.GetAt(pos) + "|"; m_dockingbars.GetNext(pos); } s.strShaderListScreenSpace = strList; } s.fToggleShader = m_bToggleShader; s.fToggleShaderScreenSpace = m_bToggleShaderScreenSpace; s.dZoomX = m_ZoomX; s.dZoomY = m_ZoomY; //存储播放列表 m_wndPlaylistBar.SavePlaylist(); //存储控制条 SaveControlBars(); ShowWindow(SW_HIDE); //关闭媒体(非private) CloseMedia(); s.WinLircClient.DisConnect(); s.UIceClient.DisConnect(); SendAPICommand(CMD_DISCONNECT, L"\0"); // according to CMD_NOTIFYENDOFSTREAM (ctrl+f it here), you're not supposed to send NULL here //调用父类onclose __super::OnClose(); }
同时还有一个定时器函数OnTimer()。根据不同的nIDEvent做不同的处理操作。
//定时刷新的操作 void CMainFrame::OnTimer(UINT_PTR nIDEvent) { switch (nIDEvent) { //当前播放到的位置 case TIMER_STREAMPOSPOLLER: if (m_iMediaLoadState == MLS_LOADED) { REFERENCE_TIME rtNow = 0, rtDur = 0; //播放方式是文件的时候(还可以是DVD或者摄像头) if (GetPlaybackMode() == PM_FILE) { //当前位置 m_pMS->GetCurrentPosition(&rtNow); //时常 m_pMS->GetDuration(&rtDur); // Casimir666 : autosave subtitle sync after play if ((m_nCurSubtitle >= 0) && (m_rtCurSubPos != rtNow)) { if (m_lSubtitleShift != 0) { if (m_wndSubresyncBar.SaveToDisk()) { m_OSD.DisplayMessage(OSD_TOPLEFT, ResStr(IDS_AG_SUBTITLES_SAVED), 500); } else { m_OSD.DisplayMessage(OSD_TOPLEFT, ResStr(IDS_MAINFRM_4)); } } m_nCurSubtitle = -1; m_lSubtitleShift = 0; } if (!m_fEndOfStream) { CAppSettings& s = AfxGetAppSettings(); if (m_bRememberFilePos) { FILE_POSITION* filePosition = s.filePositions.GetLatestEntry(); if (filePosition) { filePosition->llPosition = rtNow; LARGE_INTEGER time; QueryPerformanceCounter(&time); LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); if ((time.QuadPart - m_liLastSaveTime.QuadPart) >= 30 * freq.QuadPart) { // save every half of minute m_liLastSaveTime = time; s.filePositions.SaveLatestEntry(); } } } } if (m_rtDurationOverride >= 0) { rtDur = m_rtDurationOverride; } //设置滑动条控件的参数(位置等。。。) g_bNoDuration = rtDur <= 0; m_wndSeekBar.Enable(rtDur > 0); m_wndSeekBar.SetRange(0, rtDur); m_wndSeekBar.SetPos(rtNow); m_OSD.SetRange(0, rtDur); m_OSD.SetPos(rtNow); m_Lcd.SetMediaRange(0, rtDur); m_Lcd.SetMediaPos(rtNow); } else if (GetPlaybackMode() == PM_CAPTURE) { //如果是摄像头的话,就没有时长信息了 m_pMS->GetCurrentPosition(&rtNow); if (m_fCapturing && m_wndCaptureBar.m_capdlg.m_pMux) { CComQIPtr<IMediaSeeking> pMuxMS = m_wndCaptureBar.m_capdlg.m_pMux; if (!pMuxMS || FAILED(pMuxMS->GetCurrentPosition(&rtNow))) { rtNow = 0; } } if (m_rtDurationOverride >= 0) { rtDur = m_rtDurationOverride; } g_bNoDuration = rtDur <= 0; m_wndSeekBar.Enable(false); m_wndSeekBar.SetRange(0, rtDur); m_wndSeekBar.SetPos(rtNow); m_OSD.SetRange(0, rtDur); m_OSD.SetPos(rtNow); m_Lcd.SetMediaRange(0, rtDur); m_Lcd.SetMediaPos(rtNow); } if (m_pCAP && GetPlaybackMode() != PM_FILE) { g_bExternalSubtitleTime = true; if (m_pDVDI) { DVD_PLAYBACK_LOCATION2 Location; if (m_pDVDI->GetCurrentLocation(&Location) == S_OK) { double fps = Location.TimeCodeFlags == DVD_TC_FLAG_25fps ? 25.0 : Location.TimeCodeFlags == DVD_TC_FLAG_30fps ? 30.0 : Location.TimeCodeFlags == DVD_TC_FLAG_DropFrame ? 29.97 : 25.0; REFERENCE_TIME rtTimeCode = HMSF2RT(Location.TimeCode, fps); m_pCAP->SetTime(rtTimeCode); } else { m_pCAP->SetTime(/*rtNow*/m_wndSeekBar.GetPos()); } } else { // Set rtNow to support DVB subtitle m_pCAP->SetTime(rtNow); } } else { g_bExternalSubtitleTime = false; } } break; case TIMER_STREAMPOSPOLLER2: if (m_iMediaLoadState == MLS_LOADED) { __int64 start, stop, pos; m_wndSeekBar.GetRange(start, stop); pos = m_wndSeekBar.GetPosReal(); GUID tf; m_pMS->GetTimeFormat(&tf); if (GetPlaybackMode() == PM_CAPTURE && !m_fCapturing) { CString str = ResStr(IDS_CAPTURE_LIVE); long lChannel = 0, lVivSub = 0, lAudSub = 0; if (m_pAMTuner && m_wndCaptureBar.m_capdlg.IsTunerActive() && SUCCEEDED(m_pAMTuner->get_Channel(&lChannel, &lVivSub, &lAudSub))) { CString ch; ch.Format(_T(" (ch%d)"), lChannel); str += ch; } m_wndStatusBar.SetStatusTimer(str); } else { m_wndStatusBar.SetStatusTimer(pos, stop, !!m_wndSubresyncBar.IsWindowVisible(), &tf); if (m_bRemainingTime) { m_OSD.DisplayMessage(OSD_TOPLEFT, m_wndStatusBar.GetStatusTimer()); } } m_wndSubresyncBar.SetTime(pos); if (m_pCAP && GetMediaState() == State_Paused) { m_pCAP->Paint(false); } } break; case TIMER_FULLSCREENCONTROLBARHIDER: { CPoint p; GetCursorPos(&p); CRect r; GetWindowRect(r); bool fCursorOutside = !r.PtInRect(p); CWnd* pWnd = WindowFromPoint(p); if (pWnd && (m_wndView == *pWnd || m_wndView.IsChild(pWnd) || fCursorOutside)) { if (AfxGetAppSettings().nShowBarsWhenFullScreenTimeOut >= 0) { ShowControls(CS_NONE); } } } break; case TIMER_FULLSCREENMOUSEHIDER: { CPoint p; GetCursorPos(&p); CRect r; GetWindowRect(r); bool fCursorOutside = !r.PtInRect(p); CWnd* pWnd = WindowFromPoint(p); if (IsD3DFullScreenMode()) { if (pWnd && !m_bInOptions && *pWnd == *m_pFullscreenWnd) { m_pFullscreenWnd->ShowCursor(false); } KillTimer(TIMER_FULLSCREENMOUSEHIDER); } else { if (pWnd && !m_bInOptions && (m_wndView == *pWnd || m_wndView.IsChild(pWnd) || fCursorOutside)) { m_fHideCursor = true; SetCursor(nullptr); } } } break; //统计量 case TIMER_STATS: { //接收端质量信息:抖动,抖动,视音频同步情况等。。。 if (m_pQP) { CString rate; rate.Format(_T("%.2f"), m_dSpeedRate); rate = _T("(") + rate + _T("x)"); //信息 CString info; int val = 0; //平均帧率 m_pQP->get_AvgFrameRate(&val); // We hang here due to a lock that never gets released. info.Format(_T("%d.%02d %s"), val / 100, val % 100, rate); m_wndStatsBar.SetLine(ResStr(IDS_AG_FRAMERATE), info); int avg, dev; //抖动 m_pQP->get_AvgSyncOffset(&avg); m_pQP->get_DevSyncOffset(&dev); info.Format(ResStr(IDS_STATSBAR_SYNC_OFFSET_FORMAT), avg, dev); m_wndStatsBar.SetLine(ResStr(IDS_STATSBAR_SYNC_OFFSET), info); //掉帧 int drawn, dropped; m_pQP->get_FramesDrawn(&drawn); m_pQP->get_FramesDroppedInRenderer(&dropped); info.Format(IDS_MAINFRM_6, drawn, dropped); m_wndStatsBar.SetLine(ResStr(IDS_AG_FRAMES), info); //抖动 m_pQP->get_Jitter(&val); info.Format(_T("%d ms"), val); m_wndStatsBar.SetLine(ResStr(IDS_STATSBAR_JITTER), info); } //缓存信息 if (m_pBI) { CAtlList<CString> sl; //获取数量 for (int i = 0, j = m_pBI->GetCount(); i < j; i++) { int samples, size; //获取缓存状态 if (S_OK == m_pBI->GetStatus(i, samples, size)) { CString str; str.Format(_T("[%d]: %03d/%d KB"), i, samples, size / 1024); sl.AddTail(str); } } if (!sl.IsEmpty()) { CString str; str.Format(_T("%s (p%u)"), Implode(sl, ' '), m_pBI->GetPriority()); m_wndStatsBar.SetLine(ResStr(IDS_AG_BUFFERS), str); } } //比特率信息 CInterfaceList<IBitRateInfo> pBRIs; BeginEnumFilters(m_pGB, pEF, pBF) { BeginEnumPins(pBF, pEP, pPin) { if (CComQIPtr<IBitRateInfo> pBRI = pPin) { pBRIs.AddTail(pBRI); } } EndEnumPins; if (!pBRIs.IsEmpty()) { CAtlList<CString> sl; POSITION pos = pBRIs.GetHeadPosition(); for (int i = 0; pos; i++) { //比特率接口 IBitRateInfo* pBRI = pBRIs.GetNext(pos); //当前比特率 DWORD cur = pBRI->GetCurrentBitRate() / 1000; //平均比特率 DWORD avg = pBRI->GetAverageBitRate() / 1000; if (avg == 0) { continue; } //添加到字符串 CString str; if (cur != avg) { str.Format(_T("[%d]: %u/%u Kb/s"), i, avg, cur); } else { str.Format(_T("[%d]: %u Kb/s"), i, avg); } //加入 sl.AddTail(str); } if (!sl.IsEmpty()) { m_wndStatsBar.SetLine(ResStr(IDS_STATSBAR_BITRATE), Implode(sl, ' ') + ResStr(IDS_STATSBAR_BITRATE_AVG_CUR)); } break; } } EndEnumFilters; if (GetPlaybackMode() == PM_DVD) { // we also use this timer to update the info panel for DVD playback ULONG ulAvailable, ulCurrent; // Location CString Location('-'); DVD_PLAYBACK_LOCATION2 loc; ULONG ulNumOfVolumes, ulVolume; DVD_DISC_SIDE Side; ULONG ulNumOfTitles; ULONG ulNumOfChapters; if (SUCCEEDED(m_pDVDI->GetCurrentLocation(&loc)) && SUCCEEDED(m_pDVDI->GetNumberOfChapters(loc.TitleNum, &ulNumOfChapters)) && SUCCEEDED(m_pDVDI->GetDVDVolumeInfo(&ulNumOfVolumes, &ulVolume, &Side, &ulNumOfTitles))) { Location.Format(IDS_MAINFRM_9, ulVolume, ulNumOfVolumes, loc.TitleNum, ulNumOfTitles, loc.ChapterNum, ulNumOfChapters); ULONG tsec = (loc.TimeCode.bHours * 3600) + (loc.TimeCode.bMinutes * 60) + (loc.TimeCode.bSeconds); /* This might not always work, such as on resume */ if (loc.ChapterNum != m_lCurrentChapter) { m_lCurrentChapter = loc.ChapterNum; m_lChapterStartTime = tsec; } else { /* If a resume point was used, and the user chapter jumps, then it might do some funky time jumping. Try to 'fix' the chapter start time if this happens */ if (m_lChapterStartTime > tsec) { m_lChapterStartTime = tsec; } } } m_wndInfoBar.SetLine(ResStr(IDS_INFOBAR_LOCATION), Location); // Video CString Video('-'); DVD_VideoAttributes VATR; if (SUCCEEDED(m_pDVDI->GetCurrentAngle(&ulAvailable, &ulCurrent)) && SUCCEEDED(m_pDVDI->GetCurrentVideoAttributes(&VATR))) { Video.Format(IDS_MAINFRM_10, ulCurrent, ulAvailable, VATR.ulSourceResolutionX, VATR.ulSourceResolutionY, VATR.ulFrameRate, VATR.ulAspectX, VATR.ulAspectY); } m_wndInfoBar.SetLine(ResStr(IDS_INFOBAR_VIDEO), Video); // Audio CString Audio('-'); DVD_AudioAttributes AATR; if (SUCCEEDED(m_pDVDI->GetCurrentAudio(&ulAvailable, &ulCurrent)) && SUCCEEDED(m_pDVDI->GetAudioAttributes(ulCurrent, &AATR))) { CString lang; if (AATR.Language) { int len = GetLocaleInfo(AATR.Language, LOCALE_SENGLANGUAGE, lang.GetBuffer(64), 64); lang.ReleaseBufferSetLength(max(len - 1, 0)); } else { lang.Format(IDS_AG_UNKNOWN, ulCurrent + 1); } switch (AATR.LanguageExtension) { case DVD_AUD_EXT_NotSpecified: default: break; case DVD_AUD_EXT_Captions: lang += _T(" (Captions)"); break; case DVD_AUD_EXT_VisuallyImpaired: lang += _T(" (Visually Impaired)"); break; case DVD_AUD_EXT_DirectorComments1: lang += _T(" (Director Comments 1)"); break; case DVD_AUD_EXT_DirectorComments2: lang += _T(" (Director Comments 2)"); break; } CString format = GetDVDAudioFormatName(AATR); Audio.Format(IDS_MAINFRM_11, lang, format, AATR.dwFrequency, AATR.bQuantization, AATR.bNumberOfChannels, (AATR.bNumberOfChannels > 1 ? ResStr(IDS_MAINFRM_13) : ResStr(IDS_MAINFRM_12))); m_wndStatusBar.SetStatusBitmap( AATR.bNumberOfChannels == 1 ? IDB_AUDIOTYPE_MONO : AATR.bNumberOfChannels >= 2 ? IDB_AUDIOTYPE_STEREO : IDB_AUDIOTYPE_NOAUDIO); } m_wndInfoBar.SetLine(ResStr(IDS_INFOBAR_AUDIO), Audio); // Subtitles CString Subtitles('-'); BOOL bIsDisabled; DVD_SubpictureAttributes SATR; if (SUCCEEDED(m_pDVDI->GetCurrentSubpicture(&ulAvailable, &ulCurrent, &bIsDisabled)) && SUCCEEDED(m_pDVDI->GetSubpictureAttributes(ulCurrent, &SATR))) { CString lang; int len = GetLocaleInfo(SATR.Language, LOCALE_SENGLANGUAGE, lang.GetBuffer(64), 64); lang.ReleaseBufferSetLength(max(len - 1, 0)); switch (SATR.LanguageExtension) { case DVD_SP_EXT_NotSpecified: default: break; case DVD_SP_EXT_Caption_Normal: lang += _T(""); break; case DVD_SP_EXT_Caption_Big: lang += _T(" (Big)"); break; case DVD_SP_EXT_Caption_Children: lang += _T(" (Children)"); break; case DVD_SP_EXT_CC_Normal: lang += _T(" (CC)"); break; case DVD_SP_EXT_CC_Big: lang += _T(" (CC Big)"); break; case DVD_SP_EXT_CC_Children: lang += _T(" (CC Children)"); break; case DVD_SP_EXT_Forced: lang += _T(" (Forced)"); break; case DVD_SP_EXT_DirectorComments_Normal: lang += _T(" (Director Comments)"); break; case DVD_SP_EXT_DirectorComments_Big: lang += _T(" (Director Comments, Big)"); break; case DVD_SP_EXT_DirectorComments_Children: lang += _T(" (Director Comments, Children)"); break; } if (bIsDisabled) { lang = _T("-"); } Subtitles.Format(_T("%s"), lang); } m_wndInfoBar.SetLine(ResStr(IDS_INFOBAR_SUBTITLES), Subtitles); } else if (GetPlaybackMode() == PM_CAPTURE && AfxGetAppSettings().iDefaultCaptureDevice == 1) { CComQIPtr<IBDATuner> pTun = m_pGB; BOOLEAN bPresent; BOOLEAN bLocked; LONG lDbStrength; LONG lPercentQuality; CString Signal; if (SUCCEEDED(pTun->GetStats(bPresent, bLocked, lDbStrength, lPercentQuality)) && bPresent) { Signal.Format(ResStr(IDS_STATSBAR_SIGNAL_FORMAT), (int)lDbStrength, lPercentQuality); m_wndStatsBar.SetLine(ResStr(IDS_STATSBAR_SIGNAL), Signal); } } else if (GetPlaybackMode() == PM_FILE) { UpdateChapterInInfoBar(); } if (GetMediaState() == State_Running && !m_fAudioOnly) { BOOL fActive = FALSE; if (SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &fActive, 0)) { SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, nullptr, SPIF_SENDWININICHANGE); // this might not be needed at all... SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, fActive, nullptr, SPIF_SENDWININICHANGE); } fActive = FALSE; if (SystemParametersInfo(SPI_GETPOWEROFFACTIVE, 0, &fActive, 0)) { SystemParametersInfo(SPI_SETPOWEROFFACTIVE, FALSE, nullptr, SPIF_SENDWININICHANGE); // this might not be needed at all... SystemParametersInfo(SPI_SETPOWEROFFACTIVE, fActive, nullptr, SPIF_SENDWININICHANGE); } // prevent screensaver activate, monitor sleep/turn off after playback SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED); } } break; case TIMER_STATUSERASER: { KillTimer(TIMER_STATUSERASER); m_playingmsg.Empty(); } break; case TIMER_DVBINFO_UPDATER: KillTimer(TIMER_DVBINFO_UPDATER); ShowCurrentChannelInfo(false, false); break; } __super::OnTimer(nIDEvent); }
核心类已经分析的差不多了,现在可以看一看其他类的定义了。可是如此多的类,看看什么好呢?本着由易到难的原则,应该先看看“关于”对话框的代码。“关于”对话框作为mpc-hc系统的一部分,比较有代表性,而且代码相对来说十分简单,因而适合刚入门的人进行学习。
如图所示,“关于”对话框类的定义和实现都在最前面(因为开头是'A'......= =)。类的名字叫做CAboutDlg,定义位于AboutDlg.h,实现位于AboutDlg.cpp。
先看看“关于”对话框是什么样子的吧:
其实相比于其他的“关于”对话框来说,这个还算是一个相对比较复杂的。包含了编译器信息,版本等等信息。
CAboutDlg定义如下所示:
/* * (C) 2012 see Authors.txt * * This file is part of MPC-HC. * * MPC-HC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * MPC-HC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ #pragma once #include <afxwin.h> #include "resource.h" class CAboutDlg : public CDialog { CStatic m_icon; CString m_appname; CString m_strBuildNumber; CString m_MPCCompiler; #ifndef MPCHC_LITE CString m_FFmpegCompiler; #endif CString m_credits; CString m_AuthorsPath; public: CAboutDlg(); virtual BOOL OnInitDialog(); afx_msg void OnHomepage(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnAuthors(NMHDR* pNMHDR, LRESULT* pResult); // Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: //{{AFX_MSG(CAboutDlg) // No message handlers //}}AFX_MSG DECLARE_MESSAGE_MAP() };
从代码上来看。该对话框类和普通的MFC对话框类没有什么区别。不过这个“高端”的“关于”对话框确实包含了不少信息:mpc-hc版本,ffmpeg版本,编译器版本等等。这里就不再多说了,看看它类的实现部分的代码:
/* * (C) 2012-2013 see Authors.txt * * This file is part of MPC-HC. * * MPC-HC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * MPC-HC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ #include "stdafx.h" #include "AboutDlg.h" #include "mpc-hc_config.h" #ifndef MPCHC_LITE #include "FGFilterLAV.h" #endif #include "mplayerc.h" #include "version.h" #include "SysVersion.h" #include "WinAPIUtils.h" ///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) , m_appname(_T("")) , m_strBuildNumber(_T("")) , m_MPCCompiler(_T("")) #ifndef MPCHC_LITE , m_LAVFiltersVersion(_T("")) #endif { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT } //初始化 BOOL CAboutDlg::OnInitDialog() { // Get the default text before it is overwritten by the call to __super::OnInitDialog() GetDlgItem(IDC_STATIC1)->GetWindowText(m_appname); GetDlgItem(IDC_AUTHORS_LINK)->GetWindowText(m_credits); #ifndef MPCHC_LITE GetDlgItem(IDC_LAVFILTERS_VERSION)->GetWindowText(m_LAVFiltersVersion); #endif __super::OnInitDialog(); // Because we set LR_SHARED, there is no need to explicitly destroy the icon m_icon.SetIcon((HICON)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON, 48, 48, LR_SHARED)); #if MPC_BETA_RELEASE || _WIN64 m_appname += _T(" ("); #endif #if MPC_BETA_RELEASE m_appname += MPC_VERSION_BETA; #endif #if MPC_BETA_RELEASE && _WIN64 m_appname += _T(", "); #endif #ifdef _WIN64 m_appname += _T("64-bit"); #endif #if MPC_BETA_RELEASE || _WIN64 m_appname += _T(")"); #endif #ifdef MPCHC_LITE m_appname += _T(" Lite"); #endif // Build the path to Authors.txt m_AuthorsPath = GetProgramPath() + _T("Authors.txt"); // Check if the file exists if (FileExists(m_AuthorsPath)) { // If it does, we make the filename clickable m_credits.Replace(_T("Authors.txt"), _T("<a>Authors.txt</a>")); } m_homepage.Format(_T("<a>%s</a>"), WEBSITE_URL); m_strBuildNumber = MPC_VERSION_STR_FULL; #if defined(__INTEL_COMPILER) #if (__INTEL_COMPILER >= 1210) m_MPCCompiler = _T("ICL ") MAKE_STR(__INTEL_COMPILER) _T(" Build ") MAKE_STR(__INTEL_COMPILER_BUILD_DATE); #else #error Compiler is not supported! #endif #elif defined(_MSC_VER) #if (_MSC_VER == 1700) // 2012 #if (_MSC_FULL_VER == 170060610) m_MPCCompiler = _T("MSVC 2012 Update 3"); #elif (_MSC_FULL_VER == 170060315) // MSVC 2012 Update 2 #error VS2012 Update 2 is not supported because the binaries will not run on XP. Install Update 3 instead. #elif (_MSC_FULL_VER == 170051106) m_MPCCompiler = _T("MSVC 2012 Update 1"); #elif (_MSC_FULL_VER < 170050727) // MSVC 2012 #error Please install the latest Update for VS2012. #else m_MPCCompiler = _T("MSVC 2012"); #endif #elif (_MSC_VER == 1600) // 2010 #if (_MSC_FULL_VER >= 160040219) m_MPCCompiler = _T("MSVC 2010 SP1"); #else m_MPCCompiler = _T("MSVC 2010"); #endif #elif (_MSC_VER < 1600) #error Compiler is not supported! #endif #else #error Please add support for your compiler #endif #if (__AVX__) m_MPCCompiler += _T(" (AVX)"); #elif (__SSSE3__) m_MPCCompiler += _T(" (SSSE3)"); #elif (__SSE3__) m_MPCCompiler += _T(" (SSE3)"); #elif !defined(_M_X64) && defined(_M_IX86_FP) #if (_M_IX86_FP == 2) // /arch:SSE2 was used m_MPCCompiler += _T(" (SSE2)"); #elif (_M_IX86_FP == 1) // /arch:SSE was used m_MPCCompiler += _T(" (SSE)"); #endif #endif #ifdef _DEBUG m_MPCCompiler += _T(" Debug"); #endif #ifndef MPCHC_LITE //版本 CString LAVFiltersVersion = CFGFilterLAV::GetVersion(); if (!LAVFiltersVersion.IsEmpty()) { m_LAVFiltersVersion = LAVFiltersVersion; } #endif m_buildDate = _T(__DATE__) _T(" ") _T(__TIME__); OSVERSIONINFOEX osVersion = SysVersion::GetFullVersion(); m_OSName.Format(_T("Windows NT %1u.%1u (build %u"), osVersion.dwMajorVersion, osVersion.dwMinorVersion, osVersion.dwBuildNumber); if (osVersion.szCSDVersion[0]) { m_OSName.AppendFormat(_T(", %s)"), osVersion.szCSDVersion); } else { m_OSName += _T(")"); } m_OSVersion.Format(_T("%1u.%1u"), osVersion.dwMajorVersion, osVersion.dwMinorVersion); if (SysVersion::Is64Bit()) { m_OSVersion += _T(" (64-bit)"); } UpdateData(FALSE); GetDlgItem(IDOK)->SetFocus(); return FALSE; } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP DDX_Control(pDX, IDR_MAINFRAME, m_icon); DDX_Text(pDX, IDC_STATIC1, m_appname); DDX_Text(pDX, IDC_AUTHORS_LINK, m_credits); DDX_Text(pDX, IDC_HOMEPAGE_LINK, m_homepage); DDX_Text(pDX, IDC_VERSION, m_strBuildNumber); DDX_Text(pDX, IDC_MPC_COMPILER, m_MPCCompiler); #ifndef MPCHC_LITE DDX_Text(pDX, IDC_LAVFILTERS_VERSION, m_LAVFiltersVersion); #endif DDX_Text(pDX, IDC_STATIC2, m_buildDate); DDX_Text(pDX, IDC_STATIC3, m_OSName); DDX_Text(pDX, IDC_STATIC4, m_OSVersion); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP ON_NOTIFY(NM_CLICK, IDC_HOMEPAGE_LINK, OnHomepage) ON_NOTIFY(NM_CLICK, IDC_AUTHORS_LINK, OnAuthors) ON_BN_CLICKED(IDC_BUTTON1, OnCopyToClipboard) END_MESSAGE_MAP() void CAboutDlg::OnHomepage(NMHDR* pNMHDR, LRESULT* pResult) { ShellExecute(m_hWnd, _T("open"), WEBSITE_URL, nullptr, nullptr, SW_SHOWDEFAULT); *pResult = 0; } void CAboutDlg::OnAuthors(NMHDR* pNMHDR, LRESULT* pResult) { ShellExecute(m_hWnd, _T("open"), m_AuthorsPath, nullptr, nullptr, SW_SHOWDEFAULT); *pResult = 0; } //拷贝到剪切板 void CAboutDlg::OnCopyToClipboard() { //把各种信息添加到一个字符串中 CStringW info = m_appname; info += _T("\n----------------------------------\n\n"); info += _T("Build information:\n"); info += _T(" Version: ") + m_strBuildNumber + _T("\n"); info += _T(" MPC-HC compiler: ") + m_MPCCompiler + _T("\n"); info += _T(" Build date: ") + m_buildDate + _T("\n\n"); #ifndef MPCHC_LITE info += _T("LAV Filters:\n"); info += _T(" LAV Splitter: ") + CFGFilterLAV::GetVersion(CFGFilterLAV::SPLITTER) + _T("\n"); info += _T(" LAV Video: ") + CFGFilterLAV::GetVersion(CFGFilterLAV::VIDEO_DECODER) + _T("\n"); info += _T(" LAV Audio: ") + CFGFilterLAV::GetVersion(CFGFilterLAV::AUDIO_DECODER) + _T("\n\n"); #endif info += _T("Operating system:\n"); info += _T(" Name: ") + m_OSName + _T("\n"); info += _T(" Version: ") + m_OSVersion + _T("\n"); COleDataSource* pData = DEBUG_NEW COleDataSource(); int len = info.GetLength() + 1; HGLOBAL hGlob = GlobalAlloc(GMEM_FIXED, len * sizeof(WCHAR)); if (pData && hGlob) { wcscpy_s((WCHAR*)hGlob, len, (LPCWSTR)info); pData->CacheGlobalData(CF_UNICODETEXT, hGlob); // The system will take care of removing the allocated memory pData->SetClipboard(); } else if (pData) { delete pData; } else if (hGlob) { GlobalFree(hGlob); } }
发现CAboutDlg和普通的MFC对话框类其实没有什么区别。CAboutDlg功能相对比较简单,本文将会分析一个功能相对比较复杂的类:MediaInfo选项卡。在播放视频的时候,右键点击视频->选择“属性”->MediaInfo就可以查看该选项卡。一般情况下,该选项卡给出了正在播放的视频文件的详细参数(确实是非常的详细),包括:封装格式,视频编码,音频编码等等。是获取视频详细参数的最佳途径。
该选项卡的功能实际上是调用了开源项目MediaInfo的库。MediaInfo之前已经进行过详细介绍:
在此不再重复。先看看该选项卡长什么样子。
先来看看MediaInfo选项卡类的定义是什么样的吧。该类的定义位于PPageFileMediaInfo.h文件中。
/* 雷霄骅 * 中国传媒大学/数字电视技术 * [email protected] * */ /* * (C) 2009-2013 see Authors.txt * * This file is part of MPC-HC. * * MPC-HC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * MPC-HC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ #pragma once // CPPageFileMediaInfo dialog // 【属性】页面里面的【MediaInfo】 class CPPageFileMediaInfo : public CPropertyPage { DECLARE_DYNAMIC(CPPageFileMediaInfo) private: CComPtr<IFilterGraph> m_pFG; public: //构造函数都是两个参数 CPPageFileMediaInfo(CString fn, IFilterGraph* pFG); virtual ~CPPageFileMediaInfo(); // Dialog Data enum { IDD = IDD_FILEMEDIAINFO }; //显示信息的控件 CEdit m_mediainfo; CString m_fn; CFont* m_pCFont; //信息 CString MI_Text; #if !USE_STATIC_MEDIAINFO static bool HasMediaInfo(); #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //初始化,加载MediaInfo库,读取文件信息 virtual BOOL OnInitDialog(); DECLARE_MESSAGE_MAP() public: //显示窗口,并不做其他事情 afx_msg void OnShowWindow(BOOL bShow, UINT nStatus); };
该类和普通的MFC对话框类差别也不大。需要注意的有以下几点:
1.有一个变量:CComPtr<IFilterGraph> m_pFG,这个是mpc-hc中的变量,先不分析该变量的全部代码,在这里仅说一下它的作用:获取正在播放的视频文件的路径。
2.有一个控件类:CEdit m_mediainfo,对应界面上那个大框框,用于显示信息。
3.有一个字符串变量:CString MI_Text,用于存储MediaInfo得到的媒体信息。
下面来看看具体类的实现,该类的实现位于PPageFileMediaInfo.cpp文件中。
/* 雷霄骅 * 中国传媒大学/数字电视技术 * [email protected] * */ /* * (C) 2009-2013 see Authors.txt * * This file is part of MPC-HC. * * MPC-HC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * MPC-HC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ // PPageFileMediaInfo.cpp : implementation file #include "stdafx.h" #include "mplayerc.h" #include "PPageFileMediaInfo.h" #include "WinAPIUtils.h" #if USE_STATIC_MEDIAINFO #include "MediaInfo/MediaInfo.h" using namespace MediaInfoLib; #else #include "MediaInfoDLL.h" using namespace MediaInfoDLL; #endif // CPPageFileMediaInfo dialog IMPLEMENT_DYNAMIC(CPPageFileMediaInfo, CPropertyPage) CPPageFileMediaInfo::CPPageFileMediaInfo(CString fn, IFilterGraph* pFG) : CPropertyPage(CPPageFileMediaInfo::IDD, CPPageFileMediaInfo::IDD) , m_fn(fn) , m_pFG(pFG) , m_pCFont(nullptr) { } CPPageFileMediaInfo::~CPPageFileMediaInfo() { delete m_pCFont; m_pCFont = nullptr; } void CPPageFileMediaInfo::DoDataExchange(CDataExchange* pDX) { __super::DoDataExchange(pDX); DDX_Control(pDX, IDC_MIEDIT, m_mediainfo); } BEGIN_MESSAGE_MAP(CPPageFileMediaInfo, CPropertyPage) ON_WM_SHOWWINDOW() END_MESSAGE_MAP() // CPPageFileMediaInfo message handlers static WNDPROC OldControlProc; static LRESULT CALLBACK ControlProc(HWND control, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_KEYDOWN) { if ((LOWORD(wParam) == 'A' || LOWORD(wParam) == 'a') && (GetKeyState(VK_CONTROL) < 0)) { CEdit* pEdit = (CEdit*)CWnd::FromHandle(control); pEdit->SetSel(0, pEdit->GetWindowTextLength(), TRUE); return 0; } } return CallWindowProc(OldControlProc, control, message, wParam, lParam); // call edit control's own windowproc } //初始化,加载MediaInfo库,读取文件信息 BOOL CPPageFileMediaInfo::OnInitDialog() { __super::OnInitDialog(); if (!m_pCFont) { m_pCFont = DEBUG_NEW CFont; } if (!m_pCFont) { return TRUE; } if (m_fn.IsEmpty()) { BeginEnumFilters(m_pFG, pEF, pBF) { CComQIPtr<IFileSourceFilter> pFSF = pBF; if (pFSF) { //当前文件路径 LPOLESTR pFN = nullptr; //媒体类型 AM_MEDIA_TYPE mt; //获取当前文件的路径和媒体类型 if (SUCCEEDED(pFSF->GetCurFile(&pFN, &mt)) && pFN && *pFN) { m_fn = CStringW(pFN); CoTaskMemFree(pFN); } break; } } EndEnumFilters; } #if USE_STATIC_MEDIAINFO //使用静态库MediaInfo //文件路径 MediaInfoLib::String f_name = m_fn; MediaInfoLib::MediaInfo MI; #else MediaInfoDLL::String f_name = m_fn; MediaInfo MI; #endif //设置 MI.Option(_T("ParseSpeed"), _T("0")); MI.Open(f_name); MI.Option(_T("Complete")); MI.Option(_T("Language"), _T(" Config_Text_ColumnSize;30")); //信息字符串 MI_Text = MI.Inform().c_str(); MI.Close(); if (!MI_Text.Find(_T("Unable to load"))) { MI_Text = _T(""); } LOGFONT lf; ZeroMemory(&lf, sizeof(lf)); lf.lfPitchAndFamily = DEFAULT_PITCH | FF_MODERN; // The empty string will fallback to the first font that matches the other specified attributes. LPCTSTR fonts[] = { _T("Lucida Console"), _T("Courier New"), _T("") }; // Use a negative value to match the character height instead of the cell height. int fonts_size[] = { -10, -11, -11 }; UINT i = 0; BOOL success; do { _tcscpy_s(lf.lfFaceName, fonts[i]); lf.lfHeight = fonts_size[i]; success = IsFontInstalled(fonts[i]) && m_pCFont->CreateFontIndirect(&lf); i++; } while (!success && i < _countof(fonts)); //控件设置字体和内容 m_mediainfo.SetFont(m_pCFont); m_mediainfo.SetWindowText(MI_Text); // subclass the edit control OldControlProc = (WNDPROC)SetWindowLongPtr(m_mediainfo.m_hWnd, GWLP_WNDPROC, (LONG_PTR)ControlProc); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } //显示or不显示? void CPPageFileMediaInfo::OnShowWindow(BOOL bShow, UINT nStatus) { __super::OnShowWindow(bShow, nStatus); if (bShow) { GetParent()->GetDlgItem(IDC_BUTTON_MI)->ShowWindow(SW_SHOW); } else { GetParent()->GetDlgItem(IDC_BUTTON_MI)->ShowWindow(SW_HIDE); } } #if !USE_STATIC_MEDIAINFO bool CPPageFileMediaInfo::HasMediaInfo() { MediaInfo MI; return MI.IsReady(); } #endif
可以看出,主要的工作都是在OnInitDialog()函数中实现的。大体的步骤如下:
1.通过调用pFSF->GetCurFile(&pFN, &mt),获得当前文件的路径,存入pFN中。
2.因为字符串类型不同,几经转换把pFN转换为MediaInfo可以识别的字符串f_name
3.根据该路径,调用MediaInfo库,获得视频的详细信息存入字符串变量MI_Text。
4.将MI_Text显示到控件上。
总体说来,过程并不复杂,理解起来还是比较简单的。
本文分析一下mpc-hc的详细信息选项卡。在播放视频的时候,右键点击视频->选择“属性”后默认打开的就是该选项卡。一般情况下,该选项卡给出了正在播放的视频文件的一些基本参数:视频大小,分辨率,时长等。注意:详细信息选项卡和MediaInfo选项卡获得视频参数的原理是不一样的。详细信息选项卡是通过调用DirectShow函数接口而获得的视频的参数。而MediaInfo选项卡则是通过调用MediaInfo类库而获得视频的参数。
先看看选项卡长什么样子:
先来看看详细信息选项卡类的定义是什么样的吧。该类的定义位于PPageFileInfoDetails.h文件中。
/* 雷霄骅 * 中国传媒大学/数字电视技术 * [email protected] * */ /* * (C) 2003-2006 Gabest * (C) 2006-2012 see Authors.txt * * This file is part of MPC-HC. * * MPC-HC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * MPC-HC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ #pragma once #include "../SubPic/ISubPic.h" #include <afxwin.h> //取得一些基本信息 // CPPageFileInfoDetails dialog class CPPageFileInfoDetails : public CPropertyPage { DECLARE_DYNAMIC(CPPageFileInfoDetails) private: CComPtr<IFilterGraph> m_pFG; CComPtr<ISubPicAllocatorPresenter> m_pCAP; HICON m_hIcon; void InitEncoding(); public: CPPageFileInfoDetails(CString fn, IFilterGraph* pFG, ISubPicAllocatorPresenter* pCAP); virtual ~CPPageFileInfoDetails(); // Dialog Data enum { IDD = IDD_FILEPROPDETAILS }; CStatic m_icon; CString m_fn; CString m_type; CString m_size; CString m_time; CString m_res; CString m_created; CEdit m_encoding; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support virtual BOOL OnInitDialog(); virtual BOOL OnSetActive(); virtual LRESULT OnSetPageFocus(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() public: };
该类的定义和普通的MFC对话框类差不多,有以下几点是需要注意的:
1.以下两个变量是用于获得对话框上显示的相关的信息的:
CComPtr<IFilterGraph> m_pFG;
CComPtr<ISubPicAllocatorPresenter> m_pCAP;
2.有一系列的字符串变量:m_fn, m_type, m_size, m_time等用于存储获得的信息
详细信息选项卡类的实现位于PPageFileInfoDetails.cpp文件中。
/* 雷霄骅 * 中国传媒大学/数字电视技术 * [email protected] * */ /* * (C) 2003-2006 Gabest * (C) 2006-2013 see Authors.txt * * This file is part of MPC-HC. * * MPC-HC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * MPC-HC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ //取得一些基本信息 #include "stdafx.h" #include "mplayerc.h" #include "PPageFileInfoDetails.h" #include <atlbase.h> #include "DSUtil.h" #include "text.h" #include <d3d9.h> #include <vmr9.h> #include "moreuuids.h" // CPPageFileInfoDetails dialog IMPLEMENT_DYNAMIC(CPPageFileInfoDetails, CPropertyPage) CPPageFileInfoDetails::CPPageFileInfoDetails(CString fn, IFilterGraph* pFG, ISubPicAllocatorPresenter* pCAP) : CPropertyPage(CPPageFileInfoDetails::IDD, CPPageFileInfoDetails::IDD) , m_fn(fn) , m_pFG(pFG) , m_pCAP(pCAP) , m_hIcon(nullptr) , m_type(ResStr(IDS_AG_NOT_KNOWN)) , m_size(ResStr(IDS_AG_NOT_KNOWN)) , m_time(ResStr(IDS_AG_NOT_KNOWN)) , m_res(ResStr(IDS_AG_NOT_KNOWN)) , m_created(ResStr(IDS_AG_NOT_KNOWN)) { } CPPageFileInfoDetails::~CPPageFileInfoDetails() { if (m_hIcon) { DestroyIcon(m_hIcon); } } void CPPageFileInfoDetails::DoDataExchange(CDataExchange* pDX) { __super::DoDataExchange(pDX); DDX_Control(pDX, IDC_DEFAULTICON, m_icon); DDX_Text(pDX, IDC_EDIT1, m_fn); DDX_Text(pDX, IDC_EDIT4, m_type); DDX_Text(pDX, IDC_EDIT3, m_size); DDX_Text(pDX, IDC_EDIT2, m_time); DDX_Text(pDX, IDC_EDIT5, m_res); DDX_Text(pDX, IDC_EDIT6, m_created); DDX_Control(pDX, IDC_EDIT7, m_encoding); } #define SETPAGEFOCUS (WM_APP + 252) // arbitrary number, can be changed if necessary BEGIN_MESSAGE_MAP(CPPageFileInfoDetails, CPropertyPage) ON_MESSAGE(SETPAGEFOCUS, OnSetPageFocus) END_MESSAGE_MAP() // CPPageFileInfoDetails message handlers static bool GetProperty(IFilterGraph* pFG, LPCOLESTR propName, VARIANT* vt) { BeginEnumFilters(pFG, pEF, pBF) { if (CComQIPtr<IPropertyBag> pPB = pBF) if (SUCCEEDED(pPB->Read(propName, vt, nullptr))) { return true; } } EndEnumFilters; return false; } static CString FormatDateTime(FILETIME tm) { SYSTEMTIME st; FileTimeToSystemTime(&tm, &st); TCHAR buff[256]; GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, nullptr, buff, _countof(buff)); CString ret(buff); ret += _T(" "); GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, buff, _countof(buff)); ret += buff; return ret; } BOOL CPPageFileInfoDetails::OnInitDialog() { __super::OnInitDialog(); if (m_fn.IsEmpty()) { BeginEnumFilters(m_pFG, pEF, pBF) { CComQIPtr<IFileSourceFilter> pFSF = pBF; if (pFSF) { LPOLESTR pFN = nullptr; AM_MEDIA_TYPE mt; if (SUCCEEDED(pFSF->GetCurFile(&pFN, &mt)) && pFN && *pFN) { m_fn = CStringW(pFN); CoTaskMemFree(pFN); } break; } } EndEnumFilters; } CString ext = m_fn.Left(m_fn.Find(_T("://")) + 1).TrimRight(':'); if (ext.IsEmpty() || !ext.CompareNoCase(_T("file"))) { ext = _T(".") + m_fn.Mid(m_fn.ReverseFind('.') + 1); } m_hIcon = LoadIcon(m_fn, false); if (m_hIcon) { m_icon.SetIcon(m_hIcon); } if (!LoadType(ext, m_type)) { m_type.LoadString(IDS_AG_NOT_KNOWN); } CComVariant vt; if (::GetProperty(m_pFG, L"CurFile.TimeCreated", &vt)) { if (V_VT(&vt) == VT_UI8) { ULARGE_INTEGER uli; uli.QuadPart = V_UI8(&vt); FILETIME ft; ft.dwLowDateTime = uli.LowPart; ft.dwHighDateTime = uli.HighPart; m_created = FormatDateTime(ft); } } WIN32_FIND_DATA wfd; HANDLE hFind = FindFirstFile(m_fn, &wfd); if (hFind != INVALID_HANDLE_VALUE) { FindClose(hFind); __int64 size = (__int64(wfd.nFileSizeHigh) << 32) | wfd.nFileSizeLow; const int MAX_FILE_SIZE_BUFFER = 65; WCHAR szFileSize[MAX_FILE_SIZE_BUFFER]; StrFormatByteSizeW(size, szFileSize, MAX_FILE_SIZE_BUFFER); CString szByteSize; szByteSize.Format(_T("%I64d"), size); m_size.Format(_T("%s (%s bytes)"), szFileSize, FormatNumber(szByteSize)); if (m_created.IsEmpty()) { m_created = FormatDateTime(wfd.ftCreationTime); } } //获取时常 REFERENCE_TIME rtDur = 0; CComQIPtr<IMediaSeeking> pMS = m_pFG; if (pMS && SUCCEEDED(pMS->GetDuration(&rtDur)) && rtDur > 0) { m_time = ReftimeToString2(rtDur); } CSize wh(0, 0), arxy(0, 0); //获取宽和高 if (m_pCAP) { wh = m_pCAP->GetVideoSize(false); arxy = m_pCAP->GetVideoSize(true); } else { if (CComQIPtr<IBasicVideo> pBV = m_pFG) { if (SUCCEEDED(pBV->GetVideoSize(&wh.cx, &wh.cy))) { if (CComQIPtr<IBasicVideo2> pBV2 = m_pFG) { pBV2->GetPreferredAspectRatio(&arxy.cx, &arxy.cy); } } else { wh.SetSize(0, 0); } } if (wh.cx == 0 && wh.cy == 0) { BeginEnumFilters(m_pFG, pEF, pBF) { if (CComQIPtr<IBasicVideo> pBV = pBF) { pBV->GetVideoSize(&wh.cx, &wh.cy); if (CComQIPtr<IBasicVideo2> pBV2 = pBF) { pBV2->GetPreferredAspectRatio(&arxy.cx, &arxy.cy); } break; } else if (CComQIPtr<IVMRWindowlessControl> pWC = pBF) { pWC->GetNativeVideoSize(&wh.cx, &wh.cy, &arxy.cx, &arxy.cy); break; } else if (CComQIPtr<IVMRWindowlessControl9> pWC9 = pBF) { pWC9->GetNativeVideoSize(&wh.cx, &wh.cy, &arxy.cx, &arxy.cy); break; } } EndEnumFilters; } } if (wh.cx > 0 && wh.cy > 0) { m_res.Format(_T("%dx%d"), wh.cx, wh.cy); int gcd = GCD(arxy.cx, arxy.cy); if (gcd > 1) { arxy.cx /= gcd; arxy.cy /= gcd; } if (arxy.cx > 0 && arxy.cy > 0 && arxy.cx * wh.cy != arxy.cy * wh.cx) { CString ar; ar.Format(_T(" (AR %d:%d)"), arxy.cx, arxy.cy); m_res += ar; } } m_fn.TrimRight('/'); m_fn.Replace('\\', '/'); m_fn = m_fn.Mid(m_fn.ReverseFind('/') + 1); UpdateData(FALSE); InitEncoding(); m_pFG = nullptr; m_pCAP = nullptr; return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } BOOL CPPageFileInfoDetails::OnSetActive() { BOOL ret = __super::OnSetActive(); PostMessage(SETPAGEFOCUS, 0, 0L); return ret; } LRESULT CPPageFileInfoDetails::OnSetPageFocus(WPARAM wParam, LPARAM lParam) { CPropertySheet* psheet = (CPropertySheet*) GetParent(); psheet->GetTabControl()->SetFocus(); return 0; } void CPPageFileInfoDetails::InitEncoding() { CAtlList<CString> sl; BeginEnumFilters(m_pFG, pEF, pBF) { CComPtr<IBaseFilter> pUSBF = GetUpStreamFilter(pBF); if (GetCLSID(pBF) == CLSID_NetShowSource) { continue; } else if (GetCLSID(pUSBF) != CLSID_NetShowSource) { if (IPin* pPin = GetFirstPin(pBF, PINDIR_INPUT)) { CMediaType mt; if (FAILED(pPin->ConnectionMediaType(&mt)) || mt.majortype != MEDIATYPE_Stream) { continue; } } if (IPin* pPin = GetFirstPin(pBF, PINDIR_OUTPUT)) { CMediaType mt; if (SUCCEEDED(pPin->ConnectionMediaType(&mt)) && mt.majortype == MEDIATYPE_Stream) { continue; } } } BeginEnumPins(pBF, pEP, pPin) { CMediaTypeEx mt; PIN_DIRECTION dir; if (FAILED(pPin->QueryDirection(&dir)) || dir != PINDIR_OUTPUT || FAILED(pPin->ConnectionMediaType(&mt))) { continue; } CString str = mt.ToString(); if (!str.IsEmpty()) { if (mt.majortype == MEDIATYPE_Video) { // Sort streams, set Video streams at head bool found_video = false; for (POSITION pos = sl.GetTailPosition(); pos; sl.GetPrev(pos)) { CString Item = sl.GetAt(pos); if (!Item.Find(_T("Video:"))) { sl.InsertAfter(pos, str + CString(L" [" + GetPinName(pPin) + L"]")); found_video = true; break; } } if (!found_video) { sl.AddHead(str + CString(L" [" + GetPinName(pPin) + L"]")); } } else { sl.AddTail(str + CString(L" [" + GetPinName(pPin) + L"]")); } } } EndEnumPins; } EndEnumFilters; CString text = Implode(sl, '\n'); text.Replace(_T("\n"), _T("\r\n")); m_encoding.SetWindowText(text); }
从代码中可以看出,CPPageFileInfoDetails的功能是在OnInitDialog()中完成的。通过调用DirectShow相应的接口,分别获取了视频的宽高,时长等信息。