diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 668268c..19bae14 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -18,6 +18,7 @@ set(HEADER_FILES include/chiaki/gkcrypt.h include/chiaki/audio.h include/chiaki/audioreceiver.h + include/chiaki/video.h include/chiaki/videoreceiver.h) set(SOURCE_FILES diff --git a/lib/include/chiaki/video.h b/lib/include/chiaki/video.h new file mode 100644 index 0000000..76a4f61 --- /dev/null +++ b/lib/include/chiaki/video.h @@ -0,0 +1,40 @@ +/* + * 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_VIDEO_H +#define CHIAKI_VIDEO_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct chiaki_video_profile_t +{ + unsigned int width; + unsigned int height; + size_t header_sz; + uint8_t *header; +} ChiakiVideoProfile; + +#ifdef __cplusplus +} +#endif + +#endif // CHIAKI_VIDEO_H diff --git a/lib/include/chiaki/videoreceiver.h b/lib/include/chiaki/videoreceiver.h index 3b2c23a..1d94ffa 100644 --- a/lib/include/chiaki/videoreceiver.h +++ b/lib/include/chiaki/videoreceiver.h @@ -20,21 +20,34 @@ #include "common.h" #include "log.h" +#include "video.h" #ifdef __cplusplus extern "C" { #endif +#define CHIAKI_VIDEO_PROFILES_MAX 8 + typedef struct chiaki_video_receiver_t { struct chiaki_session_t *session; ChiakiLog *log; - + ChiakiVideoProfile profiles[CHIAKI_VIDEO_PROFILES_MAX]; + size_t profiles_count; } ChiakiVideoReceiver; CHIAKI_EXPORT void chiaki_video_receiver_init(ChiakiVideoReceiver *video_receiver, struct chiaki_session_t *session); CHIAKI_EXPORT void chiaki_video_receiver_fini(ChiakiVideoReceiver *video_receiver); +/** + * Called after receiving the Stream Info Packet. + * + * @param video_receiver + * @param profiles Array of profiles. Ownership of the contained header buffers will be transferred to the ChiakiVideoReceiver! + * @param profiles_count must be <= CHIAKI_VIDEO_PROFILES_MAX + */ +CHIAKI_EXPORT void chiaki_video_receiver_stream_info(ChiakiVideoReceiver *video_receiver, ChiakiVideoProfile *profiles, size_t profiles_count); + static inline ChiakiVideoReceiver *chiaki_video_receiver_new(struct chiaki_session_t *session) { ChiakiVideoReceiver *video_receiver = CHIAKI_NEW(ChiakiVideoReceiver); diff --git a/lib/src/nagare.c b/lib/src/nagare.c index 479213d..64b6eb9 100644 --- a/lib/src/nagare.c +++ b/lib/src/nagare.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -305,6 +306,44 @@ error: chiaki_mirai_signal(&nagare->mirai, NAGARE_MIRAI_RESPONSE_FAIL); } +typedef struct decode_resolutions_context_t +{ + ChiakiNagare *nagare; + ChiakiVideoProfile video_profiles[CHIAKI_VIDEO_PROFILES_MAX]; + size_t video_profiles_count; +} DecodeResolutionsContext; + +static bool pb_decode_resolution(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + DecodeResolutionsContext *ctx = *arg; + + tkproto_ResolutionPayload resolution = { 0 }; + ChiakiPBDecodeBufAlloc header_buf = { 0 }; + resolution.video_header.arg = &header_buf; + resolution.video_header.funcs.decode = chiaki_pb_decode_buf_alloc; + if(!pb_decode(stream, tkproto_ResolutionPayload_fields, &resolution)) + return false; + + if(!header_buf.buf) + { + CHIAKI_LOGE(&ctx->nagare->session->log, "Failed to decode video header\n"); + return true; + } + + if(ctx->video_profiles_count >= CHIAKI_VIDEO_PROFILES_MAX) + { + CHIAKI_LOGE(&ctx->nagare->session->log, "Received more resolutions than the maximum\n"); + return true; + } + + ChiakiVideoProfile *profile = &ctx->video_profiles[ctx->video_profiles_count++]; + profile->width = resolution.width; + profile->height = resolution.height; + profile->header_sz = header_buf.size; + profile->header = header_buf.buf; + return true; +} + static void nagare_takion_data_expect_streaminfo(ChiakiNagare *nagare, uint8_t *buf, size_t buf_size) { @@ -316,7 +355,12 @@ static void nagare_takion_data_expect_streaminfo(ChiakiNagare *nagare, uint8_t * 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 + DecodeResolutionsContext decode_resolutions_context; + decode_resolutions_context.nagare = nagare; + memset(decode_resolutions_context.video_profiles, 0, sizeof(decode_resolutions_context.video_profiles)); + decode_resolutions_context.video_profiles_count = 0; + msg.stream_info_payload.resolution.arg = &decode_resolutions_context; + msg.stream_info_payload.resolution.funcs.decode = pb_decode_resolution; pb_istream_t stream = pb_istream_from_buffer(buf, buf_size); bool r = pb_decode(&stream, tkproto_TakionMessage_fields, &msg); @@ -342,6 +386,10 @@ static void nagare_takion_data_expect_streaminfo(ChiakiNagare *nagare, uint8_t * chiaki_audio_header_load(&audio_header_s, audio_header); chiaki_audio_receiver_stream_info(nagare->session->audio_receiver, &audio_header_s); + chiaki_video_receiver_stream_info(nagare->session->video_receiver, + decode_resolutions_context.video_profiles, + decode_resolutions_context.video_profiles_count); + // TODO: do some checks? nagare_send_streaminfo_ack(nagare); diff --git a/lib/src/pb_utils.h b/lib/src/pb_utils.h index db9bf8c..4183945 100644 --- a/lib/src/pb_utils.h +++ b/lib/src/pb_utils.h @@ -72,4 +72,23 @@ static bool chiaki_pb_decode_buf(pb_istream_t *stream, const pb_field_t *field, } +typedef struct chiaki_pb_decode_buf_alloc_t +{ + size_t size; + uint8_t *buf; +} ChiakiPBDecodeBufAlloc; + +static bool chiaki_pb_decode_buf_alloc(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + ChiakiPBDecodeBufAlloc *buf = *arg; + buf->size = stream->bytes_left; + buf->buf = malloc(buf->size); + if(!buf->buf) + return false; + bool r = pb_read(stream, buf->buf, buf->size); + if(!r) + buf->size = 0; + return r; +} + #endif // CHIAKI_PB_UTILS_H diff --git a/lib/src/videoreceiver.c b/lib/src/videoreceiver.c index eb2f4af..0e675b8 100644 --- a/lib/src/videoreceiver.c +++ b/lib/src/videoreceiver.c @@ -18,13 +18,38 @@ #include #include +#include CHIAKI_EXPORT void chiaki_video_receiver_init(ChiakiVideoReceiver *video_receiver, struct chiaki_session_t *session) { video_receiver->session = session; video_receiver->log = &session->log; + memset(video_receiver->profiles, 0, sizeof(video_receiver->profiles)); + video_receiver->profiles_count = 0; } CHIAKI_EXPORT void chiaki_video_receiver_fini(ChiakiVideoReceiver *video_receiver) { + for(size_t i=0; iprofiles_count; i++) + free(video_receiver->profiles[i].header); +} + +CHIAKI_EXPORT void chiaki_video_receiver_stream_info(ChiakiVideoReceiver *video_receiver, ChiakiVideoProfile *profiles, size_t profiles_count) +{ + if(video_receiver->profiles_count > 0) + { + CHIAKI_LOGE(video_receiver->log, "Video Receiver profiles already set\n"); + return; + } + + memcpy(video_receiver->profiles, profiles, profiles_count * sizeof(ChiakiVideoProfile)); + video_receiver->profiles_count = profiles_count; + + CHIAKI_LOGI(video_receiver->log, "Video Profiles:\n"); + for(size_t i=0; iprofiles_count; i++) + { + ChiakiVideoProfile *profile = &video_receiver->profiles[i]; + CHIAKI_LOGI(video_receiver->log, " %zu: %ux%u\n", i, profile->width, profile->height); + chiaki_log_hexdump(video_receiver->log, CHIAKI_LOG_DEBUG, profile->header, profile->header_sz); + } } \ No newline at end of file