mirror of
https://github.com/libretro/RetroArch
synced 2025-02-06 00:39:53 +00:00
Integrate record_driver.c into retroarch.c
This commit is contained in:
parent
ee2745d1bd
commit
f061941456
@ -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 \
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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 <objbase.h>
|
||||
#endif
|
||||
|
||||
/* griffin hack */
|
||||
/* Griffin hack */
|
||||
#ifdef HAVE_QT
|
||||
#ifndef HAVE_MAIN
|
||||
#define HAVE_MAIN
|
||||
|
@ -50,7 +50,6 @@
|
||||
|
||||
#include "../../configuration.h"
|
||||
#include "../../dynamic.h"
|
||||
#include "../../record/record_driver.h"
|
||||
|
||||
#include "../../retroarch.h"
|
||||
#include "../../verbosity.h"
|
||||
|
@ -33,7 +33,6 @@
|
||||
|
||||
#include "../../configuration.h"
|
||||
#include "../../dynamic.h"
|
||||
#include "../../record/record_driver.h"
|
||||
#include "../../managers/state_manager.h"
|
||||
|
||||
#include "../../retroarch.h"
|
||||
|
@ -48,7 +48,6 @@
|
||||
|
||||
#import "../../driver.h"
|
||||
#import "../../configuration.h"
|
||||
#import "../../record/record_driver.h"
|
||||
|
||||
#import "../../retroarch.h"
|
||||
#import "../../verbosity.h"
|
||||
|
@ -47,7 +47,6 @@
|
||||
|
||||
#include "../../driver.h"
|
||||
#include "../../configuration.h"
|
||||
#include "../../record/record_driver.h"
|
||||
#include "../../managers/state_manager.h"
|
||||
|
||||
#include "../../retroarch.h"
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <compat/strl.h>
|
||||
#include <retro_common_api.h>
|
||||
#include <file/config_file.h>
|
||||
#include <features/features_cpu.h>
|
||||
#include <file/file_path.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <retro_math.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
#include <retro_assert.h>
|
||||
#include <gfx/scaler/pixconv.h>
|
||||
#include <gfx/scaler/scaler.h>
|
||||
#include <gfx/video_frame.h>
|
||||
#include <formats/image.h>
|
||||
|
||||
#include "../menu/menu_shader.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
#include "../dynamic.h"
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
#include <rthreads/rthreads.h>
|
||||
#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"
|
@ -1120,7 +1120,6 @@ WIFI
|
||||
/*============================================================
|
||||
RECORDING
|
||||
============================================================ */
|
||||
#include "../record/record_driver.c"
|
||||
#include "../record/drivers/record_null.c"
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
|
@ -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"
|
||||
|
@ -79,8 +79,6 @@
|
||||
#include "../cheevos-new/cheevos.h"
|
||||
#endif
|
||||
|
||||
#include "../../record/record_driver.h"
|
||||
|
||||
#ifdef __WINRT__
|
||||
#include "../../uwp/uwp_func.h"
|
||||
#endif
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -64,8 +64,6 @@ extern "C" {
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "../record_driver.h"
|
||||
|
||||
#include "../../configuration.h"
|
||||
#include "../../retroarch.h"
|
||||
#include "../../verbosity.h"
|
||||
|
@ -25,7 +25,7 @@
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#include "../record_driver.h"
|
||||
#include "../../retroarch.h"
|
||||
|
||||
static void record_null_free(void *data)
|
||||
{
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <file/file_path.h>
|
||||
#include <compat/strl.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <retro_math.h>
|
||||
|
||||
#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;
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef __RECORD_DRIVER_H
|
||||
#define __RECORD_DRIVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <retro_common_api.h>
|
||||
|
||||
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
|
628
retroarch.c
628
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;
|
||||
|
||||
|
156
retroarch.h
156
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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user