C#编写的基于VLC的播放器

时间:2022-08-18 15:54:28

首先看一下最终的程序效果

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#编写的基于VLC的播放器C#编写的基于VLC的播放器


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)窗口布局编辑

C#编写的基于VLC的播放器

(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