timeline refactor, bit broken rn
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
#include "render.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "util.h"
|
||||
@@ -9,7 +12,7 @@
|
||||
#define PCLOSE _pclose
|
||||
#define PWRITE_MODE "wb"
|
||||
#define PREAD_MODE "r"
|
||||
#else
|
||||
#elif __unix__
|
||||
#define POPEN popen
|
||||
#define PCLOSE pclose
|
||||
#define PWRITE_MODE "w"
|
||||
@@ -25,44 +28,87 @@ namespace anm2ed
|
||||
{
|
||||
constexpr auto FFMPEG_POPEN_ERROR = "popen() (for FFmpeg) failed!\n{}";
|
||||
|
||||
constexpr auto GIF_FORMAT = "\"{0}\" -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-lavfi \"split[s0][s1];"
|
||||
"[s0]palettegen=stats_mode=full[p];"
|
||||
"[s1][p]paletteuse=dither=floyd_steinberg\" "
|
||||
"-loop 0 \"{4}\"";
|
||||
|
||||
constexpr auto WEBM_FORMAT = "\"{0}\" -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-c:v libvpx-vp9 -crf 30 -b:v 0 -pix_fmt yuva420p -row-mt 1 -threads 0 -speed 2 "
|
||||
"-auto-alt-ref 0 -an \"{4}\"";
|
||||
|
||||
constexpr auto* MP4_FORMAT = "\"{0}\" -y "
|
||||
"-f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0 "
|
||||
"-vf \"format=yuv420p,scale=trunc(iw/2)*2:trunc(ih/2)*2\" "
|
||||
"-c:v libx265 -crf 20 -preset slow "
|
||||
"-tag:v hvc1 -movflags +faststart -an \"{4}\"";
|
||||
|
||||
bool animation_render(const std::string& ffmpegPath, const std::string& path, std::vector<Texture>& frames,
|
||||
render::Type type, ivec2 size, int fps)
|
||||
AudioStream& audioStream, render::Type type, ivec2 size, int fps)
|
||||
{
|
||||
if (frames.empty() || size.x <= 0 || size.y <= 0 || fps <= 0 || ffmpegPath.empty() || path.empty()) return false;
|
||||
|
||||
std::filesystem::path audioPath{};
|
||||
std::string audioInputArguments{};
|
||||
std::string audioOutputArguments{"-an"};
|
||||
std::string command{};
|
||||
|
||||
auto remove_audio_file = [&]()
|
||||
{
|
||||
if (!audioPath.empty())
|
||||
{
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(audioPath, ec);
|
||||
}
|
||||
};
|
||||
|
||||
if (type != render::GIF && !audioStream.stream.empty() && audioStream.spec.freq > 0 &&
|
||||
audioStream.spec.channels > 0)
|
||||
{
|
||||
audioPath = std::filesystem::temp_directory_path() / std::format("{}.f32", path);
|
||||
|
||||
std::ofstream audioFile(audioPath, std::ios::binary);
|
||||
|
||||
if (audioFile)
|
||||
{
|
||||
auto data = (const char*)audioStream.stream.data();
|
||||
auto byteCount = audioStream.stream.size() * sizeof(float);
|
||||
audioFile.write(data, byteCount);
|
||||
audioFile.close();
|
||||
|
||||
audioInputArguments = std::format("-f f32le -ar {0} -ac {1} -i \"{2}\"", audioStream.spec.freq,
|
||||
audioStream.spec.channels, audioPath.string());
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case render::WEBM:
|
||||
audioOutputArguments = "-c:a libopus -b:a 160k -shortest";
|
||||
break;
|
||||
case render::MP4:
|
||||
audioOutputArguments = "-c:a aac -b:a 192k -shortest";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.warning("Failed to open temporary audio file; exporting video without audio.");
|
||||
remove_audio_file();
|
||||
}
|
||||
}
|
||||
|
||||
command = std::format("\"{0}\" -y -f rawvideo -pix_fmt rgba -s {1}x{2} -r {3} -i pipe:0", ffmpegPath, size.x,
|
||||
size.y, fps);
|
||||
|
||||
if (!audioInputArguments.empty()) command += " " + audioInputArguments;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case render::GIF:
|
||||
command = std::format(GIF_FORMAT, ffmpegPath, size.x, size.y, fps, path);
|
||||
command +=
|
||||
" -lavfi \"split[s0][s1];[s0]palettegen=stats_mode=full[p];[s1][p]paletteuse=dither=floyd_steinberg\""
|
||||
" -loop 0";
|
||||
command += std::format(" \"{}\"", path);
|
||||
break;
|
||||
case render::WEBM:
|
||||
command = std::format(WEBM_FORMAT, ffmpegPath, size.x, size.y, fps, path);
|
||||
command += " -c:v libvpx-vp9 -crf 30 -b:v 0 -pix_fmt yuva420p -row-mt 1 -threads 0 -speed 2 -auto-alt-ref 0";
|
||||
if (!audioOutputArguments.empty()) command += " " + audioOutputArguments;
|
||||
command += std::format(" \"{}\"", path);
|
||||
break;
|
||||
case render::MP4:
|
||||
command = std::format(MP4_FORMAT, ffmpegPath, size.x, size.y, fps, path);
|
||||
command += " -vf \"format=yuv420p,scale=trunc(iw/2)*2:trunc(ih/2)*2\" -c:v libx265 -crf 20 -preset slow"
|
||||
" -tag:v hvc1 -movflags +faststart";
|
||||
if (!audioOutputArguments.empty()) command += " " + audioOutputArguments;
|
||||
command += std::format(" \"{}\"", path);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
@@ -75,6 +121,7 @@ namespace anm2ed
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
remove_audio_file();
|
||||
logger.error(std::format(FFMPEG_POPEN_ERROR, strerror(errno)));
|
||||
return false;
|
||||
}
|
||||
@@ -85,12 +132,14 @@ namespace anm2ed
|
||||
|
||||
if (fwrite(frame.pixels.data(), 1, frameSize, fp) != frameSize)
|
||||
{
|
||||
remove_audio_file();
|
||||
PCLOSE(fp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto code = PCLOSE(fp);
|
||||
remove_audio_file();
|
||||
return (code == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user