mirror of
https://github.com/libretro/RetroArch.git
synced 2025-02-04 00:06:11 +00:00
Merge pull request #13216 from libretro/splitup-record
Split up recording code
This commit is contained in:
commit
1280151d13
@ -234,6 +234,7 @@ endif
|
||||
|
||||
OBJ += frontend/frontend_driver.o \
|
||||
retroarch.o \
|
||||
record/record_driver.o \
|
||||
command.o \
|
||||
msg_hash.o \
|
||||
midi_driver.o \
|
||||
@ -1093,13 +1094,13 @@ endif
|
||||
ifeq ($(HAVE_VIDEO_LAYOUT), 1)
|
||||
DEFINES += -DHAVE_VIDEO_LAYOUT
|
||||
OBJ += \
|
||||
gfx/video_layout.o \
|
||||
gfx/video_layout/view.o \
|
||||
gfx/video_layout/element.o \
|
||||
gfx/video_layout/component.o \
|
||||
gfx/video_layout/internal.o \
|
||||
gfx/video_layout/scope.o \
|
||||
gfx/video_layout/load.o
|
||||
gfx/video_layout.o \
|
||||
gfx/video_layout/view.o \
|
||||
gfx/video_layout/element.o \
|
||||
gfx/video_layout/component.o \
|
||||
gfx/video_layout/internal.o \
|
||||
gfx/video_layout/scope.o \
|
||||
gfx/video_layout/load.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_STB_FONT), 1)
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "../retroarch.h"
|
||||
#include "../list_special.h"
|
||||
#include "../file_path_special.h"
|
||||
#include "../record/record_driver.h"
|
||||
#include "../tasks/task_content.h"
|
||||
#include "../verbosity.h"
|
||||
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "verbosity.h"
|
||||
|
||||
#include "audio/audio_driver.h"
|
||||
#include "record/record_driver.h"
|
||||
#include "gfx/gfx_animation.h"
|
||||
|
||||
#include "tasks/task_content.h"
|
||||
@ -1433,6 +1434,7 @@ static struct config_path_setting *populate_settings_path(
|
||||
settings_t *settings, int *size)
|
||||
{
|
||||
unsigned count = 0;
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
struct config_path_setting *tmp = (struct config_path_setting*)calloc(1, (*size + 1) * sizeof(struct config_path_setting));
|
||||
|
||||
if (!tmp)
|
||||
@ -1562,16 +1564,10 @@ static struct config_path_setting *populate_settings_path(
|
||||
"screenshot_directory",
|
||||
settings->paths.directory_screenshot, true, NULL, false);
|
||||
|
||||
{
|
||||
global_t *global = global_get_ptr();
|
||||
if (global)
|
||||
{
|
||||
SETTING_PATH("recording_output_directory",
|
||||
global->record.output_dir, false, NULL, true);
|
||||
SETTING_PATH("recording_config_directory",
|
||||
global->record.config_dir, false, NULL, true);
|
||||
}
|
||||
}
|
||||
SETTING_PATH("recording_output_directory",
|
||||
recording_st->output_dir, false, NULL, true);
|
||||
SETTING_PATH("recording_config_directory",
|
||||
recording_st->config_dir, false, NULL, true);
|
||||
|
||||
SETTING_ARRAY("log_dir", settings->paths.log_dir, true, NULL, true);
|
||||
|
||||
@ -2383,6 +2379,7 @@ void config_set_defaults(void *data)
|
||||
#endif
|
||||
global_t *global = (global_t*)data;
|
||||
settings_t *settings = config_st;
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
int bool_settings_size = sizeof(settings->bools) / sizeof(settings->bools.placeholder);
|
||||
int float_settings_size = sizeof(settings->floats) / sizeof(settings->floats.placeholder);
|
||||
int int_settings_size = sizeof(settings->ints) / sizeof(settings->ints.placeholder);
|
||||
@ -2668,23 +2665,20 @@ void config_set_defaults(void *data)
|
||||
retroarch_ctl(RARCH_CTL_UNSET_BPS_PREF, NULL);
|
||||
retroarch_ctl(RARCH_CTL_UNSET_IPS_PREF, NULL);
|
||||
|
||||
if (global)
|
||||
{
|
||||
*global->record.output_dir = '\0';
|
||||
*global->record.config_dir = '\0';
|
||||
}
|
||||
*recording_st->output_dir = '\0';
|
||||
*recording_st->config_dir = '\0';
|
||||
|
||||
*settings->paths.path_core_options = '\0';
|
||||
*settings->paths.path_content_favorites = '\0';
|
||||
*settings->paths.path_content_history = '\0';
|
||||
*settings->paths.path_core_options = '\0';
|
||||
*settings->paths.path_content_favorites = '\0';
|
||||
*settings->paths.path_content_history = '\0';
|
||||
*settings->paths.path_content_image_history = '\0';
|
||||
*settings->paths.path_content_music_history = '\0';
|
||||
*settings->paths.path_content_video_history = '\0';
|
||||
*settings->paths.path_cheat_settings = '\0';
|
||||
*settings->paths.path_cheat_settings = '\0';
|
||||
#if !defined(__APPLE__)
|
||||
*settings->arrays.bundle_assets_src = '\0';
|
||||
*settings->arrays.bundle_assets_dst = '\0';
|
||||
*settings->arrays.bundle_assets_dst_subdir = '\0';
|
||||
*settings->arrays.bundle_assets_src = '\0';
|
||||
*settings->arrays.bundle_assets_dst = '\0';
|
||||
*settings->arrays.bundle_assets_dst_subdir = '\0';
|
||||
#endif
|
||||
*settings->paths.path_cheat_database = '\0';
|
||||
*settings->paths.path_menu_wallpaper = '\0';
|
||||
@ -2872,13 +2866,13 @@ void config_set_defaults(void *data)
|
||||
g_defaults.dirs[DEFAULT_DIR_LOGS]);
|
||||
|
||||
if (!string_is_empty(g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT]))
|
||||
fill_pathname_expand_special(global->record.output_dir,
|
||||
fill_pathname_expand_special(recording_st->output_dir,
|
||||
g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT],
|
||||
sizeof(global->record.output_dir));
|
||||
sizeof(recording_st->output_dir));
|
||||
if (!string_is_empty(g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG]))
|
||||
fill_pathname_expand_special(global->record.config_dir,
|
||||
fill_pathname_expand_special(recording_st->config_dir,
|
||||
g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG],
|
||||
sizeof(global->record.config_dir));
|
||||
sizeof(recording_st->config_dir));
|
||||
|
||||
if (!string_is_empty(g_defaults.path_config))
|
||||
{
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "../../dynamic.h"
|
||||
|
||||
#include "../../retroarch.h"
|
||||
#include "../../record/record_driver.h"
|
||||
#include "../../verbosity.h"
|
||||
#include "../common/gl2_common.h"
|
||||
|
||||
@ -3617,7 +3618,8 @@ static void *gl2_init(const video_info_t *video,
|
||||
const char *version = NULL;
|
||||
struct retro_hw_render_callback *hwr = NULL;
|
||||
char *error_string = NULL;
|
||||
gl2_t *gl = (gl2_t*)calloc(1, sizeof(gl2_t));
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
gl2_t *gl = (gl2_t*)calloc(1, sizeof(gl2_t));
|
||||
const gfx_ctx_driver_t *ctx_driver = gl2_get_context(gl);
|
||||
|
||||
if (!gl || !ctx_driver)
|
||||
@ -3953,12 +3955,12 @@ static void *gl2_init(const video_info_t *video,
|
||||
FONT_DRIVER_RENDER_OPENGL_API);
|
||||
|
||||
/* Only bother with PBO readback if we're doing GPU recording.
|
||||
* Check recording_is_enabled() and not
|
||||
* Check recording_st->enable and not
|
||||
* driver.recording_data, because recording is
|
||||
* not initialized yet.
|
||||
*/
|
||||
gl->pbo_readback_enable = video_gpu_record
|
||||
&& recording_is_enabled();
|
||||
&& recording_st->enable;
|
||||
|
||||
if (gl->pbo_readback_enable && gl2_init_pbo_readback(gl))
|
||||
{
|
||||
|
@ -45,6 +45,7 @@
|
||||
#endif
|
||||
|
||||
#include "../font_driver.h"
|
||||
#include "../../record/record_driver.h"
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
#include "../../menu/menu_driver.h"
|
||||
@ -1331,7 +1332,8 @@ static void *gl_core_init(const video_info_t *video,
|
||||
FONT_DRIVER_RENDER_OPENGL_CORE_API);
|
||||
}
|
||||
|
||||
gl->pbo_readback_enable = video_gpu_record && recording_is_enabled();
|
||||
gl->pbo_readback_enable = video_gpu_record
|
||||
&& recording_state_get_ptr()->enable;
|
||||
|
||||
if (gl->pbo_readback_enable && gl_core_init_pbo_readback(gl))
|
||||
{
|
||||
|
@ -52,6 +52,7 @@
|
||||
|
||||
#include "../../retroarch.h"
|
||||
#include "../../verbosity.h"
|
||||
#include "../../record/record_driver.h"
|
||||
|
||||
#include "../video_coord_array.h"
|
||||
|
||||
@ -1131,12 +1132,14 @@ static void vulkan_init_hw_render(vk_t *vk)
|
||||
static void vulkan_init_readback(vk_t *vk)
|
||||
{
|
||||
/* Only bother with this if we're doing GPU recording.
|
||||
* Check recording_is_enabled() and not
|
||||
* Check recording_st->enable and not
|
||||
* driver.recording_data, because recording is
|
||||
* not initialized yet.
|
||||
*/
|
||||
settings_t *settings = config_get_ptr();
|
||||
bool recording_enabled = recording_is_enabled();
|
||||
recording_state_t
|
||||
*recording_st = recording_state_get_ptr();
|
||||
bool recording_enabled = recording_st->enable;
|
||||
bool video_gpu_record = settings->bools.video_gpu_record;
|
||||
vk->readback.streamed = video_gpu_record && recording_enabled;
|
||||
|
||||
|
@ -50,6 +50,7 @@
|
||||
|
||||
#include "../audio/audio_driver.h"
|
||||
#include "../frontend/frontend_driver.h"
|
||||
#include "../record/record_driver.h"
|
||||
#include "../ui/ui_companion_driver.h"
|
||||
#include "../driver.h"
|
||||
#include "../file_path_special.h"
|
||||
|
@ -1271,6 +1271,7 @@ WIFI
|
||||
/*============================================================
|
||||
RECORDING
|
||||
============================================================ */
|
||||
#include "../record/record_driver.c"
|
||||
#ifdef HAVE_FFMPEG
|
||||
#include "../record/drivers/record_ffmpeg.c"
|
||||
#endif
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "../../configuration.h"
|
||||
#include "../../core_info.h"
|
||||
#include "../../audio/audio_driver.h"
|
||||
#include "../../record/record_driver.h"
|
||||
#include "../../frontend/frontend_driver.h"
|
||||
#include "../../defaults.h"
|
||||
#include "../../core_option_manager.h"
|
||||
@ -552,6 +553,7 @@ int generic_action_ok_displaylist_push(const char *path,
|
||||
#endif
|
||||
const char *dir_menu_content = settings->paths.directory_menu_content;
|
||||
const char *dir_libretro = settings->paths.directory_libretro;
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
|
||||
if (!menu || string_is_equal(menu_ident, "null"))
|
||||
{
|
||||
@ -1070,7 +1072,7 @@ int generic_action_ok_displaylist_push(const char *path,
|
||||
global_t *global = global_get_ptr();
|
||||
info.type = type;
|
||||
info.directory_ptr = idx;
|
||||
info_path = global->record.config_dir;
|
||||
info_path = recording_st->config_dir;
|
||||
info_label = label;
|
||||
dl_type = DISPLAYLIST_FILE_BROWSER_SELECT_FILE;
|
||||
}
|
||||
@ -1081,7 +1083,7 @@ int generic_action_ok_displaylist_push(const char *path,
|
||||
global_t *global = global_get_ptr();
|
||||
info.type = type;
|
||||
info.directory_ptr = idx;
|
||||
info_path = global->record.config_dir;
|
||||
info_path = recording_st->config_dir;
|
||||
info_label = label;
|
||||
dl_type = DISPLAYLIST_FILE_BROWSER_SELECT_FILE;
|
||||
}
|
||||
|
@ -76,6 +76,7 @@
|
||||
#endif
|
||||
|
||||
#include "../audio/audio_driver.h"
|
||||
#include "../record/record_driver.h"
|
||||
#include "menu_cbs.h"
|
||||
#include "menu_driver.h"
|
||||
#include "menu_entries.h"
|
||||
@ -3000,7 +3001,8 @@ static int menu_displaylist_parse_load_content_settings(
|
||||
|
||||
if (string_is_not_equal(settings->arrays.record_driver, "null"))
|
||||
{
|
||||
if (!recording_is_enabled())
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
if (!recording_st->enable)
|
||||
{
|
||||
if (settings->bools.quick_menu_show_start_recording && !settings->bools.kiosk_mode_enable)
|
||||
{
|
||||
@ -3022,7 +3024,8 @@ static int menu_displaylist_parse_load_content_settings(
|
||||
}
|
||||
else
|
||||
{
|
||||
if (streaming_is_enabled())
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
if (recording_st->streaming_enable)
|
||||
{
|
||||
if (menu_entries_append_enum(list,
|
||||
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QUICK_MENU_STOP_STREAMING),
|
||||
|
@ -84,6 +84,7 @@
|
||||
#include "../wifi/wifi_driver.h"
|
||||
#include "../midi_driver.h"
|
||||
#include "../location_driver.h"
|
||||
#include "../record/record_driver.h"
|
||||
#include "../tasks/tasks_internal.h"
|
||||
#include "../config.def.h"
|
||||
#include "../ui/ui_companion_driver.h"
|
||||
@ -8885,6 +8886,7 @@ static bool setting_append_list(
|
||||
unsigned user;
|
||||
rarch_setting_group_info_t group_info;
|
||||
rarch_setting_group_info_t subgroup_info;
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
|
||||
group_info.name = NULL;
|
||||
subgroup_info.name = NULL;
|
||||
@ -13731,8 +13733,8 @@ static bool setting_append_list(
|
||||
|
||||
CONFIG_DIR(
|
||||
list, list_info,
|
||||
global->record.output_dir,
|
||||
sizeof(global->record.output_dir),
|
||||
recording_st->output_dir,
|
||||
sizeof(recording_st->output_dir),
|
||||
MENU_ENUM_LABEL_RECORDING_OUTPUT_DIRECTORY,
|
||||
MENU_ENUM_LABEL_VALUE_RECORDING_OUTPUT_DIRECTORY,
|
||||
g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT],
|
||||
@ -20038,8 +20040,8 @@ static bool setting_append_list(
|
||||
{
|
||||
CONFIG_DIR(
|
||||
list, list_info,
|
||||
global->record.output_dir,
|
||||
sizeof(global->record.output_dir),
|
||||
recording_st->output_dir,
|
||||
sizeof(recording_st->output_dir),
|
||||
MENU_ENUM_LABEL_RECORDING_OUTPUT_DIRECTORY,
|
||||
MENU_ENUM_LABEL_VALUE_RECORDING_OUTPUT_DIRECTORY,
|
||||
g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT],
|
||||
@ -20053,8 +20055,8 @@ static bool setting_append_list(
|
||||
|
||||
CONFIG_DIR(
|
||||
list, list_info,
|
||||
global->record.config_dir,
|
||||
sizeof(global->record.config_dir),
|
||||
recording_st->config_dir,
|
||||
sizeof(recording_st->config_dir),
|
||||
MENU_ENUM_LABEL_RECORDING_CONFIG_DIRECTORY,
|
||||
MENU_ENUM_LABEL_VALUE_RECORDING_CONFIG_DIRECTORY,
|
||||
g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG],
|
||||
|
@ -42,6 +42,8 @@
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#include "../record_driver.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
479
record/record_driver.c
Normal file
479
record/record_driver.c
Normal file
@ -0,0 +1,479 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2021 - Daniel De Matteis
|
||||
* Copyright (C) 2016-2019 - Andr<EFBFBD>s Su<EFBFBD>rez
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <compat/strl.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <file/file_path.h>
|
||||
#include <retro_math.h>
|
||||
|
||||
#include "../configuration.h"
|
||||
#include "../list_special.h"
|
||||
#include "../gfx/video_driver.h"
|
||||
#include "../paths.h"
|
||||
#include "../retroarch.h"
|
||||
#include "../runloop.h"
|
||||
#include "../verbosity.h"
|
||||
|
||||
#include "record_driver.h"
|
||||
|
||||
static recording_state_t recording_state = {0};
|
||||
|
||||
static const record_driver_t record_null = {
|
||||
NULL, /* new */
|
||||
NULL, /* free */
|
||||
NULL, /* push_video */
|
||||
NULL, /* push_audio */
|
||||
NULL, /* finalize */
|
||||
"null",
|
||||
};
|
||||
|
||||
const record_driver_t *record_drivers[] = {
|
||||
#ifdef HAVE_FFMPEG
|
||||
&record_ffmpeg,
|
||||
#endif
|
||||
&record_null,
|
||||
NULL,
|
||||
};
|
||||
|
||||
recording_state_t *recording_state_get_ptr(void)
|
||||
{
|
||||
return &recording_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* config_get_record_driver_options:
|
||||
*
|
||||
* Get an enumerated list of all record driver names, separated by '|'.
|
||||
*
|
||||
* Returns: string listing of all record driver names, separated by '|'.
|
||||
**/
|
||||
const char* config_get_record_driver_options(void)
|
||||
{
|
||||
return char_list_new_special(STRING_LIST_RECORD_DRIVERS, NULL);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* TODO/FIXME - not used apparently */
|
||||
static void find_record_driver(const char *prefix,
|
||||
bool verbosity_enabled)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
int i = (int)driver_find_index(
|
||||
"record_driver",
|
||||
settings->arrays.record_driver);
|
||||
|
||||
if (i >= 0)
|
||||
recording_state.driver = (const record_driver_t*)record_drivers[i];
|
||||
else
|
||||
{
|
||||
if (verbosity_enabled)
|
||||
{
|
||||
unsigned d;
|
||||
|
||||
RARCH_ERR("[recording] Couldn't find any %s named \"%s\"\n", prefix,
|
||||
settings->arrays.record_driver);
|
||||
RARCH_LOG_OUTPUT("Available %ss are:\n", prefix);
|
||||
for (d = 0; record_drivers[d]; d++)
|
||||
RARCH_LOG_OUTPUT("\t%s\n", record_drivers[d].ident);
|
||||
RARCH_WARN("[recording] Going to default to first %s...\n", prefix);
|
||||
}
|
||||
|
||||
recording_state.driver = (const record_driver_t*)record_drivers[0];
|
||||
|
||||
if (!recording_state.driver)
|
||||
retroarch_fail(1, "find_record_driver()");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ffemu_find_backend:
|
||||
* @ident : Identifier of driver to find.
|
||||
*
|
||||
* Finds a recording driver with the name @ident.
|
||||
*
|
||||
* Returns: recording driver handle if successful, otherwise
|
||||
* NULL.
|
||||
**/
|
||||
static const record_driver_t *ffemu_find_backend(const char *ident)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; record_drivers[i]; i++)
|
||||
{
|
||||
if (string_is_equal(record_drivers[i]->ident, ident))
|
||||
return record_drivers[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void recording_driver_free_state(void)
|
||||
{
|
||||
/* TODO/FIXME - this is not being called anywhere */
|
||||
recording_state.gpu_width = 0;
|
||||
recording_state.gpu_height = 0;
|
||||
recording_state.width = 0;
|
||||
recording_stte.height = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* gfx_ctx_init_first:
|
||||
* @backend : Recording backend handle.
|
||||
* @data : Recording data handle.
|
||||
* @params : Recording info parameters.
|
||||
*
|
||||
* Finds first suitable recording context driver and initializes.
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
**/
|
||||
static bool record_driver_init_first(
|
||||
const record_driver_t **backend, void **data,
|
||||
const struct record_params *params)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; record_drivers[i]; i++)
|
||||
{
|
||||
void *handle = record_drivers[i]->init(params);
|
||||
|
||||
if (!handle)
|
||||
continue;
|
||||
|
||||
*backend = record_drivers[i];
|
||||
*data = handle;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool recording_deinit(void)
|
||||
{
|
||||
recording_state_t *recording_st = &recording_state;
|
||||
if ( !recording_st->data
|
||||
|| !recording_st->driver)
|
||||
return false;
|
||||
|
||||
if (recording_st->driver->finalize)
|
||||
recording_st->driver->finalize(recording_st->data);
|
||||
|
||||
if (recording_st->driver->free)
|
||||
recording_st->driver->free(recording_st->data);
|
||||
|
||||
recording_st->data = NULL;
|
||||
recording_st->driver = NULL;
|
||||
|
||||
video_driver_gpu_record_deinit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void streaming_set_state(bool state)
|
||||
{
|
||||
recording_state_t *recording_st = &recording_state;
|
||||
recording_st->streaming_enable = state;
|
||||
}
|
||||
|
||||
bool recording_init(void)
|
||||
{
|
||||
char output[PATH_MAX_LENGTH];
|
||||
char buf[PATH_MAX_LENGTH];
|
||||
struct record_params params = {0};
|
||||
settings_t *settings = config_get_ptr();
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
struct retro_system_av_info *av_info = &video_st->av_info;
|
||||
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
||||
bool video_gpu_record = settings->bools.video_gpu_record;
|
||||
bool video_force_aspect = settings->bools.video_force_aspect;
|
||||
const enum rarch_core_type
|
||||
current_core_type = runloop_st->current_core_type;
|
||||
const enum retro_pixel_format
|
||||
video_driver_pix_fmt = video_st->pix_fmt;
|
||||
recording_state_t *recording_st = &recording_state;
|
||||
bool recording_enable = recording_st->enable;
|
||||
|
||||
if (!recording_enable)
|
||||
return false;
|
||||
|
||||
output[0] = '\0';
|
||||
|
||||
if (current_core_type == CORE_TYPE_DUMMY)
|
||||
{
|
||||
RARCH_WARN("[recording] %s\n",
|
||||
msg_hash_to_str(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!video_gpu_record && video_driver_is_hw_context())
|
||||
{
|
||||
RARCH_WARN("[recording] %s.\n",
|
||||
msg_hash_to_str(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING));
|
||||
return false;
|
||||
}
|
||||
|
||||
RARCH_LOG("[recording] %s: FPS: %.4f, Sample rate: %.4f\n",
|
||||
msg_hash_to_str(MSG_CUSTOM_TIMING_GIVEN),
|
||||
(float)av_info->timing.fps,
|
||||
(float)av_info->timing.sample_rate);
|
||||
|
||||
if (!string_is_empty(recording_st->path))
|
||||
strlcpy(output, recording_st->path, sizeof(output));
|
||||
else
|
||||
{
|
||||
const char *stream_url = settings->paths.path_stream_url;
|
||||
unsigned video_record_quality = settings->uints.video_record_quality;
|
||||
unsigned video_stream_port = settings->uints.video_stream_port;
|
||||
if (recording_st->streaming_enable)
|
||||
if (!string_is_empty(stream_url))
|
||||
strlcpy(output, stream_url, sizeof(output));
|
||||
else
|
||||
/* Fallback, stream locally to 127.0.0.1 */
|
||||
snprintf(output, sizeof(output), "udp://127.0.0.1:%u",
|
||||
video_stream_port);
|
||||
else
|
||||
{
|
||||
const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME));
|
||||
/* Fallback to core name if started without content */
|
||||
if (string_is_empty(game_name))
|
||||
game_name = runloop_st->system.info.library_name;
|
||||
|
||||
if (video_record_quality < RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST)
|
||||
{
|
||||
fill_str_dated_filename(buf, game_name,
|
||||
"mkv", sizeof(buf));
|
||||
fill_pathname_join(output, recording_st->output_dir, buf, sizeof(output));
|
||||
}
|
||||
else if (video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST
|
||||
&& video_record_quality < RECORD_CONFIG_TYPE_RECORDING_GIF)
|
||||
{
|
||||
fill_str_dated_filename(buf, game_name,
|
||||
"webm", sizeof(buf));
|
||||
fill_pathname_join(output, recording_st->output_dir, buf, sizeof(output));
|
||||
}
|
||||
else if (video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_GIF
|
||||
&& video_record_quality < RECORD_CONFIG_TYPE_RECORDING_APNG)
|
||||
{
|
||||
fill_str_dated_filename(buf, game_name,
|
||||
"gif", sizeof(buf));
|
||||
fill_pathname_join(output, recording_st->output_dir, buf, sizeof(output));
|
||||
}
|
||||
else
|
||||
{
|
||||
fill_str_dated_filename(buf, game_name,
|
||||
"png", sizeof(buf));
|
||||
fill_pathname_join(output, recording_st->output_dir, buf, sizeof(output));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.audio_resampler = settings->arrays.audio_resampler;
|
||||
params.video_gpu_record = settings->bools.video_gpu_record;
|
||||
params.video_record_scale_factor = settings->uints.video_record_scale_factor;
|
||||
params.video_stream_scale_factor = settings->uints.video_stream_scale_factor;
|
||||
params.video_record_threads = settings->uints.video_record_threads;
|
||||
params.streaming_mode = settings->uints.streaming_mode;
|
||||
|
||||
params.out_width = av_info->geometry.base_width;
|
||||
params.out_height = av_info->geometry.base_height;
|
||||
params.fb_width = av_info->geometry.max_width;
|
||||
params.fb_height = av_info->geometry.max_height;
|
||||
params.channels = 2;
|
||||
params.filename = output;
|
||||
params.fps = av_info->timing.fps;
|
||||
params.samplerate = av_info->timing.sample_rate;
|
||||
params.pix_fmt =
|
||||
(video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888)
|
||||
? FFEMU_PIX_ARGB8888
|
||||
: FFEMU_PIX_RGB565;
|
||||
params.config = NULL;
|
||||
|
||||
if (!string_is_empty(recording_st->config))
|
||||
params.config = recording_st->config;
|
||||
else
|
||||
{
|
||||
if (recording_st->streaming_enable)
|
||||
{
|
||||
params.config = settings->paths.path_stream_config;
|
||||
params.preset = (enum record_config_type)
|
||||
settings->uints.video_stream_quality;
|
||||
}
|
||||
else
|
||||
{
|
||||
params.config = settings->paths.path_record_config;
|
||||
params.preset = (enum record_config_type)
|
||||
settings->uints.video_record_quality;
|
||||
}
|
||||
}
|
||||
|
||||
if (settings->bools.video_gpu_record
|
||||
&& video_st->current_video->read_viewport)
|
||||
{
|
||||
unsigned gpu_size;
|
||||
struct video_viewport vp;
|
||||
|
||||
vp.x = 0;
|
||||
vp.y = 0;
|
||||
vp.width = 0;
|
||||
vp.height = 0;
|
||||
vp.full_width = 0;
|
||||
vp.full_height = 0;
|
||||
|
||||
video_driver_get_viewport_info(&vp);
|
||||
|
||||
if (!vp.width || !vp.height)
|
||||
{
|
||||
RARCH_ERR("[recording] Failed to get viewport information from video driver. "
|
||||
"Cannot start recording ...\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
params.out_width = vp.width;
|
||||
params.out_height = vp.height;
|
||||
params.fb_width = next_pow2(vp.width);
|
||||
params.fb_height = next_pow2(vp.height);
|
||||
|
||||
if (video_force_aspect &&
|
||||
(video_st->aspect_ratio > 0.0f))
|
||||
params.aspect_ratio = video_st->aspect_ratio;
|
||||
else
|
||||
params.aspect_ratio = (float)vp.width / vp.height;
|
||||
|
||||
params.pix_fmt = FFEMU_PIX_BGR24;
|
||||
recording_st->gpu_width = vp.width;
|
||||
recording_st->gpu_height = vp.height;
|
||||
|
||||
RARCH_LOG("[recording] %s %u x %u\n", msg_hash_to_str(MSG_DETECTED_VIEWPORT_OF),
|
||||
vp.width, vp.height);
|
||||
|
||||
gpu_size = vp.width * vp.height * 3;
|
||||
if (!(video_st->record_gpu_buffer = (uint8_t*)malloc(gpu_size)))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (recording_state.width || recording_state.height)
|
||||
{
|
||||
params.out_width = recording_state.width;
|
||||
params.out_height = recording_state.height;
|
||||
}
|
||||
|
||||
if (video_force_aspect &&
|
||||
(video_st->aspect_ratio > 0.0f))
|
||||
params.aspect_ratio = video_st->aspect_ratio;
|
||||
else
|
||||
params.aspect_ratio = (float)params.out_width / params.out_height;
|
||||
|
||||
#ifdef HAVE_VIDEO_FILTER
|
||||
if (settings->bools.video_post_filter_record
|
||||
&& !!video_st->state_filter)
|
||||
{
|
||||
unsigned max_width = 0;
|
||||
unsigned max_height = 0;
|
||||
|
||||
params.pix_fmt = FFEMU_PIX_RGB565;
|
||||
|
||||
if (video_st->state_out_rgb32)
|
||||
params.pix_fmt = FFEMU_PIX_ARGB8888;
|
||||
|
||||
rarch_softfilter_get_max_output_size(
|
||||
video_st->state_filter,
|
||||
&max_width, &max_height);
|
||||
params.fb_width = next_pow2(max_width);
|
||||
params.fb_height = next_pow2(max_height);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
RARCH_LOG("[recording] %s %s @ %ux%u. (FB size: %ux%u pix_fmt: %u)\n",
|
||||
msg_hash_to_str(MSG_RECORDING_TO),
|
||||
output,
|
||||
params.out_width, params.out_height,
|
||||
params.fb_width, params.fb_height,
|
||||
(unsigned)params.pix_fmt);
|
||||
|
||||
if (!record_driver_init_first(
|
||||
&recording_state.driver,
|
||||
&recording_state.data, ¶ms))
|
||||
{
|
||||
RARCH_ERR("[recording] %s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_START_RECORDING));
|
||||
video_driver_gpu_record_deinit();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void recording_driver_update_streaming_url(void)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
const char *youtube_url = "rtmp://a.rtmp.youtube.com/live2/";
|
||||
const char *twitch_url = "rtmp://live.twitch.tv/app/";
|
||||
const char *facebook_url = "rtmps://live-api-s.facebook.com:443/rtmp/";
|
||||
|
||||
if (!settings)
|
||||
return;
|
||||
|
||||
switch (settings->uints.streaming_mode)
|
||||
{
|
||||
case STREAMING_MODE_TWITCH:
|
||||
if (!string_is_empty(settings->arrays.twitch_stream_key))
|
||||
{
|
||||
strlcpy(settings->paths.path_stream_url,
|
||||
twitch_url,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
strlcat(settings->paths.path_stream_url,
|
||||
settings->arrays.twitch_stream_key,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
}
|
||||
break;
|
||||
case STREAMING_MODE_YOUTUBE:
|
||||
if (!string_is_empty(settings->arrays.youtube_stream_key))
|
||||
{
|
||||
strlcpy(settings->paths.path_stream_url,
|
||||
youtube_url,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
strlcat(settings->paths.path_stream_url,
|
||||
settings->arrays.youtube_stream_key,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
}
|
||||
break;
|
||||
case STREAMING_MODE_LOCAL:
|
||||
/* TODO: figure out default interface and bind to that instead */
|
||||
snprintf(settings->paths.path_stream_url, sizeof(settings->paths.path_stream_url),
|
||||
"udp://%s:%u", "127.0.0.1", settings->uints.video_stream_port);
|
||||
break;
|
||||
case STREAMING_MODE_CUSTOM:
|
||||
default:
|
||||
/* Do nothing, let the user input the URL */
|
||||
break;
|
||||
case STREAMING_MODE_FACEBOOK:
|
||||
if (!string_is_empty(settings->arrays.facebook_stream_key))
|
||||
{
|
||||
strlcpy(settings->paths.path_stream_url,
|
||||
facebook_url,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
strlcat(settings->paths.path_stream_url,
|
||||
settings->arrays.facebook_stream_key,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
166
record/record_driver.h
Normal file
166
record/record_driver.h
Normal file
@ -0,0 +1,166 @@
|
||||
#ifndef _RECORD_DRIVER_H
|
||||
#define _RECORD_DRIVER_H
|
||||
|
||||
#include <boolean.h>
|
||||
|
||||
enum ffemu_pix_format
|
||||
{
|
||||
FFEMU_PIX_RGB565 = 0,
|
||||
FFEMU_PIX_BGR24,
|
||||
FFEMU_PIX_ARGB8888
|
||||
};
|
||||
|
||||
enum streaming_mode
|
||||
{
|
||||
STREAMING_MODE_TWITCH = 0,
|
||||
STREAMING_MODE_YOUTUBE,
|
||||
STREAMING_MODE_FACEBOOK,
|
||||
STREAMING_MODE_LOCAL,
|
||||
STREAMING_MODE_CUSTOM
|
||||
};
|
||||
|
||||
enum record_config_type
|
||||
{
|
||||
RECORD_CONFIG_TYPE_RECORDING_CUSTOM = 0,
|
||||
RECORD_CONFIG_TYPE_RECORDING_LOW_QUALITY,
|
||||
RECORD_CONFIG_TYPE_RECORDING_MED_QUALITY,
|
||||
RECORD_CONFIG_TYPE_RECORDING_HIGH_QUALITY,
|
||||
RECORD_CONFIG_TYPE_RECORDING_LOSSLESS_QUALITY,
|
||||
RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST,
|
||||
RECORD_CONFIG_TYPE_RECORDING_WEBM_HIGH_QUALITY,
|
||||
RECORD_CONFIG_TYPE_RECORDING_GIF,
|
||||
RECORD_CONFIG_TYPE_RECORDING_APNG,
|
||||
RECORD_CONFIG_TYPE_STREAMING_CUSTOM,
|
||||
RECORD_CONFIG_TYPE_STREAMING_LOW_QUALITY,
|
||||
RECORD_CONFIG_TYPE_STREAMING_MED_QUALITY,
|
||||
RECORD_CONFIG_TYPE_STREAMING_HIGH_QUALITY,
|
||||
RECORD_CONFIG_TYPE_STREAMING_NETPLAY
|
||||
};
|
||||
|
||||
/* Parameters passed to ffemu_new() */
|
||||
struct record_params
|
||||
{
|
||||
/* Framerate per second of input video. */
|
||||
double fps;
|
||||
/* Sample rate of input audio. */
|
||||
double samplerate;
|
||||
|
||||
/* Filename to dump to. */
|
||||
const char *filename;
|
||||
|
||||
/* Path to config. Optional. */
|
||||
const char *config;
|
||||
|
||||
const char *audio_resampler;
|
||||
|
||||
/* Desired output resolution. */
|
||||
unsigned out_width;
|
||||
unsigned out_height;
|
||||
|
||||
/* Total size of framebuffer used in input. */
|
||||
unsigned fb_width;
|
||||
unsigned fb_height;
|
||||
|
||||
/* Audio channels. */
|
||||
unsigned channels;
|
||||
|
||||
unsigned video_record_scale_factor;
|
||||
unsigned video_stream_scale_factor;
|
||||
unsigned video_record_threads;
|
||||
unsigned streaming_mode;
|
||||
|
||||
/* Aspect ratio of input video. Parameters are passed to the muxer,
|
||||
* the video itself is not scaled.
|
||||
*/
|
||||
float aspect_ratio;
|
||||
|
||||
enum record_config_type preset;
|
||||
|
||||
/* Input pixel format. */
|
||||
enum ffemu_pix_format pix_fmt;
|
||||
|
||||
bool video_gpu_record;
|
||||
};
|
||||
|
||||
struct record_video_data
|
||||
{
|
||||
const void *data;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
int pitch;
|
||||
bool is_dupe;
|
||||
};
|
||||
|
||||
struct record_audio_data
|
||||
{
|
||||
const void *data;
|
||||
size_t frames;
|
||||
};
|
||||
|
||||
typedef struct record_driver
|
||||
{
|
||||
void *(*init)(const struct record_params *params);
|
||||
void (*free)(void *data);
|
||||
bool (*push_video)(void *data,
|
||||
const struct record_video_data *video_data);
|
||||
bool (*push_audio)(void *data,
|
||||
const struct record_audio_data *audio_data);
|
||||
bool (*finalize)(void *data);
|
||||
const char *ident;
|
||||
} record_driver_t;
|
||||
|
||||
|
||||
struct recording
|
||||
{
|
||||
const record_driver_t *driver;
|
||||
void *data;
|
||||
|
||||
size_t gpu_width;
|
||||
size_t gpu_height;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
|
||||
char path[8192];
|
||||
char config[8192];
|
||||
char output_dir[8192];
|
||||
char config_dir[8192];
|
||||
|
||||
bool enable;
|
||||
bool streaming_enable;
|
||||
bool use_output_dir;
|
||||
};
|
||||
|
||||
typedef struct recording recording_state_t;
|
||||
|
||||
extern const record_driver_t record_ffmpeg;
|
||||
|
||||
/**
|
||||
* config_get_record_driver_options:
|
||||
*
|
||||
* Get an enumerated list of all record driver names, separated by '|'.
|
||||
*
|
||||
* Returns: string listing of all record driver names, separated by '|'.
|
||||
**/
|
||||
const char* config_get_record_driver_options(void);
|
||||
|
||||
void recording_driver_update_streaming_url(void);
|
||||
|
||||
bool recording_deinit(void);
|
||||
|
||||
/**
|
||||
* recording_init:
|
||||
*
|
||||
* Initializes recording.
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
**/
|
||||
bool recording_init(void);
|
||||
|
||||
void streaming_set_state(bool state);
|
||||
|
||||
recording_state_t *recording_state_get_ptr(void);
|
||||
|
||||
extern const record_driver_t *record_drivers[];
|
||||
|
||||
#endif
|
546
retroarch.c
546
retroarch.c
@ -123,6 +123,7 @@
|
||||
|
||||
#include "runtime_file.h"
|
||||
#include "runloop.h"
|
||||
#include "record/record_driver.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
@ -467,22 +468,6 @@ static const ui_companion_driver_t *ui_companion_drivers[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static const record_driver_t record_null = {
|
||||
NULL, /* new */
|
||||
NULL, /* free */
|
||||
NULL, /* push_video */
|
||||
NULL, /* push_audio */
|
||||
NULL, /* finalize */
|
||||
"null",
|
||||
};
|
||||
|
||||
static const record_driver_t *record_drivers[] = {
|
||||
#ifdef HAVE_FFMPEG
|
||||
&record_ffmpeg,
|
||||
#endif
|
||||
&record_null,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void *nullcamera_init(const char *device, uint64_t caps,
|
||||
unsigned width, unsigned height) { return (void*)-1; }
|
||||
@ -587,14 +572,10 @@ struct rarch_state
|
||||
bool wifi_driver_active;
|
||||
bool camera_driver_active;
|
||||
|
||||
bool streaming_enable;
|
||||
bool main_ui_companion_is_on_foreground;
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
static bool recording_init(settings_t *settings,
|
||||
struct rarch_state *p_rarch);
|
||||
static bool recording_deinit(void);
|
||||
static void retroarch_fail(int error_code, const char *error);
|
||||
|
||||
#ifdef HAVE_LIBNX
|
||||
@ -656,7 +637,6 @@ retro_keybind_set input_config_binds[MAX_USERS];
|
||||
retro_keybind_set input_autoconf_binds[MAX_USERS];
|
||||
|
||||
static runloop_state_t runloop_state = {0};
|
||||
static recording_state_t recording_state = {0};
|
||||
static access_state_t access_state_st = {0};
|
||||
|
||||
access_state_t *access_state_get_ptr(void)
|
||||
@ -664,12 +644,6 @@ access_state_t *access_state_get_ptr(void)
|
||||
return &access_state_st;
|
||||
}
|
||||
|
||||
|
||||
recording_state_t *recording_state_get_ptr(void)
|
||||
{
|
||||
return &recording_state;
|
||||
}
|
||||
|
||||
/* GLOBAL POINTER GETTERS */
|
||||
#ifdef HAVE_REWIND
|
||||
bool state_manager_frame_is_reversed(void)
|
||||
@ -4114,16 +4088,16 @@ static void runloop_runahead_clear_variables(runloop_state_t *runloop_st)
|
||||
**/
|
||||
bool command_event(enum event_command cmd, void *data)
|
||||
{
|
||||
bool boolean = false;
|
||||
struct rarch_state *p_rarch = &rarch_st;
|
||||
runloop_state_t *runloop_st = &runloop_state;
|
||||
access_state_t *access_st = access_state_get_ptr();
|
||||
bool boolean = false;
|
||||
struct rarch_state *p_rarch = &rarch_st;
|
||||
runloop_state_t *runloop_st = &runloop_state;
|
||||
access_state_t *access_st = access_state_get_ptr();
|
||||
#ifdef HAVE_MENU
|
||||
struct menu_state *menu_st = menu_state_get_ptr();
|
||||
struct menu_state *menu_st = menu_state_get_ptr();
|
||||
#endif
|
||||
video_driver_state_t
|
||||
*video_st = video_state_get_ptr();
|
||||
settings_t *settings = config_get_ptr();
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
settings_t *settings = config_get_ptr();
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
@ -4188,7 +4162,7 @@ bool command_event(enum event_command cmd, void *data)
|
||||
{
|
||||
#ifdef HAVE_BSV_MOVIE
|
||||
input_driver_state_t *input_st = input_state_get_ptr();
|
||||
if (!recording_is_enabled())
|
||||
if (!recording_st->enable)
|
||||
command_event(CMD_EVENT_RECORD_INIT, NULL);
|
||||
else
|
||||
command_event(CMD_EVENT_RECORD_DEINIT, NULL);
|
||||
@ -4254,7 +4228,7 @@ bool command_event(enum event_command cmd, void *data)
|
||||
break;
|
||||
}
|
||||
case CMD_EVENT_STREAMING_TOGGLE:
|
||||
if (streaming_is_enabled())
|
||||
if (recording_st->streaming_enable)
|
||||
command_event(CMD_EVENT_RECORD_DEINIT, NULL);
|
||||
else
|
||||
{
|
||||
@ -4297,7 +4271,7 @@ bool command_event(enum event_command cmd, void *data)
|
||||
}
|
||||
break;
|
||||
case CMD_EVENT_RECORDING_TOGGLE:
|
||||
if (recording_is_enabled())
|
||||
if (recording_st->enable)
|
||||
command_event(CMD_EVENT_RECORD_DEINIT, NULL);
|
||||
else
|
||||
command_event(CMD_EVENT_RECORD_INIT, NULL);
|
||||
@ -4797,14 +4771,14 @@ bool command_event(enum event_command cmd, void *data)
|
||||
#endif
|
||||
break;
|
||||
case CMD_EVENT_RECORD_DEINIT:
|
||||
recording_state.enable = false;
|
||||
recording_st->enable = false;
|
||||
streaming_set_state(false);
|
||||
if (!recording_deinit())
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_RECORD_INIT:
|
||||
recording_state.enable = true;
|
||||
if (!recording_init(settings, p_rarch))
|
||||
recording_st->enable = true;
|
||||
if (!recording_init())
|
||||
{
|
||||
command_event(CMD_EVENT_RECORD_DEINIT, NULL);
|
||||
return false;
|
||||
@ -7410,6 +7384,7 @@ bool runloop_environment_cb(unsigned cmd, void *data)
|
||||
unsigned p;
|
||||
struct rarch_state *p_rarch = &rarch_st;
|
||||
runloop_state_t *runloop_st = &runloop_state;
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
|
||||
settings_t *settings = config_get_ptr();
|
||||
rarch_system_info_t *system = &runloop_st->system;
|
||||
@ -8348,6 +8323,8 @@ bool runloop_environment_cb(unsigned cmd, void *data)
|
||||
case RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK:
|
||||
#ifdef HAVE_THREADS
|
||||
{
|
||||
recording_state_t
|
||||
*recording_st = recording_state_get_ptr();
|
||||
audio_driver_state_t
|
||||
*audio_st = audio_state_get_ptr();
|
||||
const struct
|
||||
@ -8357,7 +8334,7 @@ bool runloop_environment_cb(unsigned cmd, void *data)
|
||||
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
|
||||
return false;
|
||||
#endif
|
||||
if (recording_state.data) /* A/V sync is a must. */
|
||||
if (recording_st->data) /* A/V sync is a must. */
|
||||
return false;
|
||||
if (cb)
|
||||
audio_st->callback = *cb;
|
||||
@ -8437,6 +8414,8 @@ bool runloop_environment_cb(unsigned cmd, void *data)
|
||||
* without video driver initialisation) */
|
||||
if (audio_latency_new != audio_latency_current)
|
||||
{
|
||||
recording_state_t
|
||||
*recording_st = recording_state_get_ptr();
|
||||
bool video_fullscreen = settings->bools.video_fullscreen;
|
||||
int reinit_flags = DRIVERS_CMD_ALL &
|
||||
~(DRIVER_VIDEO_MASK | DRIVER_INPUT_MASK | DRIVER_MENU_MASK);
|
||||
@ -8446,9 +8425,12 @@ bool runloop_environment_cb(unsigned cmd, void *data)
|
||||
command_event(CMD_EVENT_REINIT, &reinit_flags);
|
||||
video_driver_set_aspect_ratio();
|
||||
|
||||
/* Cannot continue recording with different parameters.
|
||||
* Take the easiest route out and just restart the recording. */
|
||||
if (recording_state.data)
|
||||
/* Cannot continue recording with different
|
||||
* parameters.
|
||||
* Take the easiest route out and just restart
|
||||
* the recording. */
|
||||
|
||||
if (recording_st->data)
|
||||
{
|
||||
runloop_msg_queue_push(
|
||||
msg_hash_to_str(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT),
|
||||
@ -8677,8 +8659,9 @@ bool runloop_environment_cb(unsigned cmd, void *data)
|
||||
video_display_server_set_refresh_rate(refresh_rate);
|
||||
|
||||
/* Cannot continue recording with different parameters.
|
||||
* Take the easiest route out and just restart the recording. */
|
||||
if (recording_state.data)
|
||||
* Take the easiest route out and just restart
|
||||
* the recording. */
|
||||
if (recording_st->data)
|
||||
{
|
||||
runloop_msg_queue_push(
|
||||
msg_hash_to_str(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT),
|
||||
@ -8898,7 +8881,8 @@ bool runloop_environment_cb(unsigned cmd, void *data)
|
||||
* using core-dependent aspect ratios. */
|
||||
video_driver_set_aspect_ratio();
|
||||
|
||||
/* TODO: Figure out what to do, if anything, with recording. */
|
||||
/* TODO: Figure out what to do, if anything, with
|
||||
recording. */
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -10799,448 +10783,6 @@ void ui_companion_driver_log_msg(const char *msg)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* RECORDING */
|
||||
|
||||
/**
|
||||
* config_get_record_driver_options:
|
||||
*
|
||||
* Get an enumerated list of all record driver names, separated by '|'.
|
||||
*
|
||||
* Returns: string listing of all record driver names, separated by '|'.
|
||||
**/
|
||||
const char* config_get_record_driver_options(void)
|
||||
{
|
||||
return char_list_new_special(STRING_LIST_RECORD_DRIVERS, NULL);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* TODO/FIXME - not used apparently */
|
||||
static void find_record_driver(const char *prefix,
|
||||
bool verbosity_enabled)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
int i = (int)driver_find_index(
|
||||
"record_driver",
|
||||
settings->arrays.record_driver);
|
||||
|
||||
if (i >= 0)
|
||||
recording_state.driver = (const record_driver_t*)record_drivers[i];
|
||||
else
|
||||
{
|
||||
if (verbosity_enabled)
|
||||
{
|
||||
unsigned d;
|
||||
|
||||
RARCH_ERR("[recording] Couldn't find any %s named \"%s\"\n", prefix,
|
||||
settings->arrays.record_driver);
|
||||
RARCH_LOG_OUTPUT("Available %ss are:\n", prefix);
|
||||
for (d = 0; record_drivers[d]; d++)
|
||||
RARCH_LOG_OUTPUT("\t%s\n", record_drivers[d].ident);
|
||||
RARCH_WARN("[recording] Going to default to first %s...\n", prefix);
|
||||
}
|
||||
|
||||
recording_state.driver = (const record_driver_t*)record_drivers[0];
|
||||
|
||||
if (!recording_state.driver)
|
||||
retroarch_fail(1, "find_record_driver()");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ffemu_find_backend:
|
||||
* @ident : Identifier of driver to find.
|
||||
*
|
||||
* Finds a recording driver with the name @ident.
|
||||
*
|
||||
* Returns: recording driver handle if successful, otherwise
|
||||
* NULL.
|
||||
**/
|
||||
static const record_driver_t *ffemu_find_backend(const char *ident)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; record_drivers[i]; i++)
|
||||
{
|
||||
if (string_is_equal(record_drivers[i]->ident, ident))
|
||||
return record_drivers[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void recording_driver_free_state(void)
|
||||
{
|
||||
/* TODO/FIXME - this is not being called anywhere */
|
||||
recording_state.gpu_width = 0;
|
||||
recording_state.gpu_height = 0;
|
||||
recording_state.width = 0;
|
||||
recording_stte.height = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* gfx_ctx_init_first:
|
||||
* @backend : Recording backend handle.
|
||||
* @data : Recording data handle.
|
||||
* @params : Recording info parameters.
|
||||
*
|
||||
* Finds first suitable recording context driver and initializes.
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
**/
|
||||
static bool record_driver_init_first(
|
||||
const record_driver_t **backend, void **data,
|
||||
const struct record_params *params)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; record_drivers[i]; i++)
|
||||
{
|
||||
void *handle = record_drivers[i]->init(params);
|
||||
|
||||
if (!handle)
|
||||
continue;
|
||||
|
||||
*backend = record_drivers[i];
|
||||
*data = handle;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool recording_deinit(void)
|
||||
{
|
||||
if (!recording_state.data || !recording_state.driver)
|
||||
return false;
|
||||
|
||||
if (recording_state.driver->finalize)
|
||||
recording_state.driver->finalize(recording_state.data);
|
||||
|
||||
if (recording_state.driver->free)
|
||||
recording_state.driver->free(recording_state.data);
|
||||
|
||||
recording_state.data = NULL;
|
||||
recording_state.driver = NULL;
|
||||
|
||||
video_driver_gpu_record_deinit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool recording_is_enabled(void)
|
||||
{
|
||||
return recording_state.enable;
|
||||
}
|
||||
|
||||
bool streaming_is_enabled(void)
|
||||
{
|
||||
struct rarch_state *p_rarch = &rarch_st;
|
||||
return p_rarch->streaming_enable;
|
||||
}
|
||||
|
||||
void streaming_set_state(bool state)
|
||||
{
|
||||
struct rarch_state *p_rarch = &rarch_st;
|
||||
p_rarch->streaming_enable = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* recording_init:
|
||||
*
|
||||
* Initializes recording.
|
||||
*
|
||||
* Returns: true (1) if successful, otherwise false (0).
|
||||
**/
|
||||
static bool recording_init(
|
||||
settings_t *settings,
|
||||
struct rarch_state *p_rarch)
|
||||
{
|
||||
char output[PATH_MAX_LENGTH];
|
||||
char buf[PATH_MAX_LENGTH];
|
||||
struct record_params params = {0};
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
struct retro_system_av_info *av_info = &video_st->av_info;
|
||||
runloop_state_t *runloop_st = &runloop_state;
|
||||
global_t *global = global_get_ptr();
|
||||
bool video_gpu_record = settings->bools.video_gpu_record;
|
||||
bool video_force_aspect = settings->bools.video_force_aspect;
|
||||
const enum rarch_core_type
|
||||
current_core_type = runloop_st->current_core_type;
|
||||
const enum retro_pixel_format
|
||||
video_driver_pix_fmt = video_st->pix_fmt;
|
||||
bool recording_enable = recording_state.enable;
|
||||
|
||||
if (!recording_enable)
|
||||
return false;
|
||||
|
||||
output[0] = '\0';
|
||||
|
||||
if (current_core_type == CORE_TYPE_DUMMY)
|
||||
{
|
||||
RARCH_WARN("[recording] %s\n",
|
||||
msg_hash_to_str(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!video_gpu_record && video_driver_is_hw_context())
|
||||
{
|
||||
RARCH_WARN("[recording] %s.\n",
|
||||
msg_hash_to_str(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING));
|
||||
return false;
|
||||
}
|
||||
|
||||
RARCH_LOG("[recording] %s: FPS: %.4f, Sample rate: %.4f\n",
|
||||
msg_hash_to_str(MSG_CUSTOM_TIMING_GIVEN),
|
||||
(float)av_info->timing.fps,
|
||||
(float)av_info->timing.sample_rate);
|
||||
|
||||
if (!string_is_empty(global->record.path))
|
||||
strlcpy(output, global->record.path, sizeof(output));
|
||||
else
|
||||
{
|
||||
const char *stream_url = settings->paths.path_stream_url;
|
||||
unsigned video_record_quality = settings->uints.video_record_quality;
|
||||
unsigned video_stream_port = settings->uints.video_stream_port;
|
||||
if (p_rarch->streaming_enable)
|
||||
if (!string_is_empty(stream_url))
|
||||
strlcpy(output, stream_url, sizeof(output));
|
||||
else
|
||||
/* Fallback, stream locally to 127.0.0.1 */
|
||||
snprintf(output, sizeof(output), "udp://127.0.0.1:%u",
|
||||
video_stream_port);
|
||||
else
|
||||
{
|
||||
const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME));
|
||||
/* Fallback to core name if started without content */
|
||||
if (string_is_empty(game_name))
|
||||
game_name = runloop_st->system.info.library_name;
|
||||
|
||||
if (video_record_quality < RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST)
|
||||
{
|
||||
fill_str_dated_filename(buf, game_name,
|
||||
"mkv", sizeof(buf));
|
||||
fill_pathname_join(output, global->record.output_dir, buf, sizeof(output));
|
||||
}
|
||||
else if (video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST
|
||||
&& video_record_quality < RECORD_CONFIG_TYPE_RECORDING_GIF)
|
||||
{
|
||||
fill_str_dated_filename(buf, game_name,
|
||||
"webm", sizeof(buf));
|
||||
fill_pathname_join(output, global->record.output_dir, buf, sizeof(output));
|
||||
}
|
||||
else if (video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_GIF
|
||||
&& video_record_quality < RECORD_CONFIG_TYPE_RECORDING_APNG)
|
||||
{
|
||||
fill_str_dated_filename(buf, game_name,
|
||||
"gif", sizeof(buf));
|
||||
fill_pathname_join(output, global->record.output_dir, buf, sizeof(output));
|
||||
}
|
||||
else
|
||||
{
|
||||
fill_str_dated_filename(buf, game_name,
|
||||
"png", sizeof(buf));
|
||||
fill_pathname_join(output, global->record.output_dir, buf, sizeof(output));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.audio_resampler = settings->arrays.audio_resampler;
|
||||
params.video_gpu_record = settings->bools.video_gpu_record;
|
||||
params.video_record_scale_factor = settings->uints.video_record_scale_factor;
|
||||
params.video_stream_scale_factor = settings->uints.video_stream_scale_factor;
|
||||
params.video_record_threads = settings->uints.video_record_threads;
|
||||
params.streaming_mode = settings->uints.streaming_mode;
|
||||
|
||||
params.out_width = av_info->geometry.base_width;
|
||||
params.out_height = av_info->geometry.base_height;
|
||||
params.fb_width = av_info->geometry.max_width;
|
||||
params.fb_height = av_info->geometry.max_height;
|
||||
params.channels = 2;
|
||||
params.filename = output;
|
||||
params.fps = av_info->timing.fps;
|
||||
params.samplerate = av_info->timing.sample_rate;
|
||||
params.pix_fmt =
|
||||
(video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888)
|
||||
? FFEMU_PIX_ARGB8888
|
||||
: FFEMU_PIX_RGB565;
|
||||
params.config = NULL;
|
||||
|
||||
if (!string_is_empty(global->record.config))
|
||||
params.config = global->record.config;
|
||||
else
|
||||
{
|
||||
if (p_rarch->streaming_enable)
|
||||
{
|
||||
params.config = settings->paths.path_stream_config;
|
||||
params.preset = (enum record_config_type)
|
||||
settings->uints.video_stream_quality;
|
||||
}
|
||||
else
|
||||
{
|
||||
params.config = settings->paths.path_record_config;
|
||||
params.preset = (enum record_config_type)
|
||||
settings->uints.video_record_quality;
|
||||
}
|
||||
}
|
||||
|
||||
if (settings->bools.video_gpu_record
|
||||
&& video_st->current_video->read_viewport)
|
||||
{
|
||||
unsigned gpu_size;
|
||||
struct video_viewport vp;
|
||||
|
||||
vp.x = 0;
|
||||
vp.y = 0;
|
||||
vp.width = 0;
|
||||
vp.height = 0;
|
||||
vp.full_width = 0;
|
||||
vp.full_height = 0;
|
||||
|
||||
video_driver_get_viewport_info(&vp);
|
||||
|
||||
if (!vp.width || !vp.height)
|
||||
{
|
||||
RARCH_ERR("[recording] Failed to get viewport information from video driver. "
|
||||
"Cannot start recording ...\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
params.out_width = vp.width;
|
||||
params.out_height = vp.height;
|
||||
params.fb_width = next_pow2(vp.width);
|
||||
params.fb_height = next_pow2(vp.height);
|
||||
|
||||
if (video_force_aspect &&
|
||||
(video_st->aspect_ratio > 0.0f))
|
||||
params.aspect_ratio = video_st->aspect_ratio;
|
||||
else
|
||||
params.aspect_ratio = (float)vp.width / vp.height;
|
||||
|
||||
params.pix_fmt = FFEMU_PIX_BGR24;
|
||||
recording_state.gpu_width = vp.width;
|
||||
recording_state.gpu_height = vp.height;
|
||||
|
||||
RARCH_LOG("[recording] %s %u x %u\n", msg_hash_to_str(MSG_DETECTED_VIEWPORT_OF),
|
||||
vp.width, vp.height);
|
||||
|
||||
gpu_size = vp.width * vp.height * 3;
|
||||
if (!(video_st->record_gpu_buffer = (uint8_t*)malloc(gpu_size)))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (recording_state.width || recording_state.height)
|
||||
{
|
||||
params.out_width = recording_state.width;
|
||||
params.out_height = recording_state.height;
|
||||
}
|
||||
|
||||
if (video_force_aspect &&
|
||||
(video_st->aspect_ratio > 0.0f))
|
||||
params.aspect_ratio = video_st->aspect_ratio;
|
||||
else
|
||||
params.aspect_ratio = (float)params.out_width / params.out_height;
|
||||
|
||||
#ifdef HAVE_VIDEO_FILTER
|
||||
if (settings->bools.video_post_filter_record
|
||||
&& !!video_st->state_filter)
|
||||
{
|
||||
unsigned max_width = 0;
|
||||
unsigned max_height = 0;
|
||||
|
||||
params.pix_fmt = FFEMU_PIX_RGB565;
|
||||
|
||||
if (video_st->state_out_rgb32)
|
||||
params.pix_fmt = FFEMU_PIX_ARGB8888;
|
||||
|
||||
rarch_softfilter_get_max_output_size(
|
||||
video_st->state_filter,
|
||||
&max_width, &max_height);
|
||||
params.fb_width = next_pow2(max_width);
|
||||
params.fb_height = next_pow2(max_height);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
RARCH_LOG("[recording] %s %s @ %ux%u. (FB size: %ux%u pix_fmt: %u)\n",
|
||||
msg_hash_to_str(MSG_RECORDING_TO),
|
||||
output,
|
||||
params.out_width, params.out_height,
|
||||
params.fb_width, params.fb_height,
|
||||
(unsigned)params.pix_fmt);
|
||||
|
||||
if (!record_driver_init_first(
|
||||
&recording_state.driver,
|
||||
&recording_state.data, ¶ms))
|
||||
{
|
||||
RARCH_ERR("[recording] %s\n",
|
||||
msg_hash_to_str(MSG_FAILED_TO_START_RECORDING));
|
||||
video_driver_gpu_record_deinit();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void recording_driver_update_streaming_url(void)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
const char *youtube_url = "rtmp://a.rtmp.youtube.com/live2/";
|
||||
const char *twitch_url = "rtmp://live.twitch.tv/app/";
|
||||
const char *facebook_url = "rtmps://live-api-s.facebook.com:443/rtmp/";
|
||||
|
||||
if (!settings)
|
||||
return;
|
||||
|
||||
switch (settings->uints.streaming_mode)
|
||||
{
|
||||
case STREAMING_MODE_TWITCH:
|
||||
if (!string_is_empty(settings->arrays.twitch_stream_key))
|
||||
{
|
||||
strlcpy(settings->paths.path_stream_url,
|
||||
twitch_url,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
strlcat(settings->paths.path_stream_url,
|
||||
settings->arrays.twitch_stream_key,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
}
|
||||
break;
|
||||
case STREAMING_MODE_YOUTUBE:
|
||||
if (!string_is_empty(settings->arrays.youtube_stream_key))
|
||||
{
|
||||
strlcpy(settings->paths.path_stream_url,
|
||||
youtube_url,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
strlcat(settings->paths.path_stream_url,
|
||||
settings->arrays.youtube_stream_key,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
}
|
||||
break;
|
||||
case STREAMING_MODE_LOCAL:
|
||||
/* TODO: figure out default interface and bind to that instead */
|
||||
snprintf(settings->paths.path_stream_url, sizeof(settings->paths.path_stream_url),
|
||||
"udp://%s:%u", "127.0.0.1", settings->uints.video_stream_port);
|
||||
break;
|
||||
case STREAMING_MODE_CUSTOM:
|
||||
default:
|
||||
/* Do nothing, let the user input the URL */
|
||||
break;
|
||||
case STREAMING_MODE_FACEBOOK:
|
||||
if (!string_is_empty(settings->arrays.facebook_stream_key))
|
||||
{
|
||||
strlcpy(settings->paths.path_stream_url,
|
||||
facebook_url,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
strlcat(settings->paths.path_stream_url,
|
||||
settings->arrays.facebook_stream_key,
|
||||
sizeof(settings->paths.path_stream_url));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* INPUT */
|
||||
|
||||
/**
|
||||
@ -13008,6 +12550,7 @@ static bool retroarch_parse_input_and_config(
|
||||
bool cli_active = false;
|
||||
bool cli_core_set = false;
|
||||
bool cli_content_set = false;
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
runloop_state_t *runloop_st = &runloop_state;
|
||||
settings_t *settings = config_get_ptr();
|
||||
@ -13347,10 +12890,10 @@ static bool retroarch_parse_input_and_config(
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
strlcpy(global->record.path, optarg,
|
||||
sizeof(global->record.path));
|
||||
if (recording_state.enable)
|
||||
recording_state.enable = true;
|
||||
strlcpy(recording_st->path, optarg,
|
||||
sizeof(recording_st->path));
|
||||
if (recording_st->enable)
|
||||
recording_st->enable = true;
|
||||
break;
|
||||
|
||||
case RA_OPT_SET_SHADER:
|
||||
@ -13562,8 +13105,8 @@ static bool retroarch_parse_input_and_config(
|
||||
|
||||
case RA_OPT_SIZE:
|
||||
if (sscanf(optarg, "%ux%u",
|
||||
&recording_state.width,
|
||||
&recording_state.height) != 2)
|
||||
&recording_st->width,
|
||||
&recording_st->height) != 2)
|
||||
{
|
||||
RARCH_ERR("Wrong format for --size.\n");
|
||||
retroarch_print_help(argv[0]);
|
||||
@ -13572,8 +13115,8 @@ static bool retroarch_parse_input_and_config(
|
||||
break;
|
||||
|
||||
case RA_OPT_RECORDCONFIG:
|
||||
strlcpy(global->record.config, optarg,
|
||||
sizeof(global->record.config));
|
||||
strlcpy(recording_st->config, optarg,
|
||||
sizeof(recording_st->config));
|
||||
break;
|
||||
|
||||
case RA_OPT_MAX_FRAMES:
|
||||
@ -13755,6 +13298,8 @@ bool retroarch_main_init(int argc, char *argv[])
|
||||
*input_st = input_state_get_ptr();
|
||||
video_driver_state_t*video_st= video_state_get_ptr();
|
||||
settings_t *settings = config_get_ptr();
|
||||
recording_state_t
|
||||
*recording_st = recording_state_get_ptr();
|
||||
global_t *global = &p_rarch->g_extern;
|
||||
access_state_t *access_st = access_state_get_ptr();
|
||||
#ifdef HAVE_ACCESSIBILITY
|
||||
@ -14027,7 +13572,7 @@ bool retroarch_main_init(int argc, char *argv[])
|
||||
command_event(CMD_EVENT_REWIND_INIT, NULL);
|
||||
#endif
|
||||
command_event(CMD_EVENT_CONTROLLER_INIT, NULL);
|
||||
if (!string_is_empty(global->record.path))
|
||||
if (!string_is_empty(recording_st->path))
|
||||
command_event(CMD_EVENT_RECORD_INIT, NULL);
|
||||
|
||||
path_init_savefile(runloop_st);
|
||||
@ -16007,6 +15552,7 @@ int runloop_iterate(void)
|
||||
input_driver_state_t *input_st = input_state_get_ptr();
|
||||
audio_driver_state_t *audio_st = audio_state_get_ptr();
|
||||
video_driver_state_t *video_st = video_state_get_ptr();
|
||||
recording_state_t *recording_st = recording_state_get_ptr();
|
||||
settings_t *settings = config_get_ptr();
|
||||
runloop_state_t *runloop_st = &runloop_state;
|
||||
unsigned video_frame_delay = settings->uints.video_frame_delay;
|
||||
@ -16045,7 +15591,7 @@ int runloop_iterate(void)
|
||||
retro_time_t current = current_time;
|
||||
bool is_locked_fps = (runloop_st->paused
|
||||
|| input_st->nonblocking_flag)
|
||||
| !!recording_state.data;
|
||||
| !!recording_st->data;
|
||||
retro_time_t delta = (!runloop_last_frame_time || is_locked_fps)
|
||||
? runloop_st->frame_time.reference
|
||||
: (current - runloop_last_frame_time);
|
||||
|
51
retroarch.h
51
retroarch.h
@ -127,39 +127,6 @@ void rarch_favorites_deinit(void);
|
||||
**/
|
||||
const char* config_get_audio_driver_options(void);
|
||||
|
||||
/* Recording */
|
||||
|
||||
typedef struct record_driver
|
||||
{
|
||||
void *(*init)(const struct record_params *params);
|
||||
void (*free)(void *data);
|
||||
bool (*push_video)(void *data, const struct record_video_data *video_data);
|
||||
bool (*push_audio)(void *data, const struct record_audio_data *audio_data);
|
||||
bool (*finalize)(void *data);
|
||||
const char *ident;
|
||||
} record_driver_t;
|
||||
|
||||
extern const record_driver_t record_ffmpeg;
|
||||
|
||||
/**
|
||||
* config_get_record_driver_options:
|
||||
*
|
||||
* Get an enumerated list of all record driver names, separated by '|'.
|
||||
*
|
||||
* Returns: string listing of all record driver names, separated by '|'.
|
||||
**/
|
||||
const char* config_get_record_driver_options(void);
|
||||
|
||||
bool recording_is_enabled(void);
|
||||
|
||||
void streaming_set_state(bool state);
|
||||
|
||||
bool streaming_is_enabled(void);
|
||||
|
||||
void recording_driver_update_streaming_url(void);
|
||||
|
||||
/* Video */
|
||||
|
||||
/* BSV Movie */
|
||||
|
||||
void bsv_movie_frame_rewind(void);
|
||||
@ -253,24 +220,6 @@ typedef enum apple_view_type
|
||||
|
||||
bool retroarch_get_current_savestate_path(char *path, size_t len);
|
||||
|
||||
struct recording
|
||||
{
|
||||
const record_driver_t *driver;
|
||||
void *data;
|
||||
|
||||
size_t gpu_width;
|
||||
size_t gpu_height;
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
|
||||
bool enable;
|
||||
};
|
||||
|
||||
typedef struct recording recording_state_t;
|
||||
|
||||
recording_state_t *recording_state_get_ptr(void);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -169,41 +169,6 @@ enum runloop_action
|
||||
RUNLOOP_ACTION_AUTOSAVE
|
||||
};
|
||||
|
||||
|
||||
enum ffemu_pix_format
|
||||
{
|
||||
FFEMU_PIX_RGB565 = 0,
|
||||
FFEMU_PIX_BGR24,
|
||||
FFEMU_PIX_ARGB8888
|
||||
};
|
||||
|
||||
enum streaming_mode
|
||||
{
|
||||
STREAMING_MODE_TWITCH = 0,
|
||||
STREAMING_MODE_YOUTUBE,
|
||||
STREAMING_MODE_FACEBOOK,
|
||||
STREAMING_MODE_LOCAL,
|
||||
STREAMING_MODE_CUSTOM
|
||||
};
|
||||
|
||||
enum record_config_type
|
||||
{
|
||||
RECORD_CONFIG_TYPE_RECORDING_CUSTOM = 0,
|
||||
RECORD_CONFIG_TYPE_RECORDING_LOW_QUALITY,
|
||||
RECORD_CONFIG_TYPE_RECORDING_MED_QUALITY,
|
||||
RECORD_CONFIG_TYPE_RECORDING_HIGH_QUALITY,
|
||||
RECORD_CONFIG_TYPE_RECORDING_LOSSLESS_QUALITY,
|
||||
RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST,
|
||||
RECORD_CONFIG_TYPE_RECORDING_WEBM_HIGH_QUALITY,
|
||||
RECORD_CONFIG_TYPE_RECORDING_GIF,
|
||||
RECORD_CONFIG_TYPE_RECORDING_APNG,
|
||||
RECORD_CONFIG_TYPE_STREAMING_CUSTOM,
|
||||
RECORD_CONFIG_TYPE_STREAMING_LOW_QUALITY,
|
||||
RECORD_CONFIG_TYPE_STREAMING_MED_QUALITY,
|
||||
RECORD_CONFIG_TYPE_STREAMING_HIGH_QUALITY,
|
||||
RECORD_CONFIG_TYPE_STREAMING_NETPLAY
|
||||
};
|
||||
|
||||
typedef struct rarch_memory_descriptor
|
||||
{
|
||||
struct retro_memory_descriptor core; /* uint64_t alignment */
|
||||
@ -332,20 +297,6 @@ typedef struct rarch_resolution
|
||||
|
||||
typedef struct global
|
||||
{
|
||||
/* Recording. */
|
||||
struct
|
||||
{
|
||||
size_t gpu_width;
|
||||
size_t gpu_height;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
char path[8192];
|
||||
char config[8192];
|
||||
char output_dir[8192];
|
||||
char config_dir[8192];
|
||||
bool use_output_dir;
|
||||
} record;
|
||||
|
||||
/* Settings and/or global state that is specific to
|
||||
* a console-style implementation. */
|
||||
struct
|
||||
@ -430,67 +381,6 @@ typedef struct content_state
|
||||
bool pending_rom_crc;
|
||||
} content_state_t;
|
||||
|
||||
/* Parameters passed to ffemu_new() */
|
||||
struct record_params
|
||||
{
|
||||
/* Framerate per second of input video. */
|
||||
double fps;
|
||||
/* Sample rate of input audio. */
|
||||
double samplerate;
|
||||
|
||||
/* Filename to dump to. */
|
||||
const char *filename;
|
||||
|
||||
/* Path to config. Optional. */
|
||||
const char *config;
|
||||
|
||||
const char *audio_resampler;
|
||||
|
||||
/* Desired output resolution. */
|
||||
unsigned out_width;
|
||||
unsigned out_height;
|
||||
|
||||
/* Total size of framebuffer used in input. */
|
||||
unsigned fb_width;
|
||||
unsigned fb_height;
|
||||
|
||||
/* Audio channels. */
|
||||
unsigned channels;
|
||||
|
||||
unsigned video_record_scale_factor;
|
||||
unsigned video_stream_scale_factor;
|
||||
unsigned video_record_threads;
|
||||
unsigned streaming_mode;
|
||||
|
||||
/* Aspect ratio of input video. Parameters are passed to the muxer,
|
||||
* the video itself is not scaled.
|
||||
*/
|
||||
float aspect_ratio;
|
||||
|
||||
enum record_config_type preset;
|
||||
|
||||
/* Input pixel format. */
|
||||
enum ffemu_pix_format pix_fmt;
|
||||
|
||||
bool video_gpu_record;
|
||||
};
|
||||
|
||||
struct record_video_data
|
||||
{
|
||||
const void *data;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
int pitch;
|
||||
bool is_dupe;
|
||||
};
|
||||
|
||||
struct record_audio_data
|
||||
{
|
||||
const void *data;
|
||||
size_t frames;
|
||||
};
|
||||
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user