From 193c056067ad46fd2f403735e854b219e52771e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Sun, 30 Jun 2019 12:23:42 +0200 Subject: [PATCH] Add ChiakiFeedbackSender --- gui/src/streamsession.cpp | 4 +- lib/CMakeLists.txt | 8 +- lib/include/chiaki/controller.h | 52 ++++++++++ lib/include/chiaki/feedback.h | 1 + lib/include/chiaki/feedbacksender.h | 54 ++++++++++ lib/include/chiaki/session.h | 4 + lib/include/chiaki/streamconnection.h | 15 ++- lib/src/controller.c | 26 +++++ lib/src/feedbacksender.c | 137 ++++++++++++++++++++++++++ lib/src/session.c | 46 ++++++--- lib/src/streamconnection.c | 37 +++++-- 11 files changed, 352 insertions(+), 32 deletions(-) create mode 100644 lib/include/chiaki/controller.h create mode 100644 lib/include/chiaki/feedbacksender.h create mode 100644 lib/src/controller.c create mode 100644 lib/src/feedbacksender.c diff --git a/gui/src/streamsession.cpp b/gui/src/streamsession.cpp index f0ad71e..34442f2 100644 --- a/gui/src/streamsession.cpp +++ b/gui/src/streamsession.cpp @@ -127,12 +127,12 @@ void StreamSession::SendFeedbackState() { if(!gamepad) return; - ChiakiFeedbackState state; + ChiakiControllerState state; state.left_x = static_cast(gamepad->axisLeftX() * 0x7fff); state.left_y = static_cast(gamepad->axisLeftX() * 0x7fff); state.right_x = static_cast(gamepad->axisLeftX() * 0x7fff); state.right_y = static_cast(gamepad->axisLeftX() * 0x7fff); - chiaki_stream_connection_send_feedback_state(&session.stream_connection, &state); + chiaki_session_set_controller_state(&session, &state); } void StreamSession::PushAudioFrame(int16_t *buf, size_t samples_count) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6821ea3..10dfcb6 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -27,7 +27,9 @@ set(HEADER_FILES include/chiaki/stoppipe.h include/chiaki/reorderqueue.h include/chiaki/discoveryservice.h - include/chiaki/feedback.h) + include/chiaki/feedback.h + include/chiaki/feedbacksender.h + include/chiaki/controller.h) set(SOURCE_FILES src/common.c @@ -57,7 +59,9 @@ set(SOURCE_FILES src/stoppipe.c src/reorderqueue.c src/discoveryservice.c - src/feedback.c) + src/feedback.c + src/feedbacksender.c + src/controller.c) add_subdirectory(protobuf) include_directories("${NANOPB_SOURCE_DIR}") diff --git a/lib/include/chiaki/controller.h b/lib/include/chiaki/controller.h new file mode 100644 index 0000000..d60f820 --- /dev/null +++ b/lib/include/chiaki/controller.h @@ -0,0 +1,52 @@ +/* + * This file is part of Chiaki. + * + * Chiaki 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 Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chiaki 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 Chiaki. If not, see . + */ + +#ifndef CHIAKI_CONTROLLER_H +#define CHIAKI_CONTROLLER_H + +#include +#include + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct chiaki_controller_state_t +{ + int16_t left_x; + int16_t left_y; + int16_t right_x; + int16_t right_y; +} ChiakiControllerState; + +CHIAKI_EXPORT void chiaki_controller_state_set_idle(ChiakiControllerState *state); + +static inline bool chiaki_controller_state_equals(ChiakiControllerState *a, ChiakiControllerState *b) +{ + return a->left_x == b->left_x + && a->left_y == b->left_y + && a->right_x == b->right_x + && a->right_y == b->right_y; +} + +#ifdef __cplusplus +} +#endif + +#endif // CHIAKI_CONTROLLER_H diff --git a/lib/include/chiaki/feedback.h b/lib/include/chiaki/feedback.h index 4fa2a37..e0f548a 100644 --- a/lib/include/chiaki/feedback.h +++ b/lib/include/chiaki/feedback.h @@ -19,6 +19,7 @@ #define CHIAKI_FEEDBACK_H #include "common.h" +#include "log.h" #include diff --git a/lib/include/chiaki/feedbacksender.h b/lib/include/chiaki/feedbacksender.h new file mode 100644 index 0000000..94d32e4 --- /dev/null +++ b/lib/include/chiaki/feedbacksender.h @@ -0,0 +1,54 @@ +/* + * This file is part of Chiaki. + * + * Chiaki 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 Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chiaki 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 Chiaki. If not, see . + */ + +#ifndef CHIAKI_FEEDBACKSENDER_H +#define CHIAKI_FEEDBACKSENDER_H + +#include "controller.h" +#include "takion.h" +#include "thread.h" +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct chiaki_feedback_sender_t +{ + ChiakiLog *log; + ChiakiTakion *takion; + ChiakiThread thread; + + ChiakiSeqNum16 state_seq_num; + + bool should_stop; + ChiakiControllerState controller_state_prev; + ChiakiControllerState controller_state; + bool controller_state_changed; + ChiakiMutex state_mutex; + ChiakiCond state_cond; +} ChiakiFeedbackSender; + +CHIAKI_EXPORT ChiakiErrorCode chiaki_feedback_sender_init(ChiakiFeedbackSender *feedback_sender, ChiakiTakion *takion); +CHIAKI_EXPORT void chiaki_feedback_sender_fini(ChiakiFeedbackSender *feedback_sender); +CHIAKI_EXPORT ChiakiErrorCode chiaki_feedback_sender_set_controller_state(ChiakiFeedbackSender *feedback_sender, ChiakiControllerState *state); + +#ifdef __cplusplus +} +#endif + +#endif // CHIAKI_FEEDBACKSENDER_H diff --git a/lib/include/chiaki/session.h b/lib/include/chiaki/session.h index 230f303..ece2683 100644 --- a/lib/include/chiaki/session.h +++ b/lib/include/chiaki/session.h @@ -29,6 +29,7 @@ #include "audio.h" #include "audioreceiver.h" #include "videoreceiver.h" +#include "controller.h" #include #include @@ -138,12 +139,15 @@ typedef struct chiaki_session_t ChiakiStreamConnection stream_connection; ChiakiAudioReceiver *audio_receiver; ChiakiVideoReceiver *video_receiver; + + ChiakiControllerState controller_state; } ChiakiSession; CHIAKI_EXPORT ChiakiErrorCode chiaki_session_init(ChiakiSession *session, ChiakiConnectInfo *connect_info); CHIAKI_EXPORT void chiaki_session_fini(ChiakiSession *session); CHIAKI_EXPORT ChiakiErrorCode chiaki_session_start(ChiakiSession *session); CHIAKI_EXPORT ChiakiErrorCode chiaki_session_join(ChiakiSession *session); +CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_controller_state(ChiakiSession *session, ChiakiControllerState *state); static inline void chiaki_session_set_event_cb(ChiakiSession *session, ChiakiEventCallback cb, void *user) { diff --git a/lib/include/chiaki/streamconnection.h b/lib/include/chiaki/streamconnection.h index 1042a02..fd6b409 100644 --- a/lib/include/chiaki/streamconnection.h +++ b/lib/include/chiaki/streamconnection.h @@ -18,7 +18,7 @@ #ifndef CHIAKI_STREAMCONNECTION_H #define CHIAKI_STREAMCONNECTION_H -#include "mirai.h" +#include "feedbacksender.h" #include "takion.h" #include "log.h" #include "ecdh.h" @@ -41,7 +41,16 @@ typedef struct chiaki_stream_connection_t ChiakiGKCrypt *gkcrypt_local; ChiakiGKCrypt *gkcrypt_remote; - ChiakiSeqNum16 feedback_state_seq_num; + ChiakiFeedbackSender feedback_sender; + /** + * whether feedback_sender is initialized + * only if this is true, feedback_sender may be accessed! + */ + bool feedback_sender_active; + /** + * protects feedback_sender and feedback_sender_active + */ + ChiakiMutex feedback_sender_mutex; /** * signaled on change of state_finished or should_stop @@ -72,8 +81,6 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_run(ChiakiStreamConnectio */ CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_stop(ChiakiStreamConnection *stream_connection); -CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_send_feedback_state(ChiakiStreamConnection *stream_connection, ChiakiFeedbackState *state); - #ifdef __cplusplus } #endif diff --git a/lib/src/controller.c b/lib/src/controller.c new file mode 100644 index 0000000..864af11 --- /dev/null +++ b/lib/src/controller.c @@ -0,0 +1,26 @@ +/* + * This file is part of Chiaki. + * + * Chiaki 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 Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chiaki 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 Chiaki. If not, see . + */ + +#include + +CHIAKI_EXPORT void chiaki_controller_state_set_idle(ChiakiControllerState *state) +{ + state->left_x = 0; + state->left_y = 0; + state->right_x = 0; + state->right_y = 0; +} diff --git a/lib/src/feedbacksender.c b/lib/src/feedbacksender.c new file mode 100644 index 0000000..cff68b6 --- /dev/null +++ b/lib/src/feedbacksender.c @@ -0,0 +1,137 @@ +/* + * This file is part of Chiaki. + * + * Chiaki 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 Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chiaki 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 Chiaki. If not, see . + */ + +#include + +#define FEEDBACK_STATE_TIMEOUT_MIN_MS 8 // minimum time to wait between sending 2 packets +#define FEEDBACK_STATE_TIMEOUT_MAX_MS 200 // maximum time to wait between sending 2 packets + +static void *feedback_sender_thread_func(void *user); + +CHIAKI_EXPORT ChiakiErrorCode chiaki_feedback_sender_init(ChiakiFeedbackSender *feedback_sender, ChiakiTakion *takion) +{ + feedback_sender->log = takion->log; + feedback_sender->takion = takion; + + chiaki_controller_state_set_idle(&feedback_sender->controller_state_prev); + chiaki_controller_state_set_idle(&feedback_sender->controller_state); + + feedback_sender->state_seq_num = 0; + + ChiakiErrorCode err = chiaki_mutex_init(&feedback_sender->state_mutex, false); + if(err != CHIAKI_ERR_SUCCESS) + return err; + + err = chiaki_cond_init(&feedback_sender->state_cond); + if(err != CHIAKI_ERR_SUCCESS) + goto error_mutex; + + err = chiaki_thread_create(&feedback_sender->thread, feedback_sender_thread_func, feedback_sender); + if(err != CHIAKI_ERR_SUCCESS) + goto error_cond; + + return CHIAKI_ERR_SUCCESS; +error_cond: + chiaki_cond_fini(&feedback_sender->state_cond); +error_mutex: + chiaki_mutex_fini(&feedback_sender->state_mutex); + return err; +} + +CHIAKI_EXPORT void chiaki_feedback_sender_fini(ChiakiFeedbackSender *feedback_sender) +{ + chiaki_mutex_lock(&feedback_sender->state_mutex); + feedback_sender->should_stop = true; + chiaki_mutex_unlock(&feedback_sender->state_mutex); + chiaki_cond_signal(&feedback_sender->state_cond); + chiaki_thread_join(&feedback_sender->thread, NULL); + chiaki_cond_fini(&feedback_sender->state_cond); + chiaki_mutex_fini(&feedback_sender->state_mutex); +} + +CHIAKI_EXPORT ChiakiErrorCode chiaki_feedback_sender_set_controller_state(ChiakiFeedbackSender *feedback_sender, ChiakiControllerState *state) +{ + ChiakiErrorCode err = chiaki_mutex_lock(&feedback_sender->state_mutex); + if(err != CHIAKI_ERR_SUCCESS) + return err; + + if(chiaki_controller_state_equals(&feedback_sender->controller_state, state)) + { + chiaki_mutex_unlock(&feedback_sender->state_mutex); + return CHIAKI_ERR_SUCCESS; + } + + feedback_sender->controller_state = *state; + feedback_sender->controller_state_changed = true; + + chiaki_mutex_unlock(&feedback_sender->state_mutex); + chiaki_cond_signal(&feedback_sender->state_cond); + + return CHIAKI_ERR_SUCCESS; +} + +static void feedback_sender_send_state(ChiakiFeedbackSender *feedback_sender) +{ + ChiakiFeedbackState state; + state.left_x = feedback_sender->controller_state.left_x; + state.left_y = feedback_sender->controller_state.left_y; + state.right_x = feedback_sender->controller_state.right_x; + state.right_y = feedback_sender->controller_state.right_y; + ChiakiErrorCode err = chiaki_takion_send_feedback_state(feedback_sender->takion, feedback_sender->state_seq_num++, &state); + if(err != CHIAKI_ERR_SUCCESS) + CHIAKI_LOGE(feedback_sender->log, "FeedbackSender failed to send Feedback State\n"); +} + +static bool state_cond_check(void *user) +{ + ChiakiFeedbackSender *feedback_sender = user; + return feedback_sender->should_stop || feedback_sender->controller_state_changed; +} + +static void *feedback_sender_thread_func(void *user) +{ + ChiakiFeedbackSender *feedback_sender = user; + + ChiakiErrorCode err = chiaki_mutex_lock(&feedback_sender->state_mutex); + if(err != CHIAKI_ERR_SUCCESS) + return NULL; + + uint64_t next_timeout = FEEDBACK_STATE_TIMEOUT_MAX_MS; + while(true) + { + err = chiaki_cond_timedwait_pred(&feedback_sender->state_cond, &feedback_sender->state_mutex, next_timeout, state_cond_check, feedback_sender); + if(err != CHIAKI_ERR_SUCCESS && err != CHIAKI_ERR_TIMEOUT) + break; + + if(feedback_sender->should_stop) + break; + + if(feedback_sender->controller_state_changed) + { + // TODO: FEEDBACK_STATE_TIMEOUT_MIN_MS + feedback_sender->controller_state_changed = false; + } + + feedback_sender_send_state(feedback_sender); + + feedback_sender->controller_state_prev = feedback_sender->controller_state; + } + + chiaki_mutex_unlock(&feedback_sender->state_mutex); + + return NULL; +} \ No newline at end of file diff --git a/lib/src/session.c b/lib/src/session.c index 9684c29..aeb09b3 100644 --- a/lib/src/session.c +++ b/lib/src/session.c @@ -51,15 +51,19 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_init(ChiakiSession *session, Chiaki session->quit_reason = CHIAKI_QUIT_REASON_NONE; - if(chiaki_cond_init(&session->ctrl_cond) != CHIAKI_ERR_SUCCESS) - { - return CHIAKI_ERR_UNKNOWN; - } + ChiakiErrorCode err = chiaki_cond_init(&session->ctrl_cond); + if(err != CHIAKI_ERR_SUCCESS) + goto error; - if(chiaki_mutex_init(&session->ctrl_cond_mutex, false) != CHIAKI_ERR_SUCCESS) + err = chiaki_mutex_init(&session->ctrl_cond_mutex, false); + if(err != CHIAKI_ERR_SUCCESS) + goto error_ctrl_cond; + + err = chiaki_stream_connection_init(&session->stream_connection, session); + if(err != CHIAKI_ERR_SUCCESS) { - chiaki_cond_fini(&session->ctrl_cond); - return CHIAKI_ERR_UNKNOWN; + CHIAKI_LOGE(&session->log, "StreamConnection init failed\n"); + goto error_ctrl_cond_mutex; } int r = getaddrinfo(connect_info->host, NULL, NULL, &session->connect_info.host_addrinfos); @@ -83,17 +87,26 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_init(ChiakiSession *session, Chiaki return CHIAKI_ERR_MEMORY; } + chiaki_controller_state_set_idle(&session->controller_state); + memcpy(session->connect_info.auth, connect_info->auth, sizeof(session->connect_info.auth)); memcpy(session->connect_info.morning, connect_info->morning, sizeof(session->connect_info.morning)); memcpy(session->connect_info.did, connect_info->did, sizeof(session->connect_info.did)); return CHIAKI_ERR_SUCCESS; +error_ctrl_cond_mutex: + chiaki_mutex_fini(&session->ctrl_cond_mutex); +error_ctrl_cond: + chiaki_cond_fini(&session->ctrl_cond); +error: + return err; } CHIAKI_EXPORT void chiaki_session_fini(ChiakiSession *session) { if(!session) return; + chiaki_stream_connection_fini(&session->stream_connection); chiaki_cond_fini(&session->ctrl_cond); chiaki_mutex_fini(&session->ctrl_cond_mutex); free(session->connect_info.regist_key); @@ -111,6 +124,18 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_join(ChiakiSession *session) return chiaki_thread_join(&session->session_thread, NULL); } +CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_controller_state(ChiakiSession *session, ChiakiControllerState *state) +{ + ChiakiErrorCode err = chiaki_mutex_lock(&session->stream_connection.feedback_sender_mutex); + if(err != CHIAKI_ERR_SUCCESS) + return err; + session->controller_state = *state; + if(session->stream_connection.feedback_sender_active) + chiaki_feedback_sender_set_controller_state(&session->stream_connection.feedback_sender, &session->controller_state); + chiaki_mutex_unlock(&session->stream_connection.feedback_sender_mutex); + return CHIAKI_ERR_SUCCESS; +} + static void session_send_event(ChiakiSession *session, ChiakiEvent *event) { if(!session->event_cb) @@ -199,13 +224,6 @@ static void *session_thread_func(void *arg) goto quit_audio_receiver; } - err = chiaki_stream_connection_init(&session->stream_connection, session); - if(err != CHIAKI_ERR_SUCCESS) - { - CHIAKI_LOGE(&session->log, "StreamConnection init failed\n"); - goto quit_video_receiver; - } - err = chiaki_stream_connection_run(&session->stream_connection); if(err != CHIAKI_ERR_SUCCESS) { diff --git a/lib/src/streamconnection.c b/lib/src/streamconnection.c index 1297b90..ec6aa6b 100644 --- a/lib/src/streamconnection.c +++ b/lib/src/streamconnection.c @@ -72,8 +72,6 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_init(ChiakiStreamConnecti stream_connection->gkcrypt_remote = NULL; stream_connection->gkcrypt_local = NULL; - stream_connection->feedback_state_seq_num = 0; - ChiakiErrorCode err = chiaki_mutex_init(&stream_connection->state_mutex, false); if(err != CHIAKI_ERR_SUCCESS) goto error; @@ -82,6 +80,10 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_init(ChiakiStreamConnecti if(err != CHIAKI_ERR_SUCCESS) goto error_state_mutex; + err = chiaki_mutex_init(&stream_connection->feedback_sender_mutex, false); + if(err != CHIAKI_ERR_SUCCESS) + goto error_state_cond; + stream_connection->state = STATE_IDLE; stream_connection->state_finished = false; stream_connection->state_failed = false; @@ -89,6 +91,8 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_init(ChiakiStreamConnecti return CHIAKI_ERR_SUCCESS; +error_state_cond: + chiaki_cond_fini(&stream_connection->state_cond); error_state_mutex: chiaki_mutex_fini(&stream_connection->state_mutex); error: @@ -102,6 +106,8 @@ CHIAKI_EXPORT void chiaki_stream_connection_fini(ChiakiStreamConnection *stream_ free(stream_connection->ecdh_secret); + chiaki_mutex_fini(&stream_connection->feedback_sender_mutex); + chiaki_cond_fini(&stream_connection->state_cond); chiaki_mutex_fini(&stream_connection->state_mutex); } @@ -200,6 +206,19 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_run(ChiakiStreamConnectio CHIAKI_LOGI(&session->log, "StreamConnection successfully received streaminfo\n"); + err = chiaki_mutex_lock(&stream_connection->feedback_sender_mutex); + assert(err == CHIAKI_ERR_SUCCESS); + err = chiaki_feedback_sender_init(&stream_connection->feedback_sender, &stream_connection->takion); + if(err != CHIAKI_ERR_SUCCESS) + { + chiaki_mutex_unlock(&stream_connection->feedback_sender_mutex); + CHIAKI_LOGE(stream_connection->log, "StreamConnection failed to start Feedback Sender\n"); + goto disconnect; + } + stream_connection->feedback_sender_active = true; + chiaki_feedback_sender_set_controller_state(&stream_connection->feedback_sender, &session->controller_state); + chiaki_mutex_unlock(&stream_connection->feedback_sender_mutex); + stream_connection->state = STATE_IDLE; stream_connection->state_finished = false; stream_connection->state_failed = false; @@ -216,6 +235,12 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_run(ChiakiStreamConnectio CHIAKI_LOGI(stream_connection->log, "StreamConnection sent heartbeat\n"); } + err = chiaki_mutex_lock(&stream_connection->feedback_sender_mutex); + assert(err == CHIAKI_ERR_SUCCESS); + stream_connection->feedback_sender_active = false; + chiaki_feedback_sender_fini(&stream_connection->feedback_sender); + chiaki_mutex_unlock(&stream_connection->feedback_sender_mutex); + err = CHIAKI_ERR_SUCCESS; disconnect: @@ -729,11 +754,3 @@ static ChiakiErrorCode stream_connection_send_heartbeat(ChiakiStreamConnection * return chiaki_takion_send_message_data(&stream_connection->takion, 1, 1, buf, stream.bytes_written); } - -CHIAKI_EXPORT ChiakiErrorCode chiaki_stream_connection_send_feedback_state(ChiakiStreamConnection *stream_connection, ChiakiFeedbackState *state) -{ - ChiakiErrorCode err = chiaki_takion_send_feedback_state(&stream_connection->takion, stream_connection->feedback_state_seq_num++, state); - if(err != CHIAKI_ERR_SUCCESS) - CHIAKI_LOGE(stream_connection->log, "StreamConnection failed to send feedback state\n"); - return err; -} \ No newline at end of file