(十四)C++自制植物大战僵尸游戏windows平台视频播放实现

植物大战僵尸游戏开发教程专栏地址http://t.****img.cn/8UFMs


VLC库

在Cocos2d-x游戏开发框架中,没有实现windows平台视频播放的功能,需要自定义实现。在本项目中使用vlc库实现windows平台的视频播放功能。

vlc官网:网址

下载完成后,找到所下载文件的sdk目录 ,里面有实现视频播放的库文件。将头文件和库文件配置到项目中,然后使用其播放视频。


代码文件位置 

代码文件的位置在Class\Based文件夹中。


VideoPlayer.h 

在实现的头文件中引入vlc库的头文件。

#include "Based/vlc/vlc.h"

将用到的库文件进行声明。 

#pragma comment (lib, "libvlc.lib")
#pragma comment (lib, "libvlccore.lib")

定义VideoPlayer类实现视频播放,继承Sprite。

class VideoPlayer : public Sprite
{
public:
    ~VideoPlayer();

    static VideoPlayer* instance(void);

    bool init(void);
    void play(const char* path);
    void play();
    void stop(void);
    void pause(void);
    void draw(Renderer* renderer, const Mat4& transform, uint32_t flags);
    void changeVideoState();
    void setPrecent(const float precent);
    float getPrecent();
    void setVolume(int volume);
    int getVolume() const;
    void setVideoTime(const libvlc_time_t time);
    libvlc_time_t getVideoTime() const;

protected:
    VideoPlayer();
  
private:
    libvlc_instance_t* vlc;
    libvlc_media_player_t* vlc_player;
    libvlc_media_list_player_t* vlc_list_player;
    libvlc_media_list_t* vlc_list;
    libvlc_media_t* vlc_media;
    libvlc_time_t video_length;

    unsigned int width;
    unsigned int height;

    static VideoPlayer* _instance;
};

以下是VideoPlayer类的成员函数和成员变量:

  • 成员函数:

    • 析构函数 ~VideoPlayer():用于销毁VideoPlayer对象。
    • 静态成员函数 instance():返回VideoPlayer类的单例对象。
    • init():初始化视频播放器。
    • play(const char* path):播放指定路径的视频。
    • play():播放当前视频。
    • stop():停止视频播放。
    • pause():暂停视频播放。
    • draw(Renderer* renderer, const Mat4& transform, uint32_t flags):绘制函数,用于渲染视频画面。
    • changeVideoState():改变视频状态。
    • setPrecent(const float precent):设置视频播放进度。
    • getPrecent():获取当前视频播放进度。
    • setVolume(int volume):设置音量大小。
    • getVolume() const:获取当前音量大小。
    • setVideoTime(const libvlc_time_t time):设置视频播放时间。
    • getVideoTime() const:获取当前视频播放时间。
  • 成员变量:

    • vlc:libvlc实例。
    • vlc_player:libvlc媒体播放器。
    • vlc_list_player:libvlc媒体列表播放器。
    • vlc_list:libvlc媒体列表。
    • vlc_media:libvlc媒体。
    • video_length:视频长度。
    • width:视频宽度。
    • height:视频高度。
    • 静态成员变量 _instanceVideoPlayer类的单例对象。

VideoPlayer.cpp 

构造函数

VideoPlayer::VideoPlayer() :
    vlc(0), vlc_player(0), video_length(-1)
{
    init();
}

在构造函数中,初始化了VideoPlayer类的成员变量。具体来说:

  • vlcvlc_playervideo_length被初始化为0或-1。

  • 调用init()函数进行初始化。

构造函数的主要目的是设置初始状态和准备视频播放器的环境。由于构造函数调用了init()函数,在init()函数中进行了与视频播放器相关的初始化操作。


析构函数 

VideoPlayer::~VideoPlayer()
{
    CCSprite::~CCSprite();
    free(videobuf);

    libvlc_media_player_stop(vlc_player);
    libvlc_media_player_release(vlc_player);
    libvlc_release(vlc);
}

