C# 调用LibVLC库,采用句柄、回调函数两种方式播放视频

本文详细介绍了如何使用C#通过LibVLC库实现视频播放,包括句柄和回调函数两种方式,以及遇到的问题和解决方案,为有同样需求的开发者提供参考。
摘要由CSDN通过智能技术生成

C# 调用LibVLC库,采用句柄、回调函数两种方式播放视频

最近做个项目,需要使用C#实现播放视频,找来找去还是觉得使用LibVLC库来实现比较靠谱,结合网上朋友提供的信息,已经官网的函数描述,终于成功实现了既可以句柄播放,也可以回调函数方式播放,为啥要舍近求远、费劲巴拉的用回调函数来实现播放了?主要是因为句柄方式播放的话,就无法实现一个视频显示两个播放界面(至少我目前还不知道怎么实现,因为传递的参数太多太复杂了,官方的文档也没说怎么用,试了大部分都没法实现,譬如播放过程中如何插入参数);
废话少说吧,简单描述下两种实现的方式:
1、先定义相关的变量:

private MediaPlayer mediaPlayer;
private Media media;
private LibVLC libVLC;
private string[] arguments;

2、然后实例化

arguments = GetArguments();//数组函数
libVLC = new LibVLC(arguments);

 mediaPlayer = new MediaPlayer(libVLC);
 mediaPlayer.SnapshotTaken += SnapshotTakenEvent;  //截图回调函数
 mediaPlayer.TimeChanged += EventTimeChanged;  //  时间改变回调函数

//数组函数如下:
private string[] GetArguments()
{
    return new string[]{
        "-I", "dummy", "--ignore-config",

            //"--no-audio",       //ok
            /*"--no-video",*/   //ok
            //"--grayscale",      //faild     灰度模式(黑白)输出视频  

            "--fullscreen",     //faild     全屏视频输出  
            "--no-embedded-video",  //faild 
    };
}

3、句柄播放

edia = new Media(libVLC, @textBox2.Text/*openDialog.FileName*/, FromType.FromPath);
 //赋值播放的句柄
 mediaPlayer.Hwnd = this.panel1.Handle;
 mediaPlayer.Play(media);

4、回调函数播放

//  先声明下 回调函数时需要的变量:
        private const int _width = 960;
        private const int _height = 720;
        private const int _pixelBytes = 4;
        private const int _pitch = _width * _pixelBytes;
        private IntPtr _buff = IntPtr.Zero;

回调播放
media = new Media(libVLC, @textBox2.Text/*openDialog.FileName*/, FromType.FromPath);
            _buff = Marshal.AllocHGlobal(_pitch * _height);
            

            //回调函数播放时,需要调用的参数,
            mediaPlayer.SetVideoFormat("RV32", _width, _height, _pitch);
            //VideoLockCallBack不能为null,后两个可以为null
            //VideoLockCallBack回调函数获取帧数据,DisplayVideo显示帧数据
            mediaPlayer.SetVideoCallbacks(VideoLockCallBack, null/*VideoUnlockCallBack*/, DisplayVideo);
            callbackCount = 0;
            mediaPlayer.Play(media);

5、Demo的界面
在这里插入图片描述
6、此单元文件的所有源码:

using LibVLCSharp.Shared;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows;

namespace LibVLCDemo
{


    public partial class Form1 : Form
    {
        [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
        private static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);

        private delegate void DelegateShowText(object obj, string text);
        private DelegateShowText mDelegateShowText;

        private MediaPlayer mediaPlayer;
        private Media media;
        private LibVLC libVLC;
        private static string pluginPath = System.Environment.CurrentDirectory;
        private static string plugin_arg = "--plugin-path=" + pluginPath;
        private string[] arguments;

        //视频播放回调时使用
        private Bitmap _bitmap;
        private byte[] _bitmapBuffer;
        /// <summary>
        /// 视频的播放宽度和高度,从配置参数中获取
        /// </summary>
        private int _videoWidth, _videoHeight;
        private Graphics _graphics;


