基于libVLC的视频播放器
本文来自于:http://blog.csdn.net/leixiaohua1020/article/details/42363079
最简单的基于libVLC的例子:最简单的基于libVLC的视频播放器
本文记录使用libVLC的开发的最简单的视频播放器示例。VLC Media Player是一款优秀的播放器,但是由于它的源代码编译的难度比较大,一直没有深入研究过它的开发方面的技术。此前触到了一些VLC开发方面的东西,因 此总结了一下libVLC的开发示例程序。
VLC开发所需的库文件可以有2种获取方法:
1. 自行编译
2. 直接从安装目录里面拷贝出来
第一种方法难度要大一些。尤其是在Windows下编译VLC是个比较麻烦的事情。一般情况下可以选择第二种方法获取VLC开发所需的文件。
开发VLC所需的文件的位置:
1. 动态链接库*.dll:安装目录下的libvlc.dll,libvlccore.dll以及plugins目录下的所有文件。
PS:VLC支持非常多的plugin。因此plugins目录的体积确实是非常大的。
2. 静态链接库*.lib:安装目录/sdk/lib
3. 头文件*.h:安装目录/sdk/include
新建一个VC工程后,把上述三类文件分别拷贝至新工程目录下,并且配置它们的路径之后,就可以使用libVLC进行开发了。
使用libVLC开发一个播放器十分的容易。最简单的基于libVLC的视频播放器的流程图如下图所示。
流程图中包含了3个结构体:
libvlc_instance_t:代表一个libVLC的实例。
libvlc_media_t:代表一个可以播放的媒体。
libvlc_media_player_t:代表一个VLC媒体播放器(一个视频播放器播放一个视频)。注意VLC并不仅仅用于媒体播放。
创建或者以上3个结构体通过以下6个函数:
libvlc_new():创建libvlc_instance_t。
libvlc_media_new_path():创建libvlc_media_t。
libvlc_media_player_new_from_media():创建libvlc_media_player_t。
libvlc_media_player_release():释放libvlc_media_player_t
libvlc_media_release():释放libvlc_media_t。
libvlc_release():释放libvlc_instance_t。
可以已通过下面的函数控制媒体的播放或者暂停,这些函数都需要使用libvlc_media_player_t作为参数。这里处于简化的目的,只使用了播放和停止函数:
libvlc_media_player_play():播放。
libvlc_media_player_pause():暂停。
libvlc_media_player_stop():停止。
除了上述3个函数之外,还包括libvlc_media_player_set_position()等函数,这里不再一一记录。
创 建libvlc_media_t有两种方法:libvlc_media_new_path()和 libvlc_media_new_location()。简单描述一下这两个函数的区别:libvlc_media_new_location()用于 打开协议,而libvlc_media_new_path()用于打开文件。因而传递给libvlc_media_new_path()的就是普通的文件 路径(绝对路径例如D:\xxx.flv,或者相对路径例如xxx.flv),而传递给libvlc_media_new_location()的就是协 议地址(例如"udp://….","http://")。但是这里有一点需要注意,在VLC中"文件"也属于一种广义上的"协议"。因此使用 libvlc_media_new_location()也可以打开文件,但是必须在文件路径前面加上"文件协议"的标记"file:///"。例如打开 "F:\movie\cuc_ieschool.flv"下的视频,实际使用的代码如下所示。
libvlc_media_new_location (inst, "file:///F:\\movie\\cuc_ieschool.flv");
此外,VLC还支持很多"神奇"的协议,比如输入"screen://"协议就可以进行屏幕录制,代码如下。
libvlc_media_new_location (inst, "screen://");
在这里我只实践过Windows下把libVLC的弹出窗口嵌入到程序中。将窗口或者控件的句柄传递给libvlc_media_player_set_hwnd()函数即可。
这里有一点需要注意,如果把libVLC弹出窗口嵌入到程序中的话,"全屏"功能就不能使用了。
在 libVLC中可以通过 libvlc_media_player_get_length(),libvlc_video_get_width(),libvlc_video_get_height() 等函数获取到视频的时长,宽,高等信息。但是有一个很奇怪的现象:如果在调用完libvlc_media_player_play()之后立即调用上述3 个函数的话,返回的值都是0,只有"等待"一段时间(例如调用sleep())后再调用上述函数,才能得到正确的数值。对于这种现象,我觉得可能是 libVLC加载完成之后,才能通过上述几个函数获得正确的值(自己推测的,还没有深究)。
#include <Windows.h> #include "vlc/vlc.h" int main(int argc, char* argv[]) { libvlc_instance_t * inst; libvlc_media_player_t *mp; libvlc_media_t *m; libvlc_time_t length; int width; int height; int wait_time=5000; //libvlc_time_t length; /* Load the VLC engine */ inst = libvlc_new (0, NULL); //Create a new item //Method 1: //m = libvlc_media_new_location (inst, "file:///F:\\movie\\cuc_ieschool.flv"); //Screen Capture //m = libvlc_media_new_location (inst, "screen://"); //Method 2: m = libvlc_media_new_path (inst, "cuc_ieschool.flv"); /* Create a media player playing environement */ mp = libvlc_media_player_new_from_media (m); /* No need to keep the media now */ libvlc_media_release (m); // play the media_player libvlc_media_player_play (mp); //wait until the tracks are created _sleep (wait_time); length = libvlc_media_player_get_length(mp); width = libvlc_video_get_width(mp); height = libvlc_video_get_height(mp); printf("Stream Duration: %ds\n",length/1000); printf("Resolution: %d x %d\n",width,height); //Let it play _sleep (length-wait_time); // Stop playing libvlc_media_player_stop (mp); // Free the media_player libvlc_media_player_release (mp); libvlc_release (inst); return 0; }
最简单的基于libVLC的例子:最简单的基于libVLC的视频播放器(图形界面版)
本文记录使用libVLC开发的一个简单的图形界面的视频播放器。由于是示例程序,只包含了最简单的媒体播放方面的功能。其中有一些功能还不完善,有时间以后再做修改。
最简单的基于libVLC的视频播放器(图形界面版)
这 是使用libVLC基于MFC开发的一个示例播放器。实现了一个播放器的基本功能:播放,暂停/继续,停止,播放时间轴的显示,以及从任一点开始播放媒 体。并且支持将媒体文件拖拽至播放器进行播放。播放前将媒体文件的路径输入到"URL"栏中,然后单击"Start"即可开始播放。
播放时候的效果截图如下所示。
源代码比较长,不再详细记录。简单记录一下其中几个主要功能的实现机制。
视频的播放/暂停/继续/停止
播放
视频"播放"的源代码如下所示。简单来说,完成了以下视频播放的初始化工作:
(1) 输入的URL转换为UTF-8编码(后文详细记录)
(2) 将显示视频画面的控件的句柄提供给libVLC
(3) 初始化libVLC并开始播放
(4) 开启定时器,用于更新视频播放的进度(后文详细记录)
void CplayerGUIDlg::OnBnClickedStart() { CStringW cstr_url; #ifdef _UNICODE m_url.GetWindowText(cstr_url); #else USES_CONVERSION; CStringA cstr_urla; m_url.GetWindowText(cstr_urla); cstr_url.Format(L"%s",A2W(cstr_urla)); #endif std::string str_url; UNICODE_to_UTF8(cstr_url, str_url); const char *char_url=str_url.c_str(); if(strcmp(char_url,"")==0){ AfxMessageBox(_T("Input URL is NULL!")); return; } HWND screen_hwnd=NULL; screen_hwnd = this->GetDlgItem(IDC_SCREEN)->m_hWnd; if(playerstate!=STATE_PREPARE){ AfxMessageBox(_T("Media is playing now.")); return; } /* Create a new item */ //m = libvlc_media_new_location (libvlc_inst, "http://mycool.movie.com/test.mov"); libvlc_m = libvlc_media_new_path (libvlc_inst, char_url); /* Create a media player playing environement */ libvlc_mp = libvlc_media_player_new_from_media (libvlc_m); /* No need to keep the media now */ libvlc_media_release (libvlc_m); //on windows libvlc_media_player_set_hwnd (libvlc_mp,screen_hwnd); /* play the media_player */ int x=libvlc_media_player_play (libvlc_mp); //_sleep (30000); /* Let it play a bit */ playerstate=STATE_PLAY; SetBtn(STATE_PLAY); SetTimer(1,1000,NULL); }
暂停/继续
视频"暂停/继续"的源代码如下所示。其中调用了libvlc_media_player_set_pause()设定"暂停"或者是"继续"。
void CplayerGUIDlg::OnBnClickedPause() { if(playerstate==STATE_PLAY){ libvlc_media_player_set_pause(libvlc_mp,1); playerstate=STATE_PAUSE; GetDlgItem(ID_PAUSE)->SetWindowText(_T("Resume")); }else if(playerstate==STATE_PAUSE){ libvlc_media_player_set_pause(libvlc_mp,0); playerstate=STATE_PLAY; GetDlgItem(ID_PAUSE)->SetWindowText(_T("Pause")); } }
停止
视频"停止"的源代码如下所示。其中调用了libvlc_media_player_stop()停止视频播放,并且调用libvlc_media_player_release()释放相应的libvlc_media_player_t结构体。
void CplayerGUIDlg::OnBnClickedStop() { if(libvlc_mp!=NULL){ libvlc_media_player_stop (libvlc_mp); libvlc_media_player_release (libvlc_mp); KillTimer(1); } SystemClear(); }
视频播放进度杂时间轴的显示
随着视频的播放,需要在视频播放进度的时间轴上更新播放进度信息。在程序中使用了一个定时器完成这个功能。
在视频开始播放的时候,调用SetTimer()开启定时器。时间间隔设置为1000ms。
SetTimer(1,1000,NULL);
在视频停止播放的时候,调用KillTimer()结束定时器。
KillTimer(1);
在定时器的消息响应函数中,调用libvlc_media_player_get_time()获取当前视频的播放进度,此外调用libvlc_media_player_get_length()获取视频的总时长。
经过计算之后,就可以把计算的结果设置到相应的编辑框(Edit Control)以及滑动控制条(Slider Control)上。
void CplayerGUIDlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1){ CString curtimestr,durationstr; int curtime; int duration; int tns, thh, tmm, tss; int progress; //ms curtime = libvlc_media_player_get_time(libvlc_mp); if(curtime!=0){ //change to second tns = curtime/1000; thh = tns / 3600; tmm = (tns % 3600) / 60; tss = (tns % 60); curtimestr.Format(_T("%02d:%02d:%02d"),thh,tmm,tss); m_curtime.SetWindowText(curtimestr); } duration = libvlc_media_player_get_length(libvlc_mp); if(duration!=0){ tns = duration/1000; thh = tns / 3600; tmm = (tns % 3600) / 60; tss = (tns % 60); durationstr.Format(_T("%02d:%02d:%02d"),thh,tmm,tss); m_duration.SetWindowText(durationstr); progress=curtime*100/duration; m_progress.SetPos(progress); } } //Stop in the end if(libvlc_media_player_get_state(libvlc_mp)==libvlc_Ended) OnBnClickedStop(); CDialogEx::OnTimer(nIDEvent); }
当鼠标拖动滑动控制条(Slider Control)控件上的滑块的时候,需要根据拖动的位置设置视频的播放进度。此时调用libvlc_media_player_set_position()设定视频的播放进度。消息响应函数中的代码如下所示。
void CplayerGUIDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if (pScrollBar->GetSafeHwnd() == m_progress.GetSafeHwnd()){ float posf=0.0; if(nSBCode==SB_THUMBPOSITION){ posf=(float)nPos/100.0; libvlc_media_player_set_position(libvlc_mp,posf); } } CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar); }
libVLC 使用英文作为输入路径的时候是没有问题的。但是当我们直接传递中文路径的时候,会出现libVLC将中文解析为乱码从而导致无法播放的问题。这个问题卡了 我一阵子时间。造成这一问题的原因在于VLC的输入文件路径是使用UTF-8编码的。因此我们需要将输入路径转码为UTF-8编码。转码之后这一问题即得 到了解决。
Unicode转码为UTF-8的函数代码如下所示。
void CplayerGUIDlg::UNICODE_to_UTF8(CStringW& unicodeString, std::string& str) { int stringLength = ::WideCharToMultiByte(CP_UTF8, NULL, unicodeString, wcslen(unicodeString), NULL, 0, NULL, NULL); char* buffer = new char[stringLength + 1]; ::WideCharToMultiByte(CP_UTF8, NULL, unicodeString, wcslen(unicodeString), buffer, stringLength, NULL, NULL); buffer[stringLength] = '\0'; str = buffer; delete[] buffer; }
最简单的基于libVLC的例子:最简单的基于libVLC的推流器
本文记录基于libVLC的最简单的推流器。该推流器可以将本地的视频文件转码后推送至目标流媒体服务器(也可以是一个组播地址)。
最简单的基于libVLC的推流器
该推流器的代码十分简单,只用到了几个函数:
libvlc_new():创建libvlc_instance_t。
libvlc_vlm_add_broadcast():增加一个广播(broadcast)。
libvlc_vlm_play_media():播放指定名称的广播(broadcast)。
libvlc_vlm_stop_media():停止指定名称的广播(broadcast)。
libvlc_vlm_release():释放ibvlc_instance_t。
本示例的输入视频文件为"cuc_ieschool.flv"。这个推流器实现了:
- 输入视频转码为H.264
- 音频转码为mp2
- 封装格式设置为MPEG-TS
- 以UDP的形式发送至组播地址"udp://233.233.233.233:6666"
在程序发送广播的同时,会弹出一个窗口显示当前正在发送的视频。
#include <Windows.h> #include "vlc/vlc.h" int main(int argc, char **argv) { libvlc_instance_t *vlc; const char *url; //Send File //Transcode it. Video codec use x264. Audio codec use mpga. //Mux it to mpegts format. //And stream it to udp://233.233.233.233:6666 /* const char *sout = "#transcode{vcodec=h264,fps=25,venc=x264{preset=ultrafast,"\ "profile=main,tune=zerolatency},vb=512,scale=0.5," \ "acodec=mpa,aenc=ffmpeg,ab=64,channels=2}" \ ":standard{access=udp,mux=ts,dst=233.233.233.233:6666}"; */ //Send and playing at same time const char *sout = "#transcode{vcodec=h264,fps=25,venc=x264{preset=ultrafast,"\ "profile=baseline,tune=zerolatency},vb=512," \ "acodec=mpga,ab=64,channels=2}" \ ":duplicate{dst=display,dst=standard{access=udp,mux=ts,dst=233.233.233.233:6666}}"; const char *media_name = "Lei's test"; //Screen Capture //url = "screen://"; url = "cuc_ieschool.flv"; vlc = libvlc_new(0, NULL); libvlc_vlm_add_broadcast(vlc, media_name, url, sout, 0, NULL, true, false); libvlc_vlm_play_media(vlc, media_name); //play 30s _sleep(30000); libvlc_vlm_stop_media(vlc, media_name); libvlc_vlm_release(vlc); return 0; }