Skip to content

Commit

Permalink
FEAT: Add screen recorder as a tool (Not for game footage) (#1299)
Browse files Browse the repository at this point in the history
Target 4.3 [#1165]

## Adds screen record ability for demoing apps/content/features/bugs

### Not really suitable for games in current build as it's CPU encoding.
There is a mi_venc module that I'm currently unable to work out.

- Will add a screen recorder tool to the Tweaks -> Tools -> Screen
recorder control
- Submenu with the following options:
- Toggle indicator icon - Flashes/shows an icon (depending on which app
you're in) to remind you you're recording
- Toggle countdown pulse - Pulses the screen white 3 times as a count in
to starting recording (3 - 2 - 1 - go), also does the same when stopping
to signify it's stopped.
- Toggle hotkey - Enables or disables the hotkey to start/stop recording
(Hold MENU+A for 2 seconds)
- Reset screen recorder - Hardkills ffmpeg, removes all flags and stops
any recording
- Delete all recordings - Probably not needed, but it's a quick way to
delete all recordings in the directory
- Could benefit from overclocking
- Stripped/basic version of ffmpeg with "just enough" (but has neon
support) compiled in for this function. I can recompile with more as and
when required.

<table>
    <tr>
        <td>
<img
src="https://github.com/OnionUI/Onion/assets/47260768/57818b25-ed58-4ce2-bc3f-e170b3508ee3"
alt="MENU">
        </td>
        <td>
<img
src="https://github.com/OnionUI/Onion/assets/47260768/ecbad100-1b0d-4f23-b837-5e2cd8da3010"
alt="MENU">
        </td>
    </tr>
    <tr>
        <td colspan="2" align="center">
            <br>
<a
href="https://github.com/OnionUI/Onion/assets/47260768/af670b76-8abc-4eb8-a166-ad9d94d8b4a9">VIDEO
LINK</a>
        </td>
    </tr>
</table>

---------

Co-authored-by: XK9274 <XK9274@users.noreply.github.com>
Co-authored-by: Schmurtz <Schmurtz@gmail.com>
Co-authored-by: schmurtzm <schmurtzm@users.noreply.github.com>
  • Loading branch information
4 people committed Dec 14, 2023
1 parent 4911548 commit d414814
Show file tree
Hide file tree
Showing 12 changed files with 468 additions and 2 deletions.
16 changes: 15 additions & 1 deletion src/common/system/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define RECENTLIST_HIDDEN_PATH "/mnt/SDCARD/Roms/recentlist-hidden.json"
#define RECENTLISTMIGRATED "/mnt/SDCARD/Saves/CurrentProfile/config/.recentListMigrated"
#define DEFAULT_THEME_PATH "/mnt/SDCARD/Themes/Silky by DiMo/"
#define RECORDED_DIR "/mnt/SDCARD/Media/Videos/Recorded"

typedef struct settings_s {
int volume;
Expand Down Expand Up @@ -55,6 +56,9 @@ typedef struct settings_s {
bool disable_standby;
int pwmfrequency;
bool enable_logging;
bool rec_indicator;
bool rec_hotkey;
int rec_countdown;
int blue_light_state;
int blue_light_level;
int blue_light_rgb;
Expand Down Expand Up @@ -112,7 +116,11 @@ static settings_s __default_settings = (settings_s){
.blue_light_time_off = "08:00",
.pwmfrequency = 7,
.mainui_button_x = "",
.mainui_button_y = ""};
.mainui_button_y = "",
//utility
.rec_countdown = false,
.rec_indicator = false,
.rec_hotkey = false};

void _settings_clone(settings_s *dst, settings_s *src)
{
Expand Down Expand Up @@ -189,6 +197,8 @@ void settings_load(void)
settings.mute = config_flag_get(".muteVolume");
settings.disable_standby = config_flag_get(".disableStandby");
settings.enable_logging = config_flag_get(".logging");
settings.rec_indicator = config_flag_get(".recIndicator");
settings.rec_hotkey = config_flag_get(".recHotkey");
settings.blue_light_state = config_flag_get(".blf");

if (config_flag_get(".noLowBatteryAutoSave")) // flag is deprecated, but keep compatibility
Expand All @@ -211,6 +221,7 @@ void settings_load(void)
config_get("display/blueLightTimeOff", CONFIG_STR, &settings.blue_light_time_off);
config_get("display/blueLightRGB", CONFIG_INT, &settings.blue_light_rgb);
config_get("pwmfrequency", CONFIG_INT, &settings.pwmfrequency);
config_get("recCountdown", CONFIG_INT, &settings.rec_countdown);

if (config_flag_get(".menuInverted")) { // flag is deprecated, but keep compatibility
settings.ingame_single_press = 2;
Expand Down Expand Up @@ -325,13 +336,16 @@ void settings_save(void)
config_flag_set(".muteVolume", settings.mute);
config_flag_set(".disableStandby", settings.disable_standby);
config_flag_set(".logging", settings.enable_logging);
config_flag_set(".recIndicator", settings.rec_indicator);
config_flag_set(".recHotkey", settings.rec_hotkey);
config_flag_set(".blf", settings.blue_light_state);
config_setNumber("battery/warnAt", settings.low_battery_warn_at);
config_setNumber("battery/exitAt", settings.low_battery_autosave_at);
config_setNumber("startup/app", settings.startup_application);
config_setNumber("startup/addHours", settings.time_skip);
config_setNumber("vibration", settings.vibration);
config_setNumber("startup/tab", settings.startup_tab);
config_setNumber("recCountdown", settings.rec_countdown);
config_setNumber("display/blueLightLevel", settings.blue_light_level);
config_setNumber("display/blueLightRGB", settings.blue_light_rgb);
config_setString("display/blueLightTime", settings.blue_light_time);
Expand Down
32 changes: 32 additions & 0 deletions src/common/utils/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,38 @@ FILE *file_open_ensure_path(const char *path, const char *mode)
return fopen(path, mode);
}

bool file_findNewest(const char *dir_path, char *newest_file, size_t buffer_size)
{
DIR *d;
struct dirent *dir;
struct stat file_stat;
time_t newest_mtime = 0;

d = opendir(dir_path);
if (d == NULL) {
return false;
}

bool found = false;
while ((dir = readdir(d)) != NULL) {
if (dir->d_type == DT_REG) {
char full_path[PATH_MAX];
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, dir->d_name);

if (stat(full_path, &file_stat) == 0) {
if (!found || file_stat.st_mtime > newest_mtime) {
newest_mtime = file_stat.st_mtime;
strncpy(newest_file, dir->d_name, buffer_size);
newest_file[buffer_size - 1] = '\0';
found = true;
}
}
}
}

closedir(d);
return found;
}
char *file_read_lineN(const char *filename, int n)
{
char line[STR_MAX * 4];
Expand Down
2 changes: 2 additions & 0 deletions src/common/utils/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ void file_changeKeyValue(const char *file_path, const char *key,

bool file_path_relative_to(char *path_out, const char *path_from, const char *path_to);

bool file_findNewest(const char *dir_path, char *newest_file, size_t buffer_size);

FILE *file_open_ensure_path(const char *path, const char *mode);

char *file_read_lineN(const char *filename, int n);
Expand Down
32 changes: 32 additions & 0 deletions src/keymon/keymon.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ int main(void)
int konamiCodeIndex = 0;
bool b_BTN_Not_Menu_Pressed = false;
bool b_BTN_Menu_Pressed = false;
bool a_Pressed = false;
bool power_pressed = false;
bool volUp_state = false;
bool volUp_active = false;
Expand All @@ -368,6 +369,8 @@ int main(void)
bool comboKey_volume = false;
bool comboKey_menu = false;
bool comboKey_select = false;
bool menuAndAPressed = false;
int menuAndAPressedTime = 0;

int ticks = getMilliseconds();
int hibernate_start = ticks;
Expand Down Expand Up @@ -561,6 +564,15 @@ int main(void)
case HW_BTN_MENU:

if (!temp_flag_get("disable_menu_button")) {
if (val == PRESSED) {
if (a_Pressed) {
menuAndAPressed = true;
menuAndAPressedTime = getMilliseconds();
}
}
else if (val == RELEASED) {
menuAndAPressed = false;
}
system_state_update();
comboKey_menu = menuButtonAction(val, comboKey_menu);
}
Expand All @@ -577,6 +589,18 @@ int main(void)
applyExtraButtonShortcut(1);
break;
case HW_BTN_A:
if (val == PRESSED) {
a_Pressed = true;
if (b_BTN_Menu_Pressed) {
menuAndAPressed = true;
menuAndAPressedTime = getMilliseconds();
}
}
else if (val == RELEASED) {
a_Pressed = false;
menuAndAPressed = false;
}
break;
case HW_BTN_B:
if (val == PRESSED && system_state == MODE_MAIN_UI)
temp_flag_set("launch_alt", false);
Expand Down Expand Up @@ -668,6 +692,14 @@ int main(void)
break;
}

// start screen recording after holding for >2secs
if (menuAndAPressed && (getMilliseconds() - menuAndAPressedTime >= 2000)) {
system("/mnt/SDCARD/.tmp_update/script/screen_recorder.sh toggle &");

menuAndAPressed = false;
menuAndAPressedTime = 0;
}

if (val == PRESSED && !osd_bar_activated) {
osd_hideBar();
}
Expand Down
61 changes: 61 additions & 0 deletions src/tweaks/actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "./appstate.h"
#include "./diags.h"
#include "./reset.h"
#include "./values.h"

void action_setAppShortcut(void *pt)
Expand Down Expand Up @@ -375,6 +376,66 @@ void action_setAltBrightness(void *pt)
config_flag_set(".altBrightness", ((ListItem *)pt)->value);
}

void action_toggleScreenRecIndicator(void *pt)
{
config_flag_set(".recIndicator", ((ListItem *)pt)->value);
settings.rec_indicator = ((ListItem *)pt)->value == 1;
}

void action_toggleScreenRecCountdown(void *pt)
{
config_setNumber("recCountdown", ((ListItem *)pt)->value);
settings.rec_countdown = ((ListItem *)pt)->value;
}

void action_toggleScreenRecHotkey(void *pt)
{
config_flag_set(".recHotkey", ((ListItem *)pt)->value);
settings.rec_hotkey = ((ListItem *)pt)->value == 1;
}

void action_hardKillFFmpeg(void *pt)
{
ListItem *item = (ListItem *)pt;
int status = system("/mnt/SDCARD/.tmp_update/script/screen_recorder.sh hardkill");
if (status != 0) {
list_updateStickyNote(item, "Status: Error occurred.");
}
else {
list_updateStickyNote(item, "Status: FFmpeg process stopped");
}
list_changed = true;
}

void action_deleteAllRecordings(void *pt)
{
ListItem *item = (ListItem *)pt;
int fileCheck;
fileCheck = exists("/tmp/recorder_active");

if (fileCheck) {
if (!_disable_confirm && !_confirmReset("Recording!", "You're still recording!\nAre you sure you want to\ndelete all recordings?")) {
return;
}
else {
system("/mnt/SDCARD/.tmp_update/script/screen_recorder.sh hardkill &");
strncpy(_menu_screen_recorder.items[0].sticky_note, "Status: Idle.", sizeof(_menu_screen_recorder.items[0].sticky_note) - 1);
_menu_screen_recorder.items[0].sticky_note[sizeof(_menu_screen_recorder.items[0].sticky_note) - 1] = '\0';
}
}
else {
if (!_disable_confirm && !_confirmReset("Delete?", "Are you sure you want to\ndelete all recordings?")) {
list_updateStickyNote(item, "Cancelled");
return;
}
}

system("rm -f /mnt/SDCARD/Media/Videos/Recorded/*.mp4");
list_updateStickyNote(item, "Recorded directory emptied!");
if (!_disable_confirm)
_notifyResetDone("Deleted!");
}

void action_advancedSetLcdVoltage(void *pt)
{
int value = 0x0e - ((ListItem *)pt)->value;
Expand Down
4 changes: 3 additions & 1 deletion src/tweaks/appstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ static List _menu_main;
static List _menu_system;
static List _menu_date_time;
static List _menu_system_display;
static List _menu_user_blue_light;
static List _menu_system_startup;
static List _menu_button_action;
static List _menu_button_action_mainui_menu;
Expand All @@ -72,6 +71,8 @@ static List _menu_reset_settings;
static List _menu_tools;
static List _menu_tools_m3uGenerator;
static List _menu_diagnostics;
static List _menu_screen_recorder;
static List _menu_user_blue_light;

void menu_free_all(void)
{
Expand All @@ -91,6 +92,7 @@ void menu_free_all(void)
list_free(&_menu_tools);
list_free(&_menu_tools_m3uGenerator);
list_free(&_menu_diagnostics);
list_free(&_menu_screen_recorder);
list_free(&_menu_user_blue_light);

menu_icons_free_all();
Expand Down
71 changes: 71 additions & 0 deletions src/tweaks/menus.h
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,73 @@ void menu_advanced(void *_)
header_changed = true;
}

void menu_screen_recorder(void *pt)
{
if (!_menu_screen_recorder._created) {
_menu_screen_recorder = list_createWithSticky(7, "Screen recorder setup");
list_addItemWithInfoNote(&_menu_screen_recorder,
(ListItem){
.label = "Start/stop recorder",
.sticky_note = "Status:...",
.action = tool_screenRecorder},
"Start or stop the recorder");
list_addItemWithInfoNote(&_menu_screen_recorder,
(ListItem){
.label = "Countdown (seconds)",
.sticky_note = "Specify the countdown",
.item_type = MULTIVALUE,
.value_max = 10,
.value = (int)settings.rec_countdown,
.action = action_toggleScreenRecCountdown},
"Countdown when starting recording. \n\n"
"The screen will pulse white n times \n"
"to signify recording has started/stopped");
list_addItemWithInfoNote(&_menu_screen_recorder,
(ListItem){
.label = "Toggle indicator icon",
.sticky_note = "Turn the indicator on/off",
.item_type = TOGGLE,
.value = (int)settings.rec_indicator,
.action = action_toggleScreenRecIndicator},
"Toggles the display of a\n"
"a flashing icon to remind you\n"
"that you're still recording.");
list_addItemWithInfoNote(&_menu_screen_recorder,
(ListItem){
.label = "Toggle hotkey",
.sticky_note = "Turn the hotkey (Menu+A) on/off ",
.item_type = TOGGLE,
.value = (int)settings.rec_hotkey,
.action = action_toggleScreenRecHotkey},
"Enable the hotkey function.\n\n"
"Recording can be started/stopped\n"
"with Menu+A");
list_addItemWithInfoNote(&_menu_screen_recorder,
(ListItem){
.label = "Reset screen recorder",
.sticky_note = "Hard kill ffmpeg if it's crashed",
.action = action_hardKillFFmpeg},
"Performs a hard kill of ffmpeg.\n\n"
"WARNING: If you're currently\n"
"recording, you may lose the file!");
list_addItemWithInfoNote(&_menu_screen_recorder,
(ListItem){
.label = "Delete all recordings",
.sticky_note = "Empties the recordings directory",
.action = action_deleteAllRecordings},
"Deletes all recorded videos. \n\n"
"WARNING: This action cannot\n"
"be undone!");
}

int isRecordingActive = exists("/tmp/recorder_active");
const char *recordingStatus = isRecordingActive ? "Status: Now recording..." : "Status: Idle.";
strncpy(_menu_screen_recorder.items[0].sticky_note, recordingStatus, sizeof(_menu_screen_recorder.items[0].sticky_note) - 1);
_menu_screen_recorder.items[0].sticky_note[sizeof(_menu_screen_recorder.items[0].sticky_note) - 1] = '\0';
menu_stack[++menu_level] = &_menu_screen_recorder;
header_changed = true;
}

void menu_tools_m3uGenerator(void *_)
{
if (!_menu_tools_m3uGenerator._created) {
Expand Down Expand Up @@ -803,6 +870,10 @@ void menu_tools(void *_)
"This generates a 'miyoogamelist.xml' file\n"
"which comes with some limitations, such\n"
"as no subfolder support.");
list_addItem(&_menu_tools,
(ListItem){
.label = "Screen recorder...",
.action = menu_screen_recorder});
list_addItemWithInfoNote(&_menu_tools,
(ListItem){
.label = "Regenerate game switcher list",
Expand Down
28 changes: 28 additions & 0 deletions src/tweaks/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,34 @@ void tool_sortAppsZA(void *pt)
_runCommandPopup(tools_short_names[4], "/mnt/SDCARD/.tmp_update/script/app_sorter.sh desc");
}

void tool_screenRecorder(void *pt)
{
ListItem *item = (ListItem *)pt;
char cmd[STR_MAX];
char newestFile[STR_MAX / 2];
snprintf(cmd, sizeof(cmd), "/mnt/SDCARD/.tmp_update/script/screen_recorder.sh toggle &");
int fileCheck;
fileCheck = exists("/tmp/recorder_active");

if (!fileCheck) {
list_updateStickyNote(item, "Status: Now recording...");
system(cmd);
}
else {
if (file_findNewest(RECORDED_DIR, newestFile, sizeof(newestFile))) {
char note[STR_MAX];
system(cmd);
snprintf(note, sizeof(note), "Stopped, saved as: %s", newestFile);
list_updateStickyNote(item, note);
}
else {
list_updateStickyNote(item, "Status: Recording ended, no new file found.");
snprintf(cmd, sizeof(cmd), "/mnt/SDCARD/.tmp_update/script/screen_recorder.sh hardkill &");
}
}
list_changed = true;
}

void tool_generateGsList(void *pt)
{
_runCommandPopup(tools_short_names[3], "/mnt/SDCARD/.tmp_update/script/gameswitcher_list_gen.sh");
Expand Down
Binary file added static/build/.tmp_update/bin/ffmpeg
Binary file not shown.
Binary file added static/build/.tmp_update/lib/libx264.so.155
Binary file not shown.
Binary file added static/build/.tmp_update/res/rec.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d414814

Please sign in to comment.