Vlc开发总结


***【在线视频教程】***

好文章,来自【福优学苑@音视频+流媒体】

Vlc开发总结 


    本来想使用OpenCV开发视频实时播放的ocx插件,但是看了一些关于opencv的资料发现在摄像头检测以及网络摄像头识别的细节几乎没有,而且opencv在显示的时候也没发现怎么显示到mfc的Dialog上,面对貌似强大的opencv,我只能说我了解的还太少。因此转向了网上讨论比较多的vlc开发,在网上看了一下vlc的介绍:


设计原理: VLC有着其强的模块化设计,这使得它对新文件格式解析、解码器或流方法模块能很容易地包含进来。这个原则也延伸到了其它领域,使得VLC可以对接口,音视频输出控制以及音视频滤波模块有着广泛的选择空间。现在,VLC内部模块已经超过300个。


特性: VLC受欢迎的原因是它有极强的播放能力,不完整、未下载完成或损坏的视频文件都能顺利地播放。Vlc常被用来播放网络视频,因此少许延迟没关系,但是播放实时监控视频流的话,这是不能接受的!这个问题有待研究。

 


一.Vlc开发依赖项


    Vlc支持跨:Windows、WinCE、Linux、MacOSX等操作平台,本次开发需要结合windows的mfc因此以visual studio 2010编译环境,网上有提供的vlc支持库libvlccore.dll、libvlc.dll、libvlc.lib等。上面的依赖库也可以自行编译。

     


二.Vlc提供的接口


libvlc_new():用于初始化一个libvlc的实例,argc表示参数的个数,argv表示参数,返回创建的实例若当发生错误时返回NULL


libvlc_release():用于销毁一个libvlc的实例libvlcerrorhandling


libvlc_errmsg():返回的是在当前线程中产生的最新的libvlc错误,这个错误信息至少在另外一个错误发生之前(至少再调用一次libvlc)都是有效的,当没有任何错误的时候返回的是NULL


libvlc_clearerr():用于清除当前线程的libvlc的错误状态.此操作是可选的,默认情况下,错误状态是会在新的错误发生时被覆盖.


libvlc_vprinterr():用于设置当前线程的libvlc的错误状态和消息.无论何时都返回一个nul字符


libvlc_printerr():打印错误信息


libvlc_retain():增加libvlc的引用计数,任何新的libvlc实例的引用计数为1


libvlc_add_intf():尝试启动libvlc实例的用户接口,p_instance表示要启动的实例,name为接口名,NULL表示默认,返回0表示成功-1表示发生错误


libvlc_set_exits_handler():此函数用于为一个已存在的libvlc事件注册一个回调.此方法在你用libvlc_add_intf()开启了至少一个接口时非常有用.典型的,这个函数将唤醒你的程序主循环(从其他线程).参数p_instance表示libvlc实例,cb表示当libvlc要退出时要调用的回调.opaque表示回调的数据指针.警告:此函数不能同libvlc_wait()同时调用.


libvlc_wait():等待到有一个接口引发实例的推出动作.必须先用libvlc_add_intf()开启至少一个接口.


libvlc_set_user_agent():设置应用程序名,当有协议要求的时候,libvlc将把这个名字作为用户代理串传递给它.参数name应该是一个可读的应用程序名,例如"FooBarplayer1.2.3",http参数为HTTPUserAgent。例如"FooBar/1.2.3Python/2.6.0。


libvlc_get_Version():返回libvlc的版本号


libvlc_get_compiler():返回编译libvlc的编译器的版本。


libvlc_get_changeset():返回libvlc的changeset?libvlcasynchronouseventslibvlc发出不同步事件许多libvlc对象,如libvlc_instance_tlibvlc_media_player_t不同步的产生时间,它们中的每一个都提供了libvlc_event_manager_t事件管理器。你可以通过libvlc_event_attach()来订阅这些事件以及用libvlc_event_detach()来退订事件。


libvlc_event_manager_t属于libvlc对象的事件管理器 libvlc_event_type_t:表示libvlc的事件


libvlc_callback_t():回调函数通知(callbackfunctionnotification翻译不准确),参数p_event为触发回调的时间.


libvlc_event_attach():注册一个eventnotification。参数p_event_manager想要绑定的事件管理器.通常来说它是由vlc_my_object_event_manager()处获得的,此处的my_object是你想要监听的对象,i_event_type是想要监听的事件,f_callback是当i_event_type发生时要调用的函数。user_data是用户提供的伴随事件而传递的数据。成功时此函数返回0,发生错误时返回ENOMEM。


libvlc_event_detach():退订一个event notification。


libvlc_event_type_name():获得一个事件的类型名libvlc_logLibVLClogginglibvlc_log系列函数提供了访问libvlc消息日志的方法.这些函数仅用于高级用户或调试之用.


libvlc_get_log_verbosity():获得VLC消息的详细级别


libvlc_set_log_verbosity():设置VLC消息的详细级别


