Splayer(射手影音播放器)是一款高畫面、低消耗、智能化、精致美觀的影音播放器。

具有如下八個獨創:

獨創ShaderEngineTM圖像增強引擎,4倍速驅動,降低畫面噪點,銳利畫質呈現,低畫質視頻照样全屏放
獨創LiveColorTM彩色增強算法,畫面色彩更豔麗
獨創SmartAmplifyTM智能音場平衡技術,聲效更顯震撼

獨創FastMotionTM解碼優化技術,大幅降低CPU和內存占用。多核 SSE2 SSE3 MMX GPU優化
獨創PowerTravelTM旅行節電模式,降低能耗,增加筆記本的巡航時間
獨創EyeCareTM護眼模式,久看不疲勞,健康最重要

獨創Anti-SillyTM智能免配置,數百邏輯分支自動根據硬件配置選擇最佳模式。
獨創CloudMatchingTM智能顯示字幕技術,外語片從此不再需要找尋匹配字幕

Splayer 是一款開源項目,項目總共有87個工程(含測試工程),如圖:

1348816290_1409  

效果如圖:

1348816475_1692  

 

 

 

這是我目前接觸最大的項目,剛接觸時不知道如何下手,於是把所有的庫都大致的分析了一遍,由於缺少資料所以很多分析都帶有主觀,難免有瑕疵,

但我然仍貼出來,以鼓勵我繼續研究,我計劃把Splayer集成为一個視頻播放的控件,現在計劃已經進行了38%,但由於工作原因,這項計劃被擱置了,

但我相信我會慢慢把它完成,因为我是一個碼農,o(∩_∩)o 哈哈!~~~

2012tvod項目如圖:

1348816501_5416  

效果如圖:

1348832959_4888  

 

 

 進入主題了,由於Splayer項目過多,所以把它分成二個部分(主程序、類庫),現在從類庫開始分析了。

 

libpng   libpng是OSI認證開源軟件,主要負責讀寫png文件。該庫是用來創立和操作png格式的圖像文件.png格式是設計來替代gif,他對於更小範圍的tiff(標記圖
象文件格式)。Splayer中類CPngIMage是對該庫的應用,該類在libpng.h中。

 

decss   DVD解密源碼,專門針對DVD安全機制設計的破解其內容混合系統的,利用decss的功能從網上下載DVD格式的數字電影,還可以用DivX軟件將下載的數字電影
壓縮存儲到硬盤或光盤上。該類庫需要用到BaseClasses庫。 該庫有五個類,分別是CDeCSSInputPin、CVobDec、CDVDSession、CLBAFile、CVobFile。
dsutil   directshow工具類庫,主要負責視頻處理、捕獲應用…等操作。該類庫需要用到BaseClasses庫和foundation庫。該庫還有類:IDSMPropertyBagImpl 編碼器選項設置接口、CDSMResource、IDSMResourceBagImpl、CDSMChapter、IDSMChapterBagImpl、CDSMChapterBag、CFontInstaller 字體的初始化、CGolombBuffer、CH264Nalu、CHdmvClipInfo、CMediaTypeEx、CNullRenderer、CNullVideoRenderer、CNullUVideoRenderer、CNullAudioRenderer、CNullUAudioRenderer、CNullTextRenderer、CCpuID。其中DSUtil.h 包含很多視頻操作函數。

 

foundation  該庫是一個輔助庫,內含:base64編解碼、同步類CriticalSection/AutoCSLock、文件路徑操作類FilePath、信息輸出類LogController、加載資源類ResLoader、定義字符串Strings、多線程輔助模塊ThreadHelperImpl、實例生成模塊LazyInstanceImpl。
id3lib 該庫是用於讀、寫和操縱ID3v1和ID3v2標簽的對於媒體類型的文件,它能夠調用id3lib庫來獲取諸如作者,唱片年代,風格等tag信息,如果是視頻媒體文件,它還會抓圖。

 

libssf 該庫为一個操作 影視字幕以及特效的類庫。
lyriclib 該庫为歌詞匹配類庫。
subpic 該庫为操作位圖類庫(例如 字幕圖片顯示)。
sqlitepp 該庫是一個C++封裝的 SQLite組件,開源、免費。目的是使用面向對象的方式來簡化SQLite的使用,具有面向對象的訪問方式。程序將通過它生成數據庫,該數據庫是settings.db 負責記錄設置信息。
subtitles 該庫是操作視頻字幕的庫。
svplib  該庫是一個 字幕獲取功能庫,內含:CSVPEqualizer、CSVPRarLib 壓縮包rar文件操作、CSVPSubfilterLib、CSVPToolBox 文件路徑操作類。
yaml-cpp 是一個 開源的YAML 解析器。
CmdUI 是一個界面設置屬性的對話框
ResizableLib 为一個界面庫,可以根據父窗口的位置和大小動態調整控件窗口的大小。
sizecbar 为實現浮動工具條!
TreePropSheet 为樹控件目錄即文件目錄
Updater 程序的升級程序
libmad 該庫是一個MPEG音頻解碼庫,是一個高-品質的??的的的MPEG音頻解碼器。目前,它支持MPEG-1和MPEG-2擴展到較低的采样頻率,以及所謂的MPEG 2.5格式,是一個獨立庫。

 

filters 該庫是滤波函數庫等,該庫主要有程序類CFilterApp、CInternalPropertyPageWnd屬性窗體、CInternalPropertyPage屬性頁、CInternalPropertyPageTempl模塊、CPinInfoWnd。
liba52  該庫是一個解碼ATSC A/52流的庫,還包括一個MPEG-1和MPEG-2節目流复用器(音頻和視頻)。編碼的環繞立體聲縮混  大多數用戶明顯增加任意縮混揚聲器配置,並實施動態範圍壓縮。
libdts  該庫是一個DTS相幹聲學解碼器庫 負責解碼DTS相幹聲學流,用於一個跨平台的視頻播放和流VLC媒體播放器。
zlib 該庫是一個壓縮算法的庫,提供數據壓縮用的函式庫。
libfaad 該庫是軟高級音頻(AAC)解碼器,含丁苯橡膠解碼。其中in_mp4 - Winamp的MPEG-4 AAC文件輸入插件,QCD的 - 典型的球員的AAC插件,QCDMp4 - 典型的播放器MP4插件,XMMS - XMMS的AAC插件,MPEG4IP - 为MPEG4IP播放器插件。
libdirac 該庫是一個開源的視頻編解碼器。它采用了傳統的混合視頻編解碼器架構,是與小波轉換而不是通常的塊轉換。運動補償使用重叠的塊,以減少區塊會擾亂變換編碼階段的文物。
baseclasses 該庫是DirectX 自帶的一個庫,是微軟推出的一套基於Windows系統的多媒體應用程式接口APIs函式。
libflac   該庫是FLAC文件解碼器的一個庫,是無損音頻編解碼器庫。
libavcodec  該庫是非常先進的音頻/視頻編解碼庫,用於各種類型聲音/圖像編解碼,無法使用VS2008編譯。
libvorbisidec 該庫是Ogg文件編解碼器。
libmpeg2 該庫是一個解碼MPEG-2和MPEG-1視頻的庫。
SyncClock   該庫是同步卡與服務器的時間設置庫,該庫主要有:CSyncClock、CSyncClockFilter。
BaseMuxer 該庫为輸入/輸出流混合器基庫,合成器可將特定的音視頻流合成为特定的媒體容器格式,並交给文件寫滤鏡寫入到磁盤中,從而最終實現媒體格式的轉換。特定的合成器需要與特定的音視頻編碼器配合才能起作用,只不過有些合成器比較挑食,只能容納一種類型的音視頻編碼,而有些合成器胃口很好,可容納的多種音視頻編碼,如WMV和ASF的合成器只能容納下WMA音頻和WMV視頻,而AVI的合成器則可以容納從非壓縮的RGB視頻及PCM音頻到高壓縮率的DivX視頻及MP3音頻,至於MKV和DSM的合成器,幾乎算是萬能合成器了,該庫主要有CBaseMuxerFilter、CBaseMuxerInputPin、CBaseMuxerOutputPin、CBaseMuxerRawOutputPin。
DSMMuxer 介紹同上,該庫主要有CDSMMuxerFilter類
MatroskaMuxer 介紹同上,是負責將音頻流和視頻流合成为MKV格式,但要輸出,還需要一個文件寫出滤鏡(File writer),它負責把“Matroska Muxer”合成的結果寫入到磁盤當中,該類庫主要有CMatroskaMuxerInputPin、CMatroskaMuxerOutputPin、CMatroskaMuxerFilter等類。
wavdest 主要用於將采集到的視頻流寫入到指定的文件,文件格式是.WAV。該庫主要有CWavDestOutputPin、CWavDestFilter等類
streamdrivethru  該庫是驅動流通過輸入/輸出引腳過滤器,該庫主要有CStreamDriveThruInputPin、CStreamDriveThruOutputPin、CStreamDriveThruFilter等類。
AviSplitter  該庫是AVI的滤鏡分離器,默認用System的,因穩定性和兼容性最好;Gabest的可以讀下載不完整的AVI文件;而Haali的支持多音軌的切換。該庫涉及:BaseSplitter庫、BaseClasses庫,該庫主要有:CAviFile、CAviPlotterWnd、CAviReportWnd、CAviSplitterOutputPin、CAviSplitterFilter、
CAviSourceFilter。
basesplitter 該庫是分詞基類,該庫涉及dsutil、unrar、AudioSwitcher類庫,該庫主要有:CAsyncFileReader、CAsyncUrlReader、Packet、CPacketQueue、CBaseSplitterInputPin、CBaseSplitterOutputPin、CBaseSplitterFilter、CBaseSplitterFile、CBaseSplitterFileEx、CMultiFiles。
diracSplitter  該庫是dirac滤鏡分離器庫,該庫涉及:BaseSplitter庫、BaseClasses庫,該庫主要有CDiracSplitterFilter、CDiracSourceFilter、CDiracVideoDecoder、CDiracSplitterFile。
DSMSplitter  該庫是dsm滤鏡分離器庫,該庫涉及:BaseSplitter庫、BaseClasses庫,該庫主要有CDSMSplitterFilter、CDSMSourceFilter、CDSMSplitterFile等類。
EASplitter  該庫是ea滤鏡分離器庫,該庫涉及:BaseSplitter庫、BaseClasses庫、DSUtil庫、ffmpeg庫等,該庫主要有CEAFile、CEASpliterFilter、CEASourceFilter。
flvsplitter  該庫是flv滤鏡分離器庫,該庫涉及:BaseSplitter庫、BaseClasses庫、DSUtil庫、libVP62(flash vp6解碼器)庫,該庫主要有CFLVSplitterFilter、CFLVSourceFilter、VP62。
MatroskaSplitter 該庫是MKV滤鏡分離器庫(MKV視頻的分離器),該庫主要涉及:BaseSplitter庫、BaseClasses庫、DSUtil庫。該庫類較多。
mp4splitter  該庫是MP4滤鏡分離器庫,該庫主要涉及:BaseSplitter庫、BaseClasses庫、DSUtil庫、Zlib庫。該庫主要有:CMP4SplitterFilter、CMP4SourceFilter、CMPEG4VideoSplitterFilter、CMPEG4VideoSourceFilter、CMP4SplitterFile……等等。
MpaSplitter  該庫是Mpa滤鏡分離器庫,該庫主要涉及:BaseSplitter庫、BaseClasses庫、DSUtil庫。該庫主要有:CMpaSplitterFilter、CMpaSplitterFile等類。
MpegSplitter  該庫是MPEG滤鏡分離器庫,一個MPEG的分割工具,可將大尺寸MPEG文件分割成數個小尺寸的MPEG文件。該庫主要涉及:BaseSplitter庫、BaseClasses庫、DSUtil庫。該庫主要有:CMpegSplitterFilter、CMpegSourceFilter、CMpegSplitterFile、CMpegSplitterSettingsWnd等類。
WMVSpliter 該庫为WMV滤鏡分離器庫,WMA是一種視頻編碼格式,該庫主要有StreamOnAsyncReader、ptr_shared_count(線程安全的共享數)、StreamInput、WMFOutputPin(輸出引腳,提供的解碼器的壓縮數據)、IEncapsulatedAccess(分配緩沖區WMF時,我們需要返回一個IMediaSample對象周圍的包裝/INSSBuffer的接口)、EncapsulateSample、WMFDemuxFilter(分析器過滤器本身)、
NutSplitter  該庫主要涉及:BaseSplitter庫、BaseClasses庫、DSUtil庫。該庫主要有:CNutFile、CNutSplitterFilter、CNutSourceFilter等類。
OggSplitter  該庫是Ogg滤鏡分離器庫,Ogg是一種新的音頻壓縮格式,類似於MP3等的音樂格式。該庫主要涉及:BaseSplitter庫、BaseClasses庫、DSUtil庫。該庫主要有:OggPage類、COggFile類、OggPacket類、COggSplitterOutputPin類、COggVorbisOutputPin類、COggDirectShowOutputPin類、COggS treamOutputPin類、COggSplitterFilter類、COggSourceFilter類…等等。
RealMediaSplitter 該庫是real滤鏡分離器庫,是real播放器为除realplay外播放器播放real格式,比如RM,RMVB等等格式的播放插件.有了這個插件,其他播放器才能夠播放REAL格式的視頻,音頻等文件.
RoQSplitter 該庫是分離式聯軸器的旋轉編碼器,該庫主要有CRoQSplitterFilter、CRoQSourceFilter、CRoQVideoDecoder、CRoQAudioDecoder等類。
ssfsplitter 該庫是特效字幕d的滤鏡分離器,該庫主要有CSSFSplitterFilter、CSSFSourceFilter等類。
asyncreader 該庫是讀取網络上的編碼後的視頻數據庫,讀取後交给解碼filter。該庫主要有:CAsyncStream、CAsyncIo、CAsyncOutputPin、CAsyncReader。
cddareader 該庫是開源滤鏡MPC,DTS技術編碼的音頻具有細節豐富、定位精確的特點,是傳統CD和Dolby環繞聲效的後繼者。該庫主要有:CCDDAStream、CCDDAReader類。
cdxareader  該庫为系統滤鏡,該庫主要有CCDXAStream、CCDXAReader類。
udpreader   該庫为系統滤鏡,該庫主要有CUDPStream、CUDPReader類。
vtsreader   該庫为系統滤鏡,該庫主要有CVTSStream、CVTSReader類。
MpcAudioRenderer 該庫为使用WASAPI音頻輸出MPC音頻渲染器,該庫主要有CMpcAudioRenderer類。
basesource  該庫是解碼文件的基礎庫,該庫主要有:CBaseSource、CBaseStream等類。
dtsac3source該庫是DTS/AC3輸出滤鏡的庫,DTS(Digital Theater Systems)是一種高質量的多聲道音頻編碼方式,目前主要用作DVD的伴音。DTS 5.1的最大碼率(Bitrate)與LPCM(LPCM是數位非經壓縮的聲音規格,音質佳,所占容量空間較大)相同,可達1536Kbps,遠高於目前另一種流行的多聲道編碼系統──Dolby Digital AC3的 448Kbps,雖然同样是有損壓縮,但它可提供更为逼真,細節更为豐富的音響效果。用DVD中DTS音軌來制作音樂CD無疑是非常理想的。該庫主要有:CDTSAC3Source、CDTSAC3Stream等類。
flicsource  該庫是處理fli文件的庫,主要有CFLICSource、CFLICStream等類。
flacsource  該庫是音頻壓縮編解碼庫,其特點是無損壓縮。不同於其他有損壓縮編碼如MP3 及 AAC,它不會破壞任何原有的音頻資訊,所以可以還原音樂光盤音質。該庫主要有:CFlacSource、CFlacStream類。該庫主要牽涉
shoutcastsource  該庫是采用了MPEG Layer 3(MP3)技術,實現了因特網上的音頻流的實時廣播或者點播。該庫主要有:CShoutcastSource、CShoutcastStream等類。
d2vsource 該庫为d2v輸出滤鏡的庫,d2v即數字化視頻,該庫主要有CMPEG2Dec、CD2VSource、CMPEG2Dec等類。
subtitlesource 該庫为字幕輸出滤鏡的庫,該庫主要有CSubtitleSource、CSubtitleStream等類。
audioswitcher  該庫是一個自動切換揚聲器的庫,涉及主程序的eqPerset負責設置音頻,該庫主要有:AudioStreamResampler、CAudioSwitcherFilter、CStreamSwitcherPassThru、CStreamSwitcherAllocator、CStreamSwitcherInputPin、CStreamSwitcherOutputPin、CStreamSwitcherFilter。
Mpeg2DecFilter 該庫是为測試程序是libmpeg2。它解碼MPEG-1和MPEG-2視頻流,還包括一個MPEG-1和MPEG-2解复用器節目流。該庫牽涉:BaseVideoFilter.lib libmpeg2.lib dsutil.lib 等庫,該庫主要有:IMpeg2DecFilter接口、CMpeg2Info、CMpeg2Dec、CMpeg2DecFilter、CMpeg2DecInputPin、CMpeg2DecOutputPin、CSubpicInputPin、CMpe g2DecSettingsWnd等類。
avi2ac3filter 該庫为AC3音效輔助插件庫,目前DVDrip在制作音頻部分時普遍采用兩種方式:MP3格式壓縮音軌和保留DVD中原有的AC3音頻文件。一般來說,Windows操作系統已經自帶了MP3的解碼器——Fhg Radium MP3 codec,因此采用MP3格式做的DVDrip音頻我們都可以聽到。但對於以AC3音頻文件制作的DVDrip,我們就只能看到圖像而聽不到聲音了,該庫主要有CAVI2AC3Filter類。
bufferfilter 該庫为滤鏡輸出緩沖區庫,該庫主要有CBufferFilter、CBufferFilterOutputPin等類。
decssfilter 該庫为CSS滤鏡,對常規的CSS的一個擴展子集,可以使應用對象(文字,圖片...)產生類似於PHOTOSHOP中的模糊,通透,邊緣發光等效果。該庫主要有CDeCSSFilter類。
Mpeg2DecFilter 該庫为mpeg1、mpeg2解碼過滤器,該庫主要有CMpeg2Info、CMpeg2Dec、CMpeg2DecFilter、CMpeg2DecInputPin、CMpeg2DecOutputPin、CSubpicInputPin、CClosedCaptionOutputPin、CMpeg2DecSettingsWnd等類。
MpaDecFilter  該庫为MPC H264/VC1硬解碼器(主音頻解碼器),該庫主要有CMpaDecFilter、CMpaDecInputPin、CMpaDecSettingsWnd等類。
basevideofilter 該庫为解碼視頻播放過滤器基庫,該庫主要有CBaseVideoFilter、CBaseVideoInputAllocator、CBaseVideoInputPin、CBaseVideoInputPin、CBaseVideoOutputPin等類。
MPCVideoDec 該庫为主視頻解碼類庫,該庫主要有CMPCAudioDecFilter、CMPCVideoDecFilter、CMPCVideoDecSettingsWnd等類。
svpfilter 該庫为字幕滤鏡類庫,該庫主要有CSVPSubFilter類。

 

mplayerc为主程序,主程序自繪控件,以及應用視頻音頻編解碼播放,此處就簡單的看看涉及視頻播放主要有哪些類!

//渲染操作圖像基類 定義如下
class CBaseGraph
	: public CUnknown
	, public IGraphBuilder2  //IGraphBuilder接口允許應用程序調用時的過滤器圖表管理器試圖建立一個完整的過滤器圖表
, public IMediaControl  //IMediaControl接口提供控制的數據流通過過滤器圖的方法。它包括運行,暫停和停止圖形的方法
, public IMediaEventEx  //其中包含了用於檢索事件通知和用於重寫篩選器圖形的默認事件處理的方法
, public IMediaSeeking  //IMediaSeeking接口包含尋求內流的位置,並設置播放速度的方法
, public IVideoWindow  //IVideoWindow接口設置視頻窗口的屬性。應用程序可以使用它來 ​​設置窗口的所有者窗口的位置和尺寸,和其他屬性。
, public IBasicVideo  //IBasicVideo接口設置視頻屬性,如目標和源矩形。
, public IBasicAudio   //IBasicAudio接口控制音頻流的音量和平衡。
, public IAMOpenProgress    //IAMOpenProgress接口報告的文件打開操作的進度,使應用程序能夠取消操作。
	, public IGraphEngine	//渲染引擎接口
//播放flash類
class CShockwaveGraph : public CBaseGraph
class CMacrovisionKicker
	: public CUnknown
	, public IKsPropertySet	//IKsPropertySet接口的最初目的是作为一種有效的方式來設置和檢索WDM驅動程序的設備屬性,使用KSProxy翻譯用戶模式COM方法調用進入內核模式屬性的WDM流類驅動程序所使用的設置
interface __declspec(uuid("165BE9D6-0929-4363-9BA3-580D735AA0F6")) IGraphBuilder2 : public IFilterGraph2
//IFilterGraph2接口擴展IFilterGraph的的IGraphBuilder接口,其中包含建立過滤器圖表的方法。
//篩選器圖形管理器實現了這個接口。應用程序可以使用它建立圖表時,利用它提供的其他方法的優勢。
class __declspec(uuid("96F3E0BE-1BA4-4E79-973D-191FE425C86B")) CDeinterlacerFilter : public CTransformFilter
//CTransformFilter類是为實施變換過滤器的基類。這個類是專为實施變換過滤器與一個輸入引腳和一個輸出引腳。
//它使用的輸入引腳和輸出引腳獨立的分配器。要創建一個過滤器,數據處理,使用CTransInPlaceFilter類。
	//DX7分配器演示
	class CDX7AllocatorPresenter
		: public ISubPicAllocatorPresenterImpl  //圖像分配操作接口
	//DX9分配器演示
	class CDX9AllocatorPresenter
		: public ISubPicAllocatorPresenterImpl //圖像分配操作接口
	//DXR分配器演示
	class CDXRAllocatorPresenter:
		public ISubPicAllocatorPresenterImpl  //圖像分配操作接口
