restored gl screen capture; get points on circle utility function
This commit is contained in:
parent
14759a1c79
commit
2831f2fc60
|
@ -33,8 +33,7 @@ void Configuration::set_defaults()
|
||||||
};
|
};
|
||||||
sys_config["input"] = {
|
sys_config["input"] = {
|
||||||
{"suppress-any-key-on-mods", true},
|
{"suppress-any-key-on-mods", true},
|
||||||
{"system-any-key-ignore-commands",
|
{"system-any-key-ignore-commands", {"fullscreen", "screenshot", "toggle-framerate", "record", "quit"}},
|
||||||
{"fullscreen", "screenshot", "toggle-framerate", "record", "quit"}},
|
|
||||||
{"any-key-ignore-commands", {}},
|
{"any-key-ignore-commands", {}},
|
||||||
{"default-unsuppress-delay", 700},
|
{"default-unsuppress-delay", 700},
|
||||||
{"ignore-repeat-keypress", true}
|
{"ignore-repeat-keypress", true}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#include "Display.hpp"
|
#include "Display.hpp"
|
||||||
#include "Game.hpp"
|
#include "Game.hpp"
|
||||||
|
|
||||||
|
/* Create a Display instance and subscribe to commands */
|
||||||
Display::Display(Node* parent) : Node(parent)
|
Display::Display(Node* parent) : Node(parent)
|
||||||
{
|
{
|
||||||
get_delegate().subscribe(&Display::respond, this);
|
get_delegate().subscribe(&Display::respond, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the (x, y) size in pixels of the window as an integer vector */
|
||||||
glm::ivec2 Display::get_window_size() const
|
glm::ivec2 Display::get_window_size() const
|
||||||
{
|
{
|
||||||
glm::ivec2 size;
|
glm::ivec2 size;
|
||||||
|
@ -13,11 +15,13 @@ glm::ivec2 Display::get_window_size() const
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the window dimensions as a Box object */
|
||||||
Box Display::get_window_box() const
|
Box Display::get_window_box() const
|
||||||
{
|
{
|
||||||
return Box(glm::vec2(0, 0), get_window_size());
|
return Box(glm::vec2(0, 0), get_window_size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the pixel format of display at specified index (defaults to index 0) */
|
||||||
Uint32 Display::get_pixel_format(int display_index) const
|
Uint32 Display::get_pixel_format(int display_index) const
|
||||||
{
|
{
|
||||||
SDL_DisplayMode display_mode;
|
SDL_DisplayMode display_mode;
|
||||||
|
@ -32,30 +36,32 @@ Uint32 Display::get_pixel_format(int display_index) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Fill the supplied, pre-allocated buffer with 32-bit pixels (8 bits per component) from the GL
|
||||||
|
* read buffer if in GL context or from the SDL renderer if in SDL context */
|
||||||
void Display::get_screen_pixels(unsigned char* pixels, int w, int h, int x, int y) const
|
void Display::get_screen_pixels(unsigned char* pixels, int w, int h, int x, int y) const
|
||||||
{
|
{
|
||||||
if (get_root()->is_gl_context)
|
if (get_root()->is_gl_context)
|
||||||
{
|
{
|
||||||
// GLenum format;
|
GLenum format;
|
||||||
// #if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||||
// format = GL_BGRA;
|
format = GL_BGRA;
|
||||||
// #else
|
#else
|
||||||
// format = GL_RGBA;
|
format = GL_RGBA;
|
||||||
// #endif
|
#endif
|
||||||
// glReadBuffer(GL_FRONT);
|
glReadBuffer(GL_FRONT);
|
||||||
// glReadPixels(x, y, w, h, format, GL_UNSIGNED_BYTE, pixels);
|
glReadPixels(x, y, w, h, format, GL_UNSIGNED_BYTE, pixels);
|
||||||
// // SDL_Log("(%i, %i, %i) (%i, %i, %i, %i)",
|
// SDL_Log("(%i, %i, %i) (%i, %i, %i, %i)",
|
||||||
// // glCheckFramebufferStatus(GL_FRAMEBUFFER),
|
// glCheckFramebufferStatus(GL_FRAMEBUFFER),
|
||||||
// // glCheckFramebufferStatus(GL_READ_FRAMEBUFFER),
|
// glCheckFramebufferStatus(GL_READ_FRAMEBUFFER),
|
||||||
// // glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER),
|
// glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER),
|
||||||
// // pixels[0], pixels[1], pixels[2], pixels[3]);
|
// pixels[0], pixels[1], pixels[2], pixels[3]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SDL_Renderer* renderer = const_cast<SDL_Renderer*>(get_renderer());
|
SDL_Renderer* renderer = const_cast<SDL_Renderer*>(get_renderer());
|
||||||
SDL_SetRenderTarget(renderer, nullptr);
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
SDL_RenderPresent(renderer);
|
SDL_RenderPresent(renderer);
|
||||||
SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_RGBA32, pixels, bpp / 8 * w);
|
SDL_RenderReadPixels(renderer, nullptr, SDL_PIXELFORMAT_RGBA32, pixels, bpp / 8 * w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +70,7 @@ SDL_Surface* Display::get_screen_surface() const
|
||||||
glm::ivec2 size = get_window_size();
|
glm::ivec2 size = get_window_size();
|
||||||
unsigned char* pixels = new unsigned char[bpp / 8 * size.x * size.y];
|
unsigned char* pixels = new unsigned char[bpp / 8 * size.x * size.y];
|
||||||
get_screen_pixels(pixels, size.x, size.y);
|
get_screen_pixels(pixels, size.x, size.y);
|
||||||
SDL_Surface* surface = get_screen_surface_from_pixels(
|
SDL_Surface* surface = get_screen_surface_from_pixels(pixels, get_root()->is_gl_context);
|
||||||
pixels, get_root()->is_gl_context);
|
|
||||||
delete[] pixels;
|
delete[] pixels;
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
@ -101,6 +106,7 @@ SDL_Surface* Display::get_screen_surface_from_pixels(unsigned char* pixels, bool
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle fullscreen request */
|
||||||
void Display::respond(SDL_Event& event)
|
void Display::respond(SDL_Event& event)
|
||||||
{
|
{
|
||||||
if (get_delegate().compare(event, "fullscreen"))
|
if (get_delegate().compare(event, "fullscreen"))
|
||||||
|
@ -109,17 +115,19 @@ void Display::respond(SDL_Event& event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Use SDL window flags to determine if fullscreen is on or off and use SDL fullscreen
|
||||||
|
* function to toggle the fullscreen state */
|
||||||
void Display::toggle_fullscreen() const
|
void Display::toggle_fullscreen() const
|
||||||
{
|
{
|
||||||
SDL_Window* window = const_cast<SDL_Window*>(get_window());
|
SDL_Window* window = const_cast<SDL_Window*>(get_window());
|
||||||
if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN)
|
if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN)
|
||||||
{
|
{
|
||||||
SDL_Log("fullscreen requested");
|
log("fullscreen requested");
|
||||||
SDL_SetWindowFullscreen(window, 0);
|
SDL_SetWindowFullscreen(window, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SDL_Log("exit fullscreen requested");
|
log("exit fullscreen requested");
|
||||||
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
|
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
src/Game.cpp
23
src/Game.cpp
|
@ -629,6 +629,8 @@ Audio& Game::get_audio()
|
||||||
return audio;
|
return audio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Applies delta timing to a vector: returns the passed vector as weighted by the amount of time passed since the
|
||||||
|
* last frame update, allowing for vector values to change the same amount over time independent of the frame rate */
|
||||||
glm::vec2 Game::weight(glm::vec2 motion)
|
glm::vec2 Game::weight(glm::vec2 motion)
|
||||||
{
|
{
|
||||||
return {weight(motion.x), weight(motion.y)};
|
return {weight(motion.x), weight(motion.y)};
|
||||||
|
@ -669,10 +671,10 @@ void Game::frame(float ticks)
|
||||||
// std::cout << ", last_frame_length: " << last_frame_length << " [rendering frame]";
|
// std::cout << ", last_frame_length: " << last_frame_length << " [rendering frame]";
|
||||||
if (last_frame_length < 1000)
|
if (last_frame_length < 1000)
|
||||||
{
|
{
|
||||||
if (!is_gl_context)
|
// if (!is_gl_context)
|
||||||
{
|
// {
|
||||||
recorder.update();
|
recorder.update();
|
||||||
}
|
// }
|
||||||
delegate.dispatch();
|
delegate.dispatch();
|
||||||
audio.update();
|
audio.update();
|
||||||
input.unsuppress_animation.update();
|
input.unsuppress_animation.update();
|
||||||
|
@ -719,16 +721,23 @@ void Game::flag_to_end()
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::set_framerate(int f)
|
/* Set the length of a frame in seconds by passing the framerate, the amount of frames to display
|
||||||
|
* per second */
|
||||||
|
void Game::set_framerate(int framerate)
|
||||||
{
|
{
|
||||||
if (f < 1)
|
if (framerate < 1)
|
||||||
{
|
{
|
||||||
f = 1;
|
framerate = 1;
|
||||||
}
|
}
|
||||||
framerate = f;
|
|
||||||
frame_length = 1000.0 / framerate;
|
frame_length = 1000.0 / framerate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the length of a frame in seconds */
|
||||||
|
float Game::get_frame_length() const
|
||||||
|
{
|
||||||
|
return frame_length;
|
||||||
|
}
|
||||||
|
|
||||||
void Game::handle_quit_event(SDL_Event &event)
|
void Game::handle_quit_event(SDL_Event &event)
|
||||||
{
|
{
|
||||||
if (event.type == SDL_QUIT)
|
if (event.type == SDL_QUIT)
|
||||||
|
|
12
src/Game.hpp
12
src/Game.hpp
|
@ -50,10 +50,14 @@ class Game : public Node
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
int ticks;
|
||||||
|
float frame_length = 1000.0 / 60.0;
|
||||||
|
|
||||||
static void sdl_log_override(void*, int, SDL_LogPriority, const char*);
|
static void sdl_log_override(void*, int, SDL_LogPriority, const char*);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/* Prevent an instance of this class from being copied or moved */
|
||||||
Game(const Game&) = delete;
|
Game(const Game&) = delete;
|
||||||
Game& operator=(const Game&) = delete;
|
Game& operator=(const Game&) = delete;
|
||||||
Game(Game&&) = delete;
|
Game(Game&&) = delete;
|
||||||
|
@ -63,9 +67,8 @@ public:
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
SDL_Renderer* renderer = nullptr;
|
SDL_Renderer* renderer = nullptr;
|
||||||
SDL_GLContext glcontext = nullptr;
|
SDL_GLContext glcontext = nullptr;
|
||||||
int frame_count_this_second = 0, framerate, ticks, last_frame_length;
|
int frame_count_this_second = 0, last_frame_length;
|
||||||
float frame_length = 1000.0 / 60.0, frame_time_overflow = 0, last_frame_timestamp,
|
float frame_time_overflow = 0, last_frame_timestamp, last_frame_count_timestamp;
|
||||||
last_frame_count_timestamp, emscripten_previous_time;
|
|
||||||
bool done = false, show_framerate = true, is_gl_context = true;
|
bool done = false, show_framerate = true, is_gl_context = true;
|
||||||
Configuration configuration = Configuration(this);
|
Configuration configuration = Configuration(this);
|
||||||
Delegate delegate = Delegate(this);
|
Delegate delegate = Delegate(this);
|
||||||
|
@ -106,11 +109,14 @@ public:
|
||||||
void flag_to_end();
|
void flag_to_end();
|
||||||
virtual void update() {};
|
virtual void update() {};
|
||||||
void set_framerate(int);
|
void set_framerate(int);
|
||||||
|
float get_frame_length() const;
|
||||||
void handle_quit_event(SDL_Event&);
|
void handle_quit_event(SDL_Event&);
|
||||||
void quit();
|
void quit();
|
||||||
virtual std::string get_class_name() const { return "Game"; }
|
virtual std::string get_class_name() const { return "Game"; }
|
||||||
~Game();
|
~Game();
|
||||||
|
|
||||||
|
/* Applies delta timing to a value: returns the value as weighted by the amount of time passed since the
|
||||||
|
* last frame update, allowing for values to change the same amount over time independent of the frame rate */
|
||||||
template<typename T>
|
template<typename T>
|
||||||
float weight(T amount)
|
float weight(T amount)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,23 +3,28 @@
|
||||||
#include "extension.hpp"
|
#include "extension.hpp"
|
||||||
#include "Recorder.hpp"
|
#include "Recorder.hpp"
|
||||||
|
|
||||||
|
/* Create a Recorder instance. Subscribe to command input and set audio callback.
|
||||||
|
* Only will be active if enabled by the configuration, in which case frames will
|
||||||
|
* automatically begin to be stashed */
|
||||||
Recorder::Recorder(Node* parent) : Node(parent)
|
Recorder::Recorder(Node* parent) : Node(parent)
|
||||||
{
|
{
|
||||||
get_delegate().subscribe(&Recorder::respond, this);
|
get_delegate().subscribe(&Recorder::respond, this);
|
||||||
animation.play();
|
animation.play();
|
||||||
Mix_SetPostMix(&process_audio, this);
|
Mix_SetPostMix(Recorder::process_audio, this);
|
||||||
if (!get_configuration()["recording"]["enabled"])
|
if (!get_configuration()["recording"]["enabled"])
|
||||||
{
|
{
|
||||||
deactivate();
|
deactivate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float Recorder::get_frame_length()
|
/* Returns length of a recorded video frame in seconds. Defaults to the frame length of the game if this hasn't
|
||||||
|
* been configured by the user. */
|
||||||
|
float Recorder::frame_length()
|
||||||
{
|
{
|
||||||
return get_configuration()["recording"].value(
|
return get_configuration()["recording"].value("video-frame-length", get_root()->get_frame_length());
|
||||||
"video-frame-length", get_root()->frame_length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle commands for screenshot, record video and save video */
|
||||||
void Recorder::respond(SDL_Event& event)
|
void Recorder::respond(SDL_Event& event)
|
||||||
{
|
{
|
||||||
if (get_delegate().compare(event, "screenshot"))
|
if (get_delegate().compare(event, "screenshot"))
|
||||||
|
@ -51,40 +56,48 @@ void Recorder::respond(SDL_Event& event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Save the current screen pixels as a PNG in the path specified by the configuration. Parent
|
||||||
|
* directories in the path will be created if necessary. The file name will be auto generated
|
||||||
|
* based on the configuration. An index will be included in the file name based on previous
|
||||||
|
* screenshots found in the output directory. */
|
||||||
void Recorder::capture_screen()
|
void Recorder::capture_screen()
|
||||||
{
|
{
|
||||||
nlohmann::json config = get_configuration();
|
nlohmann::json config = get_configuration();
|
||||||
SDL_Surface* surface = get_display().get_screen_surface();
|
SDL_Surface* surface = get_display().get_screen_surface();
|
||||||
fs::path directory = config["recording"]["screenshot-directory"];
|
fs::path directory = config["recording"]["screenshot-directory"];
|
||||||
fs::create_directories(directory);
|
fs::create_directories(directory);
|
||||||
std::string prefix = config["recording"]["screenshot-prefix"].
|
std::string prefix = config["recording"]["screenshot-prefix"].get<std::string>();
|
||||||
get<std::string>();
|
std::string extension = config["recording"]["screenshot-extension"].get<std::string>();
|
||||||
std::string extension = config["recording"]["screenshot-extension"].
|
|
||||||
get<std::string>();
|
|
||||||
int zfill = config["recording"]["screenshot-zfill"];
|
int zfill = config["recording"]["screenshot-zfill"];
|
||||||
fs::path path = sfw::get_next_file_name(directory, zfill, prefix, extension);
|
fs::path path = sfw::get_next_file_name(directory, zfill, prefix, extension);
|
||||||
IMG_SavePNG(surface, path.c_str());
|
IMG_SavePNG(surface, path.c_str());
|
||||||
SDL_FreeSurface(surface);
|
SDL_FreeSurface(surface);
|
||||||
SDL_Log("Saved screenshot to %s", path.c_str());
|
std::ostringstream message;
|
||||||
|
message << "saved screenshot to " << path;
|
||||||
|
log(message.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Writes a video of what was just displayed on the screen up until the function was called. The length
|
||||||
|
* of the video is determined by the stash length written in the configuration. This is accomplished by
|
||||||
|
* writing the contents of the most recent Stash object. */
|
||||||
void Recorder::grab_stash()
|
void Recorder::grab_stash()
|
||||||
{
|
{
|
||||||
if (!is_recording and !writing_recording)
|
if (!is_recording and !writing_recording)
|
||||||
{
|
{
|
||||||
int length = get_configuration()["recording"]["max-stash-length"];
|
int length = get_configuration()["recording"]["max-stash-length"];
|
||||||
SDL_Log("Stashing most recent %i seconds of video...", length / 1000);
|
std::ostringstream message;
|
||||||
|
message << "stashing most recent " << length / 1000.0f << " seconds of video";
|
||||||
|
log(message.str());
|
||||||
most_recent_stash = current_stash;
|
most_recent_stash = current_stash;
|
||||||
current_stash = Stash();
|
current_stash = Stash();
|
||||||
writing_recording = true;
|
writing_recording = true;
|
||||||
std::function<void()> f =
|
std::function<void()> f = std::bind(&Recorder::write_most_recent_frames, this);
|
||||||
std::bind(&Recorder::write_most_recent_frames, this);
|
|
||||||
std::thread writing(f);
|
std::thread writing(f);
|
||||||
writing.detach();
|
writing.detach();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SDL_Log("Recording in progress, cannot grab most recent frames");
|
log("recording in progress, cannot grab most recent frames");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,19 +108,16 @@ void Recorder::write_most_recent_frames()
|
||||||
open_audio_file();
|
open_audio_file();
|
||||||
while (!most_recent_stash.audio_buffers.empty())
|
while (!most_recent_stash.audio_buffers.empty())
|
||||||
{
|
{
|
||||||
write_audio(most_recent_stash.audio_buffers.front(),
|
write_audio(most_recent_stash.audio_buffers.front(), most_recent_stash.audio_buffer_lengths.front());
|
||||||
most_recent_stash.audio_buffer_lengths.front());
|
most_recent_stash.audio_buffers.erase(most_recent_stash.audio_buffers.begin());
|
||||||
most_recent_stash.audio_buffers.erase(
|
most_recent_stash.audio_buffer_lengths.erase(most_recent_stash.audio_buffer_lengths.begin());
|
||||||
most_recent_stash.audio_buffers.begin());
|
|
||||||
most_recent_stash.audio_buffer_lengths.erase(
|
|
||||||
most_recent_stash.audio_buffer_lengths.begin());
|
|
||||||
}
|
}
|
||||||
audio_file.close();
|
audio_file.close();
|
||||||
if (get_configuration()["recording"]["write-mp4"])
|
if (get_configuration()["recording"]["write-mp4"])
|
||||||
{
|
{
|
||||||
write_mp4();
|
write_mp4();
|
||||||
}
|
}
|
||||||
SDL_Log("Wrote video frames to %s", current_video_directory.c_str());
|
SDL_Log("wrote video frames to %s", current_video_directory.c_str());
|
||||||
writing_recording = false;
|
writing_recording = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +125,7 @@ void Recorder::start_recording()
|
||||||
{
|
{
|
||||||
if (!writing_recording)
|
if (!writing_recording)
|
||||||
{
|
{
|
||||||
SDL_Log("Starting recording...");
|
log("starting recording");
|
||||||
is_recording = true;
|
is_recording = true;
|
||||||
video_stashes.push_back(Stash());
|
video_stashes.push_back(Stash());
|
||||||
make_directory();
|
make_directory();
|
||||||
|
@ -123,7 +133,7 @@ void Recorder::start_recording()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SDL_Log("Writing in progress, cannot start recording");
|
log("writing in progress, cannot start recording");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +152,7 @@ void Recorder::add_frame()
|
||||||
unsigned char* pixels = new unsigned char[bytes];
|
unsigned char* pixels = new unsigned char[bytes];
|
||||||
get_display().get_screen_pixels(pixels, size.x, size.y);
|
get_display().get_screen_pixels(pixels, size.x, size.y);
|
||||||
int max_length = get_configuration()["recording"]["max-stash-length"];
|
int max_length = get_configuration()["recording"]["max-stash-length"];
|
||||||
float length = get_frame_length() * current_stash.pixel_buffers.size();
|
float length = frame_length() * current_stash.pixel_buffers.size();
|
||||||
if (length > max_length)
|
if (length > max_length)
|
||||||
{
|
{
|
||||||
delete[] current_stash.pixel_buffers.front();
|
delete[] current_stash.pixel_buffers.front();
|
||||||
|
@ -157,10 +167,9 @@ void Recorder::add_frame()
|
||||||
memcpy(vid_pixels, pixels, bytes);
|
memcpy(vid_pixels, pixels, bytes);
|
||||||
video_stashes.back().pixel_buffers.push_back(vid_pixels);
|
video_stashes.back().pixel_buffers.push_back(vid_pixels);
|
||||||
video_stashes.back().flipped.push_back(get_root()->is_gl_context);
|
video_stashes.back().flipped.push_back(get_root()->is_gl_context);
|
||||||
if (video_stashes.back().pixel_buffers.size() * get_frame_length() > max_length)
|
if (video_stashes.back().pixel_buffers.size() * frame_length() > max_length)
|
||||||
{
|
{
|
||||||
std::function<void(Stash*)> f =
|
std::function<void(Stash*)> f = std::bind(&Recorder::write_stash_frames, this, std::placeholders::_1);
|
||||||
std::bind(&Recorder::write_stash_frames, this, std::placeholders::_1);
|
|
||||||
std::thread writing(f, &video_stashes.back());
|
std::thread writing(f, &video_stashes.back());
|
||||||
writing.detach();
|
writing.detach();
|
||||||
// int frame_offset = video_stashes.back().frame_offset;
|
// int frame_offset = video_stashes.back().frame_offset;
|
||||||
|
@ -182,9 +191,8 @@ void Recorder::add_frame()
|
||||||
// write_stash_frames(video_stashes.back().pixel_buffers,
|
// write_stash_frames(video_stashes.back().pixel_buffers,
|
||||||
// video_stashes.back().flipped,
|
// video_stashes.back().flipped,
|
||||||
// video_stashes.back().frame_offset);
|
// video_stashes.back().frame_offset);
|
||||||
video_stashes.push_back(
|
video_stashes.push_back(Stash(video_stashes.back().frame_offset +
|
||||||
Stash(video_stashes.back().frame_offset +
|
video_stashes.back().pixel_buffers.size()));
|
||||||
video_stashes.back().pixel_buffers.size()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,7 +272,7 @@ void Recorder::write_stash_frames(Stash* stash)
|
||||||
GifWriteFrame(&gif_writer, (const uint8_t*) converted->pixels,
|
GifWriteFrame(&gif_writer, (const uint8_t*) converted->pixels,
|
||||||
frame->w, frame->h, gif_frame_length / 10);
|
frame->w, frame->h, gif_frame_length / 10);
|
||||||
}
|
}
|
||||||
elapsed += get_frame_length();
|
elapsed += frame_length();
|
||||||
delete[] stash->pixel_buffers.front();
|
delete[] stash->pixel_buffers.front();
|
||||||
stash->pixel_buffers.erase(stash->pixel_buffers.begin());
|
stash->pixel_buffers.erase(stash->pixel_buffers.begin());
|
||||||
stash->flipped.erase(stash->flipped.begin());
|
stash->flipped.erase(stash->flipped.begin());
|
||||||
|
@ -336,7 +344,7 @@ void Recorder::write_mp4()
|
||||||
std::string pixel_format = get_configuration()["recording"]["mp4-pixel-format"].get<std::string>();
|
std::string pixel_format = get_configuration()["recording"]["mp4-pixel-format"].get<std::string>();
|
||||||
fs::path images_match = current_video_directory / "%05d.png";
|
fs::path images_match = current_video_directory / "%05d.png";
|
||||||
mp4_command << "ffmpeg -f s16le -ac 2 -ar 22050 -i " << current_audio_path.string() <<
|
mp4_command << "ffmpeg -f s16le -ac 2 -ar 22050 -i " << current_audio_path.string() <<
|
||||||
" -f image2 -framerate " << (1000 / get_frame_length()) <<
|
" -f image2 -framerate " << (1000 / frame_length()) <<
|
||||||
" -i " << images_match.string() << " -s " << size.x << "x" << size.y <<
|
" -i " << images_match.string() << " -s " << size.x << "x" << size.y <<
|
||||||
" -c:v libx264 -crf 17 -pix_fmt " << pixel_format << " " <<
|
" -c:v libx264 -crf 17 -pix_fmt " << pixel_format << " " <<
|
||||||
current_video_directory.string() << ".mp4";
|
current_video_directory.string() << ".mp4";
|
||||||
|
@ -352,22 +360,21 @@ void Recorder::write_audio(Uint8* stream, int len)
|
||||||
|
|
||||||
void Recorder::update()
|
void Recorder::update()
|
||||||
{
|
{
|
||||||
if (is_recording and get_memory_size() >
|
if (is_recording and get_memory_size() > get_configuration()["recording"]["max-video-memory"])
|
||||||
get_configuration()["recording"]["max-video-memory"])
|
|
||||||
{
|
{
|
||||||
end_recording();
|
end_recording();
|
||||||
}
|
}
|
||||||
animation.set_frame_length(get_frame_length());
|
animation.set_frame_length(frame_length());
|
||||||
animation.update();
|
animation.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_audio(void* context, Uint8* stream, int len)
|
void Recorder::process_audio(void* context, Uint8* stream, int len)
|
||||||
{
|
{
|
||||||
Recorder* recorder = static_cast<Recorder*>(context);
|
Recorder* recorder = static_cast<Recorder*>(context);
|
||||||
if (recorder->is_active())
|
if (recorder->is_active())
|
||||||
{
|
{
|
||||||
int max_length = recorder->get_configuration()["recording"]["max-stash-length"];
|
int max_length = recorder->get_configuration()["recording"]["max-stash-length"];
|
||||||
float length = recorder->get_frame_length() * recorder->current_stash.pixel_buffers.size();
|
float length = recorder->frame_length() * recorder->current_stash.pixel_buffers.size();
|
||||||
if (length > max_length)
|
if (length > max_length)
|
||||||
{
|
{
|
||||||
delete[] recorder->current_stash.audio_buffers.front();
|
delete[] recorder->current_stash.audio_buffers.front();
|
||||||
|
|
|
@ -32,9 +32,11 @@ struct Stash
|
||||||
Stash(int frame_offset = 0) : frame_offset(frame_offset) {}
|
Stash(int frame_offset = 0) : frame_offset(frame_offset) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Recorder : Node
|
class Recorder : public Node
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
Stash current_stash = Stash();
|
Stash current_stash = Stash();
|
||||||
Stash most_recent_stash;
|
Stash most_recent_stash;
|
||||||
std::list<Stash> in_game_stashes;
|
std::list<Stash> in_game_stashes;
|
||||||
|
@ -44,8 +46,12 @@ struct Recorder : Node
|
||||||
bool is_recording = false, writing_recording = false, writing_most_recent = false;
|
bool is_recording = false, writing_recording = false, writing_most_recent = false;
|
||||||
std::ofstream audio_file;
|
std::ofstream audio_file;
|
||||||
|
|
||||||
|
float frame_length();
|
||||||
|
static void process_audio(void*, Uint8*, int);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
Recorder(Node*);
|
Recorder(Node*);
|
||||||
float get_frame_length();
|
|
||||||
void respond(SDL_Event&);
|
void respond(SDL_Event&);
|
||||||
void capture_screen();
|
void capture_screen();
|
||||||
void grab_stash();
|
void grab_stash();
|
||||||
|
@ -66,6 +72,4 @@ struct Recorder : Node
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void process_audio(void*, Uint8*, int);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -6,11 +6,32 @@ void sfw::set_magnitude(glm::vec2& vector, float magnitude)
|
||||||
vector = glm::normalize(vector) * magnitude;
|
vector = glm::normalize(vector) * magnitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return coordinates of a point x, y at specified angle on a circle described by center and radius */
|
||||||
glm::vec2 sfw::get_point_on_circle(const glm::vec2& center, float radius, float angle)
|
glm::vec2 sfw::get_point_on_circle(const glm::vec2& center, float radius, float angle)
|
||||||
{
|
{
|
||||||
return {center.x + std::sin(angle) * radius, center.y - std::cos(angle) * radius};
|
return {center.x + std::sin(angle) * radius, center.y - std::cos(angle) * radius};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return a point x, y at specified angle on a circle at the origin with specified radius (default 1) */
|
||||||
|
glm::vec2 sfw::get_point_on_circle(float angle, float radius)
|
||||||
|
{
|
||||||
|
return get_point_on_circle({0, 0}, radius, angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return a vector of count number of points evenly spaced around a circle starting at the angle offset
|
||||||
|
* (defaults to 0) */
|
||||||
|
std::vector<glm::vec2> sfw::get_points_on_circle(int count, float radius, const glm::vec2& center, float offset)
|
||||||
|
{
|
||||||
|
std::vector<glm::vec2> points;
|
||||||
|
points.reserve(count);
|
||||||
|
float step = glm::two_pi<float>() / count;
|
||||||
|
for (int ii = 0; ii < count; ii++)
|
||||||
|
{
|
||||||
|
points.push_back(get_point_on_circle(center, radius, ii * step + offset));
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
Box sfw::get_texture_box(SDL_Texture* texture)
|
Box sfw::get_texture_box(SDL_Texture* texture)
|
||||||
{
|
{
|
||||||
int width, height;
|
int width, height;
|
||||||
|
@ -637,6 +658,12 @@ std::ostream& operator<<(std::ostream& out, const glm::vec2& vector)
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, const glm::vec3& vector)
|
||||||
|
{
|
||||||
|
out << "{" << vector.x << ", " << vector.y << ", " << vector.z << "}";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, const SDL_Color& color)
|
std::ostream& operator<<(std::ostream& out, const SDL_Color& color)
|
||||||
{
|
{
|
||||||
out << "{" << static_cast<int>(color.r) << ", " << static_cast<int>(color.g) << ", " <<
|
out << "{" << static_cast<int>(color.r) << ", " << static_cast<int>(color.g) << ", " <<
|
||||||
|
|
|
@ -37,6 +37,8 @@ namespace sfw
|
||||||
|
|
||||||
void set_magnitude(glm::vec2&, float);
|
void set_magnitude(glm::vec2&, float);
|
||||||
glm::vec2 get_point_on_circle(const glm::vec2&, float, float);
|
glm::vec2 get_point_on_circle(const glm::vec2&, float, float);
|
||||||
|
glm::vec2 get_point_on_circle(float, float = 1.0f);
|
||||||
|
std::vector<glm::vec2> get_points_on_circle(int, float = 1.0f, const glm::vec2& = {0, 0}, float = 0.0f);
|
||||||
Box get_texture_box(SDL_Texture*);
|
Box get_texture_box(SDL_Texture*);
|
||||||
glm::vec2 fit_and_preserve_aspect(const glm::vec2&, const glm::vec2&);
|
glm::vec2 fit_and_preserve_aspect(const glm::vec2&, const glm::vec2&);
|
||||||
std::vector<std::vector<Box>> get_blinds_boxes(glm::vec2, float = 0.05f, int = 4);
|
std::vector<std::vector<Box>> get_blinds_boxes(glm::vec2, float = 0.05f, int = 4);
|
||||||
|
@ -187,6 +189,7 @@ std::ostream& operator<<(std::ostream& out, const std::vector<T>& members)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream&, const glm::vec2&);
|
std::ostream& operator<<(std::ostream&, const glm::vec2&);
|
||||||
|
std::ostream& operator<<(std::ostream&, const glm::vec3&);
|
||||||
std::ostream& operator<<(std::ostream&, const SDL_Color&);
|
std::ostream& operator<<(std::ostream&, const SDL_Color&);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue