Skip to content

Commit

Permalink
Add gdextension build
Browse files Browse the repository at this point in the history
  • Loading branch information
EIREXE committed Jul 24, 2023
1 parent 5b63ba4 commit 5d80278
Show file tree
Hide file tree
Showing 22 changed files with 294,634 additions and 94 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "gdextension_build/godot-cpp"]
path = gdextension_build/godot-cpp
url = https://github.com/godotengine/godot-cpp.git
42 changes: 25 additions & 17 deletions SCsub
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
#!/usr/bin/env python

import ffmpeg_download
Import("env")
from gdextension_build import ffmpeg_download
from gdextension_build import methods
from pathlib import Path
import os

FFMPEG_DOWNLOAD_WINDOWS = "https://github.com/EIRTeam/FFmpeg-Builds/releases/download/autobuild-2023-07-23-11-40/ffmpeg-N-111601-g89f5124d0a-win64-lgpl-godot.zip"
Import("env")

if not "RPATH" in env:
env.Append(RPATH=env.Literal('\\$$ORIGIN'))
env.Append(RPATH=env.Literal("\\$$ORIGIN"))

env_ffmpeg = env.Clone()

env_ffmpeg.Depends("thirdparty/ffmpeg/", "gdextension_build/ffmpeg_download.py")
env_ffmpeg.CommandNoCache(
"thirdparty/ffmpeg/",
"ffmpeg_download.py",
"gdextension_build/ffmpeg_download.py",
env_ffmpeg.Run(ffmpeg_download.download_ffmpeg, "Downloading FFMPEG library"),
)

Expand All @@ -24,28 +27,33 @@ sources = [x for x in Glob("*.cpp") if str(x) not in excluded]

env_ffmpeg.Prepend(CPPPATH="thirdparty/ffmpeg/include")
env.Append(LIBPATH="/home/eirexe/repos/godot-ph4/modules/ffmpeg/thirdparty/ffmpeg/lib")
env.Append(LIBS=[
"avcodec",
"avfilter",
"avformat",
"avutil",
"swresample",
"swscale"
])
ffmpeg_libs = ["avcodec", "avfilter", "avformat", "avutil", "swresample", "swscale"]
env.Append(LIBS=ffmpeg_libs)

ffmpeg_libs_to_install = []

if env["platform"] == "linuxbsd":
for lib in ffmpeg_libs:
# Make sure we copy the ffmpeg library with the correct version extension
lib_file_name = f"lib{lib}.so"
lib_so_path = f"thirdparty/ffmpeg/lib/{lib_file_name}"
if os.path.isfile(lib_so_path):
lib_soname = methods.get_soname(lib_so_path)
if lib_soname:
ffmpeg_libs_to_install.append(f"thirdparty/ffmpeg/lib/{lib_soname}")

env.Install("#bin", Glob("thirdparty/ffmpeg/lib/*.so"))
env_ffmpeg.Install("#bin", ffmpeg_libs_to_install)

#env_ffmpeg.Append(CPPDEFINES=["FFMPEG_MT_GPU_UPLOAD"])
# env_ffmpeg.Append(CPPDEFINES=["FFMPEG_MT_GPU_UPLOAD"])
if ARGUMENTS.get("ffmpeg_shared", "no") == "yes":
# Shared lib compilation
env_ffmpeg.Append(CCFLAGS=["-fPIC"])
env_ffmpeg["LIBS"] = FFMPEG_LIBS
shared_lib = env_ffmpeg.SharedLibrary(target="#bin/ffmpeg", source=sources)
shared_lib_shim = shared_lib[0].name.rsplit(".", 1)[0]
env.Append(LIBPATH=["#bin"])
env.Append(LIBS=["libffmpeg.linuxbsd.editor.dev.x86_64"])
env.Append(LIBS=[shared_lib_shim])
else:
env_ffmpeg.add_source_files(module_obj, sources)
env.Depends(module_obj, "thirdparty/ffmpeg/")
env.modules_sources += module_obj
env.modules_sources += module_obj
52 changes: 34 additions & 18 deletions et_video_stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
/**************************************************************************/

#include "et_video_stream.h"
#include "ffmpeg_frame.h"

#ifdef GDEXTENSION
#include "gdextension_build/gdex_print.h"
#endif

void ETVideoStreamPlayback::seek_into_sync() {
decoder->seek(playback_position);
Expand All @@ -56,7 +59,7 @@ bool ETVideoStreamPlayback::check_next_frame_valid(Ref<DecodedFrame> p_decoded_f
return p_decoded_frame->get_time() <= playback_position && Math::abs(p_decoded_frame->get_time() - playback_position) < LENIENCE_BEFORE_SEEK;
}

void ETVideoStreamPlayback::update(double p_delta) {
void ETVideoStreamPlayback::update_internal(double p_delta) {
if (paused || !playing) {
return;
}
Expand Down Expand Up @@ -120,10 +123,17 @@ void ETVideoStreamPlayback::update(double p_delta) {
}
}

Vector<float> audio_frames = decoder->get_decoded_audio_frames();
PackedFloat32Array audio_frames = decoder->get_decoded_audio_frames();

#ifdef GDEXTENSION
if (audio_frames.size() > 0) {
mix_audio(audio_frames.size() / decoder->get_audio_channel_count(), audio_frames, 0);
}
#else
if (mix_callback && audio_frames.size() > 0) {
mix_callback(mix_udata, audio_frames.ptr(), audio_frames.size() / decoder->get_audio_channel_count());
}
#endif

buffering = decoder->is_running() && available_frames.size() == 0;

Expand All @@ -137,63 +147,69 @@ void ETVideoStreamPlayback::load(Ref<FileAccess> p_file_access) {

decoder->start_decoding();
Vector2i size = decoder->get_size();
texture = ImageTexture::create_from_image(Image::create_empty(size.x, size.y, false, Image::FORMAT_RGBA8));
if (decoder->get_decoder_state() != VideoDecoder::FAULTED) {
#ifdef GDEXTENSION
texture = ImageTexture::create_from_image(Image::create(size.x, size.y, false, Image::FORMAT_RGBA8));
#else
texture = ImageTexture::create_from_image(Image::create_empty(size.x, size.y, false, Image::FORMAT_RGBA8));
#endif
}
}

bool ETVideoStreamPlayback::is_paused() const {
bool ETVideoStreamPlayback::is_paused_internal() const {
return paused;
}

bool ETVideoStreamPlayback::is_playing() const {
bool ETVideoStreamPlayback::is_playing_internal() const {
return playing;
}

void ETVideoStreamPlayback::set_paused(bool p_paused) {
void ETVideoStreamPlayback::set_paused_internal(bool p_paused) {
paused = p_paused;
}

void ETVideoStreamPlayback::play() {
void ETVideoStreamPlayback::play_internal() {
if (!playing) {
playback_position = 0;
} else {
stop();
stop_internal();
}
playing = true;
}

void ETVideoStreamPlayback::stop() {
void ETVideoStreamPlayback::stop_internal() {
if (playing) {
clear();
seek(0);
seek_internal(0);
}
playing = false;
}

void ETVideoStreamPlayback::seek(double p_time) {
seek(p_time);
void ETVideoStreamPlayback::seek_internal(double p_time) {
decoder->seek(p_time);
}

double ETVideoStreamPlayback::get_length() const {
double ETVideoStreamPlayback::get_length_internal() const {
return decoder->get_duration();
}

Ref<Texture2D> ETVideoStreamPlayback::get_texture() const {
Ref<Texture2D> ETVideoStreamPlayback::get_texture_internal() const {
#ifdef FFMPEG_MT_GPU_UPLOAD
return last_frame_texture;
#else
return texture;
#endif
}

double ETVideoStreamPlayback::get_playback_position() const {
double ETVideoStreamPlayback::get_playback_position_internal() const {
return playback_position;
}

int ETVideoStreamPlayback::get_mix_rate() const {
int ETVideoStreamPlayback::get_mix_rate_internal() const {
return decoder->get_audio_mix_rate();
}

int ETVideoStreamPlayback::get_channels() const {
int ETVideoStreamPlayback::get_channels_internal() const {
return decoder->get_audio_channel_count();
}

Expand Down
92 changes: 75 additions & 17 deletions et_video_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,47 @@
#ifndef ET_VIDEO_STREAM_H
#define ET_VIDEO_STREAM_H

#ifdef GDEXTENSION

// Headers for building as GDExtension plug-in.
#include <godot_cpp/classes/video_stream.hpp>
#include <godot_cpp/classes/video_stream_playback.hpp>
#include <godot_cpp/godot.hpp>
#include <godot_cpp/templates/list.hpp>
#include <godot_cpp/templates/vector.hpp>

using namespace godot;

#else

#include "core/object/ref_counted.h"
#include "core/templates/local_vector.h"
#include "ffmpeg_frame.h"
#include "scene/resources/video_stream.h"

#endif

#include "video_decoder.h"

// We have to use this function redirection system for GDExtension because the naming conventions
// for the functions we are supposed to override are different there

#ifdef GDEXTENSION
#define STREAM_FUNCNAME(funcname) _##funcname
#else
#define STREAM_FUNCNAME(funcname) funcname
#endif

#define STREAM_FUNC_REDIRECT_0_(retval, funcname, const) \
virtual retval STREAM_FUNCNAME(funcname)() const override { return funcname##_internal(); };

#define STREAM_FUNC_REDIRECT_1_(retval, funcname, const, arg0type, arg0name) \
virtual retval STREAM_FUNCNAME(funcname)(arg0type arg0name) const override { return funcname##_internal(arg0name); };

#define STREAM_FUNC_REDIRECT_0(retval, funcname) STREAM_FUNC_REDIRECT_0_(retval, funcname, );
#define STREAM_FUNC_REDIRECT_0_CONST(retval, funcname) STREAM_FUNC_REDIRECT_0_(retval, funcname, const);

#define STREAM_FUNC_REDIRECT_1(retval, funcname, arg0type, arg0name) STREAM_FUNC_REDIRECT_1_(retval, funcname, , arg0type, arg0name);
#define STREAM_FUNC_REDIRECT_1_CONST(retval, funcname, arg0type, arg0name) STREAM_FUNC_REDIRECT_1_(retval, funcname, const, arg0type, arg0name);

class ETVideoStreamPlayback : public VideoStreamPlayback {
GDCLASS(ETVideoStreamPlayback, VideoStreamPlayback);
const int LENIENCE_BEFORE_SEEK = 2500;
Expand All @@ -59,37 +94,60 @@ class ETVideoStreamPlayback : public VideoStreamPlayback {
bool paused = false;
bool playing = false;

private:
bool is_paused_internal() const;
void update_internal(double p_delta);
bool is_playing_internal() const;
void set_paused_internal(bool p_paused);
void play_internal();
void stop_internal();
void seek_internal(double p_time);
double get_length_internal() const;
Ref<Texture2D> get_texture_internal() const;
double get_playback_position_internal() const;
int get_mix_rate_internal() const;
int get_channels_internal() const;

protected:
void clear();
static void _bind_methods(){}; // Required by GDExtension, do not remove

public:
void update(double p_delta) override;
void load(Ref<FileAccess> p_file_access);
virtual bool is_paused() const override;
virtual bool is_playing() const override;
virtual void set_paused(bool p_paused) override;
virtual void play() override;
virtual void stop() override;
virtual void seek(double p_time) override;
virtual double get_length() const override;
virtual Ref<Texture2D> get_texture() const override;
virtual double get_playback_position() const override;
virtual int get_mix_rate() const override;
virtual int get_channels() const override;

STREAM_FUNC_REDIRECT_0_CONST(bool, is_paused);
STREAM_FUNC_REDIRECT_1(void, update, double, p_delta);
STREAM_FUNC_REDIRECT_0_CONST(bool, is_playing);
STREAM_FUNC_REDIRECT_1(void, set_paused, bool, p_paused);
STREAM_FUNC_REDIRECT_0(void, play);
STREAM_FUNC_REDIRECT_0(void, stop);
STREAM_FUNC_REDIRECT_1(void, seek, double, p_time);
STREAM_FUNC_REDIRECT_0_CONST(double, get_length);
STREAM_FUNC_REDIRECT_0_CONST(Ref<Texture2D>, get_texture);
STREAM_FUNC_REDIRECT_0_CONST(double, get_playback_position);
STREAM_FUNC_REDIRECT_0_CONST(int, get_mix_rate);
STREAM_FUNC_REDIRECT_0_CONST(int, get_channels);
ETVideoStreamPlayback();
};

class ETVideoStream : public VideoStream {
GDCLASS(ETVideoStream, VideoStream);

public:
virtual Ref<VideoStreamPlayback> instantiate_playback() override {
protected:
static void _bind_methods(){}; // Required by GDExtension, do not remove
Ref<VideoStreamPlayback> instantiate_playback_internal() {
Ref<FileAccess> fa = FileAccess::open(get_file(), FileAccess::READ);
if (!fa.is_valid()) {
return Ref<VideoStreamPlayback>();
}
Ref<ETVideoStreamPlayback> pb;
pb.instantiate();
Ref<FileAccess> fa = FileAccess::open(file, FileAccess::READ);
pb->load(fa);
return pb;
}

public:
STREAM_FUNC_REDIRECT_0(Ref<VideoStreamPlayback>, instantiate_playback);
};

#endif // ET_VIDEO_STREAM_H
14 changes: 14 additions & 0 deletions ffmpeg_codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,23 @@
extern "C" {
#include "libavcodec/avcodec.h"
}

#ifdef GDEXTENSION

// Headers for building as GDExtension plug-in.
#include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/godot.hpp>
#include <godot_cpp/templates/vector.hpp>

using namespace godot;

#else

#include "core/object/ref_counted.h"
#include "core/templates/vector.h"

#endif

class FFmpegCodec : public RefCounted {
const AVCodec *codec = nullptr;
bool has_cached_hw_device_types = false;
Expand Down
15 changes: 9 additions & 6 deletions ffmpeg_frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,23 @@ AVFrame *FFmpegFrame::get_frame() const {
}

void FFmpegFrame::do_return() {
if (return_func.is_valid()) {
Array arr;
arr.push_back(this);
return_func.callv(arr);
if (return_func && return_instance.is_valid()) {
return_func(return_instance, this);
}
}

FFmpegFrame::FFmpegFrame(Callable p_return_func) {
if (p_return_func.is_valid()) {
FFmpegFrame::FFmpegFrame(Ref<RefCounted> p_return_func_instance, return_frame_callback_t p_return_func) {
if (p_return_func) {
return_func = p_return_func;
return_instance = p_return_func_instance;
}
frame = av_frame_alloc();
}

FFmpegFrame::FFmpegFrame() {
frame = av_frame_alloc();
}

FFmpegFrame::~FFmpegFrame() {
av_frame_free(&frame);
}
Loading

0 comments on commit 5d80278

Please sign in to comment.