//EVR方式輸出
class COuterEVR:
	public CUnknown,
	public IVMRffdshow9,
	public IVMRMixerBitmap9,  //使應用程序從位圖或Direct3D表面上的視頻流,混合靜態圖像,當使用視頻混合渲染過滤器(VMR-9)。
	public IBaseFilter   //IBaseFilter接口是DirectShow過滤器的主界面
//EVR分配器演示
class CEVRAllocatorPresenter:
	public CDX9AllocatorPresenter,
	public IMFGetService,  //查詢指定的服務接口的對象。
	public IMFTopologyServiceLookupClient,  //初始化視頻混合器或演示。此接口由混頻器和演示,使他們能夠增強視頻渲染器(EVR)查詢接口指針。
	public IMFVideoDeviceID,  //返回視頻渲染組件支持的設備標識符。實現此接口由混頻器和增強視頻渲染器(EVR)的主持人。
	public IMFVideoPresenter,  //增強視頻渲染器(EVR)提供了一個默認的視頻演示,應用程序可以實現自定義的主持人。
	public IDirect3DDeviceManager9,  //使兩個線程共享相同的Direct3D 9設備,並提供訪問的DirectX視頻加速(DXVA)功能的設備。
	public IMFAsyncCallback,  //回調接口的異步方法完成時通知應用程序。
	public IQualProp,			//IQualProp接口提供視頻渲染性能信息檢索的方法。
	public IMFRateSupport,			//查詢範圍的支持,包括反向播放的播放率。	
	public IMFVideoDisplayControl,	//控制如何增強型視頻渲染器(EVR)顯示視頻。
	public IEVRTrustedVideoPlugin		//1启用插件在增強視頻渲染器(EVR)與保護媒體工作的組成部分。
//映射過滤器
class CFilterMapper2 : 
	protected CUnknown, 
	public IFilterMapper2	//注冊和取消注冊的過滤器,並在注冊表中找到過滤器。
//過滤器重叠 (找最近的過滤器)
class FilterOverride
//FG過滤器
class CFGFilter
//FG過滤器注冊
class CFGFilterRegistry :
	public CFGFilter
//FG過滤器文件
class CFGFilterFile :
	public CFGFilter
//FG過滤器視頻渲染
class CFGFilterVideoRenderer : 
	public CFGFilter
//FG過滤器管理
class CFGManager
	: public CUnknown
	, public IGraphBuilder2
	, public IGraphBuilderDeadEnd
	, public CCritSec			//CCritSec類提供了一個線程锁。
//添加過滤器
class CFGManagerCustom : public CFGManager
//視頻播放時觸發 尋找和鏈接過滤器
class CFGManagerPlayer 
	: public CFGManagerCustom
//DVD播放時觸發 添加對應的過滤器以及渲染圖像
class CFGManagerDVD 
	: public CFGManagerPlayer
//視頻捕捉觸發
class CFGManagerCapture 
	: public CFGManagerPlayer
//合成器
class CFGManagerMuxer 
	: public CFGManagerCustom
//FG聚合 可以根據riid查詢對應的接口
class CFGAggregator 
	: public CUnknown
	//同步锁
	class CGenlock
//QT視圖操作接口
interface __declspec(uuid("A6AE36F7-A6F2-4157-AF54-6599857E4E20")) IQTVideoSurface : public IUnknown
//視頻解碼設置類 讀取顯卡信息
class MPCVideoDecSetting
//VMR9方式輸出
class COuterVMR9:
	public CUnknown,
	public IVideoWindow,	//視頻窗口接口
	public IBasicVideo2,  //視頻渲染過滤器和視頻混合渲染過滤器實現這個接口,但通過過滤器圖表管理器的應用程序接口暴露。
	public IVMRWindowlessControl,	//IVMRWindowlessControl界面控制視頻混合渲染過滤器(VMR-7)如何呈現在一個容器內窗口的視頻流
	public IVMRffdshow9,
	public IVMRMixerBitmap9	//IVMRMixerBitmap9接口,使應用程序從位圖或Direct3D表面上的視頻流,混合靜態圖像,當使用視頻混合渲染過滤器(VMR-9)
//像素着色器編譯器
class CPixelShaderCompiler
	//QT7分配器演示
	class CQT7AllocatorPresenter
		: public CDX7AllocatorPresenter //
		, public IQTVideoSurface  //qt視圖操作接口
	//QT9分配器演示
	class CQT9AllocatorPresenter:
		public CDX9AllocatorPresenter,
		public IQTVideoSurface //QT視圖操作接口
	//實現QuickTime多媒體文件播放
	class CQuicktimeWindow : public CPlayerWindow
//QuickTime多媒體文件渲染
class CQuicktimeGraph :
public CBaseGraph,  //基類渲染
public IVideoFrameStep//實現單幀前進後退
	//實現RealMedia多媒體文件播放
	class CRealMediaPlayer
		: public CUnknown
		, public IRMAErrorSink //錯誤處理接口
		, public IRMAClientAdviseSink //能夠使 RealPlayer的上層感知內核中播放 狀態的改變以及其他相關信息
		, public IRMAAuthenticationManager	//可以控制網络上多個數碼复合機的認證數據,並能對每個用戶的認證數據進行簡單有效的設置。
		, public IRMASiteSupplier  //rma站點提供接口
		, public IRMAPassiveSiteWatcher	//通過站點觀察接口
		, public IRMAAudioHook		//音頻鉤子接口
//RealMedia媒體視頻視圖操作
class CRealMediaVideoSurface
	: public CUnknown
	, public IRMAVideoSurface //視圖操作接口
	//RM7分配器演示
	class CRM7AllocatorPresenter
		: public CDX7AllocatorPresenter
		, public IRMAVideoSurface  //視圖操作接口
	//RM9分配器演示
	class CRM9AllocatorPresenter: 
		public CDX9AllocatorPresenter,
		public IRMAVideoSurface  //RMA視圖操作接口
	//VMR7分配器演示
	class CVMR7AllocatorPresenter
		: public CDX7AllocatorPresenter
		, public IVMRSurfaceAllocator	//分配器演示使用這個接口進行通信的VMR
		, public IVMRImagePresenter	//IVMRImagePresenter9接口來實現的默認分配器演示視頻混合呈現過滤器(VMR-9),VMR-9使用此接口通知分配器演示的,它應該呈現的視頻幀中所提供的Direct3D表面。
		, public IVMRWindowlessControl	//顯示窗口控制接口
	//VMR9分配器演示
	class CVMR9AllocatorPresenter
		: public CDX9AllocatorPresenter, 
		public IVMRSurfaceAllocator9,	//分配器演示使用這個接口進行通信的VMR
		public IVMRImagePresenter9,		//IVMRImagePresenter9接口來實現的默認分配器演示視頻混合呈現過滤器(VMR-9),VMR-9使用此接口通知分配器演示的,它應該呈現的視頻幀中所提供的Direct3D表面。
		public IVMRWindowlessControl9	//顯示窗口控制接口
class __declspec(uuid("96F3E0BE-1BA4-4E79-973D-191FE425C86B")) CDeinterlacerFilter 
	: public CTransformFilter // 用來瀏覽視頻 時候旋轉 
class __declspec(uuid("E2BA9B7B-B65D-4804-ACB2-89C3E55511DB")) CTextPassThruFilter : 
	public CBaseFilter, 
	public CCritSec

 

視頻播放大致跟這些類/接口有關!

 

主程序是如何播放的?先來看看主程序的幾個成員函數:

 

  // Operations   准備視頻播放操作
  bool OpenMediaPrivate(CAutoPtr<OpenMediaData> pOMD);
  // 准備關閉當前視頻操作
  void CloseMediaPrivate();
	//創建渲染對象
  void OpenCreateGraphObject(OpenMediaData* pOMD);
  //路徑操作
  HRESULT OpenMMSUrlStream(CString szFn);
  //打開目標文件
  void OpenFile(OpenFileData* pOFD);
  //打開DVD
  void OpenDVD(OpenDVDData* pODD);
  //打開視頻捕捉
  void OpenCapture(OpenDeviceData* pODD);
  //通用渲染
  void OpenCustomizeGraph();
  //初始化視頻操作
  void OpenSetupVideo();
  //初始化音頻操作
  void OpenSetupAudio();

這些函數進行了視頻相關操作!~

 

當打開文件時觸發

 

 m_pGraphThread = (CGraphThread*)AfxBeginThread(RUNTIME_CLASS(CGraphThread));
if(m_pGraphThread)
    m_pGraphThread->SetMainFrame(this);

銷毀時觸發

 

 

  if(m_pGraphThread)
  {
    CAMEvent 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, -1);
    }
  }

 

打開文件流程分析:
void CMainFrame::OnFileOpenQuick()
{
   正在加載或列表未生成 則直接返回
  if(m_iMediaLoadState == MLS_LOADING || !IsWindow(m_wndPlaylistBar)) return;

   近期文件列表
  CRecentFileList& MRU = AfxGetAppSettings().MRU;

  CString szLastFile;  最後一次看的視頻文件

  MRU.ReadList();    讀取列表的文件路徑

  if(MRU.GetSize() > 0){
    szLastFile = MRU[0];    把讀取的文件信息取出
  }

  CString filter;
  CAtlArray<CString> mask;
  從設置裏面讀取過滤的設置
  AfxGetAppSettings().Formats.GetFilter(filter, mask);

  COpenFileDlg fd(mask, true, NULL, szLastFile, 
    OFN_EXPLORER|OFN_ENABLESIZING|OFN_HIDEREADONLY|OFN_ALLOWMULTISELECT|OFN_ENABLEINCLUDENOTIFY, 
    filter, this);
  if(fd.DoModal() != IDOK) return;    顯示出打開文件對話框

  CAtlList<CString> fns;

  POSITION pos = fd.GetStartPosition();
  while(pos) fns.AddTail(fd.GetNextPathName(pos)); 把選中的文件加入進來

  bool fMultipleFiles = false;

  判斷是否多選 或選中的是目錄
  if(fns.GetCount() > 1 
    || fns.GetCount() == 1 
    && (fns.GetHead()[fns.GetHead().GetLength()-1] == '\\'
    || fns.GetHead()[fns.GetHead().GetLength()-1] == '*'))
  {
    fMultipleFiles = true;
  }

  猜測 關閉Media
  SendMessage(WM_COMMAND, ID_FILE_CLOSEMEDIA);

  ShowWindow(SW_SHOW);
  SetForegroundWindow();

  列表打開文件
  m_wndPlaylistBar.Open(fns, fMultipleFiles);

  播放列表個數为一 且可見 並且不是自由的 則顯示或隱藏
  if(m_wndPlaylistBar.GetCount() == 1 && m_wndPlaylistBar.IsWindowVisible() && !m_wndPlaylistBar.IsFloating())
  {
    ShowControlBar(&m_wndPlaylistBar, FALSE, TRUE);
  }

  OpenCurPlaylistItem();  打開當前列表選中項
}

進入 OpenCurPlaylistItem 函數分析  参數为樹節點 默認为零
void CMainFrame::OpenCurPlaylistItem(REFERENCE_TIME rtStart)
{
  如果列表中沒有  則直接退出
  if(m_wndPlaylistBar.GetCount() == 0)    return;

  CPlaylistItem pli;
   得到當前選中項 如果沒有則選中第一項
  if(!m_wndPlaylistBar.GetCur(pli)) m_wndPlaylistBar.SetFirst();
  if(!m_wndPlaylistBar.GetCur(pli)) return;

  AppSettings& s = AfxGetAppSettings();

  if(rtStart == 0){
    CString fn;
    fn = pli.m_fns.GetHead(); 獲得文件路徑

    // SVP_LogMsg5(L"GetFav Start1 %s", fn);
    favtype ft ;
    ft = FAV_FILE;

    if (!fn.IsEmpty() && s.autoResumePlay){
      文件存在 且 自動恢复播放
      std::string str = Strings::WStringToString(fn.GetBuffer());
      CString szMatchmd5 = HashController::GetInstance()->GetMD5Hash(str.c_str(), str.length()).c_str();

      SVP_LogMsg5(L"GetFav Start %s", szMatchmd5);

      CAtlList<CString> sl;
      s.GetFav(ft, sl, TRUE); 得到以前保存的數據

      CString PosStr ;
      POSITION pos = sl.GetHeadPosition(); 獲得
      while(pos){   //判斷以前是否播放過
        PosStr = sl.GetNext(pos) ;
        if( PosStr.Find(szMatchmd5 + _T(";")) == 0 ){
          break;
        }else{
          PosStr.Empty();
        }
      }
      if(!PosStr.IsEmpty()){  如果未播放 則記錄


        int iPos = PosStr.ReverseFind( _T(';') );
        if(iPos >= 0){
          CString s2 = PosStr.Right( PosStr.GetLength() - iPos - 1 );
          _stscanf(s2, _T("%I64d"), &rtStart); // pos
        }
        SVP_LogMsg5(L"Got %f", double(rtStart) );
      }
      是否恢复退出時的狀態
      m_is_resume_from_last_exit_point = TRUE;
      //SVP_LogMsg5(L"GetFav Done");
    }
  }else if(rtStart == -1){
    rtStart = 0;
  }
  
   打開媒體文件  根據OpenMediaData*初始化 OpenMediaData裏面保存了路徑和rtStart
  CAutoPtr<OpenMediaData> p(m_wndPlaylistBar.GetCurOMD(rtStart));

  if(p) OpenMedia(p); 根據CAutoPtr<OpenMediaData> 進行打開播放
}


  進入OpenMedia進行 打開播放
void CMainFrame::OpenMedia(CAutoPtr<OpenMediaData> pOMD)
{
  // shortcut 
  if(OpenDeviceData* p = dynamic_cast<OpenDeviceData*>(pOMD.m_p))
  {
    如果是DeviceData數據 
    if(m_iMediaLoadState == MLS_LOADED && 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);
      return;
    }
  }

  如果當前正在播放則關閉
  if(m_iMediaLoadState != MLS_CLOSED)
    CloseMedia();

  更改狀態
  m_iMediaLoadState = MLS_LOADING; // HACK: hides the logo
  m_wndView.Invalidate();

  AppSettings& s = AfxGetAppSettings();

  bool fUseThread = true;

  if(OpenFileData* p = dynamic_cast<OpenFileData*>(pOMD.m_p))
  {
     如果是打開的文件
    if(p->fns.GetCount() > 0)
    {
      解碼類型
      engine_t e = s.Formats.GetEngine(p->fns.GetHead());
      如果是DirectShow  類型  只有.ram .rm .ra .qt .mov .ram 不需要
      fUseThread = e == DirectShow /*|| e == RealMedia || e == QuickTime*/;
    }
  }
  else if(OpenDeviceData* p = dynamic_cast<OpenDeviceData*>(pOMD.m_p))
  {
    fUseThread = false;
  }

   如果渲染線程存在 且 需要使用線程 且 需要打開工作線程
  if(m_pGraphThread && fUseThread&&AfxGetAppSettings().fEnableWorkerThreadForOpening)
    m_pGraphThread->PostThreadMessage(CGraphThread::TM_OPEN, 0, (LPARAM)pOMD.Detach());
  else
    OpenMediaPrivate(pOMD);
}

進入 CGraphThread::TM_OPEN 事件  用一個新的線程打開播放文件
void CGraphThread::OnOpen(WPARAM wParam, LPARAM lParam)
{
  if(m_pMainFrame)
  {
    CAutoPtr<OpenMediaData> pOMD((OpenMediaData*)lParam);
    m_pMainFrame->OpenMediaPrivate(pOMD);
  }
}

進入OpenMediaPrivate 分析
bool CMainFrame::OpenMediaPrivate(CAutoPtr<OpenMediaData> pOMD)
{
  CString err, aborted(_T("Aborted"));
  AppSettings& s = AfxGetAppSettings(); 獲得設置信息

  {
    打開關閉锁
    CAutoLock mOpenCloseLock(&m_csOpenClose);
    s.bIsIVM = false;
    s.szCurrentExtension.Empty();

    如果狀態不是關閉 且 不是正在加載  則出錯
    一定要是加載狀態 或是關閉狀態
    if(m_iMediaLoadState != MLS_CLOSED && m_iMediaLoadState != MLS_LOADING)
    {
      ASSERT(0);
      return(false);
    }

    s.bExternalSubtitleTime = false;

    設置Slider
    m_wndSeekBar.SetRange(0, 100);

    記錄
    s.szFGMLog.Empty();

    設置聲道
    s.SetNumberOfSpeakers(s.iDecSpeakers , -1);

    正在加載狀態
    m_iMediaLoadState = MLS_LOADING;

    2.5秒加載
    SetTimer(TIMER_LOADING, 2500, NULL); 

    不顯示關閉
    // FIXME: Don't show "Closed" initially
    PostMessage(WM_KICKIDLE);              調用主線程更新的東西

    m_fUpdateInfoBar = false;

    try
    {
      打開終止 異常
      if(m_fOpeningAborted) throw aborted;

      if(OpenFileData* pOFD = dynamic_cast<OpenFileData*>(pOMD.m_p))
      {
        文件不存在異常
        if(pOFD->fns.IsEmpty()) throw ResStr(IDS_MSG_THROW_FILE_NOT_FOUND);

        CString fn = pOFD->fns.GetHead();

        取出格式
        s.szCurrentExtension = fn.Right(4).MakeLower();

        格式比較
        .ivm格式
        if(s.szCurrentExtension == _T(".ivm"))
          s.bIsIVM = true;

        s.bDisableSoftCAVCForce = false;
        
        .mkv格式
        if(!(s.useGPUAcel && s.bHasCUDAforCoreAVC) && !s.bDisableSoftCAVC 
             && s.szCurrentExtension == _T(".mkv"))
        {
          FILE* fp;
          if ( _wfopen_s( &fp, fn, _T("rb")) == 0)
          {
            char matchbuf[0x4000];
            size_t iRead = fread(matchbuf, sizeof( char ), 0x4000 ,fp);
            if( iRead > 200 && find_string_in_buf(matchbuf, iRead-100, "wpredp=2") > 0 )
              s.bDisableSoftCAVCForce = true;

            fclose(fp);
          }
        }

        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(ResStr(IDS_MSG_WARN_NOT_FOUND_AND_PLS_INSERT_DISK), fn);
                ret = AfxMessageBox(msg, MB_RETRYCANCEL);
              }
            }

            if(ret != IDOK) throw aborted;
          }else{
            否則是本地盤  判斷文件是否存在
            CSVPToolBox svpTool;
            if(!svpTool.ifFileExist(fn, true)){
              //SVP_LogMsg5(L"SVP 文件不存在" );
              throw ResStr(IDS_MSG_THROW_FILE_NOT_EXIST);
            }
          }
        }
      }

      打開終止異常
      if(m_fOpeningAborted) throw aborted;

      創建渲染對象  關鍵一
      OpenCreateGraphObject(pOMD);   待分析

      打開終止異常
      if(m_fOpeningAborted) throw aborted;

      m_pCAP2 = NULL;
      m_pCAP = NULL;
      m_pSVPSub = NULL;

      判斷打開的文件的類型  待分析
      if(OpenFileData* p = dynamic_cast<OpenFileData*>(pOMD.m_p)) OpenFile(p);
      else if(OpenDVDData* p = dynamic_cast<OpenDVDData*>(pOMD.m_p)) OpenDVD(p);
      else if(OpenDeviceData* p = dynamic_cast<OpenDeviceData*>(pOMD.m_p)) OpenCapture(p);
      else throw _T("Can't open, invalid input parameters");

      查找相關接口
      pGB->FindInterface(__uuidof(ISubPicAllocatorPresenter), (void**)&m_pCAP, FALSE);
      pGB->FindInterface(__uuidof(ISubPicAllocatorPresenterRender), (void**)&m_pCAPR, TRUE);
      pGB->FindInterface(__uuidof(ISubPicAllocatorPresenter2), (void**)&m_pCAP2, TRUE);
      pGB->FindInterface(__uuidof(IVMRMixerControl9),(void**)&m_pMC,  TRUE);

       如果未找到 則用另外一種方法查找
      if(!m_pCAP){
        CComQIPtr<ISubPicAllocatorPresenter> pCAP =  FindFilter(__uuidof(CSVPSubFilter), pGB);
        if(pCAP){
          m_pCAP = pCAP;
          CComQIPtr<ISVPSubFilter> pSVPSub= pCAP;
          if(pSVPSub)
            m_pSVPSub = pSVPSub;
        }
      }

       如果找到 則進行相關操作
      if (m_pMC)
      {
        if (SetVMR9ColorControl(s.dBrightness, s.dContrast, s.dHue, s.dSaturation) == FALSE)
          OsdMsg_SetShader();
        else
        {
          CString  szMsg;
          szMsg.Format(ResStr(IDS_OSD_MSG_BRIGHT_CONTRAST_CHANGED), s.dBrightness, s.dContrast);
          SendStatusMessage(szMsg, 3000);
        }
        SetShaders(true);
      }

      // === EVR !
      pGB->FindInterface(__uuidof(IMFVideoDisplayControl), (void**)&m_pMFVDC,  TRUE);
      if (m_pMFVDC)
      {
        RECT Rect;
        ::GetClientRect (m_wndView.m_hWnd, &Rect);
        m_pMFVDC->SetVideoWindow (m_wndView.m_hWnd);
        m_pMFVDC->SetVideoPosition(NULL, &Rect);
      }

      打開終止異常
      if(m_fOpeningAborted) throw aborted;

      打開自定義大小的圖像
      OpenCustomizeGraph();   待分析

      打開終止異常
      if(m_fOpeningAborted) throw aborted;

      打開初始化視頻
      OpenSetupVideo();    待分析

      打開終止異常
      if(m_fOpeningAborted) throw aborted;

      打開初始化音頻
      OpenSetupAudio();    待分析

      打開終止異常
      if(m_fOpeningAborted) throw aborted;

      打開終止異常
      if(m_fOpeningAborted) throw aborted;

      改變聲道
      if(m_iAudioChannelMaping)
        OnAudioChannalMapMenu(IDS_AUDIOCHANNALMAPNORMAL+m_iAudioChannelMaping);  待分析

      加載完畢
      m_iMediaLoadState = MLS_LOADED;

      記錄開始播放時間
      time(&m_tPlayStartTime);

      發送暫停消息
      PostMessage(WM_COMMAND, ID_PLAY_PAUSE);  待分析

      如果是打開  則發送播放消息
      if(!(AfxGetAppSettings().nCLSwitches&CLSW_OPEN))
        PostMessage(WM_COMMAND, ID_PLAY_PLAY);          待分析

      AfxGetAppSettings().nCLSwitches &= ~CLSW_OPEN;

      if(OpenFileData* p = dynamic_cast<OpenFileData*>(pOMD.m_p))
      {
        如果有記錄 則跳到記錄位置
        if(p->rtStart > 0)
          PostMessage(WM_RESUMEFROMSTATE, (WPARAM)PM_FILE, (LPARAM)(p->rtStart/10000)); // REFERENCE_TIME doesn't fit in LPARAM under a 32bit env.
      }
      else if(OpenDVDData* p = dynamic_cast<OpenDVDData*>(pOMD.m_p))
      {
        if(p->pDvdState)
          PostMessage(WM_RESUMEFROMSTATE, (WPARAM)PM_DVD, (LPARAM)(CComPtr<IDvdState>(p->pDvdState).Detach())); // must be released by the called message handler
      }
      else if(OpenDeviceData* p = dynamic_cast<OpenDeviceData*>(pOMD.m_p))
      {
        m_wndCaptureBar.m_capdlg.SetVideoInput(p->vinput);
        m_wndCaptureBar.m_capdlg.SetVideoChannel(p->vchannel);
        m_wndCaptureBar.m_capdlg.SetAudioInput(p->ainput);
      }

      進行加載字幕
      if(m_pCAP && (!m_fAudioOnly || m_fRealMediaGraph))
      {
        POSITION pos = pOMD->subs.GetHeadPosition();
        while(pos){ LoadSubtitle(pOMD->subs.GetNext(pos));}
      }

      當前進程是不是主線程
      if(::GetCurrentThreadId() == AfxGetApp()->m_nThreadID)
      {
        OnFilePostOpenmedia();
      }
      else
      {
        PostMessage(WM_COMMAND, ID_FILE_POST_OPENMEDIA);    待分析
      }

      初始化標題
      OpenSetupWindowTitle(pOMD->title);    待分析

      初始化打開時間
      time_t tOpening = time(NULL);

      當狀態不是加載完成或正在加載 則循環  還在加載前則循環
      while(m_iMediaLoadState != MLS_LOADED 
        && m_iMediaLoadState != MLS_CLOSING // FIXME
        )
      {
        if( (time(NULL) - tOpening) > 10)
          throw aborted;
        Sleep(50);
      }
    }
    catch(LPCTSTR msg)
    {
      err = msg;
    }
    catch(CString msg)
    {
      err = msg;
    }
  }

  if(!err.IsEmpty())
  {
    存在錯誤
    SendStatusMessage(err, 3000);
    CloseMediaPrivate();
    m_closingmsg = err;

    OpenFileData* p = dynamic_cast<OpenFileData*>(pOMD.m_p);
    if(p && err != aborted)
    {
      m_wndPlaylistBar.SetCurValid(false);
      if(m_wndPlaylistBar.GetCount() > 1)
      {
        CPlaylistItem pli[2];
        m_wndPlaylistBar.GetCur(pli[0]);
        m_wndPlaylistBar.SetNext();
        m_wndPlaylistBar.GetCur(pli[1]);
        if(pli[0].m_id != pli[1].m_id)
        {
          //CAutoPtr<OpenMediaData> p(m_wndPlaylistBar.GetCurOMD());
          //if(p) OpenMediaPrivate(p);
        }
      }
    }
  }
  else
  {
    m_wndPlaylistBar.SetCurValid(true);  列表設置
  }

  調用主線程更新的東西
  PostMessage(WM_KICKIDLE); // calls main thread to update things  
  return(err.IsEmpty());
}

