本文有雷,慎入!
「羅貫中可以改寫三國志,吳宇森為什麼不可以說一套自己的三國故事?」
在看完第一集以後,一直給自己這樣的心理建設,但看完第二集還是忍不住要罵「合理一點吧!」
雖然說看到小喬入敵營、劉備殺入曹寨等等諸多大改的劇情十分意外,但這些都還可以算是「驚喜」,但劇中有太多細節讓人「訝異」!
例如
弓箭與連弩並列射擊:把長短程兵器擺在一起,怪得亂七八糟!
甘興用「魚油炸彈」炸毀木寨城牆:爆裂火器和燃燒火器差很多,爆裂式的火器到宋朝才出現吧!不愧是超時空的創意!
騎兵怕步兵:劇中曹營騎兵出來只敢繞著吳軍的龜甲方陣旁邊走,完全忽視基本的騎兵戰術「衝擊力」的應用,這種打法太可笑了吧!
打城腳的投石機:投石機和弓箭應該是不同距離互補的遠攻兵器,結果弓箭都開始射了投石機才打,那不成了打城腳的投石機!?不知道他彈道怎麼算的!?
總之,要創意球創新不是不行,但不求合理性的話,我只能說會帶給觀眾創傷啊!這部戲只能歸類於「歡樂武俠」系列,畫面熱鬧、大牌雲集、服裝講究、配樂精緻,一看就是砸大錢拍的,只可惜在細節上太不用心,以致於看得倒盡胃口啊!
[C#]全域鍵盤事件攔截(Low Level Keyboard Hook)
寫XDHome的時候其實只有兩個技術問題需要突破,一個是上次提過的影片播放,另一個就是要如何在視窗不被focus的時候也能夠抓到鍵盤。
基本上要解決這個問題,必須要呼叫Windows API去設定所謂的Low Level Hook,網路上有一些範例可以抓,基本上我參考了George Mamaladze和Huan Lin兩人的著作,將程式改為指針對Keyboard的KeyDown事件的版本,程式碼如下:
Low Level Keyboard Hook服務程式
此段程式碼提供兩個static KeyDown Event供客戶端程式碼新增Even Handler
客戶端程式碼,Handel static的全域KeyDown Event
要加入全域事件時
要取消全域事件處理時
基本上要解決這個問題,必須要呼叫Windows API去設定所謂的Low Level Hook,網路上有一些範例可以抓,基本上我參考了George Mamaladze和Huan Lin兩人的著作,將程式改為指針對Keyboard的KeyDown事件的版本,程式碼如下:
Low Level Keyboard Hook服務程式
此段程式碼提供兩個static KeyDown Event供客戶端程式碼新增Even Handler
    // Low Level Keyboard Hook
    public class KeyboardHook
    {
        // System Code: Low Level Keyboard Hook
        private const int WH_KEYBOARD_LL = 13;
        // System Event Code: Key Down Event
        private const int WM_KEYDOWN = 0x100;
        
        // System Event Code: System Key Down Event
        private const int WM_SYSKEYDOWN = 0x104;
        // Stores the handle to the Keyboard hook procedure.
        private static int s_KeyboardHookHandle;
        //是否只由這個Global Hook抓取鍵盤事件
        public static bool globalControlOnly = false;
        //Private KeyDown Event, 與GlobalKeyDown配合使用
        private static event KeyEventHandler _globalKeyDown;
        // Public KeyDown Event
        // 每次只能一個Event Handler處理這個Event
        // 加入和解除EventHandler時會自動安裝和解除Hook
        public static event KeyEventHandler GlobalKeyDown 
        {
            add 
            {
                KeyboardHook.HookKeyboard();
                KeyboardHook._globalKeyDown += value;
            }
            remove
            {
                KeyboardHook._globalKeyDown -= value;
                KeyboardHook.UnhookKeyboard();
            }
        }
        
        // 當hook抓到key event時的處理程序
        public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
        // Hook handle
        private static int m_HookHandle = 0;
        
        // Keyboard Hook函式指標
        private static HookProc m_KbdHookProc;  
        // WinAPI 取得Module Handle
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);
        // WinAPI 加入Hook
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
        // WinAPI 解除Hook
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);
        // WinAPI  將Event傳給下一個Hook,如不執行此項,則只有這個Hook會被執行
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
        private static void HookKeyboard()
        {
            if (m_HookHandle == 0)
            {
                KeyboardHook.s_KeyboardHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL,  m_KbdHookProc,
                                                                     Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
                using (Process curProcess = Process.GetCurrentProcess())
                {
                    using (ProcessModule curModule = curProcess.MainModule)
                    {
                        m_KbdHookProc = new HookProc(KeyboardHookProc);
                        m_HookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, m_KbdHookProc, GetModuleHandle(curModule.ModuleName), 0);
                    }
                }
                if (m_HookHandle == 0)
                {
                    throw new Exception("Install Global Keyboard Hook Faild.");
                }
            
            }
        }
        private static void UnhookKeyboard()
        {
            
            if (m_HookHandle != 0)
            {
                bool ret = UnhookWindowsHookEx(m_HookHandle);
                if (ret)
                {
                    m_HookHandle = 0; 
                }
                else
                {
                    throw new Exception("Uninstall Global Keyboard Hook Faild.");
                }
            
            }
        
        }
        private static int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            bool handled = false;
            if(nCode >= 0)
            {
            if ((int)wParam == WM_KEYDOWN || (int)wParam == WM_SYSKEYDOWN)
            {
                KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
                Keys keyData = (Keys)MyKeyboardHookStruct.VirtualKeyCode;
                KeyEventArgs e = new KeyEventArgs(keyData);
                if (KeyboardHook.globalControlOnly)
                {
                    e.Handled = true;
                }
                else
                {
                    e.Handled = false;
                }
                
                _globalKeyDown.Invoke(null, e);
                handled = e.Handled;
            }
            }
            if (KeyboardHook.globalControlOnly) return -1;
            return CallNextHookEx(s_KeyboardHookHandle, nCode, wParam, lParam);
            
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct KeyboardHookStruct
        {
            /// 
            /// Specifies a virtual-key code. The code must be a value in the range 1 to 254. 
            /// 
            public int VirtualKeyCode;
            /// 
            /// Specifies a hardware scan code for the key. 
            /// 
            public int ScanCode;
            /// 
            /// Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
            /// 
            public int Flags;
            /// 
            /// Specifies the Time stamp for this message.
            /// 
            public int Time;
            /// 
            /// Specifies extra information associated with the message. 
            /// 
            public int ExtraInfo;
        }
    }
