vlc音视频开发(四)封装播放器对象

来源:微信公众号「编程学习基地」

一、了解vlc常用API

libvlc_new

LIBVLC_API libvlc_instance_t *
libvlc_new( int argc , const char *const *argv );

返回值是libvlc_instance_t* vlc播放器实例

看起来很简单的函数,里面学问很大,我个人喜欢这样使用它

/* Load the VLC engine */
const char* const vlc_args[] = {
    
    
        "--demux=h264",
        "--ipv4",
        "--no-prefer-system-codecs",
        "--rtsp-caching=300",
        "--network-caching=500",	//网络额外缓存值 (ms)
        "--rtsp-frame-buffer-size=10000000",
        "--rtsp-tcp",				//RTSP采用TCP传输方式
};
libvlc_inst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);

当然也可以通过libvlc_media_add_option设置这些参数

libvlc_media_add_option(vlc_media, ":demux=h264");

如果是本地文件,非网络串流,你只要了解 demux 解码方式即可,解码方式有哪些呢?

--demux={
    
    any,mp4,avi,asf,es,flacsys,tta,nuv,mpc,wav,caf,sid,sap,mkv,ogg,diracsys,live555,smf,adaptive,webvtt,ttml,voc,gme,rawvid,pva,image,ts,aiff,mod,directory,xa,au,nsv,ps,ty,h26x,h26x,mjpeg,mpgv,dvdnav,libbluray,rawdv,demux_cdg,nsc,avcodec,ps,vobsub,demux_stl,vc1,demuxdump,webvtt,real,subtitle,stats,es,rawaud,none} 

vlc很强大,支持解码当下几乎所有数据流,当然自定义的私有流不在此列

libvlc_media_new_location/libvlc_media_new_path

LIBVLC_API libvlc_media_t *libvlc_media_new_location(
                                   libvlc_instance_t *p_instance,
                                   const char * psz_mrl );
LIBVLC_API libvlc_media_t *libvlc_media_new_path(
                                   libvlc_instance_t *p_instance,
                                   const char *path );

返回值是libvlc_media_t * vlc音视频文件指针

libvlc_media_new_locationlibvlc_media_new_path 这两个函数是一对

libvlc_media_new_location 用于加载网络串流,例如rtsp流,加载的通常是形如 rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov的网络串流

libvlc_media_new_path 用于加载本地文件播放

libvlc_media_player_new_from_media

LIBVLC_API libvlc_media_player_t * libvlc_media_player_new_from_media( libvlc_media_t *p_md );

创建vlc播放环境,返回一个 libvlc_media_player_t* 指针

libvlc_media_release

libvlc_media_releaselibvlc_media_player_new_from_media是一对,当视频文件被加载到vlc播放环境之后要释放掉

//从音视频文件中创建vlc播放器
libvlc_media_player_t*libvlc_mp = libvlc_media_player_new_from_media(libvlc_m);
//释放音视频文件资源
libvlc_media_release(libvlc_m);

libvlc_media_player_play

LIBVLC_API int libvlc_media_player_play ( libvlc_media_player_t *p_mi );

当这个函数执行的时候视频才开始播放

libvlc_media_player_set_hwnd

这个函数是设置播放器的播放窗口,在UI设置里面要用到

MFC直接就可以获取 hwnd 窗口句柄,qt中需要先获取WId,然后才可以设置播放窗口

//qt下设置播放窗口
WId libvlc_WId = libvlc_mainWindow->winId();
libvlc_media_player_set_hwnd(libvlc_mp, (void*)libvlc_WId);

libvlc_media_player_stop

LIBVLC_API void libvlc_media_player_stop ( libvlc_media_player_t *p_mi );

结束播放

libvlc_media_player_release

LIBVLC_API void libvlc_media_player_release( libvlc_media_player_t *p_mi );

释放播放器资源

libvlc_release

LIBVLC_API void libvlc_release( libvlc_instance_t *p_instance );

释放播放器实例资源

这三个函数几乎是放在一起用的,具体看后面的VlcPlayer播放器对象封装

二、了解vlc播放状态

typedef enum libvlc_state_t
{
    
    
    libvlc_NothingSpecial=0,	//播放准备状态
    libvlc_Opening,				//libvlc_media_player_new_from_media调用后触发该状态
    libvlc_Buffering, /* XXX: Deprecated value. Check the
                       * libvlc_MediaPlayerBuffering event to know the
                       * buffering state of a libvlc_media_player */
    libvlc_Playing,
    libvlc_Paused,
    libvlc_Stopped,
    libvlc_Ended,
    libvlc_Error
} libvlc_state_t;

这个状态看着没啥用,但是对于理解播放器的播放过程至关重要

