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工程)

第一步::准备

  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)

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

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

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

Filters分为以下几种:

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

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

再看看主程序Apps工程吧:

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

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

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

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

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

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

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系统的一部分,比较有代表性,而且代码相对来说十分简单,因而适合刚入门的人进行学习。

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

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

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

CAboutDlg定义如下所示:

/*
 * (C) 2012 see Authors.txt
 *
 * This file is part of MPC-HC.
 *
 * MPC-HC is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * MPC-HC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#pragma once

#include <afxwin.h>
#include "resource.h"

class CAboutDlg : public CDialog  
{  
    CStatic m_icon;  

    CString m_appname;  
    CString m_strBuildNumber;  
    CString m_MPCCompiler;  
#ifndef MPCHC_LITE
    CString m_FFmpegCompiler;  
#endif
    CString m_credits;  
    CString m_AuthorsPath;  

public:  
    CAboutDlg();  

virtual BOOL OnInitDialog();  

    afx_msg void OnHomepage(NMHDR* pNMHDR, LRESULT* pResult);  
    afx_msg void OnAuthors(NMHDR* pNMHDR, LRESULT* pResult);  

// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };  
//}}AFX_DATA

// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:  
virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
//}}AFX_VIRTUAL

// Implementation
protected:  
//{{AFX_MSG(CAboutDlg)
// No message handlers
//}}AFX_MSG
    DECLARE_MESSAGE_MAP()  
};

从代码上来看。该对话框类和普通的MFC对话框类没有什么区别。不过这个“高端”的“关于”对话框确实包含了不少信息:mpc-hc版本,ffmpeg版本,编译器版本等等。这里就不再多说了,看看它类的实现部分的代码:

/*
 * (C) 2012-2013 see Authors.txt
 *
 * This file is part of MPC-HC.
 *
 * MPC-HC is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * MPC-HC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "stdafx.h"
#include "AboutDlg.h"
#include "mpc-hc_config.h"
#ifndef MPCHC_LITE
#include "FGFilterLAV.h"
#endif
#include "mplayerc.h"
#include "version.h"
#include "SysVersion.h"
#include "WinAPIUtils.h"

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)  
    , m_appname(_T(""))  
    , m_strBuildNumber(_T(""))  
    , m_MPCCompiler(_T(""))  
#ifndef MPCHC_LITE
    , m_LAVFiltersVersion(_T(""))  
#endif
{  
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}  
//初始化
BOOL CAboutDlg::OnInitDialog()  
{  
// Get the default text before it is overwritten by the call to __super::OnInitDialog()
    GetDlgItem(IDC_STATIC1)->GetWindowText(m_appname);  
    GetDlgItem(IDC_AUTHORS_LINK)->GetWindowText(m_credits);  
#ifndef MPCHC_LITE
    GetDlgItem(IDC_LAVFILTERS_VERSION)->GetWindowText(m_LAVFiltersVersion);  
#endif

    __super::OnInitDialog();  

// Because we set LR_SHARED, there is no need to explicitly destroy the icon
    m_icon.SetIcon((HICON)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON, 48, 48, LR_SHARED));  

#if MPC_BETA_RELEASE || _WIN64
    m_appname += _T(" (");  
#endif

#if MPC_BETA_RELEASE
    m_appname += MPC_VERSION_BETA;  
#endif

#if MPC_BETA_RELEASE && _WIN64
    m_appname += _T(", ");  
#endif

#ifdef _WIN64
    m_appname += _T("64-bit");  
#endif

#if MPC_BETA_RELEASE || _WIN64
    m_appname += _T(")");  
#endif

#ifdef MPCHC_LITE
    m_appname += _T(" Lite");  
#endif

// Build the path to Authors.txt
    m_AuthorsPath = GetProgramPath() + _T("Authors.txt");  
// Check if the file exists
if (FileExists(m_AuthorsPath)) {  
// If it does, we make the filename clickable
        m_credits.Replace(_T("Authors.txt"), _T("<a>Authors.txt</a>"));  
    }  

    m_homepage.Format(_T("<a>%s</a>"), WEBSITE_URL);  

    m_strBuildNumber = MPC_VERSION_STR_FULL;  

#if defined(__INTEL_COMPILER)
#if (__INTEL_COMPILER >= 1210)
    m_MPCCompiler = _T("ICL ") MAKE_STR(__INTEL_COMPILER) _T(" Build ") MAKE_STR(__INTEL_COMPILER_BUILD_DATE);  
#else
#error Compiler is not supported!
#endif
#elif defined(_MSC_VER)
#if (_MSC_VER == 1700) // 2012
#if (_MSC_FULL_VER == 170060610)
    m_MPCCompiler = _T("MSVC 2012 Update 3");  
#elif (_MSC_FULL_VER == 170060315)  // MSVC 2012 Update 2
#error VS2012 Update 2 is not supported because the binaries will not run on XP. Install Update 3 instead.
#elif (_MSC_FULL_VER == 170051106)
    m_MPCCompiler = _T("MSVC 2012 Update 1");  
#elif (_MSC_FULL_VER < 170050727)   // MSVC 2012
#error Please install the latest Update for VS2012.
#else
    m_MPCCompiler = _T("MSVC 2012");  
#endif
#elif (_MSC_VER == 1600) // 2010
#if (_MSC_FULL_VER >= 160040219)
    m_MPCCompiler = _T("MSVC 2010 SP1");  
#else
    m_MPCCompiler = _T("MSVC 2010");  
#endif
#elif (_MSC_VER < 1600)
#error Compiler is not supported!
#endif
#else
#error Please add support for your compiler
#endif

#if (__AVX__)
    m_MPCCompiler += _T(" (AVX)");  
#elif (__SSSE3__)
    m_MPCCompiler += _T(" (SSSE3)");  
#elif (__SSE3__)
    m_MPCCompiler += _T(" (SSE3)");  
#elif !defined(_M_X64) && defined(_M_IX86_FP)
#if (_M_IX86_FP == 2)   // /arch:SSE2 was used
    m_MPCCompiler += _T(" (SSE2)");  
#elif (_M_IX86_FP == 1) // /arch:SSE was used
    m_MPCCompiler += _T(" (SSE)");  
#endif
#endif

#ifdef _DEBUG
    m_MPCCompiler += _T(" Debug");  
#endif

#ifndef MPCHC_LITE
//版本
    CString LAVFiltersVersion = CFGFilterLAV::GetVersion();  
if (!LAVFiltersVersion.IsEmpty()) {  
        m_LAVFiltersVersion = LAVFiltersVersion;  
    }  
#endif

    m_buildDate = _T(__DATE__) _T(" ") _T(__TIME__);  

    OSVERSIONINFOEX osVersion = SysVersion::GetFullVersion();  
    m_OSName.Format(_T("Windows NT %1u.%1u (build %u"),  
                    osVersion.dwMajorVersion, osVersion.dwMinorVersion, osVersion.dwBuildNumber);  
if (osVersion.szCSDVersion[0]) {  
        m_OSName.AppendFormat(_T(", %s)"), osVersion.szCSDVersion);  
    } else {  
        m_OSName += _T(")");  
    }  
    m_OSVersion.Format(_T("%1u.%1u"), osVersion.dwMajorVersion, osVersion.dwMinorVersion);  
if (SysVersion::Is64Bit()) {  
        m_OSVersion += _T(" (64-bit)");  
    }  

    UpdateData(FALSE);  

    GetDlgItem(IDOK)->SetFocus();  

return FALSE;  
}  

void CAboutDlg::DoDataExchange(CDataExchange* pDX)  
{  
    CDialog::DoDataExchange(pDX);  
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
    DDX_Control(pDX, IDR_MAINFRAME, m_icon);  
    DDX_Text(pDX, IDC_STATIC1, m_appname);  
    DDX_Text(pDX, IDC_AUTHORS_LINK, m_credits);  
    DDX_Text(pDX, IDC_HOMEPAGE_LINK, m_homepage);  
    DDX_Text(pDX, IDC_VERSION, m_strBuildNumber);  
    DDX_Text(pDX, IDC_MPC_COMPILER, m_MPCCompiler);  
#ifndef MPCHC_LITE
    DDX_Text(pDX, IDC_LAVFILTERS_VERSION, m_LAVFiltersVersion);  
#endif
    DDX_Text(pDX, IDC_STATIC2, m_buildDate);  
    DDX_Text(pDX, IDC_STATIC3, m_OSName);  
    DDX_Text(pDX, IDC_STATIC4, m_OSVersion);  
}  

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)  
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
    ON_NOTIFY(NM_CLICK, IDC_HOMEPAGE_LINK, OnHomepage)  
    ON_NOTIFY(NM_CLICK, IDC_AUTHORS_LINK, OnAuthors)  
    ON_BN_CLICKED(IDC_BUTTON1, OnCopyToClipboard)  
END_MESSAGE_MAP()  

void CAboutDlg::OnHomepage(NMHDR* pNMHDR, LRESULT* pResult)  
{  
    ShellExecute(m_hWnd, _T("open"), WEBSITE_URL, nullptr, nullptr, SW_SHOWDEFAULT);  
    *pResult = 0;  
}  

void CAboutDlg::OnAuthors(NMHDR* pNMHDR, LRESULT* pResult)  
{  
    ShellExecute(m_hWnd, _T("open"), m_AuthorsPath, nullptr, nullptr, SW_SHOWDEFAULT);  
    *pResult = 0;  
}  
//拷贝到剪切板
void CAboutDlg::OnCopyToClipboard()  
{  
//把各种信息添加到一个字符串中
    CStringW info = m_appname;  
    info += _T("\n----------------------------------\n\n");  
    info += _T("Build information:\n");  
    info += _T("    Version:            ") + m_strBuildNumber + _T("\n");  
    info += _T("    MPC-HC compiler:    ") + m_MPCCompiler + _T("\n");  
    info += _T("    Build date:         ") + m_buildDate + _T("\n\n");  
#ifndef MPCHC_LITE
    info += _T("LAV Filters:\n");  
    info += _T("    LAV Splitter:       ") + CFGFilterLAV::GetVersion(CFGFilterLAV::SPLITTER) + _T("\n");  
    info += _T("    LAV Video:          ") + CFGFilterLAV::GetVersion(CFGFilterLAV::VIDEO_DECODER) + _T("\n");  
    info += _T("    LAV Audio:          ") + CFGFilterLAV::GetVersion(CFGFilterLAV::AUDIO_DECODER) + _T("\n\n");  
#endif
    info += _T("Operating system:\n");  
    info += _T("    Name:               ") + m_OSName + _T("\n");  
    info += _T("    Version:            ") + m_OSVersion + _T("\n");  

    COleDataSource* pData = DEBUG_NEW COleDataSource();  

int len = info.GetLength() + 1;  
HGLOBAL hGlob = GlobalAlloc(GMEM_FIXED, len * sizeof(WCHAR));  

if (pData && hGlob) {  
        wcscpy_s((WCHAR*)hGlob, len, (LPCWSTR)info);  

        pData->CacheGlobalData(CF_UNICODETEXT, hGlob);  

// The system will take care of removing the allocated memory
        pData->SetClipboard();  
    } else if (pData) {  
delete pData;  
    } else if (hGlob) {  
        GlobalFree(hGlob);  
    }  
}

6:MediaInfo选项卡 (CPPageFileMediaInfo)

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

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

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

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

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

先来看看MediaInfo选项卡类的定义是什么样的吧。该类的定义位于PPageFileMediaInfo.h文件中。

/* 雷霄骅 
 * 中国传媒大学/数字电视技术 
 * leixiaohua1020@126.com 
 * 
 */
/*
 * (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文件中。

/* 雷霄骅 
 * 中国传媒大学/数字电视技术 
 * leixiaohua1020@126.com 
 * 
 */
/*
 * (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类库而获得视频的参数。

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

先来看看详细信息选项卡类的定义是什么样的吧。该类的定义位于PPageFileInfoDetails.h文件中。

/* 雷霄骅 
 * 中国传媒大学/数字电视技术 
 * leixiaohua1020@126.com 
 * 
 */
/*
 * (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文件中。

/* 雷霄骅 
 * 中国传媒大学/数字电视技术 
 * leixiaohua1020@126.com 
 * 
 */
/*
 * (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相应的接口,分别获取了视频的宽高,时长等信息。

posted @ 2015-05-23 14:58  Mr.Rico  阅读(2809)  评论(0编辑  收藏  举报