Skip to content

Commit

Permalink
improved session start request
Browse files Browse the repository at this point in the history
  • Loading branch information
mariotaku committed Jul 26, 2023
1 parent 59b6eeb commit 5b94568
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 44 deletions.
7 changes: 4 additions & 3 deletions core/libgamestream/libgamestream/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef struct _SERVER_DATA {
bool supports4K;
bool supportsHdr;
bool unsupported;
bool isGfe;
int currentGame;
int serverMajorVersion;
const char *gsVersion;
Expand All @@ -46,17 +47,17 @@ typedef struct _SERVER_DATA {

typedef struct GS_CLIENT_T *GS_CLIENT;

GS_CLIENT gs_new(const char *keydir, int log_level);
GS_CLIENT gs_new(const char *keydir);

int gs_conf_init(const char *keydir);

void gs_destroy(GS_CLIENT hnd);

void gs_set_timeout(GS_CLIENT hnd, int timeout);
void gs_set_timeout(GS_CLIENT hnd, int timeout_secs);

int gs_get_status(GS_CLIENT hnd, PSERVER_DATA server, const char *address, bool unsupported);

int gs_start_app(GS_CLIENT hnd, PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops,
int gs_start_app(GS_CLIENT hnd, PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool is_gfe, bool sops,
bool localaudio, int gamepad_mask);

int gs_applist(GS_CLIENT hnd, const SERVER_DATA *server, PAPP_LIST *app_list);
Expand Down
2 changes: 1 addition & 1 deletion core/libgamestream/libgamestream/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ typedef struct _HTTP_DATA {
size_t size;
} HTTP_DATA;

HTTP *http_create(const char *keydir, int verbosity);
HTTP *http_create(const char *keydir);

int http_request(HTTP *http, char *url, HTTP_DATA * data);

Expand Down
76 changes: 63 additions & 13 deletions core/libgamestream/src/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
static bool construct_url(GS_CLIENT, char *url, size_t ulen, bool secure, const char *address, const char *action,
const char *fmt, ...);

static bool append_param(char *url, size_t ulen, const char *param, const char *value_fmt, ...);

static int load_server_status(GS_CLIENT hnd, PSERVER_DATA server) {
int ret;
char url[4096];
Expand Down Expand Up @@ -158,6 +160,10 @@ static int load_server_status(GS_CLIENT hnd, PSERVER_DATA server) {
server->supports4K = serverCodecModeSupport != 0;
server->supportsHdr = serverCodecModeSupport & 0x200;
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
// Real Nvidia host software (GeForce Experience and RTX Experience) both use the 'Mjolnir'
// codename in the state field and no version of Sunshine does. We can use this to bypass
// some assumptions about Nvidia hardware that don't apply to Sunshine hosts.
server->isGfe = strstr(stateText, "MJOLNIR") != NULL;

if (strstr(stateText, "_SERVER_BUSY") == NULL) {
// After GFE 2.8, current game remains set even after streaming
Expand Down Expand Up @@ -617,7 +623,7 @@ int gs_applist(GS_CLIENT hnd, const SERVER_DATA *server, PAPP_LIST *list) {
return ret;
}

int gs_start_app(GS_CLIENT hnd, PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, bool sops,
int gs_start_app(GS_CLIENT hnd, PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, bool is_gfe, bool sops,
bool localaudio, int gamepad_mask) {
int ret = GS_OK;
char *result = NULL;
Expand Down Expand Up @@ -665,21 +671,43 @@ int gs_start_app(GS_CLIENT hnd, PSERVER_DATA server, STREAM_CONFIGURATION *confi
bytes_to_hex((unsigned char *) config->remoteInputAesKey, rikey_hex, 16);

data = http_data_alloc();
bool resume = server->currentGame == 0;

if (resume) {
gs_set_timeout(hnd, 30);
} else {
gs_set_timeout(hnd, 120);
}

// Using an FPS value over 60 causes SOPS to default to 720p60,
// so force it to 0 to ensure the correct resolution is set. We
// used to use 60 here but that locked the frame rate to 60 FPS
// on GFE 3.20.3.
int fps = config->fps > 60 ? 0 : config->fps;
int fps = is_gfe && config->fps > 60 ? 0 : config->fps;

int surround_info = SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config->audioConfiguration);
construct_url(hnd, url, sizeof(url), true, server->serverInfo.address,
server->currentGame == 0 ? "launch" : "resume",
"appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d%s",
appId, config->width, config->height, fps, sops, rikey_hex, rikeyid, localaudio, surround_info,
gamepad_mask, gamepad_mask, config->supportedVideoFormats & VIDEO_FORMAT_MASK_10BIT
? "&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0"
: "");
const char *action = resume ? "launch" : "resume";
construct_url(hnd, url, sizeof(url), true, server->serverInfo.address, action, "appid=%d", appId);

append_param(url, sizeof(url), "mode", "%dx%dx%d", config->width, config->height, fps);
append_param(url, sizeof(url), "additionalStates", "1");
append_param(url, sizeof(url), "sops", "%d", sops);
append_param(url, sizeof(url), "rikey", "%s", rikey_hex);
append_param(url, sizeof(url), "rikeyid", "%d", rikeyid);
append_param(url, sizeof(url), "surroundAudioInfo", "%d", surround_info);
if (!resume || !is_gfe) {
if (config->supportedVideoFormats & VIDEO_FORMAT_MASK_10BIT) {
append_param(url, sizeof(url), "hdrMode", "1");
append_param(url, sizeof(url), "clientHdrCapVersion", "0");
append_param(url, sizeof(url), "clientHdrCapSupportedFlagsInUint32", "0");
append_param(url, sizeof(url), "clientHdrCapMetaDataId", "NV_STATIC_METADATA_TYPE_1");
append_param(url, sizeof(url), "clientHdrCapDisplayData", "0x0x0x0x0x0x0x0x0x0x0");
}
append_param(url, sizeof(url), "localAudioPlayMode", "%d", localaudio);
append_param(url, sizeof(url), "remoteControllersBitmap", "%d", gamepad_mask);
append_param(url, sizeof(url), "gcmap", "%d", gamepad_mask);
}

if ((ret = http_request(hnd->http, url, data)) == GS_OK) {
server->currentGame = appId;
} else {
Expand Down Expand Up @@ -786,15 +814,15 @@ int gs_download_cover(GS_CLIENT hnd, const SERVER_DATA *server, int appid, const
return ret;
}

GS_CLIENT gs_new(const char *keydir, int log_level) {
GS_CLIENT gs_new(const char *keydir) {
struct GS_CLIENT_T *hnd = malloc(sizeof(struct GS_CLIENT_T));
memset(hnd, 0, sizeof(struct GS_CLIENT_T));
if (gs_conf_load(hnd, keydir) != GS_OK) {
free(hnd);
return NULL;
}

HTTP *http = http_create(keydir, log_level);
HTTP *http = http_create(keydir);
if (http == NULL) {
free(hnd);
return NULL;
Expand All @@ -811,8 +839,8 @@ void gs_destroy(GS_CLIENT hnd) {
free((void *) hnd);
}

void gs_set_timeout(GS_CLIENT hnd, int timeout) {
http_set_timeout(hnd->http, timeout);
void gs_set_timeout(GS_CLIENT hnd, int timeout_secs) {
http_set_timeout(hnd->http, timeout_secs);
}

int gs_get_status(GS_CLIENT hnd, PSERVER_DATA server, const char *address, bool unsupported) {
Expand Down Expand Up @@ -844,3 +872,25 @@ static bool construct_url(GS_CLIENT hnd, char *url, size_t ulen, bool secure, co
}
return true;
}

static bool append_param(char *url, size_t ulen, const char *param, const char *value_fmt, ...) {
char value[256];
va_list ap;
va_start(ap, value_fmt);
vsnprintf(value, 256, value_fmt, ap);
va_end(ap);

size_t plen = strlen(param);
size_t vlen = strlen(value);
char *p = url + strlen(url);
size_t append_len = plen + vlen + 3 /* "&param=value\0" */;
if ((p - url) + append_len > ulen) {
return false;
}
*p++ = '&';
p = stpncpy(p, param, plen);
*p++ = '=';
p = stpncpy(p, value, vlen);
*p = '\0';
return true;
}
18 changes: 11 additions & 7 deletions core/libgamestream/src/http.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "http.h"
#include "errors.h"
#include "set_error.h"
#include "logging.h"

#include <string.h>
#include <curl/curl.h>
Expand All @@ -35,7 +36,6 @@

struct HTTP_T {
CURL *curl;
int verbosity;
pthread_mutex_t mutex;
};

Expand All @@ -53,7 +53,7 @@ static size_t write_fn(void *contents, size_t size, size_t nmemb, void *userp) {
return realsize;
}

HTTP *http_create(const char *keydir, int verbosity) {
HTTP *http_create(const char *keydir) {
CURL *curl = curl_easy_init();
if (curl == NULL) {
gs_set_error(GS_ERROR, "Failed to create cURL instance");
Expand All @@ -80,7 +80,6 @@ HTTP *http_create(const char *keydir, int verbosity) {
struct HTTP_T *http = malloc(sizeof(struct HTTP_T));
assert(http != NULL);
http->curl = curl;
http->verbosity = verbosity;
pthread_mutex_init(&http->mutex, NULL);
return http;
}
Expand All @@ -100,17 +99,22 @@ int http_request(HTTP *http, char *url, HTTP_DATA *data) {
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
curl_easy_setopt(curl, CURLOPT_URL, url);

if (http->verbosity) {
printf("Request %s\n", url);
}
commons_log_debug("GameStream", "Request %p %s", data, url);

int ret = GS_FAILED;
CURLcode res = curl_easy_perform(curl);

if (res != CURLE_OK) {
ret = gs_set_error(GS_IO_ERROR, "cURL error: %s", curl_easy_strerror(res));
const char *errmsg = curl_easy_strerror(res);
ret = gs_set_error(GS_IO_ERROR, "cURL error: %s", errmsg);
commons_log_debug("GameStream", "Request %p error %d: %s", data, ret, errmsg);
goto finish;
}
assert (data->memory != NULL);
int http_code = 0;
curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &http_code);
commons_log_debug("GameStream", "Request %p response %d", data, http_code);
commons_log_verbose("GameStream", "Request %p body %s", data, data->memory);

ret = GS_OK;
finish:
Expand Down
4 changes: 2 additions & 2 deletions src/app/backend/backend_gs.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ GS_CLIENT app_gs_client_new(app_t *app) {
SDL_assert_release(app->backend.gs_client_mutex != NULL);
SDL_LockMutex(app->backend.gs_client_mutex);
SDL_assert_release(app_configuration != NULL);
GS_CLIENT client = gs_new(app_configuration->key_dir, app_configuration->debug_level);
GS_CLIENT client = gs_new(app_configuration->key_dir);
if (client == NULL && gs_get_error(NULL) == GS_BAD_CONF) {
if (gs_conf_init(app_configuration->key_dir) != GS_OK) {
const char *message = NULL;
Expand All @@ -23,7 +23,7 @@ GS_CLIENT app_gs_client_new(app_t *app) {
"Details: %s", message);
app_halt(app);
} else {
client = gs_new(app_configuration->key_dir, app_configuration->debug_level);
client = gs_new(app_configuration->key_dir);
}
}
if (client == NULL) {
Expand Down
2 changes: 2 additions & 0 deletions src/app/backend/pcmanager/pcmanager.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "pclist.h"
#include "app.h"
#include "backend/pcmanager/worker/worker.h"
#include "logging.h"

pcmanager_t *pcmanager_new(app_t *app, executor_t *executor) {
pcmanager_t *manager = SDL_calloc(1, sizeof(pcmanager_t));
Expand Down Expand Up @@ -43,6 +44,7 @@ bool pcmanager_quitapp(pcmanager_t *manager, const uuidstr_t *uuid, pcmanager_ca

void pcmanager_request_update(pcmanager_t *manager, const uuidstr_t *uuid, pcmanager_callback_t callback,
void *userdata) {
commons_log_info("PcManager", "Requesting update for %s", (const char*) uuid);
worker_context_t *ctx = worker_context_new(manager, uuid, callback, userdata);
pcmanager_worker_queue(manager, worker_host_update, ctx);
}
Expand Down
5 changes: 3 additions & 2 deletions src/app/backend/pcmanager/worker/update.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,18 @@ int pcmanager_update_by_ip(worker_context_t *context, const char *ip, bool force
}
}
if (ret == GS_OK) {
commons_log_info("PCManager", "Finished updating status from %s", server->serverInfo.address);
uuidstr_t uuid;
uuidstr_fromstr(&uuid, server->uuid);
SERVER_STATE state = {.code = server->paired ? SERVER_STATE_AVAILABLE : SERVER_STATE_NOT_PAIRED};
pclist_upsert(manager, &uuid, &state, server);
} else {
const char *gs_error = NULL;
ret = gs_get_error(&gs_error);
commons_log_warn("PCManager", "Error %d while updating status from %s: %s", ret, server->serverInfo.address,
gs_error);
context->error = gs_error != NULL ? strdup(gs_error) : NULL;
if (existing && ret == GS_IO_ERROR) {
commons_log_warn("PCManager", "IO error while updating status from %s: %s", server->serverInfo.address,
gs_error);
SERVER_STATE state = {.code = SERVER_STATE_OFFLINE};
pclist_upsert(manager, &existing->id, &state, NULL);
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/stream/session_worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ int session_worker(session_t *session) {
commons_log_info("Session", "Launch app %d...", appId);
GS_CLIENT client = app_gs_client_new(app);
gs_set_timeout(client, 30);
int ret = gs_start_app(client, server, &session->config.stream, appId, session->config.sops,
int ret = gs_start_app(client, server, &session->config.stream, appId, server->isGfe, session->config.sops,
session->config.local_audio, app_input_gamepads_mask(&app->input));
if (ret != GS_OK) {
session_set_state(session, STREAMING_ERROR);
Expand Down
22 changes: 13 additions & 9 deletions src/app/ui/launcher/server.context_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "util/i18n.h"
#include "lvgl/util/lv_app_utils.h"
#include "util/nullable.h"

typedef struct context_menu_t {
lv_fragment_t base;
Expand Down Expand Up @@ -85,7 +86,7 @@ static lv_obj_t *create_obj(lv_fragment_t *self, lv_obj_t *parent) {

static void context_menu_cancel_cb(lv_event_t *e) {
lv_obj_t *target = lv_event_get_target(e);
if (target->parent != lv_event_get_current_target(e)) return;
if (target->parent != lv_event_get_current_target(e)) { return; }
lv_msgbox_close(lv_event_get_current_target(e)->parent);
}

Expand All @@ -97,9 +98,9 @@ static void context_menu_short_click_cb(lv_event_t *e) {
static void context_menu_click_cb(lv_event_t *e) {
lv_obj_t *target = lv_event_get_target(e);
context_menu_t *controller = lv_event_get_user_data(e);
if (!controller->single_clicked) return;
if (!controller->single_clicked) { return; }
lv_obj_t *current_target = lv_event_get_current_target(e);
if (target->parent != current_target) return;
if (target->parent != current_target) { return; }
void *target_userdata = lv_obj_get_user_data(target);
lv_obj_t *mbox = lv_event_get_current_target(e)->parent;
const pclist_t *node = pcmanager_node(pcmanager, &controller->uuid);
Expand All @@ -116,14 +117,17 @@ static void context_menu_click_cb(lv_event_t *e) {

static void open_info(const pclist_t *node) {
static const char *btn_txts[] = {translatable("OK"), ""};
lv_obj_t *mbox = lv_msgbox_create_i18n(NULL, node->server->hostname, "placeholder", btn_txts, false);
const SERVER_DATA *server = node->server;
lv_obj_t *mbox = lv_msgbox_create_i18n(NULL, server->hostname, "placeholder", btn_txts, false);
lv_obj_t *message = lv_msgbox_get_text(mbox);
lv_label_set_text_fmt(message, locstr("IP address: %s\nGPU: %s\nSupports 4K: %s\n"
"Supports HDR: %s\nGeForce Experience: %s"),
node->server->serverInfo.address, node->server->gpuType,
node->server->supports4K ? "YES" : "NO",
node->server->supportsHdr ? "YES" : "NO",
node->server->serverInfo.serverInfoGfeVersion);
"Supports HDR: %s\nHost Software Version: %s\n"
"GeForce Experience: %s"),
server->serverInfo.address, str_null_or_empty(server->gpuType) ? "Unknown" : server->gpuType,
server->supports4K ? "YES" : "NO",
server->supportsHdr ? "YES" : "NO",
server->serverInfo.serverInfoAppVersion,
server->isGfe ? server->serverInfo.serverInfoGfeVersion : "NO");
lv_obj_add_event_cb(mbox, info_action_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_center(mbox);
}
Expand Down
13 changes: 9 additions & 4 deletions src/app/util/nullable.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@
#include <stdlib.h>
#include <string.h>

void free_nullable(void *p) {
if (!p) return;
void free_nullable(void *p) {
if (!p) { return; }
free(p);
}

char *strdup_nullable(const char *p) {
if (!p) return NULL;
char *strdup_nullable(const char *p) {
if (!p) { return NULL; }
return strdup(p);
}

bool str_null_or_empty(const char *p) {
if (p == NULL) { return true; }
return *p == '\0';
}
4 changes: 4 additions & 0 deletions src/app/util/nullable.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#pragma once

#include <stdbool.h>

void free_nullable(void *p);

char *strdup_nullable(const char *p);

bool str_null_or_empty(const char *p);

0 comments on commit 5b94568

Please sign in to comment.