三、了解vlc事件处理

libvlc_event_e :事件类型

libvlc_event_manager_t:事件管理器

他们怎么联系起来呢?通过 libvlc_media_player_event_manager 订阅事件

LIBVLC_API libvlc_event_manager_t * libvlc_media_player_event_manager ( libvlc_media_player_t *p_mi );

订阅完事件需要给事件注册事件处理函数

libvlc_event_attach

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

为了不一个个注册,直接循环注册,qt使用如下

// 事件列表
QList<libvlc_event_e> events;
events << libvlc_MediaPlayerOpening
       << libvlc_MediaPlayerBuffering
       << libvlc_MediaPlayerPlaying
       << libvlc_MediaPlayerPaused
       << libvlc_MediaPlayerStopped
       << libvlc_MediaPlayerEncounteredError
       << libvlc_MediaPlayerEndReached;
// 订阅事件
libvlc_eventManager = libvlc_media_player_event_manager(libvlc_mp);
foreach (const libvlc_event_e &event, events) {
    
    
    libvlc_event_attach(libvlc_eventManager, event, handleEvents, this);
}

事件处理函数如下

void VlcPlayer::handleEvents(const libvlc_event_t *event, void *userData)
{
    
    
    VlcPlayer* player = static_cast<VlcPlayer*>(userData);
    switch (event->type) {
    
    
    // 播放状态改变
    case libvlc_MediaPlayerOpening:{
    
    
        qDebug()<<"libvlc_MediaPlayerOpening"<<endl;
        break;
    }
    case libvlc_MediaPlayerBuffering:{
    
    
        break;
    }
    case libvlc_MediaPlayerPlaying: {
    
    
        break;
    }
    case libvlc_MediaPlayerPaused: {
    
    
        break;
    }
    case libvlc_MediaPlayerStopped: {
    
    
        //当 libvlc_media_player_stop(libvlc_mp); 被调用才会触发该信号
        break;
    }
    case libvlc_MediaPlayerEncounteredError: {
    
    
        break;
    }
    // 时长改变时获取一次总时长
    case libvlc_MediaPlayerLengthChanged: {
    
    
        break;
    }
    // 播放时间改变
    case libvlc_MediaPlayerTimeChanged: {
    
    
        //播放时间是一直在变的
        break;
    }
    // 播放位置改变
    case libvlc_MediaPlayerPositionChanged: {
    
    
        //播放位置是一直在变的,个人认为等同 libvlc_MediaPlayerTimeChanged
        break;
    }
    case libvlc_MediaPlayerEndReached:
        //当视频播放结束时触发该信号
        break;
    default:
        break;
    }
}

四、了解vlc辅助类API

libvlc_audio_set_volume

LIBVLC_API int libvlc_audio_set_volume( libvlc_media_player_t *p_mi, int i_volume );

设置播放声音,i_volume值为 0~100

libvlc_media_player_set_position

LIBVLC_API void libvlc_media_player_set_position( libvlc_media_player_t *p_mi, float f_pos );

设置播放位置,这个位置不是随便设置的,f_pos 0~1

void VlcPlayer::setPosition(float position)
{
    
    
    float length = libvlc_media_player_get_length(libvlc_mp);
    libvlc_media_player_set_position(libvlc_mp,position/length);
}

libvlc_media_player_get_length

LIBVLC_API libvlc_time_t libvlc_media_player_get_length( libvlc_media_player_t *p_mi );

获取视频的长度,在设置播放位置有用到,很实用

libvlc_video_get_width/libvlc_video_get_height

LIBVLC_DEPRECATED LIBVLC_API
int libvlc_video_get_width( libvlc_media_player_t *p_mi );
LIBVLC_DEPRECATED LIBVLC_API
int libvlc_video_get_height( libvlc_media_player_t *p_mi );

获取视频长宽,看着也就是数据,但是如果你要获取视频的每一帧保存就需要获取视频的长宽

五、封装vlc播放器对象

vlcplayer.h

#ifndef PLAYER_H
#define PLAYER_H

#include <QObject>
#include <QWidget>
#include "vlc/vlc.h"
#include "vlc/libvlc.h"

