Skip to content

Commit

Permalink
Proper rebase and minor refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
khanghugo committed Dec 11, 2022
1 parent 4a2b72d commit 41a3df8
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 136 deletions.
8 changes: 4 additions & 4 deletions src/hooks/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2122,12 +2122,12 @@ pub mod exported {
pub unsafe extern "C" fn my_CL_PlayDemo_f() {
abort_on_panic(move || {
let marker = MainThreadMarker::new();
let s = CStr::from_ptr(Cmd_Argv.get(marker)(1)).to_str().unwrap();
// let s = CStr::from_ptr(Cmd_Argv.get(marker)(1)).to_str().unwrap();

demo_playback::CURRENT_DEMO.borrow_mut(marker).clear();
demo_playback::CURRENT_DEMO.borrow_mut(marker).push_str(s);
// demo_playback::CURRENT_DEMO.borrow_mut(marker).clear();
// demo_playback::CURRENT_DEMO.borrow_mut(marker).push_str(s);

capture::on_cl_playdemo_f(marker);
// capture::on_cl_playdemo_f(marker);

CL_PlayDemo_f.get(marker)();
})
Expand Down
156 changes: 29 additions & 127 deletions src/modules/capture/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
//! Video capture.
use std::mem;
use std::path::Path;

use color_eyre::eyre::Context;

use super::cvars::CVar;
use super::{demo_playback, Module};
use super::Module;
use crate::hooks::engine::{self, con_print};
use crate::modules::commands::Command;
use crate::utils::*;
Expand All @@ -23,7 +22,7 @@ impl Module for Capture {
}

fn commands(&self) -> &'static [&'static Command] {
static COMMANDS: &[&Command] = &[&BXT_CAP_START_SEGMENT, &BXT_CAP_START, &BXT_CAP_STOP];
static COMMANDS: &[&Command] = &[&BXT_CAP_START, &BXT_CAP_STOP];
COMMANDS
}

Expand All @@ -33,7 +32,7 @@ impl Module for Capture {
&BXT_CAP_VOLUME,
&BXT_CAP_SOUND_EXTRA,
&BXT_CAP_SLOWDOWN,
&BXT_CAP_SEEMLESS,
&BXT_CAP_SKIP_NON_GAMEPLAY_FRAMES,
&BXT_CAP_SAMPLING_EXPOSURE,
&BXT_CAP_FORCE_FALLBACK,
&BXT_CAP_OVERRIDE_FFMPEG_ARGS,
Expand All @@ -56,7 +55,6 @@ impl Module for Capture {
&& (engine::Sys_VID_FlipScreen.is_set(marker)
|| engine::Sys_VID_FlipScreen_old.is_set(marker))
&& engine::window_rect.is_set(marker)
&& demo_playback::DemoPlayback.is_enabled(marker)
}
}

Expand Down Expand Up @@ -99,16 +97,14 @@ Slowdown factor for the recording.
For example, `2` means that the video will be two times slower than the realtime playback. \
Especially useful for TASes.",
);
static BXT_CAP_SEEMLESS: CVar = CVar::new(
b"_bxt_cap_seemless\0",
b"1\0",
static BXT_CAP_SKIP_NON_GAMEPLAY_FRAMES: CVar = CVar::new(
b"_bxt_cap_skip_non_gameplay_frames\0",
b"2\0",
"\
Skipping recording non-gameplay frames such as one frame in main menu or loading screen.
Set to `0` to disable. Set to `1` to enable. \
Any value greater than `1` will be the extra amount of frames skipped \
as soon as a demo or a map is loaded. The recommended value for such situation \
should be `7` (six first frames will not be captured).",
Skipping recording non-gameplay frames such as main menu, loading screen, or demo load.
Set to `0` to disable. Set to `1` to enable. Default is `2`. \
Any values higher than `1` will be the extra 'gameplay' frames being skipped. \
For example, `2` means one extra gameplay frame skipped during capture.",
);
static BXT_CAP_SAMPLING_MIN_FPS: CVar = CVar::new(
b"_bxt_cap_sampling_min_fps\0",
Expand Down Expand Up @@ -212,8 +208,6 @@ enum State {
}

static STATE: MainThreadRefCell<State> = MainThreadRefCell::new(State::Idle);
static WITH_SEGMENT: MainThreadCell<bool> = MainThreadCell::new(false);
static WITH_SEGMENT_DIR: MainThreadRefCell<String> = MainThreadRefCell::new(String::new());

static BXT_CAP_START: Command = Command::new(
b"bxt_cap_start\0",
Expand All @@ -228,68 +222,10 @@ If the filename ends with `.wav`, captures only the sound.",
),
);

static BXT_CAP_START_SEGMENT: Command = Command::new(
b"bxt_cap_start_segment\0",
handler!(
"bxt_cap_start_segment [directory]
This command should be coupled with `bxt_play_run/folder`.
Starts capturing video. Each demo will have its own seperate video named after its demo.
Outputs are stored in the specified directory. Default folder name is `output`.
If demos are played within another folder, the folder name will be excluded from output.",
cap_start_segment as fn(_),
cap_start_segment_with_dir as fn(_, _)
),
);

fn cap_start(marker: MainThreadMarker) {
cap_start_with_filename(marker, "output.mp4".to_string());
}

fn cap_start_segment(marker: MainThreadMarker) {
cap_start_segment_with_dir(marker, "output".to_string());
}

fn cap_start_segment_with_dir(marker: MainThreadMarker, prefix: String) {
let mut state = WITH_SEGMENT_DIR.borrow_mut(marker);

state.clear();
state.push_str(prefix.as_str());

match std::fs::create_dir_all(state.to_string()) {
Ok(_) => (),
Err(e) => {
con_print(marker, &format!("{}\n", e));
return;
}
}

WITH_SEGMENT.set(marker, true);

drop(state);

_cap_start_segment_with_dir(marker);
}

fn _cap_start_segment_with_dir(marker: MainThreadMarker) {
let demo_fullname = demo_playback::CURRENT_DEMO.borrow(marker).to_string();

let demo_name = match Path::new(&demo_fullname).file_stem() {
Some(str) => str.to_str().unwrap(),
None => {
con_print(marker, "Cannot find any demo. Fallback to `output.mp4`\n");
"output"
}
};

cap_start_with_filename(
marker,
format!("{}/{}.mp4", WITH_SEGMENT_DIR.borrow(marker), demo_name).to_string(),
);
}

fn cap_start_with_filename(marker: MainThreadMarker, filename: String) {
if !Capture.is_enabled(marker) {
return;
Expand All @@ -309,20 +245,6 @@ fn cap_start_with_filename(marker: MainThreadMarker, filename: String) {
return;
}

if WITH_SEGMENT.get(marker) {
let remaining = demo_playback::DEMOS.borrow(marker).len();
let total = demo_playback::DEMOS_COUNT.get(marker);
con_print(
marker,
&format!(
"Recording started: {} ({}/{})\n",
filename,
total - remaining,
total
),
);
}

*state = State::Starting(filename);
}

Expand Down Expand Up @@ -579,29 +501,13 @@ pub unsafe fn on_cl_disconnect(marker: MainThreadMarker) {

// Will play another demo right after.
if cls_demos.demonum != -1 && cls_demos.demos[0][0] != 0 {
if !WITH_SEGMENT.get(marker) {
return;
}
return;
}
}

cap_stop(marker);
}

pub fn on_cl_playdemo_f(marker: MainThreadMarker) {
if WITH_SEGMENT.get(marker) {
let demonum = unsafe { (&*engine::cls_demos.get(marker)).demonum };
if demonum == -1 {
// resetting these or the on_cl_disconnect() will go wild
_cap_start_segment_with_dir(marker);
WITH_SEGMENT_DIR.borrow_mut(marker).clear();
WITH_SEGMENT.set(marker, false);
return;
}
_cap_start_segment_with_dir(marker);
}
}

static INSIDE_KEY_EVENT: MainThreadCell<bool> = MainThreadCell::new(false);

pub fn on_key_event_start(marker: MainThreadMarker) {
Expand Down Expand Up @@ -631,30 +537,26 @@ pub unsafe fn time_passed(marker: MainThreadMarker) {
// ca_uninitialized=4,
// ca_active=5
return;
} else {
// demoplayback is updated to 1 after state 4 is done.
// The current implementation will skip all the frames until some viewmodel values are set.
if (&*engine::cls_demos.get(marker)).demoplayback == 1 {
// For some reasons, the "true" first frame is in this true condition...
// That's a good thing because it technically captures all non-loading frames.
// Broken frames before that in a demo are counted as normal frames.
if (*engine::cl_stats.get(marker))[2] == 0 {
// Fallback when there is no viewmodel assigned ever. Happens when no weapons are picked up.
// Frame 7 is guaranteed to be the "start" frame most of the time.
if *engine::cls_demoframecount.get(marker) < 7 {
return;
}
} else {
// Alternative use of the cvar.
// Recording FPS must not be below demo FPS so the skip can work properly. Eg: recording 60fps of 250fps demo will be iffy.
if FRAME_SKIP.get(marker) + 1
< BXT_CAP_SKIP_NON_GAMEPLAY_FRAMES.as_f32(marker) as u32
{
FRAME_SKIP.set(marker, FRAME_SKIP.get(marker) + 1);
return;
}
}
// demoplayback is updated to 1 after state 4 is done.
// The current implementation will skip all the frames until some viewmodel values are set.
if (&*engine::cls_demos.get(marker)).demoplayback == 1 {
// For some reasons, the "true" first frame is in this true condition...
// That's a good thing because it technically captures all non-loading frames.
// Broken frames before that in a demo are counted as normal frames.
if (*engine::cl_stats.get(marker))[2] == 0 {
// Fallback when there is no viewmodel assigned ever. Happens when no weapons are picked up.
// Frame 7 is guaranteed to be the "start" frame most of the time.
if *engine::cls_demoframecount.get(marker) < 7 {
return;
}
}
// Alternative use of the cvar.
// Recording FPS must not be below demo FPS so the skip can work properly. Eg: recording 60fps of 250fps demo will be iffy.
if FRAME_SKIP.get(marker) + 1 < BXT_CAP_SKIP_NON_GAMEPLAY_FRAMES.as_f32(marker) as u32 {
FRAME_SKIP.set(marker, FRAME_SKIP.get(marker) + 1);
return;
}
}
}

Expand Down
6 changes: 1 addition & 5 deletions src/modules/demo_playback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ impl Module for DemoPlayback {
}
}

pub static DEMOS: MainThreadRefCell<Vec<Vec<u8>>> = MainThreadRefCell::new(Vec::new());
pub static DEMOS_COUNT: MainThreadCell<usize> = MainThreadCell::new(0);
pub static CURRENT_DEMO: MainThreadRefCell<String> = MainThreadRefCell::new(String::new());
static DEMOS: MainThreadRefCell<Vec<Vec<u8>>> = MainThreadRefCell::new(Vec::new());

static BXT_PLAY_RUN: Command = Command::new(
b"bxt_play_run\0",
Expand Down Expand Up @@ -185,8 +183,6 @@ pub fn queue_for_playing(

con_print(marker, &format!("Playing {} demos.\n", demos.len()));

DEMOS_COUNT.set(marker, demos.len());

drop(demos);
set_next_demo(marker);

Expand Down

0 comments on commit 41a3df8

Please sign in to comment.