diff --git a/config.def.h b/config.def.h index 618fba6174..85a7104506 100644 --- a/config.def.h +++ b/config.def.h @@ -125,6 +125,10 @@ static const unsigned window_height = 720; static const unsigned fullscreen_x = 0; static const unsigned fullscreen_y = 0; +/* Number of threads to use for video recording */ + +static const unsigned video_record_threads = 2; + /* Amount of transparency to use for the main window. * 1 is the most transparent while 100 is opaque. */ diff --git a/configuration.c b/configuration.c index 947011d30f..0d209a5940 100644 --- a/configuration.c +++ b/configuration.c @@ -1676,6 +1676,8 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, SETTING_UINT("video_windowed_position_width", &settings->uints.window_position_width, true, window_width, false); SETTING_UINT("video_windowed_position_height", &settings->uints.window_position_height, true, window_height, false); + SETTING_UINT("video_record_threads", &settings->uints.video_record_threads, true, video_record_threads, false); + *size = count; return tmp; diff --git a/configuration.h b/configuration.h index 7555118d62..dee5497cb8 100644 --- a/configuration.h +++ b/configuration.h @@ -443,6 +443,8 @@ typedef struct settings unsigned window_position_y; unsigned window_position_width; unsigned window_position_height; + + unsigned video_record_threads; } uints; struct diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index f400919e98..15bd1b26c3 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3206,6 +3206,10 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_VIDEO_SCALE, "Windowed Scale" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_RECORD_THREADS, + "Recording Threads" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_VIDEO_SCALE_INTEGER, "Integer Scale" diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 36a9713301..11150ebf71 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -7316,6 +7316,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, menu_displaylist MENU_ENUM_LABEL_STREAMING_MODE, PARSE_ONLY_UINT, false) == 0) count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_VIDEO_RECORD_THREADS, + PARSE_ONLY_UINT, true) == 0) + count++; if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_STREAMING_TITLE, PARSE_ONLY_STRING, false) == 0) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 1d76dedf57..edf7979b35 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -336,16 +336,16 @@ static void setting_get_string_representation_video_stream_quality( /* TODO/FIXME - localize this */ switch (*setting->value.target.unsigned_integer) { - case 5: + case 8: strlcpy(s, "Custom", len); break; - case 6: + case 9: strlcpy(s, "Low", len); break; - case 7: + case 10: strlcpy(s, "Medium", len); break; - case 8: + case 11: strlcpy(s, "High", len); break; } @@ -375,6 +375,15 @@ static void setting_get_string_representation_video_record_quality(rarch_setting case 4: strlcpy(s, "Lossless", len); break; + case 5: + strlcpy(s, "WebM Fast", len); + break; + case 6: + strlcpy(s, "WebM High Quality", len); + break; + case 7: + strlcpy(s, "GIF", len); + break; } } @@ -6689,7 +6698,7 @@ static bool setting_append_list( (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; (*list)[list_info->index - 1].get_string_representation = &setting_get_string_representation_video_record_quality; - menu_settings_list_current_add_range(list, list_info, RECORD_CONFIG_TYPE_RECORDING_CUSTOM, RECORD_CONFIG_TYPE_RECORDING_LOSSLESS_QUALITY, 1, true, true); + menu_settings_list_current_add_range(list, list_info, RECORD_CONFIG_TYPE_RECORDING_CUSTOM, RECORD_CONFIG_TYPE_RECORDING_GIF, 1, true, true); CONFIG_PATH( list, list_info, @@ -6750,7 +6759,7 @@ static bool setting_append_list( (*list)[list_info->index - 1].offset_by = 1; menu_settings_list_current_add_range(list, list_info, 1, 65536, 1, true, true); - CONFIG_UINT( + CONFIG_UINT( list, list_info, &settings->uints.video_stream_quality, MENU_ENUM_LABEL_VIDEO_STREAM_QUALITY, @@ -6764,7 +6773,6 @@ static bool setting_append_list( (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; (*list)[list_info->index - 1].get_string_representation = &setting_get_string_representation_video_stream_quality; - (*list)[list_info->index - 1].offset_by = 5; menu_settings_list_current_add_range(list, list_info, RECORD_CONFIG_TYPE_STREAMING_CUSTOM, RECORD_CONFIG_TYPE_STREAMING_HIGH_QUALITY, 1, true, true); CONFIG_PATH( @@ -6795,6 +6803,22 @@ static bool setting_append_list( general_read_handler); settings_data_list_current_add_flags(list, list_info, SD_FLAG_ALLOW_INPUT); + + CONFIG_UINT( + list, list_info, + &settings->uints.video_record_threads, + MENU_ENUM_LABEL_VIDEO_RECORD_THREADS, + MENU_ENUM_LABEL_VALUE_VIDEO_RECORD_THREADS, + video_record_threads, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint_special; + menu_settings_list_current_add_range(list, list_info, 1, 8, 1, true, true); + settings_data_list_current_add_flags(list, list_info, SD_FLAG_LAKKA_ADVANCED); + CONFIG_DIR( list, list_info, global->record.output_dir, diff --git a/msg_hash.h b/msg_hash.h index e457eb3c30..3c16be4e89 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -758,6 +758,7 @@ enum msg_hash_enums MENU_LABEL(VIDEO_FORCE_SRGB_DISABLE), MENU_LABEL(VIDEO_ROTATION), MENU_LABEL(VIDEO_SCALE), + MENU_LABEL(VIDEO_RECORD_THREADS), MENU_LABEL(VIDEO_SMOOTH), MENU_LABEL(VIDEO_CROP_OVERSCAN), diff --git a/record/drivers/record_ffmpeg.c b/record/drivers/record_ffmpeg.c index b181492bde..f085a66a7e 100644 --- a/record/drivers/record_ffmpeg.c +++ b/record/drivers/record_ffmpeg.c @@ -97,6 +97,14 @@ extern "C" { #define PIX_FMT_BGR24 AV_PIX_FMT_BGR24 #endif +#ifndef PIX_FMT_RGB24 +#define PIX_FMT_RGB24 AV_PIX_FMT_RGB24 +#endif + +#ifndef PIX_FMT_RGB8 +#define PIX_FMT_RGB8 AV_PIX_FMT_RGB8 +#endif + #ifndef PIX_FMT_RGB565 #define PIX_FMT_RGB565 AV_PIX_FMT_RGB565 #endif @@ -556,7 +564,7 @@ static bool ffmpeg_init_config_common(struct ff_config_param *params, unsigned p { case RECORD_CONFIG_TYPE_RECORDING_LOW_QUALITY: case RECORD_CONFIG_TYPE_STREAMING_LOW_QUALITY: - params->threads = 1; + params->threads = settings->uints.video_record_threads; params->frame_drop_ratio = 1; params->audio_enable = true; params->audio_global_quality = 75; @@ -572,7 +580,7 @@ static bool ffmpeg_init_config_common(struct ff_config_param *params, unsigned p break; case RECORD_CONFIG_TYPE_RECORDING_MED_QUALITY: case RECORD_CONFIG_TYPE_STREAMING_MED_QUALITY: - params->threads = 1; + params->threads = settings->uints.video_record_threads; params->frame_drop_ratio = 1; params->audio_enable = true; params->audio_global_quality = 75; @@ -588,7 +596,7 @@ static bool ffmpeg_init_config_common(struct ff_config_param *params, unsigned p break; case RECORD_CONFIG_TYPE_RECORDING_HIGH_QUALITY: case RECORD_CONFIG_TYPE_STREAMING_HIGH_QUALITY: - params->threads = 1; + params->threads = settings->uints.video_record_threads; params->frame_drop_ratio = 1; params->audio_enable = true; params->audio_global_quality = 100; @@ -603,10 +611,10 @@ static bool ffmpeg_init_config_common(struct ff_config_param *params, unsigned p av_dict_set(¶ms->audio_opts, "audio_global_quality", "100", 0); break; case RECORD_CONFIG_TYPE_RECORDING_LOSSLESS_QUALITY: - params->threads = 1; + params->threads = settings->uints.video_record_threads; params->frame_drop_ratio = 1; params->audio_enable = true; - params->audio_global_quality = 100; + params->audio_global_quality = 80; params->out_pix_fmt = PIX_FMT_BGR24; strlcpy(params->vcodec, "libx264rgb", sizeof(params->vcodec)); @@ -615,10 +623,51 @@ static bool ffmpeg_init_config_common(struct ff_config_param *params, unsigned p av_dict_set(¶ms->video_opts, "preset", "medium", 0); av_dict_set(¶ms->video_opts, "tune", "animation", 0); av_dict_set(¶ms->video_opts, "crf", "0", 0); - av_dict_set(¶ms->audio_opts, "audio_global_quality", "100", 0); + av_dict_set(¶ms->audio_opts, "audio_global_quality", "80", 0); + break; + case RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST: + params->threads = settings->uints.video_record_threads; + params->frame_drop_ratio = 1; + params->audio_enable = true; + params->audio_global_quality = 50; + params->out_pix_fmt = PIX_FMT_YUV420P; + + strlcpy(params->vcodec, "libvpx", sizeof(params->vcodec)); + strlcpy(params->acodec, "libopus", sizeof(params->acodec)); + + av_dict_set(¶ms->video_opts, "deadline", "realtime", 0); + av_dict_set(¶ms->video_opts, "crf", "14", 0); + av_dict_set(¶ms->audio_opts, "audio_global_quality", "50", 0); + break; + case RECORD_CONFIG_TYPE_RECORDING_WEBM_HIGH_QUALITY: + params->threads = settings->uints.video_record_threads; + params->frame_drop_ratio = 1; + params->audio_enable = true; + params->audio_global_quality = 75; + params->out_pix_fmt = PIX_FMT_YUV420P; + + strlcpy(params->vcodec, "libvpx", sizeof(params->vcodec)); + strlcpy(params->acodec, "libopus", sizeof(params->acodec)); + + av_dict_set(¶ms->video_opts, "deadline", "realtime", 0); + av_dict_set(¶ms->video_opts, "crf", "4", 0); + av_dict_set(¶ms->audio_opts, "audio_global_quality", "75", 0); + break; + case RECORD_CONFIG_TYPE_RECORDING_GIF: + params->threads = settings->uints.video_record_threads; + params->frame_drop_ratio = 1; + params->audio_enable = false; + params->audio_global_quality = 0; + params->out_pix_fmt = PIX_FMT_RGB8; + + strlcpy(params->vcodec, "gif", sizeof(params->vcodec)); + strlcpy(params->acodec, "", sizeof(params->acodec)); + + av_dict_set(¶ms->video_opts, "framerate", "50", 0); + av_dict_set(¶ms->audio_opts, "audio_global_quality", "0", 0); break; case RECORD_CONFIG_TYPE_STREAMING_NETPLAY: - params->threads = 1; + params->threads = settings->uints.video_record_threads; params->frame_drop_ratio = 1; params->audio_enable = true; params->audio_global_quality = 50; @@ -645,6 +694,24 @@ static bool ffmpeg_init_config_common(struct ff_config_param *params, unsigned p params->scale_factor = 1; strlcpy(params->format, "matroska", sizeof(params->format)); } + else if (preset >= RECORD_CONFIG_TYPE_RECORDING_WEBM_FAST && settings->uints.video_record_quality < RECORD_CONFIG_TYPE_RECORDING_GIF) + { + if (!settings->bools.video_gpu_record) + params->scale_factor = settings->uints.video_record_scale_factor > 0 ? + settings->uints.video_record_scale_factor : 1; + else + params->scale_factor = 1; + strlcpy(params->format, "webm", sizeof(params->format)); + } + else if (preset <= RECORD_CONFIG_TYPE_STREAMING_LOW_QUALITY) + { + if (!settings->bools.video_gpu_record) + params->scale_factor = settings->uints.video_record_scale_factor > 0 ? + settings->uints.video_record_scale_factor : 1; + else + params->scale_factor = 1; + strlcpy(params->format, "gif", sizeof(params->format)); + } else if (preset <= RECORD_CONFIG_TYPE_STREAMING_HIGH_QUALITY) { if (!settings->bools.video_gpu_record) diff --git a/record/record_driver.c b/record/record_driver.c index 9426b58e2f..e92f0d8724 100644 --- a/record/record_driver.c +++ b/record/record_driver.c @@ -366,10 +366,24 @@ bool recording_init(void) else { const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME)); - fill_str_dated_filename(buf, game_name, - "mkv", sizeof(buf)); - fill_pathname_join(output, global->record.output_dir, buf, sizeof(output)); - + 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 + { + fill_str_dated_filename(buf, game_name, + "gif", sizeof(buf)); + fill_pathname_join(output, global->record.output_dir, buf, sizeof(output)); + } } } diff --git a/record/record_driver.h b/record/record_driver.h index 6f29831549..8d034f8939 100644 --- a/record/record_driver.h +++ b/record/record_driver.h @@ -46,11 +46,15 @@ enum record_config_type 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_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() */