————————————————————————————————————————————
 2.5秒 循環載入
  case TIMER_LOADING:
    {
      KillTimer(TIMER_LOADING);
      if (IsSomethingLoading())
      {
        SetTimer( TIMER_LOADING , 1000, NULL);
        CString msg;
        if (m_fBuffering)
        {
          BeginEnumFilters(pGB, pEF, pBF)
          {
            if (CComQIPtr<IAMNetworkStatus, &IID_IAMNetworkStatus> pAMNS = pBF)
            {
              long BufferingProgress = 0;
              if (SUCCEEDED(pAMNS->get_BufferingProgress(&BufferingProgress)) && BufferingProgress > 0 && BufferingProgress < 99)
              {
                msg.Format(ResStr(IDS_CONTROLS_BUFFERING), BufferingProgress);
                SendStatusMessage(msg,1000);
                SVP_LogMsg5(msg);
              }
              break;
            }
          }
          EndEnumFilters
        }
        else if (pAMOP)
        {
          __int64 t = 0, c = 0;
          if (SUCCEEDED(pAMOP->QueryProgress(&t, &c)) && t > 0 && c < t)
          {
            msg.Format(ResStr(IDS_CONTROLS_BUFFERING), c*100/t);
            SendStatusMessage(msg,1000);
            SVP_LogMsg5(msg);
          }
        }
        else
          SendStatusMessage(ResStr(IDS_CONTROLS_OPENING), 1000);
      }
    }
    break;

   進入CMainFrame::OpenCreateGraphObject  分析
void CMainFrame::OpenCreateGraphObject(OpenMediaData* pOMD)
{
  解碼接口不为空
  ASSERT(pGB == NULL);
  m_fCustomGraph = false;
  m_fRealMediaGraph = m_fShockwaveGraph = m_fQuicktimeGraph = false;

  AppSettings& s = AfxGetAppSettings();

  if(OpenFileData* p = dynamic_cast<OpenFileData*>(pOMD))
  {
    獲得解碼類型
    engine_t engine = s.Formats.GetEngine(p->fns.GetHead());

    std::wstring ct = ContentType::Get(p->fns.GetHead());

    if(ct == L"video/x-ms-asf")
    {
      // TODO: put something here to make the windows media source filter load later
    }
    else if (ct == L"audio/x-pn-realaudio"
      || ct == L"audio/x-pn-realaudio-plugin"
      || ct == L"audio/x-realaudio-secure"
      || ct == L"video/vnd.rn-realvideo-secure"
      || ct == L"application/vnd.rn-realmedia"
      || ct.find(L"vnd.rn-") != ct.npos
      || ct.find(L"realaudio") != ct.npos
      || ct.find(L"realvideo") != ct.npos)
    {
      // TODO: go fuck this!!!
      engine = RealMedia;
    }
    else if (ct == L"application/x-shockwave-flash")
    {
      engine = ShockWave;
    }
    else if (ct == L"video/quicktime"
      || ct == L"application/x-quicktimeplayer")
    {
      engine = QuickTime;
    }
    if (engine == RealMedia)
      engine = DirectShow;

    SVP_LogMsg5(L"got content type %s %d", ct.c_str(), engine );

    HRESULT hr;
    CComPtr<IUnknown> pUnk;

    if(engine == RealMedia)
    { 
      if(!(pUnk = (IUnknown*)(INonDelegatingUnknown*)new CRealMediaGraph(m_wndView.m_hWnd, hr)))
        throw _T("Out of memory");

      if(SUCCEEDED(hr) && !!(pGB = CComQIPtr<IGraphBuilder>(pUnk)))
        m_fRealMediaGraph = true;
    }
    else if(engine == ShockWave)
    {
      if(!(pUnk = (IUnknown*)(INonDelegatingUnknown*)new CShockwaveGraph(m_wndView.m_hWnd, hr)))
        throw _T("Out of memory");

      if(FAILED(hr) || !(pGB = CComQIPtr<IGraphBuilder>(pUnk)))
        throw _T("Can't create shockwave control");

      m_fShockwaveGraph = true;
    }
    else if(engine == QuickTime)
    {
      if(!(pUnk = (IUnknown*)(INonDelegatingUnknown*)new CQuicktimeGraph(m_wndView.m_hWnd, hr)))
        throw _T("Out of memory");

      if(SUCCEEDED(hr) && !!(pGB = CComQIPtr<IGraphBuilder>(pUnk)))
        m_fQuicktimeGraph = true;
    }

    m_fCustomGraph = m_fRealMediaGraph || m_fShockwaveGraph || m_fQuicktimeGraph;

    if(!m_fCustomGraph)
    {
      視頻播放解碼 生成接口
      pGB = new CFGManagerPlayer(_T("CFGManagerPlayer"), NULL, s.SrcFilters, s.TraFilters, m_wndView.m_hWnd);
    }
  }
  else if(OpenDVDData* p = dynamic_cast<OpenDVDData*>(pOMD))
  {
    pGB = new CFGManagerDVD(_T("CFGManagerDVD"), NULL, s.SrcFilters, s.TraFilters, m_wndView.m_hWnd);
  }
  else if(OpenDeviceData* p = dynamic_cast<OpenDeviceData*>(pOMD))
  {
    pGB = new CFGManagerCapture(_T("CFGManagerCapture"), NULL, s.SrcFilters, s.TraFilters, m_wndView.m_hWnd);
  }

  if(!pGB)
  {
    throw _T("Failed to create the filter graph object");
  }

  pGB->AddToROT();

  把視頻解碼接口賦值给其它
  pMC = pGB; pME = pGB; pMS = pGB; // general
  pVW = pGB; pBV = pGB; // video
  pBA = pGB; // audio
  pFS = pGB;

  if(!(pMC && pME && pMS)
    || !(pVW && pBV)
    || !(pBA))
  {
    throw ResStr(IDS_MSG_THROW_BROKEN_DIRECTX_SUPPORT);
  }

  發送WM_GRAPHNOTIFY 消息
  if(FAILED(pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0)))
  {
    throw _T("Could not set target window for graph notification");
  }

  m_pProv = (IUnknown*)new CKeyProvider();   鍵盤提供者接口

  if(CComQIPtr<IObjectWithSite> pObjectWithSite = pGB)
    pObjectWithSite->SetSite(m_pProv);     鍵盤站點  捕獲鍵盤消息


  m_pCB = new CDSMChapterBag(NULL, NULL); dstuil庫中類
}

  進入OpenFile分析
void CMainFrame::OpenFile(OpenFileData* pOFD)
{
  if(pOFD->fns.IsEmpty())   文件为空  則拋出異常
    throw _T("Invalid argument");

  AppSettings& s = AfxGetAppSettings();  獲得設置信息

  bool fFirst = true;

  POSITION pos = pOFD->fns.GetHeadPosition();
  while(pos)
  {
    CString fn = pOFD->fns.GetNext(pos);

    fn.Trim();
    if(fn.IsEmpty() && !fFirst)
      break;

    CString fnLower = fn;
    fnLower.MakeLower();

    HRESULT hr = -1;
    如果不是mms://路徑或mmsh://路徑
    if(FAILED(hr) && ( fnLower.Find(_T("mms://")) == 0 || fnLower.Find(_T("mmsh://")) == 0 )){ // 
      //render mms our own way
      hr = OpenMMSUrlStream(fn);
    }
    if(FAILED(hr))  渲染文件  持續了很長時間
      hr = pGB->RenderFile(CStringW(fn), NULL);  

    如果是網络文件  則大概網络流
    if(FAILED(hr) && ( fnLower.Find(_T("http") == 0 || fnLower.Find(_T("https://")) == 0
      || fnLower.Find(_T("udp://")) == 0 || fnLower.Find(_T("tcp://")) == 0)) ){ // 
        //render mms our own way
        hr = OpenMMSUrlStream(fn);
    }

    /* not sure why this is not work for http youku etc  不知道這是为什麼不适用於HTTP優酷等
    HRESULT hr = -1;
    if( ( fn.MakeLower().Find(_T("mms://")) == 0 || fn.MakeLower().Find(_T("mmsh://")) == 0 || (fn.MakeLower().Find(_T("http:")) == 0 && fn.MakeLower().Find(_T(":8902")) > 0 ))){ 
    //render mms our own way 
    hr = OpenMMSUrlStream(fn);
    }

    if(FAILED(hr))
    hr = pGB->RenderFile(CStringW(fn), NULL);
    */

    依然失敗 則尋找錯誤  記錄錯誤
    if(FAILED(hr))
    {
      if(fFirst)
      {
        //if(s.fReportFailedPins)
        {
          CComQIPtr<IGraphBuilderDeadEnd> pGBDE = pGB;
          if(pGBDE && pGBDE->GetCount()) CMediaTypesDlg(pGBDE, this).DoModal();
        }

        CString err;

        switch(hr)
        {
        case E_ABORT: err = ResStr(IDS_MSG_THROW_OPRATION_CANCELED); break;
        case E_FAIL: case E_POINTER: default: 
          err.Format(ResStr(IDS_MSG_THROW_UNABLE_OPEN_FILE) , hr);
          break;
        case E_INVALIDARG: err = ResStr(IDS_MSG_THROW_ILLEGE_FILENAME); break;
        case E_OUTOFMEMORY: err = ResStr(IDS_MSG_THROW_OUTOF_MEMORY); break;
        case VFW_E_CANNOT_CONNECT: err = ResStr(IDS_MSG_THROW_UNABLE_DECODE); break;
        case VFW_E_CANNOT_LOAD_SOURCE_FILTER: err = ResStr(IDS_MSG_THROW_UNSUPPORT_SOURCE); break;
        case VFW_E_CANNOT_RENDER: err = ResStr(IDS_MSG_THROW_FAIL_CREATE_RENDER); break;
        case VFW_E_INVALID_FILE_FORMAT: err = _T("Invalid file format"); break;
        case VFW_E_NOT_FOUND: err = ResStr(IDS_MSG_THROW_FILE_NOT_FOUND); break;
        case VFW_E_UNKNOWN_FILE_TYPE: err = ResStr(IDS_MSG_THROW_UNKNOWN_FILE_TYPE); break;
        case VFW_E_UNSUPPORTED_STREAM: err = ResStr(IDS_MSG_THROW_UNSUPPORT_STREAM_TYPE); break;
        }

        throw err;
      }
    }

    是否保存記錄
    if(s.fKeepHistory)
    {
      if(this->m_lastUrl == fn){   本次與上次是否相同
        CRecentFileList* pMRU = &s.MRUUrl;
        pMRU->ReadList();
        pMRU->Add(fn);
        pMRU->WriteList();
        this->m_lastUrl.Empty();
      }else{
        不同則寫入
        CRecentFileList* pMRU = fFirst ? &s.MRU : &s.MRUDub;
        pMRU->ReadList();
        pMRU->Add(fn);
        pMRU->WriteList();
      }
    }

    if(fFirst)  第一次運行? 搜索字幕
    {
      AppSettings& s = AfxGetAppSettings();
      pOFD->title = fn;
      m_fnCurPlayingFile = fn;
      //是否有字幕? 沒有則下載字幕
      CSVPToolBox svpTool;
      //搜索目錄下同名字幕
      CAtlArray<CString> subSearchPaths;
      subSearchPaths.Add(_T("."));
      subSearchPaths.Add(s.GetSVPSubStorePath());
      subSearchPaths.Add(svpTool.GetPlayerPath(L"SVPSub"));
      subSearchPaths.Add(_T(".\\subtitles"));
      subSearchPaths.Add(_T(".\\Subs"));
      subSearchPaths.Add(_T("c:\\subtitles"));

      CAtlArray<SubFile> ret;

      POSITION pos = pOFD->subs.GetHeadPosition();
      while(pos){
        POSITION cur = pos;
        CString szSubFn = pOFD->subs.GetNext(pos);
        if(!svpTool.ifFileExist(szSubFn))
          pOFD->subs.RemoveAt(cur);
      }
      CSVPRarLib svpRar;
      if( svpRar.SplitPath(fn) ){
        GetSubFileNames(svpRar.m_fnRAR, subSearchPaths, ret);
        CAtlArray<SubFile> ret2;
        GetSubFileNames(svpRar.m_fnInsideRar, subSearchPaths, ret2);
        ret.Append(ret2);
      }else{
        GetSubFileNames(fn, subSearchPaths, ret);
        //AfxMessageBox(fn);
      }
      for(int i = 0; i < ret.GetCount(); i++){
        SubFile szBuf = ret.GetAt(i);
        //AfxMessageBox(szBuf.fn);
        if ( pOFD->subs.Find( szBuf.fn ) == NULL && svpTool.ifFileExist(szBuf.fn)){
          pOFD->subs.AddTail(szBuf.fn);
          //AfxMessageBox(szBuf.fn);
        }
      }
      //AfxMessageBox(_T("1"));
      if ( pOFD->subs.GetCount() <= 0){
        // AfxMessageBox(_T("2"));




        if(s.autoDownloadSVPSub){
          CPath fPath(fn);
          CString szExt;
          szExt.Format(_T(" %s;"),fPath.GetExtension());
          if(s.CheckSVPSubExts.Find(szExt) >= 0 ){
            SVPSubDownloadByVPath(fn);
          }else{
            //SendStatusMessage(  _T("正在播放的文件類型看來不需要字幕,終止自動智能匹配"), 1000);
          }
        }
      }
    }
    fFirst = false;

    if(m_fCustomGraph) break;
  }   智能匹配字幕結束

  //if(s.fReportFailedPins)
  {  如果繪圖掛了 類型對話框彈出
    CComQIPtr<IGraphBuilderDeadEnd> pGBDE = pGB;
    if(pGBDE && pGBDE->GetCount()) CMediaTypesDlg(pGBDE, this).DoModal();
  }   

  if(!(pAMOP = pGB))
  {
    BeginEnumFilters(pGB, pEF, pBF)
      if(pAMOP = pBF) break;
    EndEnumFilters
  }

  if(FindFilter(__uuidof(CShoutcastSource), pGB))
    m_fUpdateInfoBar = true;

  SetupChapters();  初始化章節

  枚舉 尋找IKeyFrameInfo 接口
  CComQIPtr<IKeyFrameInfo> pKFI;
  BeginEnumFilters(pGB, pEF, pBF)
    if(pKFI = pBF) break;
  EndEnumFilters
    UINT nKFs = 0, nKFsTmp = 0;
  if(pKFI && S_OK == pKFI->GetKeyFrameCount(nKFs) && nKFs > 0)
  {
    m_kfs.SetCount(nKFsTmp = nKFs);
    if(S_OK != pKFI->GetKeyFrames(&TIME_FORMAT_MEDIA_TIME, m_kfs.GetData(), nKFsTmp) || nKFsTmp != nKFs)
      m_kfs.RemoveAll();
  }

  m_iPlaybackMode = PM_FILE;
}

進入SetupChapters() 初始化章節
void CMainFrame::SetupChapters()
{
  ASSERT(m_pCB); 解碼不为空

  m_pCB->ChapRemoveAll();

  查找過滤接口  並添加到pBFs
  CInterfaceList<IBaseFilter> pBFs;
  BeginEnumFilters(pGB, pEF, pBF) 
    pBFs.AddTail(pBF);
  EndEnumFilters
  
    POSITION pos;

  循環遍曆過滤接口 進行追加操作 具體什麼 待分析
  pos = pBFs.GetHeadPosition();
  while(pos && !m_pCB->ChapGetCount())
  {
    IBaseFilter* pBF = pBFs.GetNext(pos);

    CComQIPtr<IDSMChapterBag> pCB = pBF;
    if(!pCB) continue;

    for(DWORD i = 0, cnt = pCB->ChapGetCount(); i < cnt; i++)
    {
      REFERENCE_TIME rt;
      CComBSTR name;
      if(SUCCEEDED(pCB->ChapGet(i, &rt, &name)))
        m_pCB->ChapAppend(rt, name);
    }
  }

  循環遍曆過滤接口 進行追加操作 具體什麼 待分析
  pos = pBFs.GetHeadPosition();
  while(pos && !m_pCB->ChapGetCount())
  {
    IBaseFilter* pBF = pBFs.GetNext(pos);

    CComQIPtr<IChapterInfo> pCI = pBF;
    if(!pCI) continue;

    CHAR iso6391[3];
    ::GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, iso6391, 3);
    CStringA iso6392 = ISO6391To6392(iso6391);
    if(iso6392.GetLength() < 3) iso6392 = "eng";

    UINT cnt = pCI->GetChapterCount(CHAPTER_ROOT_ID);
    for(UINT i = 1; i <= cnt; i++)
    {
      UINT cid = pCI->GetChapterId(CHAPTER_ROOT_ID, i);

      ChapterElement ce;
      if(pCI->GetChapterInfo(cid, &ce))
      {
        char pl[3] = {iso6392[0], iso6392[1], iso6392[2]};
        char cc[] = "  ";
        CComBSTR name;
        name.Attach(pCI->GetChapterStringInfo(cid, pl, cc));
        m_pCB->ChapAppend(ce.rtStart, name);
      }
    }
  }

  循環遍曆過滤接口 進行追加操作 具體什麼 待分析
  pos = pBFs.GetHeadPosition();
  while(pos && !m_pCB->ChapGetCount())
  {
    IBaseFilter* pBF = pBFs.GetNext(pos);

    CComQIPtr<IAMExtendedSeeking, &IID_IAMExtendedSeeking> pES = pBF;
    if(!pES) continue;

    long MarkerCount = 0;
    if(SUCCEEDED(pES->get_MarkerCount(&MarkerCount)))
    {
      for(long i = 1; i <= MarkerCount; i++)
      {
        double MarkerTime = 0;
        if(SUCCEEDED(pES->GetMarkerTime(i, &MarkerTime)))
        {
          CStringW name;
          name.Format(L"Chapter %d", i);

          CComBSTR bstr;
          if(S_OK == pES->GetMarkerName(i, &bstr))
            name = bstr;

          m_pCB->ChapAppend(REFERENCE_TIME(MarkerTime*10000000), name);
        }
      }
    }
  }

  循環遍曆過滤接口 進行追加操作 具體什麼 待分析
  pos = pBFs.GetHeadPosition();
  while(pos && !m_pCB->ChapGetCount())
  {
    IBaseFilter* pBF = pBFs.GetNext(pos);

    if(GetCLSID(pBF) != CLSID_OggSplitter)
      continue;

    BeginEnumPins(pBF, pEP, pPin)
    {
      if(m_pCB->ChapGetCount()) break;

      if(CComQIPtr<IPropertyBag> pPB = pPin)
      {
        for(int i = 1; ; i++)
        {
          CStringW str;
          CComVariant var;

          var.Clear();
          str.Format(L"CHAPTER%02d", i);
          if(S_OK != pPB->Read(str, &var, NULL)) 
            break;
          int h, m, s, ms;
          WCHAR wc;
          if(7 != swscanf(CStringW(var), L"%d%c%d%c%d%c%d", &h, &wc, &m, &wc, &s, &wc, &ms)) 
            break;
          CStringW name;
          name.Format(L"Chapter %d", i);
          var.Clear();
          str += L"NAME";
          if(S_OK == pPB->Read(str, &var, NULL))
            name = var;
          m_pCB->ChapAppend(10000i64*(((h*60 + m)*60 + s)*1000 + ms), name);
        }
      }
    }
    EndEnumPins
  }
  m_pCB->ChapSort();
}

   進入OpenCustomizeGraph  自定義圖像大小