析构函数用于在对象销毁时执行清理操作。具体来说:

  • 调用基类CCSprite的析构函数CCSprite::~CCSprite(),以释放基类的资源。

  • 使用free()函数释放videobuf的内存。

  • 调用libvlc_media_player_stop()函数停止视频播放。

  • 调用libvlc_media_player_release()函数释放视频播放器相关的资源。

  • 调用libvlc_release()函数释放libvlc实例的资源。

这些操作用于确保在销毁VideoPlayer对象时,相关的资源得到正确释放,避免内存泄漏和资源泄漏。


instance函数

VideoPlayer* VideoPlayer::instance()
{
    if (_instance == nullptr)
        _instance = new VideoPlayer();
    return _instance;
}

这个函数实现了单例模式,用于获取VideoPlayer类的唯一实例。

  • 首先,它检查静态成员变量_instance是否为空指针。
  • 如果_instance为空指针,则创建一个新的VideoPlayer对象,并将其赋值给_instance
  • 最后,返回_instance指针。

这样,每次调用instance()函数时,只会返回同一个VideoPlayer对象的指针,确保只有一个VideoPlayer实例存在。


init函数 

bool VideoPlayer::init(void)
{
    vlc = libvlc_new(0, nullptr);
    vlc_player = libvlc_media_player_new(vlc);

    Size size = Director::getInstance()->getWinSize();
    width = size.width;
    height = size.height;
    videobuf = (char*)malloc((width * height) << 2);
    memset(videobuf, 0, (width * height) << 2);
    libvlc_video_set_callbacks(vlc_player, lock, unlock, display, nullptr);
    libvlc_video_set_format(vlc_player, "RGBA", width, height, width << 2);

    Texture2D* texture = new Texture2D();
    texture->initWithData(videobuf, sizeof(videobuf), Texture2D::PixelFormat::RGBA8888, width, height, size);
    return initWithTexture(texture);
}

该函数用于初始化视频播放器。具体来说:

  • 调用libvlc_new()函数创建一个新的libvlc实例,并将其赋值给成员变量vlc

  • 使用libvlc_media_player_new()函数创建一个新的libvlc_media_player,并将其赋值给成员变量vlc_player

  • 获取窗口的大小,并将宽度和高度存储在成员变量widthheight中。

  • 使用malloc()函数分配内存给videobuf,大小为(width * height) << 2字节(即每个像素占4字节)。

  • 使用memset()函数将videobuf的内存清零。

  • 使用libvlc_video_set_callbacks()函数设置视频回调函数lockunlockdisplay,以及传递nullptr作为用户数据。

  • 使用libvlc_video_set_format()函数设置视频格式为RGBA,宽度和高度为widthheight,行字节数为width << 2

  • 使用Texture2D类创建一个新的纹理对象texture,并使用initWithData()函数初始化纹理数据,包括videobuf的数据、像素格式为RGBA8888、宽度和高度为widthheight

  • 最后,调用基类initWithTexture()函数初始化VideoPlayer对象的纹理,并返回初始化结果。

该函数的作用是设置视频播放器的初始化状态,包括创建libvlc实例、创建libvlc_media_player、设置视频回调函数、设置视频格式,并初始化纹理对象。


play(path)函数 

void VideoPlayer::play(const char* path)
{
    vlc_list = libvlc_media_list_new(vlc);
    vlc_media = libvlc_media_new_path(vlc, path);
    libvlc_media_list_add_media(vlc_list, vlc_media);

    vlc_list_player = libvlc_media_list_player_new(vlc);
    libvlc_media_list_player_set_media_list(vlc_list_player, vlc_list);
    libvlc_media_list_player_set_media_player(vlc_list_player, vlc_player);
    libvlc_media_list_player_set_playback_mode(vlc_list_player, libvlc_playback_mode_loop);
    libvlc_media_list_player_play(vlc_list_player);
}