客戶端程式碼,Handel static的全域KeyDown Event
        private void KeyboardHook_KeyDown(object sender, KeyEventArgs e)
        {
            string key = e.KeyCode.ToString();  //取得KeyCode字串
            //TODO: 加入處理KeyDown事件的程式
        }
要加入全域事件時
            KeyboardHook.globalControlOnly = true;  //只有Global KeyDown會起作用,其他程式不能攔截鍵盤事件
            KeyboardHook.GlobalKeyDown += KeyboardHook_KeyDown; 
要取消全域事件處理時
            KeyboardHook.GlobalKeyDown -= KeyboardHook_KeyDown;
繼續閱讀...
XDHome 0.6.1 HTPC播放軟體 小改版釋出
張貼者:
Aaron Liu
於
晚上9:45
 | 
      
下載XDHome 0.6.1
XDHome 0.6.1版釋出,這次除了修正一些bug之外,還將原始檔一併公布。
修正: Pause鍵按第二次時不會恢復播放狀態
目前bin和原始檔都公布在SourceForge上面,原始碼以LGPL方式授權,歡迎有興趣的朋友一起研究。
XDHome 0.6.1版釋出,這次除了修正一些bug之外,還將原始檔一併公布。
修正: Pause鍵按第二次時不會恢復播放狀態
目前bin和原始檔都公布在SourceForge上面,原始碼以LGPL方式授權,歡迎有興趣的朋友一起研究。
繼續閱讀...
[分享]XDHome的由來 - 窮人的HTPC改造計畫
用客廳裡的大電視看低調影片一直是我的夢想,特別是有一台HTPC能身兼動物機和播放機的話,就更棒了!無奈阮囊羞澀,要買一台小PC加上iMon之類的周邊的話,耗費不少小朋友的!
前一陣子藉著搬家的機會,下定決心敗入Benq HS3742,但沒有HTPC的他,只是讓Full HD的性能空顯寂寞而已!

主角1 Benq SH3742

Full HD貼紙驗明正身
剛好,家裡還有學生時代留下來的HP NC6000筆電,雖然是五年前的古董,但當一台動物機兼播放機應該還綽綽有餘吧!?以VGA線插上電視一試竟然可以上1920 x 1080的Full HD解析度!雖然只能在單螢幕模式做這種輸出,但對於這台老古董來講,真是喜出望外啊!不讓他當HTPC真是太可惜了!

主角2 HP NC6000

1920x1080 Full HD輸出
有了這個想法以後,尋找電腦遙控器就成了關鍵。像iMon這樣動輒兩三張小朋友的高貴產品,顯然不合乎我克難的需求,用微軟的MCE遙控器是當時主要的考量之一,可是我偶然間逛到Y拍上有賣這款大樂RM03遙控器,「號稱」支援我最熟悉的Media Player Classic,似乎有機會擺脫Windows MCE,而且售價又在一張小朋友以內,於是馬上敗入。

大樂RM-03 電腦遙控器
所謂代誌不是憨人想的那樣簡單,RM-03雖然號稱支援Media Player Classic,可是Media Player Classic沒有瀏覽檔案的功能,字體也太小,而且RM-03附的驅動程式我個人也不是很能接受,於是使用RM03方案很快就被我判出局了!
所謂山不轉路轉,遙控器附的程式我不滿意,大不了我自己寫一個啊!於是XDHome這套軟體誕生了。由於RM03在不裝驅動程式的狀態下就是一支鍵盤,因此XDHome就把基本的播放功能,如開檔、暫停、快轉、倒轉、全螢幕....等指令變成以鍵盤的方式觸發,再加上一個可以自訂按鈕的介面,RM03就變得頗好用了!
有圖有真相:

用XDHome選擇要播放的檔案

全螢幕播放720p的影片