void CMainFrame::OpenCustomizeGraph()
{
  總共有enum {PM_NONE, PM_FILE, PM_DVD, PM_CAPTURE}種模式

  PM_CAPTURE 模式 不支持自定義圖像大小
  if(m_iPlaybackMode == PM_CAPTURE)    return;

  CleanGraph();   清楚畫像

  if(m_iPlaybackMode == PM_FILE)  文件模式
  {
    if(m_pCAP) {
      if(AfxGetAppSettings().fAutoloadSubtitles) {
        AddTextPassThruFilter();    進行了很多連接  需要分析
      }
    }
  }
  
  同步視頻
  AppSettings& s = AfxGetAppSettings();
  if (s.m_RenderSettings.bSynchronizeVideo)
  {
    HRESULT hr;
    m_pRefClock = DNew CSyncClockFilter(NULL, &hr);
    CStringW name;
    name.Format(L"SyncClock Filter");
    pGB->AddFilter(m_pRefClock, name);

    CComPtr<IReferenceClock> refClock;
    m_pRefClock->QueryInterface(IID_IReferenceClock, reinterpret_cast<void**>(&refClock));
    CComPtr<IMediaFilter> mediaFilter;
    pGB->QueryInterface(IID_IMediaFilter, reinterpret_cast<void**>(&mediaFilter));
    mediaFilter->SetSyncSource(refClock);
    mediaFilter = NULL;
    refClock = NULL;

    m_pRefClock->QueryInterface(IID_ISyncClock, reinterpret_cast<void**>(&m_pSyncClock));
  }

  BeginEnumFilters(pGB, pEF, pBF)
  {
    查找CLSID_OggSplitter 控件
    if(GetCLSID(pBF) == CLSID_OggSplitter)
    {
       查找IAMStreamSelect接口
      if(CComQIPtr<IAMStreamSelect> pSS = pBF)
      {
        LCID idAudio = AfxGetAppSettings().idAudioLang;
        if(!idAudio) idAudio = GetUserDefaultLCID();
        LCID idSub = AfxGetAppSettings().idSubtitlesLang;
        if(!idSub) idSub = GetUserDefaultLCID();

        DWORD cnt = 0;
        pSS->Count(&cnt);
        for(DWORD i = 0; i < cnt; i++)
        {
          AM_MEDIA_TYPE* pmt = NULL;
          DWORD dwFlags = 0;
          LCID lcid = 0;
          DWORD dwGroup = 0;
          WCHAR* pszName = NULL;
          if(SUCCEEDED(pSS->Info((long)i, &pmt, &dwFlags, &lcid, &dwGroup, &pszName, NULL, NULL)))
          {
            CStringW name(pszName), sound(L"Sound"), subtitle(L"Subtitle");

            if(idAudio != -1 && (idAudio&0x3ff) == (lcid&0x3ff) // sublang seems to be zeroed out in ogm...
              && name.GetLength() > sound.GetLength()
              && !name.Left(sound.GetLength()).CompareNoCase(sound))
            {
              if(SUCCEEDED(pSS->Enable(i, AMSTREAMSELECTENABLE_ENABLE)))
                idAudio = -1;
            }

            if(idSub != -1 && (idSub&0x3ff) == (lcid&0x3ff) // sublang seems to be zeroed out in ogm...
              && name.GetLength() > subtitle.GetLength()
              && !name.Left(subtitle.GetLength()).CompareNoCase(subtitle)
              && name.Mid(subtitle.GetLength()).Trim().CompareNoCase(L"off"))
            {
              if(SUCCEEDED(pSS->Enable(i, AMSTREAMSELECTENABLE_ENABLE)))
                idSub = -1;
            }

            if(pmt) DeleteMediaType(pmt);
            if(pszName) CoTaskMemFree(pszName);
          }
        }
      }
    }
  }
  EndEnumFilters

    CleanGraph();
}

 進入OpenSetupVideo() 初始化視頻
