From 5786516d4d6b517e6a28aec191f944aecf2f544d Mon Sep 17 00:00:00 2001 From: reduz Date: Fri, 17 Jun 2022 00:55:19 +0200 Subject: Implement Running Godot as Movie Writer * Allows running the game in "movie writer" mode. * It ensures entirely stable framerate, so your run can be saved stable and with proper sound (which is impossible if your CPU/GPU can't sustain doing this in real-time). * If disabling vsync, it can save movies faster than the game is run, but if you want to control the interaction it can get difficult. * Implements a simple, default MJPEG writer. This new features has two main use cases, which have high demand: * Saving game videos in high quality and ensuring the frame rate is *completely* stable, always. * Using Godot as a tool to make movies and animations (which is ideal if you want interaction, or creating them procedurally. No other software is as good for this). **Note**: This feature **IS NOT** for capturing real-time footage. Use something like OBS, SimpleScreenRecorder or FRAPS to achieve that, as they do a much better job at intercepting the compositor than Godot can probably do using Vulkan or OpenGL natively. If your game runs near real-time when capturing, you can still use this feature but it will play no sound (sound will be saved directly). Usage: $ godot --write-movie movie.avi [scene_file.tscn] Missing: * Options for configuring video writing via GLOBAL_DEF * UI Menu for launching with this mode from the editor. * Add to list of command line options. * Add a feature tag to override configurations when movie writing (fantastic for saving videos with highest quality settings). --- modules/jpg/SCsub | 1 + modules/jpg/image_loader_jpegd.cpp | 56 +++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) (limited to 'modules/jpg') diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub index 7c6ceeea29..b840542c1b 100644 --- a/modules/jpg/SCsub +++ b/modules/jpg/SCsub @@ -13,6 +13,7 @@ thirdparty_obj = [] thirdparty_dir = "#thirdparty/jpeg-compressor/" thirdparty_sources = [ "jpgd.cpp", + "jpge.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp index 51358876a4..0e03fa65c8 100644 --- a/modules/jpg/image_loader_jpegd.cpp +++ b/modules/jpg/image_loader_jpegd.cpp @@ -33,7 +33,8 @@ #include "core/os/os.h" #include "core/string/print_string.h" -#include +#include "thirdparty/jpeg-compressor/jpgd.h" +#include "thirdparty/jpeg-compressor/jpge.h" #include Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) { @@ -131,6 +132,59 @@ static Ref _jpegd_mem_loader_func(const uint8_t *p_png, int p_size) { return img; } +static Error _jpgd_save_func(const String &p_path, const Ref &p_img, float p_quality) { + return OK; +} + +class ImageLoaderJPGOSFile : public jpge::output_stream { +public: + Ref f; + + virtual bool put_buf(const void *Pbuf, int len) { + f->store_buffer((const uint8_t *)Pbuf, len); + return true; + } +}; + +class ImageLoaderJPGOSBuffer : public jpge::output_stream { +public: + Vector *buffer = nullptr; + virtual bool put_buf(const void *Pbuf, int len) { + uint32_t base = buffer->size(); + buffer->resize(base + len); + memcpy(buffer->ptrw() + base, Pbuf, len); + return true; + } +}; + +static Vector _jpgd_buffer_save_func(const Ref &p_img, float p_quality) { + ERR_FAIL_COND_V(p_img.is_null() || p_img->is_empty(), Vector()); + Ref image = p_img; + if (image->get_format() != Image::FORMAT_RGB8) { + image->convert(Image::FORMAT_ETC2_RGB8); + } + + jpge::params p; + p.m_quality = CLAMP(p_quality * 100, 1, 100); + Vector output; + ImageLoaderJPGOSBuffer ob; + ob.buffer = &output; + + jpge::jpeg_encoder enc; + enc.init(&ob, image->get_width(), image->get_height(), 3, p); + + const uint8_t *src_data = image->get_data().ptr(); + for (int i = 0; i < image->get_height(); i++) { + enc.process_scanline(&src_data[i * image->get_width() * 3]); + } + + enc.process_scanline(nullptr); + + return output; +} + ImageLoaderJPG::ImageLoaderJPG() { Image::_jpg_mem_loader_func = _jpegd_mem_loader_func; + Image::save_jpg_func = _jpgd_save_func; + Image::save_jpg_buffer_func = _jpgd_buffer_save_func; } -- cgit v1.2.3