diff --git a/tests/xrdp/test_tconfig.c b/tests/xrdp/test_tconfig.c new file mode 100644 index 00000000..586278eb --- /dev/null +++ b/tests/xrdp/test_tconfig.c @@ -0,0 +1,50 @@ +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#include "xrdp_tconfig.h" +#include "test_xrdp.h" +#include "xrdp.h" + +START_TEST(test_tconfig_gfx_always_success) +{ + ck_assert_int_eq(1, 1); +} +END_TEST + +START_TEST(test_tconfig_gfx_x264_load_basic) +{ + struct xrdp_tconfig_gfx gfxconfig; + int rv = tconfig_load_gfx(XRDP_TOP_SRCDIR "/xrdp/gfx.toml", &gfxconfig); + + ck_assert_int_eq(rv, 0); + + /* default */ + ck_assert_str_eq(gfxconfig.x264_param[0].preset, "ultrafast"); + ck_assert_str_eq(gfxconfig.x264_param[0].tune, "zerolatency"); + ck_assert_str_eq(gfxconfig.x264_param[0].profile, "main"); + ck_assert_int_eq(gfxconfig.x264_param[0].vbv_max_bitrate, 0); + ck_assert_int_eq(gfxconfig.x264_param[0].vbv_buffer_size, 0); + ck_assert_int_eq(gfxconfig.x264_param[0].fps_num, 24); + ck_assert_int_eq(gfxconfig.x264_param[0].fps_den, 1); + +} +END_TEST + +/******************************************************************************/ +Suite * +make_suite_tconfig_load_gfx(void) +{ + Suite *s; + TCase *tc_tconfig_load_gfx; + + s = suite_create("GfxLoad"); + + tc_tconfig_load_gfx = tcase_create("xrdp_tconfig_load_gfx"); + tcase_add_test(tc_tconfig_load_gfx, test_tconfig_gfx_always_success); + tcase_add_test(tc_tconfig_load_gfx, test_tconfig_gfx_x264_load_basic); + + suite_add_tcase(s, tc_tconfig_load_gfx); + + return s; +} diff --git a/tests/xrdp/test_xrdp.h b/tests/xrdp/test_xrdp.h index ab1a8c94..8e2e9af4 100644 --- a/tests/xrdp/test_xrdp.h +++ b/tests/xrdp/test_xrdp.h @@ -7,5 +7,6 @@ Suite *make_suite_test_bitmap_load(void); Suite *make_suite_test_keymap_load(void); Suite *make_suite_egfx_base_functions(void); Suite *make_suite_region(void); +Suite *make_suite_tconfig_load_gfx(void); #endif /* TEST_XRDP_H */ diff --git a/tests/xrdp/test_xrdp_main.c b/tests/xrdp/test_xrdp_main.c index 9236e59d..cac79e16 100644 --- a/tests/xrdp/test_xrdp_main.c +++ b/tests/xrdp/test_xrdp_main.c @@ -58,6 +58,7 @@ int main (void) srunner_add_suite(sr, make_suite_test_keymap_load()); srunner_add_suite(sr, make_suite_egfx_base_functions()); srunner_add_suite(sr, make_suite_region()); + srunner_add_suite(sr, make_suite_tconfig_load_gfx()); srunner_set_tap(sr, "-"); srunner_run_all (sr, CK_ENV); diff --git a/xrdp/gfx.toml b/xrdp/gfx.toml new file mode 100644 index 00000000..26bcff90 --- /dev/null +++ b/xrdp/gfx.toml @@ -0,0 +1,27 @@ + +[x264.default] +preset = "ultrafast" +tune = "zerolatency" +profile = "main" +vbv_max_bitrate = 0 +vbv_buffer_size = 0 +fps_num = 24 +fps_den = 1 + + +[x264.lan] +[x264.wan] +[x264.broadband_high] +[x264.satellite] + +[x264.broadband_low] +preset = "veryfast" +tune = "zerolatency" +vbv_max_bitrate = 1800 +vbv_buffer_size = 40 + +[x264.modem] +preset = "fast" +tune = "zerolatency" +vbv_max_bitrate = 500 +vbv_buffer_size = 40 diff --git a/xrdp/xrdp_encoder_x264.c b/xrdp/xrdp_encoder_x264.c index 81fd7179..d8ff5c9b 100644 --- a/xrdp/xrdp_encoder_x264.c +++ b/xrdp/xrdp_encoder_x264.c @@ -32,6 +32,7 @@ #include "arch.h" #include "os_calls.h" #include "xrdp_encoder_x264.h" +#include "xrdp_tconfig.h" #define X264_MAX_ENCODERS 16 @@ -47,6 +48,7 @@ struct x264_encoder struct x264_global { struct x264_encoder encoders[X264_MAX_ENCODERS]; + struct xrdp_tconfig_gfx_x264_param x264_param[NUM_CONNECTION_TYPES]; }; /*****************************************************************************/ @@ -54,7 +56,17 @@ void * xrdp_encoder_x264_create(void) { LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_encoder_x264_create:"); - return g_new0(struct x264_global, 1); + + struct x264_global *xg; + struct xrdp_tconfig_gfx gfxconfig; + xg = g_new0(struct x264_global, 1); + tconfig_load_gfx(GFX_CONF, &gfxconfig); + + memcpy(&xg->x264_param, &gfxconfig.x264_param, + sizeof(struct xrdp_tconfig_gfx_x264_param) * NUM_CONNECTION_TYPES); + + return xg; + } /*****************************************************************************/ @@ -128,20 +140,19 @@ xrdp_encoder_x264_encode(void *handle, int session, int left, int top, } if ((width > 0) && (height > 0)) { - //x264_param_default_preset(&(xe->x264_params), "superfast", "zerolatency"); - x264_param_default_preset(&(xe->x264_params), "ultrafast", "zerolatency"); + x264_param_default_preset(&(xe->x264_params), + xg->x264_param[ct].preset, + xg->x264_param[ct].tune); xe->x264_params.i_threads = 1; xe->x264_params.i_width = (width + 15) & ~15; xe->x264_params.i_height = (height + 15) & ~15; - xe->x264_params.i_fps_num = 24; - xe->x264_params.i_fps_den = 1; - //xe->x264_params.b_cabac = 1; - //xe->x264_params.i_bframe = 0; - //xe->x264_params.rc.i_rc_method = X264_RC_CQP; - //xe->x264_params.rc.i_qp_constant = 23; - //x264_param_apply_profile(&(xe->x264_params), "high"); - x264_param_apply_profile(&(xe->x264_params), "main"); - //x264_param_apply_profile(&(xe->x264_params), "baseline"); + xe->x264_params.i_fps_num = xg->x264_param[ct].fps_num; + xe->x264_params.i_fps_den = xg->x264_param[ct].fps_den; + xe->x264_params.rc.i_rc_method = X264_RC_CRF; + xe->x264_params.rc.i_vbv_max_bitrate = xg->x264_param[ct].vbv_max_bitrate; + xe->x264_params.rc.i_vbv_buffer_size = xg->x264_param[ct].vbv_buffer_size; + x264_param_apply_profile(&(xe->x264_params), + xg->x264_param[ct].profile); xe->x264_enc_han = x264_encoder_open(&(xe->x264_params)); LOG(LOG_LEVEL_INFO, "xrdp_encoder_x264_encode: " "x264_encoder_open rv %p for width %d height %d", diff --git a/xrdp/xrdp_tconfig.c b/xrdp/xrdp_tconfig.c new file mode 100644 index 00000000..d16a0f4a --- /dev/null +++ b/xrdp/xrdp_tconfig.c @@ -0,0 +1,244 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Koichiro Iwao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * @file xrdp_tconfig.c + * @brief TOML config loader + * @author Koichiro Iwao + * + */ + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include +#include +#include + +#include "arch.h" +#include "os_calls.h" +#include "parse.h" +#include "toml.h" +#include "ms-rdpbcgr.h" +#include "xrdp_tconfig.h" +#include "string_calls.h" + +#define TCLOG(log_level, args...) LOG(log_level, "TConfig: " args) + +#define X264_DEFAULT_PRESET "ultrafast" +#define X264_DEFAULT_TUNE "zerolatency" +#define X264_DEFAULT_PROFILE "main" +#define X264_DEFAULT_FPS_NUM 24 +#define X264_DEFAULT_FPS_DEN 1 + +static int +tconfig_load_gfx_x264_ct(toml_table_t *tfile, const int connection_type, + struct xrdp_tconfig_gfx_x264_param *param) +{ + if (connection_type > NUM_CONNECTION_TYPES) + { + TCLOG(LOG_LEVEL_ERROR, "Invalid connection type is given"); + return 1; + } + + toml_table_t *x264 = toml_table_in(tfile, "x264"); + if (!x264) + { + TCLOG(LOG_LEVEL_WARNING, "x264 params are not defined"); + return 1; + } + + toml_table_t *x264_ct = + toml_table_in(x264, rdpbcgr_connection_type_names[connection_type]); + toml_datum_t datum; + + if (!x264_ct) + { + TCLOG(LOG_LEVEL_WARNING, "x264 params for connection type [%s] is not defined", + rdpbcgr_connection_type_names[connection_type]); + return 1; + } + + /* preset */ + datum = toml_string_in(x264_ct, "preset"); + if (datum.ok) + { + g_strncpy(param[connection_type].preset, + datum.u.s, + sizeof(param[connection_type].preset) - 1); + free(datum.u.s); + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "x264 param preset is not set for connection type [%s], " + "adopting the default value \"" X264_DEFAULT_PRESET "\"", + rdpbcgr_connection_type_names[connection_type]); + g_strncpy(param[connection_type].preset, + X264_DEFAULT_PRESET, + sizeof(param[connection_type].preset) - 1); + } + + /* tune */ + datum = toml_string_in(x264_ct, "tune"); + if (datum.ok) + { + g_strncpy(param[connection_type].tune, + datum.u.s, + sizeof(param[connection_type].tune) - 1); + free(datum.u.s); + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "x264 param tune is not set for connection type [%s], " + "adopting the default value \"" X264_DEFAULT_TUNE "\"", + rdpbcgr_connection_type_names[connection_type]); + g_strncpy(param[connection_type].tune, + X264_DEFAULT_TUNE, + sizeof(param[connection_type].tune) - 1); + } + + /* profile */ + datum = toml_string_in(x264_ct, "profile"); + if (datum.ok) + { + g_strncpy(param[connection_type].profile, + datum.u.s, + sizeof(param[connection_type].profile) - 1); + free(datum.u.s); + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "x264 param profile is not set for connection type [%s], " + "adopting the default value \"" X264_DEFAULT_PROFILE "\"", + rdpbcgr_connection_type_names[connection_type]); + g_strncpy(param[connection_type].profile, + X264_DEFAULT_PROFILE, + sizeof(param[connection_type].profile) - 1); + } + + /* vbv_max_bitrate */ + datum = toml_int_in(x264_ct, "vbv_max_bitrate"); + if (datum.ok) + { + param[connection_type].vbv_max_bitrate = datum.u.i; + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "x264 param vbv_max_bit_rate is not set for connection type [%s], " + "adopting the default value [0]", + rdpbcgr_connection_type_names[connection_type]); + param[connection_type].vbv_max_bitrate = 0; + } + + /* vbv_buffer_size */ + datum = toml_int_in(x264_ct, "vbv_buffer_size"); + if (datum.ok) + { + param[connection_type].vbv_buffer_size = datum.u.i; + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "x264 param vbv_buffer_size is not set for connection type [%s], " + "adopting the default value [0]", + rdpbcgr_connection_type_names[connection_type]); + param[connection_type].vbv_buffer_size = 0; + } + + /* fps_num */ + datum = toml_int_in(x264_ct, "fps_num"); + if (datum.ok) + { + param[connection_type].fps_num = datum.u.i; + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "x264 param fps_num is not set for connection type [%s], " + "adopting the default value [0]", + rdpbcgr_connection_type_names[connection_type]); + param[connection_type].fps_num = X264_DEFAULT_FPS_NUM; + } + + /* fps_den */ + datum = toml_int_in(x264_ct, "fps_den"); + if (datum.ok) + { + param[connection_type].fps_den = datum.u.i; + } + else if (connection_type == 0) + { + TCLOG(LOG_LEVEL_WARNING, + "x264 param fps_den is not set for connection type [%s], " + "adopting the default value [0]", + rdpbcgr_connection_type_names[connection_type]); + param[connection_type].fps_num = X264_DEFAULT_FPS_DEN; + } + + return 0; +} + +int +tconfig_load_gfx(const char *filename, struct xrdp_tconfig_gfx *config) +{ + FILE *fp; + char errbuf[200]; + toml_table_t *tfile; + + if ((fp = fopen(filename, "r")) == NULL) + { + TCLOG(LOG_LEVEL_ERROR, "Error loading GFX config file %s (%s)", + filename, g_get_strerror()); + return 1; + } + else if ((tfile = toml_parse_file(fp, errbuf, sizeof(errbuf))) == NULL) + { + TCLOG(LOG_LEVEL_ERROR, "Error in GFX config file %s - %s", filename, errbuf); + fclose(fp); + return 1; + } + else + { + TCLOG(LOG_LEVEL_INFO, "Loading GFX config file %s", filename); + fclose(fp); + + memset(config, 0, sizeof(struct xrdp_tconfig_gfx)); + + /* First of all, read the default params and override later */ + tconfig_load_gfx_x264_ct(tfile, 0, config->x264_param); + + for (int ct = CONNECTION_TYPE_MODEM; ct < NUM_CONNECTION_TYPES; ct++) + { + memcpy(&config->x264_param[ct], &config->x264_param[0], + sizeof(struct xrdp_tconfig_gfx_x264_param)); + + tconfig_load_gfx_x264_ct(tfile, ct, config->x264_param); + } + + toml_free(tfile); + + return 0; + } +} + diff --git a/xrdp/xrdp_tconfig.h b/xrdp/xrdp_tconfig.h new file mode 100644 index 00000000..6a5ba970 --- /dev/null +++ b/xrdp/xrdp_tconfig.h @@ -0,0 +1,66 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Koichiro Iwao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * @file xrdp_tconfig.c + * @brief TOML config loader and structures + * @author Koichiro Iwao + * + */ +#ifndef _XRDP_TCONFIG_H_ +#define _XRDP_TCONFIG_H_ + +/* The number of connection types in MS-RDPBCGR 2.2.1.3.2 */ +#define NUM_CONNECTION_TYPES 7 +#define GFX_CONF XRDP_CFG_PATH "/gfx.toml" + +/* nc stands for new config */ +struct xrdp_tconfig_gfx_x264_param +{ + char preset[16]; + char tune[16]; + char profile[16]; + int vbv_max_bitrate; + int vbv_buffer_size; + int fps_num; + int fps_den; +}; + +struct xrdp_tconfig_gfx +{ + /* store x264 parameters for each connection type */ + struct xrdp_tconfig_gfx_x264_param x264_param[NUM_CONNECTION_TYPES]; +}; + +static const char *const rdpbcgr_connection_type_names[] = +{ + "default", /* for xrdp internal use */ + "modem", + "broadband_low", + "satellite", + "broadband_high", + "wan", + "lan", + "autodetect", + 0 +}; + +int tconfig_load_gfx(const char *filename, struct xrdp_tconfig_gfx *config); + +#endif