        //锁定一个图片缓冲区
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]//声明对c语言的约束
        public delegate IntPtr VideoLockCB(IntPtr opaque, IntPtr planes);
        //解锁一个图片缓冲区
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void VideoUnlockCB(IntPtr opaque, IntPtr picture, IntPtr planes);
        //显示图片
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void VideoDisplayCB(IntPtr opaque, IntPtr picture);

        private VideoLockCB _videoLockCB;
        private VideoUnlockCB _videoUnlockCB;
        private VideoDisplayCB _videoDisplayCB;

        public Form1() 
        {
            InitializeComponent();
            mDelegateShowText = new DelegateShowText(ShowTextEvent);
        }

        bool obj = false;
        private void Lock()
        {
            obj = true;
        }
        private void Unlock()
        {
            obj = false;
        }
        private bool Islock()
        {
            return obj;
        }

        private DateTime _start;
        private void Form1_Load(object sender, EventArgs e)
        {

            //var libDirectory = new DirectoryInfo(Path.Combine(System.Environment.CurrentDirectory, "libvlc", IntPtr.Size == 4 ? "win-x86" : "win-x64"));

            arguments = GetArguments();
            libVLC = new LibVLC(arguments);

            mediaPlayer = new MediaPlayer(libVLC);
            mediaPlayer.SnapshotTaken += SnapshotTakenEvent;
            mediaPlayer.TimeChanged += EventTimeChanged;

        }

        private const int _width = 960;
        private const int _height = 720;
        private const int _pixelBytes = 4;
        private const int _pitch = _width * _pixelBytes;
        private IntPtr _buff = IntPtr.Zero;

        private IntPtr VideoLockCallBack(IntPtr opaque, IntPtr planes)
        {

            Lock();
            //_buff = Marshal.AllocHGlobal(_pitch * _height);
            Marshal.WriteIntPtr(planes, _buff);//初始化
            return opaque/*IntPtr.Zero*/;
        }


        private IntPtr LockVideo(IntPtr userData, IntPtr planes)
        {
            Marshal.WriteIntPtr(planes, userData);
            return userData;
        }

        private void VideoUnlockCallBack(IntPtr opaque, IntPtr picture, IntPtr planes)
        {
            Marshal.FreeHGlobal(_buff);//释放缓冲区
            Unlock();
        }

        private void DisplayVideo(IntPtr userData, IntPtr picture)
        {
            //及时处于暂停状态,回调函数依然一直在回调
            //暂停的话,就不要刷新图片了
            if (mediaPlayer.State == VLCState.Paused)
                return;

            if (Islock())
            {
                //if (DateTime.Now.Subtract(_start).TotalSeconds > 2)
                {
                    using (Bitmap bmp = new Bitmap(_width, _height, PixelFormat.Format32bppPArgb))
                    {
                        Rectangle rect = new Rectangle(0, 0, _width, _height);
                        BitmapData bp = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
                        CopyMemory(bp.Scan0, _buff, (uint)(_height * _pitch));
                        bmp.UnlockBits(bp);
                        //bmp.Save("d:\\vlc.bmp");
                        Bitmap bitmap = (Bitmap)bmp.Clone();
                        //if (this.pictureBox1.Image != null)
                        //    this.pictureBox1.Image.Dispose();
                        this.pictureBox1.Image = bitmap;
                        callbackCount++;
                        ShowText(label7, "回调计数:" + callbackCount.ToString());
                    }
                }
            }
        }


        private void ShowText(object obj, string text)
        {
            if (obj is Label)
                ((Label)obj).Invoke(mDelegateShowText, obj, text);
            if (obj is Button)
                ((Button)obj).Invoke(mDelegateShowText, obj, text);
            if (obj is TextBox)
                ((TextBox)obj).Invoke(mDelegateShowText, obj, text);
            else if (obj is RichTextBox)
                ((RichTextBox)obj).Invoke(mDelegateShowText, obj, text);
        }

