1. 程式人生 > >C#編寫的基於VLC的播放器

C#編寫的基於VLC的播放器

首先看一下最終的程式效果


實現的功能:

1:開啟播放的音視訊檔案((1)選單欄“檔案”->“開啟”,(2)工具欄(下面)“開啟”(3)播放器右鍵->開啟)

2:暫停,繼續播放,停止音視訊檔案

3:進度條和右下角文字框顯示播放進度

4:拖動進度條對視訊定位播放

5:工具欄(下面)“快進”,“快退”均為5s

6:音量調節

7:選單欄“檔案”下可記錄最近播放的三個檔案

8:在有記錄的情況下,初始狀態時雙擊視訊播放區或單擊“播放”播放上次關閉時播放的視訊

需準備的東西:

VLC的動態連結庫*.dll

1,在網上直接下載VLC播放器安裝http://www.videolan.org點選開啟連結

/

2,安裝好後在VLC安裝目錄下libvlc.dll,libvlccore.dll及plugins目錄下的所有檔案拷貝到C#的debug目錄下(下左圖VLC安裝目錄,右圖C#專案debug目錄)

若是release 也需放到release目錄


C#程式設計

(1)建立一個C#的winform專案

(2)c#封裝libvlc的API介面

新建一個類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;

namespace WindowsFormsApplication1
{
    class VlcPlayer
    {
        private IntPtr libvlc_instance_;
        private IntPtr libvlc_media_player_;
        private double duration_;
        public VlcPlayer(string pluginPath)
        {
            string plugin_arg = "--plugin-path=" + pluginPath;
            string[] arguments = { "-I", "dummy", "--ignore-config", "--no-video-title", plugin_arg };
            libvlc_instance_ = LibVlcAPI.libvlc_new(arguments);

            libvlc_media_player_ = LibVlcAPI.libvlc_media_player_new(libvlc_instance_);
        }
        public void SetRenderWindow(int wndHandle)
        {
            if (libvlc_instance_ != IntPtr.Zero && wndHandle != 0)
            {
                LibVlcAPI.libvlc_media_player_set_hwnd(libvlc_media_player_, wndHandle);
            }
        }
        public void PlayFile(string filePath)
        {
            IntPtr libvlc_media = LibVlcAPI.libvlc_media_new_path(libvlc_instance_, filePath);
            if (libvlc_media != IntPtr.Zero)
            {
                LibVlcAPI.libvlc_media_parse(libvlc_media);
                duration_ = LibVlcAPI.libvlc_media_get_duration(libvlc_media) / 1000.0;
                LibVlcAPI.libvlc_media_player_set_media(libvlc_media_player_, libvlc_media);
                LibVlcAPI.libvlc_media_release(libvlc_media);
                LibVlcAPI.libvlc_media_player_play(libvlc_media_player_);
            }
        }
        public void Pause()
        {
            if (libvlc_media_player_ != IntPtr.Zero)
            {
                LibVlcAPI.libvlc_media_player_pause(libvlc_media_player_);
            }
        }
        public void Play()
        {
            if (libvlc_media_player_ != IntPtr.Zero)
            {
                LibVlcAPI.libvlc_media_player_play(libvlc_media_player_);
              //  LibVlcAPI.libvlc_media_player_pause(libvlc_media_player_);
            }
        }
        public void Stop()
        {
            if (libvlc_media_player_ != IntPtr.Zero)
            {
                LibVlcAPI.libvlc_media_player_stop(libvlc_media_player_);
            }
        }
      //  public void FastForward()
       // {
        //    if (libvlc_media_player_ != IntPtr.Zero)
         //   {
          //      LibVlcAPI.libvlc_media_player_fastforward(libvlc_media_player_);
           // }
       // }
        public double GetPlayTime()
        {
            return LibVlcAPI.libvlc_media_player_get_time(libvlc_media_player_) / 1000.0;
        }
        public void SetPlayTime(double seekTime)
        {
            LibVlcAPI.libvlc_media_player_set_time(libvlc_media_player_, (Int64)(seekTime * 1000));
        }
        public int GetVolume()
        {
            return LibVlcAPI.libvlc_audio_get_volume(libvlc_media_player_);
        }
        public void SetVolume(int volume)
        {
            LibVlcAPI.libvlc_audio_set_volume(libvlc_media_player_, volume);
        }
        public void SetFullScreen(bool istrue)
        {
            LibVlcAPI.libvlc_set_fullscreen(libvlc_media_player_, istrue ? 1 : 0);
        }
        public double Duration()
        {
            return duration_;
        }
        public string Version()
        {
            return LibVlcAPI.libvlc_get_version();
        }
    }
    internal static class LibVlcAPI
    {
        internal struct PointerToArrayOfPointerHelper
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
            public IntPtr[] pointers;
        }
        public static IntPtr libvlc_new(string[] arguments)
        {
            PointerToArrayOfPointerHelper argv = new PointerToArrayOfPointerHelper();
            argv.pointers = new IntPtr[11];
            for (int i = 0; i < arguments.Length; i++)
            {
                argv.pointers[i] = Marshal.StringToHGlobalAnsi(arguments[i]);
            }
            IntPtr argvPtr = IntPtr.Zero;
            try
            {
                int size = Marshal.SizeOf(typeof(PointerToArrayOfPointerHelper));
                argvPtr = Marshal.AllocHGlobal(size);
                Marshal.StructureToPtr(argv, argvPtr, false);
                return libvlc_new(arguments.Length, argvPtr);
            }
            finally
            {
                for (int i = 0; i < arguments.Length + 1; i++)
                {
                    if (argv.pointers[i] != IntPtr.Zero)
                    {
                        Marshal.FreeHGlobal(argv.pointers[i]);
                    }
                }
                if (argvPtr != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(argvPtr);
                }
            }
        }
        public static IntPtr libvlc_media_new_path(IntPtr libvlc_instance, string path)
        {
            IntPtr pMrl = IntPtr.Zero;
            try
            {
                byte[] bytes = Encoding.UTF8.GetBytes(path);
                pMrl = Marshal.AllocHGlobal(bytes.Length + 1);
                Marshal.Copy(bytes, 0, pMrl, bytes.Length);
                Marshal.WriteByte(pMrl, bytes.Length, 0);
                return libvlc_media_new_path(libvlc_instance, pMrl);
            }
            finally
            {
                if (pMrl != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pMrl);
                }
            }
        }
        public static IntPtr libvlc_media_new_location(IntPtr libvlc_instance, string path)
        {
            IntPtr pMrl = IntPtr.Zero;
            try
            {
                byte[] bytes = Encoding.UTF8.GetBytes(path);
                pMrl = Marshal.AllocHGlobal(bytes.Length + 1);
                Marshal.Copy(bytes, 0, pMrl, bytes.Length);
                Marshal.WriteByte(pMrl, bytes.Length, 0);
                return libvlc_media_new_path(libvlc_instance, pMrl);
            }
            finally
            {
                if (pMrl != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pMrl);
                }
            }
        }

        // ----------------------------------------------------------------------------------------
        // 以下是libvlc.dll匯出函式

        // 建立一個libvlc例項,它是引用計數的
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_new(int argc, IntPtr argv);

        // 釋放libvlc例項
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_release(IntPtr libvlc_instance);

        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern String libvlc_get_version();

        // 從視訊來源(例如Url)構建一個libvlc_meida
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_media_new_location(IntPtr libvlc_instance, IntPtr path);

        // 從本地檔案路徑構建一個libvlc_media
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_media_new_path(IntPtr libvlc_instance, IntPtr path);

        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_media_release(IntPtr libvlc_media_inst);

        // 建立libvlc_media_player(播放核心)
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern IntPtr libvlc_media_player_new(IntPtr libvlc_instance);

        // 將視訊(libvlc_media)繫結到播放器上
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_media_player_set_media(IntPtr libvlc_media_player, IntPtr libvlc_media);

        // 設定影象輸出的視窗
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_media_player_set_hwnd(IntPtr libvlc_mediaplayer, Int32 drawable);

        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_media_player_play(IntPtr libvlc_mediaplayer);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="libvlc_mediaplayer"></param>
        //[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        //[SuppressUnmanagedCodeSecurity]
       // public static extern void libvlc_media_player_fastforward(IntPtr libvlc_mediaplayer);

        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_media_player_pause(IntPtr libvlc_mediaplayer);

        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_media_player_stop(IntPtr libvlc_mediaplayer);

        // 解析視訊資源的媒體資訊(如時長等)
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_media_parse(IntPtr libvlc_media);

        // 返回視訊的時長(必須先呼叫libvlc_media_parse之後,該函式才會生效)
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern Int64 libvlc_media_get_duration(IntPtr libvlc_media);

        // 當前播放的時間
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern Int64 libvlc_media_player_get_time(IntPtr libvlc_mediaplayer);

        // 設定播放位置(拖動)
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_media_player_set_time(IntPtr libvlc_mediaplayer, Int64 time);

        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_media_player_release(IntPtr libvlc_mediaplayer);

        // 獲取和設定音量
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern int libvlc_audio_get_volume(IntPtr libvlc_media_player);

        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_audio_set_volume(IntPtr libvlc_media_player, int volume);

        // 設定全屏
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
        [SuppressUnmanagedCodeSecurity]
        public static extern void libvlc_set_fullscreen(IntPtr libvlc_media_player, int isFullScreen);

    }
}
(3)窗口布局編輯


