From 0b768d0460478a28427baded7044cbe8617e46be Mon Sep 17 00:00:00 2001 From: twinaphex Date: Wed, 10 Nov 2021 02:34:04 +0100 Subject: [PATCH] Split up recording code --- Makefile.common | 15 +- audio/audio_driver.c | 1 + configuration.c | 46 ++- gfx/drivers/gl2.c | 8 +- gfx/drivers/gl3.c | 4 +- gfx/drivers/vulkan.c | 7 +- gfx/video_driver.c | 1 + griffin/griffin.c | 1 + menu/cbs/menu_cbs_ok.c | 6 +- menu/menu_displaylist.c | 7 +- menu/menu_setting.c | 14 +- record/drivers/record_ffmpeg.c | 2 + record/record_driver.c | 479 +++++++++++++++++++++++++++++ record/record_driver.h | 166 ++++++++++ retroarch.c | 546 +++------------------------------ retroarch.h | 51 --- retroarch_types.h | 110 ------- runloop.h | 1 + 18 files changed, 755 insertions(+), 710 deletions(-) create mode 100644 record/record_driver.c create mode 100644 record/record_driver.h diff --git a/Makefile.common b/Makefile.common index 048f3f67d2..491b00bd88 100644 --- a/Makefile.common +++ b/Makefile.common @@ -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) diff --git a/audio/audio_driver.c b/audio/audio_driver.c index 7f49b43b1c..898195a5aa 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -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" diff --git a/configuration.c b/configuration.c index 453c670786..79190ecd7f 100644 --- a/configuration.c +++ b/configuration.c @@ -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)) { diff --git a/gfx/drivers/gl2.c b/gfx/drivers/gl2.c index f499749a18..6ca6503510 100644 --- a/gfx/drivers/gl2.c +++ b/gfx/drivers/gl2.c @@ -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)) { diff --git a/gfx/drivers/gl3.c b/gfx/drivers/gl3.c index fe2a150ecf..2417066d28 100644 --- a/gfx/drivers/gl3.c +++ b/gfx/drivers/gl3.c @@ -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)) { diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 3082f74d37..135f1edbcd 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -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; diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 5ce3dfb705..728f4e2f7c 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -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" diff --git a/griffin/griffin.c b/griffin/griffin.c index a209d0642c..6e6323ffa2 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1271,6 +1271,7 @@ WIFI /*============================================================ RECORDING ============================================================ */ +#include "../record/record_driver.c" #ifdef HAVE_FFMPEG #include "../record/drivers/record_ffmpeg.c" #endif diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index d7bfb1625d..a8a664411c 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -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; } diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index d5ede45890..24f52c0734 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -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), diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 1eee1503b1..606927d85d 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -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], diff --git a/record/drivers/record_ffmpeg.c b/record/drivers/record_ffmpeg.c index 20ba3ba731..16029944c9 100644 --- a/record/drivers/record_ffmpeg.c +++ b/record/drivers/record_ffmpeg.c @@ -42,6 +42,8 @@ #include #endif +#include "../record_driver.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/record/record_driver.c b/record/record_driver.c new file mode 100644 index 0000000000..20f1e8b953 --- /dev/null +++ b/record/record_driver.c @@ -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�s Su�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 . + */ + +#include +#include +#include +#include +#include + +#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; + } +} diff --git a/record/record_driver.h b/record/record_driver.h new file mode 100644 index 0000000000..58392d2230 --- /dev/null +++ b/record/record_driver.h @@ -0,0 +1,166 @@ +#ifndef _RECORD_DRIVER_H +#define _RECORD_DRIVER_H + +#include + +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 diff --git a/retroarch.c b/retroarch.c index d26365455c..dcdff4c509 100644 --- a/retroarch.c +++ b/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; @@ -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, ¶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 */ /** @@ -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); diff --git a/retroarch.h b/retroarch.h index cb3ed6b7b8..7047edd467 100644 --- a/retroarch.h +++ b/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 diff --git a/retroarch_types.h b/retroarch_types.h index 17764d9012..2858e5737e 100644 --- a/retroarch_types.h +++ b/retroarch_types.h @@ -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 diff --git a/runloop.h b/runloop.h index caaf2b3031..4b7974fcd5 100644 --- a/runloop.h +++ b/runloop.h @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef HAVE_CONFIG_H #include "config.h"