void CMainFrame::OpenSetupVideo()
{
  只自動?
  m_fAudioOnly = true;

  if (m_pMFVDC) // EVR 
  {
    EVR  不支持
    m_fAudioOnly = false;
  }
  else if(m_pCAPR)
  {
     獲得視頻大小
    CSize vs = m_pCAPR->GetVideoSize();
    m_fAudioOnly = (vs.cx <= 0 || vs.cy <= 0);
  }
  else
  {
    否則其它
    {
      long w = 0, h = 0;

      if(CComQIPtr<IBasicVideo> pBV = pGB)
      {
        pBV->GetVideoSize(&w, &h);
      }

      if(w > 0 && h > 0)
      {
        m_fAudioOnly = false;
      }
    }

    if(m_fAudioOnly)
    {
      BeginEnumFilters(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)  flash控件
  {
    m_fAudioOnly = false;
  }

  if(m_pCAP)
  {
    SetShaders();
  }
  // else
  {
    // TESTME

    pVW->put_Owner((OAHWND)m_wndView.m_hWnd);
    pVW->put_WindowStyle(WS_CHILD|WS_CLIPSIBLINGS|WS_CLIPCHILDREN);
    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
  }
}

 進入SetShaders( BOOL silent )  設置着色
void CMainFrame::SetShaders( BOOL silent )
{
  if(!m_pCAPR) return;   不存在則直接返回

  AppSettings& s = AfxGetAppSettings();

  CAtlStringMap<const AppSettings::Shader*> s2s;

  POSITION pos = s.m_shaders.GetHeadPosition();
  while(pos)
  {
    const AppSettings::Shader* pShader = &s.m_shaders.GetNext(pos); 關鍵取值
    s2s[pShader->label] = pShader;
  }
  if(!silent){
    m_pCAPR->SetPixelShader(NULL, NULL);
    if (m_pCAP2)
      m_pCAP2->SetPixelShader2(NULL, NULL, true);
  }

  CAtlList<CString> labels;

  pos = m_shaderlabels.GetHeadPosition();
  while(pos)
  {
    const AppSettings::Shader* pShader = NULL;
    if(s2s.Lookup(m_shaderlabels.GetNext(pos), pShader))
    {
      CStringA target = pShader->target;
      CStringA srcdata = pShader->srcdata;

      HRESULT hr = m_pCAPR->SetPixelShader(srcdata, target );

      if(FAILED(hr))
      {
        //m_pCAP->SetPixelShader(NULL, NULL);
        if (m_pCAP2)
          hr = m_pCAP2->SetPixelShader2(srcdata, target, true);
        if(FAILED(hr)){
          // if (m_pCAP2)
          // m_pCAP2->SetPixelShader2(NULL, NULL, true,  !silent);
          if(!silent)
          {
            CString label = pShader->label;
            OsdMsg_SetShader(&label);
          }
        }
        return;
      }
      labels.AddTail(pShader->label);
    }
  }

  if(m_iMediaLoadState == MLS_LOADED)
  {
    CString str = Implode(labels, '|');
    str.Replace(_T("|"), _T(", "));
    if(!silent) SendStatusMessage(_T("Shader: ") + str, 3000);
  }
  if(!silent){
    if (SetVMR9ColorControl(s.dBrightness , s.dContrast, 0, 0, true) == FALSE)
      OsdMsg_SetShader();
  }
}

  進入OpenSetupAudio()  初始化音頻
void CMainFrame::OpenSetupAudio()
{
  賦值
  pBA->put_Volume(m_wndToolBar.Volume);

  // FIXME
  // 又是從設置中取得數據
  int balance = AfxGetAppSettings().nBalance;
  int sign = balance>0?-1:1;
  balance = max(100-abs(balance), 1);
  balance = (int)((log10(1.0*balance)-2)*5000*sign);
  balance = max(min(balance, 10000), -10000);
  pBA->put_Balance(balance);
}

  進入CMainFrame::OpenSetupWindowTitle 初始化標題
void CMainFrame::OpenSetupWindowTitle(CString fn)
{
  CString title(MAKEINTRESOURCE(IDR_MAINFRAME));

  AppSettings& s = AfxGetAppSettings();

  int i = s.iTitleBarTextStyle;

  if(!fn.IsEmpty() && (i == 0 || i == 1))
  {
    if(i == 1)
    {
      if(m_iPlaybackMode == PM_FILE)
      {
        fn.Replace('\\', '/');
        CString fn2 = fn.Mid(fn.ReverseFind('/')+1);
        if(!fn2.IsEmpty()) fn = fn2;

        if(s.fTitleBarTextTitle)
        {
          BeginEnumFilters(pGB, pEF, pBF)
          {
            if(CComQIPtr<IAMMediaContent, &IID_IAMMediaContent> pAMMC = pBF)
            {
              CComBSTR bstr;
              if(SUCCEEDED(pAMMC->get_Title(&bstr)) && bstr.Length())
              {
                fn = CString(bstr.m_str);
                break;
              }
            }
          }
          EndEnumFilters
        }
      }
      else if(m_iPlaybackMode == PM_DVD)
      {
        fn = _T("DVD");
      }
      else if(m_iPlaybackMode == PM_CAPTURE)
      {
        fn = _T("Live");
      }
    }
    title = fn + _T(" - ") + title;
  }
  //CString szBuild;
  //szBuild.Format(_T(" (Build %s)"),SVP_REV_STR);
  //title += szBuild;
  //SetWindowText(title);
  m_szTitle = title;
  RedrawNonClientArea();
}

在OpenMediaPrivate()中
PostMessage(WM_COMMAND, ID_PLAY_PAUSE);
if(!(AfxGetAppSettings().nCLSwitches&CLSW_OPEN))
        PostMessage(WM_COMMAND, ID_PLAY_PLAY);

 緊緊只是更新UI?
ON_UPDATE_COMMAND_UI(ID_PLAY_PLAY, OnUpdatePlayPauseStop)

   進入OnUpdatePlayPauseStop 分析
void CMainFrame::OnUpdatePlayPauseStop(CCmdUI* pCmdUI)
{
  OAFilterState fs = m_fFrameSteppingActive ? State_Paused : GetMediaState();

  UI消息
  pCmdUI->SetCheck(fs == State_Running && pCmdUI->m_nID == ID_PLAY_PLAY
    || fs == State_Paused && pCmdUI->m_nID == ID_PLAY_PAUSE
    || fs == State_Stopped && pCmdUI->m_nID == ID_PLAY_STOP
    || (fs == State_Paused || fs == State_Running) && pCmdUI->m_nID == ID_PLAY_PLAYPAUSE);

  bool fEnable = false;

  if(fs >= 0)
  {
    if(m_iPlaybackMode == PM_FILE || m_iPlaybackMode == PM_CAPTURE)
    {
      fEnable = true;

      不能進入暫停狀態,從停止使用rm
      if(fs == State_Stopped && pCmdUI->m_nID == ID_PLAY_PAUSE && m_fRealMediaGraph) fEnable = false; // can't go into paused state from stopped with rm
      else if(m_fCapturing) fEnable = false;
      else if(m_fLiveWM && pCmdUI->m_nID == ID_PLAY_PAUSE) fEnable = false;
    }
    else if(m_iPlaybackMode == PM_DVD)
    {
      fEnable = m_iDVDDomain != DVD_DOMAIN_VideoManagerMenu 
        && m_iDVDDomain != DVD_DOMAIN_VideoTitleSetMenu;

      if(fs == State_Stopped && pCmdUI->m_nID == ID_PLAY_PAUSE) fEnable = false;
    }
  }
  if(pCmdUI->m_nID == ID_PLAY_PLAY || pCmdUI->m_nID == ID_PLAY_PLAYPAUSE) fEnable = true;
  pCmdUI->Enable(fEnable);
}

ON_COMMAND(ID_PLAY_PAUSE, OnPlayPause)
void CMainFrame::OnPlayPause()
//支持ffdshow的排隊。
//为了避免暫停黑掉,我們以锁定g_ffdshowReceive同步與ReceiveMine的。
void CMainFrame::OnPlayPause()
{
  // Support ffdshow queueing.
  // To avoid black out on pause, we have to lock g_ffdshowReceive to synchronize with ReceiveMine.
  time(&m_tPlayPauseTime);
  if(queueu_ffdshow_support)
  {
    CAutoLock lck(&g_ffdshowReceive);
    return OnPlayPauseI();
  }
  OnPlayPauseI();
}

  進入OnPlayPauseI()分析
void CMainFrame::OnPlayPauseI()
{
  if(m_iMediaLoadState == MLS_LOADED)   已經加載
  {
    if(m_iPlaybackMode == PM_FILE)
    {
      pMC->Pause();   停止
    }
    else if(m_iPlaybackMode == PM_DVD)
    {
      pMC->Pause();
    }
    else if(m_iPlaybackMode == PM_CAPTURE)
    {
      pMC->Pause();
    } 

    SetTimer(TIMER_STREAMPOSPOLLER, 40, NULL);
    SetTimer(TIMER_STREAMPOSPOLLER2, 500, NULL);
    SetTimer(TIMER_STATS, 1000, NULL);

    KillTimer(TIMER_IDLE_TASK);
    SetTimer(TIMER_IDLE_TASK, 30000, NULL);

    SetAlwaysOnTop(AfxGetAppSettings().iOnTop);  設置置頂
  }
  MoveVideoWindow();
}

    移動視頻窗體
void CMainFrame::MoveVideoWindow(bool fShowStats)
{
   視頻已經加載  不是自動  窗體可見
  if(m_iMediaLoadState == MLS_LOADED && !m_fAudioOnly && IsWindowVisible())
  {
    AppSettings &s = AfxGetAppSettings();
    CRect wr;

    if(!m_fFullScreen && (s.bUserAeroUI() || (!s.bUserAeroUI() && ( s.nCS & CS_TOOLBAR ))) )
    {
      m_wndView.GetClientRect(wr);
    }
    else
    {
      GetWindowRect(&wr);
      // HACK
      CRect r;
      m_wndView.GetWindowRect(&r);
      wr -= r.TopLeft();
    }
    CRect vr = CRect(0,0,0,0);

    OAFilterState fs = GetMediaState();
    if(fs == State_Paused || fs == State_Running || fs == State_Stopped && (m_fShockwaveGraph || m_fQuicktimeGraph))
    {
      CSize arxy = GetVideoSize();

      int iDefaultVideoSize = AfxGetAppSettings().iDefaultVideoSize;

      CSize ws = 
        iDefaultVideoSize == DVS_HALF ? CSize(arxy.cx/2, arxy.cy/2) :
        iDefaultVideoSize == DVS_NORMAL ? arxy :
        iDefaultVideoSize == DVS_DOUBLE ? CSize(arxy.cx*2, arxy.cy*2) :
        wr.Size();

      int w = ws.cx;
      int h = ws.cy;

      if(!m_fShockwaveGraph) //&& !m_fQuicktimeGraph)
      {
        if(iDefaultVideoSize == DVS_FROMINSIDE || iDefaultVideoSize == DVS_FROMOUTSIDE)
        {
          h = ws.cy;
          w = MulDiv(h, arxy.cx, arxy.cy);

          if(iDefaultVideoSize == DVS_FROMINSIDE && w > ws.cx
            || iDefaultVideoSize == DVS_FROMOUTSIDE && w < ws.cx)
          {
            w = ws.cx;
            h = MulDiv(w, arxy.cy, arxy.cx);
          }
        }
      }

      CSize size(
        (int)(m_ZoomX*w), 
        (int)(m_ZoomY*h));

      CPoint pos(
        (int)(m_PosX*(wr.Width()*3 - m_ZoomX*w) - wr.Width()), 
        (int)(m_PosY*(wr.Height()*3 - m_ZoomY*h) - wr.Height()));

      /* CPoint pos(
      (int)(m_PosX*(wr.Width() - size.cx)), 
      (int)(m_PosY*(wr.Height() - size.cy)));


      */
      vr = CRect(pos, size);
    }

    wr |= CRect(0,0,0,0);
    vr |= CRect(0,0,0,0);

    //CString szLog;
    //szLog.Format(_T("WVSize3 %d %d %d %d %d %d %d "), wr.Width(), wr.Height(), vr.Width(), vr.Height(), m_AngleX , m_AngleY , m_AngleZ);
    //SVP_LogMsg(szLog);
    if(m_pCAPR)
    {
      m_pCAPR->SetPosition(wr, vr);
      m_pCAPR->SetVideoAngle(Vector(DegToRad(m_AngleX), DegToRad(m_AngleY), DegToRad(m_AngleZ)));
    }
    else
    {
      HRESULT hr;
      hr = pBV->SetDefaultSourcePosition();
      hr = pBV->SetDestinationPosition(vr.left, vr.top, vr.Width(), vr.Height());
      hr = pVW->SetWindowPosition(wr.left, wr.top, wr.Width(), wr.Height());

      if (m_pMFVDC) m_pMFVDC->SetVideoPosition (NULL, wr);
    }
    //SVP_LogMsg5(_T("MoveVideoWindow %d ") , wr.Height());
    m_wndView.SetVideoRect(wr);

    if(fShowStats && vr.Height() > 0)
    {
      CString info(L"正在使用智能拖拽功能。可在畫面右上角進行拖拽");
      // info.Format(_T("Pos %.2f %.2f, Zoom %.2f %.2f, AR %.2f"), m_PosX, m_PosY, m_ZoomX, m_ZoomY, (float)vr.Width()/vr.Height());
      SendStatusMessage(info, 3000);
    }
  }
  else
  {
    m_wndView.SetVideoRect();
  }

  CRect r;
  m_wndView.GetClientRect(r);
  if(m_iMediaLoadState == MLS_LOADED){
    float fViewRatio = (float)r.Width() / r.Height();
    CSize vsize = GetVideoSize();
    float fVideoRatio = (float)vsize.cx / vsize.cy;
    m_fScreenHigherThanVideo = (fViewRatio < fVideoRatio );
  }
}

  僅僅只是更新UI
void CMainFrame::OnUpdatePlayPauseStop(CCmdUI* pCmdUI)
{
  OAFilterState fs = m_fFrameSteppingActive ? State_Paused : GetMediaState();

  pCmdUI->SetCheck(fs == State_Running && pCmdUI->m_nID == ID_PLAY_PLAY
    || fs == State_Paused && pCmdUI->m_nID == ID_PLAY_PAUSE
    || fs == State_Stopped && pCmdUI->m_nID == ID_PLAY_STOP
    || (fs == State_Paused || fs == State_Running) && pCmdUI->m_nID == ID_PLAY_PLAYPAUSE);

  bool fEnable = false;

  if(fs >= 0)
  {
    if(m_iPlaybackMode == PM_FILE || m_iPlaybackMode == PM_CAPTURE)
    {
      fEnable = true;

      if(fs == State_Stopped && pCmdUI->m_nID == ID_PLAY_PAUSE && m_fRealMediaGraph) fEnable = false; // can't go into paused state from stopped with rm
      else if(m_fCapturing) fEnable = false;
      else if(m_fLiveWM && pCmdUI->m_nID == ID_PLAY_PAUSE) fEnable = false;
    }
    else if(m_iPlaybackMode == PM_DVD)
    {
      fEnable = m_iDVDDomain != DVD_DOMAIN_VideoManagerMenu 
        && m_iDVDDomain != DVD_DOMAIN_VideoTitleSetMenu;

      if(fs == State_Stopped && pCmdUI->m_nID == ID_PLAY_PAUSE) fEnable = false;
    }
  }
  if(pCmdUI->m_nID == ID_PLAY_PLAY || pCmdUI->m_nID == ID_PLAY_PLAYPAUSE) fEnable = true;
  pCmdUI->Enable(fEnable);
}

// play 播放事件
void CMainFrame::OnPlayPlay()
{
  if(m_iMediaLoadState == MLS_LOADED) 必須載入
  {
    if(GetMediaState() == State_Stopped) {  m_iSpeedLevel = 0; time(&m_tPlayStartTime);} 記錄
    try
    {
      if(m_iPlaybackMode == PM_FILE)  文件格式
      {
        time_t ttNow;
        time(&ttNow);
        if( m_tPlayPauseTime > m_tPlayStartTime){
          m_tPlayStartTime += (ttNow - m_tPlayPauseTime);
        }
        if(m_fEndOfStream){   如果文件流沒有 則停止
          SendMessage(WM_COMMAND, ID_PLAY_STOP);
          Sleep(1500);
        }
        pMC->Run();   運行
      }
      else if(m_iPlaybackMode == PM_DVD) DVD格式
      {
        double dRate = 1.0;
        //if(m_iSpeedLevel != -4 && m_iSpeedLevel != 0)
        // dRate = pow(2.0, m_iSpeedLevel >= -3 ? m_iSpeedLevel : (-m_iSpeedLevel - 8));
        dRate = 1.0 + m_iSpeedLevel * 0.1;
        pDVDC->PlayForwards(dRate, DVD_CMD_FLAG_Block, NULL);
        pDVDC->Pause(FALSE);
        pMC->Run();
      }
      else if(m_iPlaybackMode == PM_CAPTURE)  CAPTURE 格式
      { 
        pMC->Stop(); // audio preview won't be in sync if we run it from paused state
        pMC->Run();
      }
    }
    catch (...)
    {
      SendMessage(WM_COMMAND, ID_FILE_CLOSEMEDIA);  異常 就關閉視頻
      return;
    }
    SetTimer(TIMER_STREAMPOSPOLLER, 40, NULL);  位置信息
    SetTimer(TIMER_STREAMPOSPOLLER2, 500, NULL);  位置2信息
    SetTimer(TIMER_STATS, 1000, NULL);   狀態信息

    if(m_fFrameSteppingActive) // FIXME   第一次  設置聲音
    {
      m_fFrameSteppingActive = false;
      pBA->put_Volume(m_VolumeBeforeFrameStepping);
    }

    SetAlwaysOnTop(AfxGetAppSettings().iOnTop);   置頂
    m_wndColorControlBar.CheckAbility();   控件狀態位置等信息
    MoveVideoWindow();   移動視頻
  }else if(m_iMediaLoadState == MLS_CLOSED){  如果是關閉狀態 則重新播放
    if(m_WndSizeInited >= 2){
      if(m_wndPlaylistBar.GetCount()){
        OpenCurPlaylistItem();
        //}else
        // SendMessage(WM_COMMAND, ID_FILE_OPENMEDIA);
      }
    }
  }
  //KillTimer(TIMER_SNAP);
  //SetTimer(TIMER_SNAP, 10000, NULL);
}

  進入OnFilePostOpenmedia() 分析
void CMainFrame::OnFilePostOpenmedia()
{
  OpenSetupClipInfo();  初始化剪切板信息
  //A-B Control
  m_aRefTime = 0;
  m_bRefTime = 0;
  ABControlOn = FALSE;

  OpenSetupCaptureBar();  初始化控件

  AppSettings& s = AfxGetAppSettings();
  m_wndColorControlBar.CheckAbility();  檢查能力

  if(m_wndToolBar.IsVisible())
    ShowControls(AfxGetAppSettings().nCS | CS_SEEKBAR, false);  顯示控件

  __int64 rtDur = 0;
  pMS->GetDuration(&rtDur);   從視頻接口獲得當前位置
  m_wndPlaylistBar.SetCurTime(rtDur);  設置控件位置

  if(m_iPlaybackMode == PM_CAPTURE)
  {
    ShowControlBar(&m_wndPlaylistBar, FALSE, TRUE);
    ShowControlBar(&m_wndCaptureBar, TRUE, TRUE);
  }
  
/ /重要:不能調用任何窗口封郵件之前
  / /這一點上,它會僵局時OpenMediaPrivate
  / /仍在運行,並創建渲染窗口
  / /相同的工作者線程
  std::wstring szFileHash = HashController::GetInstance()->GetSPHash(m_fnCurPlayingFile);

  CString FPath = szFileHash.c_str();

   加載第二字幕
  if(m_pCAP && (!m_fAudioOnly || m_fRealMediaGraph))
  {
    if(m_pSubStreams.GetCount() == 0){
      if ( !m_wndPlaylistBar.m_pl.szPlayListSub.IsEmpty() ){
        int delayms = 0 - m_wndPlaylistBar.GetTotalTimeBeforeCur();
        LoadSubtitle(m_wndPlaylistBar.m_pl.szPlayListSub, delayms , true); 
      }
    }

    if(s.fEnableSubtitles && m_pSubStreams.GetCount() > 0){
      BOOL HavSubs1 = FALSE;

      Sql 操作  搜索曆史記錄
      if(AfxGetMyApp()->sqlite_local_record ){
        CString szSQL;
        szSQL.Format(L"SELECT subid FROM histories WHERE fpath = \"%s\" ", FPath);
        int subid = AfxGetMyApp()->sqlite_local_record->get_single_int_from_sql(szSQL.GetBuffer(), -1);
        if(subid >= 0){
          //SVP_LogMsg5(L"subid %d %d", subid, m_pSubStreams.GetCount());
          int i = subid;

      字幕流?
          POSITION pos = m_pSubStreams.GetHeadPosition();
          while(pos && i >= 0)
          {
            循環查找
            CComPtr<ISubStream> pSubStream = m_pSubStreams.GetNext(pos);

            if(i < pSubStream->GetStreamCount()) 
            {
              SendStatusMessage(ResStr(IDS_OSD_MSG_RESTORE_TO_LAST_REMEMBER_SUBTITLE_TRACK),3000);
              CAutoLock cAutoLock(&m_csSubLock);
              pSubStream->SetStream(i);
              SetSubtitle(pSubStream);
              HavSubs1 = true;
              break;
            }
            i -= pSubStream->GetStreamCount();
          }
        }
      }

      字幕流?
      if(!HavSubs1 && !s.sSubStreamName1.IsEmpty()){
        POSITION pos = m_pSubStreams.GetHeadPosition();
        while(pos){
          CComPtr<ISubStream> pSubStream = m_pSubStreams.GetNext(pos);
          if(!pSubStream) continue;
          for(int i = 0, j = pSubStream->GetStreamCount(); i < j; i++)
          {
            WCHAR* pName = NULL;
            if(SUCCEEDED(pSubStream->GetStreamInfo(i, &pName, NULL)))
            {
              CString name(pName);
              //SVP_LogMsg5(L"sub2 %s", name);
              if( name == s.sSubStreamName1){
                SetSubtitle( pSubStream);
                HavSubs1 = true;
              }
              CoTaskMemFree(pName);
              if(HavSubs1)
                break;
            }
          }
          if(HavSubs1)
            break;
        }
      }

      設置字幕流
      if(!HavSubs1){
        POSITION pos = m_pSubStreams.GetHeadPosition();
        while(pos){
          CComPtr<ISubStream> pSubStream = m_pSubStreams.GetNext(pos);
          if(!pSubStream) continue;
          for(int i = 0, j = pSubStream->GetStreamCount(); i < j; i++)
          {
            WCHAR* pName = NULL;
            if(SUCCEEDED(pSubStream->GetStreamInfo(i, &pName, NULL)))
            {
              CString name(pName);
              //SVP_LogMsg5(L"sub2 %s", name);
              if( name.Find(L"plain text") > 0){ //Apple Text Media Handler (plain text)
                SetSubtitle( pSubStream);
                HavSubs1 = true;
              }
              CoTaskMemFree(pName);
              if(HavSubs1)
                break;
            }
          }
          if(HavSubs1)
            break;
        }
      }

      字幕流
      if(!HavSubs1)
        SetSubtitle(m_pSubStreams.GetHead());

      if(s.fAutoloadSubtitles2 && m_pSubStreams2.GetCount() > 1 ){
        BOOL HavSubs = false;
        if(AfxGetMyApp()->sqlite_local_record ){
          CString szSQL;
          szSQL.Format(L"SELECT subid2 FROM histories WHERE fpath = \"%s\" ", FPath);
          int subid = AfxGetMyApp()->sqlite_local_record->get_single_int_from_sql(szSQL.GetBuffer(), -1);
          if(subid >= 0){
            //SVP_LogMsg5(L"subid %d %d", subid, m_pSubStreams.GetCount());
            int i = subid;
            POSITION pos = m_pSubStreams2.GetHeadPosition();
            while(pos && i >= 0)
            {
              CComPtr<ISubStream> pSubStream = m_pSubStreams2.GetNext(pos);
              if(i < pSubStream->GetStreamCount()) 
              {
                CAutoLock cAutoLock(&m_csSubLock);
                pSubStream->SetStream(i);
                SetSubtitle2(pSubStream);
                //SendStatusMessage(ResStr(IDS_OSD_MSG_RESTORE_TO_LAST_REMEMBER_SUBTITLE2_TRACK),3000);
                HavSubs = true;
                break;
              }
              i -= pSubStream->GetStreamCount();
            }
          }
        }

        字幕流
        if(!HavSubs && !s.sSubStreamName2.IsEmpty()){
          POSITION pos = m_pSubStreams2.GetHeadPosition();
          while(pos){
            CComPtr<ISubStream> pSubStream = m_pSubStreams2.GetNext(pos);

            if(!pSubStream) continue;

            for(int i = 0, j = pSubStream->GetStreamCount(); i < j; i++)
            {
              WCHAR* pName = NULL;
              if(SUCCEEDED(pSubStream->GetStreamInfo(i, &pName, NULL)))
              {
                CString name(pName);
                //SVP_LogMsg5(L"sub2 %s", name);
                if((!s.sSubStreamName2.IsEmpty() && name == s.sSubStreamName2)  ){
                  SetSubtitle2( pSubStream);
                  HavSubs = true;
                }
                CoTaskMemFree(pName);
                if(HavSubs)
                  break;
              }
            }
            if(HavSubs)
              break;
          }
        }

        字幕流
        if(!HavSubs){
          POSITION pos = m_pSubStreams2.GetHeadPosition();
          while(pos){
            CComPtr<ISubStream> pSubStream = m_pSubStreams2.GetNext(pos);

            if(!pSubStream) continue;

            for(int i = 0, j = pSubStream->GetStreamCount(); i < j; i++)
            {
              WCHAR* pName = NULL;
              if(SUCCEEDED(pSubStream->GetStreamInfo(i, &pName, NULL)))
              {
                CString name(pName);
                //SVP_LogMsg5(L"sub2 %s", name);
                if( ( name.Find(_T("en")) >= 0 ||  name.Find(_T("eng")) >= 0 ||  name.Find(_T("英文")) >= 0) ){
                  SetSubtitle2( pSubStream);
                  HavSubs = true;
                }
                CoTaskMemFree(pName);
                if(HavSubs)
                  break;
              }
            }
            if(HavSubs)
              break;
          }
        }
        if(!HavSubs){
          POSITION pos = m_pSubStreams2.GetHeadPosition();
          m_pSubStreams2.GetNext(pos);
          if(pos)
            SetSubtitle2( m_pSubStreams2.GetNext(pos));
        }
      }
    }
    //make sure the subtitle displayed
    UpdateSubtitle(true);  設置字幕流
    UpdateSubtitle2(true);  設置字幕流
  }

  根據Sql查找曆史   發送狀態
  if(AfxGetMyApp()->sqlite_local_record ){
    CString szSQL;
    szSQL.Format(L"SELECT audioid FROM histories WHERE fpath = \"%s\" ", FPath);
    //SVP_LogMsg5(szSQL);
    int audid = AfxGetMyApp()->sqlite_local_record->get_single_int_from_sql(szSQL.GetBuffer(), -1);
    if(audid > 0){
      SVP_LogMsg5(L"audid %d subs %d", audid, m_pSubStreams.GetCount());
      CComQIPtr<IAMStreamSelect> pSS = FindFilter(__uuidof(CAudioSwitcherFilter), pGB);
      if(!pSS) pSS = FindFilter(L"{D3CD7858-971A-4838-ACEC-40CA5D529DC8}", pGB);
      if( pSS)
      {
        pSS->Enable(audid, AMSTREAMSELECTENABLE_ENABLE);
        SendStatusMessage(ResStr(IDS_OSD_MSG_RESTORE_TO_LAST_REMEMBER_AUDIO_TRACK),3000);
      }
    }
  }

  根據SQL 設置字幕
  if (AfxGetMyApp()->sqlite_local_record)
  {
    CString szSQL;
    if (s.fEnableSubtitles ){
      szSQL.Format(L"SELECT subid FROM histories_stream WHERE fpath = \"%s\" ", FPath);
      int subid = AfxGetMyApp()->sqlite_local_record->get_single_int_from_sql(szSQL.GetBuffer(), -1);
      if(subid > 0){
        OnPlayLanguage(subid);
      }
    }
    szSQL.Format(L"SELECT audioid FROM histories_stream WHERE fpath = \"%s\" ", FPath);
    int audid = AfxGetMyApp()->sqlite_local_record->get_single_int_from_sql(szSQL.GetBuffer(), -1);
    if(audid > 0){
      OnPlayLanguage(audid);
    }
  }

  if(!m_pCAP && m_fAudioOnly){ / /這是我們第一次檢測,這是僅音頻文件

    m_wndView.m_strAudioInfo.Empty();
    //This is silly
    if(!m_fLastIsAudioOnly)
      ShowControlBar(&m_wndPlaylistBar, FALSE, TRUE);

    KillTimer(TIMER_TRANSPARENTTOOLBARSTAT);
    SetTimer(TIMER_TRANSPARENTTOOLBARSTAT, 20,NULL);
    rePosOSD();  重新計算位置
  }

  {
    WINDOWPLACEMENT wp;
    wp.length = sizeof(wp);
    GetWindowPlacement(&wp);  記錄窗體位置

    // restore magnification   只有音頻的情況下
    if(IsWindowVisible() && AfxGetAppSettings().fRememberZoomLevel
      && !(m_fFullScreen || wp.showCmd == SW_SHOWMAXIMIZED || wp.showCmd == SW_SHOWMINIMIZED))
    {
      if(m_fAudioOnly && m_fLastIsAudioOnly){
      }else{
        ZoomVideoWindow();  全屏播放
        // m_fFullScreen意思現在是指當前播放模式是全屏還是非全屏,如果是全屏就會在Toogle中變为非全屏,反之亦然
        if (!m_fFullScreen)
        {
          PlayerPreference* pref = PlayerPreference::GetInstance();
          bool bToBeFullScreen = pref->GetIntVar(INTVAR_TOGGLEFULLSCRENWHENPLAYBACKSTARTED);
          if (bToBeFullScreen)   是否全屏
          {
            ToggleFullscreen(true, true);
            SetCursor(NULL);
          }
        }
      }
      m_fLastIsAudioOnly = m_fAudioOnly;
    }
  }

  if(!m_fAudioOnly && (s.nCLSwitches&CLSW_FULLSCREEN))  全屏時
  {
    SendMessage(WM_COMMAND, ID_VIEW_FULLSCREEN);
    s.nCLSwitches &= ~CLSW_FULLSCREEN;
  }
  m_bDxvaInUse = false;
  m_bMustUseExternalTimer = false;
  m_haveSubVoted = false;
  m_DXVAMode = _T("");
  視頻解碼接口
  CComQIPtr<IMPCVideoDecFilter> pMDF  = FindFilter(__uuidof(CMPCVideoDecFilter), pGB);
  if(pMDF){
    GUID* DxvaGui = NULL;
    DxvaGui = pMDF->GetDXVADecoderGuid();
    if (DxvaGui != NULL)
    {
      m_DXVAMode = GetDXVAMode (DxvaGui);
      m_bDxvaInUse = (m_DXVAMode != _T("Not using DXVA"));
    }
  }else if(FindFilter(L"{09571A4B-F1FE-4C60-9760-DE6D310C7C31}", pGB)) {
    if(s.bHasCUDAforCoreAVC){
      m_bDxvaInUse = true;
      m_DXVAMode = _T("CoreAVC");
    }
  }
  /*
  CComQIPtr<IWMReaderAdvanced2> pWMR  = FindFilter(__uuidof(IWMReaderAdvanced2), pGB);
  if(pWMR){
  pWMR->SetPlayMode(WMT_PLAY_MODE_STREAMING);
  AfxMessageBox(L"1");
  }*/
  if(FindFilter(L"{FA10746C-9B63-4B6C-BC49-FC300EA5F256}", pGB)){
    m_bEVRInUse = true;
  }
  if( FindFilter(L"{6F513D27-97C3-453C-87FE-B24AE50B1601}", pGB)){//m_bEVRInUse
    SVP_LogMsg5(L"Has Divx H264 Dec and EVR so use External Timer");
    m_bMustUseExternalTimer = true;
  }

  RedrawNonClientArea();  重新繪畫客戶區

  if(m_iPlaybackMode == PM_FILE){  回放模式
    if(!s.bDontNeedSVPSubFilter && !m_pCAP && s.iSVPRenderType && !m_fAudioOnly ){
      s.iSVPRenderType = 0;
      SendStatusMessage( ResStr(IDS_OSD_MSG_DEVICE_NOT_SUPPORT_VIDEO_QMODE), 2000);
    }

    if(m_fAudioOnly && m_fnCurPlayingFile.Find(L"://") < 0){
       只有音頻時  查找歌詞
      //find lyric file
      m_LyricFilePaths.RemoveAll();    
      CAtlArray<CString> lrcSearchPaths;
      lrcSearchPaths.Add(_T("."));
      lrcSearchPaths.Add(s.GetSVPSubStorePath());

      CAtlArray<LrcFile> ret;
      int bGotLrc = 0 ; 
      m_Lyric.GetLrcFileNames( m_fnCurPlayingFile , lrcSearchPaths, ret);
      if( ret.GetCount() ){
        LrcFile oLrcFile = ret.GetAt(0);
        if( m_Lyric.LoadLyricFile( oLrcFile.fn) >= 0)  裝載歌詞
        {
          //maybe we should do something here?
          if(m_Lyric.m_has_lyric)
            bGotLrc = 1;
        }
      }
      if( !bGotLrc && s.autoDownloadSVPSub  ) {   沒有歌詞 但自動下載
        //debug
        //m_Lyric.LoadLyricFile(L"D:\\-=SVN=-\\test.lrc");

        m_Lyric.Empty();
        //download it by thead
        m_Lyric.title = GetClipInfo(IDS_INFOBAR_TITLE).c_str();
        m_Lyric.artist = GetClipInfo(IDS_INFOBAR_AUTHOR).c_str();
        m_Lyric.album = GetClipInfo(IDS_INFOBAR_DESCRIPTION).c_str();
        m_Lyric.m_sz_current_music_file = m_fnCurPlayingFile;
        //m_Lyric.m_stop_downloading = 1;
        //TerminateThread(m_lyricDownloadThread , 0);
        启動線程下載
        m_lyricDownloadThread = AfxBeginThread(lyric_fetch_Proc, &m_Lyric, THREAD_PRIORITY_LOWEST, 0, CREATE_SUSPENDED);
        m_lyricDownloadThread->m_pMainWnd = AfxGetMainWnd();
        m_lyricDownloadThread->ResumeThread();
      }
    }
  }
  // send sphash to remote  發送sphash到遠程
  m_wndToolBar.HideMovieShareBtn(TRUE);
  UserShareController::GetInstance()->HideCommentPlane();
  UserShareController::GetInstance()->CloseShooterMedia();
  if(IsSomethingLoaded() && !m_fAudioOnly && (UINT)((INT64)rtDur/10000000) > 90)
  {
    m_movieShared = false;
    m_wndToolBar.HideMovieShareBtn(FALSE);
    SetTimer(TIMER_MOVIESHARE, 300000, NULL);   
  }
  KillTimer(TIMER_IDLE_TASK);
}

  進入CMainFrame::OpenSetupCaptureBar 分析
void CMainFrame::OpenSetupCaptureBar()  初始化頭導航
{
  初始化控件
  if(m_iPlaybackMode == PM_CAPTURE)
  {
    if(pVidCap && pAMVSCCap) 
    {
      CComQIPtr<IAMVfwCaptureDialogs> pVfwCD = pVidCap;

      if(!pAMXBar && pVfwCD)
      {
        m_wndCaptureBar.m_capdlg.SetupVideoControls(m_VidDispName, pAMVSCCap, pVfwCD);
      }
      else
      {
        m_wndCaptureBar.m_capdlg.SetupVideoControls(m_VidDispName, pAMVSCCap, pAMXBar, pAMTuner);
      }
    }

    if(pAudCap && pAMASC)
    {
      CInterfaceArray<IAMAudioInputMixer> pAMAIM;
      BeginEnumPins(pAudCap, pEP, pPin)
      {
        if(CComQIPtr<IAMAudioInputMixer> pAIM = pPin) 
          pAMAIM.Add(pAIM);
      }
      EndEnumPins
        m_wndCaptureBar.m_capdlg.SetupAudioControls(m_AudDispName, pAMASC, pAMAIM);
    }
  }

  BuildGraphVideoAudio(
    m_wndCaptureBar.m_capdlg.m_fVidPreview, false, 
    m_wndCaptureBar.m_capdlg.m_fAudPreview, false);
}

  進入CMainFrame::BuildGraphVideoAudio 分析
bool CMainFrame::BuildGraphVideoAudio(int fVPreview, bool fVCapture, int fAPreview, bool fACapture)
{
  if(!pCGB) return(false);  不存在直接返回  關鍵是pCGB另一個

  SaveMediaState;

  HRESULT hr;

  pGB->NukeDownstream(pVidCap);
  pGB->NukeDownstream(pAudCap);

  CleanGraph();

  if(pAMVSCCap) hr = pAMVSCCap->SetFormat(&m_wndCaptureBar.m_capdlg.m_mtv);
  if(pAMVSCPrev) hr = pAMVSCPrev->SetFormat(&m_wndCaptureBar.m_capdlg.m_mtv);
  if(pAMASC) hr = pAMASC->SetFormat(&m_wndCaptureBar.m_capdlg.m_mta);

  CComPtr<IBaseFilter> pVidBuffer = m_wndCaptureBar.m_capdlg.m_pVidBuffer;
  CComPtr<IBaseFilter> pAudBuffer = m_wndCaptureBar.m_capdlg.m_pAudBuffer;
  CComPtr<IBaseFilter> pVidEnc = m_wndCaptureBar.m_capdlg.m_pVidEnc;
  CComPtr<IBaseFilter> pAudEnc = m_wndCaptureBar.m_capdlg.m_pAudEnc;
  CComPtr<IBaseFilter> pMux = m_wndCaptureBar.m_capdlg.m_pMux;
  CComPtr<IBaseFilter> pDst = m_wndCaptureBar.m_capdlg.m_pDst;
  CComPtr<IBaseFilter> pAudMux = m_wndCaptureBar.m_capdlg.m_pAudMux;
  CComPtr<IBaseFilter> pAudDst = m_wndCaptureBar.m_capdlg.m_pAudDst;

  bool fFileOutput = (pMux && pDst) || (pAudMux && pAudDst);
  bool fCapture = (fVCapture || fACapture);

  if(pAudCap)
  {
    AM_MEDIA_TYPE* pmt = &m_wndCaptureBar.m_capdlg.m_mta;
    int ms = (fACapture && fFileOutput && m_wndCaptureBar.m_capdlg.m_fAudOutput) ? AUDIOBUFFERLEN : 60;
    if(pMux != pAudMux && fACapture) SetLatency(pAudCap, -1);
    else if(pmt->pbFormat) SetLatency(pAudCap, ((WAVEFORMATEX*)pmt->pbFormat)->nAvgBytesPerSec * ms / 1000);
  }

  CComPtr<IPin> pVidCapPin, pVidPrevPin, pAudCapPin, pAudPrevPin;
  BuildToCapturePreviewPin(pVidCap, &pVidCapPin, &pVidPrevPin, pAudCap, &pAudCapPin, &pAudPrevPin);

  // if(pVidCap)
  {
    bool fVidPrev = pVidPrevPin && fVPreview;
    bool fVidCap = pVidCapPin && fVCapture && fFileOutput && m_wndCaptureBar.m_capdlg.m_fVidOutput;

    if(fVPreview == 2 && !fVidCap && pVidCapPin)
    {
      pVidPrevPin = pVidCapPin;
      pVidCapPin = NULL;
    }

    if(fVidPrev)
    {
      m_pCAP = NULL;
      m_pCAP2 = NULL;
      m_pCAPR = NULL;
      pGB->Render(pVidPrevPin);
      pGB->FindInterface(__uuidof(ISubPicAllocatorPresenter), (void**)&m_pCAP, FALSE);
      pGB->FindInterface(__uuidof(ISubPicAllocatorPresenterRender), (void**)&m_pCAPR, TRUE);
      pGB->FindInterface(__uuidof(ISubPicAllocatorPresenter2), (void**)&m_pCAP2, TRUE);
    }

    if(fVidCap)
    {
      IBaseFilter* pBF[3] = {pVidBuffer, pVidEnc, pMux};
      HRESULT hr = BuildCapture(pVidCapPin, pBF, MEDIATYPE_Video, &m_wndCaptureBar.m_capdlg.m_mtcv);
    }

    pAMDF = NULL;
    pCGB->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pVidCap, IID_IAMDroppedFrames, (void**)&pAMDF);
  }

  // if(pAudCap)
  {
    bool fAudPrev = pAudPrevPin && fAPreview;
    bool fAudCap = pAudCapPin && fACapture && fFileOutput && m_wndCaptureBar.m_capdlg.m_fAudOutput;

    if(fAPreview == 2 && !fAudCap && pAudCapPin)
    {
      pAudPrevPin = pAudCapPin;
      pAudCapPin = NULL;
    }

    if(fAudPrev)
    {
      pGB->Render(pAudPrevPin);
    }

    if(fAudCap)
    {
      IBaseFilter* pBF[3] = {pAudBuffer, pAudEnc, pAudMux ? pAudMux : pMux};
      HRESULT hr = BuildCapture(pAudCapPin, pBF, MEDIATYPE_Audio, &m_wndCaptureBar.m_capdlg.m_mtca);
    }
  }

  if((pVidCap || pAudCap) && fCapture && fFileOutput)
  {
    if(pMux != pDst)
    {
      hr = pGB->AddFilter(pDst, L"File Writer V/A");
      hr = pGB->ConnectFilter(GetFirstPin(pMux, PINDIR_OUTPUT), pDst);
    }

    if(CComQIPtr<IConfigAviMux> pCAM = pMux)
    {
      int nIn, nOut, nInC, nOutC;
      CountPins(pMux, nIn, nOut, nInC, nOutC);
      pCAM->SetMasterStream(nInC-1);
      // pCAM->SetMasterStream(-1);
      pCAM->SetOutputCompatibilityIndex(FALSE);
    }

    if(CComQIPtr<IConfigInterleaving> pCI = pMux)
    {
      // if(FAILED(pCI->put_Mode(INTERLEAVE_CAPTURE)))
      if(FAILED(pCI->put_Mode(INTERLEAVE_NONE_BUFFERED)))
        pCI->put_Mode(INTERLEAVE_NONE);

      REFERENCE_TIME rtInterleave = 10000i64*AUDIOBUFFERLEN, rtPreroll = 0;//10000i64*500
      pCI->put_Interleaving(&rtInterleave, &rtPreroll);
    }

    if(pMux != pAudMux && pAudMux != pAudDst)
    {
      hr = pGB->AddFilter(pAudDst, L"File Writer A");
      hr = pGB->ConnectFilter(GetFirstPin(pAudMux, PINDIR_OUTPUT), pAudDst);
    }
  }

  REFERENCE_TIME stop = MAX_TIME;
  hr = pCGB->ControlStream(&PIN_CATEGORY_CAPTURE, NULL, NULL, NULL, &stop, 0, 0); // stop in the infinite

  CleanGraph();

  OpenSetupVideo();
  OpenSetupAudio();

  RestoreMediaState;

  return(true);
}

OnFilePostOpenmedia() 中  SetTimer(TIMER_MOVIESHARE, 300000, NULL);  
  case TIMER_MOVIESHARE:
    {
      KillTimer(TIMER_MOVIESHARE);
      if (IsSomethingLoaded() && !m_fAudioOnly && !m_movieShared)  已經加載  不是音頻 文件不共享
      {
        m_movieShared = true;
        std::wstring uuid, moviehash;
        SPlayerGUID::GenerateGUID(uuid);
        UserShareController* usc = UserShareController::GetInstance();
        usc->CreateCommentPlane();
        moviehash = HashController::GetInstance()->GetSPHash(m_fnCurPlayingFile);
        usc->ShareMovie(uuid, moviehash, m_fnCurPlayingFile.GetString());
      }
    }
    break;

   進入CMainFrame::ZoomVideoWindow 分析
void CMainFrame::ZoomVideoWindow(double scale)
{
  if(m_iMediaLoadState != MLS_LOADED)    return;  沒有裝載 直接退出 ?

  AppSettings& s = AfxGetAppSettings();

  if(s.bUserAeroUI()){   用戶區域UI
    m_lTransparentToolbarStat = 0;
    m_wndFloatToolBar->ShowWindow(SW_HIDE);
  }

  BOOL bThisIsAutoZoom = false;  自動大小

  if(scale <= 0)
  {
    bThisIsAutoZoom = true;
    scale = 
      s.iZoomLevel == 0 ? 0.5 : 
      s.iZoomLevel == 1 ? 1.0 : 
      s.iZoomLevel == 2 ? 2.0 : 
      s.iZoomLevel == 3 ? GetZoomAutoFitScale() : 
      1.0;
    m_last_size_of_current_kind_of_video.cx = -1;
    m_last_size_of_current_kind_of_video.cy = -1;
  }

  if(m_fFullScreen)  如果全屏 則全屏
  {
    OnViewFullscreen();
  }

  MINMAXINFO mmi;
  OnGetMinMaxInfo(&mmi);

  CRect r;
  int w = 0, h = 0;

  if(!m_fAudioOnly)  不是音頻
  {
    CSize arxy = m_original_size_of_current_video = GetVideoSize();
    long lWidth = int(arxy.cx * scale + 0.5);
    long lHeight = int(arxy.cy * scale + 0.5);
    CString string_remember_windows_size_for_this_video_size_parm;
    string_remember_windows_size_for_this_video_size_parm.Format(L"ORGSIZE%dx%d", m_original_size_of_current_video.cx, m_original_size_of_current_video.cy );
    AppSettings& s = AfxGetAppSettings();
    long lPerfWidth = m_last_size_of_current_kind_of_video.cx = AfxGetMyApp()->GetProfileInt(ResStr(IDS_R_SETTINGS)+L"REMENBERWNDSIZE", string_remember_windows_size_for_this_video_size_parm+L"W", -1);
    long lPerfHeight = m_last_size_of_current_kind_of_video.cy = AfxGetMyApp()->GetProfileInt(ResStr(IDS_R_SETTINGS)+L"REMENBERWNDSIZE", string_remember_windows_size_for_this_video_size_parm+L"H", -1);

    DWORD style = GetStyle();

    CRect r3 ,r4;
    GetWindowRect(r3);
    m_wndView.GetWindowRect(r4);
    //GetClientRect(&r1);
    //m_wndView.GetClientRect(&r2);
    int wDelta = 0;
    int hDelta = 0;

    wDelta = r3.Width() - r4.Width();
    hDelta = r3.Height() - r4.Height();

    if(style&WS_CAPTION)
    {
      //h += GetSystemMetrics(SM_CYCAPTION);
      //w += 2; h += 2; // for the 1 pixel wide sunken frame
      //w += 2; h += 3; // for the inner black border
    }

    GetWindowRect(r);

    if (lHeight + hDelta < 280 || lWidth + wDelta < 480) {
      int w1 = 480 - wDelta;
      int h1 = 280 - hDelta;
      SVP_ASSERT(w1 > 0);
      SVP_ASSERT(h1 > 0);

      // Re-evaluate current 'w' and 'h' to keep aspect ratio
      int h2 = arxy.cy * w1 / arxy.cx, w2 = arxy.cx * h1 / arxy.cy;
      // Choose by the fitting rectangle.
      if (h2 + hDelta >= 280) {
        w = 480;
        h = h2 + hDelta;
      } else {
        w = w2 + wDelta;
        h = 280;
      }


    } else {
      h = lHeight + hDelta;
      w = lWidth + wDelta;
    }


    if(bThisIsAutoZoom && 0){
      double mratio = (double)lHeight/lWidth;
      //SVP_LogMsg5(L"%d %d %f %d %d %f",h , w, w * mratio + (h - lHeight), lHeight, lWidth, mratio);
      h = max(h , w * mratio + (h - lHeight));
    }


    if(bThisIsAutoZoom && lPerfWidth > 240 && lPerfHeight > 120)
    {
      //Only do this if its auto zoom
      w = lPerfWidth;
      h = lPerfHeight;
    }
  }
  else
  {
    GetWindowRect(r);


    //w = r.Width(); //;mmi.ptMinTrackSize.x;
    //h = r.Height();//;mmi.ptMinTrackSize.y;
    w = 320;
    if(s.bUserAeroUI()){
      w /= 0.9;
    }
    h = 110;


    AppSettings& s = AfxGetAppSettings();
    long lPerfWidth = m_last_size_of_current_kind_of_video.cx = AfxGetMyApp()->GetProfileInt(ResStr(IDS_R_SETTINGS)+L"REMENBERWNDSIZE", L"ORGSIZE0x0W", -1);
    long lPerfHeight = m_last_size_of_current_kind_of_video.cy = AfxGetMyApp()->GetProfileInt(ResStr(IDS_R_SETTINGS)+L"REMENBERWNDSIZE", L"ORGSIZE0x0H", -1);


    w = max(w, lPerfWidth);
    h = max(h, lPerfHeight);


  }


  // center window
  //if(!s.fRememberWindowPos)
  {
    CPoint cp = r.CenterPoint();
    r.left = cp.x - w/2;
    r.top = cp.y - h/2;
  }


  r.right = r.left + w;
  r.bottom = r.top + h;


  MONITORINFO mi;
  mi.cbSize = sizeof(MONITORINFO);


  獲得窗體信息
  GetMonitorInfo(MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST), &mi);
  if(r.right > mi.rcWork.right) r.OffsetRect(mi.rcWork.right-r.right, 0);
  if(r.left < mi.rcWork.left) r.OffsetRect(mi.rcWork.left-r.left, 0);
  if(r.bottom > mi.rcWork.bottom) r.OffsetRect(0, mi.rcWork.bottom-r.bottom);
  if(r.top < mi.rcWork.top) r.OffsetRect(0, mi.rcWork.top-r.top);
  CRect rcWork(mi.rcWork);
  if(r.Width() >  rcWork.Width() ){
    r.left = rcWork.left;
    r.right = rcWork.right;
  }
  if(r.Height() >  rcWork.Height() ){
    r.top = rcWork.top;
    r.bottom = rcWork.bottom;
  }
  if(m_fFullScreen || !s.HasFixedWindowSize())
  {
    MoveWindow(r);
  }


  //AfxMessageBox(_T("1"));
  //Sleep(200);
  // ShowWindow(SW_SHOWNORMAL);


  MoveVideoWindow();
}




   進入CMainFrame::ToggleFullscreen 分析
