stash and write audio to recording
This commit is contained in:
parent
437f2d321c
commit
7e51a04b71
|
@ -140,13 +140,11 @@ Demo::Demo()
|
|||
Mix_PlayMusic(music, -1);
|
||||
load_gl_context();
|
||||
delegate.subscribe(&Demo::respond, this);
|
||||
// Input* l = new Input(this);
|
||||
// delete l;
|
||||
// input.print_branch();
|
||||
// mushroom.print_branch();
|
||||
// Input* i = new Input(this);
|
||||
// get_delegate().unsubscribe(i);
|
||||
// delete i;
|
||||
// audio_file.open("audio.raw", std::ios::binary);
|
||||
// SDL_AudioSpec spec;
|
||||
// audio_device_id = SDL_OpenAudioDevice(NULL, SDL_TRUE, &spec, &spec, 0);
|
||||
// SDL_Log("opened audio device %i", audio_device_id);
|
||||
// SDL_PauseAudioDevice(audio_device_id, SDL_FALSE);
|
||||
}
|
||||
|
||||
void Demo::load_sdl_context()
|
||||
|
@ -317,6 +315,11 @@ void Demo::respond(SDL_Event& event)
|
|||
load_gl_context();
|
||||
}
|
||||
}
|
||||
else if (delegate.compare(event, "play-sound"))
|
||||
{
|
||||
Mix_Chunk* music = Mix_LoadWAV("resource/Ag.ogg");
|
||||
Mix_PlayChannel(-1, music, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Demo::update()
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
|
||||
#include <SDL_image.h>
|
||||
#include "sdl2-gfx/SDL2_gfxPrimitives.h"
|
||||
|
@ -54,7 +55,7 @@ struct Demo : Game
|
|||
SDL_Texture *grass_texture;
|
||||
int frame_count = 0, frame_count_timestamp;
|
||||
bool right_active = false, down_active = false, left_active = false,
|
||||
up_active = false;
|
||||
up_active = false, is_writing_audio = true;
|
||||
SDL_Event event;
|
||||
GLuint vbo, space_texture_id, mvp_id, framerate_texture_id, flat_program,
|
||||
world_program, fake_texture_id;
|
||||
|
@ -62,6 +63,8 @@ struct Demo : Game
|
|||
int abc, def, ghi, jkl, mno, pqr, stu, vwx, yz;
|
||||
Mushroom mushroom = Mushroom(this);
|
||||
Sprite grass = Sprite(this, "resource/Field.png");
|
||||
// SDL_AudioDeviceID audio_device_id;
|
||||
// std::ofstream audio_file;
|
||||
|
||||
Demo();
|
||||
void load_sdl_context();
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
"keys":
|
||||
{
|
||||
"context": " ",
|
||||
"print-video-memory": "?"
|
||||
"print-video-memory": "?",
|
||||
"play-sound": "s"
|
||||
},
|
||||
"recording":
|
||||
{
|
||||
|
|
Binary file not shown.
|
@ -57,6 +57,14 @@ Game::Game()
|
|||
{
|
||||
print_sdl_error("Could not set up audio");
|
||||
}
|
||||
std::cout << "Using audio driver: " << SDL_GetCurrentAudioDriver() <<
|
||||
std::endl;
|
||||
const int audio_device_count = SDL_GetNumAudioDevices(SDL_TRUE);
|
||||
for (int ii = 0; ii < audio_device_count; ii++)
|
||||
{
|
||||
std::cout << "Found audio capture device " << ii << ": " <<
|
||||
SDL_GetAudioDeviceName(ii, SDL_TRUE) << std::endl;
|
||||
}
|
||||
last_frame_timestamp = SDL_GetTicks();
|
||||
}
|
||||
|
||||
|
|
109
src/Recorder.cpp
109
src/Recorder.cpp
|
@ -6,6 +6,7 @@ Recorder::Recorder(Node* parent) : Node(parent)
|
|||
{
|
||||
get_delegate().subscribe(&Recorder::respond, this);
|
||||
animation.play();
|
||||
Mix_SetPostMix(&process_audio, this);
|
||||
}
|
||||
|
||||
float Recorder::get_frame_length()
|
||||
|
@ -32,14 +33,17 @@ void Recorder::respond(SDL_Event& event)
|
|||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Writing in progress, cannot start recording" <<
|
||||
std::endl;
|
||||
SDL_Log("Writing in progress, cannot start recording");
|
||||
}
|
||||
}
|
||||
else if (get_delegate().compare(event, "save-current-stash"))
|
||||
{
|
||||
grab_stash();
|
||||
}
|
||||
else if (get_delegate().compare(event, "print-video-memory-size"))
|
||||
{
|
||||
SDL_Log("Video memory size is %iMB", get_memory_size());
|
||||
}
|
||||
}
|
||||
|
||||
void Recorder::capture_screen()
|
||||
|
@ -56,7 +60,7 @@ void Recorder::capture_screen()
|
|||
fs::path path = sfw::get_next_file_name(directory, zfill, prefix, extension);
|
||||
IMG_SavePNG(surface, path.c_str());
|
||||
SDL_FreeSurface(surface);
|
||||
std::cout << "Saved screenshot to " << path.string() << std::endl;
|
||||
SDL_Log("Saved screenshot to %s", path.c_str());
|
||||
}
|
||||
|
||||
void Recorder::grab_stash()
|
||||
|
@ -64,8 +68,7 @@ void Recorder::grab_stash()
|
|||
if (not is_recording and not writing_recording)
|
||||
{
|
||||
int length = get_configuration()["recording"]["max-stash-length"];
|
||||
std::cout << "stashing most recent " << (length / 1000) <<
|
||||
"seconds of video..." << std::endl;
|
||||
SDL_Log("Stashing most recent %i seconds of video...", length / 1000);
|
||||
most_recent_stash = current_stash;
|
||||
current_stash = Stash();
|
||||
writing_recording = true;
|
||||
|
@ -76,20 +79,30 @@ void Recorder::grab_stash()
|
|||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Recording in progress, cannot grab most recent frames" <<
|
||||
std::endl;
|
||||
SDL_Log("Recording in progress, cannot grab most recent frames");
|
||||
}
|
||||
}
|
||||
|
||||
void Recorder::write_most_recent_frames()
|
||||
{
|
||||
make_directory();
|
||||
write_stash_frames(false, 0);
|
||||
open_audio_file();
|
||||
while (not most_recent_stash.audio_buffers.empty())
|
||||
{
|
||||
write_audio(most_recent_stash.audio_buffers.front(),
|
||||
most_recent_stash.audio_buffer_lengths.front());
|
||||
most_recent_stash.audio_buffers.erase(
|
||||
most_recent_stash.audio_buffers.begin());
|
||||
most_recent_stash.audio_buffer_lengths.erase(
|
||||
most_recent_stash.audio_buffer_lengths.begin());
|
||||
}
|
||||
audio_file.close();
|
||||
if (get_configuration()["recording"]["write-mp4"])
|
||||
{
|
||||
write_mp4();
|
||||
}
|
||||
std::cout << "Wrote video frames to " << current_video_directory.string() <<
|
||||
std::endl;
|
||||
SDL_Log("Wrote video frames to %s", current_video_directory.c_str());
|
||||
writing_recording = false;
|
||||
}
|
||||
|
||||
|
@ -97,16 +110,26 @@ void Recorder::start_recording()
|
|||
{
|
||||
if (not writing_recording)
|
||||
{
|
||||
std::cout << "Starting recording..." << std::endl;
|
||||
SDL_Log("Starting recording...");
|
||||
is_recording = true;
|
||||
video_stashes.push_back(Stash());
|
||||
make_directory();
|
||||
open_audio_file();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Writing in progress, cannot start recording" << std::endl;
|
||||
SDL_Log("Writing in progress, cannot start recording");
|
||||
}
|
||||
}
|
||||
|
||||
void Recorder::open_audio_file()
|
||||
{
|
||||
std::stringstream audio_path;
|
||||
audio_path << current_video_directory.string() << ".raw";
|
||||
current_audio_path = audio_path.str();
|
||||
audio_file.open(audio_path.str(), std::ios::binary);
|
||||
}
|
||||
|
||||
void Recorder::add_frame()
|
||||
{
|
||||
glm::ivec2 size = get_display().get_window_size();
|
||||
|
@ -143,31 +166,36 @@ void Recorder::add_frame()
|
|||
int Recorder::get_memory_size()
|
||||
{
|
||||
glm::ivec2 window = get_display().get_window_size();
|
||||
int bytes = Display::bpp / 8 * window.x * window.y;
|
||||
int size = 0;
|
||||
int bytes_per_buffer = Display::bpp / 8 * window.x * window.y;
|
||||
int count = 0;
|
||||
for (Stash& stash : in_game_stashes)
|
||||
{
|
||||
size += stash.pixel_buffers.size();
|
||||
count += stash.pixel_buffers.size();
|
||||
}
|
||||
for (Stash& stash : video_stashes)
|
||||
{
|
||||
size += stash.pixel_buffers.size();
|
||||
count += stash.pixel_buffers.size();
|
||||
}
|
||||
return size * bytes / 1000000;
|
||||
count += current_stash.pixel_buffers.size();
|
||||
count += most_recent_stash.pixel_buffers.size();
|
||||
int size_in_bytes = 0;
|
||||
size_in_bytes = count * bytes_per_buffer;
|
||||
return size_in_bytes / 1000000;
|
||||
}
|
||||
|
||||
void Recorder::make_directory()
|
||||
{
|
||||
nlohmann::json config = get_configuration();
|
||||
fs::path root = config["path"]["video"];
|
||||
fs::create_directories(root);
|
||||
fs::path directory = sfw::get_next_file_name(root, 5, "video-");
|
||||
fs::create_directories(directory);
|
||||
current_video_directory = directory;
|
||||
}
|
||||
|
||||
void Recorder::write_stash_frames(bool is_video, int index)
|
||||
{
|
||||
int frame_offset = is_video ? video_stashes[index].frame_offset : 0;
|
||||
if (not frame_offset)
|
||||
{
|
||||
nlohmann::json config = get_configuration();
|
||||
fs::path root = config["path"]["video"];
|
||||
fs::create_directories(root);
|
||||
fs::path directory = sfw::get_next_file_name(root, 5, "video-");
|
||||
fs::create_directories(directory);
|
||||
current_video_directory = directory;
|
||||
}
|
||||
std::cout << "Writing stash index " << index << " to " << current_video_directory << "..." <<
|
||||
std::endl;
|
||||
SDL_Surface* frame;
|
||||
|
@ -248,6 +276,7 @@ void Recorder::keep_stash()
|
|||
void Recorder::end_recording()
|
||||
{
|
||||
std::cout << "Ending recording..." << std::endl;
|
||||
audio_file.close();
|
||||
is_recording = false;
|
||||
writing_recording = true;
|
||||
std::function<void()> f = std::bind(&Recorder::finish_writing_video, this);
|
||||
|
@ -288,7 +317,8 @@ void Recorder::write_mp4()
|
|||
std::string pixel_format =
|
||||
get_configuration()["recording"]["mp4-pixel-format"].get<std::string>();
|
||||
fs::path images_match = current_video_directory / "%05d.png";
|
||||
mp4_command << "ffmpeg -f image2 -framerate " << (1000 / get_frame_length()) <<
|
||||
mp4_command << "ffmpeg -f s16le -ac 2 -ar 22050 -i " << current_audio_path.string() <<
|
||||
" -f image2 -framerate " << (1000 / get_frame_length()) <<
|
||||
" -i " << images_match.string() << " -s " << size.x << "x" << size.y <<
|
||||
" -c:v libx264 -crf 17 -pix_fmt " << pixel_format << " " <<
|
||||
current_video_directory.string() << ".mp4";
|
||||
|
@ -296,6 +326,11 @@ void Recorder::write_mp4()
|
|||
std::system(mp4_command.str().c_str());
|
||||
}
|
||||
|
||||
void Recorder::write_audio(Uint8* stream, int len)
|
||||
{
|
||||
audio_file.write(reinterpret_cast<char*>(stream), len);
|
||||
}
|
||||
|
||||
void Recorder::update()
|
||||
{
|
||||
if (is_recording and get_memory_size() >
|
||||
|
@ -306,3 +341,25 @@ void Recorder::update()
|
|||
animation.set_frame_length(get_frame_length());
|
||||
animation.update();
|
||||
}
|
||||
|
||||
void process_audio(void* user_data, Uint8* stream, int len)
|
||||
{
|
||||
Recorder* recorder = static_cast<Recorder*>(user_data);
|
||||
int max_length = recorder->get_configuration()["recording"]["max-stash-length"];
|
||||
float length = recorder->get_frame_length() * recorder->current_stash.pixel_buffers.size();
|
||||
if (length > max_length)
|
||||
{
|
||||
delete[] recorder->current_stash.audio_buffers.front();
|
||||
recorder->current_stash.audio_buffers.erase(recorder->current_stash.audio_buffers.begin());
|
||||
recorder->current_stash.audio_buffer_lengths.erase(
|
||||
recorder->current_stash.audio_buffer_lengths.begin());
|
||||
}
|
||||
Uint8* stream_copy = new Uint8[len];
|
||||
std::memcpy(stream_copy, stream, len);
|
||||
recorder->current_stash.audio_buffers.push_back(stream_copy);
|
||||
recorder->current_stash.audio_buffer_lengths.push_back(len);
|
||||
if (recorder->is_recording)
|
||||
{
|
||||
recorder->write_audio(stream_copy, len);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
struct Stash
|
||||
{
|
||||
std::vector<unsigned char*> pixel_buffers;
|
||||
std::vector<Uint8*> audio_buffers;
|
||||
std::vector<int> audio_buffer_lengths;
|
||||
int frame_offset;
|
||||
|
||||
Stash(int frame_offset = 0) : frame_offset(frame_offset) {}
|
||||
|
@ -35,9 +37,10 @@ struct Recorder : Node
|
|||
Stash most_recent_stash;
|
||||
std::vector<Stash> in_game_stashes;
|
||||
std::vector<Stash> video_stashes;
|
||||
fs::path current_video_directory;
|
||||
fs::path current_video_directory, current_audio_path;
|
||||
Animation animation = Animation(&Recorder::add_frame, this);
|
||||
bool is_recording = false, writing_recording = false, writing_most_recent = false;
|
||||
std::ofstream audio_file;
|
||||
|
||||
Recorder(Node*);
|
||||
float get_frame_length();
|
||||
|
@ -46,16 +49,21 @@ struct Recorder : Node
|
|||
void grab_stash();
|
||||
void write_most_recent_frames();
|
||||
void start_recording();
|
||||
void open_audio_file();
|
||||
void add_frame();
|
||||
int get_memory_size();
|
||||
void make_directory();
|
||||
void write_stash_frames(bool, int);
|
||||
void keep_stash();
|
||||
void end_recording();
|
||||
void finish_writing_video();
|
||||
void write_mp4();
|
||||
void write_audio(Uint8*, int);
|
||||
void update();
|
||||
std::string get_class_name() { return "Recorder"; }
|
||||
|
||||
};
|
||||
|
||||
void process_audio(void*, Uint8*, int);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue