Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

18 changes: 6 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
cmake_minimum_required(VERSION 3.3.0)
cmake_minimum_required(VERSION 3.21)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64")

project(VideoPlayer VERSION 1.0.0)

Expand All @@ -10,10 +11,7 @@ file(GLOB SOURCES

add_library(${PROJECT_NAME} SHARED ${SOURCES})

target_include_directories(${PROJECT_NAME} PUBLIC
include
pl_mpeg
)
target_include_directories(${PROJECT_NAME} PUBLIC include)

if (PROJECT_IS_TOP_LEVEL)
target_compile_definitions(${PROJECT_NAME} PRIVATE _VIDEO_PLAYER_EXPORTING)
Expand All @@ -27,12 +25,8 @@ endif()

add_subdirectory($ENV{GEODE_SDK} $ENV{GEODE_SDK}/build)

if(WIN32)
target_link_libraries(${PROJECT_NAME} opengl32)
elseif(APPLE)
target_link_libraries(${PROJECT_NAME} "-framework OpenGL")
elseif(ANDROID)
target_link_libraries(${PROJECT_NAME} GLESv2)
endif()
CPMAddPackage("gh:phoboslab/pl_mpeg#e11f014")

target_include_directories(${PROJECT_NAME} PUBLIC ${pl_mpeg_SOURCE_DIR})

setup_geode_mod(${PROJECT_NAME})
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# VideoPlayer

Provides an API for playing mpeg (.mpg) videos within the Geode SDK

## Usage

```cpp
#include <fig.video_player/include/VideoPlayer.hpp>

auto* node = videoplayer::VideoPlayer::create(Mod::get()->getResourcesDir() / "video.mpg");
addChild(node);
```
47 changes: 17 additions & 30 deletions include/VideoPlayer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@
#define _VIDEO_PLAYER_HPP

#include <Geode/Bindings.hpp>
#include <Geode/cocos/platform/CCGL.h>

#pragma warning(disable:4996)
#include "../pl_mpeg/pl_mpeg.h"
#pragma warning(default:4996)

#include <queue>

#ifdef GEODE_IS_WINDOWS
Expand All @@ -20,33 +14,14 @@
#define VIDEO_PLAYER_DLL
#endif

typedef unsigned int GLuint;


namespace videoplayer {
class VIDEO_PLAYER_DLL VideoPlayer : public cocos2d::CCNodeRGBA {
protected:
bool init(ghc::filesystem::path const& path, bool loop);

void initAudio();
static FMOD_RESULT F_CALLBACK audioCallback(FMOD_CHANNELCONTROL *chanControl, FMOD_CHANNELCONTROL_TYPE controlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void *commandData1, void *commandData2);

virtual void update(float delta) override;
virtual void draw() override;

virtual ~VideoPlayer();
virtual void onExit() override;

static void videoCallback(plm_t* mpeg, plm_frame_t* frame, void* user);
static void audioCallback(plm_t* mpeg, plm_samples_t* samples, void* user);

static FMOD_RESULT F_CALLBACK PCMRead(FMOD_SOUND *sound, void *data, unsigned int length);

ghc::filesystem::path m_path;
plm_t* m_stream;
struct Impl;
std::unique_ptr<Impl> m_impl;
friend struct Impl;

cocos2d::CCSize m_dimensions;
GLuint m_textures[3]; // y, cb, cr
unsigned int m_textures[3]; // y, cb, cr
std::queue<float> m_samples;

FMOD::Channel* m_channel;
Expand All @@ -56,6 +31,18 @@ namespace videoplayer {
bool m_loop;
bool m_stopped;
float m_volume = 1.0f;

protected:
bool init(std::filesystem::path const& path, bool loop);

void initAudio();

virtual void update(float delta) override;
virtual void draw() override;

VideoPlayer();
virtual ~VideoPlayer();
virtual void onExit() override;
public:

/**
Expand All @@ -65,7 +52,7 @@ namespace videoplayer {
* @param loop Whether or not playback should loop upon completion.
* @return A new initialized video player
*/
static VideoPlayer* create(ghc::filesystem::path const& path, bool loop=false);
static VideoPlayer* create(std::filesystem::path const& path, bool loop=false);

/**
* @brief Sets the content height of the video player, maintaining aspect ratio
Expand Down
12 changes: 8 additions & 4 deletions mod.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
{
"geode": "1.0.0",
"version": "v1.0.1",
"geode": "4.1.0",
"gd": {
"win": "2.2074",
"mac": "2.2074",
"android": "2.2074"
},
"version": "v1.1.0",
"id": "fig.video_player",
"name": "Video Player",
"developer": "fig",
"description": "Provides an API for playing mpeg (.mpg) videos",
"api": {
"include": [
"include/*.hpp",
"pl_mpeg/pl_mpeg.h"
"include/*.hpp"
]
}
}
1 change: 0 additions & 1 deletion pl_mpeg
Submodule pl_mpeg deleted from 73e509
61 changes: 36 additions & 25 deletions src/VideoPlayer.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// https://github.com/phoboslab/pl_mpeg/blob/master/pl_mpeg_player.c
// https://katyscode.wordpress.com/2013/02/28/cutting-your-teeth-on-fmod-part-5-real-time-streaming-of-programmatically-generated-audio/
#define PL_MPEG_IMPLEMENTATION
#define _CRT_SECURE_NO_WARNINGS 1
#include <pl_mpeg.h>
#include "VideoPlayer.hpp"
#include <math.h>

Expand Down Expand Up @@ -43,26 +45,38 @@ const char* APP_FRAGMENT_SHADER_YCRCB = APP_SHADER_SOURCE(
);

namespace videoplayer {
bool VideoPlayer::init(ghc::filesystem::path const& path, bool loop) {
struct VideoPlayer::Impl {
plm_t* m_stream = nullptr;

static void videoCallback(plm_t* mpeg, plm_frame_t* frame, void* user);
static void audioCallback(plm_t* mpeg, plm_samples_t* samples, void* user);
static FMOD_RESULT F_CALLBACK audioCallback(FMOD_CHANNELCONTROL *chanControl, FMOD_CHANNELCONTROL_TYPE controlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void *commandData1, void *commandData2);
static FMOD_RESULT F_CALLBACK PCMRead(FMOD_SOUND *sound, void *data, unsigned int length);
};

VideoPlayer::VideoPlayer() {
m_impl = std::make_unique<Impl>();
}

bool VideoPlayer::init(std::filesystem::path const& path, bool loop) {
if (!CCNode::init()) return false;

// GENERAL
m_path = path;
m_stream = plm_create_with_filename(m_path.string().c_str());
auto* stream = m_impl->m_stream = plm_create_with_filename(path.string().c_str());

if (!m_stream) {
log::error("File at " + m_path.string() + " not found.");
if (!stream) {
log::error("File at {} not found.", path);
return false;
};

plm_set_loop(m_stream, loop);
plm_set_loop(stream, loop);
m_loop = loop;

plm_set_video_decode_callback(m_stream, VideoPlayer::videoCallback, this);
plm_set_audio_decode_callback(m_stream, VideoPlayer::audioCallback, this);
plm_set_video_decode_callback(stream, Impl::videoCallback, this);
plm_set_audio_decode_callback(stream, Impl::audioCallback, this);

// VIDEO
m_dimensions = CCSize(m_stream->video_decoder->mb_width, m_stream->video_decoder->mb_height);
m_dimensions = CCSize(stream->video_decoder->mb_width, stream->video_decoder->mb_height);

CCGLProgram* shader = new CCGLProgram;

Expand All @@ -77,7 +91,7 @@ namespace videoplayer {

const char* texture_names[3] = {"texture_y", "texture_cb", "texture_cr"};

plm_frame_t* frame = &m_stream->video_decoder->frame_current;
plm_frame_t* frame = &stream->video_decoder->frame_current;
plm_plane_t planes[3] = {frame->y, frame->cb, frame->cr};

for (int i = 0; i < 3; i++) {
Expand Down Expand Up @@ -112,33 +126,31 @@ namespace videoplayer {
void VideoPlayer::initAudio() {
FMODAudioEngine* engine = FMODAudioEngine::sharedEngine();

int sampleRate = plm_get_samplerate(m_stream);
auto* stream = m_impl->m_stream;
int sampleRate = plm_get_samplerate(stream);

FMOD_CREATESOUNDEXINFO soundInfo;
memset(&soundInfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
soundInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
soundInfo.decodebuffersize = PLM_AUDIO_SAMPLES_PER_FRAME * 2;
soundInfo.length = sampleRate * 2 * sizeof(float) * plm_get_duration(m_stream);
soundInfo.length = sampleRate * 2 * sizeof(float) * plm_get_duration(stream);
soundInfo.numchannels = 2;
soundInfo.defaultfrequency = sampleRate;
soundInfo.format = FMOD_SOUND_FORMAT_PCMFLOAT;
soundInfo.pcmreadcallback = &VideoPlayer::PCMRead;
soundInfo.pcmreadcallback = &VideoPlayer::Impl::PCMRead;
soundInfo.userdata = this;

m_samples = {};
engine->m_system->createStream(nullptr, FMOD_OPENUSER, &soundInfo, &m_sound);

FMOD::ChannelGroup* group;
engine->m_globalChannel->getChannelGroup(&group);

engine->m_system->playSound(m_sound, group, false, &m_channel);
engine->m_system->playSound(m_sound, engine->m_globalChannel, false, &m_channel);
m_channel->setVolume(m_volume);

m_channel->setUserData(this);
if (m_loop) m_channel->setCallback(&VideoPlayer::audioCallback);
if (m_loop) m_channel->setCallback(&VideoPlayer::Impl::audioCallback);
}

FMOD_RESULT F_CALLBACK VideoPlayer::audioCallback(FMOD_CHANNELCONTROL *chanControl, FMOD_CHANNELCONTROL_TYPE controlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void *commandData1, void *commandData2) {
FMOD_RESULT F_CALLBACK VideoPlayer::Impl::audioCallback(FMOD_CHANNELCONTROL *chanControl, FMOD_CHANNELCONTROL_TYPE controlType, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbackType, void *commandData1, void *commandData2) {
if (callbackType != FMOD_CHANNELCONTROL_CALLBACK_END) return FMOD_OK;

VideoPlayer* self;
Expand All @@ -152,9 +164,8 @@ namespace videoplayer {
return FMOD_OK;
}

static int times = 0;
void VideoPlayer::update(float delta) {
if (!m_paused) plm_decode(m_stream, delta);
if (!m_paused) plm_decode(m_impl->m_stream, delta);
}

void VideoPlayer::draw() {
Expand Down Expand Up @@ -191,7 +202,7 @@ namespace videoplayer {
onExit();
}

void VideoPlayer::videoCallback(plm_t* mpeg, plm_frame_t* frame, void* user) {
void VideoPlayer::Impl::videoCallback(plm_t* mpeg, plm_frame_t* frame, void* user) {
VideoPlayer* self = (VideoPlayer*) user;

plm_plane_t* frames[3] = {&frame->y, &frame->cb, &frame->cr};
Expand All @@ -209,7 +220,7 @@ namespace videoplayer {
}
}

void VideoPlayer::audioCallback(plm_t* mpeg, plm_samples_t* samples, void* user) {
void VideoPlayer::Impl::audioCallback(plm_t* mpeg, plm_samples_t* samples, void* user) {
VideoPlayer* self = (VideoPlayer*) user;

for (unsigned int i = 0; i < samples->count * 2; i++) {
Expand All @@ -221,7 +232,7 @@ namespace videoplayer {
}
}

FMOD_RESULT F_CALLBACK VideoPlayer::PCMRead(FMOD_SOUND *sound, void *data, unsigned int length) {
FMOD_RESULT F_CALLBACK VideoPlayer::Impl::PCMRead(FMOD_SOUND *sound, void *data, unsigned int length) {
VideoPlayer* self;
((FMOD::Sound*)sound)->getUserData((void**)&self);
if (!self) return FMOD_OK;
Expand All @@ -239,7 +250,7 @@ namespace videoplayer {
return FMOD_OK;
}

VideoPlayer* VideoPlayer::create(ghc::filesystem::path const& path, bool loop) {
VideoPlayer* VideoPlayer::create(std::filesystem::path const& path, bool loop) {
VideoPlayer* ret = new VideoPlayer;
if (ret && ret->init(path, loop)) {
ret->autorelease();
Expand Down