class VlcPlayer : public QObject
{
    
    
    Q_OBJECT
public:
    explicit VlcPlayer(QObject *parent = nullptr);

signals:
    void signal_MediaPlayerPlaying();
    void signal_MediaPlayerStopped();
signals:
    void signal_media_player_time(int curtime);
private:
    void MediaPlayerEncounteredError();
public:
    bool setURL(QString& URL);
    void setPosition(float position);
    void media_play();
    void media_pause();
    void media_stop();
    int  media_get_length();
    void media_set_voice(int value);
    void set_media_player_hwnd(QWidget* widget);
    libvlc_state_t get_media_player_state();
private:
    // 订阅事件
    void attachEvents();
    static void handleEvents(const libvlc_event_t* event, void* userData);
private:
    libvlc_event_manager_t* libvlc_eventManager;	/*事件管理器*/
    libvlc_instance_t* libvlc_inst;					/*vlc播放器实例*/
    libvlc_media_player_t* libvlc_mp;				/*vlc播放器*/
    libvlc_media_t* libvlc_m;						/*音视频文件*/
    libvlc_state_t libvlc_state;					/*播放状态*/
    QWidget* libvlc_mainWindow;                     /*播放器窗口*/
    WId libvlc_WId;                                 /*窗口*/
    QString libvlc_url;                             /*播放的URL*/
    /*时间相关*/
    int m_timeLength;
};

#endif // PLAYER_H

vlcplayer.cpp

#include "vlcplayer.h"
#include <QDebug>
#include <QThread>
#include <windows.h>
VlcPlayer::VlcPlayer(QObject *parent) : QObject(parent)
{
    
    
    libvlc_eventManager = NULL;
    libvlc_inst = NULL;
    libvlc_mp = NULL;
    libvlc_m = NULL;
    libvlc_state = libvlc_NothingSpecial;						/*播放状态*/
    libvlc_mainWindow = NULL;
    libvlc_WId = 0;
}

bool VlcPlayer::setURL(QString &URL)
{
    
    
    if(URL.isEmpty())
    {
    
    
        qDebug()<<"URL is Empty"<<endl;
        return false;
    }
    libvlc_url = URL;
    return true;
}

void VlcPlayer::setPosition(float position)
{
    
    
    float length = libvlc_media_player_get_length(libvlc_mp);
    libvlc_media_player_set_position(libvlc_mp,position/length);
}

void VlcPlayer::media_play()
{
    
    
    if (libvlc_state != libvlc_NothingSpecial) {
    
    
        qDebug()<<"Media is playing now."<<endl;
        return;
    }

    const char* version;
    version = libvlc_get_version();
    qDebug()<<"version:"<<version<<endl;

    /* Load the VLC engine */
    const char* const vlc_args[] = {
    
    
        "--demux=h264",
        "--ipv4",
        "--no-prefer-system-codecs",
        "--rtsp-caching=300",
        "--network-caching=500",	//网络额外缓存值 (ms)
        "--rtsp-frame-buffer-size=10000000",
        "--rtsp-tcp",				//RTSP采用TCP传输方式
    };
    /* Create a new item */
    if (libvlc_url.contains("rtsp")) {
    
    
        //URL播放rtsp网络流
        /* Load the VLC engine */
        libvlc_inst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);

        if (libvlc_inst == NULL)
        {
    
    
            qDebug()<<"libvlc_new error"<<endl;
            return;
        }
        libvlc_m = libvlc_media_new_location(libvlc_inst, libvlc_url.toStdString().c_str());
        if (libvlc_m == NULL)
        {
    
    
            qDebug()<<"error :libvlc_media_new_location error"<<endl;
            return;
        }
    }
    else
    {
    
    
        libvlc_inst = libvlc_new(0, NULL);
        if (libvlc_inst == NULL)
        {
    
    
            qDebug()<<"libvlc_new error"<<endl;
            return;
        }
        //本地播放视频流
        libvlc_m = libvlc_media_new_path(libvlc_inst, libvlc_url.toStdString().c_str());
        static int index = 0;
        qDebug()<<"URL["<<index++<<"]:"<<libvlc_url.toStdString().c_str();
        if (libvlc_m == NULL)
        {
    
    
            qDebug()<<"libvlc_media_new_path error";
            media_stop();
            return;
        }
    }

    /* Create a media player playing environement */
    libvlc_mp = libvlc_media_player_new_from_media(libvlc_m);

    /*添加事件*/
    attachEvents();

    /* No need to keep the media now */
    libvlc_media_release(libvlc_m);


    //on windows
    if (libvlc_mainWindow != NULL){
    
    
        Sleep(45);
        libvlc_WId = libvlc_mainWindow->winId();
        libvlc_media_player_set_hwnd(libvlc_mp, (void*)libvlc_WId);
    }
    else
    {
    
    
        Sleep(45);
    }

    /* play the media_player */
    libvlc_media_player_play(libvlc_mp);
    libvlc_state = libvlc_Playing;
}

