一、概述

单位有个项目,程序的主界面就是一张背景图,在键盘上点击不同按键后弹出对应的视频,视频是全屏的,所以没有播放,暂停、前进、后退、进度条等任何控件。开始我用window的Media Player COM组件开发,功能全部实现了,但是有很多视频文件Media Player因为不能解码而播放不了,所以考虑使用VLC,它能支持很数视频格式。刚开始我使用的是Vlc.DotNet Form,但随后发现它的全屏属性似乎不起作用,只能依赖于窗口,对其拉伸实现全屏,这里的全屏并非真正意义上的全屏,因为窗口标题栏还显示着,需要对标题栏进行隐藏。于是又转向了LibVlc API,网上有好多C#使用LibVlc API编写播放器介绍,比如博主”清风明月我“的文章《C#利用VLC播放视频》。我利用LibVlc API实现了全屏,没有对libvlc_media_player_set_hwnd设置视频输出窗口,由Vlc自己生成一个独立窗口,其标题栏名称叫VLC (Direct3D11 output),待视频播放完毕后让它自动关闭,这时就需要用到libvlc事件机制了。经过在网上搜索,没有找到C#使用libvlc的事件,有很多是利用C++来实现的,所以下面来介绍如何使用C#语言注册(订阅)VLC的事件。

二、LIBVLC事件简要说明

libvlc异步事件,可以参看https://videolan.videolan.me/vlc/group__libvlc__event.html。我们用到是它定义的四种结构体(其中第四个是回调)libvlc_event_manager_t,libvlc_event_type_t,libvlc_event_t , void(* libvlc_callback_t) (const struct libvlc_event_t *p_event, void *p_data)
还有订阅事件的函数int libvlc_event_attach (libvlc_event_manager_t *p_event_manager, libvlc_event_type_t i_event_type, libvlc_callback_t f_callback, void *user_data)
至于它的取消订阅函数 void libvlc_event_detach (libvlc_event_manager_t *p_event_manager, libvlc_event_type_t i_event_type, libvlc_callback_t f_callback, void *p_user_data)我没有使用,如果大家需要可以自行实现一下。
我们订阅事件用的是libvlc_event_attach()函数,它的参数决定了我们要用到上面说的四种结构体。第一个参数类型是libvlc_event_manager_t,它叫事件管理器,针对不同的libvlc对象,如libvlc_media_t和 libvlc_media_player_t,它们都有一个相应的libvlc_event_manager_t事件管理器 libvlc_event_manager_t * libvlc_media_event_manager ( libvlc_media_t * p_md )libvlc_event_manager_t * libvlc_media_player_event_manager ( libvlc_media_player_t * p_mi )
第二个参数是libvlc_event_type_t类型,它是枚举类型,它定义了视频打开(libvlc_MediaPlayerOpening )、播放中(libvlc_MediaPlayerPlaying )、暂停(libvlc_MediaPlayerPaused )、结束(libvlc_MediaPlayerStopping)、停止(libvlc_MediaPlayerStopped)等57种事件类型。
第三个参数libvlc_callback_t是回调结构体,它又有两个参数,其中第一个参数libvlc_event_t也是一个结构体,包含 int type,void *p_obj两个类型元素和一个union连合体,type就是回调时发生的事件类型,即上面说的枚举的值,就是说返回了视频播放触发的事件,这个type是我们真正需要的东西。第二个参数p_obj是我们订阅事件函数libvlc_event_attach()的第四参数传入的数据,它是我们用户自己需要用的一些数据,当然也可以不用。这个回调结构体我可能也解释的有点绕了,我也是仔细阅读了文档,才慢慢理解的,不过没关系,如果大家需要理解它,可以打开https://videolan.videolan.me/vlc/group__libvlc__event.html文档慢慢研究,同时参照下面我写的代码,对它的意思也就理解了。
第四个参数void *user_data,是咱们自己自定义数据,可以在回调函数中处理后再传回来,这里我没有使用它。

三、C#的LIBVLC事件代码

上面我们对libvlc的事件作了简要说明,大家可以参照原C++文档仔细阅读理解,也可以不用去了解那么细,只需要知道libvlc订阅(或叫注册)事件的机理就行,也可以把下面的C#代码理解一下也行,或者直接照搬,能用就行。

(一)、参数的语句