libvlc_log_open():开启VLC消息日志实例(从一个libvlc实例中获得其消息日志实例)


libvlc_log_close():关闭VLC消息日志实例 libvlc_log_count():返回日志中消息条数libvlc_log_clear():清除日志实例.将把实例中的所有消息删除,为了防止消息阻塞,应该经常清除.


libvlc_log_get_iterator():定位并返回一个日志中记录的iterator


libvlc_log_iterator_free():释放一个先前定位好的iterator


libvlc_log_iterator_next():返回下一条日志消息,当接下来为空的时候返回NULL,否则返回下一个消息对象libvlc_media.hlibvlc_media_t是一个可播放的媒体的抽象表达.它包含了这个媒体的位置以及各种可选的元数据.


libvlc_state_t:此枚举类型的循序必须严格保证和源码一致,同时可参考mediacontrol_PlayerStatus,input_state_e枚举类型以及VideoLan.LibVLC.State(在bindings/cil/src/media.cs)


libvlc_media_stats_t: Libvlc的媒体统计信息。


libvlc_media_track_info_t:主要是fourcc和docec的跟踪信息。


libvlc_media_new_location():使用一个给定的媒体资源路径来建立一个libvlc_media对象.参数psz_mrl为要读取的MRL(MediaResourceLocation).此函数返回新建的对象或NULL。


libvlc_media_new_path():从本地文件系统路径新建,其他参照上一条


libvlc_media_new_as_node():使用给定的名称创建一个libvlc_media_t并将其作为一个空的节点


libvlc_media_add_option():添加一个选项到已有的libvlc_media_t,这个选项将被用于决定media_player如何读取媒体。这样一来就可以在每个媒体上指定各自的VLC的高级reading/streaming选项。


libvlc_media_add_option_flag():减价一个带有可配置标记的选贤到已有的libvlc_media_t.其他同上一条.