void VlcPlayer::media_pause()
{
    
    
    libvlc_state = libvlc_media_player_get_state(libvlc_mp);
    if (libvlc_state == libvlc_Playing) {
    
    
        if (libvlc_media_player_can_pause(libvlc_mp))
        {
    
    
            libvlc_media_player_set_pause(libvlc_mp, 1);
            libvlc_state = libvlc_Paused;
        }
    }
    else if (libvlc_state == libvlc_Paused) {
    
    
        libvlc_media_player_set_pause(libvlc_mp, 0);
        libvlc_state = libvlc_Playing;
    }
}

void VlcPlayer::media_stop()
{
    
    
    if (libvlc_mp != NULL) {
    
    
        libvlc_media_player_stop(libvlc_mp);
        libvlc_media_player_release(libvlc_mp);
        libvlc_release(libvlc_inst);
        libvlc_mp = NULL;
        libvlc_m = NULL;
        libvlc_eventManager = NULL;
        libvlc_inst = NULL;		/*vlc播放器实例*/
    }
    libvlc_state = libvlc_NothingSpecial;

}

int  VlcPlayer::media_get_length()
{
    
    
    return m_timeLength;
}

void VlcPlayer::media_set_voice(int value)
{
    
    
    if(value < 0)
        return;
    if(libvlc_mp!=NULL){
    
    
        libvlc_audio_set_volume(libvlc_mp,value);
    }
}

void VlcPlayer::MediaPlayerEncounteredError()
{
    
    
    qDebug()<<"================播放错误=================="<<endl;
}

void VlcPlayer::set_media_player_hwnd(QWidget* widget)
{
    
    
    libvlc_mainWindow = widget;
}

libvlc_state_t VlcPlayer::get_media_player_state()
{
    
    
    return libvlc_state;
}

void VlcPlayer::attachEvents()
{
    
    
    // 事件列表
    QList<libvlc_event_e> events;
    events << libvlc_MediaPlayerOpening
           << libvlc_MediaPlayerBuffering
           << libvlc_MediaPlayerPlaying
           << libvlc_MediaPlayerPaused
           << libvlc_MediaPlayerStopped
           << libvlc_MediaPlayerEncounteredError
           << libvlc_MediaPlayerMuted
           << libvlc_MediaPlayerUnmuted
           << libvlc_MediaPlayerAudioVolume
           << libvlc_MediaPlayerLengthChanged
           << libvlc_MediaPlayerTimeChanged
           << libvlc_MediaPlayerPositionChanged
           << libvlc_MediaPlayerEndReached;
    // 订阅事件
    libvlc_eventManager = libvlc_media_player_event_manager(libvlc_mp);
    foreach (const libvlc_event_e &event, events) {
    
    
        libvlc_event_attach(libvlc_eventManager, event, handleEvents, this);
    }
}

void VlcPlayer::handleEvents(const libvlc_event_t *event, void *userData)
{
    
    
    VlcPlayer* player = static_cast<VlcPlayer*>(userData);
    switch (event->type) {
    
    
    // 播放状态改变
    case libvlc_MediaPlayerOpening:{
    
    
        qDebug()<<"libvlc_MediaPlayerOpening"<<endl;
        break;
    }
    case libvlc_MediaPlayerBuffering:{
    
    
        break;
    }
    case libvlc_MediaPlayerPlaying: {
    
    
        emit player->signal_MediaPlayerPlaying();
        break;
    }
    case libvlc_MediaPlayerPaused: {
    
    
        break;
    }
    case libvlc_MediaPlayerStopped: {
    
    
        //当 libvlc_media_player_stop(libvlc_mp); 被调用才会触发该信号
        qDebug()<< "播放结束" <<endl;
        break;
    }
    case libvlc_MediaPlayerEncounteredError: {
    
    
        player->MediaPlayerEncounteredError();
        break;
    }
    // 时长改变时获取一次总时长
    case libvlc_MediaPlayerLengthChanged: {
    
    
        player->m_timeLength = libvlc_media_player_get_length(player->libvlc_mp);
        break;
    }
    // 播放时间改变
    case libvlc_MediaPlayerTimeChanged: {
    
    
        //播放时间是一直在变的
        int curtime =libvlc_media_player_get_time(player->libvlc_mp);
        player->signal_media_player_time(curtime);
        break;
    }
    // 播放位置改变
    case libvlc_MediaPlayerPositionChanged: {
    
    
        //播放位置是一直在变的,个人认为等同 libvlc_MediaPlayerTimeChanged
        break;
    }
    case libvlc_MediaPlayerEndReached:
        //当视频播放结束时触发该信号
        emit player->signal_MediaPlayerStopped();
        break;
    default:
        break;
    }
}

基本封装完成,具体使用看源代码实例程序,实例程序运行如下

vlc播放器

猜你喜欢

转载自blog.csdn.net/qq_44519484/article/details/113758834