由于项目需要用到动态的背景,测试发现,如果用帧序列动画将占用超过1G的内存,而CPU的利用率则一直保持在5%左右,所以想到将动态效果做成视频,循环播放,以作为背景之用。查询之下发现cocos2d-x本身带有一个叫做VideoPlayer的类,但是很可惜,它们只能用在移动平台上,而笔者的项目是基于Windows平台的,所以便在网上搜索实现方法,最终利用VLC Player实现了所需的效果。
首先需要下载VLC Player播放器,安装之后可以在安装目录下的SDK文件夹内找到头文件、lib、dll。
其实原理也很简单,就是利用VLC从视频中提取数据流,然后打入buffer中,然后以buffer作为纹理绘制整个画面,代码如下:
最后说明一下,可以看到有一些函数以"o_"为开头,怎么会这么奇怪呢~~笔者之前没有加这个前缀,结果resume函数跟Node类的resume函数重名了,根据类的继承规则,Node的resume函数被"遮挡"住了,导致该节点没办法正常的恢复动作,比如,笔者在init方法里调用了scheduleUpdate方法,结果update方法却一直不被调用,查来查去,才发现是Node的resume方法被"遮挡"了,节点没有被正确的恢复。
首先需要下载VLC Player播放器,安装之后可以在安装目录下的SDK文件夹内找到头文件、lib、dll。
其实原理也很简单,就是利用VLC从视频中提取数据流,然后打入buffer中,然后以buffer作为纹理绘制整个画面,代码如下:
#include "VLCPlayer.h"
USING_NS_CC;
void *lock(void *data, void **p_pixels)
{
auto player = static_cast
(data);
*p_pixels = player->m_videobuf;
return NULL;
}
void unlock(void *data, void *id, void *const *p_pixels)
{
assert(id == NULL);
}
void display(void *data, void *id)
{
auto player = static_cast
(data);
player->m_readyToShow = true;
assert(id == NULL);
}
void endReached(const struct libvlc_event_t *event, void *data)
{
if (libvlc_MediaPlayerEndReached == event->type)
{
VLCPlayer *self = (VLCPlayer *)data;
self->m_isEndReached = true;
}
}
VLCPlayer::~VLCPlayer()
{
libvlc_media_player_stop(vlc_player);
libvlc_media_player_release(vlc_player);
libvlc_release(vlc);
free(m_videobuf);
}
VLCPlayer* VLCPlayer::create(Size size)
{
auto player = new VLCPlayer;
if (player && player->init(size)) {
player->autorelease();
} else {
CC_SAFE_DELETE(player);
}
return player;
}
bool VLCPlayer::init(Size &size)
{
vlc = libvlc_new(0, NULL);
vlc_player = libvlc_media_player_new(vlc);
width = size.width;
height = size.height;
m_videobuf = (char *)malloc((width * height) << 2);
memset(m_videobuf, 0, (width * height) << 2);
libvlc_video_set_callbacks(vlc_player, lock, unlock, display, this);
libvlc_video_set_format(vlc_player, "RGBA", width, height, width << 2);
libvlc_event_attach(libvlc_media_player_event_manager(vlc_player),
libvlc_MediaPlayerEndReached,
endReached,
(void *)this);
Texture2D *texture = new Texture2D();
texture->initWithData(m_videobuf,
(width * height) << 2,
Texture2D::PixelFormat::RGBA8888,
width, height, size);
texture->autorelease();
initWithTexture(texture);
scheduleUpdate();
return true;
}
void VLCPlayer::o_play(std::string &path, bool repeat)
{
m_isEndReached = false;
m_curMedia = path;
m_repeat = repeat;
m_readyToShow = false;
libvlc_media_t *media = libvlc_media_new_path(vlc, path.c_str());
libvlc_media_player_set_media(vlc_player, media);
libvlc_media_release(media);
libvlc_media_player_play(vlc_player);
}
void VLCPlayer::o_stop(void) { libvlc_media_player_stop(vlc_player); }
void VLCPlayer::o_resume(void) { libvlc_media_player_set_pause(vlc_player, 0); }
void VLCPlayer::o_pause(void) { libvlc_media_player_pause(vlc_player); }
bool VLCPlayer::isPlaying(void) { return (1 == libvlc_media_player_is_playing(vlc_player)) ? true : false; }
bool VLCPlayer::isEndReached() { return m_isEndReached; }
void VLCPlayer::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
_insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
if(_insideBounds)
{
if (m_readyToShow) {
m_readyToShow = false;
Texture2D *texture = new Texture2D();
texture->initWithData(m_videobuf,
(width * height) << 2,
Texture2D::PixelFormat::RGBA8888,
width, height, Size(width, height));
texture->autorelease();
setTexture(texture);
}
_quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(), _blendFunc, &_quad, 1, transform);
renderer->addCommand(&_quadCommand);
#if CC_SPRITE_DEBUG_DRAW
_debugDrawNode->clear();
Vec2 vertices[4] = {
Vec2( _quad.bl.vertices.x, _quad.bl.vertices.y ),
Vec2( _quad.br.vertices.x, _quad.br.vertices.y ),
Vec2( _quad.tr.vertices.x, _quad.tr.vertices.y ),
Vec2( _quad.tl.vertices.x, _quad.tl.vertices.y ),
};
_debugDrawNode->drawPoly(vertices, 4, true, Color4F(1.0, 1.0, 1.0, 1.0));
#endif //CC_SPRITE_DEBUG_DRAW
}
}
void VLCPlayer::update(float dt)
{
if (m_repeat && isEndReached()) {
o_play(m_curMedia);
}
}
#ifndef __MOVIEPLAYER_H__
#define __MOVIEPLAYER_H__
#include "vlc/vlc.h"
#include "cocos2d.h"
class VLCPlayer : public cocos2d::Sprite
{
public:
~VLCPlayer();
static VLCPlayer* create(cocos2d::Size size);
bool init(cocos2d::Size &size);
void o_play(std::string &path, bool repeat = true);
inline void o_stop(void);
inline void o_pause(void);
inline void o_resume(void);
inline bool isPlaying(void);
inline bool isEndReached();
virtual void draw(cocos2d::Renderer *renderer, const cocos2d::Mat4 &transform, uint32_t flags) override;
virtual void update(float dt) override;
friend void endReached(const struct libvlc_event_t *event, void *data);
friend void *lock(void *data, void **p_pixels);
friend void unlock(void *data, void *id, void *const *p_pixels);
friend void display(void *data, void *id);
private:
libvlc_instance_t *vlc;
libvlc_media_player_t *vlc_player;
unsigned int width;
unsigned int height
char *m_videobuf;
bool m_isEndReached;
std::string m_curMedia;
bool m_repeat;
bool m_readyToShow;
};
#endif
当然,还有一种办法就是在VLC类的resume方法里调用Node的resume方法,不过这样做可能导致一些不灵活的情况,所以笔者最终选择了改变函数名称。