(3)選幾個要點說說,其餘參考附件的原始碼

1,播放過程中的視窗右鍵選單問題

注意窗口布局中黑色panel那塊有兩個panel,一個panel1,一個panel2,panel1位於底層,panel2位於表層,panel2背景色設定為transparent,這麼做的原因是panel1在指定成播放後contextMenuStrip屬性失效,為了能在播放後仍能在播放區使用右鍵並且不影響播放,故新增透明的panel2

2,記錄之前開啟的三次檔案

        /// <summary>
        /// 選單欄檔案實現功能
        /// 1 開啟待播放檔案
        /// 2 記錄歷史資訊
        /// 3 歷史資訊最多不超過3條
        /// 4 點選歷史資訊可以實現播放menuitem_Click()
        /// 5 如果點選歷史資訊不能播放(出現錯誤)則刪除該歷史資訊item和Menu.ini (方法:try catch)
        /// 6 Menu.ini記錄的資訊最多不超過3條,不重複記錄
        /// 7 在歷史資訊中右鍵可以選擇刪除
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// 
        #region 選單欄-檔案


        /// <summary>
        /// 開啟ToolStripMenuItem_Click
        /// 開啟檔案並將檔案目錄新增到Menu.ini
        /// 若開啟相同檔案則不新增(這個有Bug,這樣的話按tsBtn_play開啟的就不是上一個了,因為開啟相同的不新增)
        /// 若記錄行數超過3個,則先記錄後三個資料,再重新建一個Menu.ini(清除資料),接著講記錄的後三個資料寫入
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// 
        private void 開啟ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //bool isSame = false;
            openFileDialog1.FileName = "";
            if(this.openFileDialog1.ShowDialog()==DialogResult.OK)
            { 
                //StreamReader sr0 = new StreamReader(address + "\\Menu.ini", true);
               // while (sr0.Peek() > -1)
               // {
              //      if ((sr0.ReadLine() == openFileDialog1.FileName)|(openFileDialog1.FileName==""))
              //      {
              //          isSame = true;
               //     }
              //  }
               // sr0.Close();
               // if (isSame == false)// 若開啟相同檔案則不新增
              //  {
                    StreamWriter s = new StreamWriter(address + "\\Menu.ini", true);
                    s.WriteLine(openFileDialog1.FileName);
                    s.Flush();
                    s.Close();
             //   }
                string[] text = File.ReadAllLines(address + "\\Menu.ini");
                int row = text.Length;//行
                int rowcount;
                string[] tempdata = new string[] {"","",""};
                if (row > 3)// 若記錄行數超過3個,則先記錄後三個資料,再重新建一個Menu.ini(清除資料),接著講記錄的後三個資料寫入
                {
                    StreamReader sr1 = new StreamReader(address + "\\Menu.ini", true);
                    while (sr1.Peek() > -1)
                    {
                        sr1.ReadLine();//空讀,跳過原始的第一個資料,從第二個資料開始讀
                        for (rowcount = 0; rowcount < 3; rowcount++)
                        {
                            tempdata[rowcount] = sr1.ReadLine();
                        }
                    }
                    sr1.Close();
                    FileStream fs = new FileStream(address + "\\Menu.ini", FileMode.Create, FileAccess.Write);
                    fs.Close();
                    StreamWriter s1 = new StreamWriter(address + "\\Menu.ini", true);
                    s1.WriteLine(tempdata[0]);
                    s1.WriteLine(tempdata[1]);
                    s1.WriteLine(tempdata[2]);
                    s1.Flush();
                    s1.Close();
                }
               // StreamReader sr2 = new StreamReader(address + "\\Menu.ini", true);
               // while(sr2.Pee)
                vlcPlayer.PlayFile(openFileDialog1.FileName);
                trackBar1.SetRange(0, (int)vlcPlayer.Duration());
                trackBar1.Value = 0;
                timer1.Start();
                is_playinig = true;
                tSBtn_play.Image = Properties.Resources.暫停;
                tSBtn_play.Text = "暫停";
                media_is_open = true;              
                //label_media_name.Text = openFileDialog1.FileName.Substring(openFileDialog1.FileName.LastIndexOf('\\') + 1, openFileDialog1.FileName.Length - 1 - openFileDialog1.FileName.LastIndexOf('\\'));
                //獲取檔名的另一種方法,不帶字尾
                label_media_name.Text = Path.GetFileNameWithoutExtension(openFileDialog1.FileName);
                label_media_name.Show();
            }
        }
        /// <summary>
        /// 將Menu.ini中的歷史記錄新增到檔案選單欄
        /// </summary>
        private void readFilePath()
        {
            int items_count = this.檔案ToolStripMenuItem.DropDownItems.Count;
            switch (items_count)
            {
                case 4:
                    this.檔案ToolStripMenuItem.DropDownItems.RemoveAt(1);
                    break;
                case 5:
                    this.檔案ToolStripMenuItem.DropDownItems.RemoveAt(1);//移走第一項後原本第二項又成第一項了,所以繼續移走第一項
                    this.檔案ToolStripMenuItem.DropDownItems.RemoveAt(1);
                    break;
                case 6:
                    this.檔案ToolStripMenuItem.DropDownItems.RemoveAt(1);
                    this.檔案ToolStripMenuItem.DropDownItems.RemoveAt(1);
                    this.檔案ToolStripMenuItem.DropDownItems.RemoveAt(1);
                    break;
                default:
                    break;
            }
            
            StreamReader sr = new StreamReader(address + "\\Menu.ini", true);
            int i =1;
            while (sr.Peek() > -1)//peek是用來確定你read的檔案是否結束了,如果結束了會返回int型 -1 
            {
                    ToolStripMenuItem menuitem = new ToolStripMenuItem(sr.ReadLine());
                    this.檔案ToolStripMenuItem.DropDownItems.Insert(i, menuitem);
                    i++;
                    menuitem.Click += new EventHandler(menuitem_Click); 
            }
            sr.Close();
        }
        /// <summary>
        /// 開啟歷史記錄並播放
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuitem_Click(object sender, EventArgs e)
        {
            try
            {
                ToolStripMenuItem menu = (ToolStripMenuItem)sender;
                vlcPlayer.PlayFile(menu.Text);
                trackBar1.SetRange(0, (int)vlcPlayer.Duration());
                trackBar1.Value = 0;
                timer1.Start();
                is_playinig = true;
                tSBtn_play.Image = Properties.Resources.暫停;
                tSBtn_play.Text = "暫停";
                media_is_open = true;
                label_media_name.Text = Path.GetFileNameWithoutExtension(menu.Text);
                label_media_name.Show();
            }
            catch
            {
                MessageBox.Show("檔案不存在", "提示");
            }
        }

        private void 檔案ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            readFilePath();
        }
        #endregion

