Split up recording code

This commit is contained in:
twinaphex 2021-11-10 02:34:04 +01:00
parent 174ab373e4
commit 0b768d0460
18 changed files with 755 additions and 710 deletions

View file

@ -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)

View file

@ -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"

View file

@ -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"
@ -1428,6 +1429,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)
@ -1557,16 +1559,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);
@ -2378,6 +2374,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);
@ -2663,23 +2660,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';
@ -2867,13 +2861,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))
{

View file

@ -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))
{

View file

@ -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))
{

View file

@ -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;

View file

@ -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"

View file

@ -1271,6 +1271,7 @@ WIFI
/*============================================================
RECORDING
============================================================ */
#include "../record/record_driver.c"
#ifdef HAVE_FFMPEG
#include "../record/drivers/record_ffmpeg.c"
#endif

View file

@ -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;
}

View 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),

View file

@ -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],

View file

@ -42,6 +42,8 @@
#include <time.h>
#endif
#include "../record_driver.h"
#ifdef __cplusplus
extern "C" {
#endif

479
record/record_driver.c Normal file
View 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, &params))
{
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
View 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

View file

@ -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;
@ -7406,6 +7380,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;
@ -8344,6 +8319,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
@ -8353,7 +8330,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;
@ -8433,6 +8410,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);
@ -8442,9 +8421,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),
@ -8673,8 +8655,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),
@ -8894,7 +8877,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
{
@ -10795,448 +10779,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, &params))
{
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 */
/**
@ -13004,6 +12546,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();
@ -13343,10 +12886,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:
@ -13558,8 +13101,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]);
@ -13568,8 +13111,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:
@ -13751,6 +13294,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
@ -14023,7 +13568,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);
@ -16000,6 +15545,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;
@ -16038,7 +15584,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);

View file

@ -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

View file

@ -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

View file

@ -26,6 +26,7 @@
#include <retro_common_api.h>
#include <libretro.h>
#include <dynamic/dylib.h>
#include <queues/message_queue.h>
#ifdef HAVE_CONFIG_H
#include "config.h"