        private void ShowTextEvent(object obj, string text)
        {
            if (obj is Label)
                ((Label)obj).Text = text;
            if (obj is Button)
                ((Button)obj).Text = text;
            if (obj is TextBox)
                ((TextBox)obj).Text = text;
            else if (obj is RichTextBox)
                ((RichTextBox)obj).Text = text;
        }

        private string[] GetArguments()
        {
            return new string[]{
                "-I", "dummy", "--ignore-config",

                    //"--no-audio",       //ok
                    /*"--no-video",*/   //ok
                    //"--grayscale",      //faild     灰度模式(黑白)输出视频  

                    "--fullscreen",     //faild     全屏视频输出  
                    "--no-embedded-video",  //faild 

                    //"--no-snapshot-preview",     //ok   禁用截图预览 
                    //"--snapshot-prefix=qianzui",    //ok    视频截图文件前缀  
                    //"--snapshot-format=jpg",       //faild  视频截图格式  --snapshot-format={png,jpg,tiff} faild
                    //"--snapshot-sequential",        //ok    使用顺序编号代替时间戳 
                    //"--snapshot-width=125",      //faild      截图宽度

                    //**************    前提条件:视频不能嵌入到界面
                    //"--video-title=videotitle",       //OK  前提条件:视频不能嵌入到界面,注:标题如果是汉字,显示乱码
                    //"--width=800",               //OK     视频宽度      前提条件:视频不能嵌入到界面 即:mediaPlayer.Hwnd 不能赋值
                    //"--height=300",              //OK     视频高度      前提条件:视频不能嵌入到界面
                    //"--no-autoscale",            //ok     禁止视频自动缩放  
                    //****************


                    //"--monitor-par=4:3",       //ok     显示器像素宽高比  
                    //"--align=1",               //ok     视频对齐  强制视频在其窗口中对齐的位置 --align={0 (居中), 1 (左), 2 (右), 4 (上), 8 (下), 5 (左上), 6 (右上), 9 (左下), 10 (右下)} 
                    //"--sub-file=xian",          //faild 使用字幕文件 载入该字幕文件。在自动检测未能检测到字幕文件时使用。
                    //"--sub-margin=100,200",     //faild <整数>          强制字幕位置
                    //"--rate=1.0",                 //ok      定义回放的速度 (额定速度为 1.0)。  可以在播放过程中,通过mediaPlayer.SetRate函数设置

                    //"-R",
                    "--repeat",                 //faild     循环播放当前的项目
                    //"--drawable-hwnd=" + panel2.Handle,     //faild 嵌入窗口视频 (drawable)   视频将嵌入在指定的现有窗口。如果为零,则将创建新窗口。
                    "--mirror-split=0",         //faild     定义镜像切割的方向。 可以是垂直或水平   {0 (垂直), 1 (水平)} 
                    "--croppadd-croptop=100",   //faild     视频裁剪滤镜 (croppadd)  从图像顶部开始裁剪的像素数量。 从顶部开始裁剪的像素
                    "--croppadd-cropleft=200",  //faild     从图像顶部开始裁剪的像素数量。 从左侧开始裁剪的像素
                    "--canvas-width=120",       //faild     画布视频滤镜 (canvas) 输出 (画布) 图像宽度
                    
                    "--marq-marquee=showtext",  //faild     滚动文字显示 (marq)  在视频上面显示文本
                    "--marq-x=500",             //faild     从屏幕左边缘开始的 X 偏移。
                    "--marq-y=40",              //faild     从屏幕顶部向下的 Y 偏移。
                    "--marq-position=6",        //faild     强制指定滚动文字在视频上的位置,marq-position={0 (居中), 1 (左), 2 (右), 4 (上), 8 (下), 5 (左上), 6 (右上), 9 (左下), 10 (右下)}
                                                //              0=居中、1=左、2=右、4=上、8=下,您也可以组合使用这些值,例如 6=4+2 表示右上
                    plugin_arg };       
        }