這段程式碼中這個地方注意:
<span style="white-space:pre">		</span>    this.檔案ToolStripMenuItem.DropDownItems.RemoveAt(1);
                    this.檔案ToolStripMenuItem.DropDownItems.RemoveAt(1);
                    this.檔案ToolStripMenuItem.DropDownItems.RemoveAt(1);
這段原因是每移除選單選項標號1的歷史記錄後,原本的標號2又變成標號1,所以繼續刪除標號1,而不是刪除標號2;

3,在有以前播放記錄情況下開啟軟體直接點選播放時播放上次開啟的音視訊實現程式碼

private void tSBtn_play_Click(object sender, EventArgs e)
        {
            if (is_playinig)
            {
                vlcPlayer.Pause();
                timer1.Stop();
                is_playinig = false;
                tSBtn_play.Image = Properties.Resources.開始;
                tSBtn_play.Text = "播放";
            }
            else
            {
                try
                {
                    string[] text = File.ReadAllLines(address + "\\Menu.ini");
                    string openfilepath="";
                    int row = text.Length;//行
                    StreamReader sr1 = new StreamReader(address + "\\Menu.ini", true);
                    switch (row)
                    {
                        case 1:
                            openfilepath=sr1.ReadLine();
                            break;
                        case 2:
                            sr1.ReadLine();
                            openfilepath = sr1.ReadLine();
                            break;
                        case 3:
                            sr1.ReadLine();
                            sr1.ReadLine();
                            openfilepath = sr1.ReadLine();
                            break;
                        default:
                            break;
                    }
                    if(row==1||row==2||row==3)
                    {
                        
                        if (!media_is_open) 
                        {
                            vlcPlayer.PlayFile(openfilepath);
                        }
                        trackBar1.SetRange(0, (int)vlcPlayer.Duration());
                        vlcPlayer.SetPlayTime(trackBar1.Value);
                        vlcPlayer.Play();
                        trackBar1.Value = (int)vlcPlayer.GetPlayTime();
                       // trackBar1.Value = 0;
                        timer1.Start();
                        is_playinig = true;
                        tSBtn_play.Image = Properties.Resources.暫停;
                        tSBtn_play.Text = "暫停";
                        media_is_open = true;
                        label_media_name.Text = Path.GetFileNameWithoutExtension(openfilepath);
                        label_media_name.Show();
                    }
                    sr1.Close();
                }
                catch (Exception er)
                {
                    MessageBox.Show("檔案不存在", "提示");
                }
            }
        }
4,快進和快退實現方法,以快進為例
private void tSB_forward_Click(object sender, EventArgs e)
        {
            vlcPlayer.Pause();
            int time = (int)vlcPlayer.GetPlayTime() + 5;
            if (time < trackBar1.Maximum)
            {
                vlcPlayer.SetPlayTime(time);
            }
            else
            {
                vlcPlayer.SetPlayTime(trackBar1.Maximum);
            }
            vlcPlayer.Play();
            trackBar1.Value = (int)vlcPlayer.GetPlayTime();
        }
VLC的庫中並沒有快進快退方法(這個我不太確定,我沒發現,若有麻煩告知我下,非常感謝)

所以可以先將視訊暫停,然後在重新設定視訊的播放時間,再播放即可

其餘未說到的地方可以參考原始碼,歡迎跟帖交流

參考帖子:

http://www.cnblogs.com/haibindev/archive/2011/12/21/2296173.html

http://blog.csdn.net/leixiaohua1020/article/details/42363079

http://bbs.csdn.net/topics/390936942

附原始碼
http://download.csdn.net/detail/u012342996/9505082