電視+NB+遙控器接收器大合照
嘿嘿~~這就是窮人HTPC大改造!不到一張小朋友就解決哩!
前一陣子藉著搬家的機會,下定決心敗入Benq HS3742,但沒有HTPC的他,只是讓Full HD的性能空顯寂寞而已!
主角1 Benq SH3742
Full HD貼紙驗明正身
剛好,家裡還有學生時代留下來的HP NC6000筆電,雖然是五年前的古董,但當一台動物機兼播放機應該還綽綽有餘吧!?以VGA線插上電視一試竟然可以上1920 x 1080的Full HD解析度!雖然只能在單螢幕模式做這種輸出,但對於這台老古董來講,真是喜出望外啊!不讓他當HTPC真是太可惜了!
主角2 HP NC6000
1920x1080 Full HD輸出
有了這個想法以後,尋找電腦遙控器就成了關鍵。像iMon這樣動輒兩三張小朋友的高貴產品,顯然不合乎我克難的需求,用微軟的MCE遙控器是當時主要的考量之一,可是我偶然間逛到Y拍上有賣這款大樂RM03遙控器,「號稱」支援我最熟悉的Media Player Classic,似乎有機會擺脫Windows MCE,而且售價又在一張小朋友以內,於是馬上敗入。
大樂RM-03 電腦遙控器
所謂代誌不是憨人想的那樣簡單,RM-03雖然號稱支援Media Player Classic,可是Media Player Classic沒有瀏覽檔案的功能,字體也太小,而且RM-03附的驅動程式我個人也不是很能接受,於是使用RM03方案很快就被我判出局了!
所謂山不轉路轉,遙控器附的程式我不滿意,大不了我自己寫一個啊!於是XDHome這套軟體誕生了。由於RM03在不裝驅動程式的狀態下就是一支鍵盤,因此XDHome就把基本的播放功能,如開檔、暫停、快轉、倒轉、全螢幕....等指令變成以鍵盤的方式觸發,再加上一個可以自訂按鈕的介面,RM03就變得頗好用了!
有圖有真相:
用XDHome選擇要播放的檔案
全螢幕播放720p的影片
電視+NB+遙控器接收器大合照
嘿嘿~~這就是窮人HTPC大改造!不到一張小朋友就解決哩!
繼續閱讀...
XDHome 0.6 電視專用播放器
下載XDHome 0.6版
這個版本較前一版(0.1)有許多的改善,我還是重新介紹一下好了。
XDHome是一個可以完全用遙控器/鍵盤操作的影片播放器,專門用來將電腦內的影片在電視上播放用的,所以它可以:
 
 
0.6版較0.1版更新的功能有:
使用的時候請注意:
這個版本較前一版(0.1)有許多的改善,我還是重新介紹一下好了。
XDHome是一個可以完全用遙控器/鍵盤操作的影片播放器,專門用來將電腦內的影片在電視上播放用的,所以它可以:
- 以大字體瀏覽檔案,讓人在電視上也看得清楚檔名。
- 自行設定所有功能與鍵盤的對應,讓任何遙控器都可以配合使用。
- 播放影片檔,只要有裝正確的CODEC就可以播出,同時也支援外掛字幕。
 
 
0.6版較0.1版更新的功能有:
- 有完整的檔案系統瀏覽功能
- 新增自訂按鍵功能
- 新增捷徑功能
- 影片播放改用DirectShowNet,沒有相容問題
- 全新的Keyboard Hook,較前版穩定
使用的時候請注意:
- XDHome開啟時會攔截所有的鍵盤事件,其他程式會抓不到鍵盤,但少數系統鍵還是可以運作(例如Ctrl+Alt+Del)
- XDHome是以C#基於.Net 2.0和DirectShowNet 2.0開發的,必須要安裝.Net Framework 2.0和DirectX 9以上版本才能執行。
- 目前只能播放影片檔(avi、wmv、rm、rmvb、mkv),但是DVD還不行。
繼續閱讀...
[C#]使用DirectShowNet播放影片檔
DirectShowNet是SourceForge上面的開放源碼函示庫,基本上它將DirectShow 9包裝成.Net 2.0的函示庫,使用上其實有點複雜,要用的話,請先下載DirectShowNet 2.0。程式碼是按照DirectShowNet的範例改出來的,案例可以從這裡下載。
我將一些播放影片檔的基本功能功能寫成含註解的程式碼如下:
根據我自己的實測,這樣寫出來的播放器,只要Codec有裝幾乎什麼檔都可以播,包含AVI、WMV、MKV都已經測過OK,連str外掛字幕檔都可以顯示,還不賴。
我將一些播放影片檔的基本功能功能寫成含註解的程式碼如下:
    /// <summary>
    /// 播放狀態
    /// </summary>
    public enum PlayState
    {
        Stopped,    //視訊停止
        Paused,     //視訊暫停
        Playing,    //視訊播放中
        Init        //尚未開啟任何影片檔
    };
    /// <summary>
    /// 播放Video物件
    /// </summary>
    public class Video
    {
        private const int WMGraphNotify = 0x0400 + 13;
        public const int VolumeFull = 0;
        public const int VolumeSilence = -10000;
        private DirectShowLib.IGraphBuilder graphBuilder = null;
        private DirectShowLib.IMediaControl mediaControl = null;
        private DirectShowLib.IMediaEventEx mediaEventEx = null;
        private DirectShowLib.IVideoWindow videoWindow = null;
        private DirectShowLib.IBasicAudio basicAudio = null;
        private DirectShowLib.IBasicVideo basicVideo = null;
        private DirectShowLib.IMediaSeeking mediaSeeking = null;
        private DirectShowLib.IMediaPosition mediaPosition = null;
        private DirectShowLib.IVideoFrameStep frameStep = null;
        private string filePath = string.Empty;
        private bool _FullScreen = false;
        private int _Volume = VolumeFull;
        public PlayState State = PlayState.Init;
        private double currentPlaybackRate = 1.0;
        private Size _size = new Size(0, 0);
        private IntPtr hDrain = IntPtr.Zero;
    #if DEBUG
        private DsROTEntry rot = null;
    #endif
        /// <summary>
        /// 裝載視訊物件的Container
        /// </summary>
        public Control Owner;
        /// <summary>
        /// 是否全螢幕模式
        /// </summary>
        public bool FullScreen
        {
            get 
            {
                return _FullScreen;
            }
            set
            {
                if (this.State == PlayState.Init)
                {
                    throw new Exception("No video file has been opened");
                }
                if (value != _FullScreen)
                {
                    int hr = 0;
                    OABool lMode;
                    if (value)
                    {
                        //設定為全螢幕
                        hr = this.videoWindow.get_MessageDrain(out hDrain);
                        DsError.ThrowExceptionForHR(hr);
                        hr = this.videoWindow.put_MessageDrain(Owner.Handle);
                        DsError.ThrowExceptionForHR(hr);
                        lMode = OABool.True;
                        hr = this.videoWindow.put_FullScreenMode(lMode);
                        DsError.ThrowExceptionForHR(hr);
                    }
                    else
                    {
                        //回復視窗模式
                        lMode = OABool.False;
                        hr = this.videoWindow.put_FullScreenMode(lMode);
                        DsError.ThrowExceptionForHR(hr);
                        hr = this.videoWindow.put_MessageDrain(hDrain);
                        DsError.ThrowExceptionForHR(hr);
                        hr = this.videoWindow.SetWindowForeground(OABool.True);
                        DsError.ThrowExceptionForHR(hr);
                    }
                }
                this._FullScreen = value;
            }
    
        }
        /// <summary>
        /// 取得/設定寬度
        /// </summary>
        public int Width
        {
            get
            {
                return _size.Width;
            }
            set
            {
                if (this.State == PlayState.Init)
                {
                    throw new Exception("No video file has been opened");
                }
                int hr = 0;
                this.Owner.Width = value;
                hr = this.videoWindow.SetWindowPosition(0, 0, value, _size.Height);
                DsError.ThrowExceptionForHR(hr);
                this._size.Height = value;
            }
        }
        /// <summary>
        /// 取得/設定高度
        /// </summary>
        public int Height
        {
            get
            {
                return _size.Height;
            }
            set
            {
                if (this.State == PlayState.Init)
                {
                    throw new Exception("No video file has been opened");
                }
                int hr = 0;
                this.Owner.Height = value;
                hr = this.videoWindow.SetWindowPosition(0, 0, _size.Width, value);
                DsError.ThrowExceptionForHR(hr);
                this._size.Height = value;
            }
        }
        /// <summary>
        /// 取得目前影片的長度(秒數)
        /// </summary>
        public long Duration
        {
            get
            {
                if (State == PlayState.Init)
                {
                    return 0;
                }
                else
                {
                    int hr = 0;
                    long dur;
                    hr = this.mediaSeeking.GetDuration(out dur);
                    DsError.ThrowExceptionForHR(hr);
                    TimeSpan t = new TimeSpan(dur);
                    return (long)t.TotalSeconds;
                }
            }
        }
        /// <summary>
        /// 目前的位置(秒數)
        /// </summary>
        public long Position
        {
            get
            {
                if (State == PlayState.Init)
                {
                    return 0;
                }
                else
                {
                    int hr = 0;
                    long pos, stop;
                    hr = this.mediaSeeking.GetPositions(out pos, out stop);
                    TimeSpan t = new TimeSpan(pos);
                    
                    return (long)t.TotalSeconds;
                }
            }
            set
            {
                if (this.State == PlayState.Init)
                {
                    throw new Exception("No video file has been opened");
                }
                if (Position < 0 || Position > Duration)
                {
                    throw new Exception("Position out of range");
                }
                int hr = 0;
                TimeSpan t = new TimeSpan(0, 0,(int)value);
                DsLong newPosition = new DsLong(t.Ticks);
                hr = this.mediaSeeking.SetPositions(newPosition, AMSeekingSeekingFlags.AbsolutePositioning, null, AMSeekingSeekingFlags.NoPositioning);
            }
        }
        /// <summary>
        /// 目前的音量大小 0~100
        /// </summary>
        public int Volume
        {
            get
            {
                
                return this._Volume / 100 + 100;
            }
            set
            {
                if (value > 100 || value < 0)
                {
                    throw new Exception("Volume must between 0 to 100");
                }
                int hr = 0;
                this._Volume = (value - 100) * 100;
                hr = this.basicAudio.put_Volume(this._Volume);
            }
        }
       
        /// <summary>
        /// 初始化Video物件
        /// </summary>
        /// <param name="owner">Video附著的Container 通常是一個Pannel或Form</param>
        public Video(ref Control owner)
        {
            this.Owner = owner;
        }
        /// <summary>
        /// 開啟視訊檔
        /// </summary>
        /// <param name="filePath">視訊檔案路徑</param>
        /// <param name="autoPlay">開啟後是否自動播放</param>
        public void Open(string filePath, bool autoPlay)
        {
            int hr = 0;
            if (!System.IO.File.Exists(filePath))
            {
                throw new Exception("File " + filePath + "Not Found!");
            }
            if (State != PlayState.Init)
            {
                CloseVideo();
            }
            //取得檔案stream
            this.graphBuilder = (DirectShowLib.IGraphBuilder)new FilterGraph();
            hr = this.graphBuilder.RenderFile(filePath, null);
            DsError.ThrowExceptionForHR(hr);
            //Get DirectShow Control interfaces
            this.mediaControl = (DirectShowLib.IMediaControl)this.graphBuilder;
            this.mediaEventEx = (DirectShowLib.IMediaEventEx)this.graphBuilder;
            this.mediaSeeking = (DirectShowLib.IMediaSeeking)this.graphBuilder;
            this.mediaPosition = (DirectShowLib.IMediaPosition)this.graphBuilder;
            this.videoWindow = this.graphBuilder as DirectShowLib.IVideoWindow;
            this.basicVideo = this.graphBuilder as DirectShowLib.IBasicVideo;
            this.basicAudio = this.graphBuilder as DirectShowLib.IBasicAudio;
            if (!IsVideo())
            {
                throw new Exception("The File Is Not A Video File Or The Codec Is Unsupported!");
            }
            //Set the playback event handle object
            //hr = this.mediaEventEx.SetNotifyWindow(this.Owner.Handle, WMGraphNotify, IntPtr.Zero);
            //DsError.ThrowExceptionForHR(hr);
            //Set the video owner object to contain the vidwo window
            hr = this.videoWindow.put_Owner(this.Owner.Handle);
            DsError.ThrowExceptionForHR(hr);
            //Set video window layout
            hr = this.videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipSiblings | WindowStyle.ClipChildren);
            DsError.ThrowExceptionForHR(hr);
            //Get vedio size
            int lHeight, lWidth;
            hr = this.basicVideo.GetVideoSize(out lWidth, out lHeight);
            DsError.ThrowExceptionForHR(hr);
            //set owner object size as video size
            this.Owner.Width = lWidth;
            this.Owner.Height = lHeight;
            this._size.Width = lWidth;
            this._size.Height = lHeight;
            hr = this.videoWindow.SetWindowPosition(0, 0, lWidth, lHeight);
            DsError.ThrowExceptionForHR(hr);
            GetFrameStepInterface();
            this._FullScreen = false;
            this.currentPlaybackRate = 1.0;
            this.State = PlayState.Stopped;
            this.filePath = filePath;
            if (autoPlay)
            {
                Play();
            }
            
        }
        /// <summary>
        /// 播放視訊檔
        /// </summary>
        public void Play()
        {
            if (State == PlayState.Stopped || State == PlayState.Paused)
            {
                int hr = 0;
                hr = this.mediaControl.Run();
                DsError.ThrowExceptionForHR(hr);
                this.State = PlayState.Playing;
            }
            
        }
        /// <summary>
        /// 停止播放視訊檔
        /// </summary>
        public void Stop()
        {
            if (State == PlayState.Playing || State == PlayState.Paused)
            {
                int hr = 0;
                //停止播放
                hr = this.mediaControl.Stop();
                DsError.ThrowExceptionForHR(hr);
                //回到第一個影格
                DsLong pos = new DsLong(0);
                hr = this.mediaSeeking.SetPositions(pos, AMSeekingSeekingFlags.AbsolutePositioning, null, AMSeekingSeekingFlags.NoPositioning);
                //顯示第一個影格
                hr = this.mediaControl.Pause();
                
                this.State = PlayState.Stopped;
            }
        }
        /// <summary>
        /// 停止播放視訊檔
        /// </summary>
        public void Pause()
        {
            if (State == PlayState.Playing)
            {
                int hr = 0;
                hr = this.mediaControl.Pause();
                DsError.ThrowExceptionForHR(hr);
                this.State = PlayState.Paused;
            }
        }
        /// <summary>
        /// 前進
        /// </summary>
        /// <param name="second">前進的秒數</param>
        public void Forward(int second)
        {
            if (Position + second < Duration)
            {
                Position += second;
            }
            else
            {
                Position = Duration;
            }
        }
        /// <summary>
        /// 倒退
        /// </summary>
        /// <param name="second">倒退的秒數</param>
        public void Backward(int second)
        {
            if (Position - second > 0)
            {
                Position -= second;
            }
            else
            {
                Position = 0;
            }
        }
        /// <summary>
        /// 視訊檔案是否是一個被支援的視訊檔?
        /// </summary>
        /// <returns>true = 是;  false = 否</returns>
        private bool IsVideo()
        {
            int hr = 0;
            OABool lVisible;
            bool result = true;
            if ((this.videoWindow == null) || (this.basicVideo == null))
            {
                //是一個Audio檔(沒有視訊,只有音訊)
                result = false;
            }
            hr = this.videoWindow.get_Visible(out lVisible);
            if (hr < 0)
            {
                // 視訊檔使用的Codec不被支援的或根本不是視訊檔
                if (hr == unchecked((int)0x80004002)) //E_NOINTERFACE
                {
                    result = false;
                }
                else
                    DsError.ThrowExceptionForHR(hr);
            }
            return result;
        }
        /// <summary>
        /// 取得格放時要用的IVideoFrameStep介面
        /// </summary>
        /// <returns>true = 可以取得格放介面  false = 無法取得格放介面</returns>
        private bool GetFrameStepInterface()
        {
            int hr = 0;
            DirectShowLib.IVideoFrameStep frameStepTest = null;
            // Get the frame step interface, if supported
            frameStepTest = (DirectShowLib.IVideoFrameStep)this.graphBuilder;
            // Check if this decoder can step
            hr = frameStepTest.CanStep(0, null);
            if (hr == 0)
            {
                this.frameStep = frameStepTest;
                return true;
            }
            else
            {
                this.frameStep = null;
                return false;
            }
        }
        /// <summary>
        /// 關閉視訊檔
        /// </summary>
        public  void CloseVideo()
        {
            int hr = 0;
            //停止播放
            if (this.mediaControl != null)
                hr = this.mediaControl.Stop();
            // Clear global flags
            this.State = PlayState.Stopped;
            this.FullScreen = false;
            //關閉DirectShow Control Interfece
            CloseInterfaces();
            
            this.filePath = string.Empty;
            this.State = PlayState.Init;
        }
        /// <summary>
        /// 關閉DirectShow的Control Interface
        /// </summary>
        private void CloseInterfaces()
        {
            int hr = 0;
            try
            {
                lock (this)
                {
                    // Relinquish ownership (IMPORTANT!) after hiding video window
                    if (this.videoWindow != null)
                    {
                        hr = this.videoWindow.put_Visible(OABool.False);
                        DsError.ThrowExceptionForHR(hr);
                        hr = this.videoWindow.put_Owner(IntPtr.Zero);
                        DsError.ThrowExceptionForHR(hr);
                    }
                    if (this.mediaEventEx != null)
                    {
                        hr = this.mediaEventEx.SetNotifyWindow(IntPtr.Zero, 0, IntPtr.Zero);
                        DsError.ThrowExceptionForHR(hr);
                    }
    #if DEBUG
                    if (rot != null)
                    {
                        rot.Dispose();
                        rot = null;
                    }
    #endif
                    // Release and zero DirectShow interfaces
                    if (this.mediaEventEx != null)
                        this.mediaEventEx = null;
                    if (this.mediaSeeking != null)
                        this.mediaSeeking = null;
                    if (this.mediaPosition != null)
                        this.mediaPosition = null;
                    if (this.mediaControl != null)
                        this.mediaControl = null;
                    if (this.basicAudio != null)
                        this.basicAudio = null;
                    if (this.basicVideo != null)
                        this.basicVideo = null;
                    if (this.frameStep != null)
                        this.frameStep = null;
                    if (this.graphBuilder != null)
                        Marshal.ReleaseComObject(this.graphBuilder); this.graphBuilder = null;
                    GC.Collect();
                }
            }
            catch(Exception ex)
            {
                throw new Exception("Video close faild:\r\n" + ex.Message);
            }
        }
        
    }
根據我自己的實測,這樣寫出來的播放器,只要Codec有裝幾乎什麼檔都可以播,包含AVI、WMV、MKV都已經測過OK,連str外掛字幕檔都可以顯示,還不賴。
繼續閱讀...
訂閱:
意見 (Atom)
 


 
 發表文章
發表文章
 
 