该函数用于播放指定路径的视频。具体来说:

  • 使用libvlc_media_list_new()函数创建一个新的libvlc_media_list对象,并将其赋值给成员变量vlc_list

  • 使用libvlc_media_new_path()函数根据提供的路径创建一个新的libvlc_media对象,并将其赋值给成员变量vlc_media

  • 使用libvlc_media_list_add_media()函数将vlc_media添加到vlc_list中。

  • 使用libvlc_media_list_player_new()函数创建一个新的libvlc_media_list_player对象,并将其赋值给成员变量vlc_list_player

  • 使用libvlc_media_list_player_set_media_list()函数将vlc_list设置为vlc_list_player的媒体列表。

  • 使用libvlc_media_list_player_set_media_player()函数将vlc_player设置为vlc_list_player的媒体播放器。

  • 使用libvlc_media_list_player_set_playback_mode()函数设置vlc_list_player的播放模式为循环播放模式。

  • 使用libvlc_media_list_player_play()函数开始播放vlc_list_player中的媒体。

该函数的作用是设置视频播放器的相关参数,并开始播放指定路径的视频。它使用了libvlc库中的函数来实现视频播放的功能。


play()、stop()、pause()函数

void VideoPlayer::play()
{
    libvlc_media_player_play(vlc_player);
}

void VideoPlayer::stop(void)
{
    libvlc_media_player_stop(vlc_player);
}

void VideoPlayer::pause(void)
{
    libvlc_media_player_pause(vlc_player);
}

这些函数用于控制视频播放的操作。具体来说:

  • play()函数调用libvlc_media_player_play()函数,开始播放当前视频。

  • stop()函数调用libvlc_media_player_stop()函数,停止当前视频的播放。

  • pause()函数调用libvlc_media_player_pause()函数,暂停当前视频的播放。

这些函数使用libvlc库中的函数来控制视频播放器的播放、停止和暂停操作。


draw函数 

void VideoPlayer::draw(Renderer* renderer, const Mat4& transform, uint32_t flags)
{
    CC_PROFILER_START_CATEGORY(kCCProfilerCategorySprite, "CCSprite - draw");

    CC_NODE_DRAW_SETUP();

    GL::blendFunc(_blendFunc.src, _blendFunc.dst);

    if (_texture != NULL)
    {
        GL::bindTexture2D(_texture->getName());
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (uint8_t*)videobuf);
    }
    else
    {
        GL::bindTexture2D(static_cast<GLuint>(0));
    }

    //
    // Attributes
    //

    GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);

#define kQuadSize sizeof(_quad.bl)
    long offset = (long)&_quad;

    // vertex
    int diff = offsetof(ccV3F_C4B_T2F, vertices);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));

    // texCoods
    diff = offsetof(ccV3F_C4B_T2F, texCoords);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));

    // color
    diff = offsetof(ccV3F_C4B_T2F, colors);
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    CHECK_GL_ERROR_DEBUG();

    CC_INCREMENT_GL_DRAWS(1);

    CC_PROFILER_STOP_CATEGORY(kCCProfilerCategorySprite, "CCSprite - draw");
}

该函数用于绘制视频帧。具体来说:

  • 使用CC_PROFILER_START_CATEGORY()函数开始性能分析。

  • 使用CC_NODE_DRAW_SETUP()函数设置绘制环境。

  • 使用GL::blendFunc()函数设置混合函数。

  • 如果纹理_texture不为空,则绑定纹理并使用glTexImage2D()函数将视频帧数据写入纹理。

  • 如果纹理_texture为空,则绑定一个空的纹理。

  • 使用GL::enableVertexAttribs()函数启用顶点属性。

  • 计算顶点属性的偏移量,并使用glVertexAttribPointer()函数设置顶点属性的指针。

  • 使用glDrawArrays()函数绘制四边形。

  • 使用CHECK_GL_ERROR_DEBUG()函数检查OpenGL错误。

  • 使用CC_INCREMENT_GL_DRAWS()函数增加绘制计数。

  • 使用CC_PROFILER_STOP_CATEGORY()函数停止性能分析。

该函数的作用是在OpenGL上下文中绘制视频帧。它使用了OpenGL的函数和引擎中的辅助函数来实现绘制操作。


changeVideoState函数 

