Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ name: Build

on:
push:
branches: [master]
tags: ['v*']
pull_request:
branches: [master]
Expand All @@ -21,6 +20,7 @@ jobs:
run: cmake --build build --config Release

- name: Upload artifacts
if: startsWith(github.ref, 'refs/tags/v')
uses: actions/upload-artifact@v4
with:
name: Krec2MP4
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ target_link_libraries(Krec2MP4Lib PUBLIC
# --- CLI executable ---
add_executable(Krec2MP4
src/main.cpp
res/app.rc
)

target_link_libraries(Krec2MP4 PRIVATE
Expand All @@ -65,6 +66,7 @@ set_target_properties(AudioCapturePlugin PROPERTIES
if(WIN32)
add_executable(Krec2MP4_GUI WIN32
src/gui_main.cpp
res/app.rc
)

target_link_libraries(Krec2MP4_GUI PRIVATE
Expand Down
Binary file added res/app.ico
Binary file not shown.
3 changes: 3 additions & 0 deletions res/app.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "../src/gui_resources.h"

IDI_APPICON ICON "app.ico"
10 changes: 7 additions & 3 deletions src/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,10 @@ bool check_ffmpeg(const std::string& ffmpeg_path) {
}

std::string make_output_path(const std::string& input_path, const std::string& output_path) {
if (!output_path.empty()) return output_path;
fs::path p(input_path);
p.replace_extension(".mp4");
fs::path p = output_path.empty() ? fs::path(input_path) : fs::path(output_path);
if (p.extension() != ".mp4") {
p.replace_extension(".mp4");
}
return p.string();
}

Expand Down Expand Up @@ -383,6 +384,9 @@ bool convert_one(const std::string& krec_path, const std::string& output_path,
converter_log(LOG_INFO, "Running emulation (%d input frames)...", krec.total_input_frames);
m64p_error ret = emu.execute();

// Flush last PBO-buffered frame before closing encoder
frame_capture_flush();

int frames_captured = frame_capture_count();
converter_log(LOG_INFO, "Emulation finished. Captured %d frames.", frames_captured);

Expand Down
57 changes: 51 additions & 6 deletions src/ffmpeg_encoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,49 @@
#include <cstring>
#include <string>

// Quality presets for 0-51 range encoders (X264_X265, AMF, NVENC)
static const QualityPreset g_presets_51[] = {
{ "Highest", 0 },
{ "High", 18 },
{ "Medium", 23 },
{ "Low", 28 },
{ "Lowest", 36 },
};

// Quality presets for 0-255 range encoders (AMF_AV1, NVENC_AV1)
static const QualityPreset g_presets_255[] = {
{ "Highest", 0 },
{ "High", 90 },
{ "Medium", 115 },
{ "Low", 140 },
{ "Lowest", 180 },
};

static const QualityFamily g_quality_families[] = {
{ EncoderFamily::X264_X265, "CRF", g_presets_51, 5, 2 },
{ EncoderFamily::AMF, "QP", g_presets_51, 5, 2 },
{ EncoderFamily::AMF_AV1, "QP", g_presets_255, 5, 2 },
{ EncoderFamily::NVENC, "CQ", g_presets_51, 5, 2 },
{ EncoderFamily::NVENC_AV1, "CQ", g_presets_255, 5, 2 },
};

const QualityFamily& get_quality_family(EncoderFamily family) {
for (const auto& qf : g_quality_families) {
if (qf.family == family) return qf;
}
return g_quality_families[0]; // fallback to X264_X265
}

// All known encoders
static const EncoderInfo g_all_encoders[] = {
{ L"H.264 (CPU)", "libx264", false },
{ L"H.265 (CPU)", "libx265", false },
{ L"H.264 (AMD GPU)", "h264_amf", true },
{ L"H.265 (AMD GPU)", "hevc_amf", true },
{ L"H.264 (NVIDIA GPU)", "h264_nvenc", true },
{ L"H.265 (NVIDIA GPU)", "hevc_nvenc", true },
{ L"H.264 (CPU)", "libx264", false, EncoderFamily::X264_X265 },
{ L"H.265 (CPU)", "libx265", false, EncoderFamily::X264_X265 },
{ L"H.264 (AMD GPU)", "h264_amf", true, EncoderFamily::AMF },
{ L"H.265 (AMD GPU)", "hevc_amf", true, EncoderFamily::AMF },
{ L"AV1 (AMD GPU)", "av1_amf", true, EncoderFamily::AMF_AV1 },
{ L"H.264 (NVIDIA GPU)", "h264_nvenc", true, EncoderFamily::NVENC },
{ L"H.265 (NVIDIA GPU)", "hevc_nvenc", true, EncoderFamily::NVENC },
{ L"AV1 (NVIDIA GPU)", "av1_nvenc", true, EncoderFamily::NVENC_AV1 },
};

#ifdef _WIN32
Expand Down Expand Up @@ -90,6 +125,11 @@ static std::string build_encoder_flags(const std::string& encoder, int crf) {
snprintf(buf, sizeof(buf), "-c:v hevc_amf -quality quality -rc cqp -qp_i %d -qp_p %d -pix_fmt yuv420p", crf, crf);
return buf;
}
if (encoder == "av1_amf") {
char buf[128];
snprintf(buf, sizeof(buf), "-c:v av1_amf -quality quality -rc cqp -qp_i %d -qp_p %d -pix_fmt yuv420p", crf, crf);
return buf;
}
if (encoder == "h264_nvenc") {
char buf[128];
snprintf(buf, sizeof(buf), "-c:v h264_nvenc -preset p7 -rc vbr -cq %d -pix_fmt yuv420p", crf);
Expand All @@ -100,6 +140,11 @@ static std::string build_encoder_flags(const std::string& encoder, int crf) {
snprintf(buf, sizeof(buf), "-c:v hevc_nvenc -preset p7 -rc vbr -cq %d -pix_fmt yuv420p", crf);
return buf;
}
if (encoder == "av1_nvenc") {
char buf[128];
snprintf(buf, sizeof(buf), "-c:v av1_nvenc -preset p7 -rc vbr -cq %d -pix_fmt yuv420p", crf);
return buf;
}
// Fallback: treat as libx264
char buf[128];
snprintf(buf, sizeof(buf), "-c:v libx264 -preset medium -crf %d -pix_fmt yuv420p", crf);
Expand Down
24 changes: 24 additions & 0 deletions src/ffmpeg_encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,34 @@
#include <string>
#include <vector>

enum class EncoderFamily {
X264_X265, // libx264, libx265 — CRF 0-51
AMF, // h264_amf, hevc_amf — QP 0-51
AMF_AV1, // av1_amf — QP 0-255
NVENC, // h264_nvenc, hevc_nvenc — CQ 0-51
NVENC_AV1, // av1_nvenc — CQ 0-255
};

struct QualityPreset {
const char* name; // e.g. "Medium"
int value; // e.g. 23 or 115
};

struct QualityFamily {
EncoderFamily family;
const char* param_name; // "CRF", "QP", or "CQ"
const QualityPreset* presets;
int num_presets;
int default_index; // index of default preset
};

const QualityFamily& get_quality_family(EncoderFamily family);

struct EncoderInfo {
const wchar_t* label;
const char* codec;
bool hw; // true = needs hardware probe
EncoderFamily family;
};

// Returns the subset of known encoders available on this system.
Expand Down
Loading