VLC 开发包 编写简单播放器

如果要写一个播放器,所有实现都是自己完成,这个就有点难度,而且非常费时,要了解媒体文件格式,视频解码,图像绘制,音视频同步等知识点,所以要完全实现还是比较麻烦的;

通常情况,可以用一些现成的库开发,如在windows上最常用的是DirectShow,但是DirectShow的学习有一点难度,而且关于解码器的东西还是需要安装如XviD,ffdshow等,建立链路时自己选择或智能选择解码器,而且也不是很容易,对于一般应用的开发来说还是比较有难度的;我之前用DirectShow编写了一个播放器,如果不是为了实时抓图,我会用VLC编写的;

--注释:当时因为对DirectShow很熟悉,所以用DirectShow编写播放器了,其实通过VLC的回调函数函数也可以直接获取视频图像,包括网络视频流图像,原理是一样的;


也可以在网上搜索开源的播放器,然后修改一些界面,不过一般的开源软件工程比较大;


这里我推荐一个比较简单的方法,用VLC SDK编写播放器,VLC是一个款平台开源的项目,还提供二进制安装包和开发包,这里我们用windows中的安装包直接安装,安装过程中选择安装sdk和ocx,这样就可以通过VC开发播放器了;


开发过程:

//简单播放示例

在VC中配置VLC安装目录中的include文件夹和lib文件夹:

示例如下:

这个例子是网上搜索的,简单修改一下:


libvlc_instance_t *     vlc_ins    = NULL;
libvlc_media_player_t * vlc_player = NULL;
libvlc_media_t *        vlc_media  = NULL;


//播放文件
void CtestVLCDlg::OnBnClickedPlay()
{
// TODO: 在此添加控件通知处理程序代码

 
vlc_ins = libvlc_new(0, NULL);
if(vlc_ins != NULL)
{
 

               //创建一个示例:
vlc_player = libvlc_media_player_new(vlc_ins);


if(vlc_player != NULL)
{
// 通过文件路径创建一个媒体实例,这里是我的测试文件
vlc_media = libvlc_media_new_path(vlc_ins, "F:\\t.avi");


if(vlc_media != NULL)
{
// 解析媒体实例
libvlc_media_parse(vlc_media);
// 获取媒体文件的播放长度,  返回 ms
libvlc_time_t duration = libvlc_media_get_duration(vlc_media);


// 此处是获取媒体包含多个的视频和音频轨以及其他类型的轨道信息
libvlc_media_track_info_t *media_tracks = NULL;
int trackCount = libvlc_media_get_tracks_info(vlc_media, &media_tracks);
// 这里是释放内存,但我测试的时候会有问题,还没仔细研究是为何
// free(media_tracks);  // crash?


// 把打开的媒体文件设置给播放器
libvlc_media_player_set_media(vlc_player, vlc_media);


// 因为是windows系统,所以需要设置一个HWND给播放器作为窗口,这里就直接使用桌面窗口,这里仅是测试
libvlc_media_player_set_hwnd(vlc_player, /*::GetDesktopWindow()*/ AfxGetApp()->m_pMainWnd->m_hWnd );
// 开始播放视频
libvlc_media_player_play(vlc_player);
}
}
}


}


//播放完成后释放资源

void CtestVLCDlg::OnBnClickedstop()

{

libvlc_media_player_stop(vlc_player);
 
              libvlc_media_release(vlc_media);
 
libvlc_media_player_release(vlc_player);
 
libvlc_release(vlc_ins);

}

通过这个简单示例可以了解VLC播放文件的过程,VLC在播放文件的时候会在内部创建播放线程,所以libvlc_media_player_play(vlc_player);播放视频后,程序不会阻塞;

可以开启定时器或线程监控播放进度,如果播放完成,就是否资源(我没有了解VLC播放完成后是否有回调函数或事件通知,如果有可以直接用);

VLC开发播放器,网上有很多资源,这里我推荐一个封装比较简明的资源 “基于VLC的MFC播放器 完整版”,可以再csdn都到这个资源;




VLC消息添加:

VLC有很多消息,可以通过添加回调函数通知:

示例代码:

