注:写了一系列分析Media Player Classic - HC 源代码的文章,在此列一个列表:
Media Player Classic - HC 源代码分析 1:整体结构
Media Player Classic - HC 源代码分析 2:核心类 (CMainFrame)(1)
Media Player Classic - HC 源代码分析 3:核心类 (CMainFrame)(2)
Media Player Classic - HC 源代码分析 4:核心类 (CMainFrame)(3)
Media Player Classic - HC 源代码分析 5:关于对话框 (CAboutDlg)
Media Player Classic - HC 源代码分析 6:MediaInfo选项卡 (CPPageFileMediaInfo)
Media Player Classic - HC 源代码分析 7:详细信息选项卡(CPPageFileInfoDetails)
上一篇文章概括性的介绍了Media Player Classic - Home Cinema (mpc-hc)播放器的源代码:Media Player Classic - HC 源代码分析 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);