libvlc_media_retain():保留一个引用到一个媒体描述对象(libvlc_media_t.使用libvlc_media_release()来减少一个媒体描述对象的引用计数


libvlc_media_release():减少一个libvlc_media_t的引用计数,如果减少到0时,此此函数将释放此对象(销毁).它将发送一个libvlc_MediaFreed事件到所有的监听者那里。如果一个libvlc_media_t被释放了,它就再也不能使用了。


libvlc_media_get_mrl():从一个媒体描述对象处获得它的mrl


libvlc_media_duplicate():镜像一份媒体描述对象


libvlc_media_get_meta():读取媒体的元数据。如果媒体还没被解析,则返回NULL,这个方法会自动调用libvlc_media_parse_async(),因此,在调用此方法以后,你可以接收到一个libvlc_MediaMetaChanged事件。如果你希望使用一个同步的版本,请确保你在调用get_meta()之前调用了libvlc_media_parse();


libvlc_media_set_meta():设置媒体的元数据,此方法不会保存数据,还需要调用libvlc_media_save_meta()来保存.


libvlc_media_get_state():获取当前媒体描述对象的状态.可能的状态被定义在livblc_structures.c中.


libvlc_media_subitems():获得一个媒体描述对象的子项目.此方法将增加媒体描述对象的引用计数,使用libvlc_media_list_release()减少引用计数.


libvlc_media_event_manager():获得一个媒体描述对象的事件管理器.


libvlc_media_get_duration():获得一个媒体描述对象的持续时间.发生错误时返回-1.


libvlc_media_parse():解析一个本地媒体的元数据和轨道信息,此方法是同步的.


libvlc_media_parse_async():同上,此方法不同步,你可以监听libvlc_MediaParsedChanged事件来追踪他,如果已经被解析过了则此事件不会被触发。


libvlc_media_is_parsed():获得一个媒体描述对象的分析状态。当分析过了返回true。


libvlc_media_set_user_data():设置媒体描述符的用户数据,此数据仅被host程序访问,VLC.framework将它作为一个指向一个引用了一个libvlc_media_t指针的本地对象的指针来使用


libvle_media_get_tracks_info():获得媒体描述符的基本流信息.注意你必须使用--sout="#description"播放媒体恰好一次,否则将得到一个空的数组。而多次播放则会导致多个重复数据。


 


三.Mfc下核心vlc程序示例

libvlc_media_player_t *p_media_player =NULL;
void VLC_Player()
{ 
libvlc_media_player_release(p_media_player);//清理上次播放中的播放器占的内存等 libvlc_exception_t  ex; 
libvlc_exception_init(&ex);
int vlc_argc = 0; 
const char * const vlc_argv[] = {  
"-I", "dummy", /* Don't use any interface */  
"--ignore-config" /* Don't use VLC's config */   
}; 
vlc_argc = sizeof(vlc_argv)/sizeof(vlc_argv[0]); 
libvlc_instance_t *p_instance = libvlc_new(vlc_argc, vlc_argv, &ex);
 if (p_instance == NULL) 
{  AfxMessageBox("Can not allocate the libvlc");  
return; 
} 
//char* psz_mrl = "rtp://@239.255.0.1:5004";//组播,get state返回libvlc_Error 
char* psz_mrl = "..\\test.264";//这个参数可以正常播放test.264
 /*libvlc_media_t *p_media = libvlc_media_new(p_instance, "./w.sdp",&ex);*/
 libvlc_media_t *p_media = libvlc_media_new(p_instance, "./test.264",&ex);
  if (p_media == NULL)  
AfxMessageBox("Can not find the streaming"); 
p_media_player = libvlc_media_player_new_from_media(p_media, &ex);
libvlc_drawable_t hwnd = (libvlc_drawable_t)this->GetDlgItem(IDC_EDIT1)-> GetSafeHwnd(); 
if (hwnd == NULL) 
{  
AfxMessageBox("1");  return; 
} 
libvlc_video_set_parent(p_instance,hwnd,&ex);//设置播放的父窗口 
libvlc_media_player_play(p_media_player, &ex);//播放器播放media
libvlc_exception_clear(&ex);//清理libvlc_exception_t结构体
libvlc_release(p_instance);//清理libvlc实例 
libvlc_media_release(p_media); //播放后,释放新建的media内容
Sleep(0);//释放该线程时间片给界面线程,用于处理界面响应。
 }


上面仅仅是显示视频画面的核心程序,还有一些控制接口可以自行调用,根据程序的需要进行调用。


 


四.Vlc开发注意事项


    Vs环境有两种编译模式,一种是debug版编译,另一种是release版本编译。顾名思义,debug版本编译的程序可以使用vs提供的内部工具进行编译调试,比如使用断点单步执行,堆栈查看,变量跟踪等。而release方式编译的工程不能进行编译,因此往往生成的release版本的文件要小于debug版。产品的发布也是要求发布release版。


    让开发人员不爽的是,activex开发不能运行单步执行,因此单步调试的时候比较纠结。不过还有微软提供了ActiveX control Container,解决了一些问题。


 


五.已实现功能


  • 1.      内嵌于web

  • 2.      可实现窗口最大化最小化

  • 3.      停止/开始

  • 4.      录像

  • 5.      rtsp流地址改变


 


六.现存问题


1.      在网上下载了版本为vlc-0.9.9的播放器,播放rtsp的视频流发现有半秒左右的延迟,运行自己写的程序也是这样,还没发现什么原因,怀疑是缓存buffer大小有关系。


2.      生成的ocx有很多依赖项,其中包括一个文件夹plugins,里面很多dll,还没有成功打包成cab,后续研究。


VLC的C++封装  


  因为工作需要,研究了一段时间的播放器开发,如果从头开始做,可以学习下FFmpeg(http://www.ffmpeg.org/),很多播放器都是基于FFmpeg开发的,但是这样工作量和难度都比较大,如果想很快能拿出一个播放器来用的,可以研究下开源的播放器,参考下射手播放器作者的文章:媒体播放器三大底层架构。


  对比下现有的主流播放器:媒体播放器列表,VLC是在各个方面都表现很突出的一款。VLC 是一款免费、自由、开源的跨平台多媒体播放器及框架,可播放大多数多媒体文件,DVD、音频 CD、VCD 以及各类流媒体协议。VLC官网:http://www.videolan.org/。


  VLC是在Linux下用C语言开发的,如果想用其接口,可以将C的接口用C++封装一下,这样使用方便的很多,在CodeProject上搜到一个C++的封装,在他的基础上加了点函数,使用了最新的VLC版本(2.1.0),用MFC做成了如下的样子:


image.png


  主要实现功能:


  1、打开:加载音视频文件(同时开始播放)。


  2、播放/暂停:播放暂停文件播放。


  3、停止:停止播放。


  4、快进:快进5秒。


  5、快退:后退5秒。


  6、音量:音量调节(0--100)。


  7、播放进度控制:使用进度条控制。


  基本上实现的功能和CodeProject上那个demo差不多,我修改后可以播放网络流媒体,将加载的文件名修改为流媒体地址就可以了。播放上面双语字幕的文件时貌似不能正常显示中文,VLC播放器本身也有这个问题,不过可以设置修改。其他更多的功能还有待研究,VLC的接口注释都很详细,按照它的接口应该可以实现更多的功能,有问题也可以直接在VideoLan的论坛上发帖问:https://forum.videolan.org/。


  主要问题:运行时需要依赖的VLC的plugins目录里的DLL(66M大小)太多,有一些DLL是用不到的,如果封装成ActiveX给Web下调用,打包的cab的会比较大,需要裁剪。。。


  主要参考文章:


  1、http://www.codeproject.com/Articles/38952/VLCWrapper-A-Little-C-wrapper-Around-libvlc


  2、http://www.cnblogs.com/Alberl/archive/2013/11/04.html


好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***