if( m = libvlc_media_new_location (m_pVLC_Inst, szUTFName) )
    {
        if ( ( m != NULL ) && ( m_pVLC_Player = libvlc_media_player_new_from_media(m) ) )
        {
            libvlc_media_player_set_hwnd(m_pVLC_Player, m_hWnd);
            libvlc_media_player_play(m_pVLC_Player);


            // 事件管理
            libvlc_event_manager_t *vlc_evt_man = libvlc_media_player_event_manager(m_pVLC_Player);
            libvlc_event_attach(vlc_evt_man, libvlc_MediaPlayerEndReached, ::OnVLC_EndReached, this);
            libvlc_event_attach(vlc_evt_man, libvlc_MediaPlayerPositionChanged, ::OnVLC_PositionChanged, this);

            ......

         }

   }



但是如果按照上述代码,可能要添加好几十个函数才可以完成所有的消息判断,所以可以修改上述代码为,用循环添加所有消息到一个函数,然后在相应消息的函数中处理:

if( m = libvlc_media_new_location (m_pVLC_Inst, szUTFName) )
    {
        if ( ( m != NULL ) && ( m_pVLC_Player = libvlc_media_player_new_from_media(m) ) )
        {
            libvlc_media_player_set_hwnd(m_pVLC_Player, m_hWnd);
            libvlc_media_player_play(m_pVLC_Player);


            // 事件管理
            libvlc_event_manager_t *vlc_evt_man = libvlc_media_player_event_manager(m_pVLC_Player);
//             libvlc_event_attach(vlc_evt_man, libvlc_MediaPlayerEndReached, ::OnVLC_EndReached, this);
//             libvlc_event_attach(vlc_evt_man, libvlc_MediaPlayerPositionChanged, ::OnVLC_PositionChanged, this);


int i = 0 ;

for ( i = libvlc_MediaMetaChanged ; i< libvlc_VlmMediaInstanceStatusError; i++ )
{
libvlc_event_attach(vlc_evt_man, i, OnVLC_LibVlc, this);

}

            }

       }





但是上述代码还是有一个确定,没有面向对象,一个消息回调函数是一个全局的函数,如果让一个类完成所有对象的各自相应,方法就是:

在全局的消息回调中,可以传入自定义参数,可以把当前对象的指针传入,就是 this 指针,然后在类中添加一个函数相应消息,在全局函数中通过this指针调用相关对象的消息函数,实例:

//封装相关的VLC函数;

class VLCclass

{

public:

。。。。。。

On_libvlc_event( event );


}


void OnVLC_LibVlc(const libvlc_event_t *event, void *data)
{
if (data)
{
VLCclass * pPlayer = (VLCclass*)data;


pPlayer->On_libvlc_event( event );
}
}













Sample LibVLC Code

This sample code will (try to) play back an URL. There is also an example using SDL for video output.

 #include 
 #include 
 #include 
 
 int main(int argc, char* argv[])
 {
     libvlc_instance_t * inst;
     libvlc_media_player_t *mp;
     libvlc_media_t *m;
     
     /* Load the VLC engine */
     inst = libvlc_new (0, NULL);
  
     /* Create a new item */
     m = libvlc_media_new_location (inst, "http://mycool.movie.com/test.mov");
     //m = libvlc_media_new_path (inst, "/path/to/test.mov");
        
     /* 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);
 
 #if 0
     /* This is a non working code that show how to hooks into a window,
      * if we have a window around */
      libvlc_media_player_set_xwindow (mp, xid);
     /* or on windows */
      libvlc_media_player_set_hwnd (mp, hwnd);
     /* or on mac os */
      libvlc_media_player_set_nsobject (mp, view);
  #endif
 
     /* play the media_player */
     libvlc_media_player_play (mp);
    
     sleep (10); /* Let it play a bit */
    
     /* Stop playing */
     libvlc_media_player_stop (mp);
 
     /* Free the media_player */
     libvlc_media_player_release (mp);
 
     libvlc_release (inst);
 
     return 0;
 }


上面两个例子,有所不同,在较早期的VCL开发环境中,播放本地视频的函数也可以播放网络视频,如果用早期的库,用一套代码就可以了;

现在两个个函数分开应用了,但是其它部分代码几乎不变;



/


这里有一篇文章可以参考:http://www.cnblogs.com/evan-cai/archive/2013/01/25/2876803.html





































VLC 发送数据到网络:


Stream to memory (smem) tutorial

Here is a small tutorial intended to make a quick tour of the stream to memory module (smem) , included in VLC 1.1.0. This module allows you to write a code which handles the video and audio data read by VLC. This page shows a basic code written in C which uses this module.

/!\ Starting from VLC 2.2, smem uses size_t instead of unsigned int for buffer sizes.