void CMainFrame::ToggleFullscreen(bool fToNearest, bool fSwitchScreenResWhenHasTo)
{
  CRect r;
  m_lastTimeToggleFullsreen = AfxGetMyApp()->GetPerfCounter();
  // const CWnd* pWndInsertAfter;
  DWORD dwRemove = 0, dwAdd = 0;
  DWORD dwRemoveEx = 0, dwAddEx = 0;
  HMENU hMenu;


  if(!m_fFullScreen)  
  {  不是全屏


    if(m_wndPlaylistBar.IsVisible()){
      m_fPlaylistBeforeToggleFullScreen = true;
      ShowControlBar(&m_wndPlaylistBar, FALSE, TRUE);
    }


    GetWindowRect(&m_lastWindowRect);


    dispmode& dm = AfxGetAppSettings().dmFullscreenRes;
    m_dmBeforeFullscreen.fValid = false;
    if(dm.fValid && fSwitchScreenResWhenHasTo)
    {
      GetCurDispMode(m_dmBeforeFullscreen);
      SetDispMode(dm);
    }


    MONITORINFO mi;
    mi.cbSize = sizeof(MONITORINFO);
    GetMonitorInfo(MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST), &mi);


    dwRemove = WS_CAPTION|WS_THICKFRAME;
    if(fToNearest) r = mi.rcMonitor;
    else GetDesktopWindow()->GetWindowRect(&r);
    hMenu = NULL;




  }
  else
  {
    全屏
    if( AfxGetAppSettings().htpcmode){
      return;
    }
    if( m_fPlaylistBeforeToggleFullScreen )  
      ShowControlBar(&m_wndPlaylistBar, TRUE, TRUE);


    if(m_dmBeforeFullscreen.fValid)
      SetDispMode(m_dmBeforeFullscreen);


    dwAdd = (AfxGetAppSettings().fHideCaptionMenu ? 0 : WS_CAPTION) | WS_THICKFRAME;
    r = m_lastWindowRect;
    hMenu = NULL;//AfxGetAppSettings().fHideCaptionMenu ? NULL : m_hMenuDefault;
  }






  //bool fAudioOnly = m_fAudioOnly;
  //m_fAudioOnly = true;


  m_fFullScreen = !m_fFullScreen;


  SetAlwaysOnTop(AfxGetAppSettings().iOnTop);


  修改屬性  進行全屏
  ModifyStyle(dwRemove, dwAdd, SWP_NOZORDER);
  ModifyStyleEx(dwRemoveEx, dwAddEx, SWP_NOZORDER);
  ::SetMenu(m_hWnd, hMenu);
  SetWindowPos(NULL, r.left, r.top, r.Width(), r.Height(), SWP_NOZORDER|SWP_NOSENDCHANGING /*SWP_FRAMECHANGED*/);
  RedrawNonClientArea();


  KillTimer(TIMER_FULLSCREENCONTROLBARHIDER);
  KillTimer(TIMER_FULLSCREENMOUSEHIDER);
  if(m_fFullScreen)
  {
    // SVP_LogMsg5(L"Fullscreen");
    m_fHideCursor = true;
    SetTimer(TIMER_FULLSCREENMOUSEHIDER, 800, NULL);
    ShowControls(CS_NONE, false);
  }
  else
  {
    m_lastMouseMove.x = m_lastMouseMove.y = -1;
    m_fHideCursor = false;
    ShowControls(AfxGetAppSettings().nCS);
  }


  m_wndView.SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER);
  //m_fAudioOnly = fAudioOnly;


  rePosOSD();


  MoveVideoWindow();


  // insert after the HWND_TOP if the player is not full screen
  if (m_fFullScreen)  全屏的話  就置頂
    ::SetWindowPos(m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOREDRAW|SWP_NOSIZE|SWP_NOMOVE);
  else
    ::SetWindowPos(m_hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOREDRAW|SWP_NOSIZE|SWP_NOMOVE);
}




TIMER_STREAMPOSPOLLER:  定時事件
  case TIMER_STREAMPOSPOLLER:
    if (m_iMediaLoadState == MLS_LOADED)
      _HandleTimer_StreamPosPoller();
    break;


void CMainFrame::_HandleTimer_StreamPosPoller()   處理位置信息  比如播放進度
{
  REFERENCE_TIME rtNow = 0, rtDur = 0;


  if (m_iPlaybackMode == PM_FILE)
  {
    pMS->GetCurrentPosition(&rtNow);
    pMS->GetDuration(&rtDur);


    if (m_rtDurationOverride >= 0)
      rtDur = m_rtDurationOverride;


    m_wndSeekBar.Enable(rtDur > 0);
    m_wndSeekBar.SetRange(0, rtDur);
    m_wndSeekBar.SetPos(rtNow);
  }
  else if (m_iPlaybackMode == PM_CAPTURE)
  {
    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;


    m_wndSeekBar.Enable(false);
    m_wndSeekBar.SetRange(0, rtDur);
    m_wndSeekBar.SetPos(rtNow);
    /*
    if (m_fCapturing)
    {
    if (rtNow > 10000i64*1000*60*60*3)
    m_wndCaptureBar.m_capdlg.OnRecord();
    }
    */
  }


  if (m_pCAP && (m_iPlaybackMode != PM_FILE || m_bMustUseExternalTimer))
  {
    g_bExternalSubtitleTime = true;
    AfxGetAppSettings().bExternalSubtitleTime = true;
    if (pDVDI)
    {
      DVD_PLAYBACK_LOCATION2 Location;
      if (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;


        LONGLONG rtTimeCode = HMSF2RT(Location.TimeCode, fps);
        m_pCAP->SetTime(rtTimeCode);
      }
      else
        m_pCAP->SetTime(/*rtNow*/m_wndSeekBar.GetPos());
    }
    else
      m_pCAP->SetTime(/*rtNow*/m_wndSeekBar.GetPos());
  }
  else
  {
    g_bExternalSubtitleTime = false;
    AfxGetAppSettings().bExternalSubtitleTime = false;
  }
}




TIMER_STREAMPOSPOLLER2: 定時設置
  case TIMER_STREAMPOSPOLLER2:
    if (m_iMediaLoadState == MLS_LOADED)   設置位置  和  時間
    {
      __int64 start, stop, pos;
      m_wndSeekBar.GetRange(start, stop);
      pos = m_wndSeekBar.GetPosReal();


      if (ABControlOn && m_bRefTime > m_aRefTime && GetMediaState() == State_Running)
      {
        if (pos > m_bRefTime)
          //goto aRefTimer
          SeekTo(m_aRefTime, 0);
      }
      GUID tf;
      pMS->GetTimeFormat(&tf);


      if (m_iPlaybackMode == PM_CAPTURE && !m_fCapturing)
      {
        CString str = _T("Live");
        long lChannel = 0, lVivSub = 0, lAudSub = 0;
        if (pAMTuner 
          && m_wndCaptureBar.m_capdlg.IsTunerActive()
          && SUCCEEDED(pAMTuner->get_Channel(&lChannel, &lVivSub, &lAudSub)))
        {
          CString ch;
          ch.Format(_T(" (ch%d)"), lChannel);
          str += ch;
        }
        //m_wndStatusBar.SetStatusTimer(str);
        m_wndToolBar.SetStatusTimer(str);
      }
      else
      {
        double pRate;
        if (E_NOTIMPL == pMS->GetRate(&pRate))
          pRate = 0;
        m_wndToolBar.SetStatusTimer(pos, stop, 0, &tf, pRate);
        //m_wndToolBar.SetStatusTimer(str);
      }


      if (m_pCAPR && GetMediaState() == State_Paused)
        m_pCAPR->Paint(true);
    }
    break;








  case TIMER_STATS:   定時 事件
    _HandleTimer_Stats();   處理事件狀態
    break;
