From e0bef511b296cf4a9299155c9a628aef7168b8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Thu, 29 Nov 2018 17:01:32 +0100 Subject: [PATCH] Handle STREAMINFO --- lib/CMakeLists.txt | 5 +- lib/include/chiaki/audio.h | 47 ++++++++++++ lib/include/chiaki/mirai.h | 11 +-- lib/include/chiaki/nagare.h | 4 +- lib/src/audio.c | 39 ++++++++++ lib/src/mirai.c | 34 +++++---- lib/src/nagare.c | 144 +++++++++++++++++++++++++++++++----- lib/src/senkusha.c | 10 +-- 8 files changed, 249 insertions(+), 45 deletions(-) create mode 100644 lib/include/chiaki/audio.h create mode 100644 lib/src/audio.c diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 2c8a829..9c158f0 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -15,7 +15,8 @@ set(HEADER_FILES include/chiaki/ecdh.h include/chiaki/launchspec.h include/chiaki/random.h - include/chiaki/gkcrypt.h) + include/chiaki/gkcrypt.h + include/chiaki/audio.h) set(SOURCE_FILES src/common.c @@ -35,7 +36,7 @@ set(SOURCE_FILES src/ecdh.c src/launchspec.c src/random.c - src/gkcrypt.c) + src/gkcrypt.c src/audio.c) add_subdirectory(protobuf) include_directories("${NANOPB_SOURCE_DIR}") diff --git a/lib/include/chiaki/audio.h b/lib/include/chiaki/audio.h new file mode 100644 index 0000000..071f77a --- /dev/null +++ b/lib/include/chiaki/audio.h @@ -0,0 +1,47 @@ +/* + * 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_AUDIO_H +#define CHIAKI_AUDIO_H + +#include + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHIAKI_AUDIO_HEADER_SIZE 0xe + +typedef struct chiaki_audio_header_t +{ + uint8_t channels; + uint8_t bits; + uint32_t rate; + uint32_t frame_size; + uint32_t unknown; +} ChiakiAudioHeader; + +CHIAKI_EXPORT void chiaki_audio_header_load(ChiakiAudioHeader *audio_header, const uint8_t *buf); +CHIAKI_EXPORT void chiaki_audio_header_save(ChiakiAudioHeader *audio_header, uint8_t *buf); + +#ifdef __cplusplus +} +#endif + +#endif // CHIAKI_AUDIO_H diff --git a/lib/include/chiaki/mirai.h b/lib/include/chiaki/mirai.h index a0a03db..e4db2c2 100644 --- a/lib/include/chiaki/mirai.h +++ b/lib/include/chiaki/mirai.h @@ -28,17 +28,18 @@ extern "C" { typedef struct chiaki_mirai_t { - bool expected; - bool success; + int request; + int response; ChiakiMutex mutex; ChiakiCond cond; } ChiakiMirai; CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_init(ChiakiMirai *mirai); CHIAKI_EXPORT void chiaki_mirai_fini(ChiakiMirai *mirai); -CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_signal(ChiakiMirai *mirai, bool success); -CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_expect_begin(ChiakiMirai *mirai); -CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_expect_wait(ChiakiMirai *mirai, uint64_t timeout_ms); +CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_signal(ChiakiMirai *mirai, int response); +CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_request_begin(ChiakiMirai *mirai, int request, bool first); +CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_request_wait(ChiakiMirai *mirai, uint64_t timeout_ms, bool keep_locked); +CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_request_unlock(ChiakiMirai *mirai); #ifdef __cplusplus } diff --git a/lib/include/chiaki/nagare.h b/lib/include/chiaki/nagare.h index 55984d9..fb958d5 100644 --- a/lib/include/chiaki/nagare.h +++ b/lib/include/chiaki/nagare.h @@ -23,6 +23,7 @@ #include "log.h" #include "ecdh.h" #include "gkcrypt.h" +#include "audio.h" #include @@ -35,10 +36,11 @@ typedef struct chiaki_nagare_t struct chiaki_session_t *session; ChiakiLog *log; ChiakiTakion takion; - ChiakiMirai bang_mirai; + ChiakiMirai mirai; uint8_t *ecdh_secret; ChiakiGKCrypt *gkcrypt_a; ChiakiGKCrypt *gkcrypt_b; + ChiakiAudioHeader audio_header; } ChiakiNagare; CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(struct chiaki_session_t *session); diff --git a/lib/src/audio.c b/lib/src/audio.c new file mode 100644 index 0000000..e914082 --- /dev/null +++ b/lib/src/audio.c @@ -0,0 +1,39 @@ +/* + * 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 + +#include + + +void chiaki_audio_header_load(ChiakiAudioHeader *audio_header, const uint8_t *buf) +{ + audio_header->bits = buf[0]; + audio_header->channels = buf[1]; + audio_header->rate = ntohl(*((uint32_t *)(buf + 2))); + audio_header->frame_size = ntohl(*((uint32_t *)(buf + 6))); + audio_header->unknown = ntohl(*((uint32_t *)(buf + 0xa))); +} + +void chiaki_audio_header_save(ChiakiAudioHeader *audio_header, uint8_t *buf) +{ + buf[0] = audio_header->bits; + buf[1] = audio_header->channels; + *((uint32_t *)(buf + 2)) = htonl(audio_header->rate); + *((uint32_t *)(buf + 6)) = htonl(audio_header->frame_size); + *((uint32_t *)(buf + 0xa)) = htonl(audio_header->unknown); +} \ No newline at end of file diff --git a/lib/src/mirai.c b/lib/src/mirai.c index 153af61..4245bf9 100644 --- a/lib/src/mirai.c +++ b/lib/src/mirai.c @@ -19,8 +19,8 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_init(ChiakiMirai *mirai) { - mirai->expected = false; - mirai->success = false; + mirai->request = -1; + mirai->response = -1; ChiakiErrorCode err = chiaki_mutex_init(&mirai->mutex); if(err != CHIAKI_ERR_SUCCESS) return err; @@ -39,33 +39,39 @@ CHIAKI_EXPORT void chiaki_mirai_fini(ChiakiMirai *mirai) chiaki_cond_fini(&mirai->cond); } -CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_signal(ChiakiMirai *mirai, bool success) +CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_signal(ChiakiMirai *mirai, int response) { ChiakiErrorCode err = chiaki_mutex_lock(&mirai->mutex); if(err != CHIAKI_ERR_SUCCESS) return err; - mirai->success = success; + mirai->response = response; err = chiaki_cond_signal(&mirai->cond); if(err != CHIAKI_ERR_SUCCESS) return err; return chiaki_mutex_unlock(&mirai->mutex); } -CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_expect_begin(ChiakiMirai *mirai) +CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_request_begin(ChiakiMirai *mirai, int request, bool first) { - ChiakiErrorCode err = chiaki_mutex_lock(&mirai->mutex); - mirai->expected = true; + ChiakiErrorCode err = first ? chiaki_mutex_lock(&mirai->mutex) : CHIAKI_ERR_SUCCESS; + mirai->request = request; return err; } -CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_expect_wait(ChiakiMirai *mirai, uint64_t timeout_ms) +CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_request_wait(ChiakiMirai *mirai, uint64_t timeout_ms, bool keep_locked) { ChiakiErrorCode err = chiaki_cond_timedwait(&mirai->cond, &mirai->mutex, timeout_ms); - if(err != CHIAKI_ERR_SUCCESS && err != CHIAKI_ERR_TIMEOUT) - return err; - mirai->expected = false; - ChiakiErrorCode err2 = chiaki_mutex_unlock(&mirai->mutex); - if(err2 != CHIAKI_ERR_SUCCESS) - return err2; + mirai->request = -1; + if(!keep_locked) + { + ChiakiErrorCode err2 = chiaki_mutex_unlock(&mirai->mutex); + if(err2 != CHIAKI_ERR_SUCCESS) + return err2; + } return err; +} + +CHIAKI_EXPORT ChiakiErrorCode chiaki_mirai_request_unlock(ChiakiMirai *mirai) +{ + return chiaki_mutex_unlock(&mirai->mutex); } \ No newline at end of file diff --git a/lib/src/nagare.c b/lib/src/nagare.c index 48d9e8a..0bcb85a 100644 --- a/lib/src/nagare.c +++ b/lib/src/nagare.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -36,13 +37,27 @@ #define NAGARE_PORT 9296 -#define BIG_TIMEOUT_MS 5000 +#define EXPECT_TIMEOUT_MS 5000 + + +typedef enum { + NAGARE_MIRAI_REQUEST_BANG = 0, + NAGARE_MIRAI_REQUEST_STREAMINFO, +} NagareMiraiRequest; + +typedef enum { + NAGARE_MIRAI_RESPONSE_FAIL = 0, + NAGARE_MIRAI_RESPONSE_SUCCESS = 1 +} NagareMiraiResponse; + static void nagare_takion_data(uint8_t *buf, size_t buf_size, void *user); static ChiakiErrorCode nagare_send_big(ChiakiNagare *nagare); static ChiakiErrorCode nagare_send_disconnect(ChiakiNagare *nagare); static void nagare_takion_data_expect_bang(ChiakiNagare *nagare, uint8_t *buf, size_t buf_size); +static void nagare_takion_data_expect_streaminfo(ChiakiNagare *nagare, uint8_t *buf, size_t buf_size); +static ChiakiErrorCode nagare_send_streaminfo_ack(ChiakiNagare *nagare); CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(ChiakiSession *session) @@ -53,9 +68,9 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(ChiakiSession *session) nagare->ecdh_secret = NULL; - ChiakiErrorCode err = chiaki_mirai_init(&nagare->bang_mirai); + ChiakiErrorCode err = chiaki_mirai_init(&nagare->mirai); if(err != CHIAKI_ERR_SUCCESS) - goto error_bang_mirai; + goto error_mirai; ChiakiTakionConnectInfo takion_info; takion_info.log = nagare->log; @@ -64,7 +79,7 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(ChiakiSession *session) if(!takion_info.sa) { err = CHIAKI_ERR_MEMORY; - goto error_bang_mirai; + goto error_mirai; } memcpy(takion_info.sa, session->connect_info.host_addrinfo_selected->ai_addr, takion_info.sa_len); err = set_port(takion_info.sa, htons(NAGARE_PORT)); @@ -78,12 +93,12 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(ChiakiSession *session) if(err != CHIAKI_ERR_SUCCESS) { CHIAKI_LOGE(&session->log, "Nagare connect failed\n"); - goto error_bang_mirai; + goto error_mirai; } CHIAKI_LOGI(&session->log, "Nagare sending big\n"); - err = chiaki_mirai_expect_begin(&nagare->bang_mirai); + err = chiaki_mirai_request_begin(&nagare->mirai, NAGARE_MIRAI_REQUEST_BANG, true); assert(err == CHIAKI_ERR_SUCCESS); err = nagare_send_big(nagare); @@ -93,14 +108,16 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(ChiakiSession *session) goto error_takion; } - err = chiaki_mirai_expect_wait(&nagare->bang_mirai, BIG_TIMEOUT_MS); + err = chiaki_mirai_request_wait(&nagare->mirai, EXPECT_TIMEOUT_MS, true); assert(err == CHIAKI_ERR_SUCCESS || err == CHIAKI_ERR_TIMEOUT); - if(!nagare->bang_mirai.success) + if(nagare->mirai.response != NAGARE_MIRAI_RESPONSE_SUCCESS) { if(err == CHIAKI_ERR_TIMEOUT) CHIAKI_LOGE(&session->log, "Nagare bang receive timeout\n"); + chiaki_mirai_request_unlock(&nagare->mirai); + CHIAKI_LOGE(&session->log, "Nagare didn't receive bang\n"); err = CHIAKI_ERR_UNKNOWN; goto error_takion; @@ -122,24 +139,43 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_nagare_run(ChiakiSession *session) goto error_gkcrypt_a; } + err = chiaki_mirai_request_begin(&nagare->mirai, NAGARE_MIRAI_REQUEST_STREAMINFO, false); + assert(err == CHIAKI_ERR_SUCCESS); + err = chiaki_mirai_request_wait(&nagare->mirai, EXPECT_TIMEOUT_MS, false); + assert(err == CHIAKI_ERR_SUCCESS || err == CHIAKI_ERR_TIMEOUT); + + if(nagare->mirai.response != NAGARE_MIRAI_RESPONSE_SUCCESS) + { + if(err == CHIAKI_ERR_TIMEOUT) + CHIAKI_LOGE(&session->log, "Nagare streaminfo receive timeout\n"); + + chiaki_mirai_request_unlock(&nagare->mirai); + + CHIAKI_LOGE(&session->log, "Nagare didn't receive streaminfo\n"); + err = CHIAKI_ERR_UNKNOWN; + goto error_takion; + } + + CHIAKI_LOGI(&session->log, "Nagare successfully received streaminfo\n"); while(1) sleep(1); - CHIAKI_LOGI(&session->log, "Nagare is disconnecting\n"); nagare_send_disconnect(nagare); err = CHIAKI_ERR_SUCCESS; + + // TODO: can't roll everything back like this, takion has to be closed first always. chiaki_gkcrypt_free(nagare->gkcrypt_b); error_gkcrypt_a: chiaki_gkcrypt_free(nagare->gkcrypt_a); error_takion: chiaki_takion_close(&nagare->takion); CHIAKI_LOGI(&session->log, "Nagare closed takion\n"); -error_bang_mirai: - chiaki_mirai_fini(&nagare->bang_mirai); +error_mirai: + chiaki_mirai_fini(&nagare->mirai); free(nagare->ecdh_secret); return err; @@ -152,10 +188,16 @@ static void nagare_takion_data(uint8_t *buf, size_t buf_size, void *user) { ChiakiNagare *nagare = user; - if(nagare->bang_mirai.expected) + switch(nagare->mirai.request) { - nagare_takion_data_expect_bang(nagare, buf, buf_size); - return; + case NAGARE_MIRAI_REQUEST_BANG: + nagare_takion_data_expect_bang(nagare, buf, buf_size); + return; + case NAGARE_MIRAI_REQUEST_STREAMINFO: + nagare_takion_data_expect_streaminfo(nagare, buf, buf_size); + return; + default: + break; } tkproto_TakionMessage msg; @@ -173,9 +215,9 @@ static void nagare_takion_data(uint8_t *buf, size_t buf_size, void *user) static void nagare_takion_data_expect_bang(ChiakiNagare *nagare, uint8_t *buf, size_t buf_size) { char ecdh_pub_key[128]; - ChiakiPBDecodeBuf ecdh_pub_key_buf = { sizeof(ecdh_pub_key), 0, ecdh_pub_key }; + ChiakiPBDecodeBuf ecdh_pub_key_buf = { sizeof(ecdh_pub_key), 0, (uint8_t *)ecdh_pub_key }; char ecdh_sig[32]; - ChiakiPBDecodeBuf ecdh_sig_buf = { sizeof(ecdh_sig), 0, ecdh_sig }; + ChiakiPBDecodeBuf ecdh_sig_buf = { sizeof(ecdh_sig), 0, (uint8_t *)ecdh_sig }; tkproto_TakionMessage msg; memset(&msg, 0, sizeof(msg)); @@ -245,10 +287,55 @@ static void nagare_takion_data_expect_bang(ChiakiNagare *nagare, uint8_t *buf, s goto error; } - chiaki_mirai_signal(&nagare->bang_mirai, true); + chiaki_mirai_signal(&nagare->mirai, NAGARE_MIRAI_RESPONSE_SUCCESS); return; error: - chiaki_mirai_signal(&nagare->bang_mirai, false); + chiaki_mirai_signal(&nagare->mirai, NAGARE_MIRAI_RESPONSE_FAIL); +} + + +static void nagare_takion_data_expect_streaminfo(ChiakiNagare *nagare, uint8_t *buf, size_t buf_size) +{ + tkproto_TakionMessage msg; + memset(&msg, 0, sizeof(msg)); + + uint8_t audio_header[CHIAKI_AUDIO_HEADER_SIZE]; + ChiakiPBDecodeBuf audio_header_buf = { sizeof(audio_header), 0, (uint8_t *)audio_header }; + msg.stream_info_payload.audio_header.arg = &audio_header_buf; + msg.stream_info_payload.audio_header.funcs.decode = chiaki_pb_decode_buf; + + // TODO: msg.stream_info_payload.resolution + + pb_istream_t stream = pb_istream_from_buffer(buf, buf_size); + bool r = pb_decode(&stream, tkproto_TakionMessage_fields, &msg); + if(!r) + { + CHIAKI_LOGE(nagare->log, "Nagare failed to decode data protobuf\n"); + return; + } + + if(msg.type != tkproto_TakionMessage_PayloadType_STREAMINFO || !msg.has_stream_info_payload) + { + CHIAKI_LOGE(nagare->log, "Nagare expected streaminfo payload but received something else\n"); + return; + } + + if(audio_header_buf.size != CHIAKI_AUDIO_HEADER_SIZE) + { + CHIAKI_LOGE(nagare->log, "Nagare receoved invalid audio header in streaminfo\n"); + goto error; + } + + chiaki_audio_header_load(&nagare->audio_header, audio_header); + + // TODO: do some checks? + + nagare_send_streaminfo_ack(nagare); + + chiaki_mirai_signal(&nagare->mirai, NAGARE_MIRAI_RESPONSE_SUCCESS); + return; +error: + chiaki_mirai_signal(&nagare->mirai, NAGARE_MIRAI_RESPONSE_FAIL); } @@ -352,6 +439,27 @@ static ChiakiErrorCode nagare_send_big(ChiakiNagare *nagare) return err; } +static ChiakiErrorCode nagare_send_streaminfo_ack(ChiakiNagare *nagare) +{ + tkproto_TakionMessage msg; + memset(&msg, 0, sizeof(msg)); + msg.type = tkproto_TakionMessage_PayloadType_STREAMINFOACK; + + uint8_t buf[3]; + size_t buf_size; + + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + bool pbr = pb_encode(&stream, tkproto_TakionMessage_fields, &msg); + if(!pbr) + { + CHIAKI_LOGE(nagare->log, "Nagare streaminfo ack protobuf encoding failed\n"); + return CHIAKI_ERR_UNKNOWN; + } + + buf_size = stream.bytes_written; + return chiaki_takion_send_message_data(&nagare->takion, 0, 1, 9, buf, buf_size); +} + static ChiakiErrorCode nagare_send_disconnect(ChiakiNagare *nagare) { tkproto_TakionMessage msg; diff --git a/lib/src/senkusha.c b/lib/src/senkusha.c index 71a48f7..20ae0fa 100644 --- a/lib/src/senkusha.c +++ b/lib/src/senkusha.c @@ -83,7 +83,7 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_senkusha_run(ChiakiSession *session) CHIAKI_LOGI(&session->log, "Senkusha sending big\n"); - err = chiaki_mirai_expect_begin(&senkusha.bang_mirai); + err = chiaki_mirai_request_begin(&senkusha.bang_mirai, 1, true); assert(err == CHIAKI_ERR_SUCCESS); err = senkusha_send_big(&senkusha); @@ -93,10 +93,10 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_senkusha_run(ChiakiSession *session) goto error_takion; } - err = chiaki_mirai_expect_wait(&senkusha.bang_mirai, BIG_TIMEOUT_MS); + err = chiaki_mirai_request_wait(&senkusha.bang_mirai, BIG_TIMEOUT_MS, false); assert(err == CHIAKI_ERR_SUCCESS || err == CHIAKI_ERR_TIMEOUT); - if(!senkusha.bang_mirai.success) + if(!senkusha.bang_mirai.response) { if(err == CHIAKI_ERR_TIMEOUT) CHIAKI_LOGE(&session->log, "Senkusha bang receive timeout\n"); @@ -136,14 +136,14 @@ static void senkusha_takion_data(uint8_t *buf, size_t buf_size, void *user) return; } - if(senkusha->bang_mirai.expected) + if(senkusha->bang_mirai.request) { if(msg.type != tkproto_TakionMessage_PayloadType_BANG || !msg.has_bang_payload) { CHIAKI_LOGE(senkusha->log, "Senkusha expected bang payload but received something else\n"); return; } - chiaki_mirai_signal(&senkusha->bang_mirai, true); + chiaki_mirai_signal(&senkusha->bang_mirai, 1); } }