Contents

 [hide] 
  • 1 Including VLC library
  • 2 Defining callbacks
  • 3 Creating a VLC instance
  • 4 Implementing callbacks
  • 5 Handling the data

Including VLC library

#include 
#include 

#include 

Defining callbacks

VLC sends you the data you need via two functions that you define yourself. They will be run before and after the data is rendered. There are two functions for the audio data, and two for the video one. In this example I will just show how to deal with audio data, the process is the same for video handling.

void prepareRender(void* p_audio_data, uint8_t** pp_pcm_buffer , size_t size); // Audio prerender callback
void handleStream(void* p_audio_data, uint8_t* p_pcm_buffer, unsigned int channels, unsigned int rate, unsigned int nb_samples, unsigned int bits_per_sample, size_t size, int64_t pts); // Audio postrender callback
// Video prerender and postrender callbacks not implemented.

Creating a VLC instance

As with every application you build with libvlc, you have to initialize the library. But you also have to tell VLC the address of each callback you wrote. This is made by writing the address in the parameters used to launch VLC.

int main(int argc, char **argv)
{
        // VLC pointers
        libvlc_instance_t *vlcInstance;
        libvlc_media_player_t *mp;
        libvlc_media_t *media;

        // VLC options
        char smem_options[256];

        sprintf(smem_options, "#transcode{acodec=s16l}:smem{audio-postrender-callback=%lld,audio-prerender-callback=%lld}",
                // We are using transcode because smem only support raw audio and video formats

                 (long long int)(intptr_t)(void*)&handleStream, (long long int)(intptr_t)(void*)&prepareRender);
                // We print (as a decimal value) the addresses. Note that you can also define smem-audio-data : this pointer
                // will be passed to your callbacks (it may be useful to retrive some extra informations) but isn't required at all.

        const char * const vlc_args[] = {
              "--verbose=2", // Be much more verbose then normal for debugging purpose
              "--sout", smem_options // Stream to memory
               };

        // We launch VLC
        vlcInstance = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);

        mp = libvlc_media_player_new(vlcInstance);

        // To be continued.

        return 0;
}

Implementing callbacks

Multimedia Wiki has information on this entry
PCM format

The smem module sends us the data in PCM format.

The first callback, called prepareRender in this example, locks the mutex and sets the data pointer (i.e. where the data will be written). All you have to do here is to ensure that *pp_pcm_bufferpoints now to a valid array where size bytes can be written.

The second callback, here handleStream, is the place where you can do everything you want with the data you got !

The p_audio_data pointer is linked to the object you set in the command line parameters. It can be useful in order to communicate with the rest of the program.


void prepareRender (void* p_audio_data, uint8_t** pp_pcm_buffer , size_t size)
{
        // TODO: Lock the mutex

        *pp_pcm_buffer = // TODO
}

void handleStream(void* p_audio_data, uint8_t* p_pcm_buffer, unsigned int channels, unsigned int rate, unsigned int nb_samples, unsigned int bits_per_sample, size_t size, int64_t pts )
{
        // TODO: explain how data should be handled
        // TODO: Unlock the mutex
}

Handling the data

The PCM format is very raw, but you may still have to do some small transformations in order to be able to get the exact waveform of the audio stream.

First, the audio data is sent as an array of bytes, but each sample may be coded on more bytes : if you use the unchanged array, the values won't mean anything. The parameter bits_per_sample helps you to know how to tackle this problem.

Then, the data is still in an unsigned format : the negative values have an offset which makes the data look weird.







另一个示例:

libvlc_vlm_add_broadcast accepts an sout string, so this seems to do the trick:

#include 
#include 
#include 
#include 

int main(int argc, char **argv) {
    libvlc_instance_t *vlc;
    const char *url;
    const char *sout = "#transcode{acodec=mp3,ab=128,channels=2," \
                       "samplerate=44100}:http{dst=:8090/go.mp3}";
    const char *media_name = "Foo";

    if (argc != 2) {
        return 1;
    }
    url = argv[1];

    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);

    sleep(60); /* Let it play for a minute */

    libvlc_vlm_stop_media(vlc, media_name);
    libvlc_vlm_release(vlc);
    return 0;
}





















VLC是一个强大的媒体文件工具,不仅仅可以播放本地文件,还可以播放网络视频流,桌面共享,开启视频音频捕获播放设置,可以通过开发包应用开发;


你可能感兴趣的:(VLC)