void CMainFrame::_HandleTimer_Stats()
{
  m_l_been_playing_sec++;
  if (AfxGetMyApp()->IsWin7() && pTBL)
  {
    try
    {
      BOOL bHasValue = 0;
      BOOL bSetPaused = 0;
      if (IsSomethingLoaded())   確認已經加載
      {
        //TBPF_PAUSED
        //TBPF_NORMAL
        //TBPF_INDETERMINATE
        switch (GetMediaState())
        {
        case State_Paused:
          //not using TBPF_PAUSED since it hide progress
          pTBL->SetProgressState(m_hWnd,TBPF_NORMAL);
          bSetPaused = true;
          bHasValue = true;
          break;
        case State_Running:
          pTBL->SetProgressState(m_hWnd,TBPF_NORMAL);
          bHasValue = true;
          break;
        case State_Stopped:
          pTBL->SetProgressState(m_hWnd,TBPF_NOPROGRESS);
          break;
        }
      }
      else
        pTBL->SetProgressState(m_hWnd,TBPF_NOPROGRESS);


      if (bHasValue)
      {
        __int64 iSeekStart, iSeekStop;
        m_wndSeekBar.GetRange(iSeekStart, iSeekStop);
        __int64 iSeekPos = m_wndSeekBar.GetPosReal();
        pTBL->SetProgressValue(m_hWnd, iSeekPos ,(iSeekStop - iSeekStart));
      }
      if (bSetPaused)
        pTBL->SetProgressState(m_hWnd,TBPF_PAUSED);
    }
    catch (...)
    {
      pTBL->Release();
      pTBL = NULL;
    }
  }


  if (IsSomethingLoaded() && m_fAudioOnly && (!m_Lyric.m_has_lyric || m_wndView.m_strAudioInfo.IsEmpty()))
  {    已經加載  是音頻  沒有歌詞
    m_wndView.m_AudioInfoCounter++;
    BOOL bHaveInfo = false;


    for (int i = 0; i < 4; i++)
    {
      if ((m_wndView.m_AudioInfoCounter%4) == 0 || m_wndView.m_strAudioInfo.IsEmpty())
      {
        CString szInfo , szMusicTitle, szMusicAuthor;
        szMusicTitle = GetClipInfo(IDS_INFOBAR_TITLE).c_str();
        szMusicAuthor = GetClipInfo(IDS_INFOBAR_AUTHOR).c_str();


        switch (m_wndView.m_AudioInfoCounter/4 %4)
        {
        case 0:
          szInfo = szMusicTitle;
          break;
        case 1:
          szInfo = szMusicAuthor;
          break;
        case 2:
          szInfo = GetClipInfo(IDS_INFOBAR_DESCRIPTION).c_str();
          break;
        case 3:
          szInfo = GetClipInfo(IDS_INFOBAR_COPYRIGHT).c_str();
          break;
        }
        if (szInfo.IsEmpty())
        {
          m_wndView.m_AudioInfoCounter+=4;
          continue;
        }
        else
        {
          m_wndView.m_strAudioInfo = szInfo;
          CString szTitleItShouleBe = szMusicTitle + _T(" - ") + szMusicAuthor;
          if (szTitleItShouleBe != m_szTitle)
          {
            m_szTitle = szTitleItShouleBe;
            RedrawNonClientArea();
          }
          m_wndView.Invalidate();
          bHaveInfo = true;
        }
      }
      break;
    }
  }


  if (m_iPlaybackMode == PM_FILE)
  {
    REFERENCE_TIME rtNow = 0, rtDur = 0;
    pMS->GetCurrentPosition(&rtNow);
    pMS->GetDuration(&rtDur);




    if (m_fAudioOnly && m_Lyric.m_has_lyric)// && m_wndLycShowBox
    {
      int iLastingTime;
      CString szLyricLine = m_Lyric.GetCurrentLyricLineByTime(rtNow, &iLastingTime);
      if (!szLyricLine.IsEmpty())
      {
        wchar_t music_note[] = {0x266A, 0x0020, 0};
        szLyricLine.Insert(0, music_note);
        if (m_wndView.m_strAudioInfo != szLyricLine)
        {
          m_wndView.m_strAudioInfo = szLyricLine;
          if (iLastingTime > 0)
            m_wndView.SetLyricLasting(iLastingTime);
          else
            m_wndView.SetLyricLasting(15);
          m_wndView.Invalidate();
        }
      }
    }


    UINT iTotalLenSec = (UINT)( (INT64) rtDur / 20000000 );
    //如果視頻長度大於1分钟, 而且是文件模式,而且正在播放中
    if (!m_fAudioOnly && iTotalLenSec >  180 && m_iPlaybackMode == PM_FILE && GetMediaState() == State_Running)
    {
      time_t time_now = time(NULL);
      UINT totalplayedtime =  time_now - m_tPlayStartTime;
      //SVP_LogMsg5(L"time_now > ( m_tLastLogTick  %f %f" , (double)time_now , (double)m_tLastLogTick);
      if (time_now > ( m_tLastLogTick + 180 ))
      { //如果和上次檢查已經超n秒
        CString fnVideoFile , fnSubtitleFile; 
        int subDelayMS = 0;
        fnVideoFile = m_fnCurPlayingFile;
        fnSubtitleFile = getCurPlayingSubfile(&subDelayMS);


        if (!fnSubtitleFile.IsEmpty())
        { //如果有字幕
          CString szLog;
          //szLog.Format(_T(" %s ( with sub %s delay %d ) %d sec of %d sec ( 1/2 length video = %d ) ") , fnVideoFile, fnSubtitleFile,subDelayMS, totalplayedtime , iTotalLenSec, (UINT)(iTotalLenSec/2)  );
          //SVP_LogMsg(szLog);
          //if time > 50%
          if (totalplayedtime > (UINT)(iTotalLenSec/2))
          {
            if (!m_haveSubVoted && m_pCAP && rtNow > (rtDur - 3000000000i64))
            {
              //如果還沒提示過vote
              //如果時間接近最末5分钟
              AppSettings& s = AfxGetAppSettings();
              if (s.bIsChineseUIUser())
              {
                //如果是中文
                if ( m_wndPlaylistBar.GetCount() <= 1)
                {
                  //如果是1CD
                  int nSubPics;
                  REFERENCE_TIME rtSubNow,  rtSubStart, rtSubStop;
                  m_pCAP->GetSubStats(nSubPics,rtSubNow,  rtSubStart, rtSubStop);
                  if (rtSubNow > rtSubStop)
                  {
                    //如果沒有更多字幕
                    SVP_LogMsg5(L"Sub Voted Event");
                    //vote之
                    m_haveSubVoted = true;
                  }
                }
              }
            }
            //是否已經上傳過呢
            if (m_fnsAlreadyUploadedSubfile.Find(fnVideoFile+fnSubtitleFile) < 0)
            {
              //upload subtitle
              Logging(L"Uploading sub %s of %s width delay %d ms since user played %d sec of %d sec ( more than 1/2 length video ) " , fnSubtitleFile, fnVideoFile ,subDelayMS, totalplayedtime , iTotalLenSec  );
              SVP_UploadSubFileByVideoAndSubFilePath(fnVideoFile , fnSubtitleFile, subDelayMS);
              m_fnsAlreadyUploadedSubfile.Append( fnVideoFile+fnSubtitleFile+_T(";") );
            }
            int subDelayMS2 = 0;
            CString fnSubtitleFile2 = getCurPlayingSubfile(&subDelayMS2);
            if (!fnSubtitleFile2.IsEmpty())
            {
              if (m_fnsAlreadyUploadedSubfile.Find( fnVideoFile+fnSubtitleFile2 ) < 0)
              {
                //upload subtitle
                Logging((L"Uploading sub2 %s of %s width delay %d ms since user played %d sec of %d sec ( more than 1/2 length video ) ") , fnSubtitleFile2, fnVideoFile ,subDelayMS2, totalplayedtime , iTotalLenSec);
                SVP_UploadSubFileByVideoAndSubFilePath(fnVideoFile , fnSubtitleFile2, subDelayMS2);
                m_fnsAlreadyUploadedSubfile.Append( fnVideoFile+fnSubtitleFile2+_T(";") );
              }
            }
          }
        }
        m_tLastLogTick = time_now;
        //SVP_LogMsg5(L"m_tLastLogTick = time_now;   %f %f" , (double)time_now , (double)m_tLastLogTick);
      }
    }
  }


  CString msg;
  if (m_fBuffering)
  {
    BeginEnumFilters(pGB, pEF, pBF)
    {
      if (CComQIPtr<IAMNetworkStatus, &IID_IAMNetworkStatus> pAMNS = pBF)
      {
        long BufferingProgress = 0;
        if (SUCCEEDED(pAMNS->get_BufferingProgress(&BufferingProgress)) && BufferingProgress > 0 && BufferingProgress < 99)
        {
          msg.Format(ResStr(IDS_CONTROLS_BUFFERING), BufferingProgress);
          SendStatusMessage(msg,1000);
          SVP_LogMsg5(msg);
        }
        break;
      }
    }
    EndEnumFilters
  }
  else if (pAMOP)
  {
    __int64 t = 0, c = 0;
    if(SUCCEEDED(pAMOP->QueryProgress(&t, &c)) && t > 0 && c < t)
    {
      msg.Format(ResStr(IDS_CONTROLS_BUFFERING), c*100/t);
      SendStatusMessage(msg,1000);
      SVP_LogMsg5(msg);
    }
  }
  m_wndToolBar.m_buffering  = msg;


  if (m_iPlaybackMode == PM_FILE)
    SetupChapters();
  


  if (GetMediaState() == State_Running)
  {
    if (m_fAudioOnly)
    {
      UINT fSaverActive = 0;
      if (SystemParametersInfo(SPI_GETPOWEROFFACTIVE, 0, (PVOID)&fSaverActive, 0))
      {
        SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0, 0, SPIF_SENDWININICHANGE); // this might not be needed at all...
        SystemParametersInfo(SPI_SETPOWEROFFACTIVE, fSaverActive, 0, SPIF_SENDWININICHANGE);
      }
      SetThreadExecutionState(ES_SYSTEM_REQUIRED); 
    }
    else
    {
      UINT fSaverActive = 0;
      if (SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, (PVOID)&fSaverActive, 0))
      {
        SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0, 0, SPIF_SENDWININICHANGE); // this might not be needed at all...
        SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, fSaverActive, 0, SPIF_SENDWININICHANGE);
      }
      fSaverActive = 0;
      if (SystemParametersInfo(SPI_GETPOWEROFFACTIVE, 0, (PVOID)&fSaverActive, 0))
      {
        SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 0, 0, SPIF_SENDWININICHANGE); // this might not be needed at all...
        SystemParametersInfo(SPI_SETPOWEROFFACTIVE, fSaverActive, 0, SPIF_SENDWININICHANGE);
      }
      SetThreadExecutionState(ES_DISPLAY_REQUIRED|ES_SYSTEM_REQUIRED); //this is the right way, only this work under vista . no ES_CONTINUOUS  so it can goes to sleep when not playing
    }




    if (GetExStyle() & WS_EX_LAYERED)
    {
      BYTE dAlpha = 0 ;
      DWORD dwFlag = 0;
      if (AfxGetMyApp()->m_pGetLayeredWindowAttributes)
      {
        AfxGetMyApp()->m_pGetLayeredWindowAttributes(m_hWnd, NULL, &dAlpha , &dwFlag);
        if (dAlpha == 255)
          ModifyStyleEx(WS_EX_LAYERED , 0);
      }
    }
  }

namespace DSObjects  操作DirectShow類庫


空間中有
操作DirectShow  D3D7
CDX7AllocatorPresenter繼承ISubPicAllocatorPresenterImpl


操作DirectShow  D3D9
CDX9AllocatorPresenter繼承ISubPicAllocatorPresenterImpl


CDXRAllocatorPresenter繼承ISubPicAllocatorPresenterImpl


VMR7分配器
CVMR7AllocatorPresenter繼承CDX7AllocatorPresenter,繼承IVMRSurfaceAllocator,繼承IVMRImagePresenter,繼承IVMRWindowlessControl


VMR9分配器
CVMR9AllocatorPresenter繼承CDX9AllocatorPresenter,繼承IVMRSurfaceAllocator9,繼承IVMRImagePresenter9, 繼承IVMRWindowlessControl9


CGenlock   管理視頻和顯示的同步。


VR分配器
CmadVRAllocatorPresenter繼承ISubPicAllocatorPresenterImpl


QT7分配器
CQT7AllocatorPresenter繼承CDX7AllocatorPresenter,繼承IQTVideoSurface


QT9分配器
CQT9AllocatorPresenter繼承CDX9AllocatorPresenter,繼承IQTVideoSurface


Quicktime格式繪圖
CQuicktimeGraph繼承CBaseGraph,繼承IVideoFrameStep


Quicktime載體
CQuicktimeWindow繼承CPlayerWindow


Real格式繪圖
CRealMediaGraph繼承CBaseGraph


Real格式播放
CRealMediaPlayer繼承CUnknown,繼承IRMAErrorSink,繼承IRMAClientAdviseSink,繼承IRMAAuthenticationManager,繼承IRMASiteSupplier,繼承IRMAPassiveSiteWatcher,繼承IRMAAudioHook


Real格式播放載體
CRealMediaPlayerWindowed繼承CRealMediaPlayer


Real格式播放載體
CRealMediaPlayerWindowless繼承CRealMediaPlayer


Real格式視頻面
CRealMediaVideoSurface繼承CUnknown,繼承IRMAVideoSurface


Real格式載體站點
CRealMediaWindowlessSite繼承CUnknown,繼承IRMASite,繼承IRMASite2,繼承IRMASiteWindowless,繼承IRMAVideoSurface


RM7分配器
CRM7AllocatorPresenter繼承CDX7AllocatorPresenter,繼承IRMAVideoSurface


RM9分配器
CRM9AllocatorPresenter繼承CDX9AllocatorPresenter,繼承IRMAVideoSurface


Flash繪圖
CShockwaveGraph繼承CBaseGraph






主程序有的類庫:


鏈接廣告的類  實現了動畫效果
AdController繼承ThreadHelperImpl<AdController>


ButtonManage  按鈕管理類 


CAboutDlg繼承CDialog  關於對話框


自定義了一個映射模版類
template <class T = CString, class S = CString>
class CAtlStringMap : public CAtlMap<S, T, CStringElementTraits<S> > {};


一個音頻格式集合類
class CAudFormatArray : public CFormatArray<AUDIO_STREAM_CONFIG_CAPS>


一個作者登陆框
class CAuthDlg : public CDialog


視頻繪畫類
class CBaseGraph繼承CUnknown, 繼承IGraphBuilder2, public IMediaControl, 繼承IMediaEventEx, 繼承IMediaSeeking, 繼承IVideoWindow, 繼承IBasicVideo, 繼承IBasicAudio, 繼承IAMOpenProgress, public IGraphEngine




class CBtnAlign  按鈕位置區域對齊類


自繪了Edit類 闕套了Button
class CBtnEditCtrl繼承CWindowImpl<CBtnEditCtrl, CEdit>


CChildView繼承CWnd  窗體類 實現了視頻載體


COM組件屬性頁站點
CComPropertyPageSite繼承CUnknown,繼承IPropertyPageSite


CConvertChapDlg繼承CResizableDialog 轉換章節對話框


CConvertDlg繼承CResizableDialog  轉換對話框


CConvertPropsDlg繼承CResizableDialog 轉換屬性頁對話框


CConvertResDlg繼承CResizableDialog 轉換資源對話框


分行過滤器
CDeinterlacerFilter繼承CTransformFilter


CDisplaySettingDetector 顯示設置探測器


CDlgChkUpdater繼承CDialog  確認更新對話框


CDropTarget  移動文件類


EVR分配器
CEVRAllocatorPresenter繼承CDX9AllocatorPresenter,繼承IMFGetService,繼承IMFTopologyServiceLookupClient,繼承IMFVideoDeviceID,繼承IMFVideoPresenter,繼承IDirect3DDeviceManager9,繼承IMFAsyncCallback,繼承IQualProp,繼承IMFRateSupport,繼承IMFVideoDisplayControl,繼承IEVRTrustedVideoPlugin


漸變視頻解碼
CFakeDirectXVideoDecoder繼承CUnknown,繼承IDirectXVideoDecoder


CFavoriteAddDlg繼承CCmdUIDialog  收藏對話框


CFavoriteOrganizeDlg繼承CResizableDialog 收藏組織對話框


CFFindByDir  目錄查找類


CFFindMoreFiles 查找更多文件類


CFGAggregator繼承CUnknown  FG集合類


CFGFilter   FG過滤類


CFGFilterFile繼承CFGFilter FG過滤文件


CFGFilterInternal繼承CFGFilter  FG內部過滤


CFGFilterList  FG過滤列表


CFGFilterRegistry繼承CFGFilter FG過滤繼承類


CFGFilterVideoRenderer繼承CFGFilter  FG過滤視頻渲染類


FG管理類
CFGManager繼承CUnknown, 繼承IGraphBuilder2, 繼承IGraphBuilderDeadEnd, 繼承CCritSec


CFGManagerCapture繼承CFGManagerPlayer FG捕捉管理類


CFGManagerCustom繼承CFGManager   FG自定義管理類


CFGManagerDVD繼承CFGManagerPlayer  FG_DVD管理類


CFGManagerMuxer繼承CFGManagerCustom  FG 混合器管理類


CFGManagerPlayer繼承CFGManagerCustom  FG視頻管理


CFileDropTarget繼承COleDropTarget   文件移動類


CFilterMapper2繼承CUnknown繼承IFilterMapper2   過滤映射類


CFilterTreeCtrl繼承CTreeCtrl   過滤器樹列表


CFloatEdit繼承CEdit  浮動的編輯框


格式模塊
template<class T>
class CFormat : public CAutoPtrArray<CFormatElem<T> >


格式數組模塊
template<class T>
class CFormatArray : public CAutoPtrArray<CFormat<T> >


格式節點
template<class T>
class CFormatElem


CGoToDlg繼承CDialog  轉到對話框


CGraphCore / /我們需要打開/播放暫停/尋求/關閉/ VOL控制/子控制/音頻和視頻切換的類


CGraphThread繼承CWinThread  播放線程類


CHexEdit繼承CEdit  16進制編輯框


ChkDefPlayerControlBar繼承 CSVPDialog  默認播放控制航


CIfo  文件信息類


CInfoReport繼承CDialog  信息報告對話框


CInPlaceComboBox繼承CComboBox  闕套下拉框


CInPlaceEdit繼承CEdit  闕套編輯框


CInPlaceListBox繼承CListBox  闕套列表框


CIntEdit繼承CEdit   整形編輯框


CKeyProvider繼承CUnknown,繼承IServiceProvider  鍵盤提供者


CLineNumberEdit繼承CEdit   線數編輯框


CLineNumberStatic繼承CStatic  線數靜態框




CMacrovisionKicker繼承CUnknown,繼承IKsPropertySet


主程序
CMainFrame繼承CFrameWnd,繼承CDropTarget,繼承CGraphCore




CMediaFormatCategory   媒體格式分類


CMediaTypesDlg繼承CResizableDialog  媒體類型對話框


CMemoryDC繼承CDC   內存離屏DC


CMPlayerCApp繼承CWinApp 启動類


CNEWOSDWnd繼承CWnd  Osd窗體


ContentTypeTemp  內容類型


COpenCapDeviceDlg繼承CResizableDialog  打開捕捉設備對話框


COpenDlg繼承CResizableDialog  打開對話框


COpenFileDlg繼承CFileDialog  打開文件對話框


COpenURLDlg繼承CResizableDialog  打開URL 對話框


輸出EVR
COuterEVR繼承CUnknown,繼承IVMRffdshow9,繼承IVMRMixerBitmap9,繼承IBaseFilter


輸出VMR9
COuterVMR9繼承CUnknown,繼承IVideoWindow,
繼承IBasicVideo2,繼承IVMRWindowlessControl,繼承IVMRffdshow9,繼承IVMRMixerBitmap9


CPixelShaderCompiler  像素着色器編譯器


CPlayerCaptureBar繼承baseCPlayerCaptureBar  視頻捕捉條


CPlayerCaptureDialog繼承CResizableDialog //CDialog 視頻捕捉對話框


CPlayerChannelNormalizer繼承CSVPDialog  視頻通道正規化對話框


CPlayerColorControlBar繼承CSVPDialog  視頻顏色控制條


CPlayerEQControlBar繼承CSVPDialog   視頻EQ控制條


CPlayerFloatToolBar繼承CFrameWnd  視頻浮動控制條


CPlayerListCtrl繼承CListCtrl  播放器列表


CPlayerPlaylistBar繼承CSizingControlBarG  播放器列表條


CPlayerSeekBar繼承CDialogBar   視頻跳轉條


CPlayerShaderEditorBar繼承baseCPlayerShaderEditorBar 視頻着色編輯條


CPlayerToolBar繼承CToolBar  視頻工具條


CPlayerToolTopBar繼承CWnd  視頻上面的工具條


CPlayerWindow繼承 CWnd  視頻窗體


CPlaylist繼承CList<CPlaylistItem>  播放列表


CPlaylistItem  播放列表節點


CPngImage繼承CImage  處理png圖片


CPnSPresetsDlg繼承CCmdUIDialog  預設對話框


CPPageBase繼承CCmdUIPropertyPage   基本屬性頁


CPPageFileInfoClip繼承CPropertyPage 文件信息屬性頁


CPPageFileInfoDetails繼承CPropertyPage  詳細文件信息頁


CPPageFileInfoRes繼承CPPageBase  文件信息資源屬性頁


CPPageFileInfoSheet繼承CPropertySheet  文件信息頁


CPPageFileMediaInfo繼承CPropertyPage  媒體文件信息


CPPageFormats  頁格式


CResetDVD繼承CDVDSession  重設DVD


CSaveDlg繼承CCmdUIDialog  保存對話框


CSaveTextFileDialog繼承CFileDialog  保存文本文件對話框


CSaveThumbnailsDialog繼承CFileDialog  保存縮略圖對話框


CSeekBarTip繼承CWnd   賺到提示信息


CShaderAutoCompleteDlg繼承CResizableDialog  着色自動完成對話框


CShaderCombineDlg繼承CResizableDialog 着色連接對話框


CShaderEdit繼承CLineNumberEdit  着色編輯框


CShaderEditorDlg繼承CResizableDialog  着色編輯對話框


CShaderLabelComboBox繼承CComboBox 着色下拉框


CShockwaveFlash繼承CWnd   flash控件


CSUIBtnList繼承CList<CSUIButton*>  按鈕列表


CSUIButton  自繪按鈕類


CSVPButton繼承CButton   自繪按鈕


CSVPDialog繼承CWnd    自繪對話框


CSVPSliderCtrl繼承CSliderCtrl  自繪Slider


CSVPStatic繼承CStatic   自繪Static


CSVPSubUploadDl繼承CResizableDialog  字幕上傳對話框


CSVPSubVoteControlBar繼承CSVPDialog 


CTextFile繼承CStdioFile  操作文件類


CTextPassThruFilter繼承CBaseFilter,繼承CCritSec  文本直通過滤器


CTextPassThruInputPin繼承CSubtitleInputPin  文本直通輸入流


CTextPassThruOutputPin繼承CBaseOutputPin  文本直通輸出流


CTransparentControlBar繼承CSVPDialog   透明控制條


CustomDrawBtn繼承CButton  自定義按鈕


CVidFormatArray繼承CFormatArray<VIDEO_STREAM_CONFIG_CAPS>  視頻格式集合


CVolumeCtrl繼承CSliderCtrl  聲音控件


CWebTextFile繼承CTextFile  網站文本


FileAssoc   文件關聯


FilterOverride  過滤器重叠


FrameCfgFileManage  窗體設置文件管理


GUIConfigManage GUI配置管理


哈希值管理
HashController繼承LazyInstanceImpl<HashController>


HotkeyCmd繼承ACCEL   快捷鍵


HotkeyController繼承LazyInstanceImpl<HotkeyController>  快捷鍵控制


HotkeySchemeParser  快捷鍵計劃分析器


LayeredWindowUtils  窗體單元


實例模版
template<class T>
class LazyInstanceImpl


template<class T>
class MainFrameSPlayerCmd


MakeMultiplyBmp


媒體中心管理
MediaCenterController繼承LazyInstanceImpl<MediaCenterController>


媒體中心視圖
MediaCenterView繼承ATL::CWindowImpl<MediaCenterView>,繼承MediaScrollbar,繼承MediaListView


MediaCheckDB繼承ThreadHelperImpl<MediaCheckDB>  媒體確認數據庫


MediaDB  媒體數據庫


MediaListView 媒體列表視圖


MediaModel繼承SourceModel<MediaData, MediaFindCondition>  媒體模式


MediaScrollbar  媒體滾動條


MediaSpiderAbstract繼承ThreadHelperImpl<T> 媒體摘要


MediaSpiderFolderTree繼承MediaSpiderAbstract<MediaSpiderFolderTree>  媒體摘要樹


MediaSQLite  媒體SQL


MediaTreeModel 媒體樹模式