        private void SnapshotTakenEvent(object sender, MediaPlayerSnapshotTakenEventArgs e)
        {
            //截图的时候,响应的事件
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //OpenFileDialog openDialog = new OpenFileDialog();
            //openDialog.FileName = "d:\test.mpg";
            //if(openDialog.ShowDialog() == DialogResult.OK)
            {
                media = new Media(libVLC, @textBox2.Text/*openDialog.FileName*/, FromType.FromPath);
                //赋值播放的句柄
                mediaPlayer.Hwnd = this.panel1.Handle;
                mediaPlayer.Play(media);
            }
        }

        float totalTime = -1;
        float currentTime = 0;
        int totalFrame = -1;
        private void EventTimeChanged(object sender, MediaPlayerTimeChangedEventArgs e)
        { 
            if(mediaPlayer.State == VLCState.Playing ||
                mediaPlayer.State == VLCState.Paused ||
                mediaPlayer.State == VLCState.Ended)
            {
                //总时长
                if (totalTime == -1)
                {
                    totalTime = (float)mediaPlayer.Length / 1000;
                    ShowText(label1, "总时长:" + totalTime.ToString());
                    totalFrame = (int)(totalTime * 25);
                    ShowText(label3,"总帧数:" + totalFrame);    //视频为25帧/秒
                }
                if (e.Time >= 0)
                {
                    currentTime = (float)e.Time / 1000;
                    ShowText(label2, "当前时长:" + currentTime.ToString());
                    ShowText(label4, "当前帧:" + (int)(currentTime * 25));   
                }
            }
        }

      
        private void button2_Click(object sender, EventArgs e)
        {
            //句柄播放的时候,调用的截图函数
            //如果回调函数截图,则可以直接在回调函数里面保存图片,
            //第二个参数,可以直接传入保存的完整图片路径及名称,也可以只传入路径@"d:"
            mediaPlayer.TakeSnapshot(0, @"d:\capture.png", 0, 0);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //mediaPlayer.Pause();
            //如果处于暂停状态的时候,跳转,虽然已经成功了,但是因为处于暂停状态,
            //显示的画面还是之前的,继续开始play的话,才刷新到正确的画面
            try
            {
                float rec = mediaPlayer.Position = float.Parse(textBox1.Text) / (float)totalFrame;
                if(rec == -1)
                {
                    ShowText(richTextBox1, "SetPosition失败");
                }
            }
            finally
            {

            }
            //mediaPlayer.Play();
        }

        private int callbackCount = 0;
        private void button5_Click(object sender, EventArgs e)
        {
            _start = DateTime.Now;
            media = new Media(libVLC, @textBox2.Text/*openDialog.FileName*/, FromType.FromPath);
            _buff = Marshal.AllocHGlobal(_pitch * _height);
            

            //回调函数播放时,需要调用的参数,
            mediaPlayer.SetVideoFormat("RV32", _width, _height, _pitch);
            //VideoLockCallBack不能为null,后两个可以为null
            //VideoLockCallBack回调函数获取帧数据,DisplayVideo显示帧数据
            mediaPlayer.SetVideoCallbacks(VideoLockCallBack, null/*VideoUnlockCallBack*/, DisplayVideo);
            callbackCount = 0;
            mediaPlayer.Play(media);
        }

        private void button4_Click(object sender, EventArgs e)
        {
            if (mediaPlayer.State == VLCState.Playing)
            {
                mediaPlayer.Pause();
                ShowText(button4, "Continue");
            }
            else if (mediaPlayer.State == VLCState.Paused)
            {
                mediaPlayer.Play();
                ShowText(button4, "Pause");
            }
        }

        private void button6_Click(object sender, EventArgs e)
        {
            if(mediaPlayer.State == VLCState.Playing || mediaPlayer.State == VLCState.Paused)
            mediaPlayer.Stop();
        }

       
    }
}

里面有当时写的测试信息,可直接忽略。

7、nuget安装的包:
在这里插入图片描述

8、整个Demo的源码地址:
https://download.csdn.net/download/njlcfyh/88691747

写此文章,主要是记录下这段时间,对于我一个小白来说,查找C#实现视频播放的辛苦,也同时提供给像我这样如此需求的小白一个参考,大海捞针确实太辛苦了,你好我好大家好。
**

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值