针对上面的四种结构体和订阅函数,我们把C++代码转换为C#代码。
首先我们看C++的订阅函数

                  libvlc_event_attach( libvlc_event_manager_t *  p_event_manager,libvlc_event_type_t i_event_type,libvlc_callback_t   f_callback,void* user_data)

1、第一个参数,得到media_player的事件管理器

//libvlc_media_player事件
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, CharSet = CharSet.Ansi)]
public static extern IntPtr libvlc_media_player_event_manager(IntPtr libvlc_media_player);

2、第二个参数,是要订阅的事件,根据自己实际需要订阅,比如想得到视频暂停时的事件,就注册一个暂停时的枚举值261即可(libvlc_MediaPlayerPaused),也可以多订阅几个,下面我把所有事件类型都列出来,如果要全部订阅,采用枚举遍历(枚举遍历方法可以看我另一篇”C# enum枚举类型的遍历“)即可。

            #region LIBVLC_EVENT_E  事件类型 ver3.0.19/*** Event types*/public enum LIBVLC_EVENT_E{/* Append new event types at the end of a category.* Do not remove, insert or re-order any entry.* Keep this in sync with lib/event.c:libvlc_event_type_name(). */[Description("媒体元数据已更改")]libvlc_MediaMetaChanged = 0,libvlc_MediaSubItemAdded,libvlc_MediaDurationChanged,libvlc_MediaParsedChanged,libvlc_MediaFreed,libvlc_MediaStateChanged,libvlc_MediaSubItemTreeAdded,[Description("播放器媒体已更改")]libvlc_MediaPlayerMediaChanged = 0x100,//256[Description("播放准备状态")]libvlc_MediaPlayerNothingSpecial,//257[Description("播放器打开")]libvlc_MediaPlayerOpening,//258[Description("播放器缓冲")]libvlc_MediaPlayerBuffering,//259[Description("播放器播放中")]libvlc_MediaPlayerPlaying,//260[Description("播放器已暂停")]libvlc_MediaPlayerPaused,//261[Description("播放停止")]libvlc_MediaPlayerStopped,//262[Description("播放器前进")]libvlc_MediaPlayerForward,//263[Description("播放器后退")]libvlc_MediaPlayerBackward,//264[Description("播放结束")]libvlc_MediaPlayerEndReached,//265[Description("播放器遇到错误")]libvlc_MediaPlayerEncounteredError,//266libvlc_MediaPlayerTimeChanged,//267libvlc_MediaPlayerPositionChanged,//268libvlc_MediaPlayerSeekableChanged,//269libvlc_MediaPlayerPausableChanged,//270libvlc_MediaPlayerTitleChanged,//271libvlc_MediaPlayerSnapshotTaken,//272libvlc_MediaPlayerLengthChanged,//273libvlc_MediaPlayerVout,//274libvlc_MediaPlayerScrambledChanged,//275libvlc_MediaPlayerESAdded,//276libvlc_MediaPlayerESDeleted,//277libvlc_MediaPlayerESSelected,//278libvlc_MediaPlayerCorked,//279libvlc_MediaPlayerUncorked,//280libvlc_MediaPlayerMuted,//281libvlc_MediaPlayerUnmuted,//282libvlc_MediaPlayerAudioVolume,//283libvlc_MediaPlayerAudioDevice,//284libvlc_MediaPlayerChapterChanged,//285libvlc_MediaListItemAdded = 0x200,libvlc_MediaListWillAddItem,libvlc_MediaListItemDeleted,libvlc_MediaListWillDeleteItem,libvlc_MediaListEndReached,libvlc_MediaListViewItemAdded = 0x300,libvlc_MediaListViewWillAddItem,libvlc_MediaListViewItemDeleted,libvlc_MediaListViewWillDeleteItem,libvlc_MediaListPlayerPlayed = 0x400,libvlc_MediaListPlayerNextItemSet,libvlc_MediaListPlayerStopped,/*** \deprecated Useless event, it will be triggered only when calling* libvlc_media_discoverer_start()*/libvlc_MediaDiscovererStarted = 0x500,/*** \deprecated Useless event, it will be triggered only when calling* libvlc_media_discoverer_stop()*/libvlc_MediaDiscovererEnded,libvlc_RendererDiscovererItemAdded,libvlc_RendererDiscovererItemDeleted,libvlc_VlmMediaAdded = 0x600,libvlc_VlmMediaRemoved,libvlc_VlmMediaChanged,libvlc_VlmMediaInstanceStarted,libvlc_VlmMediaInstanceStopped,libvlc_VlmMediaInstanceStatusInit,libvlc_VlmMediaInstanceStatusOpening,libvlc_VlmMediaInstanceStatusPlaying,libvlc_VlmMediaInstanceStatusPause,libvlc_VlmMediaInstanceStatusEnd,libvlc_VlmMediaInstanceStatusError};#endregion  //LIBVLC_EVENT_E事件类型

3、第三个参数,这是回调函数,在C#中我们用委托定义。在我调试过中程序一直报“尝试读取或写入受保护的内存。这通常指示其他内存已损坏”异常,真是憋屈几天才解决,最终是需要在委托前面加上[UnmanagedFunctionPointer(CallingConvention.Cdecl)]这个属性。

//此语句必加,否则报错“尝试读取或写入受保护的内存。这通常指示其他内存已损坏。”
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void LIB_EVENT_CALLBACK(ref LIBVLC_EVENT_T libvlc_event, IntPtr p_data);

这个回调的第一个参数也是一个结构体,它是内嵌联合体结构的,有两种定义方式,因为我不获取联合体内容,所以用了第一种定义方式。如果要获取联合体内容,可以采用第二种定义方式,只不过要在订阅函数的对应类型要改为IntPtr,用Marshal.PtrToStructure(IntPtr ptr,Type structureType)来获取type,p_obj和联合体内容。其中,联合体中的原C++结构定义我也附上并注释掉了,对获取联合体和理解它的意思有提示作用。

            #region LIBVLC_EVENT_T结构体[StructLayoutAttribute(LayoutKind.Sequential)]public struct LIBVLC_EVENT_T{public int type;public IntPtr p_obj;[StructLayoutAttribute(LayoutKind.Explicit)]public struct u{//struct//{//    libvlc_meta_t meta_type//} media_meta_changed;[FieldOffsetAttribute(0)] public int media_meta_changed_meta_type;//struct//{//  libvlc_media_t* new_child;//} media_subitem_added;[FieldOffsetAttribute(0)] public IntPtr media_subitem_added_new_child;//struct//{//    int64_t new_duration;//} media_duration_changed;[FieldOffsetAttribute(0)] public Int64 media_duration_changed_new_duration;//struct//{//    int new_status; /**< see @ref libvlc_media_parsed_status_t *///} media_parsed_changed;[FieldOffsetAttribute(0)] public int media_parsed_changed_new_status;//struct//{//    int new_state; /**< see @ref libvlc_state_t *///} media_state_changed;[FieldOffsetAttribute(0)] public int media_state_changed_new_state;//struct//{//    libvlc_picture_t* p_thumbnail;//} media_thumbnail_generated;[FieldOffsetAttribute(0)] public IntPtr media_thumbnail_generated_p_thumbnail;//struct//{//    libvlc_media_t* item;//} media_subitemtree_added;[FieldOffsetAttribute(0)] public IntPtr media_subitemtree_added_item;//struct//{//    libvlc_picture_list_t* thumbnails;//} media_attached_thumbnails_found;[FieldOffsetAttribute(0)] public IntPtr media_attached_thumbnails_found_thumbnails;///* media instance *///struct//{//    float new_cache;//} media_player_buffering;[FieldOffsetAttribute(0)] public float media_player_buffering_new_cache;//struct//{//    int new_chapter;//} media_player_chapter_changed;[FieldOffsetAttribute(0)] public int media_player_chapter_changed_new_chapter;//struct//{//    double new_position;//} media_player_position_changed;[FieldOffsetAttribute(0)] public double media_player_position_changed_new_position;//struct//{//    libvlc_time_t new_time;//} media_player_time_changed;[FieldOffsetAttribute(0)] public Int64 media_player_time_changed_new_time;//struct//{//    const libvlc_title_description_t* title;//    int index;//} media_player_title_selection_changed;[FieldOffsetAttribute(0)] public IntPtr media_player_title_selection_changed_title;[FieldOffsetAttribute(4)] public int media_player_title_selection_changed_index;//struct//{//    int new_seekable;//} media_player_seekable_changed;[FieldOffsetAttribute(0)] public int media_player_seekable_changed_new_seekable;//struct//{//    int new_pausable;//} media_player_pausable_changed;[FieldOffsetAttribute(0)] public int media_player_pausable_changed_new_pausable;//struct//{//    int new_scrambled;//} media_player_scrambled_changed;[FieldOffsetAttribute(0)] public int media_player_scrambled_changed_new_scrambled;//struct//{//    int new_count;//} media_player_vout;[FieldOffsetAttribute(0)] public int media_player_vout_new_count;///* media list *///struct//{//    libvlc_media_t* item;//    int index;//} media_list_item_added;[FieldOffsetAttribute(0)] public IntPtr list_item_added_item;[FieldOffsetAttribute(4)] public int list_item_added_index;//struct//{//    libvlc_media_t* item;//    int index;//} media_list_will_add_item;[FieldOffsetAttribute(0)] public IntPtr list_will_add_item_item;[FieldOffsetAttribute(4)] public int list_will_add_item_index;//struct//{//    libvlc_media_t* item;//    int index;//} media_list_item_deleted;[FieldOffsetAttribute(0)] public IntPtr media_list_item_deleted_item;[FieldOffsetAttribute(4)] public int media_list_item_deleted_index;//struct//{//    libvlc_media_t* item;//    int index;//} media_list_will_delete_item;[FieldOffsetAttribute(0)] public IntPtr media_list_will_delete_item_item;[FieldOffsetAttribute(4)] public int media_list_will_delete_item_index;///* media list player *///struct//{//    libvlc_media_t* item;//} media_list_player_next_item_set;[FieldOffsetAttribute(0)] public IntPtr media_list_player_next_item_set_item;///* snapshot taken *///struct//{//     char* psz_filename;//} media_player_snapshot_taken;[FieldOffsetAttribute(0)] public IntPtr media_player_snapshot_taken_psz_filename;///* Length changed *///struct//{//    libvlc_time_t new_length;//} media_player_length_changed;[FieldOffsetAttribute(0)] public Int64 media_player_length_changed_new_length;///* Extra MediaPlayer *///struct//{//    libvlc_media_t* new_media;//} media_player_media_changed;[FieldOffsetAttribute(0)] public IntPtr media_player_media_changed_new_media;///* ESAdded, ESDeleted, ESUpdated *///struct//{//    libvlc_track_type_t i_type;//    int i_id; /**< Deprecated, use psz_id *///    /** Call libvlc_media_player_get_track_from_id() to get the track//     * description. *///    const char* psz_id;//} media_player_es_changed;[FieldOffsetAttribute(0)] public int media_player_es_changed_i_type;[FieldOffsetAttribute(4)] public int media_player_es_changed_i_id;[FieldOffsetAttribute(8)] public IntPtr media_player_es_changed_psz_id;///* ESSelected *///struct//{//    libvlc_track_type_t i_type;//    const char* psz_unselected_id;//    const char* psz_selected_id;//} media_player_es_selection_changed;[FieldOffsetAttribute(0)] public int media_player_es_selection_changed_i_type;[FieldOffsetAttribute(4)] public IntPtr media_player_es_selection_changed_psz_unselected_id;[FieldOffsetAttribute(8)] public IntPtr media_player_es_selection_changed_psz_selected_id;///* ProgramAdded, ProgramDeleted, ProgramUpdated *///struct//{//    int i_id;//} media_player_program_changed;[FieldOffsetAttribute(0)] public int media_player_program_changed_i_id;///* ProgramSelected *///struct//{//    int i_unselected_id;//    int i_selected_id;//} media_player_program_selection_changed;[FieldOffsetAttribute(0)] public int media_player_program_selection_changed_i_unselected_id;[FieldOffsetAttribute(4)] public int media_player_program_selection_changed_i_selected_id;//struct//{//    float volume;//} media_player_audio_volume;[FieldOffsetAttribute(0)] public float media_player_audio_volume_volume;//struct//{//    const char* device;//} media_player_audio_device;[FieldOffsetAttribute(0)] public IntPtr media_player_audio_device_device;//struct//{//    bool recording;//    /** Only valid when recording ends (recording == false) *///    const char* recorded_file_path;//} media_player_record_changed;[FieldOffsetAttribute(0)] public bool media_player_record_changed_recording;[FieldOffsetAttribute(4)] public IntPtr media_player_record_changed_recorded_file_path;//struct//{//    libvlc_renderer_item_t* item;//} renderer_discoverer_item_added;[FieldOffsetAttribute(0)] public IntPtr renderer_discoverer_item_added_item;//struct//{//    libvlc_renderer_item_t* item;//} renderer_discoverer_item_deleted;[FieldOffsetAttribute(0)] public IntPtr renderer_discoverer_item_deleted_item;//**********}}//[StructLayoutAttribute(LayoutKind.Explicit)]//public struct LIBVLC_EVENT_T//{//    [FieldOffsetAttribute(0)] public int type;//    [FieldOffsetAttribute(4)] public IntPtr p_obj;//    //struct//    //{//    //    libvlc_meta_t meta_type//    //} media_meta_changed;//    [FieldOffsetAttribute(8)] public int media_meta_changed_meta_type;//    //struct//    //{//    //  libvlc_media_t* new_child;//    //} media_subitem_added;//    [FieldOffsetAttribute(8)] public IntPtr media_subitem_added_new_child;//    //struct//    //{//    //    int64_t new_duration;//    //} media_duration_changed;//    [FieldOffsetAttribute(8)] public Int64 media_duration_changed_new_duration;//    //struct//    //{//    //    int new_status; /**< see @ref libvlc_media_parsed_status_t *///    //} media_parsed_changed;//    [FieldOffsetAttribute(8)] public int media_parsed_changed_new_status;//    //struct//    //{//    //    int new_state; /**< see @ref libvlc_state_t *///    //} media_state_changed;//    [FieldOffsetAttribute(8)] public int media_state_changed_new_state;//    //struct//    //{//    //    libvlc_picture_t* p_thumbnail;//    //} media_thumbnail_generated;//    [FieldOffsetAttribute(8)] public IntPtr media_thumbnail_generated_p_thumbnail;//    //struct//    //{//    //    libvlc_media_t* item;//    //} media_subitemtree_added;//    [FieldOffsetAttribute(8)] public IntPtr media_subitemtree_added_item;//    //struct//    //{//    //    libvlc_picture_list_t* thumbnails;//    //} media_attached_thumbnails_found;//    [FieldOffsetAttribute(8)] public IntPtr media_attached_thumbnails_found_thumbnails;//    ///* media instance *///    //struct//    //{//    //    float new_cache;//    //} media_player_buffering;//    [FieldOffsetAttribute(8)] public float media_player_buffering_new_cache;//    //struct//    //{//    //    int new_chapter;//    //} media_player_chapter_changed;//    [FieldOffsetAttribute(8)] public int media_player_chapter_changed_new_chapter;//    //struct//    //{//    //    double new_position;//    //} media_player_position_changed;//    [FieldOffsetAttribute(8)] public double media_player_position_changed_new_position;//    //struct//    //{//    //    libvlc_time_t new_time;//    //} media_player_time_changed;//    [FieldOffsetAttribute(8)] public Int64 media_player_time_changed_new_time;//    //struct//    //{//    //    const libvlc_title_description_t* title;//    //    int index;//    //} media_player_title_selection_changed;//    [FieldOffsetAttribute(8)] public IntPtr media_player_title_selection_changed_title;//    [FieldOffsetAttribute(12)] public int media_player_title_selection_changed_index;//    //struct//    //{//    //    int new_seekable;//    //} media_player_seekable_changed;//    [FieldOffsetAttribute(8)] public int media_player_seekable_changed_new_seekable;//    //struct//    //{//    //    int new_pausable;//    //} media_player_pausable_changed;//    [FieldOffsetAttribute(8)] public int media_player_pausable_changed_new_pausable;//    //struct//    //{//    //    int new_scrambled;//    //} media_player_scrambled_changed;//    [FieldOffsetAttribute(8)] public int media_player_scrambled_changed_new_scrambled;//    //struct//    //{//    //    int new_count;//    //} media_player_vout;//    [FieldOffsetAttribute(8)] public int media_player_vout_new_count;//    ///* media list *///    //struct//    //{//    //    libvlc_media_t* item;//    //    int index;//    //} media_list_item_added;//    [FieldOffsetAttribute(8)] public IntPtr list_item_added_item;//    [FieldOffsetAttribute(12)] public int list_item_added_index;//    //struct//    //{//    //    libvlc_media_t* item;//    //    int index;//    //} media_list_will_add_item;//    [FieldOffsetAttribute(8)] public IntPtr list_will_add_item_item;//    [FieldOffsetAttribute(12)] public int list_will_add_item_index;//    //struct//    //{//    //    libvlc_media_t* item;//    //    int index;//    //} media_list_item_deleted;//    [FieldOffsetAttribute(8)] public IntPtr media_list_item_deleted_item;//    [FieldOffsetAttribute(12)] public int media_list_item_deleted_index;//    //struct//    //{//    //    libvlc_media_t* item;//    //    int index;//    //} media_list_will_delete_item;//    [FieldOffsetAttribute(8)] public IntPtr media_list_will_delete_item_item;//    [FieldOffsetAttribute(12)] public int media_list_will_delete_item_index;//    ///* media list player *///    //struct//    //{//    //    libvlc_media_t* item;//    //} media_list_player_next_item_set;//    [FieldOffsetAttribute(8)] public IntPtr media_list_player_next_item_set_item;//    ///* snapshot taken *///    //struct//    //{//    //     char* psz_filename;//    //} media_player_snapshot_taken;//    [FieldOffsetAttribute(8)] public IntPtr media_player_snapshot_taken_psz_filename;//    ///* Length changed *///    //struct//    //{//    //    libvlc_time_t new_length;//    //} media_player_length_changed;//    [FieldOffsetAttribute(8)] public Int64 media_player_length_changed_new_length;//    ///* Extra MediaPlayer *///    //struct//    //{//    //    libvlc_media_t* new_media;//    //} media_player_media_changed;//    [FieldOffsetAttribute(8)] public IntPtr media_player_media_changed_new_media;//    ///* ESAdded, ESDeleted, ESUpdated *///    //struct//    //{//    //    libvlc_track_type_t i_type;//    //    int i_id; /**< Deprecated, use psz_id *///    //    /** Call libvlc_media_player_get_track_from_id() to get the track//    //     * description. *///    //    const char* psz_id;//    //} media_player_es_changed;//    [FieldOffsetAttribute(8)] public int media_player_es_changed_i_type;//    [FieldOffsetAttribute(12)] public int media_player_es_changed_i_id;//    [FieldOffsetAttribute(16)] public IntPtr media_player_es_changed_psz_id;//    ///* ESSelected *///    //struct//    //{//    //    libvlc_track_type_t i_type;//    //    const char* psz_unselected_id;//    //    const char* psz_selected_id;//    //} media_player_es_selection_changed;//    [FieldOffsetAttribute(8)] public int media_player_es_selection_changed_i_type;//    [FieldOffsetAttribute(12)] public IntPtr media_player_es_selection_changed_psz_unselected_id;//    [FieldOffsetAttribute(16)] public IntPtr media_player_es_selection_changed_psz_selected_id;//    ///* ProgramAdded, ProgramDeleted, ProgramUpdated *///    //struct//    //{//    //    int i_id;//    //} media_player_program_changed;//    [FieldOffsetAttribute(8)] public int media_player_program_changed_i_id;//    ///* ProgramSelected *///    //struct//    //{//    //    int i_unselected_id;//    //    int i_selected_id;//    //} media_player_program_selection_changed;//    [FieldOffsetAttribute(8)] public int media_player_program_selection_changed_i_unselected_id;//    [FieldOffsetAttribute(12)] public int media_player_program_selection_changed_i_selected_id;//    //struct//    //{//    //    float volume;//    //} media_player_audio_volume;//    [FieldOffsetAttribute(8)] public float media_player_audio_volume_volume;//    //struct//    //{//    //    const char* device;//    //} media_player_audio_device;//    [FieldOffsetAttribute(8)] public IntPtr media_player_audio_device_device;//    //struct//    //{//    //    bool recording;//    //    /** Only valid when recording ends (recording == false) *///    //    const char* recorded_file_path;//    //} media_player_record_changed;//    [FieldOffsetAttribute(8)] public bool media_player_record_changed_recording;//    [FieldOffsetAttribute(12)] public IntPtr media_player_record_changed_recorded_file_path;//    //struct//    //{//    //    libvlc_renderer_item_t* item;//    //} renderer_discoverer_item_added;//    [FieldOffsetAttribute(8)] public IntPtr renderer_discoverer_item_added_item;//    //struct//    //{//    //    libvlc_renderer_item_t* item;//    //} renderer_discoverer_item_deleted;//    [FieldOffsetAttribute(8)] public IntPtr renderer_discoverer_item_deleted_item;//}#endregion //LIBVLC_EVENT_T

4、第四个参数,是用户需要传递的数据,我这儿用不上,所以设置为空。

(二)、订阅函数的语句

根据原C++订阅函数,我们转换为C#函数

[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern int libvlc_event_attach(IntPtr libvlc_event_manager, int libvlc_event_type, LIB_EVENT_CALLBACK libvlc_callback, IntPtr user_data);

(三)、调用方法

在编写LibVlc API程序时(参见"清风明月我“的文章《C#利用VLC播放视频》),我们肯定是得到了libvlc_media_player,有了它就能订阅事件。

            //订阅事件函数public static void libvlc_media_player_events(IntPtr libvlc_media_player){//1、获取到事件订阅函数libvlc_event_attach()的第一个参数IntPtr mp_e_mananger = libvlc_media_player_event_manager(libvlc_media_player);//2、声明回调函数LIB_EVENT_CALLBACK handlsEvents = new LIB_EVENT_CALLBACK(libvlcCallBACK);//3、注册全部事件foreach (LIBVLC_EVENT_E vlc_e in Enum.GetValues(typeof(LIBVLC_EVENT_E)))//{int regInt = libvlc_event_attach(mp_e_mananger, vlc_e.GetHashCode(), handlsEvents, IntPtr.Zero);if (regInt != 0)break;}}//回调函数public static void libvlcCallBACK(ref LIBVLC_EVENT_T evt, IntPtr userdata){//只列出了部分事件回调,可以根据自己需要再增加或删除type值,即LIBVLC_EVENT_E的枚举值switch (evt.type){case 256: //媒体改变//你想要做的事情break;case 257://播放准备状态//你想要做的事情break;case 258://播放器打开//你想要做的事情break;case 259://播放器缓冲//你想要做的事情break;case 260://播放器播放中//你想要做的事情break;case 261://播放器已暂停//你想要做的事情break;case 262://播放停止//这儿是我要用到的,等视频播放结束后,我要关闭由libvlc自己生成的全屏播放窗口//窗口可见就隐藏,因为经过测试是关闭不了它的。//以下是我部分代码,只是起到个例子作用,对大家来说没用可以删除掉,换成自己的代码if (IsWindowVisible(LibVlcHandle)){ShowWindow(LibVlcHandle, 0);}LiveStatus.BeginInvoke(false, null, null);//播放已经停止break;case 263://播放器前进break;case 264://播放器后退break;case 265://播放结束//窗口可见就隐藏,因为经过测试是关闭不了它的if (IsWindowVisible(LibVlcHandle)){ShowWindow(LibVlcHandle, 0);}LiveStatus.BeginInvoke(false, null, null);//播放已经结束break;case 266://播放器遇到错误//你想要做的事情break;default:break;}}

在实际应用中,我关闭由libvlc自己生成的名为VLC (Direct3D11 output)的播放窗口,在获取到该窗口的句柄后,使用Win32的DestroyWindow(LibVlcHandle)和PostMessage(LibVlcHandle, WM_CLOSE, 0, 0)均无法关闭它,所以只能用ShowWindow来显示和隐藏它。

四、小结

本文不是写LibVlc API编写视频的,只是在特殊场景下对用到LibVlc事件的一个描述,虽然文章有些长,但我也是尽力把C#代码写的详细些。如果能为有相同需求的朋友提供帮助,那是最好不过了,同时也是我自己的一篇笔记吧。

C# 编写VLC视频事件处理程序 libvlc libvlc_event_attach libvlc_event_manager libvlc_event_type ibvlc_event_e用法相关推荐

  1. 这就是为什么我们需要在React的类组件中绑定事件处理程序

    by Saurabh Misra 索拉·米斯拉(Saurabh Misra) 这就是为什么我们需要在React的类组件中绑定事件处理程序 (This is why we need to bind ev ...

  2. javaScript事件(二)事件处理程序

    一.事件 二.事件流 以上内容见:javaScript事件(一)事件流 三.事件处理程序 前面提到,事件是用户或浏览器自身执行的某种动作,如click,load和mouseover都是事件的名字.响应 ...

  3. SharePoint Portal Server 2003 SP2对文档库事件处理程序所带来的影响

    当服务器管理员在SharePoint Portal Server 2003门户服务器上安装了SPS2003 SP2后,可能会发现原本工作正常的文档库事件处理程序突然不工作了,如果打开管理中心的事件查看 ...

  4. react中绑定点击事件_在React中绑定事件处理程序的最佳方法

    react中绑定点击事件 by Charlee Li 通过李李 在React中绑定事件处理程序的最佳方法 (The best way to bind event handlers in React) ...

  5. 事件(二):事件处理程序

    click,load,mouseover都是事件的名字,而响应某个事件的函数就叫做事件处理程序(或事件侦听器).事件处理程序的名字以'on'开头,onclick,onload 事件处理程序(html事 ...

  6. TypeScript事件处理程序

    目录 介绍 但首先,React DOM怎么样? 旁白:为什么要Type? 邪恶的东西 在我的类型系统? 结论 介绍 我对TypeScript和React的介绍性演示引发了关于在React和DOM中键入 ...

  7. button添加插槽之后绑定不来事件_javaScript基础事件(二)事件处理程序

    前面提到,事件是用户或浏览器自身执行的某种动作,如click,load和mouseover都是事件的名字.响应某个事件的函数就叫事件处理程序(也叫事件处理函数.事件句柄).事件处理程序的名字以&quo ...

  8. Android编写一个视频监控App

    Android编写一个视频监控App 很久没写app了,小项目需要写一个rtmp拉流的视频监控app,简单记录一下. 参考:Android实现rtmp推拉流摄像头(三)_空空7的博客-CSDN博客_a ...

  9. 后处理程序文件大小的变量_【每日一题】(17题)面试官问:JS中事件流,事件处理程序,事件对象的理解?...

    关注「松宝写代码」,精选好文,每日一题 作者:saucxs | songEagle 2020,实「鼠」不易 2021,「牛」转乾坤 风劲潮涌当扬帆,任重道远须奋蹄! 一.前言 2020.12.23 立 ...

最新文章

  1. NOIP2012普及组 (四年后的)解题报告 -SilverN
  2. 做俯卧撑的10个技巧
  3. 8000字干货 | 教你打造电商产品的用户体系
  4. python框架大全_常用的Python开源框架有哪些?列举这3个
  5. SpringBoot中使用POI导出Excel时怎样循环数据库数据赋值
  6. js实现上传图片及时预览
  7. mysql空洞数据,Mysql 表空间和 数据页空洞
  8. c++ STL 工程实践的15条建议
  9. educoder实训平台java入门_educoder上的实训题目(学习-Java包装类之Byte类)
  10. 【elasticsearch】elasticsearch集群更换节点操作
  11. 近 45 亿元拿下开源服务器 Nginx,F5 买断应用交付未来?
  12. 修改chrome中HTML元素,自由更改网页中的任何内容:Hack this page
  13. linux 切换 amd 显卡,ubuntu 14.10 (64位)下载amd显卡,实现双显卡切换
  14. AI基础:图解Transformer
  15. 太原科技大学调剂计算机,2016年太原科技大学考研调剂信息
  16. SWITCH 和ROUTE的故事(来自Csico论坛)
  17. CCF201509-1数列分段(C语言)
  18. DBSCAN聚类算法原理及图解
  19. WiFi模块的AP模式和STA模式
  20. Nginx_01_Nginx三大基础功能(静态服务器、虚拟主机、负载均衡/服务端代理)

热门文章

  1. Python中的程序控制结构 顺序结构和选择结构
  2. 2022,6G发展确已升温
  3. 软件工程--总体设计过程详解
  4. 高德地图 搜索定位 搜索关键词 定位
  5. html5中图片热点,HTML5 创建热点图
  6. 人机交互如何改变人类生活 | 公开课笔记
  7. 用python按比例调整图片尺寸
  8. idea Failed to clean project Failed to delete target
  9. 【小白向】利用笔记本+网线让台式机上网
  10. 含泪赔了近200万,我终于明白不是什么人都能干电商的……