MovieComment繼承CDHtmlDialog  電影評論


NetworkControlerImpl 網络任務控制接口


OpenDeviceData繼承OpenMediaData  設備打開方式


OpenDVDData繼承OpenMediaData  DVD打開方式


OpenFileData繼承OpenMediaData   文件打開方式


OpenMediaData  打開方式基類


建議選項屬性頁
OptionAdvancedPage繼承WTL::CPropertyPageImpl<OptionAdvancedPage>,繼承WTL::CWinDataExchange<OptionAdvancedPage>


關聯文件屬性頁
OptionAssociationPage繼承WTL::CPropertyPageImpl<OptionAssociationPage>,繼承WTL::CWinDataExchange<OptionAssociationPage>


基本選項屬性頁
OptionBasicPage繼承WTL::CPropertyPageImpl<OptionBasicPage>,繼承WTL::CWinDataExchange<OptionBasicPage>


OptionDlg繼承WTL::CPropertySheetImpl<OptionDlg>  選項對話框


第二字幕選項屬性頁
OptionSubtitlePage繼承WTL::CPropertyPageImpl<OptionSubtitlePage>,繼承WTL::COwnerDraw<OptionSubtitlePage>,繼承WTL::CWinDataExchange<OptionSubtitlePage>


OSDController繼承LazyInstanceImpl<OSDController> OSD控制


OSDView繼承ATL::CWindowImpl<OSDView>,繼承LayeredWindowUtils<OSDView> OSD視圖


PlayerPreference繼承LazyInstanceImpl<PlayerPreference>  播放器引用


PlaylistController繼承LazyInstanceImpl<PlaylistController>  播放列表控制


PlaylistParser  播放列表分析


播放列表視圖
PlaylistView繼承ATL::CWindowImpl<PlaylistView>,繼承WTL::COwnerDraw<PlaylistView>,繼承 WTL::CCustomDraw<PlaylistView>


PlaylistViewMfcProxy繼承CSizingControlBarG   播放列表代理


SkinDownload繼承CDHtmlDialog,繼承ThreadHelperImpl<SkinDownload> 皮膚下載


SkinFolderManager  皮膚管理類


SnapUploadController繼承ThreadHelperImpl<SnapUploadController> 對齊上傳控制器


源模式模塊
template<class TDATA, class TCONDITION>
class SourceModel


SPlayerGUID 播放ID


SQLliteapp 操作數據庫


ssftest  測試ssf


SubtitleStyle  內部風格指數提供了實際的映射  字幕渲染設置接受外部字幕滤鏡。它還提供Windows基於GDI畫畫的能力
模擬渲染對話框顯示的目的。


字幕透明控制
SubTransController繼承ThreadHelperImpl<SubTransController>,繼承NetworkControlerImpl


SubTransFormat  字幕透明格式


SUIVoteStar


SVPLycShowBox  //歌詞顯示面板 mouseover時顯示半透明玻璃背景、關閉按鈕和變色按鈕,out後僅顯示文字
//默認在屏幕底部區域顯示,可以通過拖拽改變位置  關閉後改为在主窗口界面內顯示


ThemePkg  主題背景


ThemePkgController 主題背景控制


TimeBmpManage 事件畫面控制


 上傳控制
UbdUploadController繼承ThreadHelperImpl<UbdUploadController>,繼承LazyInstanceImpl<UbdUploadController>


UnCompressZip  解壓控制


 更新控制
UpdateController繼承NetworkControlerImpl,繼承ThreadHelperImpl<UpdateController>,繼承LazyInstanceImpl<UpdateController>


用戶着色控制
UserShareController繼承NetworkControlerImpl,繼承ThreadHelperImpl<UserShareController>,繼承LazyInstanceImpl<UserShareController>


用戶行为控制
UsrBehaviorController繼承LazyInstanceImpl<UsrBehaviorController>


用戶行为數據
UsrBehaviorData


今天主要針對  SPlayer 播放器項目的  CMPlayerc 分析

mplayerc  

   -->Model\appSQLite.h   主要負責操作sql  
      類成員成員中有兩個成員
      SQLliteapp* sqlite_setting; 
      SQLliteapp* sqlite_local_record; 

      對sqlite_setting  分析

      在StoreSettingsToIni 產生一個對象  
      sqlite_setting = PlayerPreference::GetInstance()->GetSqliteSettingPtr();


      保存數據前 存在 則開始輸出 BEGIN
      if(pApp->sqlite_setting){
pApp->sqlite_setting->begin_transaction();
}
      保存數據後 存在 則開始輸出 END
      if(pApp->sqlite_setting){
pApp->sqlite_setting->end_transaction();
}

      讀取整形數據
      if(sqlite_setting){
return sqlite_setting->GetProfileInt( lpszSection,  lpszEntry,  nDefault);
}else{
return __super::GetProfileInt( lpszSection,  lpszEntry,  nDefault);
}
}

       寫入整形數據
if(sqlite_setting){
return sqlite_setting->WriteProfileInt( lpszSection,  lpszEntry,  nValue);
}else{
SVP_LogMsg6("dwqdwq");
return __super::WriteProfileInt( lpszSection,  lpszEntry,  nValue);
}

        讀取字符集數據
if(sqlite_setting){
return sqlite_setting->GetProfileString( lpszSection,  lpszEntry,
lpszDefault );
}else{
return __super::GetProfileString( lpszSection,  lpszEntry,
lpszDefault );
}


        寫入字符集數據
if(sqlite_setting){
return sqlite_setting->WriteProfileString( lpszSection,  lpszEntry,
lpszValue);
}else{
return __super::WriteProfileString( lpszSection,  lpszEntry,
lpszValue);
}

        讀取二進制數據
if(sqlite_setting){
return sqlite_setting->GetProfileBinary( lpszSection,  lpszEntry,
ppData,  pBytes);
}else{
return __super::GetProfileBinary( lpszSection,  lpszEntry,
ppData,  pBytes);
}


        寫入二進制數據
if(sqlite_setting){
return sqlite_setting->WriteProfileBinary( lpszSection,  lpszEntry,
pData,  nBytes);
}else{
return __super::WriteProfileBinary( lpszSection,  lpszEntry,
pData,  nBytes);
}

        對sqlite_setting 分析完
        對sqlite_local_record 分析
        在InitInstanceThreaded(INT64 CLS64) 函數中 產生對象


        sqlite_local_record = new SQLliteapp(tmPath.m_strPath.GetBuffer())
      
      判斷是否打開數據庫
      if(!sqlite_local_record->db_open)
      {
        delete sqlite_local_record;
        sqlite_local_record = NULL;
      }

      執行數據庫  主要是創建表
      if(sqlite_local_record)
      {
        sqlite_local_record->exec_sql(L"CREATE TABLE  IF NOT EXISTS histories_stream (\"fpath\" TEXT, \"subid\" INTEGER, \"subid2\" INTEGER, \"audioid\" INTEGER, \"videoid\" INTEGER )");
        sqlite_local_record->exec_sql(L"CREATE UNIQUE INDEX  IF NOT EXISTS \"hispks\" on histories_stream (fpath ASC)");


        sqlite_local_record->exec_sql(L"CREATE TABLE  IF NOT EXISTS histories (\"fpath\" TEXT, \"subid\" INTEGER, \"subid2\" INTEGER, \"audioid\" INTEGER, \"stoptime\" INTEGER, \"modtime\" INTEGER )");
        sqlite_local_record->exec_sql(L"CREATE UNIQUE INDEX  IF NOT EXISTS \"hispk\" on histories (fpath ASC)");
        sqlite_local_record->exec_sql(L"CREATE INDEX  IF NOT EXISTS \"modtime\" on histories (modtime ASC)");


        sqlite_local_record->exec_sql(L"CREATE TABLE  IF NOT EXISTS settingstring (\"hkey\" TEXT, \"sect\" TEXT, \"vstring\" TEXT )");
        sqlite_local_record->exec_sql(L"PRAGMA synchronous=OFF");
        //sqlite_local_record->end_transaction();
      }

      在退出程序的時候  ExitInstance()
  if(sqlite_local_record){
   CString szSQL;
   szSQL.Format(L"DELETE FROM histories WHERE modtime < '%d' ", time(NULL)-3600*24*30);
   
// SVP_LogMsg5(szSQL);
   
sqlite_local_record->exec_sql(szSQL.GetBuffer());
   
sqlite_local_record->exec_sql(L"PRAGMA synchronous=ON");
  }
  if (sqlite_local_record)    delete sqlite_local_record;
$(ConfigurationName)

 

以上是針對主程序播放視頻的分析。

 

ChuckTest工程 是針對rar包裏的測試項目,測試代碼如下:

 

int _tmain(int argc, _TCHAR* argv[])
{
  HANDLE rar_handle = NULL;
  //std::wstring fn_rar = L"E:\\-=eMule=-\\1140746814_39741.rar";
  //std::wstring fn_rar = L"E:\\-=eMule=-\\Bride.Flight.2008.Bluray.720p.AC3.x264-CHD_新娘航班\\B2_新娘航班.part1.rar";
  //std::wstring fn_rar = L"D:\\xxxx.part1.rar";
  std::wstring fn_rar = L"E:\\-=eMule=-\\Overheard.2009.REPACK.CN.DVDRip.Xvid-XTM\\sample\\xtm-overheard.repack-sample_stored.rar";
  //std::wstring fn_rar = L"E:\\-=eMule=-\\Overheard.2009.REPACK.CN.DVDRip.Xvid-XTM\\sample\\xtm-overheard.repack-sample_s.part1.rar";
  
  std::wstring tmp_dir = L"C:\\Temp\\";
  
  struct RAROpenArchiveDataEx ArchiveDataEx;
  memset(&ArchiveDataEx, 0, sizeof(ArchiveDataEx));
  
  ArchiveDataEx.ArcNameW = (wchar_t*)fn_rar.c_str();
  //ArchiveDataEx.ArcName = "E:\\-=eMule=-\\1140746814_39741.rar";

  ArchiveDataEx.OpenMode = RAR_OM_EXTRACT;
  ArchiveDataEx.CmtBuf = 0;
  rar_handle = RAROpenArchiveEx(&ArchiveDataEx);

  const long buffsize = 1000;
  char testbuff[buffsize];

  if (rar_handle)
  {
    wprintf(L"RAROpenArchiveEx open successed\n");
    struct RARHeaderDataEx HeaderDataEx;
    HeaderDataEx.CmtBuf = NULL;
    while (RARReadHeaderEx(rar_handle, &HeaderDataEx) == 0)
    {
      // 正常解壓該文件
      unsigned long long filesize = ((unsigned long long)HeaderDataEx.UnpSizeHigh << 32) + HeaderDataEx.UnpSize;
      int err = 0;
      std::wstring unp_tmpfile = tmp_dir + HeaderDataEx.FileNameW;
      err = RARProcessFileW(rar_handle, RAR_EXTRACT, (wchar_t*)tmp_dir.c_str(), HeaderDataEx.FileNameW);
      wprintf(L"RARProcessFileW to %s return %d size %lld %x %x\n", unp_tmpfile.c_str(), err, filesize, HeaderDataEx.UnpVer, HeaderDataEx.Method);
    }
    RARCloseArchive(rar_handle);
  }

  rar_handle = RAROpenArchiveEx(&ArchiveDataEx);
  if (rar_handle)
  {
    wprintf(L"RAROpenArchiveEx open successed\n");
    struct RARHeaderDataEx HeaderDataEx;
    HeaderDataEx.CmtBuf = NULL;
    while (RARReadHeaderEx(rar_handle, &HeaderDataEx) == 0)
    {
      unsigned long long filesize = ((unsigned long long)HeaderDataEx.UnpSizeHigh << 32) + HeaderDataEx.UnpSize;
      int err = 0;
      std::wstring unp_tmpfile = tmp_dir + HeaderDataEx.FileNameW;
      err = RARExtractChunkInit(rar_handle, HeaderDataEx.FileName);
      if (err != 0)
      {
        wprintf(L"RARExtractChunkInit return error %d\n", err);
        continue;
      }

      FILE* unp_filehandle = NULL;
      err = _wfopen_s(&unp_filehandle, unp_tmpfile.c_str(), L"rb");
      if (err)
      {
        wprintf(L"open extracted file fail %d %d\n", err, unp_filehandle);
        continue;
      }

      
      // 順序測試
      int iExtractRet = 0;
      unsigned long long fpos = 0;
      do
      {
        iExtractRet = RARExtractChunk(rar_handle, (char*)testbuff, buffsize);
        // Compare 
        if (compare_filebinary(unp_filehandle, fpos, testbuff, iExtractRet, 0))
        {
          wprintf(L"Sequence compare difference found at %lld for %d\n", fpos, buffsize);
          break;
        }
        //else
        //  wprintf(L"Sequence compare is same %lld %d\n", fpos, iExtractRet);

        fpos += iExtractRet;
      } while(iExtractRet > 0);

      // 隨機測試
      for (int i = 0; i < 100; i++)
      {
        unsigned long long ll_pos = rand() * filesize/RAND_MAX;
        RARExtractChunkSeek(rar_handle, ll_pos, SEEK_SET);
        RARExtractChunk(rar_handle, (char*)testbuff, buffsize);
        // Compare 
        if (compare_filebinary(unp_filehandle, ll_pos, testbuff, iExtractRet, 0))
        {
          wprintf(L"Random compare difference found at %lld\n", ll_pos);
          break;
        }
        //else
        //  wprintf(L"Random compare is same %lld\n", ll_pos);
      }
      wprintf(L"RARExtractChunk test for %s finished\n", unp_tmpfile.c_str());
    }
    RARCloseArchive(rar_handle);
  }
  wprintf(L"Test finished\n");
  scanf_s("%d");
	return 0;
}
//字符切換
std::string WStringToString(const std::wstring& s)
{
  char* ch;
  UINT bytes = WideCharToMultiByte(CP_ACP, 0, s.c_str(), -1, NULL, 0,
    NULL, NULL); 
  ch = new char[bytes];
  if(ch)
    bytes = WideCharToMultiByte(CP_ACP, 0, s.c_str(), -1, ch, bytes,
    NULL, NULL);
  std::string str = ch;
  delete[] ch;
  return str;
}

//比較文件
int compare_filebinary(FILE* filehandle, unsigned long long filepos, char* buff, unsigned long buffsize, int debug)
{
  char *fbuf = (char *)malloc(buffsize);
  _fseeki64(filehandle, filepos, SEEK_SET);
  fread_s(fbuf, buffsize, sizeof(char), buffsize, filehandle);
  int ret = memcmp(buff, fbuf, buffsize);
  
  if (debug)
  {
    for (int i = 0;i < 100;i++)
      wprintf(L"%02x", (DWORD)buff[i] & 0xff);
    wprintf(L"\n");
    for (int i = 0;i < 100;i++)
      wprintf(L"%02x", (DWORD)fbuf[i] & 0xff);
    wprintf(L"\n");
  }

  free(fbuf);
  return ret;
}



HotkeySchemeParser_UnitTest 是針對熱鍵計劃分析器單元測試,測試代碼如下:

 

 

//熱鍵計劃分析器單元測試
int _tmain(int argc, _TCHAR* argv[])
{
  HotkeySchemeParser parser;
  printf("HotkeySchemeParser unit test\n");
  printf("----------------------------------------------------------\n");
  printf("Populating default scheme (HotkeySchemeParser::PopulateDefaultScheme) ...\n");
  parser.PopulateDefaultScheme();
  printf("Attempting to write scheme file (HotkeySchemeParser::WriteToFile) ... ");
  if (!parser.WriteToFile(L"SPlayerHotKey.txt"))
  {
    printf("[FAILED]\n");
    return 0;
  }
  printf("[OK]\n");
  printf("Attempting to read scheme file (HotkeySchemeParser::ReadFromFile) ... ");
  if (!parser.ReadFromFile(L"SPlayerHotKey.txt"))
  {
    printf("[FAILED]\n");
    return 0;
  }
  printf("[OK]\n");

  //目標鍵
  HotkeySchemeParser target;
  target.PopulateDefaultScheme();
  printf("Comparing results (scheme name)... ");
  if (target.GetSchemeName() != parser.GetSchemeName())
  {
    printf("[ERROR]\n");
    return 0;
  }
  printf("[OK]\n");
  printf("Comparing results (list)... ");
  std::vector<HotkeyCmd> parser_list = parser.GetScheme();
  std::vector<HotkeyCmd> target_list = target.GetScheme();
  if (parser_list.size() != target_list.size())
  {
    printf("[SIZE MISMATCH]\n");
    return 0;
  }
  for (std::vector<HotkeyCmd>::iterator it1 = parser_list.begin(),
    it2 = target_list.begin();
    it1 != parser_list.end(),
    it2 != target_list.end();
    it1++, it2++)
  {
    if (it1->cmd != it2->cmd ||
      it1->key != it2->key ||
      it1->fVirt != it2->fVirt ||
      it1->appcmd != it2->appcmd ||
      it1->mouse != it2->mouse)
    {
      printf("[ERROR @ %d]\n", std::distance(parser_list.begin(), it1));
      return 0;
    }
  }
  printf("[OK]\n");

	return 0;
}

sqliteppTest 是針對第三方庫sqlitepp操作數據庫的測試,測試代碼如下:

 

 

/* step0 init - *******************
*first check del file Testsqlite.db*/

const wchar_t *filename=L"./Testsqlite.db";
const wchar_t *sqlc1=L"CREATE TABLE IF NOT EXISTS \"settingint\" (\"hkey\" TEXT,\"sect\" TEXT,\"sval\" INTEGER)";
const wchar_t *sqlc2=L"CREATE TABLE IF NOT EXISTS \"settingstring\" (\"hkey\" TEXT,   \"sect\" TEXT, \"vstring\" TEXT)";
const wchar_t *sqlc3 = L"CREATE TABLE IF NOT EXISTS \"settingbin2\" (\"skey\" TEXT,   \"sect\" TEXT, \"vdata\" BLOB)";
const wchar_t *sqlc4 = L"CREATE UNIQUE INDEX IF NOT EXISTS \"pkey\" on settingint (hkey ASC, sect ASC)";
const wchar_t *sqlc5 = L"CREATE UNIQUE INDEX IF NOT EXISTS \"pkeystring\" on settingstring (hkey ASC, sect ASC)";
const wchar_t *sqlc6 = L"CREATE UNIQUE INDEX IF NOT EXISTS \"pkeybin\" on settingbin2 (skey ASC, sect ASC)";

//定義
TCHAR path[MAX_PATH] = {0};
::GetModuleFileName(NULL,path,MAX_PATH);
CStringW strSQL(path);
strSQL.Replace(_T("TestSqlite.exe"),_T("Testsqlite.db"));

int i=0;
std::wstring sql,sql2,sql3,sql4,sql5;
SQLliteapp p(strSQL.GetBuffer());  //構造會創建數據庫文件

/* step1 : Create Table 執行sql語句 創建表 字段 數值*/
p.exec_sql(sqlc1);
p.exec_sql(sqlc2);
p.exec_sql(sqlc3);
p.exec_sql(sqlc4);
p.exec_sql(sqlc5);
p.exec_sql(sqlc6);

std::wstring str1=L"hello";
std::wstring str2=L"world";

/* step2 int-test  *******************
 *
 *   write int - WriteProfileInt
 get int - GetProfileInt compare    寫入 讀取整形數值 比較
  ************************************/
{
int geti1=1;
int geti2=100;

geti1=p.WriteProfileInt(str2.c_str(),str1.c_str(),-10,false);
int test01=p.GetProfileInt(str2.c_str(),str1.c_str(),10,false);
if(geti1)
printf("pass -- 1 \n");
else
printf("fail -- 1 \n");
p.WriteProfileInt(str1.c_str(),str2.c_str(),geti2,false);
geti1=p.GetProfileInt(str1.c_str(),str2.c_str(),-1,false);
if(geti2==geti1)
printf("pass -- 2 \n");
else
printf("fail -- 2 \n");
}

/* step3 string-test  ***************
 *
 *   write str - WriteProfileString
get str - GetProfileString
compare    寫入 讀取 字符集比較
 ************************************/
{
std::wstring str3s1, str3s2;
str3s1 = L"ss";
str3s2 = L"hello你好\n";
str3s1 = p.GetProfileString(str2.c_str(), str1.c_str(),L"xx",false);

if(str3s1 == L"xx")
printf("pass--3\n");   // ok
else
printf("fail--3\n");

p.WriteProfileString(str1.c_str(), str2.c_str(), str3s2.c_str());
str3s1 = p.GetProfileString(str1.c_str(), str2.c_str(), L"xx",false);

if(str3s1.compare(str3s2) == 0)
printf("pass--4\n");   // ok
else
printf("fail--4\n");
}

/* step4 binrary-test  **************
 *
 *   write bin
 get bin - donot forget new/delete   寫入 讀取 二進制文件
 **************************************/
{
char *pp = 0; //donot forget new/delete
char buf[100];
char str3[] = "hello4";
char str4[] = "world4";

for(i=0;i<80;i++)
{
buf[i] = i * 3 % 5 + 2;
}

int bPass=0;    //成功的話 bPass 返回個數
p.GetProfileBinary(str2.c_str(), str1.c_str(),(LPBYTE*)&pp,(UINT*)&bPass,false);

if( bPass == 0 )
printf("pass--5 bin\n");
else
printf("fail--5 bin\n");

if(i>0 && pp)
{
delete[] pp;
pp = NULL;
}

p.WriteProfileBinary(str1.c_str(), str2.c_str(), (LPBYTE)buf, 80); //w 80 BYTE
p.GetProfileBinary(str1.c_str(), str2.c_str(), (LPBYTE*)&pp, (UINT*)&bPass, false); //成功的話 bPass 返回個數

if(memcmp(pp,buf,30) == 0)
printf("pass--6 bin\n");
else
printf("fail--6 bin\n");

if(bPass>0 && pp)
{
delete[] pp;
pp = NULL;
}
}

 

 分析到此就結束了!~這是我針對這SPlayer項目的簡單分析!

 學習的目標是成熟!~~~

 

 

 


From:CSDN        

 




創作者介紹
創作者 shadow 的頭像
shadow

資訊園

shadow 發表在 痞客邦 留言(0) 人氣()