一、概述
单位有个项目,程序的主界面就是一张背景图,在键盘上点击不同按键后弹出对应的视频,视频是全屏的,所以没有播放,暂停、前进、后退、进度条等任何控件。开始我用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,//266
libvlc_MediaPlayerTimeChanged,//267
libvlc_MediaPlayerPositionChanged,//268
libvlc_MediaPlayerSeekableChanged,//269
libvlc_MediaPlayerPausableChanged,//270
libvlc_MediaPlayerTitleChanged,//271
libvlc_MediaPlayerSnapshotTaken,//272
libvlc_MediaPlayerLengthChanged,//273
libvlc_MediaPlayerVout,//274
libvlc_MediaPlayerScrambledChanged,//275
libvlc_MediaPlayerESAdded,//276
libvlc_MediaPlayerESDeleted,//277
libvlc_MediaPlayerESSelected,//278
libvlc_MediaPlayerCorked,//279
libvlc_MediaPlayerUncorked,//280
libvlc_MediaPlayerMuted,//281
libvlc_MediaPlayerUnmuted,//282
libvlc_MediaPlayerAudioVolume,//283
libvlc_MediaPlayerAudioDevice,//284
libvlc_MediaPlayerChapterChanged,//285
libvlc_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#代码写的详细些。如果能为有相同需求的朋友提供帮助,那是最好不过了,同时也是我自己的一篇笔记吧。