From f061941456823359828d27a90cb32ff3b2c02de1 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Mon, 17 Jun 2019 16:23:46 +0200 Subject: [PATCH] Integrate record_driver.c into retroarch.c --- Makefile.common | 1 - command.c | 1 - configuration.c | 2 - dynamic.c | 1 - frontend/frontend.c | 3 +- gfx/drivers/gl.c | 1 - gfx/drivers/gl_core.c | 1 - gfx/drivers/metal.m | 1 - gfx/drivers/vulkan.c | 1 - gfx/video_driver.c | 75 ---- griffin/griffin.c | 1 - list_special.c | 1 - menu/cbs/menu_cbs_ok.c | 2 - menu/menu_displaylist.c | 1 - menu/menu_setting.c | 1 - record/drivers/record_ffmpeg.c | 2 - record/drivers/record_null.c | 2 +- record/record_driver.c | 595 ------------------------------- record/record_driver.h | 220 ------------ retroarch.c | 628 ++++++++++++++++++++++++++++++--- retroarch.h | 156 +++++++- 21 files changed, 741 insertions(+), 955 deletions(-) delete mode 100644 gfx/video_driver.c delete mode 100644 record/record_driver.c delete mode 100644 record/record_driver.h diff --git a/Makefile.common b/Makefile.common index cebbe3c2f2..2754a843b9 100644 --- a/Makefile.common +++ b/Makefile.common @@ -260,7 +260,6 @@ OBJ += frontend/frontend.o \ input/drivers_hid/null_hid.o \ input/drivers_joypad/null_joypad.o \ playlist.o \ - record/record_driver.o \ record/drivers/record_null.o \ $(LIBRETRO_COMM_DIR)/features/features_cpu.o \ performance_counters.o \ diff --git a/command.c b/command.c index 5ad95c1561..982b2d88e8 100755 --- a/command.c +++ b/command.c @@ -74,7 +74,6 @@ #include "driver.h" #include "input/input_driver.h" #include "frontend/frontend_driver.h" -#include "record/record_driver.h" #include "file_path_special.h" #include "autosave.h" #include "core_info.h" diff --git a/configuration.c b/configuration.c index 4370531762..4df30bded5 100644 --- a/configuration.c +++ b/configuration.c @@ -54,8 +54,6 @@ #include "../list_special.h" -#include "record/record_driver.h" - #if defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP #include "uwp/uwp_func.h" #endif diff --git a/dynamic.c b/dynamic.c index acbbd91697..3fed3dce28 100644 --- a/dynamic.c +++ b/dynamic.c @@ -52,7 +52,6 @@ #include "dynamic.h" #include "command.h" -#include "record/record_driver.h" #include "driver.h" #include "performance_counters.h" #include "led/led_driver.h" diff --git a/frontend/frontend.c b/frontend/frontend.c index 51146b8bc4..b61ec9ac6c 100644 --- a/frontend/frontend.c +++ b/frontend/frontend.c @@ -38,13 +38,12 @@ #include "../paths.h" #include "../retroarch.h" #include "../verbosity.h" -#include "../record/record_driver.h" #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__) #include #endif -/* griffin hack */ +/* Griffin hack */ #ifdef HAVE_QT #ifndef HAVE_MAIN #define HAVE_MAIN diff --git a/gfx/drivers/gl.c b/gfx/drivers/gl.c index aafb2f0b3f..cd8628e96c 100644 --- a/gfx/drivers/gl.c +++ b/gfx/drivers/gl.c @@ -50,7 +50,6 @@ #include "../../configuration.h" #include "../../dynamic.h" -#include "../../record/record_driver.h" #include "../../retroarch.h" #include "../../verbosity.h" diff --git a/gfx/drivers/gl_core.c b/gfx/drivers/gl_core.c index b57cc60ef9..d5ce9299bf 100644 --- a/gfx/drivers/gl_core.c +++ b/gfx/drivers/gl_core.c @@ -33,7 +33,6 @@ #include "../../configuration.h" #include "../../dynamic.h" -#include "../../record/record_driver.h" #include "../../managers/state_manager.h" #include "../../retroarch.h" diff --git a/gfx/drivers/metal.m b/gfx/drivers/metal.m index c510250256..41a5367864 100644 --- a/gfx/drivers/metal.m +++ b/gfx/drivers/metal.m @@ -48,7 +48,6 @@ #import "../../driver.h" #import "../../configuration.h" -#import "../../record/record_driver.h" #import "../../retroarch.h" #import "../../verbosity.h" diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index edc43d0f13..aca5161b6a 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -47,7 +47,6 @@ #include "../../driver.h" #include "../../configuration.h" -#include "../../record/record_driver.h" #include "../../managers/state_manager.h" #include "../../retroarch.h" diff --git a/gfx/video_driver.c b/gfx/video_driver.c deleted file mode 100644 index 9aceff6ee3..0000000000 --- a/gfx/video_driver.c +++ /dev/null @@ -1,75 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * Copyright (C) 2011-2017 - Daniel De Matteis - * - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../menu/menu_shader.h" - -#ifdef HAVE_CONFIG_H -#include "../config.h" -#endif - -#include "../dynamic.h" - -#ifdef HAVE_THREADS -#include -#endif - -#ifdef HAVE_MENU -#include "../menu/menu_driver.h" -#include "../menu/menu_setting.h" -#ifdef HAVE_MENU_WIDGETS -#include "../menu/widgets/menu_widgets.h" -#endif -#endif - -#ifdef HAVE_VIDEO_LAYOUT -#include "video_layout.h" -#endif - -#include "video_thread_wrapper.h" -#include "video_driver.h" -#include "video_display_server.h" -#include "video_crt_switch.h" - -#include "../frontend/frontend_driver.h" -#include "../record/record_driver.h" -#include "../config.def.h" -#include "../configuration.h" -#include "../driver.h" -#include "../retroarch.h" -#include "../input/input_driver.h" -#include "../list_special.h" -#include "../core.h" -#include "../command.h" -#include "../msg_hash.h" -#include "../verbosity.h" diff --git a/griffin/griffin.c b/griffin/griffin.c index 37ce0dcdbd..41005fccb8 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1120,7 +1120,6 @@ WIFI /*============================================================ RECORDING ============================================================ */ -#include "../record/record_driver.c" #include "../record/drivers/record_null.c" #ifdef HAVE_FFMPEG diff --git a/list_special.c b/list_special.c index 7966dc1973..55caaa0647 100644 --- a/list_special.c +++ b/list_special.c @@ -39,7 +39,6 @@ #include "frontend/frontend_driver.h" #include "core_info.h" #include "input/input_driver.h" -#include "record/record_driver.h" #include "midi/midi_driver.h" #include "configuration.h" #include "retroarch.h" diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index e3128cfe91..26b625b70e 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -79,8 +79,6 @@ #include "../cheevos-new/cheevos.h" #endif -#include "../../record/record_driver.h" - #ifdef __WINRT__ #include "../../uwp/uwp_func.h" #endif diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index aad8bc90e6..811834bec1 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -76,7 +76,6 @@ #include "../managers/cheat_manager.h" #include "../managers/core_option_manager.h" #include "../paths.h" -#include "../record/record_driver.h" #include "../retroarch.h" #include "../core.h" #include "../frontend/frontend_driver.h" diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 339184936a..adad91bc71 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -75,7 +75,6 @@ #include "../dynamic.h" #include "../list_special.h" #include "../wifi/wifi_driver.h" -#include "../record/record_driver.h" #include "../input/input_driver.h" #include "../midi/midi_driver.h" #include "../tasks/tasks_internal.h" diff --git a/record/drivers/record_ffmpeg.c b/record/drivers/record_ffmpeg.c index 1592ea3adb..630dadf34d 100644 --- a/record/drivers/record_ffmpeg.c +++ b/record/drivers/record_ffmpeg.c @@ -64,8 +64,6 @@ extern "C" { } #endif -#include "../record_driver.h" - #include "../../configuration.h" #include "../../retroarch.h" #include "../../verbosity.h" diff --git a/record/drivers/record_null.c b/record/drivers/record_null.c index e6007d2804..3b6f3cbb6e 100644 --- a/record/drivers/record_null.c +++ b/record/drivers/record_null.c @@ -25,7 +25,7 @@ #include "../../config.h" #endif -#include "../record_driver.h" +#include "../../retroarch.h" static void record_null_free(void *data) { diff --git a/record/record_driver.c b/record/record_driver.c deleted file mode 100644 index c54f614920..0000000000 --- a/record/record_driver.c +++ /dev/null @@ -1,595 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * Copyright (C) 2011-2017 - Daniel De Matteis - * - * 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 - -#ifdef HAVE_CONFIG_H -#include "../config.h" -#endif - -#include "record_driver.h" - -#include "../command.h" -#include "../configuration.h" -#include "../driver.h" -#include "../retroarch.h" -#include "../verbosity.h" -#include "../msg_hash.h" -#include "../list_special.h" -#include "../paths.h" - -static const record_driver_t *record_drivers[] = { -#ifdef HAVE_FFMPEG - &record_ffmpeg, -#endif - &record_null, - NULL, -}; - -unsigned recording_width = 0; -unsigned recording_height = 0; -size_t recording_gpu_width = 0; -size_t recording_gpu_height = 0; -static bool recording_enable = false; -static bool streaming_enable = false; - -static const record_driver_t *recording_driver = NULL; -void *recording_data = NULL; - -/** - * record_driver_find_ident: - * @idx : index of driver to get handle to. - * - * Returns: Human-readable identifier of record driver at index. Can be NULL - * if nothing found. - **/ -const char *record_driver_find_ident(int idx) -{ - const record_driver_t *drv = record_drivers[idx]; - if (!drv) - return NULL; - return drv->ident; -} - -/** - * record_driver_find_handle: - * @idx : index of driver to get handle to. - * - * Returns: handle to record driver at index. Can be NULL - * if nothing found. - **/ -const void *record_driver_find_handle(int idx) -{ - const void *drv = record_drivers[idx]; - if (!drv) - return NULL; - return drv; -} - -/** - * 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); -} - -void find_record_driver(void) -{ - int i; - driver_ctx_info_t drv; - settings_t *settings = config_get_ptr(); - - drv.label = "record_driver"; - drv.s = settings->arrays.record_driver; - - driver_ctl(RARCH_DRIVER_CTL_FIND_INDEX, &drv); - - i = (int)drv.len; - - if (i >= 0) - recording_driver = (const record_driver_t*)record_driver_find_handle(i); - else - { - if (verbosity_is_enabled()) - { - unsigned d; - - RARCH_ERR("[recording] Couldn't find any record driver named \"%s\"\n", - settings->arrays.record_driver); - RARCH_LOG_OUTPUT("Available record drivers are:\n"); - for (d = 0; record_driver_find_handle(d); d++) - RARCH_LOG_OUTPUT("\t%s\n", record_driver_find_ident(d)); - RARCH_WARN("[recording] Going to default to first record driver...\n"); - } - - recording_driver = (const record_driver_t*)record_driver_find_handle(0); - - if (!recording_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. - **/ -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; -} - -/** - * 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). - **/ -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; -} - -void recording_dump_frame(const void *data, unsigned width, - unsigned height, size_t pitch, bool is_idle) -{ - bool has_gpu_record = false; - uint8_t *gpu_buf = NULL; - struct record_video_data ffemu_data = {0}; - - video_driver_get_record_status(&has_gpu_record, - &gpu_buf); - - ffemu_data.pitch = (int)pitch; - ffemu_data.width = width; - ffemu_data.height = height; - ffemu_data.data = data; - - if (has_gpu_record) - { - 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_WARN("[recording] %s \n", - msg_hash_to_str(MSG_VIEWPORT_SIZE_CALCULATION_FAILED)); - command_event(CMD_EVENT_GPU_RECORD_DEINIT, NULL); - - recording_dump_frame(data, width, height, pitch, is_idle); - return; - } - - /* User has resized. We kinda have a problem now. */ - if ( vp.width != recording_gpu_width || - vp.height != recording_gpu_height) - { - RARCH_WARN("[recording] %s\n", msg_hash_to_str(MSG_RECORDING_TERMINATED_DUE_TO_RESIZE)); - - runloop_msg_queue_push( - msg_hash_to_str(MSG_RECORDING_TERMINATED_DUE_TO_RESIZE), - 1, 180, true, - NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - command_event(CMD_EVENT_RECORD_DEINIT, NULL); - return; - } - - if (!gpu_buf) - return; - - /* Big bottleneck. - * Since we might need to do read-backs asynchronously, - * it might take 3-4 times before this returns true. */ - if (!video_driver_read_viewport(gpu_buf, is_idle)) - return; - - ffemu_data.pitch = (int)(recording_gpu_width * 3); - ffemu_data.width = (unsigned)recording_gpu_width; - ffemu_data.height = (unsigned)recording_gpu_height; - ffemu_data.data = gpu_buf + (ffemu_data.height - 1) * ffemu_data.pitch; - - ffemu_data.pitch = -ffemu_data.pitch; - } - - if (!has_gpu_record) - ffemu_data.is_dupe = !data; - - if (recording_driver && recording_driver->push_video) - recording_driver->push_video(recording_data, &ffemu_data); -} - -bool recording_deinit(void) -{ - if (!recording_data || !recording_driver) - return false; - - if (recording_driver->finalize) - recording_driver->finalize(recording_data); - - if (recording_driver->free) - recording_driver->free(recording_data); - - recording_data = NULL; - recording_driver = NULL; - - command_event(CMD_EVENT_GPU_RECORD_DEINIT, NULL); - - return true; -} - -bool recording_is_enabled(void) -{ - return recording_enable; -} - -void recording_set_state(bool state) -{ - recording_enable = state; -} - -bool streaming_is_enabled(void) -{ - return streaming_enable; -} - -void streaming_set_state(bool state) -{ - streaming_enable = state; -} - -void recording_push_audio(const int16_t *data, size_t samples) -{ - struct record_audio_data ffemu_data; - - ffemu_data.data = data; - ffemu_data.frames = samples / 2; - - if (recording_driver && recording_driver->push_audio) - recording_driver->push_audio(recording_data, &ffemu_data); -} - -/** - * recording_init: - * - * Initializes recording. - * - * Returns: true (1) if successful, otherwise false (0). - **/ -bool recording_init(void) -{ - char output[PATH_MAX_LENGTH]; - char buf[PATH_MAX_LENGTH]; - struct record_params params = {0}; - struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); - bool recording_enabled = recording_is_enabled(); - settings_t *settings = config_get_ptr(); - global_t *global = global_get_ptr(); - - if (!recording_enabled) - return false; - - output[0] = '\0'; - - if (rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) - { - RARCH_WARN("[recording] %s\n", - msg_hash_to_str(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED)); - return false; - } - - if (!settings->bools.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 - { - if(streaming_is_enabled()) - if (!string_is_empty(settings->paths.path_stream_url)) - strlcpy(output, settings->paths.path_stream_url, sizeof(output)); - else - /* Fallback, stream locally to 127.0.0.1 */ - snprintf(output, sizeof(output), "udp://127.0.0.1:%u", settings->uints.video_stream_port); - else - { - const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME)); - if (settings->uints.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 (settings->uints.video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST && settings->uints.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 (settings->uints.video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_GIF && settings->uints.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.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_get_pixel_format() == 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 (streaming_is_enabled()) - { - 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 (video_driver_supports_recording()) - { - 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 (settings->bools.video_force_aspect && - (video_driver_get_aspect_ratio() > 0.0f)) - params.aspect_ratio = video_driver_get_aspect_ratio(); - else - params.aspect_ratio = (float)vp.width / vp.height; - - params.pix_fmt = FFEMU_PIX_BGR24; - recording_gpu_width = vp.width; - recording_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_driver_gpu_record_init(gpu_size)) - return false; - } - else - { - if (recording_width || recording_height) - { - params.out_width = recording_width; - params.out_height = recording_height; - } - - if (settings->bools.video_force_aspect && - (video_driver_get_aspect_ratio() > 0.0f)) - params.aspect_ratio = video_driver_get_aspect_ratio(); - else - params.aspect_ratio = (float)params.out_width / params.out_height; - - if (settings->bools.video_post_filter_record - && video_driver_frame_filter_alive()) - { - unsigned max_width = 0; - unsigned max_height = 0; - - params.pix_fmt = FFEMU_PIX_RGB565; - - if (video_driver_frame_filter_is_32bit()) - params.pix_fmt = FFEMU_PIX_ARGB8888; - - rarch_softfilter_get_max_output_size( - video_driver_frame_filter_get_ptr(), - &max_width, &max_height); - params.fb_width = next_pow2(max_width); - params.fb_height = next_pow2(max_height); - } - } - - 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_driver, &recording_data, ¶ms)) - { - RARCH_ERR("[recording] %s\n", msg_hash_to_str(MSG_FAILED_TO_START_RECORDING)); - command_event(CMD_EVENT_GPU_RECORD_DEINIT, NULL); - - return false; - } - - return true; -} - -void *recording_driver_get_data_ptr(void) -{ - return recording_data; -} - -void recording_driver_clear_data_ptr(void) -{ - recording_data = NULL; -} - -void recording_driver_set_data_ptr(void *data) -{ - recording_data = data; -} - -void recording_driver_get_size(unsigned *width, unsigned *height) -{ - *width = recording_width; - *height = recording_height; -} - -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/"; - - if (!settings) - return; - - switch (settings->uints.streaming_mode) - { - case STREAMING_MODE_TWITCH: - { - if (!string_is_empty(settings->arrays.twitch_stream_key)) - snprintf(settings->paths.path_stream_url, sizeof(settings->paths.path_stream_url), - "%s%s", twitch_url, settings->arrays.twitch_stream_key); - else - { - /* To-Do: Show input box for twitch_stream_key*/ - RARCH_LOG("[recording] twitch streaming key empty\n"); - } - break; - } - case STREAMING_MODE_YOUTUBE: - { - if (!string_is_empty(settings->arrays.youtube_stream_key)) - { - snprintf(settings->paths.path_stream_url, sizeof(settings->paths.path_stream_url), - "%s%s", youtube_url, settings->arrays.youtube_stream_key); - } - else - { - /* To-Do: Show input box for youtube_stream_key*/ - RARCH_LOG("[recording] youtube streaming key empty\n"); - } - break; - } - case STREAMING_MODE_LOCAL: - /* To-Do: 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; - } -} - -void recording_driver_free_state(void) -{ - recording_gpu_width = 0; - recording_gpu_height = 0; - recording_width = 0; - recording_height = 0; -} diff --git a/record/record_driver.h b/record/record_driver.h deleted file mode 100644 index a2ad2cfa59..0000000000 --- a/record/record_driver.h +++ /dev/null @@ -1,220 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * Copyright (C) 2011-2017 - Daniel De Matteis - * - * 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 . - */ -#ifndef __RECORD_DRIVER_H -#define __RECORD_DRIVER_H - -#include -#include - -#include -#include - -RETRO_BEGIN_DECLS - -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_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; - - /* Desired output resolution. */ - unsigned out_width; - unsigned out_height; - - /* Total size of framebuffer used in input. */ - unsigned fb_width; - unsigned fb_height; - - /* Aspect ratio of input video. Parameters are passed to the muxer, - * the video itself is not scaled. - */ - float aspect_ratio; - - /* Audio channels. */ - unsigned channels; - - enum record_config_type preset; - - /* Input pixel format. */ - enum ffemu_pix_format pix_fmt; - - /* Filename to dump to. */ - const char *filename; - - /* Path to config. Optional. */ - const char *config; -}; - -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; - -extern const record_driver_t record_ffmpeg; -extern const record_driver_t record_null; - -/** - * 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); - -/** - * 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. - **/ -const record_driver_t *ffemu_find_backend(const char *ident); - -/** - * record_driver_find_handle: - * @idx : index of driver to get handle to. - * - * Returns: handle to record driver at index. Can be NULL - * if nothing found. - **/ -const void *record_driver_find_handle(int idx); - -/** - * record_driver_find_ident: - * @idx : index of driver to get handle to. - * - * Returns: Human-readable identifier of record driver at index. Can be NULL - * if nothing found. - **/ -const char *record_driver_find_ident(int idx); - -/** - * 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). - **/ -bool record_driver_init_first(const record_driver_t **backend, void **data, - const struct record_params *params); - -void recording_dump_frame(const void *data, unsigned width, - unsigned height, size_t pitch, bool is_idle); - -bool recording_deinit(void); - -void find_record_driver(void); - -/** - * recording_init: - * - * Initializes recording. - * - * Returns: true (1) if successful, otherwise false (0). - **/ -bool recording_init(void); - -bool recording_is_enabled(void); - -void recording_set_state(bool state); - -void streaming_set_state(bool state); - -void recording_push_audio(const int16_t *data, size_t samples); - -void *recording_driver_get_data_ptr(void); - -void recording_driver_clear_data_ptr(void); - -void recording_driver_set_data_ptr(void *data); - -void recording_driver_free_state(void); - -void recording_driver_get_size(unsigned *width, unsigned *height); - -bool recording_is_enabled(void); - -bool streaming_is_enabled(void); - -void recording_driver_update_streaming_url(void); - -extern void *recording_data; - -RETRO_END_DECLS - -#endif diff --git a/retroarch.c b/retroarch.c index 6ed371acea..ea484b5423 100644 --- a/retroarch.c +++ b/retroarch.c @@ -130,7 +130,6 @@ #endif #include "../gfx/video_display_server.h" #include "../gfx/video_crt_switch.h" -#include "record/record_driver.h" #include "wifi/wifi_driver.h" #include "led/led_driver.h" #include "midi/midi_driver.h" @@ -361,6 +360,536 @@ static void retroarch_msg_queue_init(void) #endif } +/* Recording */ + +static const record_driver_t *record_drivers[] = { +#ifdef HAVE_FFMPEG + &record_ffmpeg, +#endif + &record_null, + NULL, +}; + +unsigned recording_width = 0; +unsigned recording_height = 0; +size_t recording_gpu_width = 0; +size_t recording_gpu_height = 0; +static bool recording_enable = false; +static bool streaming_enable = false; + +static const record_driver_t *recording_driver = NULL; +static void *recording_data = NULL; + +static uint8_t *video_driver_record_gpu_buffer = NULL; + +/** + * record_driver_find_ident: + * @idx : index of driver to get handle to. + * + * Returns: Human-readable identifier of record driver at index. Can be NULL + * if nothing found. + **/ +const char *record_driver_find_ident(int idx) +{ + const record_driver_t *drv = record_drivers[idx]; + if (!drv) + return NULL; + return drv->ident; +} + +/** + * record_driver_find_handle: + * @idx : index of driver to get handle to. + * + * Returns: handle to record driver at index. Can be NULL + * if nothing found. + **/ +const void *record_driver_find_handle(int idx) +{ + const void *drv = record_drivers[idx]; + if (!drv) + return NULL; + return drv; +} + +/** + * 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(void) +{ + int i; + driver_ctx_info_t drv; + settings_t *settings = config_get_ptr(); + + drv.label = "record_driver"; + drv.s = settings->arrays.record_driver; + + driver_ctl(RARCH_DRIVER_CTL_FIND_INDEX, &drv); + + i = (int)drv.len; + + if (i >= 0) + recording_driver = (const record_driver_t*)record_driver_find_handle(i); + else + { + if (verbosity_is_enabled()) + { + unsigned d; + + RARCH_ERR("[recording] Couldn't find any record driver named \"%s\"\n", + settings->arrays.record_driver); + RARCH_LOG_OUTPUT("Available record drivers are:\n"); + for (d = 0; record_driver_find_handle(d); d++) + RARCH_LOG_OUTPUT("\t%s\n", record_driver_find_ident(d)); + RARCH_WARN("[recording] Going to default to first record driver...\n"); + } + + recording_driver = (const record_driver_t*)record_driver_find_handle(0); + + if (!recording_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_gpu_width = 0; + recording_gpu_height = 0; + recording_width = 0; + recording_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 void recording_dump_frame(const void *data, unsigned width, + unsigned height, size_t pitch, bool is_idle) +{ + struct record_video_data ffemu_data; + + ffemu_data.data = data; + ffemu_data.width = width; + ffemu_data.height = height; + ffemu_data.pitch = (int)pitch; + ffemu_data.is_dupe = false; + + if (video_driver_record_gpu_buffer != NULL) + { + 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_WARN("[recording] %s \n", + msg_hash_to_str(MSG_VIEWPORT_SIZE_CALCULATION_FAILED)); + command_event(CMD_EVENT_GPU_RECORD_DEINIT, NULL); + + recording_dump_frame(data, width, height, pitch, is_idle); + return; + } + + /* User has resized. We kinda have a problem now. */ + if ( vp.width != recording_gpu_width || + vp.height != recording_gpu_height) + { + RARCH_WARN("[recording] %s\n", msg_hash_to_str(MSG_RECORDING_TERMINATED_DUE_TO_RESIZE)); + + runloop_msg_queue_push( + msg_hash_to_str(MSG_RECORDING_TERMINATED_DUE_TO_RESIZE), + 1, 180, true, + NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + command_event(CMD_EVENT_RECORD_DEINIT, NULL); + return; + } + + /* Big bottleneck. + * Since we might need to do read-backs asynchronously, + * it might take 3-4 times before this returns true. */ + if (!video_driver_read_viewport(video_driver_record_gpu_buffer, is_idle)) + return; + + ffemu_data.pitch = (int)(recording_gpu_width * 3); + ffemu_data.width = (unsigned)recording_gpu_width; + ffemu_data.height = (unsigned)recording_gpu_height; + ffemu_data.data = video_driver_record_gpu_buffer + (ffemu_data.height - 1) * ffemu_data.pitch; + + ffemu_data.pitch = -ffemu_data.pitch; + } + else + ffemu_data.is_dupe = !data; + + recording_driver->push_video(recording_data, &ffemu_data); +} + +bool recording_deinit(void) +{ + if (!recording_data || !recording_driver) + return false; + + if (recording_driver->finalize) + recording_driver->finalize(recording_data); + + if (recording_driver->free) + recording_driver->free(recording_data); + + recording_data = NULL; + recording_driver = NULL; + + command_event(CMD_EVENT_GPU_RECORD_DEINIT, NULL); + + return true; +} + +bool recording_is_enabled(void) +{ + return recording_enable; +} + +void recording_set_state(bool state) +{ + recording_enable = state; +} + +bool streaming_is_enabled(void) +{ + return streaming_enable; +} + +void streaming_set_state(bool state) +{ + streaming_enable = state; +} + +/** + * recording_init: + * + * Initializes recording. + * + * Returns: true (1) if successful, otherwise false (0). + **/ +bool recording_init(void) +{ + char output[PATH_MAX_LENGTH]; + char buf[PATH_MAX_LENGTH]; + struct record_params params = {0}; + struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); + settings_t *settings = config_get_ptr(); + global_t *global = &g_extern; + + if (!recording_enable) + return false; + + output[0] = '\0'; + + if (rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) + { + RARCH_WARN("[recording] %s\n", + msg_hash_to_str(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED)); + return false; + } + + if (!settings->bools.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 + { + if(streaming_is_enabled()) + if (!string_is_empty(settings->paths.path_stream_url)) + strlcpy(output, settings->paths.path_stream_url, sizeof(output)); + else + /* Fallback, stream locally to 127.0.0.1 */ + snprintf(output, sizeof(output), "udp://127.0.0.1:%u", settings->uints.video_stream_port); + else + { + const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME)); + if (settings->uints.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 (settings->uints.video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST && settings->uints.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 (settings->uints.video_record_quality >= RECORD_CONFIG_TYPE_RECORDING_GIF && settings->uints.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.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_get_pixel_format() == 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 (streaming_is_enabled()) + { + 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 (video_driver_supports_recording()) + { + 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 (settings->bools.video_force_aspect && + (video_driver_get_aspect_ratio() > 0.0f)) + params.aspect_ratio = video_driver_get_aspect_ratio(); + else + params.aspect_ratio = (float)vp.width / vp.height; + + params.pix_fmt = FFEMU_PIX_BGR24; + recording_gpu_width = vp.width; + recording_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_driver_gpu_record_init(gpu_size)) + return false; + } + else + { + if (recording_width || recording_height) + { + params.out_width = recording_width; + params.out_height = recording_height; + } + + if (settings->bools.video_force_aspect && + (video_driver_get_aspect_ratio() > 0.0f)) + params.aspect_ratio = video_driver_get_aspect_ratio(); + else + params.aspect_ratio = (float)params.out_width / params.out_height; + + if (settings->bools.video_post_filter_record + && video_driver_frame_filter_alive()) + { + unsigned max_width = 0; + unsigned max_height = 0; + + params.pix_fmt = FFEMU_PIX_RGB565; + + if (video_driver_frame_filter_is_32bit()) + params.pix_fmt = FFEMU_PIX_ARGB8888; + + rarch_softfilter_get_max_output_size( + video_driver_frame_filter_get_ptr(), + &max_width, &max_height); + params.fb_width = next_pow2(max_width); + params.fb_height = next_pow2(max_height); + } + } + + 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_driver, &recording_data, ¶ms)) + { + RARCH_ERR("[recording] %s\n", msg_hash_to_str(MSG_FAILED_TO_START_RECORDING)); + command_event(CMD_EVENT_GPU_RECORD_DEINIT, NULL); + + return false; + } + + return true; +} + +void *recording_driver_get_data_ptr(void) +{ + return recording_data; +} + +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/"; + + if (!settings) + return; + + switch (settings->uints.streaming_mode) + { + case STREAMING_MODE_TWITCH: + { + if (!string_is_empty(settings->arrays.twitch_stream_key)) + snprintf(settings->paths.path_stream_url, sizeof(settings->paths.path_stream_url), + "%s%s", twitch_url, settings->arrays.twitch_stream_key); + else + { + /* To-Do: Show input box for twitch_stream_key*/ + RARCH_LOG("[recording] twitch streaming key empty\n"); + } + break; + } + case STREAMING_MODE_YOUTUBE: + { + if (!string_is_empty(settings->arrays.youtube_stream_key)) + { + snprintf(settings->paths.path_stream_url, sizeof(settings->paths.path_stream_url), + "%s%s", youtube_url, settings->arrays.youtube_stream_key); + } + else + { + /* To-Do: Show input box for youtube_stream_key*/ + RARCH_LOG("[recording] youtube streaming key empty\n"); + } + break; + } + case STREAMING_MODE_LOCAL: + /* To-Do: 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; + } +} + /* Audio */ #define AUDIO_BUFFER_FREE_SAMPLES_COUNT (8 * 1024) @@ -1142,9 +1671,15 @@ void audio_driver_sample(int16_t left, int16_t right) if (audio_driver_data_ptr < audio_driver_chunk_size) return; - if (recording_data) - recording_push_audio(audio_driver_output_samples_conv_buf, - audio_driver_data_ptr); + if (recording_data && recording_driver && recording_driver->push_audio) + { + struct record_audio_data ffemu_data; + + ffemu_data.data = audio_driver_output_samples_conv_buf; + ffemu_data.frames = audio_driver_data_ptr / 2; + + recording_driver->push_audio(recording_data, &ffemu_data); + } if (!(runloop_paused || !audio_driver_active || @@ -1171,14 +1706,28 @@ static void audio_driver_menu_sample(void) while (sample_count > 1024) { - if (recording_data) - recording_push_audio(samples_buf, 1024); + if (recording_data && recording_driver && recording_driver->push_audio) + { + struct record_audio_data ffemu_data; + + ffemu_data.data = samples_buf; + ffemu_data.frames = 1024 / 2; + + recording_driver->push_audio(recording_data, &ffemu_data); + } if (check_flush) audio_driver_flush(samples_buf, 1024, runloop_slowmotion); sample_count -= 1024; } - if (recording_data) - recording_push_audio(samples_buf, sample_count); + if (recording_data && recording_driver && recording_driver->push_audio) + { + struct record_audio_data ffemu_data; + + ffemu_data.data = samples_buf; + ffemu_data.frames = sample_count / 2; + + recording_driver->push_audio(recording_data, &ffemu_data); + } if (check_flush) audio_driver_flush(samples_buf, sample_count, runloop_slowmotion); } @@ -1201,8 +1750,15 @@ size_t audio_driver_sample_batch(const int16_t *data, size_t frames) if (audio_suspended) return frames; - if (recording_data) - recording_push_audio(data, frames << 1); + if (recording_data && recording_driver && recording_driver->push_audio) + { + struct record_audio_data ffemu_data; + + ffemu_data.data = data; + ffemu_data.frames = (frames << 1) / 2; + + recording_driver->push_audio(recording_data, &ffemu_data); + } if (!( runloop_paused || @@ -2003,10 +2559,15 @@ static void audio_driver_unset_callback(void) void audio_driver_frame_is_reverse(void) { /* We just rewound. Flush rewind audio buffer. */ - if (recording_data) - recording_push_audio( - audio_driver_rewind_buf + audio_driver_rewind_ptr, - audio_driver_rewind_size - audio_driver_rewind_ptr); + if (recording_data && recording_driver && recording_driver->push_audio) + { + struct record_audio_data ffemu_data; + + ffemu_data.data = audio_driver_rewind_buf + audio_driver_rewind_ptr; + ffemu_data.frames = (audio_driver_rewind_size - audio_driver_rewind_ptr) / 2; + + recording_driver->push_audio(recording_data, &ffemu_data); + } if (!( runloop_paused || @@ -2255,7 +2816,6 @@ static bool video_driver_cache_context = false; /* Set to true by driver if context caching succeeded. */ static bool video_driver_cache_context_ack = false; -static uint8_t *video_driver_record_gpu_buffer = NULL; #ifdef HAVE_THREADS static slock_t *display_lock = NULL; @@ -3471,7 +4031,7 @@ void video_driver_set_pixel_format(enum retro_pixel_format fmt) **/ bool video_driver_cached_frame(void) { - void *recording = recording_driver_get_data_ptr(); + void *recording = recording_data; /* Cannot allow recording when pushing duped frames. */ recording_data = NULL; @@ -4173,14 +4733,6 @@ bool video_driver_is_active(void) return video_driver_active; } -void video_driver_get_record_status( - bool *has_gpu_record, - uint8_t **gpu_buf) -{ - *gpu_buf = video_driver_record_gpu_buffer; - *has_gpu_record = video_driver_record_gpu_buffer != NULL; -} - bool video_driver_gpu_record_init(unsigned size) { video_driver_record_gpu_buffer = (uint8_t*)malloc(size); @@ -4472,6 +5024,7 @@ void video_driver_frame(const void *data, unsigned width, || !data || video_driver_record_gpu_buffer ) && recording_data + && recording_driver && recording_driver->push_video ) recording_dump_frame(data, width, height, pitch, video_info.runloop_is_idle); @@ -4491,7 +5044,8 @@ void video_driver_frame(const void *data, unsigned width, video_driver_state_buffer, output_pitch, data, width, height, pitch); - if (video_info.post_filter_record && recording_data) + if (video_info.post_filter_record && recording_data + && recording_driver && recording_driver->push_video) recording_dump_frame(video_driver_state_buffer, output_width, output_height, output_pitch, video_info.runloop_is_idle); @@ -6526,7 +7080,7 @@ static bool driver_update_system_av_info(const struct retro_system_av_info *info /* Cannot continue recording with different parameters. * Take the easiest route out and just restart the recording. */ - if (recording_driver_get_data_ptr()) + if (recording_data) { runloop_msg_queue_push( msg_hash_to_str(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT), @@ -7497,7 +8051,7 @@ static void retroarch_parse_input_and_config(int argc, char *argv[]) case 'r': strlcpy(global->record.path, optarg, sizeof(global->record.path)); - if (recording_is_enabled()) + if (recording_enable) recording_set_state(true); break; @@ -7683,21 +8237,13 @@ static void retroarch_parse_input_and_config(int argc, char *argv[]) break; case RA_OPT_SIZE: + if (sscanf(optarg, "%ux%u", + &recording_width, + &recording_height) != 2) { - unsigned recording_width = 0; - unsigned recording_height = 0; - - recording_driver_get_size(&recording_width, - &recording_height); - - if (sscanf(optarg, "%ux%u", - &recording_width, - &recording_height) != 2) - { - RARCH_ERR("Wrong format for --size.\n"); - retroarch_print_help(argv[0]); - retroarch_fail(1, "retroarch_parse_input()"); - } + RARCH_ERR("Wrong format for --size.\n"); + retroarch_print_help(argv[0]); + retroarch_fail(1, "retroarch_parse_input()"); } break; diff --git a/retroarch.h b/retroarch.h index e6164c7532..2184a8fae8 100644 --- a/retroarch.h +++ b/retroarch.h @@ -711,6 +711,158 @@ extern audio_driver_t audio_switch_thread; extern audio_driver_t audio_rwebaudio; extern audio_driver_t audio_null; +/* Recording */ + +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_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; + + /* Desired output resolution. */ + unsigned out_width; + unsigned out_height; + + /* Total size of framebuffer used in input. */ + unsigned fb_width; + unsigned fb_height; + + /* Aspect ratio of input video. Parameters are passed to the muxer, + * the video itself is not scaled. + */ + float aspect_ratio; + + /* Audio channels. */ + unsigned channels; + + enum record_config_type preset; + + /* Input pixel format. */ + enum ffemu_pix_format pix_fmt; + + /* Filename to dump to. */ + const char *filename; + + /* Path to config. Optional. */ + const char *config; +}; + +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; + +extern const record_driver_t record_ffmpeg; +extern const record_driver_t record_null; + +/** + * 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); + +/** + * record_driver_find_handle: + * @idx : index of driver to get handle to. + * + * Returns: handle to record driver at index. Can be NULL + * if nothing found. + **/ +const void *record_driver_find_handle(int idx); + +/** + * record_driver_find_ident: + * @idx : index of driver to get handle to. + * + * Returns: Human-readable identifier of record driver at index. Can be NULL + * if nothing found. + **/ +const char *record_driver_find_ident(int idx); + +bool recording_deinit(void); + +/** + * recording_init: + * + * Initializes recording. + * + * Returns: true (1) if successful, otherwise false (0). + **/ +bool recording_init(void); + +bool recording_is_enabled(void); + +void recording_set_state(bool state); + +void streaming_set_state(bool state); + +void *recording_driver_get_data_ptr(void); + +bool recording_is_enabled(void); + +bool streaming_is_enabled(void); + +void recording_driver_update_streaming_url(void); + /* Video */ #ifdef HAVE_OVERLAY @@ -1787,10 +1939,6 @@ void video_driver_reinit(void); void video_driver_get_window_title(char *buf, unsigned len); -void video_driver_get_record_status( - bool *has_gpu_record, - uint8_t **gpu_buf); - bool *video_driver_get_threaded(void); void video_driver_set_threaded(bool val);