转:Media Player Classic - HC 源代码分析

VC2010 编译 Media Player Classic - Home Cinema (mpc-hc)

Media Player Classic - Home Cinema (mpc-hc)播放器一个经典的影音播放器,免费软件,可播放CD、VCD、DVD、及MP3、MP4、AVI、AAC等多种影音格式。与此同时,它还是开源的。今天尝试着编译了一下它的源代码(还是第一次接触这么大的MFC工程)

转:Media Player Classic - HC 源代码分析

第一步::准备

  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" 可以编译所有文件

1:整体结构

Media Player Classic - Home Cinema (mpc-hc)播放器一个经典的影音播放器,可播放CD、VCD、DVD、及MP3、MP4、AVI、AAC等多种影音格式。与此同时,它还是开源的。很多知名的视频播放器都是在它的基础上建立起来的,在这里就不例举了。本文将会对其源代码进行简要的分析。

之前一篇博客中曾经介绍了它的编译过程:VC2010 编译 Media Player Classic - Home Cinema (mpc-hc)

在这里就不再重复说明了,直入主题,看看它的工程是什么样子:

转:Media Player Classic - HC 源代码分析

相信大部分人看到这个工程的第一反应就是:好大啊!确实,我看到这个工程的时候也是这个反应。mpc-hc总体上分为3个部分:Apps,Filters,Libraries。其中Apps是其主程序;Filters是其附带的一些directshow filter,比如说AVI分离器,FLV分离器等等;Libraries则是其依赖的一些库:像Zlib这类的。

来细看看mpc-hc都有什么directshow filter吧(截图都放不下了...)

转:Media Player Classic - HC 源代码分析

Filters分为以下几种:

Muxer(封装),Parser(解封装,或称为分离器),Reader(读取),Renderer(显示),Source(源),Switcher(这个我不懂),Transform(处理)

在这里就不一一例举各种Filter了,因为数量实在太多,大部分Filter工程都可以通过名称来判断其功能。

再看看主程序Apps工程吧:

转:Media Player Classic - HC 源代码分析

可见主程序包含了巨量的代码,截图也只能显示其中的一部分。因此在代码分析的时候,不可能做到面面俱到,只能选择其中的重点部分进行详细的分析。

mpc-hc的对话框数量也很惊人:

转:Media Player Classic - HC 源代码分析

在这里就不再花篇幅形容mpc-hc工程的巨大了。赶紧说说如何来研究分析它的代码。本文主要分析它的主程序即在Apps目录下的工程。先介绍一下我总结出来的一些规则:

1.以PPage开头的.cpp或.h文件通常是一些属性选项卡的对话框对应的类。随后会详细介绍一个“视频信息”选项卡的代码(在这里用到了开源库MediaInfo)

2.主框架所在的位置是Mainfrm.cpp

目前只有这两条规则,以后会随着研究的不断深入,进一步完善这些规则。

2:核心类 (CMainFrame)(1)

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);

3:核心类 (CMainFrame)(2)

本文补充介绍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);  

}

4:核心类 (CMainFrame)(3)

此前的文章一直都是围绕着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);  

}

5:关于对话框 (CAboutDlg)

核心类已经分析的差不多了,现在可以看一看其他类的定义了。可是如此多的类,看看什么好呢?本着由易到难的原则,应该先看看“关于”对话框的代码。“关于”对话框作为mpc-hc系统的一部分,比较有代表性,而且代码相对来说十分简单,因而适合刚入门的人进行学习。

转:Media Player Classic - HC 源代码分析

如图所示,“关于”对话框类的定义和实现都在最前面(因为开头是'A'......= =)。类的名字叫做CAboutDlg,定义位于AboutDlg.h,实现位于AboutDlg.cpp。

先看看“关于”对话框是什么样子的吧:

转:Media Player Classic - HC 源代码分析

其实相比于其他的“关于”对话框来说,这个还算是一个相对比较复杂的。包含了编译器信息,版本等等信息。

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);  

    }  

}

6:MediaInfo选项卡 (CPPageFileMediaInfo)

发现CAboutDlg和普通的MFC对话框类其实没有什么区别。CAboutDlg功能相对比较简单,本文将会分析一个功能相对比较复杂的类:MediaInfo选项卡。在播放视频的时候,右键点击视频->选择“属性”->MediaInfo就可以查看该选项卡。一般情况下,该选项卡给出了正在播放的视频文件的详细参数(确实是非常的详细),包括:封装格式,视频编码,音频编码等等。是获取视频详细参数的最佳途径。

该选项卡的功能实际上是调用了开源项目MediaInfo的库。MediaInfo之前已经进行过详细介绍:

C++中使用MediaInfo库获取视频信息

MediaInfo使用简介(新版本支持HEVC)

在此不再重复。先看看该选项卡长什么样子。

转:Media Player Classic - HC 源代码分析

先来看看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显示到控件上。

总体说来,过程并不复杂,理解起来还是比较简单的。

7:详细信息选项卡(CPPageFileInfoDetails)

本文分析一下mpc-hc的详细信息选项卡。在播放视频的时候,右键点击视频->选择“属性”后默认打开的就是该选项卡。一般情况下,该选项卡给出了正在播放的视频文件的一些基本参数:视频大小,分辨率,时长等。注意:详细信息选项卡和MediaInfo选项卡获得视频参数的原理是不一样的。详细信息选项卡是通过调用DirectShow函数接口而获得的视频的参数。而MediaInfo选项卡则是通过调用MediaInfo类库而获得视频的参数。

先看看选项卡长什么样子:

转:Media Player Classic - HC 源代码分析

先来看看详细信息选项卡类的定义是什么样的吧。该类的定义位于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相应的接口,分别获取了视频的宽高,时长等信息。

你可能感兴趣的:(Class)