void VideoPlayer::changeVideoState()
{
    switch (libvlc_media_get_state(vlc_media))
    {
    case libvlc_state_t::libvlc_Playing: pause(); break;
    case libvlc_state_t::libvlc_Paused:  play();  break;
    default: break;
    }
}

该函数用于切换视频的播放状态。具体来说:

  • 使用libvlc_media_get_state()函数获取当前视频的状态。

  • 根据视频的状态进行切换操作:

    • 如果视频正在播放(libvlc_Playing状态),则调用pause()函数暂停视频。
    • 如果视频已暂停(libvlc_Paused状态),则调用play()函数继续播放视频。
    • 对于其他状态,不执行任何操作。

该函数根据当前视频的状态来切换播放状态,从而实现暂停和继续播放的功能。它使用了libvlc库中的函数和VideoPlayer类中的相应函数来控制视频的播放和暂停。


 setVideoTime、getVideoTime函数

void VideoPlayer::setVideoTime(const libvlc_time_t time)
{
    libvlc_media_player_set_time(vlc_player, time);
}

libvlc_time_t VideoPlayer::getVideoTime() const
{
    return libvlc_media_player_get_time(vlc_player);
}

这些函数用于设置和获取视频的播放时间。具体来说:

  • setVideoTime()函数调用libvlc_media_player_set_time()函数,将指定的时间time设置为视频播放的时间。

  • getVideoTime()函数调用libvlc_media_player_get_time()函数,返回当前视频的播放时间。

这些函数使用了libvlc库中的函数来设置和获取视频播放器的时间信息。setVideoTime()函数用于设置视频的播放时间,而getVideoTime()函数用于获取当前视频的播放时间。


setPrecent、 getPrecent函数

void VideoPlayer::setPrecent(const float precent)
{
    video_length < 0 ? video_length = libvlc_media_get_duration(vlc_media) : video_length;
    libvlc_media_player_set_time(vlc_player, static_cast<libvlc_time_t>(video_length * precent));
}

float VideoPlayer::getPrecent()
{
    auto videoCurrentTime = libvlc_media_player_get_time(vlc_player);
    video_length < 0 ? video_length = libvlc_media_get_duration(vlc_media) : video_length;
   
    if (videoCurrentTime != -1 && video_length != -1)
    {
        return videoCurrentTime * 1.0f / video_length * 100;
    }
    return -1;
}

这些函数用于设置和获取视频的播放进度百分比。具体来说:

  • setPrecent()函数根据当前视频的总时长(video_length)和传入的百分比(precent)计算出对应的时间,然后调用libvlc_media_player_set_time()函数将视频的播放时间设置为计算得到的时间。

  • getPrecent()函数获取当前视频的播放时间(videoCurrentTime)和总时长(video_length),然后根据这两个值计算出播放进度的百分比。如果视频的播放时间和总时长都有效,则返回计算得到的进度百分比;否则返回-1。

这些函数使用了libvlc库中的函数来获取视频的总时长和当前播放时间,并通过计算得到播放进度的百分比。setPrecent()函数用于设置视频的播放进度,而getPrecent()函数用于获取当前的播放进度百分比。


setVolume、getVolume 函数

void VideoPlayer::setVolume(int volume)
{
    if (volume < 0) volume = 0;
    if (volume > 100)volume = 100;
    
    libvlc_audio_set_volume(vlc_player, volume);
}

int VideoPlayer::getVolume() const
{
    return libvlc_audio_get_volume(vlc_player);
}

这些函数用于设置和获取视频的音量。具体来说:

  • setVolume()函数用于设置视频的音量。它首先对音量进行范围检查,确保音量值在0到100之间。然后,使用libvlc_audio_set_volume()函数将音量设置为指定的值。

  • getVolume()函数用于获取当前视频的音量。它调用libvlc_audio_get_volume()函数来获取当前音量值,并将其返回。

这些函数使用了libvlc库中的函数来设置和获取视频播放器的音量信息。setVolume()函数用于设置视频的音量,而getVolume()函数用于获取当前视频的音量。

上一篇:Linux安装python3


下一篇:实现联系人前后端界面,实现分页查询04.15