diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7f31c0a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +components/third_party/* linguist-vendored +components/livekit/protocol/*.c linguist-generated +components/livekit/protocol/*.h linguist-generated \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..7df7214 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: LiveKit Community Slack + url: https://livekit.io/join-slack + about: Get your questions answered \ No newline at end of file diff --git a/.github/banner_dark.png b/.github/banner_dark.png new file mode 100644 index 0000000..854b8c2 Binary files /dev/null and b/.github/banner_dark.png differ diff --git a/.github/banner_light.png b/.github/banner_light.png new file mode 100644 index 0000000..970bce5 Binary files /dev/null and b/.github/banner_light.png differ diff --git a/.github/workflows/build_examples.yml b/.github/workflows/build_examples.yml new file mode 100644 index 0000000..0969674 --- /dev/null +++ b/.github/workflows/build_examples.yml @@ -0,0 +1,24 @@ +name: Build Examples +on: + workflow_call: {} + workflow_dispatch: {} +jobs: + build-examples: + name: Build Examples + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: [esp32s3] + idf_version: [release-v5.4, release-v5.5] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: { submodules: recursive } + - name: Build All + uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: ${{ matrix.idf_version }} + command: | + pip install idf-build-apps + idf-build-apps build -p ./examples --recursive --target ${{ matrix.target }} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d865d82 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,16 @@ +name: CI +on: + schedule: + - cron: 0 0 * * 1 + workflow_dispatch: + pull_request: + types: [opened, reopened, synchronize] + push: + branches: [main] +concurrency: + group: "ci" + cancel-in-progress: true +jobs: + build-examples: + name: Build Examples + uses: ./.github/workflows/build_examples.yml \ No newline at end of file diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..02766e7 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,36 @@ +name: Build Documentation +on: + workflow_dispatch: + push: + branches: [main] +concurrency: + group: "docs" + cancel-in-progress: true +jobs: + build-docs: + name: Build Documentation + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Doxygen + uses: mattnotmitt/doxygen-action@v1.12.0 + with: { working-directory: docs } + - name: Configure Pages + uses: actions/configure-pages@v5 + - name: Upload Generated Docs + uses: actions/upload-pages-artifact@v3 + with: { path: docs/output } + deploy: + needs: build-docs + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..88d663e --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +.config +*.o +*.pyc + +# gtags +GTAGS +GRTAGS +GPATH + +# emacs +.dir-locals.el + +# emacs temp file suffixes +*~ +.#* +\#*# +# MacOS directory files +.DS_Store + +# eclipse setting +.settings + +# Example project files + +**/sdkconfig +**/sdkconfig.old +**/build +**/managed_components +**/dependencies.lock +**/dist + +# gcov coverage reports +*.gcda +*.gcno +coverage.info +coverage_report/ + +.vscode + +# Doxygen +docs/output \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f24169c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "components/third_party/esp-webrtc-solution"] + path = components/third_party/esp-webrtc-solution + url = https://github.com/espressif/esp-webrtc-solution diff --git a/README.md b/README.md new file mode 100644 index 0000000..86f5f5b --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ + + + + + The LiveKit icon, the name of the repository and some sample code in the background. + + + +# ESP-32 SDK for LiveKit + +Use this SDK to add realtime video, audio and data features to your ESP-32 projects. By connecting to [LiveKit](https://livekit.io/) Cloud or a self-hosted server, you can quickly build applications such as multi-modal AI, live streaming, or video calls with minimal setup. + +> [!WARNING] +> This SDK is currently in Developer Preview mode and not ready for production use. +> There will be bugs and APIs may change during this period. + +## Features + +- **Supported chipsets**: ESP32-S3 and ESP32-P4 +- **Bidirectional audio**: Opus encoding, acoustic echo cancellation (AEC) +- **Bidirectional video**: *coming soon* +- **Real-time data**: data packets, remote method calls (RPC) + +## Installation + +In your application's IDF component manifest, add LiveKit as a Git dependency: + +```yaml +dependencies: + livekit: + git: https://github.com/livekit/client-sdk-esp32.git + path: components/livekit + version: +``` + +Please be sure to pin to a specific version tag as subsequent 0.x.x releases may have breaking changes. In the future, this SDK will be added to the [ESP component registry](https://components.espressif.com). + +## Examples + +One of the best ways to get started with LiveKit is by reviewing the examples and choosing one as a starting point for your project: + +- [Voice Agent](./examples/voice_agent/README.md): conversational AI voice agent that interacts with hardware based on user requests. +- *More examples coming soon* + +## Documentation + +Please refer to the [LiveKit Docs](https://docs.livekit.io/home/) for an introduction to the platform and its features, or to the API reference (*TODO: Not published yet*) for specifics about this SDK. + +## Known Issues + +- In some cases, a remote participant leaving the room can lead to lead to a disconnect. + +## Getting Help & Contributing + +We invite you to join the [LiveKit Community Slack](https://livekit.io/join-slack) to get your questions answered, suggest improvements, or discuss how you can best contribute to this SDK. + + +
+ + + + + + + + + +
LiveKit Ecosystem
LiveKit SDKsBrowser · iOS/macOS/visionOS · Android · Flutter · React Native · Rust · Node.js · Python · Unity · Unity (WebGL) · ESP-32
Server APIsNode.js · Golang · Ruby · Java/Kotlin · Python · Rust · PHP (community) · .NET (community)
UI ComponentsReact · Android Compose · SwiftUI
Agents FrameworksPython · Node.js · Playground
ServicesLiveKit server · Egress · Ingress · SIP
ResourcesDocs · Example apps · Cloud · Self-hosting · CLI
+ \ No newline at end of file diff --git a/components/livekit/CMakeLists.txt b/components/livekit/CMakeLists.txt new file mode 100644 index 0000000..66cf763 --- /dev/null +++ b/components/livekit/CMakeLists.txt @@ -0,0 +1,24 @@ +idf_component_register( + SRC_DIRS ./core ./protocol + PRIV_INCLUDE_DIRS ./core ./protocol + INCLUDE_DIRS ./include + REQUIRES + av_render + esp_capture + PRIV_REQUIRES + esp_codec_dev + esp_netif + esp_timer + esp_websocket_client + esp_webrtc + json + mbedtls + media_lib_sal + peer_default + webrtc_utils + nanopb + khash +) + +idf_component_get_property(LIVEKIT_SDK_VERSION ${COMPONENT_NAME} COMPONENT_VERSION) +target_compile_definitions(${COMPONENT_LIB} PUBLIC "LIVEKIT_SDK_VERSION=\"${LIVEKIT_SDK_VERSION}\"") \ No newline at end of file diff --git a/components/livekit/core/common.h b/components/livekit/core/common.h new file mode 100644 index 0000000..2d65356 --- /dev/null +++ b/components/livekit/core/common.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "esp_peer.h" +#include "esp_capture.h" +#include "av_render.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// State of an engine component or the engine itself. +typedef enum { + CONNECTION_STATE_DISCONNECTED = 0, /*!< Disconnected */ + CONNECTION_STATE_CONNECTING = 1, /*!< Establishing connection */ + CONNECTION_STATE_CONNECTED = 2, /*!< Connected */ + CONNECTION_STATE_RECONNECTING = 3, /*!< Connection was previously established, but was lost */ + CONNECTION_STATE_FAILED = 4 /*!< Connection failed */ +} connection_state_t; + +typedef struct { + esp_peer_media_dir_t audio_dir; + esp_peer_media_dir_t video_dir; + + esp_peer_audio_stream_info_t audio_info; + esp_peer_video_stream_info_t video_info; + + esp_capture_handle_t capturer; + av_render_handle_t renderer; +} engine_media_options_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/livekit/core/engine.c b/components/livekit/core/engine.c new file mode 100644 index 0000000..54d513e --- /dev/null +++ b/components/livekit/core/engine.c @@ -0,0 +1,709 @@ +#include "esp_log.h" +#include "media_lib_os.h" +#include "esp_codec_dev.h" + +#include "protocol.h" +#include "engine.h" +#include "signaling.h" +#include "peer.h" + +static const char *TAG = "livekit_engine"; + +#define VIDEO_TRACK_CID "video0" +#define AUDIO_TRACK_CID "audio0" + +#define VIDEO_TRACK_NAME "Video" +#define AUDIO_TRACK_NAME "Audio" +#define FRAME_INTERVAL_MS 20 + +#define SAFE_FREE(ptr) if (ptr != NULL) { \ + free(ptr); \ + ptr = NULL; \ +} + +typedef struct { + engine_options_t options; + signal_handle_t sig; + + peer_handle_t pub_peer; + peer_handle_t sub_peer; + + esp_peer_ice_server_cfg_t *ice_servers; + int ice_server_count; + + esp_capture_path_handle_t capturer_path; + bool is_media_streaming; + + esp_codec_dev_handle_t renderer_handle; + esp_peer_audio_stream_info_t sub_audio_info; + + connection_state_t state; /// Engine state, derived from signaling and peer states + connection_state_t sig_state; /// Signaling state + connection_state_t pub_state; /// Publisher peer state + connection_state_t sub_state; /// Subscriber peer state + + bool is_subscriber_primary; + char local_participant_sid[32]; + char sub_audio_track_sid[32]; +} engine_t; + +static void recalculate_state(engine_t *eng) +{ + connection_state_t new_state = eng->state; + + if (eng->sig_state == CONNECTION_STATE_FAILED || + eng->pub_state == CONNECTION_STATE_FAILED || + eng->sub_state == CONNECTION_STATE_FAILED) + { + new_state = CONNECTION_STATE_FAILED; + } + else if (eng->sig_state == CONNECTION_STATE_RECONNECTING || + eng->pub_state == CONNECTION_STATE_RECONNECTING || + eng->sub_state == CONNECTION_STATE_RECONNECTING) + { + new_state = CONNECTION_STATE_RECONNECTING; + } + + else if (eng->sig_state == CONNECTION_STATE_CONNECTED && + ( (eng->is_subscriber_primary && eng->sub_state == CONNECTION_STATE_CONNECTED) || + (!eng->is_subscriber_primary && eng->pub_state == CONNECTION_STATE_CONNECTED) )) + { + new_state = CONNECTION_STATE_CONNECTED; + } + else if (eng->sig_state == CONNECTION_STATE_DISCONNECTED && + eng->pub_state == CONNECTION_STATE_DISCONNECTED && + eng->sub_state == CONNECTION_STATE_DISCONNECTED) + { + new_state = CONNECTION_STATE_DISCONNECTED; + } + else { + new_state = CONNECTION_STATE_CONNECTING; + } + + if (new_state != eng->state) { + ESP_LOGI(TAG, "State changed: %d -> %d", eng->state, new_state); + eng->state = new_state; + eng->options.on_state_changed(eng->state, eng->options.ctx); + } +} + +static esp_capture_codec_type_t capture_audio_codec_type(esp_peer_audio_codec_t peer_codec) +{ + switch (peer_codec) { + case ESP_PEER_AUDIO_CODEC_G711A: return ESP_CAPTURE_CODEC_TYPE_G711A; + case ESP_PEER_AUDIO_CODEC_G711U: return ESP_CAPTURE_CODEC_TYPE_G711U; + case ESP_PEER_AUDIO_CODEC_OPUS: return ESP_CAPTURE_CODEC_TYPE_OPUS; + default: return ESP_CAPTURE_CODEC_TYPE_NONE; + } +} + +static esp_capture_codec_type_t capture_video_codec_type(esp_peer_video_codec_t peer_codec) +{ + switch (peer_codec) { + case ESP_PEER_VIDEO_CODEC_H264: return ESP_CAPTURE_CODEC_TYPE_H264; + case ESP_PEER_VIDEO_CODEC_MJPEG: return ESP_CAPTURE_CODEC_TYPE_MJPEG; + default: return ESP_CAPTURE_CODEC_TYPE_NONE; + } +} + +static av_render_audio_codec_t get_dec_codec(esp_peer_audio_codec_t codec) +{ + switch (codec) { + case ESP_PEER_AUDIO_CODEC_G711A: return AV_RENDER_AUDIO_CODEC_G711A; + case ESP_PEER_AUDIO_CODEC_G711U: return AV_RENDER_AUDIO_CODEC_G711U; + case ESP_PEER_AUDIO_CODEC_OPUS: return AV_RENDER_AUDIO_CODEC_OPUS; + default: return AV_RENDER_AUDIO_CODEC_NONE; + } +} + +static void convert_dec_aud_info(esp_peer_audio_stream_info_t *info, av_render_audio_info_t *dec_info) +{ + dec_info->codec = get_dec_codec(info->codec); + if (info->codec == ESP_PEER_AUDIO_CODEC_G711A || info->codec == ESP_PEER_AUDIO_CODEC_G711U) { + dec_info->sample_rate = 8000; + dec_info->channel = 1; + } else { + dec_info->sample_rate = info->sample_rate; + dec_info->channel = info->channel; + } + dec_info->bits_per_sample = 16; +} + +static void _media_stream_send_audio(engine_t *eng) +{ + esp_capture_stream_frame_t audio_frame = { + .stream_type = ESP_CAPTURE_STREAM_TYPE_AUDIO, + }; + while (esp_capture_acquire_path_frame(eng->capturer_path, &audio_frame, true) == ESP_CAPTURE_ERR_OK) { + esp_peer_audio_frame_t audio_send_frame = { + .pts = audio_frame.pts, + .data = audio_frame.data, + .size = audio_frame.size, + }; + peer_send_audio(eng->pub_peer, &audio_send_frame); + esp_capture_release_path_frame(eng->capturer_path, &audio_frame); + } +} + +static void _media_stream_send_video(engine_t *eng) +{ + esp_capture_stream_frame_t video_frame = { + .stream_type = ESP_CAPTURE_STREAM_TYPE_VIDEO, + }; + if (esp_capture_acquire_path_frame(eng->capturer_path, &video_frame, true) == ESP_CAPTURE_ERR_OK) { + esp_peer_video_frame_t video_send_frame = { + .pts = video_frame.pts, + .data = video_frame.data, + .size = video_frame.size, + }; + peer_send_video(eng->pub_peer, &video_send_frame); + esp_capture_release_path_frame(eng->capturer_path, &video_frame); + } +} + +static void media_stream_task(void *arg) +{ + engine_t *eng = (engine_t *)arg; + while (eng->is_media_streaming) { + if (eng->options.media.audio_info.codec != ESP_PEER_AUDIO_CODEC_NONE) { + _media_stream_send_audio(eng); + } + if (eng->options.media.video_info.codec != ESP_PEER_VIDEO_CODEC_NONE) { + _media_stream_send_video(eng); + } + media_lib_thread_sleep(FRAME_INTERVAL_MS); + } + media_lib_thread_destroy(NULL); +} + +static engine_err_t media_stream_begin(engine_t *eng) +{ + if (esp_capture_start(eng->options.media.capturer) != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Failed to start capture"); + return ENGINE_ERR_MEDIA; + } + media_lib_thread_handle_t handle = NULL; + eng->is_media_streaming = true; + if (media_lib_thread_create_from_scheduler(&handle, STREAM_THREAD_NAME, media_stream_task, eng) != ESP_OK) { + ESP_LOGE(TAG, "Failed to create media stream thread"); + eng->is_media_streaming = false; + return ENGINE_ERR_MEDIA; + } + return ENGINE_ERR_NONE; +} + +static engine_err_t media_stream_end(engine_t *eng) +{ + if (!eng->is_media_streaming) { + return ENGINE_ERR_NONE; + } + eng->is_media_streaming = false; + esp_capture_stop(eng->options.media.capturer); + return ENGINE_ERR_NONE; +} + +static engine_err_t send_add_audio_track(engine_t *eng) +{ + bool is_stereo = eng->options.media.audio_info.channel == 2; + livekit_pb_add_track_request_t req = { + .cid = AUDIO_TRACK_CID, + .name = AUDIO_TRACK_NAME, + .type = LIVEKIT_PB_TRACK_TYPE_AUDIO, + .source = LIVEKIT_PB_TRACK_SOURCE_MICROPHONE, + .muted = false, + .audio_features_count = is_stereo ? 1 : 0, + .audio_features = { LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_STEREO }, + .layers_count = 0 + }; + + if (signal_send_add_track(eng->sig, &req) != SIGNAL_ERR_NONE) { + ESP_LOGE(TAG, "Failed to publish audio track"); + return ENGINE_ERR_SIGNALING; + } + return ENGINE_ERR_NONE; +} + +static engine_err_t send_add_video_track(engine_t *eng) +{ + livekit_pb_video_layer_t video_layer = { + .quality = LIVEKIT_PB_VIDEO_QUALITY_HIGH, + .width = eng->options.media.video_info.width, + .height = eng->options.media.video_info.height + }; + livekit_pb_add_track_request_t req = { + .cid = VIDEO_TRACK_CID, + .name = VIDEO_TRACK_NAME, + .type = LIVEKIT_PB_TRACK_TYPE_VIDEO, + .source = LIVEKIT_PB_TRACK_SOURCE_CAMERA, + .muted = false, + .layers_count = 1, + .layers = { video_layer }, + .audio_features_count = 0 + }; + + if (signal_send_add_track(eng->sig, &req) != SIGNAL_ERR_NONE) { + ESP_LOGE(TAG, "Failed to publish video track"); + return ENGINE_ERR_SIGNALING; + } + return ENGINE_ERR_NONE; +} + +/// Begins media streaming and sends add track requests. +static engine_err_t publish_tracks(engine_t *eng) +{ + if (eng->options.media.audio_info.codec == ESP_PEER_AUDIO_CODEC_NONE && + eng->options.media.video_info.codec == ESP_PEER_VIDEO_CODEC_NONE) { + ESP_LOGI(TAG, "No media tracks to publish"); + return ENGINE_ERR_NONE; + } + + int ret = ENGINE_ERR_OTHER; + do { + if (media_stream_begin(eng) != ENGINE_ERR_NONE) { + ret = ENGINE_ERR_MEDIA; + break; + } + if (eng->options.media.audio_info.codec != ESP_PEER_AUDIO_CODEC_NONE && + send_add_audio_track(eng) != ENGINE_ERR_NONE) { + ret = ENGINE_ERR_SIGNALING; + break; + } + if (eng->options.media.video_info.codec != ESP_PEER_VIDEO_CODEC_NONE && + send_add_video_track(eng) != ENGINE_ERR_NONE) { + ret = ENGINE_ERR_SIGNALING; + break; + } + return ENGINE_ERR_NONE; + } while (0); + + media_stream_end(eng); + return ret; +} + +static engine_err_t subscribe_tracks(engine_t *eng, livekit_pb_track_info_t *tracks, int count) +{ + if (eng == NULL || tracks == NULL || count <= 0) { + return ENGINE_ERR_INVALID_ARG; + } + if (eng->sub_audio_track_sid[0] != '\0') { + return ENGINE_ERR_NONE; + } + for (int i = 0; i < count; i++) { + livekit_pb_track_info_t *track = &tracks[i]; + + if (track->type != LIVEKIT_PB_TRACK_TYPE_AUDIO) { + continue; + } + signal_send_update_subscription(eng->sig, track->sid, true); + strncpy(eng->sub_audio_track_sid, track->sid, sizeof(eng->sub_audio_track_sid)); + ESP_LOGI(TAG, "Subscribed to audio track"); + } + return ENGINE_ERR_NONE; +} + +static void free_ice_servers(engine_t *eng) +{ + if (eng == NULL || eng->ice_servers == NULL) { + return; + } + esp_peer_ice_server_cfg_t *ice_servers = eng->ice_servers; + for (int i = 0; i < eng->ice_server_count; i++) { + SAFE_FREE(ice_servers[i].stun_url); + SAFE_FREE(ice_servers[i].user); + SAFE_FREE(ice_servers[i].psw); + } + SAFE_FREE(eng->ice_servers); + eng->ice_server_count = 0; +} + +__attribute__((unused)) +static engine_err_t set_ice_servers(engine_t* eng, livekit_pb_ice_server_t *servers, int count) +{ + if (eng == NULL || servers == NULL || count <= 0) { + return ENGINE_ERR_INVALID_ARG; + } + // A single livekit_ice_server_t can contain multiple URLs, which + // will map to multiple esp_peer_ice_server_cfg_t entries. + size_t cfg_count = 0; + for (int i = 0; i < count; i++) { + if (servers[i].urls_count <= 0) { + return ENGINE_ERR_INVALID_ARG; + } + for (int j = 0; j < servers[i].urls_count; j++) { + if (servers[i].urls[j] == NULL) { + return ENGINE_ERR_INVALID_ARG; + } + cfg_count++; + } + } + + esp_peer_ice_server_cfg_t *cfgs = calloc(cfg_count, sizeof(esp_peer_ice_server_cfg_t)); + if (cfgs == NULL) { + return ENGINE_ERR_NO_MEM; + } + + int cfg_idx = 0; + for (int i = 0; i < count; i++) { + for (int j = 0; j < servers[i].urls_count; j++) { + bool has_auth = false; + cfgs[cfg_idx].stun_url = strdup(servers[i].urls[j]); + if (servers[i].username != NULL) { + cfgs[cfg_idx].user = strdup(servers[i].username); + has_auth = true; + } + if (servers[i].credential != NULL) { + cfgs[cfg_idx].psw = strdup(servers[i].credential); + has_auth = true; + } + ESP_LOGI(TAG, "Adding ICE server: has_auth=%d, url=%s", has_auth, servers[i].urls[j]); + cfg_idx++; + } + } + + free_ice_servers(eng); + eng->ice_servers = cfgs; + eng->ice_server_count = cfg_count; + + return ENGINE_ERR_NONE; +} + +static void on_peer_pub_state_changed(connection_state_t state, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + if (state == CONNECTION_STATE_CONNECTED) { + publish_tracks(eng); + } + eng->pub_state = state; + recalculate_state(eng); +} + +static void on_peer_sub_state_changed(connection_state_t state, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + eng->sub_state = state; + recalculate_state(eng); +} + +static void on_peer_pub_offer(const char *sdp, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + signal_send_offer(eng->sig, sdp); +} + +static void on_peer_sub_answer(const char *sdp, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + signal_send_answer(eng->sig, sdp); +} + +static void on_peer_ice_candidate(const char *candidate, void *ctx) +{ + ESP_LOGD(TAG, "Peer generated ice candidate: %s", candidate); +} + +static void on_peer_packet_received(livekit_pb_data_packet_t* packet, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + eng->options.on_data_packet(packet, eng->options.ctx); +} + +static void on_peer_sub_audio_info(esp_peer_audio_stream_info_t* info, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + + av_render_audio_info_t render_info = {}; + convert_dec_aud_info(info, &render_info); + ESP_LOGD(TAG, "Audio render info: codec=%d, sample_rate=%" PRIu32 ", channels=%" PRIu8, + render_info.codec, render_info.sample_rate, render_info.channel); + + if (av_render_add_audio_stream(eng->renderer_handle, &render_info) != ESP_MEDIA_ERR_OK) { + ESP_LOGE(TAG, "Failed to add audio stream to renderer"); + return; + } + eng->sub_audio_info = *info; +} + +static void on_peer_sub_audio_frame(esp_peer_audio_frame_t* frame, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + if (eng->sub_audio_info.codec == ESP_PEER_AUDIO_CODEC_NONE) return; + // TODO: Check engine state before rendering + + av_render_audio_data_t audio_data = { + .pts = frame->pts, + .data = frame->data, + .size = frame->size, + }; + av_render_add_audio_data(eng->renderer_handle, &audio_data); +} + +static void on_sig_state_changed(connection_state_t state, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + eng->sig_state = state; + recalculate_state(eng); +} + +static bool disconnect_peer(peer_handle_t *peer) +{ + if (*peer == NULL) return false; + if (peer_disconnect(*peer) != PEER_ERR_NONE) return false; + if (peer_destroy(*peer) != PEER_ERR_NONE) return false; + *peer = NULL; + return true; +} + +static bool connect_peer(engine_t *eng, peer_options_t *options, peer_handle_t *peer) +{ + disconnect_peer(peer); + if (peer_create(peer, options) != PEER_ERR_NONE) return false; + if (peer_connect(*peer) != PEER_ERR_NONE) return false; + return true; +} + +static void on_sig_join(livekit_pb_join_response_t *join_res, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + + if (join_res->subscriber_primary) { + eng->is_subscriber_primary = true; + ESP_LOGE(TAG, "Subscriber primary is not supported yet"); + return; + } + + // set_ice_servers(eng, join_res->ice_servers, join_res->ice_servers_count); + + peer_options_t options = { + // Options common to both peers + .force_relay = join_res->has_client_configuration && + join_res->client_configuration.force_relay == LIVEKIT_PB_CLIENT_CONFIG_SETTING_ENABLED, + .media = &eng->options.media, + .server_list = eng->ice_servers, + .server_count = eng->ice_server_count, + .on_ice_candidate = on_peer_ice_candidate, + .on_packet_received = on_peer_packet_received, + .ctx = eng + }; + + // 1. Publisher peer + options.is_primary = !join_res->subscriber_primary; + options.target = LIVEKIT_PB_SIGNAL_TARGET_PUBLISHER; + options.on_state_changed = on_peer_pub_state_changed; + options.on_sdp = on_peer_pub_offer; + + if (!connect_peer(eng, &options, &eng->pub_peer)) { + ESP_LOGE(TAG, "Failed to connect publisher peer"); + return; + } + + // 2. Subscriber peer + options.is_primary = join_res->subscriber_primary; + options.target = LIVEKIT_PB_SIGNAL_TARGET_SUBSCRIBER; + options.on_state_changed = on_peer_sub_state_changed; + options.on_sdp = on_peer_sub_answer; + options.on_audio_info = on_peer_sub_audio_info; + options.on_audio_frame = on_peer_sub_audio_frame; + + if (!connect_peer(eng, &options, &eng->sub_peer)) { + ESP_LOGE(TAG, "Failed to connect subscriber peer"); + return; + } + + // Store fields from join response required for later use + strncpy(eng->local_participant_sid, join_res->participant.sid, sizeof(eng->local_participant_sid)); + + // Join response contains initial room and participant info (both local and remote); + // extract and invoke the appropriate handlers. + eng->options.on_room_info(&join_res->room, eng->options.ctx); + eng->options.on_participant_info(&join_res->participant, true, eng->options.ctx); + for (int i = 0; i < join_res->other_participants_count; i++) { + eng->options.on_participant_info(&join_res->other_participants[i], false, eng->options.ctx); + } +} + +static void on_sig_leave(livekit_pb_disconnect_reason_t reason, livekit_pb_leave_request_action_t action, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + // TODO: Handle reconnect, update engine state + disconnect_peer(&eng->pub_peer); + disconnect_peer(&eng->sub_peer); + + memset(eng->local_participant_sid, 0, sizeof(eng->local_participant_sid)); + memset(eng->sub_audio_track_sid, 0, sizeof(eng->sub_audio_track_sid)); +} + +static void on_sig_room_update(const livekit_pb_room_t* info, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + eng->options.on_room_info(info, eng->options.ctx); +} + +static void on_sig_participant_update(const livekit_pb_participant_info_t* info, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + bool is_local = strncmp(info->sid, eng->local_participant_sid, sizeof(eng->local_participant_sid)) == 0; + eng->options.on_participant_info(info, is_local, eng->options.ctx); + + if (is_local) return; + subscribe_tracks(eng, info->tracks, info->tracks_count); +} + +static void on_sig_answer(const char *sdp, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + peer_handle_sdp(eng->pub_peer, sdp); +} + +static void on_sig_offer(const char *sdp, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + peer_handle_sdp(eng->sub_peer, sdp); +} + +static void on_sig_trickle(const char *ice_candidate, livekit_pb_signal_target_t target, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + peer_handle_t target_peer = target == LIVEKIT_PB_SIGNAL_TARGET_SUBSCRIBER ? + eng->sub_peer : eng->pub_peer; + peer_handle_ice_candidate(target_peer, ice_candidate); +} + +engine_err_t engine_create(engine_handle_t *handle, engine_options_t *options) +{ + if (handle == NULL || + options == NULL || + options->on_state_changed == NULL || + options->on_data_packet == NULL || + options->on_room_info == NULL || + options->on_participant_info == NULL + ) { + return ENGINE_ERR_INVALID_ARG; + } + + engine_t *eng = (engine_t *)calloc(1, sizeof(engine_t)); + if (eng == NULL) { + return ENGINE_ERR_NO_MEM; + } + eng->options = *options; + signal_options_t sig_options = { + .ctx = eng, + .on_state_changed = on_sig_state_changed, + .on_join = on_sig_join, + .on_leave = on_sig_leave, + .on_room_update = on_sig_room_update, + .on_participant_update = on_sig_participant_update, + .on_answer = on_sig_answer, + .on_offer = on_sig_offer, + .on_trickle = on_sig_trickle + }; + + if (signal_create(&eng->sig, &sig_options) != SIGNAL_ERR_NONE) { + ESP_LOGE(TAG, "Failed to create signaling client"); + free(eng); + return ENGINE_ERR_SIGNALING; + } + + esp_capture_sink_cfg_t sink_cfg = { + .audio_info = { + .codec = capture_audio_codec_type(eng->options.media.audio_info.codec), + .sample_rate = eng->options.media.audio_info.sample_rate, + .channel = eng->options.media.audio_info.channel, + .bits_per_sample = 16, + }, + .video_info = { + .codec = capture_video_codec_type(eng->options.media.video_info.codec), + .width = eng->options.media.video_info.width, + .height = eng->options.media.video_info.height, + .fps = eng->options.media.video_info.fps, + }, + }; + + if (options->media.audio_info.codec != ESP_PEER_AUDIO_CODEC_NONE) { + // TODO: Can we ensure the renderer is valid? If not, return error. + eng->renderer_handle = options->media.renderer; + } + esp_capture_setup_path(eng->options.media.capturer, ESP_CAPTURE_PATH_PRIMARY, &sink_cfg, &eng->capturer_path); + esp_capture_enable_path(eng->capturer_path, ESP_CAPTURE_RUN_TYPE_ALWAYS); + // TODO: Handle capturer error + + *handle = eng; + return ENGINE_ERR_NONE; +} + +engine_err_t engine_destroy(engine_handle_t handle) +{ + if (handle == NULL) { + return ENGINE_ERR_INVALID_ARG; + } + engine_t *eng = (engine_t *)handle; + + if (eng->pub_peer != NULL) { + peer_destroy(eng->pub_peer); + } + if (eng->sub_peer != NULL) { + peer_destroy(eng->sub_peer); + } + signal_destroy(eng->sig); + free_ice_servers(eng); + free(eng); + return ENGINE_ERR_NONE; +} + +engine_err_t engine_connect(engine_handle_t handle, const char* server_url, const char* token) +{ + if (handle == NULL || server_url == NULL || token == NULL) { + return ENGINE_ERR_INVALID_ARG; + } + engine_t *eng = (engine_t *)handle; + + if (eng->state != CONNECTION_STATE_DISCONNECTED) { + return ENGINE_ERR_OTHER; + } + if (signal_connect(eng->sig, server_url, token) != SIGNAL_ERR_NONE) { + ESP_LOGE(TAG, "Failed to connect signaling client"); + return ENGINE_ERR_SIGNALING; + } + return ENGINE_ERR_NONE; +} + +engine_err_t engine_close(engine_handle_t handle) +{ + if (handle == NULL) { + return ENGINE_ERR_INVALID_ARG; + } + engine_t *eng = (engine_t *)handle; + + if (eng->state == CONNECTION_STATE_DISCONNECTED) { + return ENGINE_ERR_OTHER; + } + media_stream_end(eng); + // TODO: Reset just the stream that was added in case users added their own streams? + av_render_reset(eng->renderer_handle); + + if (eng->sub_peer != NULL) { + peer_disconnect(eng->sub_peer); + } + if (eng->pub_peer != NULL) { + peer_disconnect(eng->pub_peer); + } + if (eng->sig != NULL) { + // TODO: Ensure the WebSocket stays open long enough for the leave message to be sent + signal_send_leave(eng->sig); + signal_close(eng->sig); + } + return ENGINE_ERR_NONE; +} + +engine_err_t engine_send_data_packet(engine_handle_t handle, const livekit_pb_data_packet_t* packet, livekit_pb_data_packet_kind_t kind) +{ + if (handle == NULL || packet == NULL) { + return ENGINE_ERR_INVALID_ARG; + } + engine_t *eng = (engine_t *)handle; + + if (eng->pub_peer == NULL || + peer_send_data_packet(eng->pub_peer, packet, kind) != PEER_ERR_NONE) { + return ENGINE_ERR_RTC; + } + return ENGINE_ERR_NONE; +} \ No newline at end of file diff --git a/components/livekit/core/engine.h b/components/livekit/core/engine.h new file mode 100644 index 0000000..5387a64 --- /dev/null +++ b/components/livekit/core/engine.h @@ -0,0 +1,68 @@ + +#pragma once + +#include "esp_peer.h" +#include "esp_peer_signaling.h" +#include "esp_capture.h" +#include "av_render.h" + +#include "common.h" +#include "protocol.h" + +#define STREAM_THREAD_NAME "lk_stream" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Handle to an engine instance. +typedef void *engine_handle_t; + +typedef enum { + ENGINE_ERR_NONE = 0, + ENGINE_ERR_INVALID_ARG = -1, + ENGINE_ERR_NO_MEM = -2, + ENGINE_ERR_SIGNALING = -3, + ENGINE_ERR_RTC = -4, + ENGINE_ERR_MEDIA = -5, + ENGINE_ERR_OTHER = -6, + // TODO: Add more error cases as needed +} engine_err_t; + +/// WebRTC media provider +/// @note Media player and capture system are created externally. +/// WebRTC will internally use the capture and player handle to capture media data and perform media playback. +typedef struct { + esp_capture_handle_t capture; /*!< Capture system handle */ + av_render_handle_t player; /*!< Player handle */ +} engine_media_provider_t; + +typedef struct { + void *ctx; + void (*on_state_changed)(connection_state_t state, void *ctx); + void (*on_data_packet)(livekit_pb_data_packet_t* packet, void *ctx); + void (*on_room_info)(const livekit_pb_room_t* info, void *ctx); + void (*on_participant_info)(const livekit_pb_participant_info_t* info, bool is_local, void *ctx); + engine_media_options_t media; +} engine_options_t; + +/// Creates a new instance. +/// @param[out] handle The handle to the new instance. +engine_err_t engine_create(engine_handle_t *handle, engine_options_t *options); + +/// Destroys an instance. +/// @param[in] handle The handle to the instance to destroy. +engine_err_t engine_destroy(engine_handle_t handle); + +/// Connect the engine. +engine_err_t engine_connect(engine_handle_t handle, const char* server_url, const char* token); + +/// Close the engine. +engine_err_t engine_close(engine_handle_t handle); + +/// Sends a data packet to the remote peer. +engine_err_t engine_send_data_packet(engine_handle_t handle, const livekit_pb_data_packet_t* packet, livekit_pb_data_packet_kind_t kind); + +#ifdef __cplusplus +} +#endif diff --git a/components/livekit/core/livekit.c b/components/livekit/core/livekit.c new file mode 100644 index 0000000..c761c35 --- /dev/null +++ b/components/livekit/core/livekit.c @@ -0,0 +1,389 @@ +#include +#include +#include "esp_peer.h" +#include "engine.h" +#include "rpc_manager.h" +#include "system.h" +#include "livekit.h" + +static const char *TAG = "livekit"; + +typedef struct { + rpc_manager_handle_t rpc_manager; + engine_handle_t engine; + livekit_room_options_t options; + livekit_connection_state_t state; +} livekit_room_t; + +static bool send_reliable_packet(const livekit_pb_data_packet_t* packet, void *ctx) +{ + livekit_room_t *room = (livekit_room_t *)ctx; + return engine_send_data_packet( + room->engine, + packet, + LIVEKIT_PB_DATA_PACKET_KIND_RELIABLE) == ENGINE_ERR_NONE; +} + +static void on_rpc_result(const livekit_rpc_result_t* result, void* ctx) +{ + livekit_room_t *room = (livekit_room_t *)ctx; + if (room->options.on_rpc_result != NULL) { + room->options.on_rpc_result(result, room->options.ctx); + } +} + +static void on_user_packet(const livekit_pb_user_packet_t* packet, const char* sender_identity, void* ctx) +{ + livekit_room_t *room = (livekit_room_t *)ctx; + if (room->options.on_data_received == NULL) { + return; + } + livekit_data_received_t data = { + .topic = packet->topic, + .payload = { + .bytes = packet->payload->bytes, + .size = packet->payload->size + }, + .sender_identity = (char*)sender_identity + }; + room->options.on_data_received(&data, room->options.ctx); +} + +static void populate_media_options( + engine_media_options_t *media_options, + const livekit_pub_options_t *pub_options, + const livekit_sub_options_t *sub_options) +{ + if (pub_options->kind & LIVEKIT_MEDIA_TYPE_AUDIO) { + media_options->audio_dir |= ESP_PEER_MEDIA_DIR_SEND_ONLY; + + esp_peer_audio_codec_t codec = ESP_PEER_AUDIO_CODEC_NONE; + switch (pub_options->audio_encode.codec) { + case LIVEKIT_AUDIO_CODEC_G711A: + codec = ESP_PEER_AUDIO_CODEC_G711A; + break; + case LIVEKIT_AUDIO_CODEC_G711U: + codec = ESP_PEER_AUDIO_CODEC_G711U; + break; + case LIVEKIT_AUDIO_CODEC_OPUS: + codec = ESP_PEER_AUDIO_CODEC_OPUS; + break; + default: + ESP_LOGE(TAG, "Unsupported audio codec"); + break; + } + media_options->audio_info.codec = codec; + media_options->audio_info.sample_rate = pub_options->audio_encode.sample_rate; + media_options->audio_info.channel = pub_options->audio_encode.channel_count; + } + if (pub_options->kind & LIVEKIT_MEDIA_TYPE_VIDEO) { + media_options->video_dir |= ESP_PEER_MEDIA_DIR_SEND_ONLY; + esp_peer_video_codec_t codec = ESP_PEER_VIDEO_CODEC_NONE; + switch (pub_options->video_encode.codec) { + case LIVEKIT_VIDEO_CODEC_H264: + codec = ESP_PEER_VIDEO_CODEC_H264; + break; + default: + ESP_LOGE(TAG, "Unsupported video codec"); + break; + } + media_options->video_info.codec = codec; + media_options->video_info.width = pub_options->video_encode.width; + media_options->video_info.height = pub_options->video_encode.height; + media_options->video_info.fps = pub_options->video_encode.fps; + } + if (sub_options->kind & LIVEKIT_MEDIA_TYPE_AUDIO) { + media_options->audio_dir |= ESP_PEER_MEDIA_DIR_RECV_ONLY; + } + if (sub_options->kind & LIVEKIT_MEDIA_TYPE_VIDEO) { + media_options->video_dir |= ESP_PEER_MEDIA_DIR_RECV_ONLY; + } + media_options->capturer = pub_options->capturer; + media_options->renderer = sub_options->renderer; +} + +static void on_eng_state_changed(connection_state_t engine_state, void *ctx) +{ + livekit_room_t *room = (livekit_room_t *)ctx; + switch (engine_state) { + case CONNECTION_STATE_DISCONNECTED: + room->state = LIVEKIT_CONNECTION_STATE_DISCONNECTED; + break; + case CONNECTION_STATE_CONNECTED: + room->state = LIVEKIT_CONNECTION_STATE_CONNECTED; + break; + case CONNECTION_STATE_CONNECTING: + room->state = LIVEKIT_CONNECTION_STATE_CONNECTING; + break; + case CONNECTION_STATE_RECONNECTING: + room->state = LIVEKIT_CONNECTION_STATE_RECONNECTING; + break; + case CONNECTION_STATE_FAILED: + room->state = LIVEKIT_CONNECTION_STATE_FAILED; + break; + default: + return; + } + if (room->options.on_state_changed != NULL) { + room->options.on_state_changed(room->state, room->options.ctx); + } +} + +static void on_eng_data_packet(livekit_pb_data_packet_t* packet, void *ctx) +{ + livekit_room_t *room = (livekit_room_t *)ctx; + switch (packet->which_value) { + case LIVEKIT_PB_DATA_PACKET_USER_TAG: + on_user_packet(&packet->value.user, packet->participant_identity, ctx); + break; + case LIVEKIT_PB_DATA_PACKET_RPC_REQUEST_TAG: + case LIVEKIT_PB_DATA_PACKET_RPC_ACK_TAG: + case LIVEKIT_PB_DATA_PACKET_RPC_RESPONSE_TAG: + rpc_manager_handle_packet(room->rpc_manager, packet); + break; + default: + break; + } +} + +static void on_eng_room_info(const livekit_pb_room_t* info, void *ctx) +{ + livekit_room_t *room = (livekit_room_t *)ctx; + if (room->options.on_room_info == NULL) { + return; + } + livekit_room_info_t room_info = { + .sid = info->sid, + .name = info->name, + .metadata = info->metadata, + .participant_count = info->num_participants, + .active_recording = info->active_recording + }; + room->options.on_room_info(&room_info, room->options.ctx); +} + +static void on_eng_participant_info(const livekit_pb_participant_info_t* info, bool is_local, void *ctx) +{ + livekit_room_t *room = (livekit_room_t *)ctx; + if (room->options.on_participant_info == NULL) { + return; + } + livekit_participant_info_t participant_info = { + .sid = info->sid, + .identity = info->identity, + .name = info->name, + .metadata = info->metadata, + // Assumes enum values are the same as defined in the protocol. + .kind = (livekit_participant_kind_t)info->kind, + .state = (livekit_participant_state_t)info->state, + }; + room->options.on_participant_info(&participant_info, room->options.ctx); +} + +livekit_err_t livekit_room_create(livekit_room_handle_t *handle, const livekit_room_options_t *options) +{ + if (handle == NULL || options == NULL) { + return LIVEKIT_ERR_INVALID_ARG; + } + if (!system_is_media_lib_setup()) { + ESP_LOGE(TAG, "Must perform system initialization before creating a room"); + return LIVEKIT_ERR_SYSTEM_INIT; + } + + // Validate options + if (options->publish.kind != LIVEKIT_MEDIA_TYPE_NONE && + options->publish.capturer == NULL) { + ESP_LOGE(TAG, "Capturer must be set for media publishing"); + return LIVEKIT_ERR_INVALID_ARG; + } + if (options->subscribe.kind != LIVEKIT_MEDIA_TYPE_NONE && + options->subscribe.renderer == NULL) { + ESP_LOGE(TAG, "Renderer must be set for subscribing to media"); + return LIVEKIT_ERR_INVALID_ARG; + } + if ((options->publish.kind & LIVEKIT_MEDIA_TYPE_AUDIO) && + (options->publish.audio_encode.codec == LIVEKIT_AUDIO_CODEC_NONE)) { + ESP_LOGE(TAG, "Encode options must be set for audio publishing"); + return LIVEKIT_ERR_INVALID_ARG; + } + if ((options->publish.kind & LIVEKIT_MEDIA_TYPE_VIDEO) && + options->publish.video_encode.codec == LIVEKIT_VIDEO_CODEC_NONE) { + ESP_LOGE(TAG, "Encode options must be set for video publishing"); + return LIVEKIT_ERR_INVALID_ARG; + } + + livekit_room_t *room = calloc(1, sizeof(livekit_room_t)); + if (room == NULL) { + return LIVEKIT_ERR_NO_MEM; + } + room->state = LIVEKIT_CONNECTION_STATE_DISCONNECTED; + room->options = *options; + + engine_media_options_t media_options = {}; + populate_media_options(&media_options, &options->publish, &options->subscribe); + + engine_options_t eng_options = { + .media = media_options, + .on_state_changed = on_eng_state_changed, + .on_data_packet = on_eng_data_packet, + .on_room_info = on_eng_room_info, + .on_participant_info = on_eng_participant_info, + .ctx = room + }; + + int ret = LIVEKIT_ERR_OTHER; + do { + if (engine_create(&room->engine, &eng_options) != ENGINE_ERR_NONE) { + ESP_LOGE(TAG, "Failed to create engine"); + ret = LIVEKIT_ERR_ENGINE; + break; + } + rpc_manager_options_t rpc_manager_options = { + .on_result = on_rpc_result, + .send_packet = send_reliable_packet, + .ctx = room + }; + if (rpc_manager_create(&room->rpc_manager, &rpc_manager_options) != RPC_MANAGER_ERR_NONE) { + ESP_LOGE(TAG, "Failed to create RPC manager"); + ret = LIVEKIT_ERR_OTHER; + break; + } + *handle = (livekit_room_handle_t)room; + return LIVEKIT_ERR_NONE; + } while (0); + + free(room); + return ret; +} + +livekit_err_t livekit_room_destroy(livekit_room_handle_t handle) +{ + livekit_room_t *room = (livekit_room_t *)handle; + if (room == NULL) { + return LIVEKIT_ERR_INVALID_ARG; + } + livekit_room_close(handle); + engine_destroy(room->engine); + free(room); + return LIVEKIT_ERR_NONE; +} + +livekit_err_t livekit_room_connect(livekit_room_handle_t handle, const char *server_url, const char *token) +{ + if (handle == NULL || server_url == NULL || token == NULL) { + return LIVEKIT_ERR_INVALID_ARG; + } + livekit_room_t *room = (livekit_room_t *)handle; + + if (engine_connect(room->engine, server_url, token) != ENGINE_ERR_NONE) { + ESP_LOGE(TAG, "Failed to connect engine"); + return LIVEKIT_ERR_OTHER; + } + return LIVEKIT_ERR_NONE; +} + +livekit_err_t livekit_room_close(livekit_room_handle_t handle) +{ + if (handle == NULL) { + return LIVEKIT_ERR_INVALID_ARG; + } + livekit_room_t *room = (livekit_room_t *)handle; + engine_close(room->engine); + return LIVEKIT_ERR_NONE; +} + +livekit_connection_state_t livekit_room_get_state(livekit_room_handle_t handle) +{ + if (handle == NULL) { + return LIVEKIT_CONNECTION_STATE_DISCONNECTED; + } + livekit_room_t *room = (livekit_room_t *)handle; + return room->state; +} + +const char* livekit_connection_state_str(livekit_connection_state_t state) +{ + switch (state) { + case LIVEKIT_CONNECTION_STATE_DISCONNECTED: return "disconnected"; + case LIVEKIT_CONNECTION_STATE_CONNECTING: return "connecting"; + case LIVEKIT_CONNECTION_STATE_CONNECTED: return "connected"; + case LIVEKIT_CONNECTION_STATE_RECONNECTING: return "reconnecting"; + case LIVEKIT_CONNECTION_STATE_FAILED: return "failed"; + default: return "unknown"; + } +} + +livekit_err_t livekit_room_publish_data(livekit_room_handle_t handle, livekit_data_publish_options_t *options) +{ + if (handle == NULL || options == NULL || options->payload == NULL) { + return LIVEKIT_ERR_INVALID_ARG; + } + livekit_room_t *room = (livekit_room_t *)handle; + + // TODO: Can this be done without allocating additional memory? + pb_bytes_array_t *bytes_array = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(options->payload->size)); + if (bytes_array == NULL) { + return LIVEKIT_ERR_NO_MEM; + } + bytes_array->size = options->payload->size; + memcpy(bytes_array->bytes, options->payload->bytes, options->payload->size); + + livekit_pb_user_packet_t user_packet = { + .topic = options->topic, + .payload = bytes_array + }; + livekit_pb_data_packet_t packet = LIVEKIT_PB_DATA_PACKET_INIT_ZERO; + packet.which_value = LIVEKIT_PB_DATA_PACKET_USER_TAG; + packet.value.user = user_packet; + + packet.destination_identities_count = options->destination_identities_count; + packet.destination_identities = options->destination_identities; + // TODO: Set sender identity + + livekit_pb_data_packet_kind_t kind = options->lossy ? + LIVEKIT_PB_DATA_PACKET_KIND_LOSSY : LIVEKIT_PB_DATA_PACKET_KIND_RELIABLE; + + if (engine_send_data_packet(room->engine, &packet, kind) != ENGINE_ERR_NONE) { + ESP_LOGE(TAG, "Failed to send data packet"); + free(bytes_array); + return LIVEKIT_ERR_ENGINE; + } + free(bytes_array); + return LIVEKIT_ERR_NONE; +} + +livekit_err_t livekit_room_rpc_register(livekit_room_handle_t handle, const char* method, livekit_rpc_handler_t handler) +{ + if (handle == NULL || method == NULL || handler == NULL) { + return LIVEKIT_ERR_INVALID_ARG; + } + livekit_room_t *room = (livekit_room_t *)handle; + + if (rpc_manager_register(room->rpc_manager, method, handler) != RPC_MANAGER_ERR_NONE) { + ESP_LOGE(TAG, "Failed to register RPC method '%s'", method); + return LIVEKIT_ERR_INVALID_STATE; + } + return LIVEKIT_ERR_NONE; +} + +livekit_err_t livekit_room_rpc_unregister(livekit_room_handle_t handle, const char* method) +{ + if (handle == NULL || method == NULL) { + return LIVEKIT_ERR_INVALID_ARG; + } + livekit_room_t *room = (livekit_room_t *)handle; + + if (rpc_manager_unregister(room->rpc_manager, method) != RPC_MANAGER_ERR_NONE) { + ESP_LOGE(TAG, "Failed to unregister RPC method '%s'", method); + return LIVEKIT_ERR_INVALID_STATE; + } + return LIVEKIT_ERR_NONE; +} + +livekit_err_t livekit_system_init(void) +{ + if (!system_setup_media_lib()) { + return LIVEKIT_ERR_SYSTEM_INIT; + } + return LIVEKIT_ERR_NONE; +} \ No newline at end of file diff --git a/components/livekit/core/peer.c b/components/livekit/core/peer.c new file mode 100644 index 0000000..f823813 --- /dev/null +++ b/components/livekit/core/peer.c @@ -0,0 +1,494 @@ +#include +#include "esp_log.h" +#include "esp_peer.h" +#include "esp_peer_default.h" +#include "esp_peer_signaling.h" +#include "esp_webrtc_defaults.h" +#include "media_lib_os.h" +#include "esp_codec_dev.h" + +#include "peer.h" + +static const char *SUB_TAG = "livekit_peer.sub"; +static const char *PUB_TAG = "livekit_peer.pub"; +#define TAG(peer) (peer->options.target == LIVEKIT_PB_SIGNAL_TARGET_SUBSCRIBER ? SUB_TAG : PUB_TAG) + +#define SUB_THREAD_NAME (PEER_THREAD_NAME_PREFIX "sub") +#define PUB_THREAD_NAME (PEER_THREAD_NAME_PREFIX "pub") + +#define RELIABLE_CHANNEL_LABEL "_reliable" +#define LOSSY_CHANNEL_LABEL "_lossy" +#define STREAM_ID_INVALID 0xFFFF + +#define PC_EXIT_BIT (1 << 0) +#define PC_PAUSED_BIT (1 << 1) +#define PC_RESUME_BIT (1 << 2) +#define PC_SEND_QUIT_BIT (1 << 3) + +typedef struct { + peer_options_t options; + bool is_primary; + esp_peer_role_t ice_role; + esp_peer_handle_t connection; + + connection_state_t state; + + bool running; + bool pause; + media_lib_event_grp_handle_t wait_event; + + uint16_t reliable_stream_id; + uint16_t lossy_stream_id; +} peer_t; + +static esp_peer_media_dir_t get_media_direction(esp_peer_media_dir_t direction, livekit_pb_signal_target_t target) { + switch (target) { + case LIVEKIT_PB_SIGNAL_TARGET_PUBLISHER: + return direction & ESP_PEER_MEDIA_DIR_SEND_ONLY; + case LIVEKIT_PB_SIGNAL_TARGET_SUBSCRIBER: + return direction & ESP_PEER_MEDIA_DIR_RECV_ONLY; + } + return ESP_PEER_MEDIA_DIR_NONE; +} + +static void peer_task(void *ctx) +{ + peer_t *peer = (peer_t *)ctx; + while (peer->running) { + if (peer->pause) { + media_lib_event_group_set_bits(peer->wait_event, PC_PAUSED_BIT); + media_lib_event_group_wait_bits(peer->wait_event, PC_RESUME_BIT, MEDIA_LIB_MAX_LOCK_TIME); + media_lib_event_group_clr_bits(peer->wait_event, PC_RESUME_BIT); + continue; + } + esp_peer_main_loop(peer->connection); + media_lib_thread_sleep(10); + } + media_lib_event_group_set_bits(peer->wait_event, PC_EXIT_BIT); + media_lib_thread_destroy(NULL); +} + +static void create_data_channels(peer_t *peer) +{ + if (peer->options.target != LIVEKIT_PB_SIGNAL_TARGET_PUBLISHER) return; + + esp_peer_data_channel_cfg_t reliable_cfg = { + .label = RELIABLE_CHANNEL_LABEL, + .type = ESP_PEER_DATA_CHANNEL_RELIABLE, + .ordered = true + }; + if (esp_peer_create_data_channel(peer->connection, &reliable_cfg) != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG(peer), "Failed to create reliable data channel"); + } + + esp_peer_data_channel_cfg_t lossy_cfg = { + .label = LOSSY_CHANNEL_LABEL, + .type = ESP_PEER_DATA_CHANNEL_PARTIAL_RELIABLE_RETX, + .ordered = false, + .max_retransmit_count = 0 + }; + if (esp_peer_create_data_channel(peer->connection, &lossy_cfg) != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG(peer), "Failed to create lossy data channel"); + } +} + +static int on_state(esp_peer_state_t rtc_state, void *ctx) +{ + peer_t *peer = (peer_t *)ctx; + ESP_LOGD(TAG(peer), "RTC state changed to %d", rtc_state); + + connection_state_t new_state = peer->state; + switch (rtc_state) { + case ESP_PEER_STATE_CONNECT_FAILED: + new_state = CONNECTION_STATE_FAILED; + break; + case ESP_PEER_STATE_DISCONNECTED: + new_state = CONNECTION_STATE_DISCONNECTED; + break; + case ESP_PEER_STATE_PAIRING: + new_state = CONNECTION_STATE_CONNECTING; + break; + case ESP_PEER_STATE_CONNECTED: + create_data_channels(peer); + break; + case ESP_PEER_STATE_DATA_CHANNEL_OPENED: + // Don't enter the connected state until both data channels are opened. + if (peer->reliable_stream_id == STREAM_ID_INVALID || + peer->lossy_stream_id == STREAM_ID_INVALID ) break; + new_state = CONNECTION_STATE_CONNECTED; + break; + default: + break; + } + if (new_state != peer->state) { + ESP_LOGI(TAG(peer), "State changed: %d -> %d", peer->state, new_state); + peer->state = new_state; + peer->options.on_state_changed(new_state, peer->options.ctx); + } + return 0; +} + +static int on_msg(esp_peer_msg_t *info, void *ctx) +{ + peer_t *peer = (peer_t *)ctx; + switch (info->type) { + case ESP_PEER_MSG_TYPE_SDP: + ESP_LOGI(TAG(peer), "Generated %s:\n%s", + peer->options.target == LIVEKIT_PB_SIGNAL_TARGET_PUBLISHER ? "offer" : "answer", + (char *)info->data); + peer->options.on_sdp((char *)info->data, peer->options.ctx); + break; + case ESP_PEER_MSG_TYPE_CANDIDATE: + ESP_LOGI(TAG(peer), "Generated candidate: %s", (char *)info->data); + peer->options.on_ice_candidate((char *)info->data, peer->options.ctx); + break; + default: + ESP_LOGD(TAG(peer), "Unhandled msg type: %d", info->type); + break; + } + return 0; +} + +static int on_audio_info(esp_peer_audio_stream_info_t *info, void *ctx) +{ + peer_t *peer = (peer_t *)ctx; + if (peer->options.on_audio_info != NULL) { + peer->options.on_audio_info(info, peer->options.ctx); + } + return 0; +} + +static int on_audio_data(esp_peer_audio_frame_t *info, void *ctx) +{ + peer_t *peer = (peer_t *)ctx; + if (peer->options.on_audio_frame != NULL) { + peer->options.on_audio_frame(info, peer->options.ctx); + } + return 0; +} + +static int on_video_info(esp_peer_video_stream_info_t *info, void *ctx) +{ + peer_t *peer = (peer_t *)ctx; + if (peer->options.on_video_info != NULL) { + peer->options.on_video_info(info, peer->options.ctx); + } + return 0; +} + +static int on_video_data(esp_peer_video_frame_t *info, void *ctx) +{ + peer_t *peer = (peer_t *)ctx; + if (peer->options.on_video_frame != NULL) { + peer->options.on_video_frame(info, peer->options.ctx); + } + return 0; +} + +static int on_channel_open(esp_peer_data_channel_info_t *ch, void *ctx) +{ + peer_t *peer = (peer_t *)ctx; + ESP_LOGI(TAG(peer), "Channel open: label=%s, stream_id=%d", ch->label, ch->stream_id); + + if (strcmp(ch->label, RELIABLE_CHANNEL_LABEL) == 0) { + peer->reliable_stream_id = ch->stream_id; + } else if (strcmp(ch->label, LOSSY_CHANNEL_LABEL) == 0) { + peer->lossy_stream_id = ch->stream_id; + } + return 0; +} + +static int on_channel_close(esp_peer_data_channel_info_t *ch, void *ctx) +{ + peer_t *peer = (peer_t *)ctx; + ESP_LOGI(TAG(peer), "Channel close: label=%s, stream_id=%d", ch->label, ch->stream_id); + + if (strcmp(ch->label, RELIABLE_CHANNEL_LABEL) == 0) { + peer->reliable_stream_id = STREAM_ID_INVALID; + } else if (strcmp(ch->label, LOSSY_CHANNEL_LABEL) == 0) { + peer->lossy_stream_id = STREAM_ID_INVALID; + } + return 0; +} + +static int on_data(esp_peer_data_frame_t *frame, void *ctx) +{ + peer_t *peer = (peer_t *)ctx; + ESP_LOGD(TAG(peer), "Data received: size=%d, stream_id=%d", frame->size, frame->stream_id); + + if (peer->options.on_packet_received == NULL) { + ESP_LOGE(TAG(peer), "Packet received handler is not set"); + return -1; + } + if (frame->type != ESP_PEER_DATA_CHANNEL_DATA) { + ESP_LOGE(TAG(peer), "Unexpected data frame type: %d", frame->type); + return -1; + } + + livekit_pb_data_packet_t packet = {}; + pb_istream_t stream = pb_istream_from_buffer((const pb_byte_t *)frame->data, frame->size); + if (!pb_decode(&stream, LIVEKIT_PB_DATA_PACKET_FIELDS, &packet)) { + ESP_LOGE(TAG(peer), "Failed to decode data packet: %s", stream.errmsg); + return -1; + } + + peer->options.on_packet_received(&packet, peer->options.ctx); + pb_release(LIVEKIT_PB_DATA_PACKET_FIELDS, &packet); + return 0; +} + +peer_err_t peer_create(peer_handle_t *handle, peer_options_t *options) +{ + if (handle == NULL || + options->on_state_changed == NULL || + options->on_ice_candidate == NULL || + options->on_sdp == NULL) { + return PEER_ERR_INVALID_ARG; + } + if (options->media->video_info.codec == ESP_PEER_VIDEO_CODEC_MJPEG) { + // MJPEG over data channel is not supported yet + return PEER_ERR_INVALID_ARG; + } + + peer_t *peer = (peer_t *)calloc(1, sizeof(peer_t)); + if (peer == NULL) { + return PEER_ERR_NO_MEM; + } + media_lib_event_group_create(&peer->wait_event); + if (peer->wait_event == NULL) { + free(peer); + return PEER_ERR_NO_MEM; + } + + peer->options = *options; + peer->ice_role = options->target == LIVEKIT_PB_SIGNAL_TARGET_SUBSCRIBER ? + ESP_PEER_ROLE_CONTROLLED : ESP_PEER_ROLE_CONTROLLING; + peer->state = CONNECTION_STATE_DISCONNECTED; + + // Set to invalid IDs to indicate that the data channels are not connected yet + peer->reliable_stream_id = STREAM_ID_INVALID; + peer->lossy_stream_id = STREAM_ID_INVALID; + + // Configuration for the default peer implementation + esp_peer_default_cfg_t default_peer_cfg = { + .agent_recv_timeout = 10000, + .data_ch_cfg = { + .cache_timeout = 5000, + .send_cache_size = 100 * 1024, + .recv_cache_size = 100 * 1024 + } + // TODO: Set options + }; + esp_peer_media_dir_t audio_dir = get_media_direction(options->media->audio_dir, peer->options.target); + esp_peer_media_dir_t video_dir = get_media_direction(options->media->video_dir, peer->options.target); + ESP_LOGD(TAG(peer), "Audio dir: %d, Video dir: %d", audio_dir, video_dir); + + esp_peer_cfg_t peer_cfg = { + .server_lists = options->server_list, + .server_num = options->server_count, + .ice_trans_policy = options->force_relay ? + ESP_PEER_ICE_TRANS_POLICY_RELAY : ESP_PEER_ICE_TRANS_POLICY_ALL, + .audio_dir = audio_dir, + .video_dir = video_dir, + .audio_info = options->media->audio_info, + .video_info = options->media->video_info, + .enable_data_channel = true, + .manual_ch_create = true, + .no_auto_reconnect = false, + .extra_cfg = &default_peer_cfg, + .extra_size = sizeof(default_peer_cfg), + .on_state = on_state, + .on_msg = on_msg, + .on_video_info = on_video_info, + .on_audio_info = on_audio_info, + .on_video_data = on_video_data, + .on_audio_data = on_audio_data, + .on_channel_open = on_channel_open, + .on_channel_close = on_channel_close, + .on_data = on_data, + .role = peer->ice_role, + .ctx = peer + }; + if (esp_peer_open(&peer_cfg, esp_peer_get_default_impl(), &peer->connection) != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG(peer), "Failed to open peer"); + media_lib_event_group_destroy(peer->wait_event); + free(peer); + return PEER_ERR_RTC; + } + *handle = (peer_handle_t)peer; + return PEER_ERR_NONE; +} + +peer_err_t peer_destroy(peer_handle_t handle) +{ + if (handle == NULL) { + return PEER_ERR_INVALID_ARG; + } + peer_t *peer = (peer_t *)handle; + free(peer); + return PEER_ERR_NONE; +} + +peer_err_t peer_connect(peer_handle_t handle) +{ + if (handle == NULL) { + return PEER_ERR_INVALID_ARG; + } + peer_t *peer = (peer_t *)handle; + + peer->running = true; + media_lib_thread_handle_t thread; + const char* thread_name = peer->options.target == LIVEKIT_PB_SIGNAL_TARGET_SUBSCRIBER ? + SUB_THREAD_NAME : PUB_THREAD_NAME; + if (media_lib_thread_create_from_scheduler(&thread, thread_name, peer_task, peer) != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG(peer), "Failed to create thread"); + return PEER_ERR_RTC; + } + + if (esp_peer_new_connection(peer->connection) != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG(peer), "Failed to start connection"); + return PEER_ERR_RTC; + } + return PEER_ERR_NONE; +} + +peer_err_t peer_disconnect(peer_handle_t handle) +{ + if (handle == NULL) { + return PEER_ERR_INVALID_ARG; + } + peer_t *peer = (peer_t *)handle; + + if (peer->connection != NULL) { + esp_peer_disconnect(peer->connection); + bool still_running = peer->running; + if (peer->pause) { + peer->pause = false; + media_lib_event_group_set_bits(peer->wait_event, PC_RESUME_BIT); + } + peer->running = false; + if (still_running) { + media_lib_event_group_wait_bits(peer->wait_event, PC_EXIT_BIT, MEDIA_LIB_MAX_LOCK_TIME); + media_lib_event_group_clr_bits(peer->wait_event, PC_EXIT_BIT); + } + esp_peer_close(peer->connection); + peer->connection = NULL; + } + if (peer->wait_event) { + media_lib_event_group_destroy(peer->wait_event); + peer->wait_event = NULL; + } + return PEER_ERR_NONE; +} + +peer_err_t peer_handle_sdp(peer_handle_t handle, const char *sdp) +{ + if (handle == NULL || sdp == NULL) { + return PEER_ERR_INVALID_ARG; + } + peer_t *peer = (peer_t *)handle; + + esp_peer_msg_t msg = { + .type = ESP_PEER_MSG_TYPE_SDP, + .data = (void *)sdp, + .size = strlen(sdp) + }; + if (esp_peer_send_msg(peer->connection, &msg) != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG(peer), "Failed to handle answer"); + return PEER_ERR_RTC; + } + return PEER_ERR_NONE; +} + +peer_err_t peer_handle_ice_candidate(peer_handle_t handle, const char *candidate) +{ + if (handle == NULL || candidate == NULL) { + return PEER_ERR_INVALID_ARG; + } + peer_t *peer = (peer_t *)handle; + + esp_peer_msg_t msg = { + .type = ESP_PEER_MSG_TYPE_CANDIDATE, + .data = (void *)candidate, + .size = strlen(candidate) + }; + if (esp_peer_send_msg(peer->connection, &msg) != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG(peer), "Failed to handle ICE candidate"); + return PEER_ERR_RTC; + } + return PEER_ERR_NONE; +} + +peer_err_t peer_send_data_packet(peer_handle_t handle, const livekit_pb_data_packet_t* packet, livekit_pb_data_packet_kind_t kind) +{ + if (handle == NULL || packet == NULL) { + return PEER_ERR_INVALID_ARG; + } + peer_t *peer = (peer_t *)handle; + + uint16_t stream_id = kind == LIVEKIT_PB_DATA_PACKET_KIND_RELIABLE ? + peer->reliable_stream_id : peer->lossy_stream_id; + if (stream_id == STREAM_ID_INVALID) { + ESP_LOGE(TAG(peer), "Required data channel not connected"); + return PEER_ERR_INVALID_STATE; + } + esp_peer_data_frame_t frame_info = { + .type = ESP_PEER_DATA_CHANNEL_DATA, + .stream_id = stream_id + }; + + // TODO: Optimize encoding + size_t encoded_size = 0; + if (!pb_get_encoded_size(&encoded_size, LIVEKIT_PB_DATA_PACKET_FIELDS, packet)) { + return PEER_ERR_MESSAGE; + } + uint8_t *enc_buf = (uint8_t *)malloc(encoded_size); + if (enc_buf == NULL) { + return PEER_ERR_NO_MEM; + } + + int ret = PEER_ERR_NONE; + do { + pb_ostream_t stream = pb_ostream_from_buffer(enc_buf, encoded_size); + if (!pb_encode(&stream, LIVEKIT_PB_DATA_PACKET_FIELDS, packet)) { + ESP_LOGE(TAG(peer), "Failed to encode data packet"); + ret = PEER_ERR_MESSAGE; + break; + } + + frame_info.data = enc_buf; + frame_info.size = stream.bytes_written; + if (esp_peer_send_data(peer->connection, &frame_info) != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG(peer), "Data channel send failed"); + ret = PEER_ERR_RTC; + break; + } + } while (0); + + free(enc_buf); + return ret; +} + +peer_err_t peer_send_audio(peer_handle_t handle, esp_peer_audio_frame_t* frame) +{ + if (handle == NULL) { + return PEER_ERR_INVALID_ARG; + } + peer_t *peer = (peer_t *)handle; + assert(peer->options.target == LIVEKIT_PB_SIGNAL_TARGET_PUBLISHER); + + esp_peer_send_audio(peer->connection, frame); + return PEER_ERR_NONE; +} + +peer_err_t peer_send_video(peer_handle_t handle, esp_peer_video_frame_t* frame) +{ + if (handle == NULL) { + return PEER_ERR_INVALID_ARG; + } + peer_t *peer = (peer_t *)handle; + assert(peer->options.target == LIVEKIT_PB_SIGNAL_TARGET_PUBLISHER); + + esp_peer_send_video(peer->connection, frame); + return PEER_ERR_NONE; +} \ No newline at end of file diff --git a/components/livekit/core/peer.h b/components/livekit/core/peer.h new file mode 100644 index 0000000..e8503d6 --- /dev/null +++ b/components/livekit/core/peer.h @@ -0,0 +1,100 @@ + +#pragma once + +#include "common.h" +#include "engine.h" +#include "protocol.h" + +#define PEER_THREAD_NAME_PREFIX "lk_peer_" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *peer_handle_t; + +typedef enum { + PEER_ERR_NONE = 0, + PEER_ERR_INVALID_ARG = -1, + PEER_ERR_NO_MEM = -2, + PEER_ERR_INVALID_STATE = -3, + PEER_ERR_RTC = -4, + PEER_ERR_MESSAGE = -5 +} peer_err_t; + +/// Options for creating a peer. +typedef struct { + /// Whether the peer is a publisher or subscriber. + livekit_pb_signal_target_t target; + + /// ICE server list. + esp_peer_ice_server_cfg_t* server_list; + + /// Number of servers in the list. + int server_count; + + /// Weather to force the use of relay ICE candidates. + bool force_relay; + + /// Whether the peer is the primary peer. + /// @note This determines which peer controls the data channels. + bool is_primary; + + /// Media options used for creating SDP messages. + engine_media_options_t* media; + + /// Invoked when the peer's connection state changes. + void (*on_state_changed)(connection_state_t state, void *ctx); + + /// Invoked when an SDP message is available. This can be either + /// an offer or answer depending on target configuration. + void (*on_sdp)(const char *sdp, void *ctx); + + /// Invoked when a new ICE candidate is available. + void (*on_ice_candidate)(const char *candidate, void *ctx); + + /// Invoked when a data packet is received over the data channel. + void (*on_packet_received)(livekit_pb_data_packet_t* packet, void *ctx); + + /// Invoked when information about an incoming audio stream is available. + void (*on_audio_info)(esp_peer_audio_stream_info_t* info, void *ctx); + + /// Invoked when an audio frame is received. + void (*on_audio_frame)(esp_peer_audio_frame_t* frame, void *ctx); + + /// Invoked when information about an incoming video stream is available. + void (*on_video_info)(esp_peer_video_stream_info_t* info, void *ctx); + + /// Invoked when a video frame is received. + void (*on_video_frame)(esp_peer_video_frame_t* frame, void *ctx); + + /// Context pointer passed to the handlers. + void *ctx; +} peer_options_t; + +peer_err_t peer_create(peer_handle_t *handle, peer_options_t *options); +peer_err_t peer_destroy(peer_handle_t handle); + +peer_err_t peer_connect(peer_handle_t handle); +peer_err_t peer_disconnect(peer_handle_t handle); + +/// Handles an SDP message from the remote peer. +peer_err_t peer_handle_sdp(peer_handle_t handle, const char *sdp); + +/// Handles an ICE candidate from the remote peer. +peer_err_t peer_handle_ice_candidate(peer_handle_t handle, const char *candidate); + +/// Sends a data packet to the remote peer. +peer_err_t peer_send_data_packet(peer_handle_t handle, const livekit_pb_data_packet_t* packet, livekit_pb_data_packet_kind_t kind); + +/// Sends an audio frame to the remote peer. +/// @warning Only use on publisher peer. +peer_err_t peer_send_audio(peer_handle_t handle, esp_peer_audio_frame_t* frame); + +/// Sends a video frame to the remote peer. +/// @warning Only use on publisher peer. +peer_err_t peer_send_video(peer_handle_t handle, esp_peer_video_frame_t* frame); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/livekit/core/protocol.h b/components/livekit/core/protocol.h new file mode 100644 index 0000000..7ef6933 --- /dev/null +++ b/components/livekit/core/protocol.h @@ -0,0 +1,10 @@ + +#pragma once + +#include "pb_encode.h" +#include "pb_decode.h" + +#include "livekit_rtc.pb.h" +#include "livekit_models.pb.h" +#include "livekit_metrics.pb.h" +#include "timestamp.pb.h" \ No newline at end of file diff --git a/components/livekit/core/rpc_manager.c b/components/livekit/core/rpc_manager.c new file mode 100644 index 0000000..aa5ee14 --- /dev/null +++ b/components/livekit/core/rpc_manager.c @@ -0,0 +1,238 @@ +#include +#include +#include +#include "esp_timer.h" +#include "rpc_manager.h" + +static const char* TAG = "livekit_rpc"; + +KHASH_MAP_INIT_STR(handlers, livekit_rpc_handler_t) + +typedef struct { + rpc_manager_options_t options; + khash_t(handlers) *handlers; +} rpc_manager_t; + +static bool on_result(const livekit_rpc_result_t* result, void* ctx) +{ + if (result == NULL || ctx == NULL) { + ESP_LOGE(TAG, "Send result missing required arguments"); + return false; + } + rpc_manager_t *manager = (rpc_manager_t *)ctx; + if (result->payload != NULL && strlen(result->payload) >= LIVEKIT_RPC_MAX_PAYLOAD_BYTES) { + ESP_LOGE(TAG, "Payload too large"); + return false; + } + + bool is_ok = result->code == LIVEKIT_RPC_RESULT_OK; + if (is_ok && result->error_message != NULL) { + ESP_LOGW(TAG, "Error message provided for OK result, ignoring"); + } + + // Send response packet + livekit_pb_data_packet_t res_packet = { + .which_value = LIVEKIT_PB_DATA_PACKET_RPC_RESPONSE_TAG + }; + strncpy(res_packet.value.rpc_response.request_id, + result->id, + sizeof(res_packet.value.rpc_response.request_id)); + + if (is_ok) { + res_packet.value.rpc_response.which_value = LIVEKIT_PB_RPC_RESPONSE_PAYLOAD_TAG; + res_packet.value.rpc_response.value.payload = result->payload; + } else { + res_packet.value.rpc_response.which_value = LIVEKIT_PB_RPC_RESPONSE_ERROR_TAG; + res_packet.value.rpc_response.value.error.code = result->code; + res_packet.value.rpc_response.value.error.data = result->error_message; + } + if (!manager->options.send_packet(&res_packet, manager->options.ctx)) { + return false; + } + return true; +} + +static rpc_manager_err_t handle_request_packet(rpc_manager_t *manager, const livekit_pb_rpc_request_t* request, const char* caller_identity) +{ + if (caller_identity == NULL || request->method == NULL || strlen(request->id) != 36) { + ESP_LOGD(TAG, "Invalid request packet"); + return RPC_MANAGER_ERR_NONE; + } + ESP_LOGD(TAG, "RPC request: method=%s, id=%s", request->method, request->id); + + livekit_pb_data_packet_t ack_packet = { + .which_value = LIVEKIT_PB_DATA_PACKET_RPC_ACK_TAG + }; + strncpy(ack_packet.value.rpc_ack.request_id, + request->id, + sizeof(ack_packet.value.rpc_ack.request_id)); + + if (!manager->options.send_packet(&ack_packet, manager->options.ctx)) { + return RPC_MANAGER_ERR_SEND_FAILED; + } + + if (request->version != 1) { + ESP_LOGD(TAG, "Unsupported version: %" PRIu32, request->version); + livekit_pb_data_packet_t res_packet = { + .which_value = LIVEKIT_PB_DATA_PACKET_RPC_RESPONSE_TAG, + .value.rpc_response = { + .which_value = LIVEKIT_PB_RPC_RESPONSE_ERROR_TAG, + .value.error = { + .code = LIVEKIT_RPC_RESULT_UNSUPPORTED_VERSION + } + } + }; + strncpy(res_packet.value.rpc_response.request_id, + request->id, + sizeof(res_packet.value.rpc_response.request_id)); + + if (!manager->options.send_packet(&res_packet, manager->options.ctx)) { + return RPC_MANAGER_ERR_SEND_FAILED; + } + return RPC_MANAGER_ERR_NONE; + } + + khiter_t key = kh_get(handlers, manager->handlers, request->method); + if (key == kh_end(manager->handlers)) { + ESP_LOGD(TAG, "No handler registered for method '%s'", request->method); + livekit_pb_data_packet_t res_packet = { + .which_value = LIVEKIT_PB_DATA_PACKET_RPC_RESPONSE_TAG, + .value.rpc_response = { + .which_value = LIVEKIT_PB_RPC_RESPONSE_ERROR_TAG, + .value.error = { + .code = LIVEKIT_RPC_RESULT_UNSUPPORTED_METHOD + } + } + }; + strncpy(res_packet.value.rpc_response.request_id, + request->id, + sizeof(res_packet.value.rpc_response.request_id)); + + if (!manager->options.send_packet(&res_packet, manager->options.ctx)) { + return RPC_MANAGER_ERR_SEND_FAILED; + } + return RPC_MANAGER_ERR_NONE; + } + livekit_rpc_handler_t handler = kh_value(manager->handlers, key); + + livekit_rpc_invocation_t invocation = { + .id = (char *)request->id, + .method = request->method, + .caller_identity = (char *)caller_identity, + .payload = request->payload, + .send_result = on_result, + .ctx = manager + }; + + // TODO: Pass through context + + int64_t start_time = esp_timer_get_time(); + handler(&invocation, NULL); + + int64_t exec_duration = esp_timer_get_time() - start_time; + ESP_LOGD(TAG, "Handler for method '%s' took %" PRIu64 "us", request->method, exec_duration / 1000); + + // After, record should be deleted or be marked pending + + return RPC_MANAGER_ERR_NONE; +} + +static rpc_manager_err_t handle_response_packet(rpc_manager_t *manager, const livekit_pb_rpc_response_t* response) +{ + // TODO: Implement + return RPC_MANAGER_ERR_NONE; +} + +static rpc_manager_err_t handle_ack_packet(rpc_manager_t *manager, const livekit_pb_rpc_ack_t* ack) +{ + // TODO: Implement + return RPC_MANAGER_ERR_NONE; +} + +rpc_manager_err_t rpc_manager_create(rpc_manager_handle_t *handle, const rpc_manager_options_t *options) +{ + if (handle == NULL || + options == NULL || + options->on_result == NULL || + options->send_packet == NULL) { + return RPC_MANAGER_ERR_INVALID_ARG; + } + rpc_manager_t *rpc = (rpc_manager_t *)calloc(1, sizeof(rpc_manager_t)); + if (rpc == NULL) { + return RPC_MANAGER_ERR_NO_MEM; + } + + rpc->handlers = kh_init(handlers); + if (rpc->handlers == NULL) { + free(rpc); + return RPC_MANAGER_ERR_NO_MEM; + } + + rpc->options = *options; + *handle = (rpc_manager_handle_t)rpc; + return RPC_MANAGER_ERR_NONE; +} + +rpc_manager_err_t rpc_manager_destroy(rpc_manager_handle_t handle) +{ + if (handle == NULL) { + return RPC_MANAGER_ERR_INVALID_ARG; + } + rpc_manager_t *rpc = (rpc_manager_t *)handle; + free(rpc); + return RPC_MANAGER_ERR_NONE; +} + +rpc_manager_err_t rpc_manager_register(rpc_manager_handle_t handle, const char* method, livekit_rpc_handler_t handler) +{ + if (handle == NULL || method == NULL || handler == NULL) { + return RPC_MANAGER_ERR_INVALID_ARG; + } + rpc_manager_t *manager = (rpc_manager_t *)handle; + + int put_flag; + khiter_t key = kh_put(handlers, manager->handlers, method, &put_flag); + if (put_flag != 1) { + return RPC_MANAGER_ERR_INVALID_STATE; + } + kh_value(manager->handlers, key) = handler; + + return RPC_MANAGER_ERR_NONE; +} + +rpc_manager_err_t rpc_manager_unregister(rpc_manager_handle_t handle, const char* method) +{ + if (handle == NULL || method == NULL) { + return RPC_MANAGER_ERR_INVALID_ARG; + } + rpc_manager_t *manager = (rpc_manager_t *)handle; + + khiter_t key = kh_get(handlers, manager->handlers, method); + if (key == kh_end(manager->handlers)) { + return RPC_MANAGER_ERR_INVALID_STATE; + } + kh_del(handlers, manager->handlers, key); + + return RPC_MANAGER_ERR_NONE; +} + +rpc_manager_err_t rpc_manager_handle_packet(rpc_manager_handle_t handle, const livekit_pb_data_packet_t* packet) +{ + if (handle == NULL || packet == NULL) { + return RPC_MANAGER_ERR_INVALID_ARG; + } + rpc_manager_t *manager = (rpc_manager_t *)handle; + + switch (packet->which_value) { + case LIVEKIT_PB_DATA_PACKET_RPC_REQUEST_TAG: + return handle_request_packet(manager, &packet->value.rpc_request, packet->participant_identity); + case LIVEKIT_PB_DATA_PACKET_RPC_ACK_TAG: + return handle_ack_packet(manager, &packet->value.rpc_ack); + case LIVEKIT_PB_DATA_PACKET_RPC_RESPONSE_TAG: + return handle_response_packet(manager, &packet->value.rpc_response); + default: + ESP_LOGD(TAG, "Unhandled packet type"); + return RPC_MANAGER_ERR_INVALID_STATE; + } + return RPC_MANAGER_ERR_NONE; +} \ No newline at end of file diff --git a/components/livekit/core/rpc_manager.h b/components/livekit/core/rpc_manager.h new file mode 100644 index 0000000..ff039e3 --- /dev/null +++ b/components/livekit/core/rpc_manager.h @@ -0,0 +1,45 @@ + +#pragma once + +#include "livekit_rpc.h" +#include "protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *rpc_manager_handle_t; + +typedef enum { + RPC_MANAGER_ERR_NONE = 0, + RPC_MANAGER_ERR_INVALID_ARG = -1, + RPC_MANAGER_ERR_NO_MEM = -2, + RPC_MANAGER_ERR_INVALID_STATE = -3, + RPC_MANAGER_ERR_SEND_FAILED = -4, + RPC_MANAGER_ERR_REGISTRATION = -5, +} rpc_manager_err_t; + +typedef struct { + void (*on_result)(const livekit_rpc_result_t* result, void* ctx); + bool (*send_packet)(const livekit_pb_data_packet_t* packet, void *ctx); + void* ctx; +} rpc_manager_options_t; + +/// Creates a new RPC manager. +rpc_manager_err_t rpc_manager_create(rpc_manager_handle_t *handle, const rpc_manager_options_t *options); + +/// Destroys an RPC manager. +rpc_manager_err_t rpc_manager_destroy(rpc_manager_handle_t handle); + +/// Registers a handler for an RPC method. +rpc_manager_err_t rpc_manager_register(rpc_manager_handle_t handle, const char* method, livekit_rpc_handler_t handler); + +/// Unregisters a handler for an RPC method. +rpc_manager_err_t rpc_manager_unregister(rpc_manager_handle_t handle, const char* method); + +/// Handles an incoming RPC packet. +rpc_manager_err_t rpc_manager_handle_packet(rpc_manager_handle_t handle, const livekit_pb_data_packet_t* packet); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/livekit/core/signaling.c b/components/livekit/core/signaling.c new file mode 100644 index 0000000..fb48a8b --- /dev/null +++ b/components/livekit/core/signaling.c @@ -0,0 +1,429 @@ +#include +#include +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#include "esp_log.h" +#include "esp_peer_signaling.h" +#include "esp_netif.h" +#include "media_lib_os.h" +#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE +#include "esp_crt_bundle.h" +#endif +#include "esp_websocket_client.h" +#include "esp_tls.h" +#include "esp_timer.h" + +#include "protocol.h" +#include "signaling.h" +#include "url.h" +#include "utils.h" + +static const char *TAG = "livekit_signaling"; + +#define SIGNAL_WS_BUFFER_SIZE 20 * 1024 +#define SIGNAL_WS_RECONNECT_TIMEOUT_MS 1000 +#define SIGNAL_WS_NETWORK_TIMEOUT_MS 10000 +#define SIGNAL_WS_CLOSE_CODE 1000 +#define SIGNAL_WS_CLOSE_TIMEOUT_MS 250 + +typedef struct { + esp_websocket_client_handle_t ws; + signal_options_t options; + esp_timer_handle_t ping_timer; + + int32_t ping_interval_ms; + int32_t ping_timeout_ms; + int64_t rtt; +} signal_t; + +static void log_error_if_nonzero(const char *message, int error_code) +{ + if (error_code != 0) { + ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code); + } +} + +static signal_err_t send_request(signal_t *sg, livekit_pb_signal_request_t *request) +{ + // TODO: Optimize (use static buffer for small messages) + ESP_LOGD(TAG, "Sending request: type=%d", request->which_message); + + size_t encoded_size = 0; + if (!pb_get_encoded_size(&encoded_size, LIVEKIT_PB_SIGNAL_REQUEST_FIELDS, request)) { + return SIGNAL_ERR_MESSAGE; + } + uint8_t *enc_buf = (uint8_t *)malloc(encoded_size); + if (enc_buf == NULL) { + return SIGNAL_ERR_NO_MEM; + } + int ret = SIGNAL_ERR_NONE; + do { + pb_ostream_t stream = pb_ostream_from_buffer(enc_buf, encoded_size); + if (!pb_encode(&stream, LIVEKIT_PB_SIGNAL_REQUEST_FIELDS, request)) { + ESP_LOGE(TAG, "Failed to encode request"); + ret = SIGNAL_ERR_MESSAGE; + break; + } + if (esp_websocket_client_send_bin(sg->ws, + (const char *)enc_buf, + stream.bytes_written, + portMAX_DELAY) < 0) { + ESP_LOGE(TAG, "Failed to send request"); + ret = SIGNAL_ERR_MESSAGE; + break; + } + } while (0); + free(enc_buf); + return ret; +} + +static void send_ping(void *arg) +{ + signal_t *sg = (signal_t *)arg; + + livekit_pb_signal_request_t req = LIVEKIT_PB_SIGNAL_REQUEST_INIT_DEFAULT; + req.which_message = LIVEKIT_PB_SIGNAL_REQUEST_PING_REQ_TAG; + req.message.ping_req.timestamp = get_unix_time_ms(); + req.message.ping_req.rtt = sg->rtt; + + send_request(sg, &req); +} + +static void handle_res(signal_t *sg, livekit_pb_signal_response_t *res) +{ + switch (res->which_message) { + case LIVEKIT_PB_SIGNAL_RESPONSE_PONG_RESP_TAG: + livekit_pb_pong_t *pong = &res->message.pong_resp; + sg->rtt = get_unix_time_ms() - pong->last_ping_timestamp; + // TODO: Reset ping timeout + break; + case LIVEKIT_PB_SIGNAL_RESPONSE_REFRESH_TOKEN_TAG: + // TODO: Handle refresh token + break; + case LIVEKIT_PB_SIGNAL_RESPONSE_JOIN_TAG: + livekit_pb_join_response_t *join_res = &res->message.join; + ESP_LOGI(TAG, "Join: subscriber_primary=%d", join_res->subscriber_primary); + + sg->ping_interval_ms = join_res->ping_interval * 1000; + sg->ping_timeout_ms = join_res->ping_timeout * 1000; + esp_timer_start_periodic(sg->ping_timer, sg->ping_interval_ms * 1000); + + sg->options.on_join(join_res, sg->options.ctx); + break; + case LIVEKIT_PB_SIGNAL_RESPONSE_LEAVE_TAG: + livekit_pb_leave_request_t *leave_res = &res->message.leave; + ESP_LOGI(TAG, "Leave: reason=%d, action=%d", leave_res->reason, leave_res->action); + esp_timer_stop(sg->ping_timer); + sg->options.on_leave(leave_res->reason, leave_res->action, sg->options.ctx); + break; + case LIVEKIT_PB_SIGNAL_RESPONSE_ROOM_UPDATE_TAG: + livekit_pb_room_update_t *room_update = &res->message.room_update; + if (!room_update->has_room) break; + sg->options.on_room_update(&room_update->room, sg->options.ctx); + break; + case LIVEKIT_PB_SIGNAL_RESPONSE_UPDATE_TAG: + livekit_pb_participant_update_t *participant_update = &res->message.update; + for (int i = 0; i < participant_update->participants_count; i++) { + sg->options.on_participant_update(&participant_update->participants[i], sg->options.ctx); + } + break; + case LIVEKIT_PB_SIGNAL_RESPONSE_OFFER_TAG: + livekit_pb_session_description_t *offer = &res->message.offer; + ESP_LOGI(TAG, "Offer: id=%" PRIu32 "\n%s", offer->id, offer->sdp); + sg->options.on_offer(offer->sdp, sg->options.ctx); + break; + case LIVEKIT_PB_SIGNAL_RESPONSE_ANSWER_TAG: + livekit_pb_session_description_t *answer = &res->message.answer; + ESP_LOGI(TAG, "Answer: id=%" PRIu32 "\n%s", answer->id, answer->sdp); + sg->options.on_answer(answer->sdp, sg->options.ctx); + break; + case LIVEKIT_PB_SIGNAL_RESPONSE_TRICKLE_TAG: + livekit_pb_trickle_request_t *trickle = &res->message.trickle; + if (trickle->candidate_init == NULL) { + ESP_LOGE(TAG, "Trickle candidate_init is NULL"); + break; + } + cJSON *candidate_init = NULL; + do { + candidate_init = cJSON_Parse(trickle->candidate_init); + if (candidate_init == NULL) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) { + ESP_LOGE(TAG, "Failed to parse candidate_init: %s", error_ptr); + } + break; + } + cJSON *candidate = cJSON_GetObjectItemCaseSensitive(candidate_init, "candidate"); + if (!cJSON_IsString(candidate) || (candidate->valuestring == NULL)) { + ESP_LOGE(TAG, "Missing candidate key in candidate_init"); + break; + } + ESP_LOGI(TAG, "Trickle: target=%d, final=%d\n%s", + trickle->target, + trickle->final, + candidate->valuestring + ); + sg->options.on_trickle(candidate->valuestring, trickle->target, sg->options.ctx); + } while (0); + cJSON_Delete(candidate_init); + break; + default: + break; + } + pb_release(LIVEKIT_PB_SIGNAL_RESPONSE_FIELDS, res); +} + +static void on_data(signal_t *sg, const char *data, size_t len) +{ + ESP_LOGD(TAG, "Incoming res: %d byte(s)", len); + livekit_pb_signal_response_t res = {}; + pb_istream_t stream = pb_istream_from_buffer((const pb_byte_t *)data, len); + if (!pb_decode(&stream, LIVEKIT_PB_SIGNAL_RESPONSE_FIELDS, &res)) { + ESP_LOGE(TAG, "Failed to decode res: %s", stream.errmsg); + return; + } + handle_res(sg, &res); +} + +static void on_ws_event(void *ctx, esp_event_base_t base, int32_t event_id, void *event_data) +{ + assert(ctx != NULL); + signal_t *sg = (signal_t *)ctx; + esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; + + switch (event_id) { + case WEBSOCKET_EVENT_CONNECTED: + ESP_LOGD(TAG, "Signaling connected"); + sg->options.on_state_changed(CONNECTION_STATE_CONNECTED, sg->options.ctx); + break; + case WEBSOCKET_EVENT_DISCONNECTED: + ESP_LOGD(TAG, "Signaling disconnected"); + + // In the normal case, this timer will be stopped when the leave message is received. + // However, if the connection is lost, we need to stop the timer manually. + esp_timer_stop(sg->ping_timer); + + log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code); + if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) { + log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err); + log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err); + log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno); + } + sg->options.on_state_changed(CONNECTION_STATE_DISCONNECTED, sg->options.ctx); + break; + case WEBSOCKET_EVENT_DATA: + if (data->op_code != WS_TRANSPORT_OPCODES_BINARY) { + ESP_LOGD(TAG, "Message: opcode=%d, len=%d", data->op_code, data->data_len); + break; + } + if (data->data_len < 1) break; + on_data(sg, data->data_ptr, data->data_len); + break; + case WEBSOCKET_EVENT_ERROR: + ESP_LOGE(TAG, "Failed to connect to server"); + esp_timer_stop(sg->ping_timer); + + log_error_if_nonzero("HTTP status code", data->error_handle.esp_ws_handshake_status_code); + if (data->error_handle.error_type == WEBSOCKET_ERROR_TYPE_TCP_TRANSPORT) { + log_error_if_nonzero("reported from esp-tls", data->error_handle.esp_tls_last_esp_err); + log_error_if_nonzero("reported from tls stack", data->error_handle.esp_tls_stack_err); + log_error_if_nonzero("captured as transport's socket errno", data->error_handle.esp_transport_sock_errno); + } + sg->options.on_state_changed(CONNECTION_STATE_FAILED, sg->options.ctx); + break; + default: break; + } +} + +signal_err_t signal_create(signal_handle_t *handle, signal_options_t *options) +{ + if (options == NULL || handle == NULL) { + return SIGNAL_ERR_INVALID_ARG; + } + + if (options->on_state_changed == NULL || + options->on_join == NULL || + options->on_leave == NULL || + options->on_room_update == NULL || + options->on_participant_update == NULL || + options->on_offer == NULL || + options->on_answer == NULL || + options->on_trickle == NULL + ) { + ESP_LOGE(TAG, "Missing required event handlers"); + return SIGNAL_ERR_INVALID_ARG; + } + + signal_t *sg = calloc(1, sizeof(signal_t)); + if (sg == NULL) { + return SIGNAL_ERR_NO_MEM; + } + sg->options = *options; + + esp_timer_create_args_t timer_args = { + .callback = send_ping, + .arg = sg, + .name = "ping" + }; + if (esp_timer_create(&timer_args, &sg->ping_timer) != ESP_OK) { + ESP_LOGE(TAG, "Failed to create ping timer"); + free(sg); + return SIGNAL_ERR_OTHER; + } + + // URL will be set on connect + static esp_websocket_client_config_t ws_config = { + .buffer_size = SIGNAL_WS_BUFFER_SIZE, + .disable_pingpong_discon = true, + .reconnect_timeout_ms = SIGNAL_WS_RECONNECT_TIMEOUT_MS, + .network_timeout_ms = SIGNAL_WS_NETWORK_TIMEOUT_MS, +#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE + .crt_bundle_attach = esp_crt_bundle_attach +#endif + }; + sg->ws = esp_websocket_client_init(&ws_config); + if (sg->ws == NULL) { + ESP_LOGE(TAG, "Failed to initialize WebSocket client"); + esp_timer_delete(sg->ping_timer); + free(sg); + return SIGNAL_ERR_WEBSOCKET; + } + esp_websocket_register_events( + sg->ws, + WEBSOCKET_EVENT_ANY, + on_ws_event, + (void *)sg + ); + *handle = sg; + return SIGNAL_ERR_NONE; +} + +signal_err_t signal_destroy(signal_handle_t handle) +{ + if (handle == NULL) { + return SIGNAL_ERR_INVALID_ARG; + } + signal_t *sg = (signal_t *)handle; + esp_timer_delete(sg->ping_timer); + signal_close(handle); + esp_websocket_client_destroy(sg->ws); + free(sg); + return SIGNAL_ERR_NONE; +} + +signal_err_t signal_connect(signal_handle_t handle, const char* server_url, const char* token) +{ + if (server_url == NULL || token == NULL || handle == NULL) { + return SIGNAL_ERR_INVALID_ARG; + } + signal_t *sg = (signal_t *)handle; + + char* url; + if (!url_build(server_url, token, &url)) { + return SIGNAL_ERR_INVALID_URL; + } + esp_websocket_client_set_uri(sg->ws, url); + free(url); + + if (esp_websocket_client_start(sg->ws) != ESP_OK) { + ESP_LOGE(TAG, "Failed to start WebSocket"); + return SIGNAL_ERR_WEBSOCKET; + } + return SIGNAL_ERR_NONE; +} + +signal_err_t signal_close(signal_handle_t handle) +{ + if (handle == NULL) { + return SIGNAL_ERR_INVALID_ARG; + } + signal_t *sg = (signal_t *)handle; + + esp_timer_stop(sg->ping_timer); + if (esp_websocket_client_is_connected(sg->ws) && + esp_websocket_client_close(sg->ws, pdMS_TO_TICKS(SIGNAL_WS_CLOSE_TIMEOUT_MS)) != ESP_OK) { + ESP_LOGE(TAG, "Failed to close WebSocket"); + return SIGNAL_ERR_WEBSOCKET; + } + return SIGNAL_ERR_NONE; +} + +signal_err_t signal_send_leave(signal_handle_t handle) +{ + if (handle == NULL) { + return SIGNAL_ERR_INVALID_ARG; + } + signal_t *sg = (signal_t *)handle; + livekit_pb_signal_request_t req = LIVEKIT_PB_SIGNAL_REQUEST_INIT_ZERO; + req.which_message = LIVEKIT_PB_SIGNAL_REQUEST_LEAVE_TAG; + + livekit_pb_leave_request_t leave = { + .reason = LIVEKIT_PB_DISCONNECT_REASON_CLIENT_INITIATED, + .action = LIVEKIT_PB_LEAVE_REQUEST_ACTION_DISCONNECT + }; + req.message.leave = leave; + return send_request(sg, &req); +} + +signal_err_t signal_send_answer(signal_handle_t handle, const char *sdp) +{ + if (sdp == NULL || handle == NULL) { + return SIGNAL_ERR_INVALID_ARG; + } + signal_t *sg = (signal_t *)handle; + livekit_pb_signal_request_t req = LIVEKIT_PB_SIGNAL_REQUEST_INIT_ZERO; + + livekit_pb_session_description_t desc = { + .type = "answer", + .sdp = (char *)sdp + }; + req.which_message = LIVEKIT_PB_SIGNAL_REQUEST_ANSWER_TAG; + req.message.answer = desc; + return send_request(sg, &req); +} + +signal_err_t signal_send_offer(signal_handle_t handle, const char *sdp) +{ + if (sdp == NULL || handle == NULL) { + return SIGNAL_ERR_INVALID_ARG; + } + signal_t *sg = (signal_t *)handle; + livekit_pb_signal_request_t req = LIVEKIT_PB_SIGNAL_REQUEST_INIT_ZERO; + + livekit_pb_session_description_t desc = { + .type = "offer", + .sdp = (char *)sdp + }; + req.which_message = LIVEKIT_PB_SIGNAL_REQUEST_OFFER_TAG; + req.message.offer = desc; + return send_request(sg, &req); +} + +signal_err_t signal_send_add_track(signal_handle_t handle, livekit_pb_add_track_request_t *add_track_req) +{ + if (handle == NULL || add_track_req == NULL) { + return SIGNAL_ERR_INVALID_ARG; + } + signal_t *sg = (signal_t *)handle; + livekit_pb_signal_request_t req = LIVEKIT_PB_SIGNAL_REQUEST_INIT_ZERO; + req.which_message = LIVEKIT_PB_SIGNAL_REQUEST_ADD_TRACK_TAG; + req.message.add_track = *add_track_req; + return send_request(sg, &req); +} + +signal_err_t signal_send_update_subscription(signal_handle_t handle, const char *sid, bool subscribe) +{ + if (sid == NULL || handle == NULL) { + return SIGNAL_ERR_INVALID_ARG; + } + signal_t *sg = (signal_t *)handle; + livekit_pb_signal_request_t req = LIVEKIT_PB_SIGNAL_REQUEST_INIT_ZERO; + + livekit_pb_update_subscription_t subscription = { + .track_sids = (char*[]){(char*)sid}, + .track_sids_count = 1, + .subscribe = subscribe + }; + req.which_message = LIVEKIT_PB_SIGNAL_REQUEST_SUBSCRIPTION_TAG; + req.message.subscription = subscription; + return send_request(sg, &req); +} \ No newline at end of file diff --git a/components/livekit/core/signaling.h b/components/livekit/core/signaling.h new file mode 100644 index 0000000..f528e34 --- /dev/null +++ b/components/livekit/core/signaling.h @@ -0,0 +1,54 @@ + +#pragma once + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *signal_handle_t; + +typedef enum { + SIGNAL_ERR_NONE = 0, + SIGNAL_ERR_INVALID_ARG = -1, + SIGNAL_ERR_NO_MEM = -2, + SIGNAL_ERR_WEBSOCKET = -3, + SIGNAL_ERR_INVALID_URL = -4, + SIGNAL_ERR_MESSAGE = -5, + SIGNAL_ERR_OTHER = -6, + // TODO: Add more error cases as needed +} signal_err_t; + +typedef struct { + void* ctx; + void (*on_state_changed)(connection_state_t state, void *ctx); + void (*on_join)(livekit_pb_join_response_t *join_res, void *ctx); + void (*on_leave)(livekit_pb_disconnect_reason_t reason, livekit_pb_leave_request_action_t action, void *ctx); + void (*on_room_update)(const livekit_pb_room_t* info, void *ctx); + void (*on_participant_update)(const livekit_pb_participant_info_t* info, void *ctx); + void (*on_answer)(const char *sdp, void *ctx); + void (*on_offer)(const char *sdp, void *ctx); + void (*on_trickle)(const char *ice_candidate, livekit_pb_signal_target_t target, void *ctx); +} signal_options_t; + +signal_err_t signal_create(signal_handle_t *handle, signal_options_t *options); +signal_err_t signal_destroy(signal_handle_t handle); + +/// Establishes the WebSocket connection +/// @note This function will close the existing connection if already connected. +signal_err_t signal_connect(signal_handle_t handle, const char* server_url, const char* token); + +/// Closes the WebSocket connection +signal_err_t signal_close(signal_handle_t handle); + +signal_err_t signal_send_leave(signal_handle_t handle); +signal_err_t signal_send_offer(signal_handle_t handle, const char *sdp); +signal_err_t signal_send_answer(signal_handle_t handle, const char *sdp); + +signal_err_t signal_send_add_track(signal_handle_t handle, livekit_pb_add_track_request_t *req); +signal_err_t signal_send_update_subscription(signal_handle_t handle, const char *sid, bool subscribe); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/livekit/core/system.c b/components/livekit/core/system.c new file mode 100644 index 0000000..dbf134f --- /dev/null +++ b/components/livekit/core/system.c @@ -0,0 +1,96 @@ +#include +#include "esp_log.h" +#include "webrtc_utils_time.h" +#include "media_lib_os.h" +#include "media_lib_adapter.h" +#include "system.h" +#include "peer.h" +#include "engine.h" + +#define VIDEO_ENCODE_THREAD_NAME "venc" +#define AUDIO_ENCODE_THREAD_NAME "aenc" +#define AUDIO_DECODE_THREAD_NAME "Adec" +#define AEC_SRC_READ_THREAD_NAME "SrcRead" +#define AEC_BUFFER_IN_THREAD_NAME "buffer_in" + +static const char *TAG = "livekit_system"; +static bool is_media_lib_setup = false; + +static void thread_scheduler(const char *thread_name, media_lib_thread_cfg_t *thread_cfg) +{ + ESP_LOGD(TAG, "Scheduling thread '%s'", thread_name); + + // LiveKit threads + if (strncmp(thread_name, PEER_THREAD_NAME_PREFIX, strlen(PEER_THREAD_NAME_PREFIX)) == 0) { + thread_cfg->stack_size = 25 * 1024; + thread_cfg->priority = 18; + thread_cfg->core_id = 1; + return; + } + if (strcmp(thread_name, STREAM_THREAD_NAME) == 0) { + thread_cfg->stack_size = 4 * 1024; + thread_cfg->priority = 15; + thread_cfg->core_id = 1; + return; + } + + // Media lib threads + if (strcmp(thread_name, AUDIO_DECODE_THREAD_NAME) == 0) { + thread_cfg->stack_size = 40 * 1024; + thread_cfg->priority = 10; + thread_cfg->core_id = 1; + return; + } + if (strcmp(thread_name, AUDIO_ENCODE_THREAD_NAME) == 0) { + // Required for Opus + thread_cfg->stack_size = 40 * 1024; + thread_cfg->priority = 10; + return; + } + if (strcmp(thread_name, AEC_SRC_READ_THREAD_NAME) == 0) { + thread_cfg->stack_size = 40 * 1024; + thread_cfg->priority = 16; + thread_cfg->core_id = 0; + return; + } + if (strcmp(thread_name, AEC_BUFFER_IN_THREAD_NAME) == 0) { + thread_cfg->stack_size = 6 * 1024; + thread_cfg->priority = 10; + thread_cfg->core_id = 0; + return; + } + if (strcmp(thread_name, VIDEO_ENCODE_THREAD_NAME) == 0) { +#if CONFIG_IDF_TARGET_ESP32S3 + thread_cfg->stack_size = 20 * 1024; +#endif + thread_cfg->priority = 10; + return; + } +} + +bool system_setup_media_lib(void) +{ + esp_err_t ret = media_lib_add_default_adapter(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to setup media lib"); + return false; + } + media_lib_thread_set_schedule_cb(thread_scheduler); + is_media_lib_setup = true; + return true; +} + +bool system_is_media_lib_setup(void) +{ + return is_media_lib_setup; +} + +bool system_sync_time(void) +{ + esp_err_t ret = webrtc_utils_time_sync_init(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to sync time"); + return false; + } + return true; +} \ No newline at end of file diff --git a/components/livekit/core/system.h b/components/livekit/core/system.h new file mode 100644 index 0000000..a8bd7c0 --- /dev/null +++ b/components/livekit/core/system.h @@ -0,0 +1,15 @@ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +bool system_setup_media_lib(void); +bool system_is_media_lib_setup(void); + +bool system_sync_time(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/livekit/core/url.c b/components/livekit/core/url.c new file mode 100644 index 0000000..e749075 --- /dev/null +++ b/components/livekit/core/url.c @@ -0,0 +1,67 @@ +#include +#include +#include "esp_log.h" +#include "esp_idf_version.h" +#include "esp_chip_info.h" + +#include "url.h" + +static const char *TAG = "livekit_url"; + +#define URL_PARAM_SDK "esp32" +#define URL_PARAM_VERSION LIVEKIT_SDK_VERSION +#define URL_PARAM_OS "idf" +#define URL_PARAM_PROTOCOL "1" + +// TODO: For now, we use a protocol version that does not support subscriber primary. +// This is to get around a limitation with re-negotiation. + +#define URL_FORMAT "%s%srtc?" \ + "sdk=" URL_PARAM_SDK \ + "&version=" URL_PARAM_VERSION \ + "&os=" URL_PARAM_OS \ + "&os_version=%s" \ + "&device_model=%d" \ + "&auto_subscribe=false" \ + "&protocol=" URL_PARAM_PROTOCOL \ + "&access_token=%s" // Keep at the end for log redaction + +bool url_build(const char *server_url, const char *token, char **out_url) +{ + if (server_url == NULL || token == NULL || out_url == NULL) { + return false; + } + size_t server_url_len = strlen(server_url); + if (server_url_len < 1) { + ESP_LOGE(TAG, "Server URL cannot be empty"); + return false; + } + if (strncmp(server_url, "ws://", 5) != 0 && strncmp(server_url, "wss://", 6) != 0) { + ESP_LOGE(TAG, "Unsupported URL scheme"); + return false; + } + // Do not add a trailing slash if the URL already has one + const char *separator = server_url[server_url_len - 1] == '/' ? "" : "/"; + + // Get chip and OS information + esp_chip_info_t chip_info; + esp_chip_info(&chip_info); + int model_code = chip_info.model; + const char* idf_version = esp_get_idf_version(); + + int final_len = asprintf(out_url, URL_FORMAT, + server_url, + separator, + idf_version, + model_code, + token + ); + if (*out_url == NULL) { + return false; + } + // Token is redacted from logging for security + ESP_LOGI(TAG, "Built signaling URL: %.*s[REDACTED]", + (int)((size_t)final_len - strlen(token)), + *out_url); + return true; +} \ No newline at end of file diff --git a/components/livekit/core/url.h b/components/livekit/core/url.h new file mode 100644 index 0000000..43630c7 --- /dev/null +++ b/components/livekit/core/url.h @@ -0,0 +1,20 @@ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Constructs a signaling URL. +/// @param server_url The server URL beginning with ws:// or wss://. +/// @param token Access token. +/// @param out_url[out] The output URL. +/// @return True if the URL is constructed successfully, false otherwise. +/// @note The caller is responsible for freeing the output URL. +bool url_build(const char *server_url, const char *token, char **out_url); + +#ifdef __cplusplus +} +#endif diff --git a/components/livekit/core/utils.c b/components/livekit/core/utils.c new file mode 100644 index 0000000..4f6339e --- /dev/null +++ b/components/livekit/core/utils.c @@ -0,0 +1,9 @@ +#include + +#include "utils.h" + +int64_t get_unix_time_ms(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (int64_t)tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); +} \ No newline at end of file diff --git a/components/livekit/core/utils.h b/components/livekit/core/utils.h new file mode 100644 index 0000000..4b0158c --- /dev/null +++ b/components/livekit/core/utils.h @@ -0,0 +1,14 @@ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int64_t get_unix_time_ms(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/livekit/idf_component.yml b/components/livekit/idf_component.yml new file mode 100644 index 0000000..9a336e1 --- /dev/null +++ b/components/livekit/idf_component.yml @@ -0,0 +1,33 @@ +description: LiveKit ESP-32 SDK +tags: [webrtc, media, agents] +url: "https://livekit.io" +repository: "https://github.com/livekit/client-sdk-esp32" +issues: "https://github.com/livekit/client-sdk-esp32/issues" +discussion: "https://livekit.io/join-slack" +version: 0.1.0 +dependencies: + idf: ">=5.4" + espressif/esp_websocket_client: ^1.4.0 + media_lib_sal: + path: ../third_party/esp-webrtc-solution/components/media_lib_sal + esp_webrtc: + path: ../third_party/esp-webrtc-solution/components/esp_webrtc + peer_default: + path: ../third_party/esp-webrtc-solution/components/esp_webrtc/impl/peer_default + public: true # Required to prevent linker error + webrtc_utils: + path: ../third_party/esp-webrtc-solution/components/webrtc_utils + capture_audio_enc: + path: ../third_party/esp-webrtc-solution/components/esp_capture/src/impl/capture_audio_enc + capture_video_enc: + path: ../third_party/esp-webrtc-solution/components/esp_capture/src/impl/capture_video_enc + capture_audio_src: + path: ../third_party/esp-webrtc-solution/components/esp_capture/src/impl/capture_audio_src + capture_video_src: + path: ../third_party/esp-webrtc-solution/components/esp_capture/src/impl/capture_video_src + nanopb: + path: ../third_party/nanopb + khash: + path: ../third_party/khash +files: + use_gitignore: true \ No newline at end of file diff --git a/components/livekit/include/livekit.h b/components/livekit/include/livekit.h new file mode 100644 index 0000000..197d061 --- /dev/null +++ b/components/livekit/include/livekit.h @@ -0,0 +1,424 @@ + +#pragma once + +#include "media_lib_os.h" +#include "esp_capture.h" +#include "av_render.h" + +#include "livekit_rpc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Error type returned by all public functions. +typedef enum { + LIVEKIT_ERR_NONE = 0, ///< No error + LIVEKIT_ERR_INVALID_ARG = -1, ///< Invalid argument + LIVEKIT_ERR_NO_MEM = -2, ///< Dynamic memory allocation failed + LIVEKIT_ERR_ENGINE = -3, ///< Engine + LIVEKIT_ERR_OTHER = -4, ///< Other error + LIVEKIT_ERR_INVALID_STATE = -5, ///< Invalid state + LIVEKIT_ERR_SYSTEM_INIT = -6 ///< System not initialized +} livekit_err_t; + +/// Connection state of a room. +/// @ingroup Connection +typedef enum { + LIVEKIT_CONNECTION_STATE_DISCONNECTED = 0, ///< Disconnected + LIVEKIT_CONNECTION_STATE_CONNECTING = 1, ///< Establishing connection + LIVEKIT_CONNECTION_STATE_CONNECTED = 2, ///< Connected + LIVEKIT_CONNECTION_STATE_RECONNECTING = 3, ///< Connection was previously established, but was lost + LIVEKIT_CONNECTION_STATE_FAILED = 4 ///< Connection failed +} livekit_connection_state_t; + +/// Video codec to use within a room. +typedef enum { + LIVEKIT_VIDEO_CODEC_NONE = 0, ///< No video codec set + LIVEKIT_VIDEO_CODEC_H264 = 1 ///< H.264 (AVC) +} livekit_video_codec_t; + +/// Audio codec to use within a room. +typedef enum { + LIVEKIT_AUDIO_CODEC_NONE = 0, ///< No audio codec set + LIVEKIT_AUDIO_CODEC_G711A = 1, ///< G.711 A-law (PCMA) + LIVEKIT_AUDIO_CODEC_G711U = 2, ///< G.711 u-law (PCMU) + LIVEKIT_AUDIO_CODEC_OPUS = 3 ///< Opus +} livekit_audio_codec_t; + +/// Media mode for the room. +typedef enum { + LIVEKIT_MEDIA_TYPE_NONE = 0, ///< No media + LIVEKIT_MEDIA_TYPE_AUDIO = (1 << 0), ///< Audio only + LIVEKIT_MEDIA_TYPE_VIDEO = (1 << 1), ///< Video only + LIVEKIT_MEDIA_TYPE_BOTH = LIVEKIT_MEDIA_TYPE_AUDIO | LIVEKIT_MEDIA_TYPE_VIDEO, ///< Audio and video +} livekit_media_kind_t; + +/// Options for the video encoder. +typedef struct { + livekit_video_codec_t codec; ///< Codec to use for encoding + int width; ///< Output frame width in pixels + int height; ///< Output frame height in pixels + int fps; ///< Output frame per second +} livekit_video_encode_options_t; + +/// Options for the audio encoder. +typedef struct { + livekit_audio_codec_t codec; ///< Codec to use for encoding + uint32_t sample_rate; ///< Output sample rate in Hz + uint8_t channel_count; ///< Output number of channels +} livekit_audio_encode_options_t; + +/// Options for publishing media. +typedef struct { + /// Kind of media that can be published. + livekit_media_kind_t kind; + + /// Video encoder options. + /// @note Only required if the room publishes video. + livekit_video_encode_options_t video_encode; + + /// Audio encoder options. + /// @note Only required if the room publishes audio. + livekit_audio_encode_options_t audio_encode; + + /// Capturer to use for obtaining media to publish. + /// @note Only required if the room publishes media. + esp_capture_handle_t capturer; +} livekit_pub_options_t; + +/// Options for subscribing to media. +typedef struct { + /// Kind of media that can be subscribed to. + livekit_media_kind_t kind; + + /// Renderer to use for subscribed media tracks. + /// @note Only required if the room subscribes to media. + av_render_handle_t renderer; +} livekit_sub_options_t; + +/// Payload containing a pointer to data and its size. +/// @ingroup DataPackets +typedef struct { + uint8_t *bytes; ///< Pointer to data + size_t size; ///< Size of the data +} livekit_data_payload_t; + +/// Information about a data packet received from a remote participant +/// passed to @ref livekit_room_options_t::on_data_received. +/// @ingroup DataPackets +typedef struct { + /// Received data. + livekit_data_payload_t payload; + + /// Topic the data was sent under if specified by the sender. + char* topic; + + /// Identity of the participant who sent the data. + char* sender_identity; +} livekit_data_received_t; + +/// Information about a room. +/// @ingroup Info +typedef struct { + /// Unique identifier generated by LiveKit server. + char* sid; + /// Optional display name. + char* name; + /// Optional arbitrary metadata in string format. + char* metadata; + /// Number of participants in the room, including the local participant. + uint32_t participant_count; + /// Whether the room is actively being recorded. + bool active_recording; +} livekit_room_info_t; + +/// Participant kind. +/// @ingroup Info +typedef enum { + /// A regular participant, typically an end-user in your application. + LIVEKIT_PARTICIPANT_KIND_STANDARD = 0, + /// A server-side process that is ingesting media into the session + /// using [LiveKit Ingress](https://docs.livekit.io/home/ingress/overview/). + LIVEKIT_PARTICIPANT_KIND_INGRESS = 1, + /// A server-side process that is recording the session using + /// [LiveKit Egress](https://docs.livekit.io/home/egress/overview/). + LIVEKIT_PARTICIPANT_KIND_EGRESS = 2, + /// A telephony user connected via [SIP](https://docs.livekit.io/sip/). + LIVEKIT_PARTICIPANT_KIND_SIP = 3, + /// An agent spawned with the [Agents Framework](https://docs.livekit.io/agents/). + LIVEKIT_PARTICIPANT_KIND_AGENT = 4 +} livekit_participant_kind_t; + +/// Participant state. +/// @ingroup Info +typedef enum { + /// The participant is in the process of joining the room. + LIVEKIT_PARTICIPANT_STATE_JOINING = 0, + /// The participant has joined the room but is not able to publish or subscribe to media yet. + LIVEKIT_PARTICIPANT_STATE_JOINED = 1, + /// The participant is connected to the room and can publish or subscribe to media. + LIVEKIT_PARTICIPANT_STATE_ACTIVE = 2, + /// The participant has disconnected from the room. + LIVEKIT_PARTICIPANT_STATE_DISCONNECTED = 3 +} livekit_participant_state_t; + +/// Information about a participant in a room. +/// @ingroup Info +typedef struct { + /// Unique identifier generated by LiveKit server. + char* sid; + /// Unique identity of the participant, as specified when connecting. + char* identity; + /// Optional display name. + char* name; + /// Optional arbitrary metadata in string format. + char* metadata; + /// The participant's kind (e.g. standard, agent, etc.). + livekit_participant_kind_t kind; + /// The current state of the participant. + livekit_participant_state_t state; +} livekit_participant_info_t; + +/// Options for creating a room. +/// +/// This is the main way a room is configured. It is passed to +/// @ref livekit_room_create. +/// +/// @ingroup Lifecycle +/// +typedef struct { + /// Options for publishing media. + /// @note Only required if the room publishes media. + livekit_pub_options_t publish; + + /// Options for subscribing to media. + /// @note Only required if the room subscribes to media. + livekit_sub_options_t subscribe; + + /// Handler for when the room's connection state changes. + /// @see Connection + void (*on_state_changed)(livekit_connection_state_t state, void* ctx); + + /// Handler for when an RPC method invoked with @ref livekit_room_rpc_invoke returns a result. + /// @see RPC + void (*on_rpc_result)(const livekit_rpc_result_t* result, void* ctx); + + /// Handler for data packets received from remote participants. + /// @see DataPackets + void (*on_data_received)(const livekit_data_received_t* data, void* ctx); + + /// Handler for when room information is received. + /// @see Info + void (*on_room_info)(const livekit_room_info_t* info, void* ctx); + + /// Handler for when participant information is received. + /// @see Info + void (*on_participant_info)(const livekit_participant_info_t* info, void* ctx); + + /// User context passed to all handlers. + void* ctx; +} livekit_room_options_t; + +/// @defgroup System System Initialization +/// Perform required one-time system initialization. +/// @{ + +/// Performs one-time system initialization. +/// +/// @return @ref LIVEKIT_ERR_NONE if successful, otherwise LIVEKIT_ERR_SYSTEM_INIT. +/// +/// Invoke this function early in the application's main function before +/// creating a room. Internally, this will setup the media library's thread scheduler. +/// +livekit_err_t livekit_system_init(void); + +/// @} + +/// @defgroup Lifecycle +/// Create and destroy room objects. +/// @{ + +/// Handle to a room object. +typedef void *livekit_room_handle_t; + +/// Creates a room. +/// @param handle[out] Room handle. +/// @param options[in] Options for the new room. +/// @return @ref LIVEKIT_ERR_NONE if successful, otherwise an error code. +livekit_err_t livekit_room_create(livekit_room_handle_t *handle, const livekit_room_options_t *options); + +/// Destroys a room. +/// @param handle[in] Room handle. +/// @warning For normal connection closure, disconnect the room first using +/// @ref livekit_room_close before destroying the room. +/// @return @ref LIVEKIT_ERR_NONE if successful, otherwise an error code. +livekit_err_t livekit_room_destroy(livekit_room_handle_t handle); + +/// @} + +/// @defgroup Connection +/// Connect and disconnect from a room. +/// +/// The connection state of a room can be monitored by setting a handler for +/// @ref livekit_room_options_t::on_state_changed. +/// +/// @{ + +/// Connects to a room asynchronously. +/// +/// @param handle[in] Room handle. +/// @param server_url[in] URL of the LiveKit server beginning with "wss://" or "ws://". +/// @param token[in] Server-generated token for authentication. +/// @return @ref LIVEKIT_ERR_NONE, otherwise an error code. +/// +livekit_err_t livekit_room_connect(livekit_room_handle_t handle, const char *server_url, const char *token); + +/// Disconnects from a room asynchronously. +/// +/// @param handle[in] Room handle. +/// @return @ref LIVEKIT_ERR_NONE if successful, otherwise an error code. +/// +livekit_err_t livekit_room_close(livekit_room_handle_t handle); + +/// Gets the current connection state of a room. +/// +/// @param handle[in] Room handle. +/// @return Current connection state. +/// +livekit_connection_state_t livekit_room_get_state(livekit_room_handle_t handle); + +/// Gets a string representation of a connection state. +/// +/// @param state[in] Connection state. +/// @return String representation of the connection state. +/// +const char* livekit_connection_state_str(livekit_connection_state_t state); + +/// @} + +/// @defgroup Info Room & Participant Info +/// +/// Get information about a room and its participants. +/// +/// If you have worked with LiveKit before on other platforms, you will be familiar +/// with the accessors on the room object and participant objects for getting information about them (e.g. sid, name, etc.). +/// In this SDK, to avoid unnecessary persistent memory allocations that aren't needed for all applications, +/// this information is instead provided through the following handlers which are invoked when the corresponding +/// information is first received or subsequently updated: +/// +/// - **Room**: @ref livekit_room_options_t::on_room_info +/// - **Participant**: @ref livekit_room_options_t::on_participant_info +/// +/// In your implementation, you are free to examine the fields in the provided info struct and copy +/// any information you need to keep for later use; the pointer to this struct is only valid until +/// the handler returns. +/// +/// The following example demonstrates how you can define an `on_participant_info` handler +/// to perform some action when a participant named "Jon" joins the room: +/// +/// @code +/// static void on_participant_info(const livekit_participant_info_t* info, void* ctx) +/// { +/// if (info->state == LIVEKIT_PARTICIPANT_STATE_ACTIVE && +/// strncmp(info->name, "Jon", 3) == 0) +/// { +/// ESP_LOGI(TAG, "Jon has joined the room"); +/// } +/// } +/// @endcode +/// + +/// @defgroup DataPackets Data Packets +/// +/// Low-level API for high-frequency data exchange. +/// +/// - **Sending**: use the @ref livekit_room_publish_data function. +/// - **Receiving**: define a handler function and set it for +/// @ref livekit_room_options_t::on_data_received in the room options. +/// +/// For more information about this feature, see the +/// [LiveKit documentation](https://docs.livekit.io/home/client/data/packets/). +/// @{ + +/// Options passed to @ref livekit_room_publish_data. +typedef struct { + /// Data to publish and its size. + livekit_data_payload_t *payload; + + /// Topic to send the data packet under. + char* topic; + + /// Whether the data packet is sent using the lossy channel. + bool lossy; + + /// Identifies of participants to send the data packet to. If not + /// specified, the data packet is sent to all participants. + char** destination_identities; + + /// Number of destination identities. + int destination_identities_count; +} livekit_data_publish_options_t; + +/// Publishes a data packet to participants in a room asynchronously. +/// +/// @param handle[in] Room handle. +/// @param options[in] Data to send with options (e.g. reliability, topic, etc.). +/// @return @ref LIVEKIT_ERR_NONE if successful, otherwise an error code. +/// +/// Example usage: +/// @code +/// const char* command = "G5 I0 J3 P0 Q-3 X2 Y3"; +/// +/// livekit_payload_t payload = { +/// .bytes = (uint8_t*)command, +/// .size = strlen(command) +/// }; +/// livekit_data_publish_options_t options = { +/// .payload = &payload, +/// .topic = "gcode", +/// .lossy = false, +/// .destination_identities = (char*[]){ "printer-1" }, +/// .destination_identities_count = 1 +/// }; +/// livekit_room_publish_data(room_handle, &options); +/// @endcode +/// +livekit_err_t livekit_room_publish_data(livekit_room_handle_t handle, livekit_data_publish_options_t *options); + +/// @} + +/// @defgroup RPC Remote Method Calls (RPC) +/// +/// Use RPC to execute custom methods on other participants in the room and +/// await a response. +/// +/// For more information about this feature, see the +/// [LiveKit documentation](https://docs.livekit.io/home/client/data/rpc/). +/// @{ + +/// Registers a handler for an RPC method. +/// +/// Once registered, the method can be invoked by remote participants in the room. +/// +/// @param handle[in] Room handle. +/// @param method[in] Name of the method to register. +/// @param handler[in] Handler function to call when the method is invoked by a remote participant. +/// @exception If a handler for the method is already registered, an error is returned. +/// @return @ref LIVEKIT_ERR_NONE if successful, otherwise an error code. +/// +livekit_err_t livekit_room_rpc_register(livekit_room_handle_t handle, const char* method, livekit_rpc_handler_t handler); + +/// Unregisters a handler for an RPC method. +/// +/// @param handle[in] Room handle. +/// @param method[in] Name of the method to unregister. +/// @return @ref LIVEKIT_ERR_NONE if successful, otherwise an error code. +/// +livekit_err_t livekit_room_rpc_unregister(livekit_room_handle_t handle, const char* method); + +/// @} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/livekit/include/livekit_rpc.h b/components/livekit/include/livekit_rpc.h new file mode 100644 index 0000000..461b9bc --- /dev/null +++ b/components/livekit/include/livekit_rpc.h @@ -0,0 +1,124 @@ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Maximum payload size for RPC messages. +/// @ingroup RPC +#define LIVEKIT_RPC_MAX_PAYLOAD_BYTES 15360 // 15 KB + +/// Built-in RPC error codes. +typedef enum { + /// The RPC method returned normally. + LIVEKIT_RPC_RESULT_OK = 0, + + /// Application error in method handler. + LIVEKIT_RPC_RESULT_APPLICATION = 1500, + + /// Connection timeout. + LIVEKIT_RPC_RESULT_CONNECTION_TIMEOUT = 1501, + + /// Response timeout. + LIVEKIT_RPC_RESULT_RESPONSE_TIMEOUT = 1502, + + /// Recipient disconnected. + LIVEKIT_RPC_RESULT_RECIPIENT_DISCONNECTED = 1503, + + /// Response payload too large. + LIVEKIT_RPC_RESULT_RESPONSE_PAYLOAD_TOO_LARGE = 1504, + + /// Failed to send. + LIVEKIT_RPC_RESULT_SEND_FAILED = 1505, + + /// Method not supported at destination. + LIVEKIT_RPC_RESULT_UNSUPPORTED_METHOD = 1400, + + /// Recipient not found. + LIVEKIT_RPC_RESULT_RECIPIENT_NOT_FOUND = 1401, + + /// Request payload too large. + LIVEKIT_RPC_RESULT_REQUEST_PAYLOAD_TOO_LARGE = 1402, + + /// RPC not supported by server. + LIVEKIT_RPC_RESULT_UNSUPPORTED_SERVER = 1403, + + /// Unsupported RPC version. + LIVEKIT_RPC_RESULT_UNSUPPORTED_VERSION = 1404 +} livekit_rpc_result_code_t; + +/// The result of an RPC method invocation. +typedef struct { + /// Invocation identifier. + char* id; + + /// The error code if the RPC method failed. + /// @note The value @ref LIVEKIT_RPC_ERR_NONE indicates an ok result. + livekit_rpc_result_code_t code; + + /// Optional, textual description of the error that occurred. + char* error_message; + + /// Payload returned to the caller. + char* payload; +} livekit_rpc_result_t; + +/// Details about an RPC method invocation. +typedef struct { + /// Invocation identifier. + char* id; + + /// The name of the method being invoked. + char* method; + + /// Participant identity of the caller. + char* caller_identity; + + /// Caller provided payload. + /// + /// If no payload is provided, this field will be NULL. Otherwise, + /// it is guaranteed to be a valid NULL-terminated string. + /// + char* payload; + + /// Sends the result of the invocation to the caller. + bool (*send_result)(const livekit_rpc_result_t* res, void* ctx); + + /// Context for the callback. + void *ctx; +} livekit_rpc_invocation_t; + +/// Handler for an RPC invocation. +/// @ingroup RPC +typedef void (*livekit_rpc_handler_t)(const livekit_rpc_invocation_t* invocation, void* ctx); + +/// Returns an ok result from an RPC handler. +/// @param _payload The payload to return to the caller. +/// @warning This macro is intended for use only in RPC handler methods, and expects the +/// invocation parameter to be named `invocation`. +#define livekit_rpc_return_ok(_payload) \ + invocation->send_result(&(livekit_rpc_result_t){ \ + .id = invocation->id, \ + .code = LIVEKIT_RPC_RESULT_OK, \ + .payload = (_payload), \ + .error_message = NULL \ + }, invocation->ctx) + +/// Returns an error result from an RPC handler. +/// @param error_message The error message or NULL. +/// @warning This macro is intended for use only in RPC handler methods, and expects the +/// invocation parameter to be named `invocation`. +#define livekit_rpc_return_error(_error_message) \ + invocation->send_result(&(livekit_rpc_result_t){ \ + .id = invocation->id, \ + .code = LIVEKIT_RPC_RESULT_APPLICATION, \ + .payload = NULL, \ + .error_message = (_error_message) \ + }, invocation->ctx); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/livekit/protocol/README.md b/components/livekit/protocol/README.md new file mode 100644 index 0000000..0161859 --- /dev/null +++ b/components/livekit/protocol/README.md @@ -0,0 +1,18 @@ +# LiveKit Protocol + +This component contains generated Protocol Buffer bindings for C (generated with [Nanopb](https://jpa.kapsi.fi/nanopb/docs/)) and a Python script to update them. + +## Update Script + +When a new protocol version is available, update the generated bindings as follows: +1. Navigate to the [*update/*](./update/) directory +2. Set the the release tag in [*version.ini*](./update/version.ini) +3. Run the update script (Nanopb must be available in your path): +```sh +python update.py +``` +4. Review and commit changes. + +## Generation Options + +Nanopb provides a rich set of [generation options](https://jpa.kapsi.fi/nanopb/docs/reference.html#generator-options) for generating bindings that are suitable for an embedded environment; *.options* files are placed alongside Protobuf files in the [*protobufs*](./protobufs/) directory. diff --git a/components/livekit/protocol/livekit_metrics.pb.c b/components/livekit/protocol/livekit_metrics.pb.c new file mode 100644 index 0000000..ccd06f7 --- /dev/null +++ b/components/livekit/protocol/livekit_metrics.pb.c @@ -0,0 +1,23 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "livekit_metrics.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(LIVEKIT_PB_METRICS_BATCH, livekit_pb_metrics_batch_t, AUTO) + + +PB_BIND(LIVEKIT_PB_TIME_SERIES_METRIC, livekit_pb_time_series_metric_t, AUTO) + + +PB_BIND(LIVEKIT_PB_METRIC_SAMPLE, livekit_pb_metric_sample_t, AUTO) + + +PB_BIND(LIVEKIT_PB_EVENT_METRIC, livekit_pb_event_metric_t, AUTO) + + + + + diff --git a/components/livekit/protocol/livekit_metrics.pb.h b/components/livekit/protocol/livekit_metrics.pb.h new file mode 100644 index 0000000..589bd93 --- /dev/null +++ b/components/livekit/protocol/livekit_metrics.pb.h @@ -0,0 +1,222 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_LIVEKIT_LIVEKIT_METRICS_PB_H_INCLUDED +#define PB_LIVEKIT_LIVEKIT_METRICS_PB_H_INCLUDED +#include +#include "timestamp.pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +/* index from [0: MAX_LABEL_PREDEFINED_MAX_VALUE) are for predefined labels (`MetricLabel`) */ +typedef enum livekit_pb_metric_label { + LIVEKIT_PB_METRIC_LABEL_AGENTS_LLM_TTFT = 0, /* time to first token from LLM */ + LIVEKIT_PB_METRIC_LABEL_AGENTS_STT_TTFT = 1, /* time to final transcription */ + LIVEKIT_PB_METRIC_LABEL_AGENTS_TTS_TTFB = 2, /* time to first byte */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_VIDEO_SUBSCRIBER_FREEZE_COUNT = 3, /* Number of video freezes */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_VIDEO_SUBSCRIBER_TOTAL_FREEZE_DURATION = 4, /* total duration of freezes */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_VIDEO_SUBSCRIBER_PAUSE_COUNT = 5, /* number of video pauses */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_VIDEO_SUBSCRIBER_TOTAL_PAUSES_DURATION = 6, /* total duration of pauses */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_AUDIO_SUBSCRIBER_CONCEALED_SAMPLES = 7, /* number of concealed (synthesized) audio samples */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_AUDIO_SUBSCRIBER_SILENT_CONCEALED_SAMPLES = 8, /* number of silent concealed samples */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_AUDIO_SUBSCRIBER_CONCEALMENT_EVENTS = 9, /* number of concealment events */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_AUDIO_SUBSCRIBER_INTERRUPTION_COUNT = 10, /* number of interruptions */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_AUDIO_SUBSCRIBER_TOTAL_INTERRUPTION_DURATION = 11, /* total duration of interruptions */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_SUBSCRIBER_JITTER_BUFFER_DELAY = 12, /* total time spent in jitter buffer */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_SUBSCRIBER_JITTER_BUFFER_EMITTED_COUNT = 13, /* total time spent in jitter buffer */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_VIDEO_PUBLISHER_QUALITY_LIMITATION_DURATION_BANDWIDTH = 14, /* total duration spent in bandwidth quality limitation */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_VIDEO_PUBLISHER_QUALITY_LIMITATION_DURATION_CPU = 15, /* total duration spent in cpu quality limitation */ + LIVEKIT_PB_METRIC_LABEL_CLIENT_VIDEO_PUBLISHER_QUALITY_LIMITATION_DURATION_OTHER = 16, /* total duration spent in other quality limitation */ + LIVEKIT_PB_METRIC_LABEL_PUBLISHER_RTT = 17, /* Publisher RTT (participant -> server) */ + LIVEKIT_PB_METRIC_LABEL_SERVER_MESH_RTT = 18, /* RTT between publisher node and subscriber node (could involve intermedia node(s)) */ + LIVEKIT_PB_METRIC_LABEL_SUBSCRIBER_RTT = 19, /* Subscribe RTT (server -> participant) */ + LIVEKIT_PB_METRIC_LABEL_METRIC_LABEL_PREDEFINED_MAX_VALUE = 4096 +} livekit_pb_metric_label_t; + +/* Struct definitions */ +typedef struct livekit_pb_metrics_batch { + int64_t timestamp_ms; /* time at which this batch is sent based on a monotonic clock (millisecond resolution) */ + bool has_normalized_timestamp; + google_protobuf_timestamp_t normalized_timestamp; + /* To avoid repeating string values, we store them in a separate list and reference them by index + This is useful for storing participant identities, track names, etc. + There is also a predefined list of labels that can be used to reference common metrics. + They have reserved indices from 0 to (METRIC_LABEL_PREDEFINED_MAX_VALUE - 1). + Indexes pointing at str_data should start from METRIC_LABEL_PREDEFINED_MAX_VALUE, + such that str_data[0] == index of METRIC_LABEL_PREDEFINED_MAX_VALUE. */ + pb_callback_t str_data; + pb_callback_t time_series; + pb_callback_t events; +} livekit_pb_metrics_batch_t; + +typedef struct livekit_pb_time_series_metric { + /* Metric name e.g "speech_probablity". The string value is not directly stored in the message, but referenced by index + in the `str_data` field of `MetricsBatch` */ + uint32_t label; + uint32_t participant_identity; /* index into `str_data` */ + uint32_t track_sid; /* index into `str_data` */ + pb_callback_t samples; + uint32_t rid; /* index into 'str_data' */ +} livekit_pb_time_series_metric_t; + +typedef struct livekit_pb_metric_sample { + int64_t timestamp_ms; /* time of metric based on a monotonic clock (in milliseconds) */ + bool has_normalized_timestamp; + google_protobuf_timestamp_t normalized_timestamp; + float value; +} livekit_pb_metric_sample_t; + +typedef struct livekit_pb_event_metric { + uint32_t label; + uint32_t participant_identity; /* index into `str_data` */ + uint32_t track_sid; /* index into `str_data` */ + int64_t start_timestamp_ms; /* start time of event based on a monotonic clock (in milliseconds) */ + bool has_end_timestamp_ms; + int64_t end_timestamp_ms; /* end time of event based on a monotonic clock (in milliseconds), if needed */ + bool has_normalized_start_timestamp; + google_protobuf_timestamp_t normalized_start_timestamp; + bool has_normalized_end_timestamp; + google_protobuf_timestamp_t normalized_end_timestamp; + pb_callback_t metadata; + uint32_t rid; /* index into 'str_data' */ +} livekit_pb_event_metric_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _LIVEKIT_PB_METRIC_LABEL_MIN LIVEKIT_PB_METRIC_LABEL_AGENTS_LLM_TTFT +#define _LIVEKIT_PB_METRIC_LABEL_MAX LIVEKIT_PB_METRIC_LABEL_METRIC_LABEL_PREDEFINED_MAX_VALUE +#define _LIVEKIT_PB_METRIC_LABEL_ARRAYSIZE ((livekit_pb_metric_label_t)(LIVEKIT_PB_METRIC_LABEL_METRIC_LABEL_PREDEFINED_MAX_VALUE+1)) + + + + + + +/* Initializer values for message structs */ +#define LIVEKIT_PB_METRICS_BATCH_INIT_DEFAULT {0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_TIME_SERIES_METRIC_INIT_DEFAULT {0, 0, 0, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_METRIC_SAMPLE_INIT_DEFAULT {0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, 0} +#define LIVEKIT_PB_EVENT_METRIC_INIT_DEFAULT {0, 0, 0, 0, false, 0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_METRICS_BATCH_INIT_ZERO {0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_TIME_SERIES_METRIC_INIT_ZERO {0, 0, 0, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_METRIC_SAMPLE_INIT_ZERO {0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, 0} +#define LIVEKIT_PB_EVENT_METRIC_INIT_ZERO {0, 0, 0, 0, false, 0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, {{NULL}, NULL}, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define LIVEKIT_PB_METRICS_BATCH_TIMESTAMP_MS_TAG 1 +#define LIVEKIT_PB_METRICS_BATCH_NORMALIZED_TIMESTAMP_TAG 2 +#define LIVEKIT_PB_METRICS_BATCH_STR_DATA_TAG 3 +#define LIVEKIT_PB_METRICS_BATCH_TIME_SERIES_TAG 4 +#define LIVEKIT_PB_METRICS_BATCH_EVENTS_TAG 5 +#define LIVEKIT_PB_TIME_SERIES_METRIC_LABEL_TAG 1 +#define LIVEKIT_PB_TIME_SERIES_METRIC_PARTICIPANT_IDENTITY_TAG 2 +#define LIVEKIT_PB_TIME_SERIES_METRIC_TRACK_SID_TAG 3 +#define LIVEKIT_PB_TIME_SERIES_METRIC_SAMPLES_TAG 4 +#define LIVEKIT_PB_TIME_SERIES_METRIC_RID_TAG 5 +#define LIVEKIT_PB_METRIC_SAMPLE_TIMESTAMP_MS_TAG 1 +#define LIVEKIT_PB_METRIC_SAMPLE_NORMALIZED_TIMESTAMP_TAG 2 +#define LIVEKIT_PB_METRIC_SAMPLE_VALUE_TAG 3 +#define LIVEKIT_PB_EVENT_METRIC_LABEL_TAG 1 +#define LIVEKIT_PB_EVENT_METRIC_PARTICIPANT_IDENTITY_TAG 2 +#define LIVEKIT_PB_EVENT_METRIC_TRACK_SID_TAG 3 +#define LIVEKIT_PB_EVENT_METRIC_START_TIMESTAMP_MS_TAG 4 +#define LIVEKIT_PB_EVENT_METRIC_END_TIMESTAMP_MS_TAG 5 +#define LIVEKIT_PB_EVENT_METRIC_NORMALIZED_START_TIMESTAMP_TAG 6 +#define LIVEKIT_PB_EVENT_METRIC_NORMALIZED_END_TIMESTAMP_TAG 7 +#define LIVEKIT_PB_EVENT_METRIC_METADATA_TAG 8 +#define LIVEKIT_PB_EVENT_METRIC_RID_TAG 9 + +/* Struct field encoding specification for nanopb */ +#define LIVEKIT_PB_METRICS_BATCH_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT64, timestamp_ms, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, normalized_timestamp, 2) \ +X(a, CALLBACK, REPEATED, STRING, str_data, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, time_series, 4) \ +X(a, CALLBACK, REPEATED, MESSAGE, events, 5) +#define LIVEKIT_PB_METRICS_BATCH_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_METRICS_BATCH_DEFAULT NULL +#define livekit_pb_metrics_batch_t_normalized_timestamp_MSGTYPE google_protobuf_timestamp_t +#define livekit_pb_metrics_batch_t_time_series_MSGTYPE livekit_pb_time_series_metric_t +#define livekit_pb_metrics_batch_t_events_MSGTYPE livekit_pb_event_metric_t + +#define LIVEKIT_PB_TIME_SERIES_METRIC_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, label, 1) \ +X(a, STATIC, SINGULAR, UINT32, participant_identity, 2) \ +X(a, STATIC, SINGULAR, UINT32, track_sid, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, samples, 4) \ +X(a, STATIC, SINGULAR, UINT32, rid, 5) +#define LIVEKIT_PB_TIME_SERIES_METRIC_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_TIME_SERIES_METRIC_DEFAULT NULL +#define livekit_pb_time_series_metric_t_samples_MSGTYPE livekit_pb_metric_sample_t + +#define LIVEKIT_PB_METRIC_SAMPLE_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT64, timestamp_ms, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, normalized_timestamp, 2) \ +X(a, STATIC, SINGULAR, FLOAT, value, 3) +#define LIVEKIT_PB_METRIC_SAMPLE_CALLBACK NULL +#define LIVEKIT_PB_METRIC_SAMPLE_DEFAULT NULL +#define livekit_pb_metric_sample_t_normalized_timestamp_MSGTYPE google_protobuf_timestamp_t + +#define LIVEKIT_PB_EVENT_METRIC_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, label, 1) \ +X(a, STATIC, SINGULAR, UINT32, participant_identity, 2) \ +X(a, STATIC, SINGULAR, UINT32, track_sid, 3) \ +X(a, STATIC, SINGULAR, INT64, start_timestamp_ms, 4) \ +X(a, STATIC, OPTIONAL, INT64, end_timestamp_ms, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, normalized_start_timestamp, 6) \ +X(a, STATIC, OPTIONAL, MESSAGE, normalized_end_timestamp, 7) \ +X(a, CALLBACK, SINGULAR, STRING, metadata, 8) \ +X(a, STATIC, SINGULAR, UINT32, rid, 9) +#define LIVEKIT_PB_EVENT_METRIC_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_EVENT_METRIC_DEFAULT NULL +#define livekit_pb_event_metric_t_normalized_start_timestamp_MSGTYPE google_protobuf_timestamp_t +#define livekit_pb_event_metric_t_normalized_end_timestamp_MSGTYPE google_protobuf_timestamp_t + +extern const pb_msgdesc_t livekit_pb_metrics_batch_t_msg; +extern const pb_msgdesc_t livekit_pb_time_series_metric_t_msg; +extern const pb_msgdesc_t livekit_pb_metric_sample_t_msg; +extern const pb_msgdesc_t livekit_pb_event_metric_t_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define LIVEKIT_PB_METRICS_BATCH_FIELDS &livekit_pb_metrics_batch_t_msg +#define LIVEKIT_PB_TIME_SERIES_METRIC_FIELDS &livekit_pb_time_series_metric_t_msg +#define LIVEKIT_PB_METRIC_SAMPLE_FIELDS &livekit_pb_metric_sample_t_msg +#define LIVEKIT_PB_EVENT_METRIC_FIELDS &livekit_pb_event_metric_t_msg + +/* Maximum encoded size of messages (where known) */ +/* livekit_pb_MetricsBatch_size depends on runtime parameters */ +/* livekit_pb_TimeSeriesMetric_size depends on runtime parameters */ +/* livekit_pb_EventMetric_size depends on runtime parameters */ +#define LIVEKIT_LIVEKIT_METRICS_PB_H_MAX_SIZE LIVEKIT_PB_METRIC_SAMPLE_SIZE +#define LIVEKIT_PB_METRIC_SAMPLE_SIZE 40 + +/* Mapping from canonical names (mangle_names or overridden package name) */ +#define livekit_MetricLabel livekit_pb_MetricLabel +#define livekit_MetricsBatch livekit_pb_MetricsBatch +#define livekit_TimeSeriesMetric livekit_pb_TimeSeriesMetric +#define livekit_MetricSample livekit_pb_MetricSample +#define livekit_EventMetric livekit_pb_EventMetric +#define _LIVEKIT_METRIC_LABEL_MIN _LIVEKIT_PB_METRIC_LABEL_MIN +#define _LIVEKIT_METRIC_LABEL_MAX _LIVEKIT_PB_METRIC_LABEL_MAX +#define _LIVEKIT_METRIC_LABEL_ARRAYSIZE _LIVEKIT_PB_METRIC_LABEL_ARRAYSIZE +#define LIVEKIT_METRICS_BATCH_INIT_DEFAULT LIVEKIT_PB_METRICS_BATCH_INIT_DEFAULT +#define LIVEKIT_TIME_SERIES_METRIC_INIT_DEFAULT LIVEKIT_PB_TIME_SERIES_METRIC_INIT_DEFAULT +#define LIVEKIT_METRIC_SAMPLE_INIT_DEFAULT LIVEKIT_PB_METRIC_SAMPLE_INIT_DEFAULT +#define LIVEKIT_EVENT_METRIC_INIT_DEFAULT LIVEKIT_PB_EVENT_METRIC_INIT_DEFAULT +#define LIVEKIT_METRICS_BATCH_INIT_ZERO LIVEKIT_PB_METRICS_BATCH_INIT_ZERO +#define LIVEKIT_TIME_SERIES_METRIC_INIT_ZERO LIVEKIT_PB_TIME_SERIES_METRIC_INIT_ZERO +#define LIVEKIT_METRIC_SAMPLE_INIT_ZERO LIVEKIT_PB_METRIC_SAMPLE_INIT_ZERO +#define LIVEKIT_EVENT_METRIC_INIT_ZERO LIVEKIT_PB_EVENT_METRIC_INIT_ZERO + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/components/livekit/protocol/livekit_models.pb.c b/components/livekit/protocol/livekit_models.pb.c new file mode 100644 index 0000000..b5082b2 --- /dev/null +++ b/components/livekit/protocol/livekit_models.pb.c @@ -0,0 +1,191 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "livekit_models.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(LIVEKIT_PB_PAGINATION, livekit_pb_pagination_t, AUTO) + + +PB_BIND(LIVEKIT_PB_LIST_UPDATE, livekit_pb_list_update_t, AUTO) + + +PB_BIND(LIVEKIT_PB_ROOM, livekit_pb_room_t, AUTO) + + +PB_BIND(LIVEKIT_PB_CODEC, livekit_pb_codec_t, AUTO) + + +PB_BIND(LIVEKIT_PB_PLAYOUT_DELAY, livekit_pb_playout_delay_t, AUTO) + + +PB_BIND(LIVEKIT_PB_PARTICIPANT_PERMISSION, livekit_pb_participant_permission_t, AUTO) + + +PB_BIND(LIVEKIT_PB_PARTICIPANT_INFO, livekit_pb_participant_info_t, AUTO) + + +PB_BIND(LIVEKIT_PB_ENCRYPTION, livekit_pb_encryption_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SIMULCAST_CODEC_INFO, livekit_pb_simulcast_codec_info_t, AUTO) + + +PB_BIND(LIVEKIT_PB_TRACK_INFO, livekit_pb_track_info_t, AUTO) + + +PB_BIND(LIVEKIT_PB_VIDEO_LAYER, livekit_pb_video_layer_t, AUTO) + + +PB_BIND(LIVEKIT_PB_DATA_PACKET, livekit_pb_data_packet_t, AUTO) + + +PB_BIND(LIVEKIT_PB_ACTIVE_SPEAKER_UPDATE, livekit_pb_active_speaker_update_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SPEAKER_INFO, livekit_pb_speaker_info_t, AUTO) + + +PB_BIND(LIVEKIT_PB_USER_PACKET, livekit_pb_user_packet_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SIP_DTMF, livekit_pb_sip_dtmf_t, AUTO) + + +PB_BIND(LIVEKIT_PB_TRANSCRIPTION, livekit_pb_transcription_t, AUTO) + + +PB_BIND(LIVEKIT_PB_TRANSCRIPTION_SEGMENT, livekit_pb_transcription_segment_t, AUTO) + + +PB_BIND(LIVEKIT_PB_CHAT_MESSAGE, livekit_pb_chat_message_t, AUTO) + + +PB_BIND(LIVEKIT_PB_RPC_REQUEST, livekit_pb_rpc_request_t, AUTO) + + +PB_BIND(LIVEKIT_PB_RPC_ACK, livekit_pb_rpc_ack_t, AUTO) + + +PB_BIND(LIVEKIT_PB_RPC_RESPONSE, livekit_pb_rpc_response_t, AUTO) + + +PB_BIND(LIVEKIT_PB_RPC_ERROR, livekit_pb_rpc_error_t, AUTO) + + +PB_BIND(LIVEKIT_PB_PARTICIPANT_TRACKS, livekit_pb_participant_tracks_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SERVER_INFO, livekit_pb_server_info_t, AUTO) + + +PB_BIND(LIVEKIT_PB_CLIENT_INFO, livekit_pb_client_info_t, AUTO) + + +PB_BIND(LIVEKIT_PB_CLIENT_CONFIGURATION, livekit_pb_client_configuration_t, AUTO) + + +PB_BIND(LIVEKIT_PB_VIDEO_CONFIGURATION, livekit_pb_video_configuration_t, AUTO) + + +PB_BIND(LIVEKIT_PB_DISABLED_CODECS, livekit_pb_disabled_codecs_t, AUTO) + + +PB_BIND(LIVEKIT_PB_RTP_DRIFT, livekit_pb_rtp_drift_t, AUTO) + + +PB_BIND(LIVEKIT_PB_RTP_STATS, livekit_pb_rtp_stats_t, 2) + + +PB_BIND(LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY, livekit_pb_rtp_stats_gap_histogram_entry_t, AUTO) + + +PB_BIND(LIVEKIT_PB_RTCP_SENDER_REPORT_STATE, livekit_pb_rtcp_sender_report_state_t, AUTO) + + +PB_BIND(LIVEKIT_PB_RTP_FORWARDER_STATE, livekit_pb_rtp_forwarder_state_t, AUTO) + + +PB_BIND(LIVEKIT_PB_RTP_MUNGER_STATE, livekit_pb_rtp_munger_state_t, AUTO) + + +PB_BIND(LIVEKIT_PB_VP8_MUNGER_STATE, livekit_pb_vp8_munger_state_t, AUTO) + + +PB_BIND(LIVEKIT_PB_TIMED_VERSION, livekit_pb_timed_version_t, AUTO) + + +PB_BIND(LIVEKIT_PB_DATA_STREAM, livekit_pb_data_stream_t, AUTO) + + +PB_BIND(LIVEKIT_PB_DATA_STREAM_TEXT_HEADER, livekit_pb_data_stream_text_header_t, AUTO) + + +PB_BIND(LIVEKIT_PB_DATA_STREAM_BYTE_HEADER, livekit_pb_data_stream_byte_header_t, AUTO) + + +PB_BIND(LIVEKIT_PB_DATA_STREAM_HEADER, livekit_pb_data_stream_header_t, AUTO) + + +PB_BIND(LIVEKIT_PB_DATA_STREAM_CHUNK, livekit_pb_data_stream_chunk_t, AUTO) + + +PB_BIND(LIVEKIT_PB_DATA_STREAM_TRAILER, livekit_pb_data_stream_trailer_t, AUTO) + + +PB_BIND(LIVEKIT_PB_WEBHOOK_CONFIG, livekit_pb_webhook_config_t, AUTO) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef PB_CONVERT_DOUBLE_FLOAT +/* On some platforms (such as AVR), double is really float. + * To be able to encode/decode double on these platforms, you need. + * to define PB_CONVERT_DOUBLE_FLOAT in pb.h or compiler command line. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) +#endif + diff --git a/components/livekit/protocol/livekit_models.pb.h b/components/livekit/protocol/livekit_models.pb.h new file mode 100644 index 0000000..287353e --- /dev/null +++ b/components/livekit/protocol/livekit_models.pb.h @@ -0,0 +1,1861 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_LIVEKIT_LIVEKIT_MODELS_PB_H_INCLUDED +#define PB_LIVEKIT_LIVEKIT_MODELS_PB_H_INCLUDED +#include +#include "timestamp.pb.h" +#include "livekit_metrics.pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum livekit_pb_audio_codec { + LIVEKIT_PB_AUDIO_CODEC_DEFAULT_AC = 0, + LIVEKIT_PB_AUDIO_CODEC_OPUS = 1, + LIVEKIT_PB_AUDIO_CODEC_AAC = 2 +} livekit_pb_audio_codec_t; + +typedef enum livekit_pb_video_codec { + LIVEKIT_PB_VIDEO_CODEC_DEFAULT_VC = 0, + LIVEKIT_PB_VIDEO_CODEC_H264_BASELINE = 1, + LIVEKIT_PB_VIDEO_CODEC_H264_MAIN = 2, + LIVEKIT_PB_VIDEO_CODEC_H264_HIGH = 3, + LIVEKIT_PB_VIDEO_CODEC_VP8 = 4 +} livekit_pb_video_codec_t; + +typedef enum livekit_pb_image_codec { + LIVEKIT_PB_IMAGE_CODEC_IC_DEFAULT = 0, + LIVEKIT_PB_IMAGE_CODEC_IC_JPEG = 1 +} livekit_pb_image_codec_t; + +/* Policy for publisher to handle subscribers that are unable to support the primary codec of a track */ +typedef enum livekit_pb_backup_codec_policy { + /* default behavior, the track prefer to regress to backup codec and all subscribers will receive the backup codec, + the sfu will try to regress codec if possible but not assured. */ + LIVEKIT_PB_BACKUP_CODEC_POLICY_PREFER_REGRESSION = 0, + /* encoding/send the primary and backup codec simultaneously */ + LIVEKIT_PB_BACKUP_CODEC_POLICY_SIMULCAST = 1, + /* force the track to regress to backup codec, this option can be used in video conference or the publisher has limited bandwidth/encoding power */ + LIVEKIT_PB_BACKUP_CODEC_POLICY_REGRESSION = 2 +} livekit_pb_backup_codec_policy_t; + +typedef enum livekit_pb_track_type { + LIVEKIT_PB_TRACK_TYPE_AUDIO = 0, + LIVEKIT_PB_TRACK_TYPE_VIDEO = 1, + LIVEKIT_PB_TRACK_TYPE_DATA = 2 +} livekit_pb_track_type_t; + +typedef enum livekit_pb_track_source { + LIVEKIT_PB_TRACK_SOURCE_UNKNOWN = 0, + LIVEKIT_PB_TRACK_SOURCE_CAMERA = 1, + LIVEKIT_PB_TRACK_SOURCE_MICROPHONE = 2, + LIVEKIT_PB_TRACK_SOURCE_SCREEN_SHARE = 3, + LIVEKIT_PB_TRACK_SOURCE_SCREEN_SHARE_AUDIO = 4 +} livekit_pb_track_source_t; + +typedef enum livekit_pb_video_quality { + LIVEKIT_PB_VIDEO_QUALITY_LOW = 0, + LIVEKIT_PB_VIDEO_QUALITY_MEDIUM = 1, + LIVEKIT_PB_VIDEO_QUALITY_HIGH = 2, + LIVEKIT_PB_VIDEO_QUALITY_OFF = 3 +} livekit_pb_video_quality_t; + +typedef enum livekit_pb_connection_quality { + LIVEKIT_PB_CONNECTION_QUALITY_POOR = 0, + LIVEKIT_PB_CONNECTION_QUALITY_GOOD = 1, + LIVEKIT_PB_CONNECTION_QUALITY_EXCELLENT = 2, + LIVEKIT_PB_CONNECTION_QUALITY_LOST = 3 +} livekit_pb_connection_quality_t; + +typedef enum livekit_pb_client_config_setting { + LIVEKIT_PB_CLIENT_CONFIG_SETTING_UNSET = 0, + LIVEKIT_PB_CLIENT_CONFIG_SETTING_DISABLED = 1, + LIVEKIT_PB_CLIENT_CONFIG_SETTING_ENABLED = 2 +} livekit_pb_client_config_setting_t; + +typedef enum livekit_pb_disconnect_reason { + LIVEKIT_PB_DISCONNECT_REASON_UNKNOWN_REASON = 0, + /* the client initiated the disconnect */ + LIVEKIT_PB_DISCONNECT_REASON_CLIENT_INITIATED = 1, + /* another participant with the same identity has joined the room */ + LIVEKIT_PB_DISCONNECT_REASON_DUPLICATE_IDENTITY = 2, + /* the server instance is shutting down */ + LIVEKIT_PB_DISCONNECT_REASON_SERVER_SHUTDOWN = 3, + /* RoomService.RemoveParticipant was called */ + LIVEKIT_PB_DISCONNECT_REASON_PARTICIPANT_REMOVED = 4, + /* RoomService.DeleteRoom was called */ + LIVEKIT_PB_DISCONNECT_REASON_ROOM_DELETED = 5, + /* the client is attempting to resume a session, but server is not aware of it */ + LIVEKIT_PB_DISCONNECT_REASON_STATE_MISMATCH = 6, + /* client was unable to connect fully */ + LIVEKIT_PB_DISCONNECT_REASON_JOIN_FAILURE = 7, + /* Cloud-only, the server requested Participant to migrate the connection elsewhere */ + LIVEKIT_PB_DISCONNECT_REASON_MIGRATION = 8, + /* the signal websocket was closed unexpectedly */ + LIVEKIT_PB_DISCONNECT_REASON_SIGNAL_CLOSE = 9, + /* the room was closed, due to all Standard and Ingress participants having left */ + LIVEKIT_PB_DISCONNECT_REASON_ROOM_CLOSED = 10, + /* SIP callee did not respond in time */ + LIVEKIT_PB_DISCONNECT_REASON_USER_UNAVAILABLE = 11, + /* SIP callee rejected the call (busy) */ + LIVEKIT_PB_DISCONNECT_REASON_USER_REJECTED = 12, + /* SIP protocol failure or unexpected response */ + LIVEKIT_PB_DISCONNECT_REASON_SIP_TRUNK_FAILURE = 13, + /* server timed out a participant session */ + LIVEKIT_PB_DISCONNECT_REASON_CONNECTION_TIMEOUT = 14, + /* media stream failure or media timeout */ + LIVEKIT_PB_DISCONNECT_REASON_MEDIA_FAILURE = 15 +} livekit_pb_disconnect_reason_t; + +typedef enum livekit_pb_reconnect_reason { + LIVEKIT_PB_RECONNECT_REASON_RR_UNKNOWN = 0, + LIVEKIT_PB_RECONNECT_REASON_RR_SIGNAL_DISCONNECTED = 1, + LIVEKIT_PB_RECONNECT_REASON_RR_PUBLISHER_FAILED = 2, + LIVEKIT_PB_RECONNECT_REASON_RR_SUBSCRIBER_FAILED = 3, + LIVEKIT_PB_RECONNECT_REASON_RR_SWITCH_CANDIDATE = 4 +} livekit_pb_reconnect_reason_t; + +typedef enum livekit_pb_subscription_error { + LIVEKIT_PB_SUBSCRIPTION_ERROR_SE_UNKNOWN = 0, + LIVEKIT_PB_SUBSCRIPTION_ERROR_SE_CODEC_UNSUPPORTED = 1, + LIVEKIT_PB_SUBSCRIPTION_ERROR_SE_TRACK_NOTFOUND = 2 +} livekit_pb_subscription_error_t; + +typedef enum livekit_pb_audio_track_feature { + LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_STEREO = 0, + LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_NO_DTX = 1, + LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_AUTO_GAIN_CONTROL = 2, + LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_ECHO_CANCELLATION = 3, + LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_NOISE_SUPPRESSION = 4, + LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_ENHANCED_NOISE_CANCELLATION = 5, + LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_PRECONNECT_BUFFER = 6 /* client will buffer audio once available and send it to the server via bytes stream once connected */ +} livekit_pb_audio_track_feature_t; + +typedef enum livekit_pb_participant_info_state { + /* websocket' connected, but not offered yet */ + LIVEKIT_PB_PARTICIPANT_INFO_STATE_JOINING = 0, + /* server received client offer */ + LIVEKIT_PB_PARTICIPANT_INFO_STATE_JOINED = 1, + /* ICE connectivity established */ + LIVEKIT_PB_PARTICIPANT_INFO_STATE_ACTIVE = 2, + /* WS disconnected */ + LIVEKIT_PB_PARTICIPANT_INFO_STATE_DISCONNECTED = 3 +} livekit_pb_participant_info_state_t; + +typedef enum livekit_pb_participant_info_kind { + /* standard participants, e.g. web clients */ + LIVEKIT_PB_PARTICIPANT_INFO_KIND_STANDARD = 0, + /* only ingests streams */ + LIVEKIT_PB_PARTICIPANT_INFO_KIND_INGRESS = 1, + /* only consumes streams */ + LIVEKIT_PB_PARTICIPANT_INFO_KIND_EGRESS = 2, + /* SIP participants */ + LIVEKIT_PB_PARTICIPANT_INFO_KIND_SIP = 3, + /* LiveKit agents */ + LIVEKIT_PB_PARTICIPANT_INFO_KIND_AGENT = 4 +} livekit_pb_participant_info_kind_t; + +typedef enum livekit_pb_participant_info_kind_detail { + LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_CLOUD_AGENT = 0, + LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_FORWARDED = 1 +} livekit_pb_participant_info_kind_detail_t; + +typedef enum livekit_pb_encryption_type { + LIVEKIT_PB_ENCRYPTION_TYPE_NONE = 0, + LIVEKIT_PB_ENCRYPTION_TYPE_GCM = 1, + LIVEKIT_PB_ENCRYPTION_TYPE_CUSTOM = 2 +} livekit_pb_encryption_type_t; + +typedef enum livekit_pb_data_packet_kind { + LIVEKIT_PB_DATA_PACKET_KIND_RELIABLE = 0, + LIVEKIT_PB_DATA_PACKET_KIND_LOSSY = 1 +} livekit_pb_data_packet_kind_t; + +typedef enum livekit_pb_server_info_edition { + LIVEKIT_PB_SERVER_INFO_EDITION_STANDARD = 0, + LIVEKIT_PB_SERVER_INFO_EDITION_CLOUD = 1 +} livekit_pb_server_info_edition_t; + +typedef enum livekit_pb_client_info_sdk { + LIVEKIT_PB_CLIENT_INFO_SDK_UNKNOWN = 0, + LIVEKIT_PB_CLIENT_INFO_SDK_JS = 1, + LIVEKIT_PB_CLIENT_INFO_SDK_SWIFT = 2, + LIVEKIT_PB_CLIENT_INFO_SDK_ANDROID = 3, + LIVEKIT_PB_CLIENT_INFO_SDK_FLUTTER = 4, + LIVEKIT_PB_CLIENT_INFO_SDK_GO = 5, + LIVEKIT_PB_CLIENT_INFO_SDK_UNITY = 6, + LIVEKIT_PB_CLIENT_INFO_SDK_REACT_NATIVE = 7, + LIVEKIT_PB_CLIENT_INFO_SDK_RUST = 8, + LIVEKIT_PB_CLIENT_INFO_SDK_PYTHON = 9, + LIVEKIT_PB_CLIENT_INFO_SDK_CPP = 10, + LIVEKIT_PB_CLIENT_INFO_SDK_UNITY_WEB = 11, + LIVEKIT_PB_CLIENT_INFO_SDK_NODE = 12, + LIVEKIT_PB_CLIENT_INFO_SDK_UNREAL = 13 +} livekit_pb_client_info_sdk_t; + +/* enum for operation types (specific to TextHeader) */ +typedef enum livekit_pb_data_stream_operation_type { + LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_CREATE = 0, + LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_UPDATE = 1, + LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_DELETE = 2, + LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_REACTION = 3 +} livekit_pb_data_stream_operation_type_t; + +/* Struct definitions */ +typedef struct livekit_pb_pagination { + pb_callback_t after_id; /* list entities which IDs are greater */ + int32_t limit; +} livekit_pb_pagination_t; + +/* ListUpdate is used for updated APIs where 'repeated string' field is modified. */ +typedef struct livekit_pb_list_update { + pb_callback_t set; /* set the field to a new list */ +} livekit_pb_list_update_t; + +typedef struct livekit_pb_room { + char *sid; + char *name; + char *metadata; + uint32_t num_participants; + bool active_recording; +} livekit_pb_room_t; + +typedef struct livekit_pb_codec { + pb_callback_t mime; + pb_callback_t fmtp_line; +} livekit_pb_codec_t; + +typedef struct livekit_pb_playout_delay { + bool enabled; + uint32_t min; + uint32_t max; +} livekit_pb_playout_delay_t; + +typedef struct livekit_pb_participant_permission { + /* allow participant to subscribe to other tracks in the room */ + bool can_subscribe; + /* allow participant to publish new tracks to room */ + bool can_publish; + /* allow participant to publish data */ + bool can_publish_data; +} livekit_pb_participant_permission_t; + +typedef struct livekit_pb_participant_info { + char *sid; + char *identity; + livekit_pb_participant_info_state_t state; + pb_size_t tracks_count; + struct livekit_pb_track_info *tracks; + char *metadata; + char *name; + livekit_pb_participant_permission_t permission; + livekit_pb_participant_info_kind_t *kind; +} livekit_pb_participant_info_t; + +typedef struct livekit_pb_encryption { + char dummy_field; +} livekit_pb_encryption_t; + +typedef struct livekit_pb_simulcast_codec_info { + pb_callback_t mime_type; + pb_callback_t mid; + pb_callback_t cid; + pb_callback_t layers; +} livekit_pb_simulcast_codec_info_t; + +typedef struct livekit_pb_track_info { + char *sid; + livekit_pb_track_type_t type; + bool muted; + /* mime type of codec */ + char *mime_type; + bool stereo; + pb_size_t audio_features_count; + livekit_pb_audio_track_feature_t audio_features[8]; +} livekit_pb_track_info_t; + +/* provide information about available spatial layers */ +typedef struct livekit_pb_video_layer { + /* for tracks with a single layer, this should be HIGH */ + livekit_pb_video_quality_t quality; + uint32_t width; + uint32_t height; +} livekit_pb_video_layer_t; + +typedef struct livekit_pb_active_speaker_update { + pb_callback_t speakers; +} livekit_pb_active_speaker_update_t; + +typedef struct livekit_pb_speaker_info { + pb_callback_t sid; + /* audio level, 0-1.0, 1 is loudest */ + float level; + /* true if speaker is currently active */ + bool active; +} livekit_pb_speaker_info_t; + +typedef struct livekit_pb_user_packet { + /* user defined payload */ + pb_bytes_array_t *payload; + /* topic under which the message was published */ + char *topic; +} livekit_pb_user_packet_t; + +typedef struct livekit_pb_sip_dtmf { + uint32_t code; + char digit[2]; +} livekit_pb_sip_dtmf_t; + +typedef struct livekit_pb_transcription { + /* Participant that got its speech transcribed */ + pb_callback_t transcribed_participant_identity; + pb_callback_t track_id; + pb_callback_t segments; +} livekit_pb_transcription_t; + +typedef struct livekit_pb_transcription_segment { + pb_callback_t id; + pb_callback_t text; + uint64_t start_time; + uint64_t end_time; + bool final; + pb_callback_t language; +} livekit_pb_transcription_segment_t; + +typedef struct livekit_pb_chat_message { + pb_callback_t id; /* uuid */ + int64_t timestamp; + bool has_edit_timestamp; + int64_t edit_timestamp; /* populated only if the intent is to edit/update an existing message */ + pb_callback_t message; + bool deleted; /* true to remove message */ + bool generated; /* true if the chat message has been generated by an agent from a participant's audio transcription */ +} livekit_pb_chat_message_t; + +typedef struct livekit_pb_rpc_request { + char id[37]; + char *method; + char *payload; + uint32_t response_timeout_ms; + uint32_t version; +} livekit_pb_rpc_request_t; + +typedef struct livekit_pb_rpc_ack { + char request_id[37]; +} livekit_pb_rpc_ack_t; + +typedef struct livekit_pb_rpc_error { + uint32_t code; + char *data; +} livekit_pb_rpc_error_t; + +typedef struct livekit_pb_rpc_response { + char request_id[37]; + pb_size_t which_value; + union { + char *payload; + livekit_pb_rpc_error_t error; + } value; +} livekit_pb_rpc_response_t; + +typedef struct livekit_pb_participant_tracks { + /* participant ID of participant to whom the tracks belong */ + pb_callback_t participant_sid; + pb_callback_t track_sids; +} livekit_pb_participant_tracks_t; + +/* details about the server */ +typedef struct livekit_pb_server_info { + livekit_pb_server_info_edition_t edition; + pb_callback_t version; + int32_t protocol; + pb_callback_t region; + pb_callback_t node_id; + /* additional debugging information. sent only if server is in development mode */ + pb_callback_t debug_info; + int32_t agent_protocol; +} livekit_pb_server_info_t; + +/* details about the client */ +typedef struct livekit_pb_client_info { + livekit_pb_client_info_sdk_t sdk; + pb_callback_t version; + int32_t protocol; + pb_callback_t os; + pb_callback_t os_version; + pb_callback_t device_model; + pb_callback_t browser; + pb_callback_t browser_version; + pb_callback_t address; + /* wifi, wired, cellular, vpn, empty if not known */ + pb_callback_t network; + /* comma separated list of additional LiveKit SDKs in use of this client, with versions + e.g. "components-js:1.2.3,track-processors-js:1.2.3" */ + pb_callback_t other_sdks; +} livekit_pb_client_info_t; + +/* server provided client configuration */ +typedef struct livekit_pb_client_configuration { + livekit_pb_client_config_setting_t resume_connection; + livekit_pb_client_config_setting_t force_relay; +} livekit_pb_client_configuration_t; + +typedef struct livekit_pb_video_configuration { + livekit_pb_client_config_setting_t hardware_encoder; +} livekit_pb_video_configuration_t; + +typedef struct livekit_pb_disabled_codecs { + /* disabled for both publish and subscribe */ + pb_callback_t codecs; + /* only disable for publish */ + pb_callback_t publish; +} livekit_pb_disabled_codecs_t; + +typedef struct livekit_pb_rtp_drift { + bool has_start_time; + google_protobuf_timestamp_t start_time; + bool has_end_time; + google_protobuf_timestamp_t end_time; + double duration; + uint64_t start_timestamp; + uint64_t end_timestamp; + uint64_t rtp_clock_ticks; + int64_t drift_samples; + double drift_ms; + double clock_rate; +} livekit_pb_rtp_drift_t; + +typedef struct livekit_pb_rtp_stats { + bool has_start_time; + google_protobuf_timestamp_t start_time; + bool has_end_time; + google_protobuf_timestamp_t end_time; + double duration; + uint32_t packets; + double packet_rate; + uint64_t bytes; + double bitrate; + uint32_t packets_lost; + double packet_loss_rate; + float packet_loss_percentage; + uint32_t packets_duplicate; + double packet_duplicate_rate; + uint64_t bytes_duplicate; + double bitrate_duplicate; + uint32_t packets_padding; + double packet_padding_rate; + uint64_t bytes_padding; + double bitrate_padding; + uint32_t packets_out_of_order; + uint32_t frames; + double frame_rate; + double jitter_current; + double jitter_max; + pb_callback_t gap_histogram; + uint32_t nacks; + uint32_t nack_misses; + uint32_t plis; + bool has_last_pli; + google_protobuf_timestamp_t last_pli; + uint32_t firs; + bool has_last_fir; + google_protobuf_timestamp_t last_fir; + uint32_t rtt_current; + uint32_t rtt_max; + uint32_t key_frames; + bool has_last_key_frame; + google_protobuf_timestamp_t last_key_frame; + uint32_t layer_lock_plis; + bool has_last_layer_lock_pli; + google_protobuf_timestamp_t last_layer_lock_pli; + uint32_t nack_acks; + uint32_t nack_repeated; + uint64_t header_bytes; + uint64_t header_bytes_duplicate; + uint64_t header_bytes_padding; + bool has_packet_drift; + livekit_pb_rtp_drift_t packet_drift; + bool has_ntp_report_drift; + livekit_pb_rtp_drift_t ntp_report_drift; + bool has_rebased_report_drift; + livekit_pb_rtp_drift_t rebased_report_drift; + bool has_received_report_drift; + livekit_pb_rtp_drift_t received_report_drift; /* NEXT_ID: 48 */ +} livekit_pb_rtp_stats_t; + +typedef struct livekit_pb_rtp_stats_gap_histogram_entry { + int32_t key; + uint32_t value; +} livekit_pb_rtp_stats_gap_histogram_entry_t; + +typedef struct livekit_pb_rtcp_sender_report_state { + uint32_t rtp_timestamp; + uint64_t rtp_timestamp_ext; + uint64_t ntp_timestamp; + int64_t at; /* time at which this happened */ + int64_t at_adjusted; + uint32_t packets; + uint64_t octets; +} livekit_pb_rtcp_sender_report_state_t; + +typedef struct livekit_pb_rtp_munger_state { + uint64_t ext_last_sequence_number; + uint64_t ext_second_last_sequence_number; + uint64_t ext_last_timestamp; + uint64_t ext_second_last_timestamp; + bool last_marker; + bool second_last_marker; +} livekit_pb_rtp_munger_state_t; + +typedef struct livekit_pb_vp8_munger_state { + int32_t ext_last_picture_id; + bool picture_id_used; + uint32_t last_tl0_pic_idx; + bool tl0_pic_idx_used; + bool tid_used; + uint32_t last_key_idx; + bool key_idx_used; +} livekit_pb_vp8_munger_state_t; + +typedef struct livekit_pb_rtp_forwarder_state { + bool started; + int32_t reference_layer_spatial; + int64_t pre_start_time; + uint64_t ext_first_timestamp; + uint64_t dummy_start_timestamp_offset; + bool has_rtp_munger; + livekit_pb_rtp_munger_state_t rtp_munger; + pb_size_t which_codec_munger; + union { + livekit_pb_vp8_munger_state_t vp8_munger; + } codec_munger; + pb_callback_t sender_report_state; +} livekit_pb_rtp_forwarder_state_t; + +typedef struct livekit_pb_timed_version { + int64_t unix_micro; + int32_t ticks; +} livekit_pb_timed_version_t; + +typedef struct livekit_pb_data_stream { + char dummy_field; +} livekit_pb_data_stream_t; + +/* header properties specific to text streams */ +typedef struct livekit_pb_data_stream_text_header { + livekit_pb_data_stream_operation_type_t operation_type; + int32_t version; /* Optional: Version for updates/edits */ + pb_callback_t reply_to_stream_id; /* Optional: Reply to specific message */ + pb_callback_t attached_stream_ids; /* file attachments for text streams */ + bool generated; /* true if the text has been generated by an agent from a participant's audio transcription */ +} livekit_pb_data_stream_text_header_t; + +/* header properties specific to byte or file streams */ +typedef struct livekit_pb_data_stream_byte_header { + pb_callback_t name; +} livekit_pb_data_stream_byte_header_t; + +/* main DataStream.Header that contains a oneof for specific headers */ +typedef struct livekit_pb_data_stream_header { + char stream_id[37]; /* unique identifier for this data stream */ + int64_t timestamp; /* using int64 for Unix timestamp */ + char *topic; + char *mime_type; + bool has_total_length; + uint64_t total_length; /* only populated for finite streams, if it's a stream of unknown size this stays empty */ + pb_size_t which_content_header; + union { + livekit_pb_data_stream_text_header_t text_header; + livekit_pb_data_stream_byte_header_t byte_header; + } content_header; +} livekit_pb_data_stream_header_t; + +typedef struct livekit_pb_data_stream_chunk { + char stream_id[37]; /* unique identifier for this data stream to map it to the correct header */ + uint64_t chunk_index; + pb_bytes_array_t *content; /* content as binary (bytes) */ + int32_t version; /* a version indicating that this chunk_index has been retroactively modified and the original one needs to be replaced */ +} livekit_pb_data_stream_chunk_t; + +typedef struct livekit_pb_data_stream_trailer { + char stream_id[37]; /* unique identifier for this data stream */ + char reason[16]; /* reason why the stream was closed (could contain "error" / "interrupted" / empty for expected end) */ +} livekit_pb_data_stream_trailer_t; + +/* new DataPacket API */ +typedef struct livekit_pb_data_packet { + pb_size_t which_value; + union { + livekit_pb_user_packet_t user; + livekit_pb_sip_dtmf_t sip_dtmf; + livekit_pb_rpc_request_t rpc_request; + livekit_pb_rpc_ack_t rpc_ack; + livekit_pb_rpc_response_t rpc_response; + livekit_pb_data_stream_header_t stream_header; + livekit_pb_data_stream_chunk_t stream_chunk; + livekit_pb_data_stream_trailer_t stream_trailer; + } value; + /* participant identity of user that sent the message */ + char *participant_identity; + /* identities of participants who will receive the message (sent to all by default) */ + pb_size_t destination_identities_count; + char **destination_identities; + /* sequence number of reliable packet */ + uint32_t sequence; + /* sid of the user that sent the message */ + char *participant_sid; +} livekit_pb_data_packet_t; + +typedef struct livekit_pb_webhook_config { + pb_callback_t url; + pb_callback_t signing_key; +} livekit_pb_webhook_config_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _LIVEKIT_PB_AUDIO_CODEC_MIN LIVEKIT_PB_AUDIO_CODEC_DEFAULT_AC +#define _LIVEKIT_PB_AUDIO_CODEC_MAX LIVEKIT_PB_AUDIO_CODEC_AAC +#define _LIVEKIT_PB_AUDIO_CODEC_ARRAYSIZE ((livekit_pb_audio_codec_t)(LIVEKIT_PB_AUDIO_CODEC_AAC+1)) + +#define _LIVEKIT_PB_VIDEO_CODEC_MIN LIVEKIT_PB_VIDEO_CODEC_DEFAULT_VC +#define _LIVEKIT_PB_VIDEO_CODEC_MAX LIVEKIT_PB_VIDEO_CODEC_VP8 +#define _LIVEKIT_PB_VIDEO_CODEC_ARRAYSIZE ((livekit_pb_video_codec_t)(LIVEKIT_PB_VIDEO_CODEC_VP8+1)) + +#define _LIVEKIT_PB_IMAGE_CODEC_MIN LIVEKIT_PB_IMAGE_CODEC_IC_DEFAULT +#define _LIVEKIT_PB_IMAGE_CODEC_MAX LIVEKIT_PB_IMAGE_CODEC_IC_JPEG +#define _LIVEKIT_PB_IMAGE_CODEC_ARRAYSIZE ((livekit_pb_image_codec_t)(LIVEKIT_PB_IMAGE_CODEC_IC_JPEG+1)) + +#define _LIVEKIT_PB_BACKUP_CODEC_POLICY_MIN LIVEKIT_PB_BACKUP_CODEC_POLICY_PREFER_REGRESSION +#define _LIVEKIT_PB_BACKUP_CODEC_POLICY_MAX LIVEKIT_PB_BACKUP_CODEC_POLICY_REGRESSION +#define _LIVEKIT_PB_BACKUP_CODEC_POLICY_ARRAYSIZE ((livekit_pb_backup_codec_policy_t)(LIVEKIT_PB_BACKUP_CODEC_POLICY_REGRESSION+1)) + +#define _LIVEKIT_PB_TRACK_TYPE_MIN LIVEKIT_PB_TRACK_TYPE_AUDIO +#define _LIVEKIT_PB_TRACK_TYPE_MAX LIVEKIT_PB_TRACK_TYPE_DATA +#define _LIVEKIT_PB_TRACK_TYPE_ARRAYSIZE ((livekit_pb_track_type_t)(LIVEKIT_PB_TRACK_TYPE_DATA+1)) + +#define _LIVEKIT_PB_TRACK_SOURCE_MIN LIVEKIT_PB_TRACK_SOURCE_UNKNOWN +#define _LIVEKIT_PB_TRACK_SOURCE_MAX LIVEKIT_PB_TRACK_SOURCE_SCREEN_SHARE_AUDIO +#define _LIVEKIT_PB_TRACK_SOURCE_ARRAYSIZE ((livekit_pb_track_source_t)(LIVEKIT_PB_TRACK_SOURCE_SCREEN_SHARE_AUDIO+1)) + +#define _LIVEKIT_PB_VIDEO_QUALITY_MIN LIVEKIT_PB_VIDEO_QUALITY_LOW +#define _LIVEKIT_PB_VIDEO_QUALITY_MAX LIVEKIT_PB_VIDEO_QUALITY_OFF +#define _LIVEKIT_PB_VIDEO_QUALITY_ARRAYSIZE ((livekit_pb_video_quality_t)(LIVEKIT_PB_VIDEO_QUALITY_OFF+1)) + +#define _LIVEKIT_PB_CONNECTION_QUALITY_MIN LIVEKIT_PB_CONNECTION_QUALITY_POOR +#define _LIVEKIT_PB_CONNECTION_QUALITY_MAX LIVEKIT_PB_CONNECTION_QUALITY_LOST +#define _LIVEKIT_PB_CONNECTION_QUALITY_ARRAYSIZE ((livekit_pb_connection_quality_t)(LIVEKIT_PB_CONNECTION_QUALITY_LOST+1)) + +#define _LIVEKIT_PB_CLIENT_CONFIG_SETTING_MIN LIVEKIT_PB_CLIENT_CONFIG_SETTING_UNSET +#define _LIVEKIT_PB_CLIENT_CONFIG_SETTING_MAX LIVEKIT_PB_CLIENT_CONFIG_SETTING_ENABLED +#define _LIVEKIT_PB_CLIENT_CONFIG_SETTING_ARRAYSIZE ((livekit_pb_client_config_setting_t)(LIVEKIT_PB_CLIENT_CONFIG_SETTING_ENABLED+1)) + +#define _LIVEKIT_PB_DISCONNECT_REASON_MIN LIVEKIT_PB_DISCONNECT_REASON_UNKNOWN_REASON +#define _LIVEKIT_PB_DISCONNECT_REASON_MAX LIVEKIT_PB_DISCONNECT_REASON_MEDIA_FAILURE +#define _LIVEKIT_PB_DISCONNECT_REASON_ARRAYSIZE ((livekit_pb_disconnect_reason_t)(LIVEKIT_PB_DISCONNECT_REASON_MEDIA_FAILURE+1)) + +#define _LIVEKIT_PB_RECONNECT_REASON_MIN LIVEKIT_PB_RECONNECT_REASON_RR_UNKNOWN +#define _LIVEKIT_PB_RECONNECT_REASON_MAX LIVEKIT_PB_RECONNECT_REASON_RR_SWITCH_CANDIDATE +#define _LIVEKIT_PB_RECONNECT_REASON_ARRAYSIZE ((livekit_pb_reconnect_reason_t)(LIVEKIT_PB_RECONNECT_REASON_RR_SWITCH_CANDIDATE+1)) + +#define _LIVEKIT_PB_SUBSCRIPTION_ERROR_MIN LIVEKIT_PB_SUBSCRIPTION_ERROR_SE_UNKNOWN +#define _LIVEKIT_PB_SUBSCRIPTION_ERROR_MAX LIVEKIT_PB_SUBSCRIPTION_ERROR_SE_TRACK_NOTFOUND +#define _LIVEKIT_PB_SUBSCRIPTION_ERROR_ARRAYSIZE ((livekit_pb_subscription_error_t)(LIVEKIT_PB_SUBSCRIPTION_ERROR_SE_TRACK_NOTFOUND+1)) + +#define _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_STEREO +#define _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MAX LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_PRECONNECT_BUFFER +#define _LIVEKIT_PB_AUDIO_TRACK_FEATURE_ARRAYSIZE ((livekit_pb_audio_track_feature_t)(LIVEKIT_PB_AUDIO_TRACK_FEATURE_TF_PRECONNECT_BUFFER+1)) + +#define _LIVEKIT_PB_PARTICIPANT_INFO_STATE_MIN LIVEKIT_PB_PARTICIPANT_INFO_STATE_JOINING +#define _LIVEKIT_PB_PARTICIPANT_INFO_STATE_MAX LIVEKIT_PB_PARTICIPANT_INFO_STATE_DISCONNECTED +#define _LIVEKIT_PB_PARTICIPANT_INFO_STATE_ARRAYSIZE ((livekit_pb_participant_info_state_t)(LIVEKIT_PB_PARTICIPANT_INFO_STATE_DISCONNECTED+1)) + +#define _LIVEKIT_PB_PARTICIPANT_INFO_KIND_MIN LIVEKIT_PB_PARTICIPANT_INFO_KIND_STANDARD +#define _LIVEKIT_PB_PARTICIPANT_INFO_KIND_MAX LIVEKIT_PB_PARTICIPANT_INFO_KIND_AGENT +#define _LIVEKIT_PB_PARTICIPANT_INFO_KIND_ARRAYSIZE ((livekit_pb_participant_info_kind_t)(LIVEKIT_PB_PARTICIPANT_INFO_KIND_AGENT+1)) + +#define _LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_MIN LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_CLOUD_AGENT +#define _LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_MAX LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_FORWARDED +#define _LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_ARRAYSIZE ((livekit_pb_participant_info_kind_detail_t)(LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_FORWARDED+1)) + +#define _LIVEKIT_PB_ENCRYPTION_TYPE_MIN LIVEKIT_PB_ENCRYPTION_TYPE_NONE +#define _LIVEKIT_PB_ENCRYPTION_TYPE_MAX LIVEKIT_PB_ENCRYPTION_TYPE_CUSTOM +#define _LIVEKIT_PB_ENCRYPTION_TYPE_ARRAYSIZE ((livekit_pb_encryption_type_t)(LIVEKIT_PB_ENCRYPTION_TYPE_CUSTOM+1)) + +#define _LIVEKIT_PB_DATA_PACKET_KIND_MIN LIVEKIT_PB_DATA_PACKET_KIND_RELIABLE +#define _LIVEKIT_PB_DATA_PACKET_KIND_MAX LIVEKIT_PB_DATA_PACKET_KIND_LOSSY +#define _LIVEKIT_PB_DATA_PACKET_KIND_ARRAYSIZE ((livekit_pb_data_packet_kind_t)(LIVEKIT_PB_DATA_PACKET_KIND_LOSSY+1)) + +#define _LIVEKIT_PB_SERVER_INFO_EDITION_MIN LIVEKIT_PB_SERVER_INFO_EDITION_STANDARD +#define _LIVEKIT_PB_SERVER_INFO_EDITION_MAX LIVEKIT_PB_SERVER_INFO_EDITION_CLOUD +#define _LIVEKIT_PB_SERVER_INFO_EDITION_ARRAYSIZE ((livekit_pb_server_info_edition_t)(LIVEKIT_PB_SERVER_INFO_EDITION_CLOUD+1)) + +#define _LIVEKIT_PB_CLIENT_INFO_SDK_MIN LIVEKIT_PB_CLIENT_INFO_SDK_UNKNOWN +#define _LIVEKIT_PB_CLIENT_INFO_SDK_MAX LIVEKIT_PB_CLIENT_INFO_SDK_UNREAL +#define _LIVEKIT_PB_CLIENT_INFO_SDK_ARRAYSIZE ((livekit_pb_client_info_sdk_t)(LIVEKIT_PB_CLIENT_INFO_SDK_UNREAL+1)) + +#define _LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_MIN LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_CREATE +#define _LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_MAX LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_REACTION +#define _LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_ARRAYSIZE ((livekit_pb_data_stream_operation_type_t)(LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_REACTION+1)) + + + + + + + +#define livekit_pb_participant_info_t_state_ENUMTYPE livekit_pb_participant_info_state_t +#define livekit_pb_participant_info_t_kind_ENUMTYPE livekit_pb_participant_info_kind_t + + + +#define livekit_pb_track_info_t_type_ENUMTYPE livekit_pb_track_type_t +#define livekit_pb_track_info_t_audio_features_ENUMTYPE livekit_pb_audio_track_feature_t + +#define livekit_pb_video_layer_t_quality_ENUMTYPE livekit_pb_video_quality_t + + + + + + + + + + + + + + +#define livekit_pb_server_info_t_edition_ENUMTYPE livekit_pb_server_info_edition_t + +#define livekit_pb_client_info_t_sdk_ENUMTYPE livekit_pb_client_info_sdk_t + +#define livekit_pb_client_configuration_t_resume_connection_ENUMTYPE livekit_pb_client_config_setting_t +#define livekit_pb_client_configuration_t_force_relay_ENUMTYPE livekit_pb_client_config_setting_t + +#define livekit_pb_video_configuration_t_hardware_encoder_ENUMTYPE livekit_pb_client_config_setting_t + + + + + + + + + + + +#define livekit_pb_data_stream_text_header_t_operation_type_ENUMTYPE livekit_pb_data_stream_operation_type_t + + + + + + + +/* Initializer values for message structs */ +#define LIVEKIT_PB_PAGINATION_INIT_DEFAULT {{{NULL}, NULL}, 0} +#define LIVEKIT_PB_LIST_UPDATE_INIT_DEFAULT {{{NULL}, NULL}} +#define LIVEKIT_PB_ROOM_INIT_DEFAULT {NULL, NULL, NULL, 0, 0} +#define LIVEKIT_PB_CODEC_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_PLAYOUT_DELAY_INIT_DEFAULT {0, 0, 0} +#define LIVEKIT_PB_PARTICIPANT_PERMISSION_INIT_DEFAULT {0, 0, 0} +#define LIVEKIT_PB_PARTICIPANT_INFO_INIT_DEFAULT {NULL, NULL, _LIVEKIT_PB_PARTICIPANT_INFO_STATE_MIN, 0, NULL, NULL, NULL, LIVEKIT_PB_PARTICIPANT_PERMISSION_INIT_DEFAULT, NULL} +#define LIVEKIT_PB_ENCRYPTION_INIT_DEFAULT {0} +#define LIVEKIT_PB_SIMULCAST_CODEC_INFO_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_TRACK_INFO_INIT_DEFAULT {NULL, _LIVEKIT_PB_TRACK_TYPE_MIN, 0, NULL, 0, 0, {_LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN}} +#define LIVEKIT_PB_VIDEO_LAYER_INIT_DEFAULT {_LIVEKIT_PB_VIDEO_QUALITY_MIN, 0, 0} +#define LIVEKIT_PB_DATA_PACKET_INIT_DEFAULT {0, {LIVEKIT_PB_USER_PACKET_INIT_DEFAULT}, NULL, 0, NULL, 0, NULL} +#define LIVEKIT_PB_ACTIVE_SPEAKER_UPDATE_INIT_DEFAULT {{{NULL}, NULL}} +#define LIVEKIT_PB_SPEAKER_INFO_INIT_DEFAULT {{{NULL}, NULL}, 0, 0} +#define LIVEKIT_PB_USER_PACKET_INIT_DEFAULT {NULL, NULL} +#define LIVEKIT_PB_SIP_DTMF_INIT_DEFAULT {0, ""} +#define LIVEKIT_PB_TRANSCRIPTION_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}, 0, 0, 0, {{NULL}, NULL}} +#define LIVEKIT_PB_CHAT_MESSAGE_INIT_DEFAULT {{{NULL}, NULL}, 0, false, 0, {{NULL}, NULL}, 0, 0} +#define LIVEKIT_PB_RPC_REQUEST_INIT_DEFAULT {"", NULL, NULL, 0, 0} +#define LIVEKIT_PB_RPC_ACK_INIT_DEFAULT {""} +#define LIVEKIT_PB_RPC_RESPONSE_INIT_DEFAULT {"", 0, {NULL}} +#define LIVEKIT_PB_RPC_ERROR_INIT_DEFAULT {0, NULL} +#define LIVEKIT_PB_PARTICIPANT_TRACKS_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_SERVER_INFO_INIT_DEFAULT {_LIVEKIT_PB_SERVER_INFO_EDITION_MIN, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_CLIENT_INFO_INIT_DEFAULT {_LIVEKIT_PB_CLIENT_INFO_SDK_MIN, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_CLIENT_CONFIGURATION_INIT_DEFAULT {_LIVEKIT_PB_CLIENT_CONFIG_SETTING_MIN, _LIVEKIT_PB_CLIENT_CONFIG_SETTING_MIN} +#define LIVEKIT_PB_VIDEO_CONFIGURATION_INIT_DEFAULT {_LIVEKIT_PB_CLIENT_CONFIG_SETTING_MIN} +#define LIVEKIT_PB_DISABLED_CODECS_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_RTP_DRIFT_INIT_DEFAULT {false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, 0, 0, 0, 0, 0, 0, 0} +#define LIVEKIT_PB_RTP_STATS_INIT_DEFAULT {false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {{NULL}, NULL}, 0, 0, 0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, 0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, 0, 0, 0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, 0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT, 0, 0, 0, 0, 0, false, LIVEKIT_PB_RTP_DRIFT_INIT_DEFAULT, false, LIVEKIT_PB_RTP_DRIFT_INIT_DEFAULT, false, LIVEKIT_PB_RTP_DRIFT_INIT_DEFAULT, false, LIVEKIT_PB_RTP_DRIFT_INIT_DEFAULT} +#define LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_INIT_DEFAULT {0, 0} +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_INIT_DEFAULT {0, 0, 0, 0, 0, 0, 0} +#define LIVEKIT_PB_RTP_FORWARDER_STATE_INIT_DEFAULT {0, 0, 0, 0, 0, false, LIVEKIT_PB_RTP_MUNGER_STATE_INIT_DEFAULT, 0, {LIVEKIT_PB_VP8_MUNGER_STATE_INIT_DEFAULT}, {{NULL}, NULL}} +#define LIVEKIT_PB_RTP_MUNGER_STATE_INIT_DEFAULT {0, 0, 0, 0, 0, 0} +#define LIVEKIT_PB_VP8_MUNGER_STATE_INIT_DEFAULT {0, 0, 0, 0, 0, 0, 0} +#define LIVEKIT_PB_TIMED_VERSION_INIT_DEFAULT {0, 0} +#define LIVEKIT_PB_DATA_STREAM_INIT_DEFAULT {0} +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_INIT_DEFAULT {_LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_MIN, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_DATA_STREAM_BYTE_HEADER_INIT_DEFAULT {{{NULL}, NULL}} +#define LIVEKIT_PB_DATA_STREAM_HEADER_INIT_DEFAULT {"", 0, NULL, NULL, false, 0, 0, {LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_INIT_DEFAULT}} +#define LIVEKIT_PB_DATA_STREAM_CHUNK_INIT_DEFAULT {"", 0, NULL, 0} +#define LIVEKIT_PB_DATA_STREAM_TRAILER_INIT_DEFAULT {"", ""} +#define LIVEKIT_PB_WEBHOOK_CONFIG_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_PAGINATION_INIT_ZERO {{{NULL}, NULL}, 0} +#define LIVEKIT_PB_LIST_UPDATE_INIT_ZERO {{{NULL}, NULL}} +#define LIVEKIT_PB_ROOM_INIT_ZERO {NULL, NULL, NULL, 0, 0} +#define LIVEKIT_PB_CODEC_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_PLAYOUT_DELAY_INIT_ZERO {0, 0, 0} +#define LIVEKIT_PB_PARTICIPANT_PERMISSION_INIT_ZERO {0, 0, 0} +#define LIVEKIT_PB_PARTICIPANT_INFO_INIT_ZERO {NULL, NULL, _LIVEKIT_PB_PARTICIPANT_INFO_STATE_MIN, 0, NULL, NULL, NULL, LIVEKIT_PB_PARTICIPANT_PERMISSION_INIT_ZERO, NULL} +#define LIVEKIT_PB_ENCRYPTION_INIT_ZERO {0} +#define LIVEKIT_PB_SIMULCAST_CODEC_INFO_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_TRACK_INFO_INIT_ZERO {NULL, _LIVEKIT_PB_TRACK_TYPE_MIN, 0, NULL, 0, 0, {_LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN}} +#define LIVEKIT_PB_VIDEO_LAYER_INIT_ZERO {_LIVEKIT_PB_VIDEO_QUALITY_MIN, 0, 0} +#define LIVEKIT_PB_DATA_PACKET_INIT_ZERO {0, {LIVEKIT_PB_USER_PACKET_INIT_ZERO}, NULL, 0, NULL, 0, NULL} +#define LIVEKIT_PB_ACTIVE_SPEAKER_UPDATE_INIT_ZERO {{{NULL}, NULL}} +#define LIVEKIT_PB_SPEAKER_INFO_INIT_ZERO {{{NULL}, NULL}, 0, 0} +#define LIVEKIT_PB_USER_PACKET_INIT_ZERO {NULL, NULL} +#define LIVEKIT_PB_SIP_DTMF_INIT_ZERO {0, ""} +#define LIVEKIT_PB_TRANSCRIPTION_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}, 0, 0, 0, {{NULL}, NULL}} +#define LIVEKIT_PB_CHAT_MESSAGE_INIT_ZERO {{{NULL}, NULL}, 0, false, 0, {{NULL}, NULL}, 0, 0} +#define LIVEKIT_PB_RPC_REQUEST_INIT_ZERO {"", NULL, NULL, 0, 0} +#define LIVEKIT_PB_RPC_ACK_INIT_ZERO {""} +#define LIVEKIT_PB_RPC_RESPONSE_INIT_ZERO {"", 0, {NULL}} +#define LIVEKIT_PB_RPC_ERROR_INIT_ZERO {0, NULL} +#define LIVEKIT_PB_PARTICIPANT_TRACKS_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_SERVER_INFO_INIT_ZERO {_LIVEKIT_PB_SERVER_INFO_EDITION_MIN, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_CLIENT_INFO_INIT_ZERO {_LIVEKIT_PB_CLIENT_INFO_SDK_MIN, {{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_CLIENT_CONFIGURATION_INIT_ZERO {_LIVEKIT_PB_CLIENT_CONFIG_SETTING_MIN, _LIVEKIT_PB_CLIENT_CONFIG_SETTING_MIN} +#define LIVEKIT_PB_VIDEO_CONFIGURATION_INIT_ZERO {_LIVEKIT_PB_CLIENT_CONFIG_SETTING_MIN} +#define LIVEKIT_PB_DISABLED_CODECS_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_RTP_DRIFT_INIT_ZERO {false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, 0, 0, 0, 0, 0, 0, 0} +#define LIVEKIT_PB_RTP_STATS_INIT_ZERO {false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {{NULL}, NULL}, 0, 0, 0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, 0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, 0, 0, 0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, 0, false, GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO, 0, 0, 0, 0, 0, false, LIVEKIT_PB_RTP_DRIFT_INIT_ZERO, false, LIVEKIT_PB_RTP_DRIFT_INIT_ZERO, false, LIVEKIT_PB_RTP_DRIFT_INIT_ZERO, false, LIVEKIT_PB_RTP_DRIFT_INIT_ZERO} +#define LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_INIT_ZERO {0, 0} +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_INIT_ZERO {0, 0, 0, 0, 0, 0, 0} +#define LIVEKIT_PB_RTP_FORWARDER_STATE_INIT_ZERO {0, 0, 0, 0, 0, false, LIVEKIT_PB_RTP_MUNGER_STATE_INIT_ZERO, 0, {LIVEKIT_PB_VP8_MUNGER_STATE_INIT_ZERO}, {{NULL}, NULL}} +#define LIVEKIT_PB_RTP_MUNGER_STATE_INIT_ZERO {0, 0, 0, 0, 0, 0} +#define LIVEKIT_PB_VP8_MUNGER_STATE_INIT_ZERO {0, 0, 0, 0, 0, 0, 0} +#define LIVEKIT_PB_TIMED_VERSION_INIT_ZERO {0, 0} +#define LIVEKIT_PB_DATA_STREAM_INIT_ZERO {0} +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_INIT_ZERO {_LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_MIN, 0, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_DATA_STREAM_BYTE_HEADER_INIT_ZERO {{{NULL}, NULL}} +#define LIVEKIT_PB_DATA_STREAM_HEADER_INIT_ZERO {"", 0, NULL, NULL, false, 0, 0, {LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_INIT_ZERO}} +#define LIVEKIT_PB_DATA_STREAM_CHUNK_INIT_ZERO {"", 0, NULL, 0} +#define LIVEKIT_PB_DATA_STREAM_TRAILER_INIT_ZERO {"", ""} +#define LIVEKIT_PB_WEBHOOK_CONFIG_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define LIVEKIT_PB_PAGINATION_AFTER_ID_TAG 1 +#define LIVEKIT_PB_PAGINATION_LIMIT_TAG 2 +#define LIVEKIT_PB_LIST_UPDATE_SET_TAG 1 +#define LIVEKIT_PB_ROOM_SID_TAG 1 +#define LIVEKIT_PB_ROOM_NAME_TAG 2 +#define LIVEKIT_PB_ROOM_METADATA_TAG 8 +#define LIVEKIT_PB_ROOM_NUM_PARTICIPANTS_TAG 9 +#define LIVEKIT_PB_ROOM_ACTIVE_RECORDING_TAG 10 +#define LIVEKIT_PB_CODEC_MIME_TAG 1 +#define LIVEKIT_PB_CODEC_FMTP_LINE_TAG 2 +#define LIVEKIT_PB_PLAYOUT_DELAY_ENABLED_TAG 1 +#define LIVEKIT_PB_PLAYOUT_DELAY_MIN_TAG 2 +#define LIVEKIT_PB_PLAYOUT_DELAY_MAX_TAG 3 +#define LIVEKIT_PB_PARTICIPANT_PERMISSION_CAN_SUBSCRIBE_TAG 1 +#define LIVEKIT_PB_PARTICIPANT_PERMISSION_CAN_PUBLISH_TAG 2 +#define LIVEKIT_PB_PARTICIPANT_PERMISSION_CAN_PUBLISH_DATA_TAG 3 +#define LIVEKIT_PB_PARTICIPANT_INFO_SID_TAG 1 +#define LIVEKIT_PB_PARTICIPANT_INFO_IDENTITY_TAG 2 +#define LIVEKIT_PB_PARTICIPANT_INFO_STATE_TAG 3 +#define LIVEKIT_PB_PARTICIPANT_INFO_TRACKS_TAG 4 +#define LIVEKIT_PB_PARTICIPANT_INFO_METADATA_TAG 5 +#define LIVEKIT_PB_PARTICIPANT_INFO_NAME_TAG 9 +#define LIVEKIT_PB_PARTICIPANT_INFO_PERMISSION_TAG 11 +#define LIVEKIT_PB_PARTICIPANT_INFO_KIND_TAG 14 +#define LIVEKIT_PB_SIMULCAST_CODEC_INFO_MIME_TYPE_TAG 1 +#define LIVEKIT_PB_SIMULCAST_CODEC_INFO_MID_TAG 2 +#define LIVEKIT_PB_SIMULCAST_CODEC_INFO_CID_TAG 3 +#define LIVEKIT_PB_SIMULCAST_CODEC_INFO_LAYERS_TAG 4 +#define LIVEKIT_PB_TRACK_INFO_SID_TAG 1 +#define LIVEKIT_PB_TRACK_INFO_TYPE_TAG 2 +#define LIVEKIT_PB_TRACK_INFO_MUTED_TAG 4 +#define LIVEKIT_PB_TRACK_INFO_MIME_TYPE_TAG 11 +#define LIVEKIT_PB_TRACK_INFO_STEREO_TAG 14 +#define LIVEKIT_PB_TRACK_INFO_AUDIO_FEATURES_TAG 19 +#define LIVEKIT_PB_VIDEO_LAYER_QUALITY_TAG 1 +#define LIVEKIT_PB_VIDEO_LAYER_WIDTH_TAG 2 +#define LIVEKIT_PB_VIDEO_LAYER_HEIGHT_TAG 3 +#define LIVEKIT_PB_ACTIVE_SPEAKER_UPDATE_SPEAKERS_TAG 1 +#define LIVEKIT_PB_SPEAKER_INFO_SID_TAG 1 +#define LIVEKIT_PB_SPEAKER_INFO_LEVEL_TAG 2 +#define LIVEKIT_PB_SPEAKER_INFO_ACTIVE_TAG 3 +#define LIVEKIT_PB_USER_PACKET_PAYLOAD_TAG 2 +#define LIVEKIT_PB_USER_PACKET_TOPIC_TAG 4 +#define LIVEKIT_PB_SIP_DTMF_CODE_TAG 3 +#define LIVEKIT_PB_SIP_DTMF_DIGIT_TAG 4 +#define LIVEKIT_PB_TRANSCRIPTION_TRANSCRIBED_PARTICIPANT_IDENTITY_TAG 2 +#define LIVEKIT_PB_TRANSCRIPTION_TRACK_ID_TAG 3 +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENTS_TAG 4 +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_ID_TAG 1 +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_TEXT_TAG 2 +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_START_TIME_TAG 3 +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_END_TIME_TAG 4 +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_FINAL_TAG 5 +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_LANGUAGE_TAG 6 +#define LIVEKIT_PB_CHAT_MESSAGE_ID_TAG 1 +#define LIVEKIT_PB_CHAT_MESSAGE_TIMESTAMP_TAG 2 +#define LIVEKIT_PB_CHAT_MESSAGE_EDIT_TIMESTAMP_TAG 3 +#define LIVEKIT_PB_CHAT_MESSAGE_MESSAGE_TAG 4 +#define LIVEKIT_PB_CHAT_MESSAGE_DELETED_TAG 5 +#define LIVEKIT_PB_CHAT_MESSAGE_GENERATED_TAG 6 +#define LIVEKIT_PB_RPC_REQUEST_ID_TAG 1 +#define LIVEKIT_PB_RPC_REQUEST_METHOD_TAG 2 +#define LIVEKIT_PB_RPC_REQUEST_PAYLOAD_TAG 3 +#define LIVEKIT_PB_RPC_REQUEST_RESPONSE_TIMEOUT_MS_TAG 4 +#define LIVEKIT_PB_RPC_REQUEST_VERSION_TAG 5 +#define LIVEKIT_PB_RPC_ACK_REQUEST_ID_TAG 1 +#define LIVEKIT_PB_RPC_ERROR_CODE_TAG 1 +#define LIVEKIT_PB_RPC_ERROR_DATA_TAG 3 +#define LIVEKIT_PB_RPC_RESPONSE_REQUEST_ID_TAG 1 +#define LIVEKIT_PB_RPC_RESPONSE_PAYLOAD_TAG 2 +#define LIVEKIT_PB_RPC_RESPONSE_ERROR_TAG 3 +#define LIVEKIT_PB_PARTICIPANT_TRACKS_PARTICIPANT_SID_TAG 1 +#define LIVEKIT_PB_PARTICIPANT_TRACKS_TRACK_SIDS_TAG 2 +#define LIVEKIT_PB_SERVER_INFO_EDITION_TAG 1 +#define LIVEKIT_PB_SERVER_INFO_VERSION_TAG 2 +#define LIVEKIT_PB_SERVER_INFO_PROTOCOL_TAG 3 +#define LIVEKIT_PB_SERVER_INFO_REGION_TAG 4 +#define LIVEKIT_PB_SERVER_INFO_NODE_ID_TAG 5 +#define LIVEKIT_PB_SERVER_INFO_DEBUG_INFO_TAG 6 +#define LIVEKIT_PB_SERVER_INFO_AGENT_PROTOCOL_TAG 7 +#define LIVEKIT_PB_CLIENT_INFO_SDK_TAG 1 +#define LIVEKIT_PB_CLIENT_INFO_VERSION_TAG 2 +#define LIVEKIT_PB_CLIENT_INFO_PROTOCOL_TAG 3 +#define LIVEKIT_PB_CLIENT_INFO_OS_TAG 4 +#define LIVEKIT_PB_CLIENT_INFO_OS_VERSION_TAG 5 +#define LIVEKIT_PB_CLIENT_INFO_DEVICE_MODEL_TAG 6 +#define LIVEKIT_PB_CLIENT_INFO_BROWSER_TAG 7 +#define LIVEKIT_PB_CLIENT_INFO_BROWSER_VERSION_TAG 8 +#define LIVEKIT_PB_CLIENT_INFO_ADDRESS_TAG 9 +#define LIVEKIT_PB_CLIENT_INFO_NETWORK_TAG 10 +#define LIVEKIT_PB_CLIENT_INFO_OTHER_SDKS_TAG 11 +#define LIVEKIT_PB_CLIENT_CONFIGURATION_RESUME_CONNECTION_TAG 3 +#define LIVEKIT_PB_CLIENT_CONFIGURATION_FORCE_RELAY_TAG 5 +#define LIVEKIT_PB_VIDEO_CONFIGURATION_HARDWARE_ENCODER_TAG 1 +#define LIVEKIT_PB_DISABLED_CODECS_CODECS_TAG 1 +#define LIVEKIT_PB_DISABLED_CODECS_PUBLISH_TAG 2 +#define LIVEKIT_PB_RTP_DRIFT_START_TIME_TAG 1 +#define LIVEKIT_PB_RTP_DRIFT_END_TIME_TAG 2 +#define LIVEKIT_PB_RTP_DRIFT_DURATION_TAG 3 +#define LIVEKIT_PB_RTP_DRIFT_START_TIMESTAMP_TAG 4 +#define LIVEKIT_PB_RTP_DRIFT_END_TIMESTAMP_TAG 5 +#define LIVEKIT_PB_RTP_DRIFT_RTP_CLOCK_TICKS_TAG 6 +#define LIVEKIT_PB_RTP_DRIFT_DRIFT_SAMPLES_TAG 7 +#define LIVEKIT_PB_RTP_DRIFT_DRIFT_MS_TAG 8 +#define LIVEKIT_PB_RTP_DRIFT_CLOCK_RATE_TAG 9 +#define LIVEKIT_PB_RTP_STATS_START_TIME_TAG 1 +#define LIVEKIT_PB_RTP_STATS_END_TIME_TAG 2 +#define LIVEKIT_PB_RTP_STATS_DURATION_TAG 3 +#define LIVEKIT_PB_RTP_STATS_PACKETS_TAG 4 +#define LIVEKIT_PB_RTP_STATS_PACKET_RATE_TAG 5 +#define LIVEKIT_PB_RTP_STATS_BYTES_TAG 6 +#define LIVEKIT_PB_RTP_STATS_BITRATE_TAG 7 +#define LIVEKIT_PB_RTP_STATS_PACKETS_LOST_TAG 8 +#define LIVEKIT_PB_RTP_STATS_PACKET_LOSS_RATE_TAG 9 +#define LIVEKIT_PB_RTP_STATS_PACKET_LOSS_PERCENTAGE_TAG 10 +#define LIVEKIT_PB_RTP_STATS_PACKETS_DUPLICATE_TAG 11 +#define LIVEKIT_PB_RTP_STATS_PACKET_DUPLICATE_RATE_TAG 12 +#define LIVEKIT_PB_RTP_STATS_BYTES_DUPLICATE_TAG 13 +#define LIVEKIT_PB_RTP_STATS_BITRATE_DUPLICATE_TAG 14 +#define LIVEKIT_PB_RTP_STATS_PACKETS_PADDING_TAG 15 +#define LIVEKIT_PB_RTP_STATS_PACKET_PADDING_RATE_TAG 16 +#define LIVEKIT_PB_RTP_STATS_BYTES_PADDING_TAG 17 +#define LIVEKIT_PB_RTP_STATS_BITRATE_PADDING_TAG 18 +#define LIVEKIT_PB_RTP_STATS_PACKETS_OUT_OF_ORDER_TAG 19 +#define LIVEKIT_PB_RTP_STATS_FRAMES_TAG 20 +#define LIVEKIT_PB_RTP_STATS_FRAME_RATE_TAG 21 +#define LIVEKIT_PB_RTP_STATS_JITTER_CURRENT_TAG 22 +#define LIVEKIT_PB_RTP_STATS_JITTER_MAX_TAG 23 +#define LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_TAG 24 +#define LIVEKIT_PB_RTP_STATS_NACKS_TAG 25 +#define LIVEKIT_PB_RTP_STATS_NACK_MISSES_TAG 26 +#define LIVEKIT_PB_RTP_STATS_PLIS_TAG 27 +#define LIVEKIT_PB_RTP_STATS_LAST_PLI_TAG 28 +#define LIVEKIT_PB_RTP_STATS_FIRS_TAG 29 +#define LIVEKIT_PB_RTP_STATS_LAST_FIR_TAG 30 +#define LIVEKIT_PB_RTP_STATS_RTT_CURRENT_TAG 31 +#define LIVEKIT_PB_RTP_STATS_RTT_MAX_TAG 32 +#define LIVEKIT_PB_RTP_STATS_KEY_FRAMES_TAG 33 +#define LIVEKIT_PB_RTP_STATS_LAST_KEY_FRAME_TAG 34 +#define LIVEKIT_PB_RTP_STATS_LAYER_LOCK_PLIS_TAG 35 +#define LIVEKIT_PB_RTP_STATS_LAST_LAYER_LOCK_PLI_TAG 36 +#define LIVEKIT_PB_RTP_STATS_NACK_ACKS_TAG 37 +#define LIVEKIT_PB_RTP_STATS_NACK_REPEATED_TAG 38 +#define LIVEKIT_PB_RTP_STATS_HEADER_BYTES_TAG 39 +#define LIVEKIT_PB_RTP_STATS_HEADER_BYTES_DUPLICATE_TAG 40 +#define LIVEKIT_PB_RTP_STATS_HEADER_BYTES_PADDING_TAG 41 +#define LIVEKIT_PB_RTP_STATS_PACKET_DRIFT_TAG 44 +#define LIVEKIT_PB_RTP_STATS_NTP_REPORT_DRIFT_TAG 45 +#define LIVEKIT_PB_RTP_STATS_REBASED_REPORT_DRIFT_TAG 46 +#define LIVEKIT_PB_RTP_STATS_RECEIVED_REPORT_DRIFT_TAG 47 +#define LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_KEY_TAG 1 +#define LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_VALUE_TAG 2 +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_RTP_TIMESTAMP_TAG 1 +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_RTP_TIMESTAMP_EXT_TAG 2 +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_NTP_TIMESTAMP_TAG 3 +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_AT_TAG 4 +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_AT_ADJUSTED_TAG 5 +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_PACKETS_TAG 6 +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_OCTETS_TAG 7 +#define LIVEKIT_PB_RTP_MUNGER_STATE_EXT_LAST_SEQUENCE_NUMBER_TAG 1 +#define LIVEKIT_PB_RTP_MUNGER_STATE_EXT_SECOND_LAST_SEQUENCE_NUMBER_TAG 2 +#define LIVEKIT_PB_RTP_MUNGER_STATE_EXT_LAST_TIMESTAMP_TAG 3 +#define LIVEKIT_PB_RTP_MUNGER_STATE_EXT_SECOND_LAST_TIMESTAMP_TAG 4 +#define LIVEKIT_PB_RTP_MUNGER_STATE_LAST_MARKER_TAG 5 +#define LIVEKIT_PB_RTP_MUNGER_STATE_SECOND_LAST_MARKER_TAG 6 +#define LIVEKIT_PB_VP8_MUNGER_STATE_EXT_LAST_PICTURE_ID_TAG 1 +#define LIVEKIT_PB_VP8_MUNGER_STATE_PICTURE_ID_USED_TAG 2 +#define LIVEKIT_PB_VP8_MUNGER_STATE_LAST_TL0_PIC_IDX_TAG 3 +#define LIVEKIT_PB_VP8_MUNGER_STATE_TL0_PIC_IDX_USED_TAG 4 +#define LIVEKIT_PB_VP8_MUNGER_STATE_TID_USED_TAG 5 +#define LIVEKIT_PB_VP8_MUNGER_STATE_LAST_KEY_IDX_TAG 6 +#define LIVEKIT_PB_VP8_MUNGER_STATE_KEY_IDX_USED_TAG 7 +#define LIVEKIT_PB_RTP_FORWARDER_STATE_STARTED_TAG 1 +#define LIVEKIT_PB_RTP_FORWARDER_STATE_REFERENCE_LAYER_SPATIAL_TAG 2 +#define LIVEKIT_PB_RTP_FORWARDER_STATE_PRE_START_TIME_TAG 3 +#define LIVEKIT_PB_RTP_FORWARDER_STATE_EXT_FIRST_TIMESTAMP_TAG 4 +#define LIVEKIT_PB_RTP_FORWARDER_STATE_DUMMY_START_TIMESTAMP_OFFSET_TAG 5 +#define LIVEKIT_PB_RTP_FORWARDER_STATE_RTP_MUNGER_TAG 6 +#define LIVEKIT_PB_RTP_FORWARDER_STATE_VP8_MUNGER_TAG 7 +#define LIVEKIT_PB_RTP_FORWARDER_STATE_SENDER_REPORT_STATE_TAG 8 +#define LIVEKIT_PB_TIMED_VERSION_UNIX_MICRO_TAG 1 +#define LIVEKIT_PB_TIMED_VERSION_TICKS_TAG 2 +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_OPERATION_TYPE_TAG 1 +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_VERSION_TAG 2 +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_REPLY_TO_STREAM_ID_TAG 3 +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_ATTACHED_STREAM_IDS_TAG 4 +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_GENERATED_TAG 5 +#define LIVEKIT_PB_DATA_STREAM_BYTE_HEADER_NAME_TAG 1 +#define LIVEKIT_PB_DATA_STREAM_HEADER_STREAM_ID_TAG 1 +#define LIVEKIT_PB_DATA_STREAM_HEADER_TIMESTAMP_TAG 2 +#define LIVEKIT_PB_DATA_STREAM_HEADER_TOPIC_TAG 3 +#define LIVEKIT_PB_DATA_STREAM_HEADER_MIME_TYPE_TAG 4 +#define LIVEKIT_PB_DATA_STREAM_HEADER_TOTAL_LENGTH_TAG 5 +#define LIVEKIT_PB_DATA_STREAM_HEADER_TEXT_HEADER_TAG 9 +#define LIVEKIT_PB_DATA_STREAM_HEADER_BYTE_HEADER_TAG 10 +#define LIVEKIT_PB_DATA_STREAM_CHUNK_STREAM_ID_TAG 1 +#define LIVEKIT_PB_DATA_STREAM_CHUNK_CHUNK_INDEX_TAG 2 +#define LIVEKIT_PB_DATA_STREAM_CHUNK_CONTENT_TAG 3 +#define LIVEKIT_PB_DATA_STREAM_CHUNK_VERSION_TAG 4 +#define LIVEKIT_PB_DATA_STREAM_TRAILER_STREAM_ID_TAG 1 +#define LIVEKIT_PB_DATA_STREAM_TRAILER_REASON_TAG 2 +#define LIVEKIT_PB_DATA_PACKET_USER_TAG 2 +#define LIVEKIT_PB_DATA_PACKET_SIP_DTMF_TAG 6 +#define LIVEKIT_PB_DATA_PACKET_RPC_REQUEST_TAG 10 +#define LIVEKIT_PB_DATA_PACKET_RPC_ACK_TAG 11 +#define LIVEKIT_PB_DATA_PACKET_RPC_RESPONSE_TAG 12 +#define LIVEKIT_PB_DATA_PACKET_STREAM_HEADER_TAG 13 +#define LIVEKIT_PB_DATA_PACKET_STREAM_CHUNK_TAG 14 +#define LIVEKIT_PB_DATA_PACKET_STREAM_TRAILER_TAG 15 +#define LIVEKIT_PB_DATA_PACKET_PARTICIPANT_IDENTITY_TAG 4 +#define LIVEKIT_PB_DATA_PACKET_DESTINATION_IDENTITIES_TAG 5 +#define LIVEKIT_PB_DATA_PACKET_SEQUENCE_TAG 16 +#define LIVEKIT_PB_DATA_PACKET_PARTICIPANT_SID_TAG 17 +#define LIVEKIT_PB_WEBHOOK_CONFIG_URL_TAG 1 +#define LIVEKIT_PB_WEBHOOK_CONFIG_SIGNING_KEY_TAG 2 + +/* Struct field encoding specification for nanopb */ +#define LIVEKIT_PB_PAGINATION_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, after_id, 1) \ +X(a, STATIC, SINGULAR, INT32, limit, 2) +#define LIVEKIT_PB_PAGINATION_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_PAGINATION_DEFAULT NULL + +#define LIVEKIT_PB_LIST_UPDATE_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, STRING, set, 1) +#define LIVEKIT_PB_LIST_UPDATE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_LIST_UPDATE_DEFAULT NULL + +#define LIVEKIT_PB_ROOM_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, STRING, sid, 1) \ +X(a, POINTER, SINGULAR, STRING, name, 2) \ +X(a, POINTER, SINGULAR, STRING, metadata, 8) \ +X(a, STATIC, SINGULAR, UINT32, num_participants, 9) \ +X(a, STATIC, SINGULAR, BOOL, active_recording, 10) +#define LIVEKIT_PB_ROOM_CALLBACK NULL +#define LIVEKIT_PB_ROOM_DEFAULT NULL + +#define LIVEKIT_PB_CODEC_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, mime, 1) \ +X(a, CALLBACK, SINGULAR, STRING, fmtp_line, 2) +#define LIVEKIT_PB_CODEC_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_CODEC_DEFAULT NULL + +#define LIVEKIT_PB_PLAYOUT_DELAY_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 1) \ +X(a, STATIC, SINGULAR, UINT32, min, 2) \ +X(a, STATIC, SINGULAR, UINT32, max, 3) +#define LIVEKIT_PB_PLAYOUT_DELAY_CALLBACK NULL +#define LIVEKIT_PB_PLAYOUT_DELAY_DEFAULT NULL + +#define LIVEKIT_PB_PARTICIPANT_PERMISSION_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, can_subscribe, 1) \ +X(a, STATIC, SINGULAR, BOOL, can_publish, 2) \ +X(a, STATIC, SINGULAR, BOOL, can_publish_data, 3) +#define LIVEKIT_PB_PARTICIPANT_PERMISSION_CALLBACK NULL +#define LIVEKIT_PB_PARTICIPANT_PERMISSION_DEFAULT NULL + +#define LIVEKIT_PB_PARTICIPANT_INFO_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, STRING, sid, 1) \ +X(a, POINTER, SINGULAR, STRING, identity, 2) \ +X(a, STATIC, SINGULAR, UENUM, state, 3) \ +X(a, POINTER, REPEATED, MESSAGE, tracks, 4) \ +X(a, POINTER, SINGULAR, STRING, metadata, 5) \ +X(a, POINTER, SINGULAR, STRING, name, 9) \ +X(a, STATIC, REQUIRED, MESSAGE, permission, 11) \ +X(a, POINTER, SINGULAR, UENUM, kind, 14) +#define LIVEKIT_PB_PARTICIPANT_INFO_CALLBACK NULL +#define LIVEKIT_PB_PARTICIPANT_INFO_DEFAULT NULL +#define livekit_pb_participant_info_t_tracks_MSGTYPE livekit_pb_track_info_t +#define livekit_pb_participant_info_t_permission_MSGTYPE livekit_pb_participant_permission_t + +#define LIVEKIT_PB_ENCRYPTION_FIELDLIST(X, a) \ + +#define LIVEKIT_PB_ENCRYPTION_CALLBACK NULL +#define LIVEKIT_PB_ENCRYPTION_DEFAULT NULL + +#define LIVEKIT_PB_SIMULCAST_CODEC_INFO_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, mime_type, 1) \ +X(a, CALLBACK, SINGULAR, STRING, mid, 2) \ +X(a, CALLBACK, SINGULAR, STRING, cid, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, layers, 4) +#define LIVEKIT_PB_SIMULCAST_CODEC_INFO_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SIMULCAST_CODEC_INFO_DEFAULT NULL +#define livekit_pb_simulcast_codec_info_t_layers_MSGTYPE livekit_pb_video_layer_t + +#define LIVEKIT_PB_TRACK_INFO_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, STRING, sid, 1) \ +X(a, STATIC, SINGULAR, UENUM, type, 2) \ +X(a, STATIC, SINGULAR, BOOL, muted, 4) \ +X(a, POINTER, SINGULAR, STRING, mime_type, 11) \ +X(a, STATIC, SINGULAR, BOOL, stereo, 14) \ +X(a, STATIC, REPEATED, UENUM, audio_features, 19) +#define LIVEKIT_PB_TRACK_INFO_CALLBACK NULL +#define LIVEKIT_PB_TRACK_INFO_DEFAULT NULL + +#define LIVEKIT_PB_VIDEO_LAYER_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, quality, 1) \ +X(a, STATIC, SINGULAR, UINT32, width, 2) \ +X(a, STATIC, SINGULAR, UINT32, height, 3) +#define LIVEKIT_PB_VIDEO_LAYER_CALLBACK NULL +#define LIVEKIT_PB_VIDEO_LAYER_DEFAULT NULL + +#define LIVEKIT_PB_DATA_PACKET_FIELDLIST(X, a) \ +X(a, STATIC, ONEOF, MESSAGE, (value,user,value.user), 2) \ +X(a, POINTER, SINGULAR, STRING, participant_identity, 4) \ +X(a, POINTER, REPEATED, STRING, destination_identities, 5) \ +X(a, STATIC, ONEOF, MESSAGE, (value,sip_dtmf,value.sip_dtmf), 6) \ +X(a, STATIC, ONEOF, MESSAGE, (value,rpc_request,value.rpc_request), 10) \ +X(a, STATIC, ONEOF, MESSAGE, (value,rpc_ack,value.rpc_ack), 11) \ +X(a, STATIC, ONEOF, MESSAGE, (value,rpc_response,value.rpc_response), 12) \ +X(a, STATIC, ONEOF, MESSAGE, (value,stream_header,value.stream_header), 13) \ +X(a, STATIC, ONEOF, MESSAGE, (value,stream_chunk,value.stream_chunk), 14) \ +X(a, STATIC, ONEOF, MESSAGE, (value,stream_trailer,value.stream_trailer), 15) \ +X(a, STATIC, SINGULAR, UINT32, sequence, 16) \ +X(a, POINTER, SINGULAR, STRING, participant_sid, 17) +#define LIVEKIT_PB_DATA_PACKET_CALLBACK NULL +#define LIVEKIT_PB_DATA_PACKET_DEFAULT NULL +#define livekit_pb_data_packet_t_value_user_MSGTYPE livekit_pb_user_packet_t +#define livekit_pb_data_packet_t_value_sip_dtmf_MSGTYPE livekit_pb_sip_dtmf_t +#define livekit_pb_data_packet_t_value_rpc_request_MSGTYPE livekit_pb_rpc_request_t +#define livekit_pb_data_packet_t_value_rpc_ack_MSGTYPE livekit_pb_rpc_ack_t +#define livekit_pb_data_packet_t_value_rpc_response_MSGTYPE livekit_pb_rpc_response_t +#define livekit_pb_data_packet_t_value_stream_header_MSGTYPE livekit_pb_data_stream_header_t +#define livekit_pb_data_packet_t_value_stream_chunk_MSGTYPE livekit_pb_data_stream_chunk_t +#define livekit_pb_data_packet_t_value_stream_trailer_MSGTYPE livekit_pb_data_stream_trailer_t + +#define LIVEKIT_PB_ACTIVE_SPEAKER_UPDATE_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, speakers, 1) +#define LIVEKIT_PB_ACTIVE_SPEAKER_UPDATE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_ACTIVE_SPEAKER_UPDATE_DEFAULT NULL +#define livekit_pb_active_speaker_update_t_speakers_MSGTYPE livekit_pb_speaker_info_t + +#define LIVEKIT_PB_SPEAKER_INFO_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, sid, 1) \ +X(a, STATIC, SINGULAR, FLOAT, level, 2) \ +X(a, STATIC, SINGULAR, BOOL, active, 3) +#define LIVEKIT_PB_SPEAKER_INFO_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SPEAKER_INFO_DEFAULT NULL + +#define LIVEKIT_PB_USER_PACKET_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, BYTES, payload, 2) \ +X(a, POINTER, OPTIONAL, STRING, topic, 4) +#define LIVEKIT_PB_USER_PACKET_CALLBACK NULL +#define LIVEKIT_PB_USER_PACKET_DEFAULT NULL + +#define LIVEKIT_PB_SIP_DTMF_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, code, 3) \ +X(a, STATIC, SINGULAR, STRING, digit, 4) +#define LIVEKIT_PB_SIP_DTMF_CALLBACK NULL +#define LIVEKIT_PB_SIP_DTMF_DEFAULT NULL + +#define LIVEKIT_PB_TRANSCRIPTION_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, transcribed_participant_identity, 2) \ +X(a, CALLBACK, SINGULAR, STRING, track_id, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, segments, 4) +#define LIVEKIT_PB_TRANSCRIPTION_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_TRANSCRIPTION_DEFAULT NULL +#define livekit_pb_transcription_t_segments_MSGTYPE livekit_pb_transcription_segment_t + +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, id, 1) \ +X(a, CALLBACK, SINGULAR, STRING, text, 2) \ +X(a, STATIC, SINGULAR, UINT64, start_time, 3) \ +X(a, STATIC, SINGULAR, UINT64, end_time, 4) \ +X(a, STATIC, SINGULAR, BOOL, final, 5) \ +X(a, CALLBACK, SINGULAR, STRING, language, 6) +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_DEFAULT NULL + +#define LIVEKIT_PB_CHAT_MESSAGE_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, id, 1) \ +X(a, STATIC, SINGULAR, INT64, timestamp, 2) \ +X(a, STATIC, OPTIONAL, INT64, edit_timestamp, 3) \ +X(a, CALLBACK, SINGULAR, STRING, message, 4) \ +X(a, STATIC, SINGULAR, BOOL, deleted, 5) \ +X(a, STATIC, SINGULAR, BOOL, generated, 6) +#define LIVEKIT_PB_CHAT_MESSAGE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_CHAT_MESSAGE_DEFAULT NULL + +#define LIVEKIT_PB_RPC_REQUEST_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, id, 1) \ +X(a, POINTER, SINGULAR, STRING, method, 2) \ +X(a, POINTER, SINGULAR, STRING, payload, 3) \ +X(a, STATIC, SINGULAR, UINT32, response_timeout_ms, 4) \ +X(a, STATIC, SINGULAR, UINT32, version, 5) +#define LIVEKIT_PB_RPC_REQUEST_CALLBACK NULL +#define LIVEKIT_PB_RPC_REQUEST_DEFAULT NULL + +#define LIVEKIT_PB_RPC_ACK_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, request_id, 1) +#define LIVEKIT_PB_RPC_ACK_CALLBACK NULL +#define LIVEKIT_PB_RPC_ACK_DEFAULT NULL + +#define LIVEKIT_PB_RPC_RESPONSE_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, request_id, 1) \ +X(a, POINTER, ONEOF, STRING, (value,payload,value.payload), 2) \ +X(a, STATIC, ONEOF, MESSAGE, (value,error,value.error), 3) +#define LIVEKIT_PB_RPC_RESPONSE_CALLBACK NULL +#define LIVEKIT_PB_RPC_RESPONSE_DEFAULT NULL +#define livekit_pb_rpc_response_t_value_error_MSGTYPE livekit_pb_rpc_error_t + +#define LIVEKIT_PB_RPC_ERROR_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, code, 1) \ +X(a, POINTER, SINGULAR, STRING, data, 3) +#define LIVEKIT_PB_RPC_ERROR_CALLBACK NULL +#define LIVEKIT_PB_RPC_ERROR_DEFAULT NULL + +#define LIVEKIT_PB_PARTICIPANT_TRACKS_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, participant_sid, 1) \ +X(a, CALLBACK, REPEATED, STRING, track_sids, 2) +#define LIVEKIT_PB_PARTICIPANT_TRACKS_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_PARTICIPANT_TRACKS_DEFAULT NULL + +#define LIVEKIT_PB_SERVER_INFO_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, edition, 1) \ +X(a, CALLBACK, SINGULAR, STRING, version, 2) \ +X(a, STATIC, SINGULAR, INT32, protocol, 3) \ +X(a, CALLBACK, SINGULAR, STRING, region, 4) \ +X(a, CALLBACK, SINGULAR, STRING, node_id, 5) \ +X(a, CALLBACK, SINGULAR, STRING, debug_info, 6) \ +X(a, STATIC, SINGULAR, INT32, agent_protocol, 7) +#define LIVEKIT_PB_SERVER_INFO_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SERVER_INFO_DEFAULT NULL + +#define LIVEKIT_PB_CLIENT_INFO_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, sdk, 1) \ +X(a, CALLBACK, SINGULAR, STRING, version, 2) \ +X(a, STATIC, SINGULAR, INT32, protocol, 3) \ +X(a, CALLBACK, SINGULAR, STRING, os, 4) \ +X(a, CALLBACK, SINGULAR, STRING, os_version, 5) \ +X(a, CALLBACK, SINGULAR, STRING, device_model, 6) \ +X(a, CALLBACK, SINGULAR, STRING, browser, 7) \ +X(a, CALLBACK, SINGULAR, STRING, browser_version, 8) \ +X(a, CALLBACK, SINGULAR, STRING, address, 9) \ +X(a, CALLBACK, SINGULAR, STRING, network, 10) \ +X(a, CALLBACK, SINGULAR, STRING, other_sdks, 11) +#define LIVEKIT_PB_CLIENT_INFO_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_CLIENT_INFO_DEFAULT NULL + +#define LIVEKIT_PB_CLIENT_CONFIGURATION_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, resume_connection, 3) \ +X(a, STATIC, SINGULAR, UENUM, force_relay, 5) +#define LIVEKIT_PB_CLIENT_CONFIGURATION_CALLBACK NULL +#define LIVEKIT_PB_CLIENT_CONFIGURATION_DEFAULT NULL + +#define LIVEKIT_PB_VIDEO_CONFIGURATION_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, hardware_encoder, 1) +#define LIVEKIT_PB_VIDEO_CONFIGURATION_CALLBACK NULL +#define LIVEKIT_PB_VIDEO_CONFIGURATION_DEFAULT NULL + +#define LIVEKIT_PB_DISABLED_CODECS_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, codecs, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, publish, 2) +#define LIVEKIT_PB_DISABLED_CODECS_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_DISABLED_CODECS_DEFAULT NULL +#define livekit_pb_disabled_codecs_t_codecs_MSGTYPE livekit_pb_codec_t +#define livekit_pb_disabled_codecs_t_publish_MSGTYPE livekit_pb_codec_t + +#define LIVEKIT_PB_RTP_DRIFT_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, start_time, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, end_time, 2) \ +X(a, STATIC, SINGULAR, DOUBLE, duration, 3) \ +X(a, STATIC, SINGULAR, UINT64, start_timestamp, 4) \ +X(a, STATIC, SINGULAR, UINT64, end_timestamp, 5) \ +X(a, STATIC, SINGULAR, UINT64, rtp_clock_ticks, 6) \ +X(a, STATIC, SINGULAR, INT64, drift_samples, 7) \ +X(a, STATIC, SINGULAR, DOUBLE, drift_ms, 8) \ +X(a, STATIC, SINGULAR, DOUBLE, clock_rate, 9) +#define LIVEKIT_PB_RTP_DRIFT_CALLBACK NULL +#define LIVEKIT_PB_RTP_DRIFT_DEFAULT NULL +#define livekit_pb_rtp_drift_t_start_time_MSGTYPE google_protobuf_timestamp_t +#define livekit_pb_rtp_drift_t_end_time_MSGTYPE google_protobuf_timestamp_t + +#define LIVEKIT_PB_RTP_STATS_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, start_time, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, end_time, 2) \ +X(a, STATIC, SINGULAR, DOUBLE, duration, 3) \ +X(a, STATIC, SINGULAR, UINT32, packets, 4) \ +X(a, STATIC, SINGULAR, DOUBLE, packet_rate, 5) \ +X(a, STATIC, SINGULAR, UINT64, bytes, 6) \ +X(a, STATIC, SINGULAR, DOUBLE, bitrate, 7) \ +X(a, STATIC, SINGULAR, UINT32, packets_lost, 8) \ +X(a, STATIC, SINGULAR, DOUBLE, packet_loss_rate, 9) \ +X(a, STATIC, SINGULAR, FLOAT, packet_loss_percentage, 10) \ +X(a, STATIC, SINGULAR, UINT32, packets_duplicate, 11) \ +X(a, STATIC, SINGULAR, DOUBLE, packet_duplicate_rate, 12) \ +X(a, STATIC, SINGULAR, UINT64, bytes_duplicate, 13) \ +X(a, STATIC, SINGULAR, DOUBLE, bitrate_duplicate, 14) \ +X(a, STATIC, SINGULAR, UINT32, packets_padding, 15) \ +X(a, STATIC, SINGULAR, DOUBLE, packet_padding_rate, 16) \ +X(a, STATIC, SINGULAR, UINT64, bytes_padding, 17) \ +X(a, STATIC, SINGULAR, DOUBLE, bitrate_padding, 18) \ +X(a, STATIC, SINGULAR, UINT32, packets_out_of_order, 19) \ +X(a, STATIC, SINGULAR, UINT32, frames, 20) \ +X(a, STATIC, SINGULAR, DOUBLE, frame_rate, 21) \ +X(a, STATIC, SINGULAR, DOUBLE, jitter_current, 22) \ +X(a, STATIC, SINGULAR, DOUBLE, jitter_max, 23) \ +X(a, CALLBACK, REPEATED, MESSAGE, gap_histogram, 24) \ +X(a, STATIC, SINGULAR, UINT32, nacks, 25) \ +X(a, STATIC, SINGULAR, UINT32, nack_misses, 26) \ +X(a, STATIC, SINGULAR, UINT32, plis, 27) \ +X(a, STATIC, OPTIONAL, MESSAGE, last_pli, 28) \ +X(a, STATIC, SINGULAR, UINT32, firs, 29) \ +X(a, STATIC, OPTIONAL, MESSAGE, last_fir, 30) \ +X(a, STATIC, SINGULAR, UINT32, rtt_current, 31) \ +X(a, STATIC, SINGULAR, UINT32, rtt_max, 32) \ +X(a, STATIC, SINGULAR, UINT32, key_frames, 33) \ +X(a, STATIC, OPTIONAL, MESSAGE, last_key_frame, 34) \ +X(a, STATIC, SINGULAR, UINT32, layer_lock_plis, 35) \ +X(a, STATIC, OPTIONAL, MESSAGE, last_layer_lock_pli, 36) \ +X(a, STATIC, SINGULAR, UINT32, nack_acks, 37) \ +X(a, STATIC, SINGULAR, UINT32, nack_repeated, 38) \ +X(a, STATIC, SINGULAR, UINT64, header_bytes, 39) \ +X(a, STATIC, SINGULAR, UINT64, header_bytes_duplicate, 40) \ +X(a, STATIC, SINGULAR, UINT64, header_bytes_padding, 41) \ +X(a, STATIC, OPTIONAL, MESSAGE, packet_drift, 44) \ +X(a, STATIC, OPTIONAL, MESSAGE, ntp_report_drift, 45) \ +X(a, STATIC, OPTIONAL, MESSAGE, rebased_report_drift, 46) \ +X(a, STATIC, OPTIONAL, MESSAGE, received_report_drift, 47) +#define LIVEKIT_PB_RTP_STATS_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_RTP_STATS_DEFAULT NULL +#define livekit_pb_rtp_stats_t_start_time_MSGTYPE google_protobuf_timestamp_t +#define livekit_pb_rtp_stats_t_end_time_MSGTYPE google_protobuf_timestamp_t +#define livekit_pb_rtp_stats_t_gap_histogram_MSGTYPE livekit_pb_rtp_stats_gap_histogram_entry_t +#define livekit_pb_rtp_stats_t_last_pli_MSGTYPE google_protobuf_timestamp_t +#define livekit_pb_rtp_stats_t_last_fir_MSGTYPE google_protobuf_timestamp_t +#define livekit_pb_rtp_stats_t_last_key_frame_MSGTYPE google_protobuf_timestamp_t +#define livekit_pb_rtp_stats_t_last_layer_lock_pli_MSGTYPE google_protobuf_timestamp_t +#define livekit_pb_rtp_stats_t_packet_drift_MSGTYPE livekit_pb_rtp_drift_t +#define livekit_pb_rtp_stats_t_ntp_report_drift_MSGTYPE livekit_pb_rtp_drift_t +#define livekit_pb_rtp_stats_t_rebased_report_drift_MSGTYPE livekit_pb_rtp_drift_t +#define livekit_pb_rtp_stats_t_received_report_drift_MSGTYPE livekit_pb_rtp_drift_t + +#define LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, key, 1) \ +X(a, STATIC, SINGULAR, UINT32, value, 2) +#define LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_CALLBACK NULL +#define LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_DEFAULT NULL + +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, rtp_timestamp, 1) \ +X(a, STATIC, SINGULAR, UINT64, rtp_timestamp_ext, 2) \ +X(a, STATIC, SINGULAR, UINT64, ntp_timestamp, 3) \ +X(a, STATIC, SINGULAR, INT64, at, 4) \ +X(a, STATIC, SINGULAR, INT64, at_adjusted, 5) \ +X(a, STATIC, SINGULAR, UINT32, packets, 6) \ +X(a, STATIC, SINGULAR, UINT64, octets, 7) +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_CALLBACK NULL +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_DEFAULT NULL + +#define LIVEKIT_PB_RTP_FORWARDER_STATE_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, started, 1) \ +X(a, STATIC, SINGULAR, INT32, reference_layer_spatial, 2) \ +X(a, STATIC, SINGULAR, INT64, pre_start_time, 3) \ +X(a, STATIC, SINGULAR, UINT64, ext_first_timestamp, 4) \ +X(a, STATIC, SINGULAR, UINT64, dummy_start_timestamp_offset, 5) \ +X(a, STATIC, OPTIONAL, MESSAGE, rtp_munger, 6) \ +X(a, STATIC, ONEOF, MESSAGE, (codec_munger,vp8_munger,codec_munger.vp8_munger), 7) \ +X(a, CALLBACK, REPEATED, MESSAGE, sender_report_state, 8) +#define LIVEKIT_PB_RTP_FORWARDER_STATE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_RTP_FORWARDER_STATE_DEFAULT NULL +#define livekit_pb_rtp_forwarder_state_t_rtp_munger_MSGTYPE livekit_pb_rtp_munger_state_t +#define livekit_pb_rtp_forwarder_state_t_codec_munger_vp8_munger_MSGTYPE livekit_pb_vp8_munger_state_t +#define livekit_pb_rtp_forwarder_state_t_sender_report_state_MSGTYPE livekit_pb_rtcp_sender_report_state_t + +#define LIVEKIT_PB_RTP_MUNGER_STATE_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT64, ext_last_sequence_number, 1) \ +X(a, STATIC, SINGULAR, UINT64, ext_second_last_sequence_number, 2) \ +X(a, STATIC, SINGULAR, UINT64, ext_last_timestamp, 3) \ +X(a, STATIC, SINGULAR, UINT64, ext_second_last_timestamp, 4) \ +X(a, STATIC, SINGULAR, BOOL, last_marker, 5) \ +X(a, STATIC, SINGULAR, BOOL, second_last_marker, 6) +#define LIVEKIT_PB_RTP_MUNGER_STATE_CALLBACK NULL +#define LIVEKIT_PB_RTP_MUNGER_STATE_DEFAULT NULL + +#define LIVEKIT_PB_VP8_MUNGER_STATE_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, ext_last_picture_id, 1) \ +X(a, STATIC, SINGULAR, BOOL, picture_id_used, 2) \ +X(a, STATIC, SINGULAR, UINT32, last_tl0_pic_idx, 3) \ +X(a, STATIC, SINGULAR, BOOL, tl0_pic_idx_used, 4) \ +X(a, STATIC, SINGULAR, BOOL, tid_used, 5) \ +X(a, STATIC, SINGULAR, UINT32, last_key_idx, 6) \ +X(a, STATIC, SINGULAR, BOOL, key_idx_used, 7) +#define LIVEKIT_PB_VP8_MUNGER_STATE_CALLBACK NULL +#define LIVEKIT_PB_VP8_MUNGER_STATE_DEFAULT NULL + +#define LIVEKIT_PB_TIMED_VERSION_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT64, unix_micro, 1) \ +X(a, STATIC, SINGULAR, INT32, ticks, 2) +#define LIVEKIT_PB_TIMED_VERSION_CALLBACK NULL +#define LIVEKIT_PB_TIMED_VERSION_DEFAULT NULL + +#define LIVEKIT_PB_DATA_STREAM_FIELDLIST(X, a) \ + +#define LIVEKIT_PB_DATA_STREAM_CALLBACK NULL +#define LIVEKIT_PB_DATA_STREAM_DEFAULT NULL + +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, operation_type, 1) \ +X(a, STATIC, SINGULAR, INT32, version, 2) \ +X(a, CALLBACK, SINGULAR, STRING, reply_to_stream_id, 3) \ +X(a, CALLBACK, REPEATED, STRING, attached_stream_ids, 4) \ +X(a, STATIC, SINGULAR, BOOL, generated, 5) +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_DEFAULT NULL + +#define LIVEKIT_PB_DATA_STREAM_BYTE_HEADER_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, name, 1) +#define LIVEKIT_PB_DATA_STREAM_BYTE_HEADER_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_DATA_STREAM_BYTE_HEADER_DEFAULT NULL + +#define LIVEKIT_PB_DATA_STREAM_HEADER_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, stream_id, 1) \ +X(a, STATIC, SINGULAR, INT64, timestamp, 2) \ +X(a, POINTER, SINGULAR, STRING, topic, 3) \ +X(a, POINTER, SINGULAR, STRING, mime_type, 4) \ +X(a, STATIC, OPTIONAL, UINT64, total_length, 5) \ +X(a, STATIC, ONEOF, MESSAGE, (content_header,text_header,content_header.text_header), 9) \ +X(a, STATIC, ONEOF, MESSAGE, (content_header,byte_header,content_header.byte_header), 10) +#define LIVEKIT_PB_DATA_STREAM_HEADER_CALLBACK NULL +#define LIVEKIT_PB_DATA_STREAM_HEADER_DEFAULT NULL +#define livekit_pb_data_stream_header_t_content_header_text_header_MSGTYPE livekit_pb_data_stream_text_header_t +#define livekit_pb_data_stream_header_t_content_header_byte_header_MSGTYPE livekit_pb_data_stream_byte_header_t + +#define LIVEKIT_PB_DATA_STREAM_CHUNK_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, stream_id, 1) \ +X(a, STATIC, SINGULAR, UINT64, chunk_index, 2) \ +X(a, POINTER, SINGULAR, BYTES, content, 3) \ +X(a, STATIC, SINGULAR, INT32, version, 4) +#define LIVEKIT_PB_DATA_STREAM_CHUNK_CALLBACK NULL +#define LIVEKIT_PB_DATA_STREAM_CHUNK_DEFAULT NULL + +#define LIVEKIT_PB_DATA_STREAM_TRAILER_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, stream_id, 1) \ +X(a, STATIC, SINGULAR, STRING, reason, 2) +#define LIVEKIT_PB_DATA_STREAM_TRAILER_CALLBACK NULL +#define LIVEKIT_PB_DATA_STREAM_TRAILER_DEFAULT NULL + +#define LIVEKIT_PB_WEBHOOK_CONFIG_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, url, 1) \ +X(a, CALLBACK, SINGULAR, STRING, signing_key, 2) +#define LIVEKIT_PB_WEBHOOK_CONFIG_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_WEBHOOK_CONFIG_DEFAULT NULL + +extern const pb_msgdesc_t livekit_pb_pagination_t_msg; +extern const pb_msgdesc_t livekit_pb_list_update_t_msg; +extern const pb_msgdesc_t livekit_pb_room_t_msg; +extern const pb_msgdesc_t livekit_pb_codec_t_msg; +extern const pb_msgdesc_t livekit_pb_playout_delay_t_msg; +extern const pb_msgdesc_t livekit_pb_participant_permission_t_msg; +extern const pb_msgdesc_t livekit_pb_participant_info_t_msg; +extern const pb_msgdesc_t livekit_pb_encryption_t_msg; +extern const pb_msgdesc_t livekit_pb_simulcast_codec_info_t_msg; +extern const pb_msgdesc_t livekit_pb_track_info_t_msg; +extern const pb_msgdesc_t livekit_pb_video_layer_t_msg; +extern const pb_msgdesc_t livekit_pb_data_packet_t_msg; +extern const pb_msgdesc_t livekit_pb_active_speaker_update_t_msg; +extern const pb_msgdesc_t livekit_pb_speaker_info_t_msg; +extern const pb_msgdesc_t livekit_pb_user_packet_t_msg; +extern const pb_msgdesc_t livekit_pb_sip_dtmf_t_msg; +extern const pb_msgdesc_t livekit_pb_transcription_t_msg; +extern const pb_msgdesc_t livekit_pb_transcription_segment_t_msg; +extern const pb_msgdesc_t livekit_pb_chat_message_t_msg; +extern const pb_msgdesc_t livekit_pb_rpc_request_t_msg; +extern const pb_msgdesc_t livekit_pb_rpc_ack_t_msg; +extern const pb_msgdesc_t livekit_pb_rpc_response_t_msg; +extern const pb_msgdesc_t livekit_pb_rpc_error_t_msg; +extern const pb_msgdesc_t livekit_pb_participant_tracks_t_msg; +extern const pb_msgdesc_t livekit_pb_server_info_t_msg; +extern const pb_msgdesc_t livekit_pb_client_info_t_msg; +extern const pb_msgdesc_t livekit_pb_client_configuration_t_msg; +extern const pb_msgdesc_t livekit_pb_video_configuration_t_msg; +extern const pb_msgdesc_t livekit_pb_disabled_codecs_t_msg; +extern const pb_msgdesc_t livekit_pb_rtp_drift_t_msg; +extern const pb_msgdesc_t livekit_pb_rtp_stats_t_msg; +extern const pb_msgdesc_t livekit_pb_rtp_stats_gap_histogram_entry_t_msg; +extern const pb_msgdesc_t livekit_pb_rtcp_sender_report_state_t_msg; +extern const pb_msgdesc_t livekit_pb_rtp_forwarder_state_t_msg; +extern const pb_msgdesc_t livekit_pb_rtp_munger_state_t_msg; +extern const pb_msgdesc_t livekit_pb_vp8_munger_state_t_msg; +extern const pb_msgdesc_t livekit_pb_timed_version_t_msg; +extern const pb_msgdesc_t livekit_pb_data_stream_t_msg; +extern const pb_msgdesc_t livekit_pb_data_stream_text_header_t_msg; +extern const pb_msgdesc_t livekit_pb_data_stream_byte_header_t_msg; +extern const pb_msgdesc_t livekit_pb_data_stream_header_t_msg; +extern const pb_msgdesc_t livekit_pb_data_stream_chunk_t_msg; +extern const pb_msgdesc_t livekit_pb_data_stream_trailer_t_msg; +extern const pb_msgdesc_t livekit_pb_webhook_config_t_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define LIVEKIT_PB_PAGINATION_FIELDS &livekit_pb_pagination_t_msg +#define LIVEKIT_PB_LIST_UPDATE_FIELDS &livekit_pb_list_update_t_msg +#define LIVEKIT_PB_ROOM_FIELDS &livekit_pb_room_t_msg +#define LIVEKIT_PB_CODEC_FIELDS &livekit_pb_codec_t_msg +#define LIVEKIT_PB_PLAYOUT_DELAY_FIELDS &livekit_pb_playout_delay_t_msg +#define LIVEKIT_PB_PARTICIPANT_PERMISSION_FIELDS &livekit_pb_participant_permission_t_msg +#define LIVEKIT_PB_PARTICIPANT_INFO_FIELDS &livekit_pb_participant_info_t_msg +#define LIVEKIT_PB_ENCRYPTION_FIELDS &livekit_pb_encryption_t_msg +#define LIVEKIT_PB_SIMULCAST_CODEC_INFO_FIELDS &livekit_pb_simulcast_codec_info_t_msg +#define LIVEKIT_PB_TRACK_INFO_FIELDS &livekit_pb_track_info_t_msg +#define LIVEKIT_PB_VIDEO_LAYER_FIELDS &livekit_pb_video_layer_t_msg +#define LIVEKIT_PB_DATA_PACKET_FIELDS &livekit_pb_data_packet_t_msg +#define LIVEKIT_PB_ACTIVE_SPEAKER_UPDATE_FIELDS &livekit_pb_active_speaker_update_t_msg +#define LIVEKIT_PB_SPEAKER_INFO_FIELDS &livekit_pb_speaker_info_t_msg +#define LIVEKIT_PB_USER_PACKET_FIELDS &livekit_pb_user_packet_t_msg +#define LIVEKIT_PB_SIP_DTMF_FIELDS &livekit_pb_sip_dtmf_t_msg +#define LIVEKIT_PB_TRANSCRIPTION_FIELDS &livekit_pb_transcription_t_msg +#define LIVEKIT_PB_TRANSCRIPTION_SEGMENT_FIELDS &livekit_pb_transcription_segment_t_msg +#define LIVEKIT_PB_CHAT_MESSAGE_FIELDS &livekit_pb_chat_message_t_msg +#define LIVEKIT_PB_RPC_REQUEST_FIELDS &livekit_pb_rpc_request_t_msg +#define LIVEKIT_PB_RPC_ACK_FIELDS &livekit_pb_rpc_ack_t_msg +#define LIVEKIT_PB_RPC_RESPONSE_FIELDS &livekit_pb_rpc_response_t_msg +#define LIVEKIT_PB_RPC_ERROR_FIELDS &livekit_pb_rpc_error_t_msg +#define LIVEKIT_PB_PARTICIPANT_TRACKS_FIELDS &livekit_pb_participant_tracks_t_msg +#define LIVEKIT_PB_SERVER_INFO_FIELDS &livekit_pb_server_info_t_msg +#define LIVEKIT_PB_CLIENT_INFO_FIELDS &livekit_pb_client_info_t_msg +#define LIVEKIT_PB_CLIENT_CONFIGURATION_FIELDS &livekit_pb_client_configuration_t_msg +#define LIVEKIT_PB_VIDEO_CONFIGURATION_FIELDS &livekit_pb_video_configuration_t_msg +#define LIVEKIT_PB_DISABLED_CODECS_FIELDS &livekit_pb_disabled_codecs_t_msg +#define LIVEKIT_PB_RTP_DRIFT_FIELDS &livekit_pb_rtp_drift_t_msg +#define LIVEKIT_PB_RTP_STATS_FIELDS &livekit_pb_rtp_stats_t_msg +#define LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_FIELDS &livekit_pb_rtp_stats_gap_histogram_entry_t_msg +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_FIELDS &livekit_pb_rtcp_sender_report_state_t_msg +#define LIVEKIT_PB_RTP_FORWARDER_STATE_FIELDS &livekit_pb_rtp_forwarder_state_t_msg +#define LIVEKIT_PB_RTP_MUNGER_STATE_FIELDS &livekit_pb_rtp_munger_state_t_msg +#define LIVEKIT_PB_VP8_MUNGER_STATE_FIELDS &livekit_pb_vp8_munger_state_t_msg +#define LIVEKIT_PB_TIMED_VERSION_FIELDS &livekit_pb_timed_version_t_msg +#define LIVEKIT_PB_DATA_STREAM_FIELDS &livekit_pb_data_stream_t_msg +#define LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_FIELDS &livekit_pb_data_stream_text_header_t_msg +#define LIVEKIT_PB_DATA_STREAM_BYTE_HEADER_FIELDS &livekit_pb_data_stream_byte_header_t_msg +#define LIVEKIT_PB_DATA_STREAM_HEADER_FIELDS &livekit_pb_data_stream_header_t_msg +#define LIVEKIT_PB_DATA_STREAM_CHUNK_FIELDS &livekit_pb_data_stream_chunk_t_msg +#define LIVEKIT_PB_DATA_STREAM_TRAILER_FIELDS &livekit_pb_data_stream_trailer_t_msg +#define LIVEKIT_PB_WEBHOOK_CONFIG_FIELDS &livekit_pb_webhook_config_t_msg + +/* Maximum encoded size of messages (where known) */ +/* livekit_pb_Pagination_size depends on runtime parameters */ +/* livekit_pb_ListUpdate_size depends on runtime parameters */ +/* livekit_pb_Room_size depends on runtime parameters */ +/* livekit_pb_Codec_size depends on runtime parameters */ +/* livekit_pb_ParticipantInfo_size depends on runtime parameters */ +/* livekit_pb_SimulcastCodecInfo_size depends on runtime parameters */ +/* livekit_pb_TrackInfo_size depends on runtime parameters */ +/* livekit_pb_DataPacket_size depends on runtime parameters */ +/* livekit_pb_ActiveSpeakerUpdate_size depends on runtime parameters */ +/* livekit_pb_SpeakerInfo_size depends on runtime parameters */ +/* livekit_pb_UserPacket_size depends on runtime parameters */ +/* livekit_pb_Transcription_size depends on runtime parameters */ +/* livekit_pb_TranscriptionSegment_size depends on runtime parameters */ +/* livekit_pb_ChatMessage_size depends on runtime parameters */ +/* livekit_pb_RpcRequest_size depends on runtime parameters */ +/* livekit_pb_RpcResponse_size depends on runtime parameters */ +/* livekit_pb_RpcError_size depends on runtime parameters */ +/* livekit_pb_ParticipantTracks_size depends on runtime parameters */ +/* livekit_pb_ServerInfo_size depends on runtime parameters */ +/* livekit_pb_ClientInfo_size depends on runtime parameters */ +/* livekit_pb_DisabledCodecs_size depends on runtime parameters */ +/* livekit_pb_RTPStats_size depends on runtime parameters */ +/* livekit_pb_RTPForwarderState_size depends on runtime parameters */ +/* livekit_pb_DataStream_TextHeader_size depends on runtime parameters */ +/* livekit_pb_DataStream_ByteHeader_size depends on runtime parameters */ +/* livekit_pb_DataStream_Header_size depends on runtime parameters */ +/* livekit_pb_DataStream_Chunk_size depends on runtime parameters */ +/* livekit_pb_WebhookConfig_size depends on runtime parameters */ +#define LIVEKIT_LIVEKIT_MODELS_PB_H_MAX_SIZE LIVEKIT_PB_RTP_DRIFT_SIZE +#define LIVEKIT_PB_CLIENT_CONFIGURATION_SIZE 4 +#define LIVEKIT_PB_DATA_STREAM_SIZE 0 +#define LIVEKIT_PB_DATA_STREAM_TRAILER_SIZE 55 +#define LIVEKIT_PB_ENCRYPTION_SIZE 0 +#define LIVEKIT_PB_PARTICIPANT_PERMISSION_SIZE 6 +#define LIVEKIT_PB_PLAYOUT_DELAY_SIZE 14 +#define LIVEKIT_PB_RPC_ACK_SIZE 38 +#define LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_SIZE 67 +#define LIVEKIT_PB_RTP_DRIFT_SIZE 119 +#define LIVEKIT_PB_RTP_MUNGER_STATE_SIZE 48 +#define LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_SIZE 17 +#define LIVEKIT_PB_SIP_DTMF_SIZE 9 +#define LIVEKIT_PB_TIMED_VERSION_SIZE 22 +#define LIVEKIT_PB_VIDEO_CONFIGURATION_SIZE 2 +#define LIVEKIT_PB_VIDEO_LAYER_SIZE 14 +#define LIVEKIT_PB_VP8_MUNGER_STATE_SIZE 31 + +/* Mapping from canonical names (mangle_names or overridden package name) */ +#define livekit_AudioCodec livekit_pb_AudioCodec +#define livekit_VideoCodec livekit_pb_VideoCodec +#define livekit_ImageCodec livekit_pb_ImageCodec +#define livekit_BackupCodecPolicy livekit_pb_BackupCodecPolicy +#define livekit_TrackType livekit_pb_TrackType +#define livekit_TrackSource livekit_pb_TrackSource +#define livekit_VideoQuality livekit_pb_VideoQuality +#define livekit_ConnectionQuality livekit_pb_ConnectionQuality +#define livekit_ClientConfigSetting livekit_pb_ClientConfigSetting +#define livekit_DisconnectReason livekit_pb_DisconnectReason +#define livekit_ReconnectReason livekit_pb_ReconnectReason +#define livekit_SubscriptionError livekit_pb_SubscriptionError +#define livekit_AudioTrackFeature livekit_pb_AudioTrackFeature +#define livekit_Pagination livekit_pb_Pagination +#define livekit_ListUpdate livekit_pb_ListUpdate +#define livekit_Room livekit_pb_Room +#define livekit_Codec livekit_pb_Codec +#define livekit_PlayoutDelay livekit_pb_PlayoutDelay +#define livekit_ParticipantPermission livekit_pb_ParticipantPermission +#define livekit_ParticipantInfo livekit_pb_ParticipantInfo +#define livekit_ParticipantInfo_State livekit_pb_ParticipantInfo_State +#define livekit_ParticipantInfo_Kind livekit_pb_ParticipantInfo_Kind +#define livekit_ParticipantInfo_KindDetail livekit_pb_ParticipantInfo_KindDetail +#define livekit_ParticipantInfo_AttributesEntry livekit_pb_ParticipantInfo_AttributesEntry +#define livekit_Encryption livekit_pb_Encryption +#define livekit_Encryption_Type livekit_pb_Encryption_Type +#define livekit_SimulcastCodecInfo livekit_pb_SimulcastCodecInfo +#define livekit_TrackInfo livekit_pb_TrackInfo +#define livekit_VideoLayer livekit_pb_VideoLayer +#define livekit_DataPacket livekit_pb_DataPacket +#define livekit_DataPacket_Kind livekit_pb_DataPacket_Kind +#define livekit_ActiveSpeakerUpdate livekit_pb_ActiveSpeakerUpdate +#define livekit_SpeakerInfo livekit_pb_SpeakerInfo +#define livekit_UserPacket livekit_pb_UserPacket +#define livekit_SipDTMF livekit_pb_SipDTMF +#define livekit_Transcription livekit_pb_Transcription +#define livekit_TranscriptionSegment livekit_pb_TranscriptionSegment +#define livekit_ChatMessage livekit_pb_ChatMessage +#define livekit_RpcRequest livekit_pb_RpcRequest +#define livekit_RpcAck livekit_pb_RpcAck +#define livekit_RpcResponse livekit_pb_RpcResponse +#define livekit_RpcError livekit_pb_RpcError +#define livekit_ParticipantTracks livekit_pb_ParticipantTracks +#define livekit_ServerInfo livekit_pb_ServerInfo +#define livekit_ServerInfo_Edition livekit_pb_ServerInfo_Edition +#define livekit_ClientInfo livekit_pb_ClientInfo +#define livekit_ClientInfo_SDK livekit_pb_ClientInfo_SDK +#define livekit_ClientConfiguration livekit_pb_ClientConfiguration +#define livekit_VideoConfiguration livekit_pb_VideoConfiguration +#define livekit_DisabledCodecs livekit_pb_DisabledCodecs +#define livekit_RTPDrift livekit_pb_RTPDrift +#define livekit_RTPStats livekit_pb_RTPStats +#define livekit_RTPStats_GapHistogramEntry livekit_pb_RTPStats_GapHistogramEntry +#define livekit_RTCPSenderReportState livekit_pb_RTCPSenderReportState +#define livekit_RTPForwarderState livekit_pb_RTPForwarderState +#define livekit_RTPMungerState livekit_pb_RTPMungerState +#define livekit_VP8MungerState livekit_pb_VP8MungerState +#define livekit_TimedVersion livekit_pb_TimedVersion +#define livekit_DataStream livekit_pb_DataStream +#define livekit_DataStream_OperationType livekit_pb_DataStream_OperationType +#define livekit_DataStream_TextHeader livekit_pb_DataStream_TextHeader +#define livekit_DataStream_ByteHeader livekit_pb_DataStream_ByteHeader +#define livekit_DataStream_Header livekit_pb_DataStream_Header +#define livekit_DataStream_Header_AttributesEntry livekit_pb_DataStream_Header_AttributesEntry +#define livekit_DataStream_Chunk livekit_pb_DataStream_Chunk +#define livekit_DataStream_Trailer livekit_pb_DataStream_Trailer +#define livekit_DataStream_Trailer_AttributesEntry livekit_pb_DataStream_Trailer_AttributesEntry +#define livekit_WebhookConfig livekit_pb_WebhookConfig +#define _LIVEKIT_AUDIO_CODEC_MIN _LIVEKIT_PB_AUDIO_CODEC_MIN +#define _LIVEKIT_AUDIO_CODEC_MAX _LIVEKIT_PB_AUDIO_CODEC_MAX +#define _LIVEKIT_AUDIO_CODEC_ARRAYSIZE _LIVEKIT_PB_AUDIO_CODEC_ARRAYSIZE +#define _LIVEKIT_VIDEO_CODEC_MIN _LIVEKIT_PB_VIDEO_CODEC_MIN +#define _LIVEKIT_VIDEO_CODEC_MAX _LIVEKIT_PB_VIDEO_CODEC_MAX +#define _LIVEKIT_VIDEO_CODEC_ARRAYSIZE _LIVEKIT_PB_VIDEO_CODEC_ARRAYSIZE +#define _LIVEKIT_IMAGE_CODEC_MIN _LIVEKIT_PB_IMAGE_CODEC_MIN +#define _LIVEKIT_IMAGE_CODEC_MAX _LIVEKIT_PB_IMAGE_CODEC_MAX +#define _LIVEKIT_IMAGE_CODEC_ARRAYSIZE _LIVEKIT_PB_IMAGE_CODEC_ARRAYSIZE +#define _LIVEKIT_BACKUP_CODEC_POLICY_MIN _LIVEKIT_PB_BACKUP_CODEC_POLICY_MIN +#define _LIVEKIT_BACKUP_CODEC_POLICY_MAX _LIVEKIT_PB_BACKUP_CODEC_POLICY_MAX +#define _LIVEKIT_BACKUP_CODEC_POLICY_ARRAYSIZE _LIVEKIT_PB_BACKUP_CODEC_POLICY_ARRAYSIZE +#define _LIVEKIT_TRACK_TYPE_MIN _LIVEKIT_PB_TRACK_TYPE_MIN +#define _LIVEKIT_TRACK_TYPE_MAX _LIVEKIT_PB_TRACK_TYPE_MAX +#define _LIVEKIT_TRACK_TYPE_ARRAYSIZE _LIVEKIT_PB_TRACK_TYPE_ARRAYSIZE +#define _LIVEKIT_TRACK_SOURCE_MIN _LIVEKIT_PB_TRACK_SOURCE_MIN +#define _LIVEKIT_TRACK_SOURCE_MAX _LIVEKIT_PB_TRACK_SOURCE_MAX +#define _LIVEKIT_TRACK_SOURCE_ARRAYSIZE _LIVEKIT_PB_TRACK_SOURCE_ARRAYSIZE +#define _LIVEKIT_VIDEO_QUALITY_MIN _LIVEKIT_PB_VIDEO_QUALITY_MIN +#define _LIVEKIT_VIDEO_QUALITY_MAX _LIVEKIT_PB_VIDEO_QUALITY_MAX +#define _LIVEKIT_VIDEO_QUALITY_ARRAYSIZE _LIVEKIT_PB_VIDEO_QUALITY_ARRAYSIZE +#define _LIVEKIT_CONNECTION_QUALITY_MIN _LIVEKIT_PB_CONNECTION_QUALITY_MIN +#define _LIVEKIT_CONNECTION_QUALITY_MAX _LIVEKIT_PB_CONNECTION_QUALITY_MAX +#define _LIVEKIT_CONNECTION_QUALITY_ARRAYSIZE _LIVEKIT_PB_CONNECTION_QUALITY_ARRAYSIZE +#define _LIVEKIT_CLIENT_CONFIG_SETTING_MIN _LIVEKIT_PB_CLIENT_CONFIG_SETTING_MIN +#define _LIVEKIT_CLIENT_CONFIG_SETTING_MAX _LIVEKIT_PB_CLIENT_CONFIG_SETTING_MAX +#define _LIVEKIT_CLIENT_CONFIG_SETTING_ARRAYSIZE _LIVEKIT_PB_CLIENT_CONFIG_SETTING_ARRAYSIZE +#define _LIVEKIT_DISCONNECT_REASON_MIN _LIVEKIT_PB_DISCONNECT_REASON_MIN +#define _LIVEKIT_DISCONNECT_REASON_MAX _LIVEKIT_PB_DISCONNECT_REASON_MAX +#define _LIVEKIT_DISCONNECT_REASON_ARRAYSIZE _LIVEKIT_PB_DISCONNECT_REASON_ARRAYSIZE +#define _LIVEKIT_RECONNECT_REASON_MIN _LIVEKIT_PB_RECONNECT_REASON_MIN +#define _LIVEKIT_RECONNECT_REASON_MAX _LIVEKIT_PB_RECONNECT_REASON_MAX +#define _LIVEKIT_RECONNECT_REASON_ARRAYSIZE _LIVEKIT_PB_RECONNECT_REASON_ARRAYSIZE +#define _LIVEKIT_SUBSCRIPTION_ERROR_MIN _LIVEKIT_PB_SUBSCRIPTION_ERROR_MIN +#define _LIVEKIT_SUBSCRIPTION_ERROR_MAX _LIVEKIT_PB_SUBSCRIPTION_ERROR_MAX +#define _LIVEKIT_SUBSCRIPTION_ERROR_ARRAYSIZE _LIVEKIT_PB_SUBSCRIPTION_ERROR_ARRAYSIZE +#define _LIVEKIT_AUDIO_TRACK_FEATURE_MIN _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN +#define _LIVEKIT_AUDIO_TRACK_FEATURE_MAX _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MAX +#define _LIVEKIT_AUDIO_TRACK_FEATURE_ARRAYSIZE _LIVEKIT_PB_AUDIO_TRACK_FEATURE_ARRAYSIZE +#define _LIVEKIT_PARTICIPANT_INFO_STATE_MIN _LIVEKIT_PB_PARTICIPANT_INFO_STATE_MIN +#define _LIVEKIT_PARTICIPANT_INFO_STATE_MAX _LIVEKIT_PB_PARTICIPANT_INFO_STATE_MAX +#define _LIVEKIT_PARTICIPANT_INFO_STATE_ARRAYSIZE _LIVEKIT_PB_PARTICIPANT_INFO_STATE_ARRAYSIZE +#define _LIVEKIT_PARTICIPANT_INFO_KIND_MIN _LIVEKIT_PB_PARTICIPANT_INFO_KIND_MIN +#define _LIVEKIT_PARTICIPANT_INFO_KIND_MAX _LIVEKIT_PB_PARTICIPANT_INFO_KIND_MAX +#define _LIVEKIT_PARTICIPANT_INFO_KIND_ARRAYSIZE _LIVEKIT_PB_PARTICIPANT_INFO_KIND_ARRAYSIZE +#define _LIVEKIT_PARTICIPANT_INFO_KIND_DETAIL_MIN _LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_MIN +#define _LIVEKIT_PARTICIPANT_INFO_KIND_DETAIL_MAX _LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_MAX +#define _LIVEKIT_PARTICIPANT_INFO_KIND_DETAIL_ARRAYSIZE _LIVEKIT_PB_PARTICIPANT_INFO_KIND_DETAIL_ARRAYSIZE +#define _LIVEKIT_ENCRYPTION_TYPE_MIN _LIVEKIT_PB_ENCRYPTION_TYPE_MIN +#define _LIVEKIT_ENCRYPTION_TYPE_MAX _LIVEKIT_PB_ENCRYPTION_TYPE_MAX +#define _LIVEKIT_ENCRYPTION_TYPE_ARRAYSIZE _LIVEKIT_PB_ENCRYPTION_TYPE_ARRAYSIZE +#define _LIVEKIT_DATA_PACKET_KIND_MIN _LIVEKIT_PB_DATA_PACKET_KIND_MIN +#define _LIVEKIT_DATA_PACKET_KIND_MAX _LIVEKIT_PB_DATA_PACKET_KIND_MAX +#define _LIVEKIT_DATA_PACKET_KIND_ARRAYSIZE _LIVEKIT_PB_DATA_PACKET_KIND_ARRAYSIZE +#define _LIVEKIT_SERVER_INFO_EDITION_MIN _LIVEKIT_PB_SERVER_INFO_EDITION_MIN +#define _LIVEKIT_SERVER_INFO_EDITION_MAX _LIVEKIT_PB_SERVER_INFO_EDITION_MAX +#define _LIVEKIT_SERVER_INFO_EDITION_ARRAYSIZE _LIVEKIT_PB_SERVER_INFO_EDITION_ARRAYSIZE +#define _LIVEKIT_CLIENT_INFO_SDK_MIN _LIVEKIT_PB_CLIENT_INFO_SDK_MIN +#define _LIVEKIT_CLIENT_INFO_SDK_MAX _LIVEKIT_PB_CLIENT_INFO_SDK_MAX +#define _LIVEKIT_CLIENT_INFO_SDK_ARRAYSIZE _LIVEKIT_PB_CLIENT_INFO_SDK_ARRAYSIZE +#define _LIVEKIT_DATA_STREAM_OPERATION_TYPE_MIN _LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_MIN +#define _LIVEKIT_DATA_STREAM_OPERATION_TYPE_MAX _LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_MAX +#define _LIVEKIT_DATA_STREAM_OPERATION_TYPE_ARRAYSIZE _LIVEKIT_PB_DATA_STREAM_OPERATION_TYPE_ARRAYSIZE +#define LIVEKIT_PAGINATION_INIT_DEFAULT LIVEKIT_PB_PAGINATION_INIT_DEFAULT +#define LIVEKIT_LIST_UPDATE_INIT_DEFAULT LIVEKIT_PB_LIST_UPDATE_INIT_DEFAULT +#define LIVEKIT_ROOM_INIT_DEFAULT LIVEKIT_PB_ROOM_INIT_DEFAULT +#define LIVEKIT_CODEC_INIT_DEFAULT LIVEKIT_PB_CODEC_INIT_DEFAULT +#define LIVEKIT_PLAYOUT_DELAY_INIT_DEFAULT LIVEKIT_PB_PLAYOUT_DELAY_INIT_DEFAULT +#define LIVEKIT_PARTICIPANT_PERMISSION_INIT_DEFAULT LIVEKIT_PB_PARTICIPANT_PERMISSION_INIT_DEFAULT +#define LIVEKIT_PARTICIPANT_INFO_INIT_DEFAULT LIVEKIT_PB_PARTICIPANT_INFO_INIT_DEFAULT +#define LIVEKIT_ENCRYPTION_INIT_DEFAULT LIVEKIT_PB_ENCRYPTION_INIT_DEFAULT +#define LIVEKIT_SIMULCAST_CODEC_INFO_INIT_DEFAULT LIVEKIT_PB_SIMULCAST_CODEC_INFO_INIT_DEFAULT +#define LIVEKIT_TRACK_INFO_INIT_DEFAULT LIVEKIT_PB_TRACK_INFO_INIT_DEFAULT +#define LIVEKIT_VIDEO_LAYER_INIT_DEFAULT LIVEKIT_PB_VIDEO_LAYER_INIT_DEFAULT +#define LIVEKIT_DATA_PACKET_INIT_DEFAULT LIVEKIT_PB_DATA_PACKET_INIT_DEFAULT +#define LIVEKIT_ACTIVE_SPEAKER_UPDATE_INIT_DEFAULT LIVEKIT_PB_ACTIVE_SPEAKER_UPDATE_INIT_DEFAULT +#define LIVEKIT_SPEAKER_INFO_INIT_DEFAULT LIVEKIT_PB_SPEAKER_INFO_INIT_DEFAULT +#define LIVEKIT_USER_PACKET_INIT_DEFAULT LIVEKIT_PB_USER_PACKET_INIT_DEFAULT +#define LIVEKIT_SIP_DTMF_INIT_DEFAULT LIVEKIT_PB_SIP_DTMF_INIT_DEFAULT +#define LIVEKIT_TRANSCRIPTION_INIT_DEFAULT LIVEKIT_PB_TRANSCRIPTION_INIT_DEFAULT +#define LIVEKIT_TRANSCRIPTION_SEGMENT_INIT_DEFAULT LIVEKIT_PB_TRANSCRIPTION_SEGMENT_INIT_DEFAULT +#define LIVEKIT_CHAT_MESSAGE_INIT_DEFAULT LIVEKIT_PB_CHAT_MESSAGE_INIT_DEFAULT +#define LIVEKIT_RPC_REQUEST_INIT_DEFAULT LIVEKIT_PB_RPC_REQUEST_INIT_DEFAULT +#define LIVEKIT_RPC_ACK_INIT_DEFAULT LIVEKIT_PB_RPC_ACK_INIT_DEFAULT +#define LIVEKIT_RPC_RESPONSE_INIT_DEFAULT LIVEKIT_PB_RPC_RESPONSE_INIT_DEFAULT +#define LIVEKIT_RPC_ERROR_INIT_DEFAULT LIVEKIT_PB_RPC_ERROR_INIT_DEFAULT +#define LIVEKIT_PARTICIPANT_TRACKS_INIT_DEFAULT LIVEKIT_PB_PARTICIPANT_TRACKS_INIT_DEFAULT +#define LIVEKIT_SERVER_INFO_INIT_DEFAULT LIVEKIT_PB_SERVER_INFO_INIT_DEFAULT +#define LIVEKIT_CLIENT_INFO_INIT_DEFAULT LIVEKIT_PB_CLIENT_INFO_INIT_DEFAULT +#define LIVEKIT_CLIENT_CONFIGURATION_INIT_DEFAULT LIVEKIT_PB_CLIENT_CONFIGURATION_INIT_DEFAULT +#define LIVEKIT_VIDEO_CONFIGURATION_INIT_DEFAULT LIVEKIT_PB_VIDEO_CONFIGURATION_INIT_DEFAULT +#define LIVEKIT_DISABLED_CODECS_INIT_DEFAULT LIVEKIT_PB_DISABLED_CODECS_INIT_DEFAULT +#define LIVEKIT_RTP_DRIFT_INIT_DEFAULT LIVEKIT_PB_RTP_DRIFT_INIT_DEFAULT +#define LIVEKIT_RTP_STATS_INIT_DEFAULT LIVEKIT_PB_RTP_STATS_INIT_DEFAULT +#define LIVEKIT_RTP_STATS_GAP_HISTOGRAM_ENTRY_INIT_DEFAULT LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_INIT_DEFAULT +#define LIVEKIT_RTCP_SENDER_REPORT_STATE_INIT_DEFAULT LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_INIT_DEFAULT +#define LIVEKIT_RTP_FORWARDER_STATE_INIT_DEFAULT LIVEKIT_PB_RTP_FORWARDER_STATE_INIT_DEFAULT +#define LIVEKIT_RTP_MUNGER_STATE_INIT_DEFAULT LIVEKIT_PB_RTP_MUNGER_STATE_INIT_DEFAULT +#define LIVEKIT_VP8_MUNGER_STATE_INIT_DEFAULT LIVEKIT_PB_VP8_MUNGER_STATE_INIT_DEFAULT +#define LIVEKIT_TIMED_VERSION_INIT_DEFAULT LIVEKIT_PB_TIMED_VERSION_INIT_DEFAULT +#define LIVEKIT_DATA_STREAM_INIT_DEFAULT LIVEKIT_PB_DATA_STREAM_INIT_DEFAULT +#define LIVEKIT_DATA_STREAM_TEXT_HEADER_INIT_DEFAULT LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_INIT_DEFAULT +#define LIVEKIT_DATA_STREAM_BYTE_HEADER_INIT_DEFAULT LIVEKIT_PB_DATA_STREAM_BYTE_HEADER_INIT_DEFAULT +#define LIVEKIT_DATA_STREAM_HEADER_INIT_DEFAULT LIVEKIT_PB_DATA_STREAM_HEADER_INIT_DEFAULT +#define LIVEKIT_DATA_STREAM_CHUNK_INIT_DEFAULT LIVEKIT_PB_DATA_STREAM_CHUNK_INIT_DEFAULT +#define LIVEKIT_DATA_STREAM_TRAILER_INIT_DEFAULT LIVEKIT_PB_DATA_STREAM_TRAILER_INIT_DEFAULT +#define LIVEKIT_WEBHOOK_CONFIG_INIT_DEFAULT LIVEKIT_PB_WEBHOOK_CONFIG_INIT_DEFAULT +#define LIVEKIT_PAGINATION_INIT_ZERO LIVEKIT_PB_PAGINATION_INIT_ZERO +#define LIVEKIT_LIST_UPDATE_INIT_ZERO LIVEKIT_PB_LIST_UPDATE_INIT_ZERO +#define LIVEKIT_ROOM_INIT_ZERO LIVEKIT_PB_ROOM_INIT_ZERO +#define LIVEKIT_CODEC_INIT_ZERO LIVEKIT_PB_CODEC_INIT_ZERO +#define LIVEKIT_PLAYOUT_DELAY_INIT_ZERO LIVEKIT_PB_PLAYOUT_DELAY_INIT_ZERO +#define LIVEKIT_PARTICIPANT_PERMISSION_INIT_ZERO LIVEKIT_PB_PARTICIPANT_PERMISSION_INIT_ZERO +#define LIVEKIT_PARTICIPANT_INFO_INIT_ZERO LIVEKIT_PB_PARTICIPANT_INFO_INIT_ZERO +#define LIVEKIT_ENCRYPTION_INIT_ZERO LIVEKIT_PB_ENCRYPTION_INIT_ZERO +#define LIVEKIT_SIMULCAST_CODEC_INFO_INIT_ZERO LIVEKIT_PB_SIMULCAST_CODEC_INFO_INIT_ZERO +#define LIVEKIT_TRACK_INFO_INIT_ZERO LIVEKIT_PB_TRACK_INFO_INIT_ZERO +#define LIVEKIT_VIDEO_LAYER_INIT_ZERO LIVEKIT_PB_VIDEO_LAYER_INIT_ZERO +#define LIVEKIT_DATA_PACKET_INIT_ZERO LIVEKIT_PB_DATA_PACKET_INIT_ZERO +#define LIVEKIT_ACTIVE_SPEAKER_UPDATE_INIT_ZERO LIVEKIT_PB_ACTIVE_SPEAKER_UPDATE_INIT_ZERO +#define LIVEKIT_SPEAKER_INFO_INIT_ZERO LIVEKIT_PB_SPEAKER_INFO_INIT_ZERO +#define LIVEKIT_USER_PACKET_INIT_ZERO LIVEKIT_PB_USER_PACKET_INIT_ZERO +#define LIVEKIT_SIP_DTMF_INIT_ZERO LIVEKIT_PB_SIP_DTMF_INIT_ZERO +#define LIVEKIT_TRANSCRIPTION_INIT_ZERO LIVEKIT_PB_TRANSCRIPTION_INIT_ZERO +#define LIVEKIT_TRANSCRIPTION_SEGMENT_INIT_ZERO LIVEKIT_PB_TRANSCRIPTION_SEGMENT_INIT_ZERO +#define LIVEKIT_CHAT_MESSAGE_INIT_ZERO LIVEKIT_PB_CHAT_MESSAGE_INIT_ZERO +#define LIVEKIT_RPC_REQUEST_INIT_ZERO LIVEKIT_PB_RPC_REQUEST_INIT_ZERO +#define LIVEKIT_RPC_ACK_INIT_ZERO LIVEKIT_PB_RPC_ACK_INIT_ZERO +#define LIVEKIT_RPC_RESPONSE_INIT_ZERO LIVEKIT_PB_RPC_RESPONSE_INIT_ZERO +#define LIVEKIT_RPC_ERROR_INIT_ZERO LIVEKIT_PB_RPC_ERROR_INIT_ZERO +#define LIVEKIT_PARTICIPANT_TRACKS_INIT_ZERO LIVEKIT_PB_PARTICIPANT_TRACKS_INIT_ZERO +#define LIVEKIT_SERVER_INFO_INIT_ZERO LIVEKIT_PB_SERVER_INFO_INIT_ZERO +#define LIVEKIT_CLIENT_INFO_INIT_ZERO LIVEKIT_PB_CLIENT_INFO_INIT_ZERO +#define LIVEKIT_CLIENT_CONFIGURATION_INIT_ZERO LIVEKIT_PB_CLIENT_CONFIGURATION_INIT_ZERO +#define LIVEKIT_VIDEO_CONFIGURATION_INIT_ZERO LIVEKIT_PB_VIDEO_CONFIGURATION_INIT_ZERO +#define LIVEKIT_DISABLED_CODECS_INIT_ZERO LIVEKIT_PB_DISABLED_CODECS_INIT_ZERO +#define LIVEKIT_RTP_DRIFT_INIT_ZERO LIVEKIT_PB_RTP_DRIFT_INIT_ZERO +#define LIVEKIT_RTP_STATS_INIT_ZERO LIVEKIT_PB_RTP_STATS_INIT_ZERO +#define LIVEKIT_RTP_STATS_GAP_HISTOGRAM_ENTRY_INIT_ZERO LIVEKIT_PB_RTP_STATS_GAP_HISTOGRAM_ENTRY_INIT_ZERO +#define LIVEKIT_RTCP_SENDER_REPORT_STATE_INIT_ZERO LIVEKIT_PB_RTCP_SENDER_REPORT_STATE_INIT_ZERO +#define LIVEKIT_RTP_FORWARDER_STATE_INIT_ZERO LIVEKIT_PB_RTP_FORWARDER_STATE_INIT_ZERO +#define LIVEKIT_RTP_MUNGER_STATE_INIT_ZERO LIVEKIT_PB_RTP_MUNGER_STATE_INIT_ZERO +#define LIVEKIT_VP8_MUNGER_STATE_INIT_ZERO LIVEKIT_PB_VP8_MUNGER_STATE_INIT_ZERO +#define LIVEKIT_TIMED_VERSION_INIT_ZERO LIVEKIT_PB_TIMED_VERSION_INIT_ZERO +#define LIVEKIT_DATA_STREAM_INIT_ZERO LIVEKIT_PB_DATA_STREAM_INIT_ZERO +#define LIVEKIT_DATA_STREAM_TEXT_HEADER_INIT_ZERO LIVEKIT_PB_DATA_STREAM_TEXT_HEADER_INIT_ZERO +#define LIVEKIT_DATA_STREAM_BYTE_HEADER_INIT_ZERO LIVEKIT_PB_DATA_STREAM_BYTE_HEADER_INIT_ZERO +#define LIVEKIT_DATA_STREAM_HEADER_INIT_ZERO LIVEKIT_PB_DATA_STREAM_HEADER_INIT_ZERO +#define LIVEKIT_DATA_STREAM_CHUNK_INIT_ZERO LIVEKIT_PB_DATA_STREAM_CHUNK_INIT_ZERO +#define LIVEKIT_DATA_STREAM_TRAILER_INIT_ZERO LIVEKIT_PB_DATA_STREAM_TRAILER_INIT_ZERO +#define LIVEKIT_WEBHOOK_CONFIG_INIT_ZERO LIVEKIT_PB_WEBHOOK_CONFIG_INIT_ZERO + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/components/livekit/protocol/livekit_rtc.pb.c b/components/livekit/protocol/livekit_rtc.pb.c new file mode 100644 index 0000000..c7a0af4 --- /dev/null +++ b/components/livekit/protocol/livekit_rtc.pb.c @@ -0,0 +1,151 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "livekit_rtc.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(LIVEKIT_PB_SIGNAL_REQUEST, livekit_pb_signal_request_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SIGNAL_RESPONSE, livekit_pb_signal_response_t, 2) + + +PB_BIND(LIVEKIT_PB_SIMULCAST_CODEC, livekit_pb_simulcast_codec_t, AUTO) + + +PB_BIND(LIVEKIT_PB_ADD_TRACK_REQUEST, livekit_pb_add_track_request_t, AUTO) + + +PB_BIND(LIVEKIT_PB_TRICKLE_REQUEST, livekit_pb_trickle_request_t, AUTO) + + +PB_BIND(LIVEKIT_PB_MUTE_TRACK_REQUEST, livekit_pb_mute_track_request_t, AUTO) + + +PB_BIND(LIVEKIT_PB_JOIN_RESPONSE, livekit_pb_join_response_t, 2) + + +PB_BIND(LIVEKIT_PB_RECONNECT_RESPONSE, livekit_pb_reconnect_response_t, AUTO) + + +PB_BIND(LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE, livekit_pb_track_published_response_t, AUTO) + + +PB_BIND(LIVEKIT_PB_TRACK_UNPUBLISHED_RESPONSE, livekit_pb_track_unpublished_response_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SESSION_DESCRIPTION, livekit_pb_session_description_t, AUTO) + + +PB_BIND(LIVEKIT_PB_PARTICIPANT_UPDATE, livekit_pb_participant_update_t, AUTO) + + +PB_BIND(LIVEKIT_PB_UPDATE_SUBSCRIPTION, livekit_pb_update_subscription_t, AUTO) + + +PB_BIND(LIVEKIT_PB_UPDATE_TRACK_SETTINGS, livekit_pb_update_track_settings_t, AUTO) + + +PB_BIND(LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK, livekit_pb_update_local_audio_track_t, AUTO) + + +PB_BIND(LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK, livekit_pb_update_local_video_track_t, AUTO) + + +PB_BIND(LIVEKIT_PB_LEAVE_REQUEST, livekit_pb_leave_request_t, AUTO) + + +PB_BIND(LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA, livekit_pb_update_participant_metadata_t, AUTO) + + +PB_BIND(LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY, livekit_pb_update_participant_metadata_attributes_entry_t, AUTO) + + +PB_BIND(LIVEKIT_PB_ICE_SERVER, livekit_pb_ice_server_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SPEAKERS_CHANGED, livekit_pb_speakers_changed_t, AUTO) + + +PB_BIND(LIVEKIT_PB_ROOM_UPDATE, livekit_pb_room_update_t, AUTO) + + +PB_BIND(LIVEKIT_PB_CONNECTION_QUALITY_INFO, livekit_pb_connection_quality_info_t, AUTO) + + +PB_BIND(LIVEKIT_PB_CONNECTION_QUALITY_UPDATE, livekit_pb_connection_quality_update_t, AUTO) + + +PB_BIND(LIVEKIT_PB_STREAM_STATE_INFO, livekit_pb_stream_state_info_t, AUTO) + + +PB_BIND(LIVEKIT_PB_STREAM_STATE_UPDATE, livekit_pb_stream_state_update_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SUBSCRIBED_QUALITY, livekit_pb_subscribed_quality_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SUBSCRIBED_CODEC, livekit_pb_subscribed_codec_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE, livekit_pb_subscribed_quality_update_t, AUTO) + + +PB_BIND(LIVEKIT_PB_TRACK_PERMISSION, livekit_pb_track_permission_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SUBSCRIPTION_PERMISSION, livekit_pb_subscription_permission_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE, livekit_pb_subscription_permission_update_t, AUTO) + + +PB_BIND(LIVEKIT_PB_ROOM_MOVED_RESPONSE, livekit_pb_room_moved_response_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SYNC_STATE, livekit_pb_sync_state_t, AUTO) + + +PB_BIND(LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE, livekit_pb_data_channel_receive_state_t, AUTO) + + +PB_BIND(LIVEKIT_PB_DATA_CHANNEL_INFO, livekit_pb_data_channel_info_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SIMULATE_SCENARIO, livekit_pb_simulate_scenario_t, AUTO) + + +PB_BIND(LIVEKIT_PB_PING, livekit_pb_ping_t, AUTO) + + +PB_BIND(LIVEKIT_PB_PONG, livekit_pb_pong_t, AUTO) + + +PB_BIND(LIVEKIT_PB_REGION_SETTINGS, livekit_pb_region_settings_t, AUTO) + + +PB_BIND(LIVEKIT_PB_REGION_INFO, livekit_pb_region_info_t, AUTO) + + +PB_BIND(LIVEKIT_PB_SUBSCRIPTION_RESPONSE, livekit_pb_subscription_response_t, AUTO) + + +PB_BIND(LIVEKIT_PB_REQUEST_RESPONSE, livekit_pb_request_response_t, AUTO) + + +PB_BIND(LIVEKIT_PB_TRACK_SUBSCRIBED, livekit_pb_track_subscribed_t, AUTO) + + + + + + + + + + + + + diff --git a/components/livekit/protocol/livekit_rtc.pb.h b/components/livekit/protocol/livekit_rtc.pb.h new file mode 100644 index 0000000..19c3cba --- /dev/null +++ b/components/livekit/protocol/livekit_rtc.pb.h @@ -0,0 +1,1477 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_LIVEKIT_LIVEKIT_RTC_PB_H_INCLUDED +#define PB_LIVEKIT_LIVEKIT_RTC_PB_H_INCLUDED +#include +#include "livekit_models.pb.h" + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum livekit_pb_signal_target { + LIVEKIT_PB_SIGNAL_TARGET_PUBLISHER = 0, + LIVEKIT_PB_SIGNAL_TARGET_SUBSCRIBER = 1 +} livekit_pb_signal_target_t; + +typedef enum livekit_pb_stream_state { + LIVEKIT_PB_STREAM_STATE_ACTIVE = 0, + LIVEKIT_PB_STREAM_STATE_PAUSED = 1 +} livekit_pb_stream_state_t; + +typedef enum livekit_pb_candidate_protocol { + LIVEKIT_PB_CANDIDATE_PROTOCOL_UDP = 0, + LIVEKIT_PB_CANDIDATE_PROTOCOL_TCP = 1, + LIVEKIT_PB_CANDIDATE_PROTOCOL_TLS = 2 +} livekit_pb_candidate_protocol_t; + +/* indicates action clients should take on receiving this message */ +typedef enum livekit_pb_leave_request_action { + LIVEKIT_PB_LEAVE_REQUEST_ACTION_DISCONNECT = 0, /* should disconnect */ + LIVEKIT_PB_LEAVE_REQUEST_ACTION_RESUME = 1, /* should attempt a resume with `reconnect=1` in join URL */ + LIVEKIT_PB_LEAVE_REQUEST_ACTION_RECONNECT = 2 /* should attempt a reconnect, i. e. no `reconnect=1` */ +} livekit_pb_leave_request_action_t; + +typedef enum livekit_pb_request_response_reason { + LIVEKIT_PB_REQUEST_RESPONSE_REASON_OK = 0, + LIVEKIT_PB_REQUEST_RESPONSE_REASON_NOT_FOUND = 1, + LIVEKIT_PB_REQUEST_RESPONSE_REASON_NOT_ALLOWED = 2, + LIVEKIT_PB_REQUEST_RESPONSE_REASON_LIMIT_EXCEEDED = 3 +} livekit_pb_request_response_reason_t; + +/* Struct definitions */ +typedef struct livekit_pb_simulcast_codec { + pb_callback_t codec; + pb_callback_t cid; +} livekit_pb_simulcast_codec_t; + +typedef struct livekit_pb_add_track_request { + /* client ID of track, to match it when RTC track is received */ + char cid[16]; + char name[16]; + livekit_pb_track_type_t type; + /* true to add track and initialize to muted */ + bool muted; + livekit_pb_track_source_t source; + pb_size_t layers_count; + livekit_pb_video_layer_t layers[1]; + pb_size_t audio_features_count; + livekit_pb_audio_track_feature_t audio_features[8]; +} livekit_pb_add_track_request_t; + +typedef struct livekit_pb_trickle_request { + char *candidate_init; + livekit_pb_signal_target_t target; + bool final; +} livekit_pb_trickle_request_t; + +typedef struct livekit_pb_mute_track_request { + pb_callback_t sid; + bool muted; +} livekit_pb_mute_track_request_t; + +typedef struct livekit_pb_reconnect_response { + pb_callback_t ice_servers; + bool has_client_configuration; + livekit_pb_client_configuration_t client_configuration; + bool has_server_info; + livekit_pb_server_info_t server_info; + /* last sequence number of reliable message received before resuming */ + uint32_t last_message_seq; +} livekit_pb_reconnect_response_t; + +typedef struct livekit_pb_track_published_response { + char *cid; + livekit_pb_track_info_t track; +} livekit_pb_track_published_response_t; + +typedef struct livekit_pb_track_unpublished_response { + pb_callback_t track_sid; +} livekit_pb_track_unpublished_response_t; + +typedef struct livekit_pb_session_description { + char type[9]; /* "answer" | "offer" | "pranswer" | "rollback" */ + char *sdp; + uint32_t id; +} livekit_pb_session_description_t; + +typedef struct livekit_pb_participant_update { + pb_size_t participants_count; + struct livekit_pb_participant_info *participants; +} livekit_pb_participant_update_t; + +typedef struct livekit_pb_update_subscription { + pb_size_t track_sids_count; + char **track_sids; + bool subscribe; +} livekit_pb_update_subscription_t; + +typedef struct livekit_pb_update_track_settings { + pb_callback_t track_sids; + /* when true, the track is placed in a paused state, with no new data returned */ + bool disabled; + /* deprecated in favor of width & height */ + livekit_pb_video_quality_t quality; + /* for video, width to receive */ + uint32_t width; + /* for video, height to receive */ + uint32_t height; + uint32_t fps; + /* subscription priority. 1 being the highest (0 is unset) + when unset, server sill assign priority based on the order of subscription + server will use priority in the following ways: + 1. when subscribed tracks exceed per-participant subscription limit, server will + pause the lowest priority tracks + 2. when the network is congested, server will assign available bandwidth to + higher priority tracks first. lowest priority tracks can be paused */ + uint32_t priority; +} livekit_pb_update_track_settings_t; + +typedef struct livekit_pb_update_local_audio_track { + pb_callback_t track_sid; + pb_callback_t features; +} livekit_pb_update_local_audio_track_t; + +typedef struct livekit_pb_update_local_video_track { + pb_callback_t track_sid; + uint32_t width; + uint32_t height; +} livekit_pb_update_local_video_track_t; + +typedef struct livekit_pb_leave_request { + livekit_pb_disconnect_reason_t reason; + livekit_pb_leave_request_action_t action; +} livekit_pb_leave_request_t; + +typedef struct livekit_pb_update_participant_metadata { + pb_callback_t metadata; + pb_callback_t name; + /* attributes to update. it only updates attributes that have been set + to delete attributes, set the value to an empty string */ + pb_callback_t attributes; + uint32_t request_id; +} livekit_pb_update_participant_metadata_t; + +typedef struct livekit_pb_update_participant_metadata_attributes_entry { + pb_callback_t key; + pb_callback_t value; +} livekit_pb_update_participant_metadata_attributes_entry_t; + +typedef struct livekit_pb_ice_server { + pb_size_t urls_count; + char **urls; + char *username; + char *credential; +} livekit_pb_ice_server_t; + +typedef struct livekit_pb_join_response { + bool has_room; + livekit_pb_room_t room; + livekit_pb_participant_info_t participant; + pb_size_t other_participants_count; + struct livekit_pb_participant_info *other_participants; + pb_size_t ice_servers_count; + livekit_pb_ice_server_t ice_servers[4]; + /* use subscriber as the primary PeerConnection */ + bool subscriber_primary; + bool has_client_configuration; + livekit_pb_client_configuration_t client_configuration; + int32_t ping_timeout; + int32_t ping_interval; +} livekit_pb_join_response_t; + +typedef struct livekit_pb_speakers_changed { + pb_callback_t speakers; +} livekit_pb_speakers_changed_t; + +typedef struct livekit_pb_room_update { + bool has_room; + livekit_pb_room_t room; +} livekit_pb_room_update_t; + +typedef struct livekit_pb_connection_quality_info { + pb_callback_t participant_sid; + livekit_pb_connection_quality_t quality; + float score; +} livekit_pb_connection_quality_info_t; + +typedef struct livekit_pb_connection_quality_update { + pb_callback_t updates; +} livekit_pb_connection_quality_update_t; + +typedef struct livekit_pb_stream_state_info { + pb_callback_t participant_sid; + pb_callback_t track_sid; + livekit_pb_stream_state_t state; +} livekit_pb_stream_state_info_t; + +typedef struct livekit_pb_stream_state_update { + pb_callback_t stream_states; +} livekit_pb_stream_state_update_t; + +typedef struct livekit_pb_subscribed_quality { + livekit_pb_video_quality_t quality; + bool enabled; +} livekit_pb_subscribed_quality_t; + +typedef struct livekit_pb_subscribed_codec { + pb_callback_t codec; + pb_callback_t qualities; +} livekit_pb_subscribed_codec_t; + +typedef struct livekit_pb_subscribed_quality_update { + pb_callback_t track_sid; + pb_callback_t subscribed_codecs; +} livekit_pb_subscribed_quality_update_t; + +typedef struct livekit_pb_track_permission { + /* permission could be granted either by participant sid or identity */ + pb_callback_t participant_sid; + bool all_tracks; + pb_callback_t track_sids; + pb_callback_t participant_identity; +} livekit_pb_track_permission_t; + +typedef struct livekit_pb_subscription_permission { + bool all_participants; + pb_callback_t track_permissions; +} livekit_pb_subscription_permission_t; + +typedef struct livekit_pb_subscription_permission_update { + pb_callback_t participant_sid; + pb_callback_t track_sid; + bool allowed; +} livekit_pb_subscription_permission_update_t; + +typedef struct livekit_pb_room_moved_response { + /* information about the new room */ + bool has_room; + livekit_pb_room_t room; + /* new reconnect token that can be used to reconnect to the new room */ + pb_callback_t token; + bool has_participant; + livekit_pb_participant_info_t participant; + pb_callback_t other_participants; +} livekit_pb_room_moved_response_t; + +typedef struct livekit_pb_sync_state { + /* last subscribe answer before reconnecting */ + bool has_answer; + livekit_pb_session_description_t answer; + bool has_subscription; + livekit_pb_update_subscription_t subscription; + pb_callback_t publish_tracks; + pb_callback_t data_channels; + /* last received server side offer before reconnecting */ + bool has_offer; + livekit_pb_session_description_t offer; + pb_callback_t track_sids_disabled; + pb_callback_t datachannel_receive_states; +} livekit_pb_sync_state_t; + +typedef struct livekit_pb_data_channel_receive_state { + pb_callback_t publisher_sid; + uint32_t last_seq; +} livekit_pb_data_channel_receive_state_t; + +typedef struct livekit_pb_data_channel_info { + pb_callback_t label; + uint32_t id; + livekit_pb_signal_target_t target; +} livekit_pb_data_channel_info_t; + +typedef struct livekit_pb_simulate_scenario { + pb_size_t which_scenario; + union { + /* simulate N seconds of speaker activity */ + int32_t speaker_update; + /* simulate local node failure */ + bool node_failure; + /* simulate migration */ + bool migration; + /* server to send leave */ + bool server_leave; + /* switch candidate protocol to tcp */ + livekit_pb_candidate_protocol_t switch_candidate_protocol; + /* maximum bandwidth for subscribers, in bps + when zero, clears artificial bandwidth limit */ + int64_t subscriber_bandwidth; + /* disconnect signal on resume */ + bool disconnect_signal_on_resume; + /* disconnect signal on resume before sending any messages from server */ + bool disconnect_signal_on_resume_no_messages; + /* full reconnect leave request */ + bool leave_request_full_reconnect; + } scenario; +} livekit_pb_simulate_scenario_t; + +typedef struct livekit_pb_ping { + int64_t timestamp; + /* rtt in milliseconds calculated by client */ + int64_t rtt; +} livekit_pb_ping_t; + +typedef struct livekit_pb_signal_request { + pb_size_t which_message; + union { + /* initial join exchange, for publisher */ + livekit_pb_session_description_t offer; + /* participant answering publisher offer */ + livekit_pb_session_description_t answer; + livekit_pb_trickle_request_t trickle; + livekit_pb_add_track_request_t add_track; + /* mute the participant's published tracks */ + livekit_pb_mute_track_request_t mute; + /* Subscribe or unsubscribe from tracks */ + livekit_pb_update_subscription_t subscription; + /* Update settings of subscribed tracks */ + livekit_pb_update_track_settings_t track_setting; + /* Immediately terminate session */ + livekit_pb_leave_request_t leave; + /* Update subscriber permissions */ + livekit_pb_subscription_permission_t subscription_permission; + /* sync client's subscribe state to server during reconnect */ + livekit_pb_sync_state_t sync_state; + /* Simulate conditions, for client validations */ + livekit_pb_simulate_scenario_t simulate; + /* client triggered ping to server */ + int64_t ping; /* deprecated by ping_req (message Ping) */ + /* update a participant's own metadata, name, or attributes + requires canUpdateOwnParticipantMetadata permission */ + livekit_pb_update_participant_metadata_t update_metadata; + livekit_pb_ping_t ping_req; + /* Update local audio track settings */ + livekit_pb_update_local_audio_track_t update_audio_track; + /* Update local video track settings */ + livekit_pb_update_local_video_track_t update_video_track; + } message; +} livekit_pb_signal_request_t; + +typedef struct livekit_pb_pong { + /* timestamp field of last received ping request */ + int64_t last_ping_timestamp; + int64_t timestamp; +} livekit_pb_pong_t; + +typedef struct livekit_pb_region_settings { + pb_callback_t regions; +} livekit_pb_region_settings_t; + +typedef struct livekit_pb_region_info { + pb_callback_t region; + pb_callback_t url; + int64_t distance; +} livekit_pb_region_info_t; + +typedef struct livekit_pb_subscription_response { + pb_callback_t track_sid; + livekit_pb_subscription_error_t err; +} livekit_pb_subscription_response_t; + +typedef struct livekit_pb_request_response { + uint32_t request_id; + livekit_pb_request_response_reason_t reason; + pb_callback_t message; +} livekit_pb_request_response_t; + +typedef struct livekit_pb_track_subscribed { + pb_callback_t track_sid; +} livekit_pb_track_subscribed_t; + +typedef struct livekit_pb_signal_response { + pb_size_t which_message; + union { + /* sent when join is accepted */ + livekit_pb_join_response_t join; + /* sent when server answers publisher */ + livekit_pb_session_description_t answer; + /* sent when server is sending subscriber an offer */ + livekit_pb_session_description_t offer; + /* sent when an ICE candidate is available */ + livekit_pb_trickle_request_t trickle; + /* sent when participants in the room has changed */ + livekit_pb_participant_update_t update; + /* sent to the participant when their track has been published */ + livekit_pb_track_published_response_t track_published; + /* Immediately terminate session */ + livekit_pb_leave_request_t leave; + /* server initiated mute */ + livekit_pb_mute_track_request_t mute; + /* indicates changes to speaker status, including when they've gone to not speaking */ + livekit_pb_speakers_changed_t speakers_changed; + /* sent when metadata of the room has changed */ + livekit_pb_room_update_t room_update; + /* when connection quality changed */ + livekit_pb_connection_quality_update_t connection_quality; + /* when streamed tracks state changed, used to notify when any of the streams were paused due to + congestion */ + livekit_pb_stream_state_update_t stream_state_update; + /* when max subscribe quality changed, used by dynamic broadcasting to disable unused layers */ + livekit_pb_subscribed_quality_update_t subscribed_quality_update; + /* when subscription permission changed */ + livekit_pb_subscription_permission_update_t subscription_permission_update; + /* update the token the client was using, to prevent an active client from using an expired token */ + pb_callback_t refresh_token; + /* server initiated track unpublish */ + livekit_pb_track_unpublished_response_t track_unpublished; + /* respond to ping */ + int64_t pong; /* deprecated by pong_resp (message Pong) */ + /* sent when client reconnects */ + livekit_pb_reconnect_response_t reconnect; + /* respond to Ping */ + livekit_pb_pong_t pong_resp; + /* Subscription response, client should not expect any media from this subscription if it fails */ + livekit_pb_subscription_response_t subscription_response; + /* Response relating to user inititated requests that carry a `request_id` */ + livekit_pb_request_response_t request_response; + /* notify to the publisher when a published track has been subscribed for the first time */ + livekit_pb_track_subscribed_t track_subscribed; + /* notify to the participant when they have been moved to a new room */ + livekit_pb_room_moved_response_t room_moved; + } message; +} livekit_pb_signal_response_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _LIVEKIT_PB_SIGNAL_TARGET_MIN LIVEKIT_PB_SIGNAL_TARGET_PUBLISHER +#define _LIVEKIT_PB_SIGNAL_TARGET_MAX LIVEKIT_PB_SIGNAL_TARGET_SUBSCRIBER +#define _LIVEKIT_PB_SIGNAL_TARGET_ARRAYSIZE ((livekit_pb_signal_target_t)(LIVEKIT_PB_SIGNAL_TARGET_SUBSCRIBER+1)) + +#define _LIVEKIT_PB_STREAM_STATE_MIN LIVEKIT_PB_STREAM_STATE_ACTIVE +#define _LIVEKIT_PB_STREAM_STATE_MAX LIVEKIT_PB_STREAM_STATE_PAUSED +#define _LIVEKIT_PB_STREAM_STATE_ARRAYSIZE ((livekit_pb_stream_state_t)(LIVEKIT_PB_STREAM_STATE_PAUSED+1)) + +#define _LIVEKIT_PB_CANDIDATE_PROTOCOL_MIN LIVEKIT_PB_CANDIDATE_PROTOCOL_UDP +#define _LIVEKIT_PB_CANDIDATE_PROTOCOL_MAX LIVEKIT_PB_CANDIDATE_PROTOCOL_TLS +#define _LIVEKIT_PB_CANDIDATE_PROTOCOL_ARRAYSIZE ((livekit_pb_candidate_protocol_t)(LIVEKIT_PB_CANDIDATE_PROTOCOL_TLS+1)) + +#define _LIVEKIT_PB_LEAVE_REQUEST_ACTION_MIN LIVEKIT_PB_LEAVE_REQUEST_ACTION_DISCONNECT +#define _LIVEKIT_PB_LEAVE_REQUEST_ACTION_MAX LIVEKIT_PB_LEAVE_REQUEST_ACTION_RECONNECT +#define _LIVEKIT_PB_LEAVE_REQUEST_ACTION_ARRAYSIZE ((livekit_pb_leave_request_action_t)(LIVEKIT_PB_LEAVE_REQUEST_ACTION_RECONNECT+1)) + +#define _LIVEKIT_PB_REQUEST_RESPONSE_REASON_MIN LIVEKIT_PB_REQUEST_RESPONSE_REASON_OK +#define _LIVEKIT_PB_REQUEST_RESPONSE_REASON_MAX LIVEKIT_PB_REQUEST_RESPONSE_REASON_LIMIT_EXCEEDED +#define _LIVEKIT_PB_REQUEST_RESPONSE_REASON_ARRAYSIZE ((livekit_pb_request_response_reason_t)(LIVEKIT_PB_REQUEST_RESPONSE_REASON_LIMIT_EXCEEDED+1)) + + + + +#define livekit_pb_add_track_request_t_type_ENUMTYPE livekit_pb_track_type_t +#define livekit_pb_add_track_request_t_source_ENUMTYPE livekit_pb_track_source_t +#define livekit_pb_add_track_request_t_audio_features_ENUMTYPE livekit_pb_audio_track_feature_t + +#define livekit_pb_trickle_request_t_target_ENUMTYPE livekit_pb_signal_target_t + + + + + + + + + +#define livekit_pb_update_track_settings_t_quality_ENUMTYPE livekit_pb_video_quality_t + +#define livekit_pb_update_local_audio_track_t_features_ENUMTYPE livekit_pb_audio_track_feature_t + + +#define livekit_pb_leave_request_t_reason_ENUMTYPE livekit_pb_disconnect_reason_t +#define livekit_pb_leave_request_t_action_ENUMTYPE livekit_pb_leave_request_action_t + + + + + + +#define livekit_pb_connection_quality_info_t_quality_ENUMTYPE livekit_pb_connection_quality_t + + +#define livekit_pb_stream_state_info_t_state_ENUMTYPE livekit_pb_stream_state_t + + +#define livekit_pb_subscribed_quality_t_quality_ENUMTYPE livekit_pb_video_quality_t + + + + + + + + + +#define livekit_pb_data_channel_info_t_target_ENUMTYPE livekit_pb_signal_target_t + +#define livekit_pb_simulate_scenario_t_scenario_switch_candidate_protocol_ENUMTYPE livekit_pb_candidate_protocol_t + + + + + +#define livekit_pb_subscription_response_t_err_ENUMTYPE livekit_pb_subscription_error_t + +#define livekit_pb_request_response_t_reason_ENUMTYPE livekit_pb_request_response_reason_t + + + +/* Initializer values for message structs */ +#define LIVEKIT_PB_SIGNAL_REQUEST_INIT_DEFAULT {0, {LIVEKIT_PB_SESSION_DESCRIPTION_INIT_DEFAULT}} +#define LIVEKIT_PB_SIGNAL_RESPONSE_INIT_DEFAULT {0, {LIVEKIT_PB_JOIN_RESPONSE_INIT_DEFAULT}} +#define LIVEKIT_PB_SIMULCAST_CODEC_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_ADD_TRACK_REQUEST_INIT_DEFAULT {"", "", _LIVEKIT_PB_TRACK_TYPE_MIN, 0, _LIVEKIT_PB_TRACK_SOURCE_MIN, 0, {LIVEKIT_PB_VIDEO_LAYER_INIT_DEFAULT}, 0, {_LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN}} +#define LIVEKIT_PB_TRICKLE_REQUEST_INIT_DEFAULT {NULL, _LIVEKIT_PB_SIGNAL_TARGET_MIN, 0} +#define LIVEKIT_PB_MUTE_TRACK_REQUEST_INIT_DEFAULT {{{NULL}, NULL}, 0} +#define LIVEKIT_PB_JOIN_RESPONSE_INIT_DEFAULT {false, LIVEKIT_PB_ROOM_INIT_DEFAULT, LIVEKIT_PB_PARTICIPANT_INFO_INIT_DEFAULT, 0, NULL, 0, {LIVEKIT_PB_ICE_SERVER_INIT_DEFAULT, LIVEKIT_PB_ICE_SERVER_INIT_DEFAULT, LIVEKIT_PB_ICE_SERVER_INIT_DEFAULT, LIVEKIT_PB_ICE_SERVER_INIT_DEFAULT}, 0, false, LIVEKIT_PB_CLIENT_CONFIGURATION_INIT_DEFAULT, 0, 0} +#define LIVEKIT_PB_RECONNECT_RESPONSE_INIT_DEFAULT {{{NULL}, NULL}, false, LIVEKIT_PB_CLIENT_CONFIGURATION_INIT_DEFAULT, false, LIVEKIT_PB_SERVER_INFO_INIT_DEFAULT, 0} +#define LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE_INIT_DEFAULT {NULL, LIVEKIT_PB_TRACK_INFO_INIT_DEFAULT} +#define LIVEKIT_PB_TRACK_UNPUBLISHED_RESPONSE_INIT_DEFAULT {{{NULL}, NULL}} +#define LIVEKIT_PB_SESSION_DESCRIPTION_INIT_DEFAULT {"", NULL, 0} +#define LIVEKIT_PB_PARTICIPANT_UPDATE_INIT_DEFAULT {0, NULL} +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_INIT_DEFAULT {0, NULL, 0} +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_INIT_DEFAULT {{{NULL}, NULL}, 0, _LIVEKIT_PB_VIDEO_QUALITY_MIN, 0, 0, 0, 0} +#define LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_INIT_DEFAULT {{{NULL}, NULL}, 0, 0} +#define LIVEKIT_PB_LEAVE_REQUEST_INIT_DEFAULT {_LIVEKIT_PB_DISCONNECT_REASON_MIN, _LIVEKIT_PB_LEAVE_REQUEST_ACTION_MIN} +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_ICE_SERVER_INIT_DEFAULT {0, NULL, NULL, NULL} +#define LIVEKIT_PB_SPEAKERS_CHANGED_INIT_DEFAULT {{{NULL}, NULL}} +#define LIVEKIT_PB_ROOM_UPDATE_INIT_DEFAULT {false, LIVEKIT_PB_ROOM_INIT_DEFAULT} +#define LIVEKIT_PB_CONNECTION_QUALITY_INFO_INIT_DEFAULT {{{NULL}, NULL}, _LIVEKIT_PB_CONNECTION_QUALITY_MIN, 0} +#define LIVEKIT_PB_CONNECTION_QUALITY_UPDATE_INIT_DEFAULT {{{NULL}, NULL}} +#define LIVEKIT_PB_STREAM_STATE_INFO_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}, _LIVEKIT_PB_STREAM_STATE_MIN} +#define LIVEKIT_PB_STREAM_STATE_UPDATE_INIT_DEFAULT {{{NULL}, NULL}} +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_INIT_DEFAULT {_LIVEKIT_PB_VIDEO_QUALITY_MIN, 0} +#define LIVEKIT_PB_SUBSCRIBED_CODEC_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_TRACK_PERMISSION_INIT_DEFAULT {{{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_INIT_DEFAULT {0, {{NULL}, NULL}} +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_ROOM_MOVED_RESPONSE_INIT_DEFAULT {false, LIVEKIT_PB_ROOM_INIT_DEFAULT, {{NULL}, NULL}, false, LIVEKIT_PB_PARTICIPANT_INFO_INIT_DEFAULT, {{NULL}, NULL}} +#define LIVEKIT_PB_SYNC_STATE_INIT_DEFAULT {false, LIVEKIT_PB_SESSION_DESCRIPTION_INIT_DEFAULT, false, LIVEKIT_PB_UPDATE_SUBSCRIPTION_INIT_DEFAULT, {{NULL}, NULL}, {{NULL}, NULL}, false, LIVEKIT_PB_SESSION_DESCRIPTION_INIT_DEFAULT, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE_INIT_DEFAULT {{{NULL}, NULL}, 0} +#define LIVEKIT_PB_DATA_CHANNEL_INFO_INIT_DEFAULT {{{NULL}, NULL}, 0, _LIVEKIT_PB_SIGNAL_TARGET_MIN} +#define LIVEKIT_PB_SIMULATE_SCENARIO_INIT_DEFAULT {0, {0}} +#define LIVEKIT_PB_PING_INIT_DEFAULT {0, 0} +#define LIVEKIT_PB_PONG_INIT_DEFAULT {0, 0} +#define LIVEKIT_PB_REGION_SETTINGS_INIT_DEFAULT {{{NULL}, NULL}} +#define LIVEKIT_PB_REGION_INFO_INIT_DEFAULT {{{NULL}, NULL}, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_SUBSCRIPTION_RESPONSE_INIT_DEFAULT {{{NULL}, NULL}, _LIVEKIT_PB_SUBSCRIPTION_ERROR_MIN} +#define LIVEKIT_PB_REQUEST_RESPONSE_INIT_DEFAULT {0, _LIVEKIT_PB_REQUEST_RESPONSE_REASON_MIN, {{NULL}, NULL}} +#define LIVEKIT_PB_TRACK_SUBSCRIBED_INIT_DEFAULT {{{NULL}, NULL}} +#define LIVEKIT_PB_SIGNAL_REQUEST_INIT_ZERO {0, {LIVEKIT_PB_SESSION_DESCRIPTION_INIT_ZERO}} +#define LIVEKIT_PB_SIGNAL_RESPONSE_INIT_ZERO {0, {LIVEKIT_PB_JOIN_RESPONSE_INIT_ZERO}} +#define LIVEKIT_PB_SIMULCAST_CODEC_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_ADD_TRACK_REQUEST_INIT_ZERO {"", "", _LIVEKIT_PB_TRACK_TYPE_MIN, 0, _LIVEKIT_PB_TRACK_SOURCE_MIN, 0, {LIVEKIT_PB_VIDEO_LAYER_INIT_ZERO}, 0, {_LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN, _LIVEKIT_PB_AUDIO_TRACK_FEATURE_MIN}} +#define LIVEKIT_PB_TRICKLE_REQUEST_INIT_ZERO {NULL, _LIVEKIT_PB_SIGNAL_TARGET_MIN, 0} +#define LIVEKIT_PB_MUTE_TRACK_REQUEST_INIT_ZERO {{{NULL}, NULL}, 0} +#define LIVEKIT_PB_JOIN_RESPONSE_INIT_ZERO {false, LIVEKIT_PB_ROOM_INIT_ZERO, LIVEKIT_PB_PARTICIPANT_INFO_INIT_ZERO, 0, NULL, 0, {LIVEKIT_PB_ICE_SERVER_INIT_ZERO, LIVEKIT_PB_ICE_SERVER_INIT_ZERO, LIVEKIT_PB_ICE_SERVER_INIT_ZERO, LIVEKIT_PB_ICE_SERVER_INIT_ZERO}, 0, false, LIVEKIT_PB_CLIENT_CONFIGURATION_INIT_ZERO, 0, 0} +#define LIVEKIT_PB_RECONNECT_RESPONSE_INIT_ZERO {{{NULL}, NULL}, false, LIVEKIT_PB_CLIENT_CONFIGURATION_INIT_ZERO, false, LIVEKIT_PB_SERVER_INFO_INIT_ZERO, 0} +#define LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE_INIT_ZERO {NULL, LIVEKIT_PB_TRACK_INFO_INIT_ZERO} +#define LIVEKIT_PB_TRACK_UNPUBLISHED_RESPONSE_INIT_ZERO {{{NULL}, NULL}} +#define LIVEKIT_PB_SESSION_DESCRIPTION_INIT_ZERO {"", NULL, 0} +#define LIVEKIT_PB_PARTICIPANT_UPDATE_INIT_ZERO {0, NULL} +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_INIT_ZERO {0, NULL, 0} +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_INIT_ZERO {{{NULL}, NULL}, 0, _LIVEKIT_PB_VIDEO_QUALITY_MIN, 0, 0, 0, 0} +#define LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_INIT_ZERO {{{NULL}, NULL}, 0, 0} +#define LIVEKIT_PB_LEAVE_REQUEST_INIT_ZERO {_LIVEKIT_PB_DISCONNECT_REASON_MIN, _LIVEKIT_PB_LEAVE_REQUEST_ACTION_MIN} +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_ICE_SERVER_INIT_ZERO {0, NULL, NULL, NULL} +#define LIVEKIT_PB_SPEAKERS_CHANGED_INIT_ZERO {{{NULL}, NULL}} +#define LIVEKIT_PB_ROOM_UPDATE_INIT_ZERO {false, LIVEKIT_PB_ROOM_INIT_ZERO} +#define LIVEKIT_PB_CONNECTION_QUALITY_INFO_INIT_ZERO {{{NULL}, NULL}, _LIVEKIT_PB_CONNECTION_QUALITY_MIN, 0} +#define LIVEKIT_PB_CONNECTION_QUALITY_UPDATE_INIT_ZERO {{{NULL}, NULL}} +#define LIVEKIT_PB_STREAM_STATE_INFO_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}, _LIVEKIT_PB_STREAM_STATE_MIN} +#define LIVEKIT_PB_STREAM_STATE_UPDATE_INIT_ZERO {{{NULL}, NULL}} +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_INIT_ZERO {_LIVEKIT_PB_VIDEO_QUALITY_MIN, 0} +#define LIVEKIT_PB_SUBSCRIBED_CODEC_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_TRACK_PERMISSION_INIT_ZERO {{{NULL}, NULL}, 0, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_INIT_ZERO {0, {{NULL}, NULL}} +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_ROOM_MOVED_RESPONSE_INIT_ZERO {false, LIVEKIT_PB_ROOM_INIT_ZERO, {{NULL}, NULL}, false, LIVEKIT_PB_PARTICIPANT_INFO_INIT_ZERO, {{NULL}, NULL}} +#define LIVEKIT_PB_SYNC_STATE_INIT_ZERO {false, LIVEKIT_PB_SESSION_DESCRIPTION_INIT_ZERO, false, LIVEKIT_PB_UPDATE_SUBSCRIPTION_INIT_ZERO, {{NULL}, NULL}, {{NULL}, NULL}, false, LIVEKIT_PB_SESSION_DESCRIPTION_INIT_ZERO, {{NULL}, NULL}, {{NULL}, NULL}} +#define LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE_INIT_ZERO {{{NULL}, NULL}, 0} +#define LIVEKIT_PB_DATA_CHANNEL_INFO_INIT_ZERO {{{NULL}, NULL}, 0, _LIVEKIT_PB_SIGNAL_TARGET_MIN} +#define LIVEKIT_PB_SIMULATE_SCENARIO_INIT_ZERO {0, {0}} +#define LIVEKIT_PB_PING_INIT_ZERO {0, 0} +#define LIVEKIT_PB_PONG_INIT_ZERO {0, 0} +#define LIVEKIT_PB_REGION_SETTINGS_INIT_ZERO {{{NULL}, NULL}} +#define LIVEKIT_PB_REGION_INFO_INIT_ZERO {{{NULL}, NULL}, {{NULL}, NULL}, 0} +#define LIVEKIT_PB_SUBSCRIPTION_RESPONSE_INIT_ZERO {{{NULL}, NULL}, _LIVEKIT_PB_SUBSCRIPTION_ERROR_MIN} +#define LIVEKIT_PB_REQUEST_RESPONSE_INIT_ZERO {0, _LIVEKIT_PB_REQUEST_RESPONSE_REASON_MIN, {{NULL}, NULL}} +#define LIVEKIT_PB_TRACK_SUBSCRIBED_INIT_ZERO {{{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define LIVEKIT_PB_SIMULCAST_CODEC_CODEC_TAG 1 +#define LIVEKIT_PB_SIMULCAST_CODEC_CID_TAG 2 +#define LIVEKIT_PB_ADD_TRACK_REQUEST_CID_TAG 1 +#define LIVEKIT_PB_ADD_TRACK_REQUEST_NAME_TAG 2 +#define LIVEKIT_PB_ADD_TRACK_REQUEST_TYPE_TAG 3 +#define LIVEKIT_PB_ADD_TRACK_REQUEST_MUTED_TAG 6 +#define LIVEKIT_PB_ADD_TRACK_REQUEST_SOURCE_TAG 8 +#define LIVEKIT_PB_ADD_TRACK_REQUEST_LAYERS_TAG 9 +#define LIVEKIT_PB_ADD_TRACK_REQUEST_AUDIO_FEATURES_TAG 17 +#define LIVEKIT_PB_TRICKLE_REQUEST_CANDIDATE_INIT_TAG 1 +#define LIVEKIT_PB_TRICKLE_REQUEST_TARGET_TAG 2 +#define LIVEKIT_PB_TRICKLE_REQUEST_FINAL_TAG 3 +#define LIVEKIT_PB_MUTE_TRACK_REQUEST_SID_TAG 1 +#define LIVEKIT_PB_MUTE_TRACK_REQUEST_MUTED_TAG 2 +#define LIVEKIT_PB_RECONNECT_RESPONSE_ICE_SERVERS_TAG 1 +#define LIVEKIT_PB_RECONNECT_RESPONSE_CLIENT_CONFIGURATION_TAG 2 +#define LIVEKIT_PB_RECONNECT_RESPONSE_SERVER_INFO_TAG 3 +#define LIVEKIT_PB_RECONNECT_RESPONSE_LAST_MESSAGE_SEQ_TAG 4 +#define LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE_CID_TAG 1 +#define LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE_TRACK_TAG 2 +#define LIVEKIT_PB_TRACK_UNPUBLISHED_RESPONSE_TRACK_SID_TAG 1 +#define LIVEKIT_PB_SESSION_DESCRIPTION_TYPE_TAG 1 +#define LIVEKIT_PB_SESSION_DESCRIPTION_SDP_TAG 2 +#define LIVEKIT_PB_SESSION_DESCRIPTION_ID_TAG 3 +#define LIVEKIT_PB_PARTICIPANT_UPDATE_PARTICIPANTS_TAG 1 +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_TRACK_SIDS_TAG 1 +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_SUBSCRIBE_TAG 2 +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_TRACK_SIDS_TAG 1 +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_DISABLED_TAG 3 +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_QUALITY_TAG 4 +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_WIDTH_TAG 5 +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_HEIGHT_TAG 6 +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_FPS_TAG 7 +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_PRIORITY_TAG 8 +#define LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK_TRACK_SID_TAG 1 +#define LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK_FEATURES_TAG 2 +#define LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_TRACK_SID_TAG 1 +#define LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_WIDTH_TAG 2 +#define LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_HEIGHT_TAG 3 +#define LIVEKIT_PB_LEAVE_REQUEST_REASON_TAG 2 +#define LIVEKIT_PB_LEAVE_REQUEST_ACTION_TAG 3 +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_METADATA_TAG 1 +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_NAME_TAG 2 +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_TAG 3 +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_REQUEST_ID_TAG 4 +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_KEY_TAG 1 +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_VALUE_TAG 2 +#define LIVEKIT_PB_ICE_SERVER_URLS_TAG 1 +#define LIVEKIT_PB_ICE_SERVER_USERNAME_TAG 2 +#define LIVEKIT_PB_ICE_SERVER_CREDENTIAL_TAG 3 +#define LIVEKIT_PB_JOIN_RESPONSE_ROOM_TAG 1 +#define LIVEKIT_PB_JOIN_RESPONSE_PARTICIPANT_TAG 2 +#define LIVEKIT_PB_JOIN_RESPONSE_OTHER_PARTICIPANTS_TAG 3 +#define LIVEKIT_PB_JOIN_RESPONSE_ICE_SERVERS_TAG 5 +#define LIVEKIT_PB_JOIN_RESPONSE_SUBSCRIBER_PRIMARY_TAG 6 +#define LIVEKIT_PB_JOIN_RESPONSE_CLIENT_CONFIGURATION_TAG 8 +#define LIVEKIT_PB_JOIN_RESPONSE_PING_TIMEOUT_TAG 10 +#define LIVEKIT_PB_JOIN_RESPONSE_PING_INTERVAL_TAG 11 +#define LIVEKIT_PB_SPEAKERS_CHANGED_SPEAKERS_TAG 1 +#define LIVEKIT_PB_ROOM_UPDATE_ROOM_TAG 1 +#define LIVEKIT_PB_CONNECTION_QUALITY_INFO_PARTICIPANT_SID_TAG 1 +#define LIVEKIT_PB_CONNECTION_QUALITY_INFO_QUALITY_TAG 2 +#define LIVEKIT_PB_CONNECTION_QUALITY_INFO_SCORE_TAG 3 +#define LIVEKIT_PB_CONNECTION_QUALITY_UPDATE_UPDATES_TAG 1 +#define LIVEKIT_PB_STREAM_STATE_INFO_PARTICIPANT_SID_TAG 1 +#define LIVEKIT_PB_STREAM_STATE_INFO_TRACK_SID_TAG 2 +#define LIVEKIT_PB_STREAM_STATE_INFO_STATE_TAG 3 +#define LIVEKIT_PB_STREAM_STATE_UPDATE_STREAM_STATES_TAG 1 +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_QUALITY_TAG 1 +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_ENABLED_TAG 2 +#define LIVEKIT_PB_SUBSCRIBED_CODEC_CODEC_TAG 1 +#define LIVEKIT_PB_SUBSCRIBED_CODEC_QUALITIES_TAG 2 +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE_TRACK_SID_TAG 1 +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE_SUBSCRIBED_CODECS_TAG 3 +#define LIVEKIT_PB_TRACK_PERMISSION_PARTICIPANT_SID_TAG 1 +#define LIVEKIT_PB_TRACK_PERMISSION_ALL_TRACKS_TAG 2 +#define LIVEKIT_PB_TRACK_PERMISSION_TRACK_SIDS_TAG 3 +#define LIVEKIT_PB_TRACK_PERMISSION_PARTICIPANT_IDENTITY_TAG 4 +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_ALL_PARTICIPANTS_TAG 1 +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_TRACK_PERMISSIONS_TAG 2 +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_PARTICIPANT_SID_TAG 1 +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_TRACK_SID_TAG 2 +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_ALLOWED_TAG 3 +#define LIVEKIT_PB_ROOM_MOVED_RESPONSE_ROOM_TAG 1 +#define LIVEKIT_PB_ROOM_MOVED_RESPONSE_TOKEN_TAG 2 +#define LIVEKIT_PB_ROOM_MOVED_RESPONSE_PARTICIPANT_TAG 3 +#define LIVEKIT_PB_ROOM_MOVED_RESPONSE_OTHER_PARTICIPANTS_TAG 4 +#define LIVEKIT_PB_SYNC_STATE_ANSWER_TAG 1 +#define LIVEKIT_PB_SYNC_STATE_SUBSCRIPTION_TAG 2 +#define LIVEKIT_PB_SYNC_STATE_PUBLISH_TRACKS_TAG 3 +#define LIVEKIT_PB_SYNC_STATE_DATA_CHANNELS_TAG 4 +#define LIVEKIT_PB_SYNC_STATE_OFFER_TAG 5 +#define LIVEKIT_PB_SYNC_STATE_TRACK_SIDS_DISABLED_TAG 6 +#define LIVEKIT_PB_SYNC_STATE_DATACHANNEL_RECEIVE_STATES_TAG 7 +#define LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE_PUBLISHER_SID_TAG 1 +#define LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE_LAST_SEQ_TAG 2 +#define LIVEKIT_PB_DATA_CHANNEL_INFO_LABEL_TAG 1 +#define LIVEKIT_PB_DATA_CHANNEL_INFO_ID_TAG 2 +#define LIVEKIT_PB_DATA_CHANNEL_INFO_TARGET_TAG 3 +#define LIVEKIT_PB_SIMULATE_SCENARIO_SPEAKER_UPDATE_TAG 1 +#define LIVEKIT_PB_SIMULATE_SCENARIO_NODE_FAILURE_TAG 2 +#define LIVEKIT_PB_SIMULATE_SCENARIO_MIGRATION_TAG 3 +#define LIVEKIT_PB_SIMULATE_SCENARIO_SERVER_LEAVE_TAG 4 +#define LIVEKIT_PB_SIMULATE_SCENARIO_SWITCH_CANDIDATE_PROTOCOL_TAG 5 +#define LIVEKIT_PB_SIMULATE_SCENARIO_SUBSCRIBER_BANDWIDTH_TAG 6 +#define LIVEKIT_PB_SIMULATE_SCENARIO_DISCONNECT_SIGNAL_ON_RESUME_TAG 7 +#define LIVEKIT_PB_SIMULATE_SCENARIO_DISCONNECT_SIGNAL_ON_RESUME_NO_MESSAGES_TAG 8 +#define LIVEKIT_PB_SIMULATE_SCENARIO_LEAVE_REQUEST_FULL_RECONNECT_TAG 9 +#define LIVEKIT_PB_PING_TIMESTAMP_TAG 1 +#define LIVEKIT_PB_PING_RTT_TAG 2 +#define LIVEKIT_PB_SIGNAL_REQUEST_OFFER_TAG 1 +#define LIVEKIT_PB_SIGNAL_REQUEST_ANSWER_TAG 2 +#define LIVEKIT_PB_SIGNAL_REQUEST_TRICKLE_TAG 3 +#define LIVEKIT_PB_SIGNAL_REQUEST_ADD_TRACK_TAG 4 +#define LIVEKIT_PB_SIGNAL_REQUEST_MUTE_TAG 5 +#define LIVEKIT_PB_SIGNAL_REQUEST_SUBSCRIPTION_TAG 6 +#define LIVEKIT_PB_SIGNAL_REQUEST_TRACK_SETTING_TAG 7 +#define LIVEKIT_PB_SIGNAL_REQUEST_LEAVE_TAG 8 +#define LIVEKIT_PB_SIGNAL_REQUEST_SUBSCRIPTION_PERMISSION_TAG 11 +#define LIVEKIT_PB_SIGNAL_REQUEST_SYNC_STATE_TAG 12 +#define LIVEKIT_PB_SIGNAL_REQUEST_SIMULATE_TAG 13 +#define LIVEKIT_PB_SIGNAL_REQUEST_PING_TAG 14 +#define LIVEKIT_PB_SIGNAL_REQUEST_UPDATE_METADATA_TAG 15 +#define LIVEKIT_PB_SIGNAL_REQUEST_PING_REQ_TAG 16 +#define LIVEKIT_PB_SIGNAL_REQUEST_UPDATE_AUDIO_TRACK_TAG 17 +#define LIVEKIT_PB_SIGNAL_REQUEST_UPDATE_VIDEO_TRACK_TAG 18 +#define LIVEKIT_PB_PONG_LAST_PING_TIMESTAMP_TAG 1 +#define LIVEKIT_PB_PONG_TIMESTAMP_TAG 2 +#define LIVEKIT_PB_REGION_SETTINGS_REGIONS_TAG 1 +#define LIVEKIT_PB_REGION_INFO_REGION_TAG 1 +#define LIVEKIT_PB_REGION_INFO_URL_TAG 2 +#define LIVEKIT_PB_REGION_INFO_DISTANCE_TAG 3 +#define LIVEKIT_PB_SUBSCRIPTION_RESPONSE_TRACK_SID_TAG 1 +#define LIVEKIT_PB_SUBSCRIPTION_RESPONSE_ERR_TAG 2 +#define LIVEKIT_PB_REQUEST_RESPONSE_REQUEST_ID_TAG 1 +#define LIVEKIT_PB_REQUEST_RESPONSE_REASON_TAG 2 +#define LIVEKIT_PB_REQUEST_RESPONSE_MESSAGE_TAG 3 +#define LIVEKIT_PB_TRACK_SUBSCRIBED_TRACK_SID_TAG 1 +#define LIVEKIT_PB_SIGNAL_RESPONSE_JOIN_TAG 1 +#define LIVEKIT_PB_SIGNAL_RESPONSE_ANSWER_TAG 2 +#define LIVEKIT_PB_SIGNAL_RESPONSE_OFFER_TAG 3 +#define LIVEKIT_PB_SIGNAL_RESPONSE_TRICKLE_TAG 4 +#define LIVEKIT_PB_SIGNAL_RESPONSE_UPDATE_TAG 5 +#define LIVEKIT_PB_SIGNAL_RESPONSE_TRACK_PUBLISHED_TAG 6 +#define LIVEKIT_PB_SIGNAL_RESPONSE_LEAVE_TAG 8 +#define LIVEKIT_PB_SIGNAL_RESPONSE_MUTE_TAG 9 +#define LIVEKIT_PB_SIGNAL_RESPONSE_SPEAKERS_CHANGED_TAG 10 +#define LIVEKIT_PB_SIGNAL_RESPONSE_ROOM_UPDATE_TAG 11 +#define LIVEKIT_PB_SIGNAL_RESPONSE_CONNECTION_QUALITY_TAG 12 +#define LIVEKIT_PB_SIGNAL_RESPONSE_STREAM_STATE_UPDATE_TAG 13 +#define LIVEKIT_PB_SIGNAL_RESPONSE_SUBSCRIBED_QUALITY_UPDATE_TAG 14 +#define LIVEKIT_PB_SIGNAL_RESPONSE_SUBSCRIPTION_PERMISSION_UPDATE_TAG 15 +#define LIVEKIT_PB_SIGNAL_RESPONSE_REFRESH_TOKEN_TAG 16 +#define LIVEKIT_PB_SIGNAL_RESPONSE_TRACK_UNPUBLISHED_TAG 17 +#define LIVEKIT_PB_SIGNAL_RESPONSE_PONG_TAG 18 +#define LIVEKIT_PB_SIGNAL_RESPONSE_RECONNECT_TAG 19 +#define LIVEKIT_PB_SIGNAL_RESPONSE_PONG_RESP_TAG 20 +#define LIVEKIT_PB_SIGNAL_RESPONSE_SUBSCRIPTION_RESPONSE_TAG 21 +#define LIVEKIT_PB_SIGNAL_RESPONSE_REQUEST_RESPONSE_TAG 22 +#define LIVEKIT_PB_SIGNAL_RESPONSE_TRACK_SUBSCRIBED_TAG 23 +#define LIVEKIT_PB_SIGNAL_RESPONSE_ROOM_MOVED_TAG 24 + +/* Struct field encoding specification for nanopb */ +#define LIVEKIT_PB_SIGNAL_REQUEST_FIELDLIST(X, a) \ +X(a, STATIC, ONEOF, MESSAGE, (message,offer,message.offer), 1) \ +X(a, STATIC, ONEOF, MESSAGE, (message,answer,message.answer), 2) \ +X(a, STATIC, ONEOF, MESSAGE, (message,trickle,message.trickle), 3) \ +X(a, STATIC, ONEOF, MESSAGE, (message,add_track,message.add_track), 4) \ +X(a, STATIC, ONEOF, MESSAGE, (message,mute,message.mute), 5) \ +X(a, STATIC, ONEOF, MESSAGE, (message,subscription,message.subscription), 6) \ +X(a, STATIC, ONEOF, MESSAGE, (message,track_setting,message.track_setting), 7) \ +X(a, STATIC, ONEOF, MESSAGE, (message,leave,message.leave), 8) \ +X(a, STATIC, ONEOF, MESSAGE, (message,subscription_permission,message.subscription_permission), 11) \ +X(a, STATIC, ONEOF, MESSAGE, (message,sync_state,message.sync_state), 12) \ +X(a, STATIC, ONEOF, MESSAGE, (message,simulate,message.simulate), 13) \ +X(a, STATIC, ONEOF, INT64, (message,ping,message.ping), 14) \ +X(a, STATIC, ONEOF, MESSAGE, (message,update_metadata,message.update_metadata), 15) \ +X(a, STATIC, ONEOF, MESSAGE, (message,ping_req,message.ping_req), 16) \ +X(a, STATIC, ONEOF, MESSAGE, (message,update_audio_track,message.update_audio_track), 17) \ +X(a, STATIC, ONEOF, MESSAGE, (message,update_video_track,message.update_video_track), 18) +#define LIVEKIT_PB_SIGNAL_REQUEST_CALLBACK NULL +#define LIVEKIT_PB_SIGNAL_REQUEST_DEFAULT NULL +#define livekit_pb_signal_request_t_message_offer_MSGTYPE livekit_pb_session_description_t +#define livekit_pb_signal_request_t_message_answer_MSGTYPE livekit_pb_session_description_t +#define livekit_pb_signal_request_t_message_trickle_MSGTYPE livekit_pb_trickle_request_t +#define livekit_pb_signal_request_t_message_add_track_MSGTYPE livekit_pb_add_track_request_t +#define livekit_pb_signal_request_t_message_mute_MSGTYPE livekit_pb_mute_track_request_t +#define livekit_pb_signal_request_t_message_subscription_MSGTYPE livekit_pb_update_subscription_t +#define livekit_pb_signal_request_t_message_track_setting_MSGTYPE livekit_pb_update_track_settings_t +#define livekit_pb_signal_request_t_message_leave_MSGTYPE livekit_pb_leave_request_t +#define livekit_pb_signal_request_t_message_subscription_permission_MSGTYPE livekit_pb_subscription_permission_t +#define livekit_pb_signal_request_t_message_sync_state_MSGTYPE livekit_pb_sync_state_t +#define livekit_pb_signal_request_t_message_simulate_MSGTYPE livekit_pb_simulate_scenario_t +#define livekit_pb_signal_request_t_message_update_metadata_MSGTYPE livekit_pb_update_participant_metadata_t +#define livekit_pb_signal_request_t_message_ping_req_MSGTYPE livekit_pb_ping_t +#define livekit_pb_signal_request_t_message_update_audio_track_MSGTYPE livekit_pb_update_local_audio_track_t +#define livekit_pb_signal_request_t_message_update_video_track_MSGTYPE livekit_pb_update_local_video_track_t + +#define LIVEKIT_PB_SIGNAL_RESPONSE_FIELDLIST(X, a) \ +X(a, STATIC, ONEOF, MESSAGE, (message,join,message.join), 1) \ +X(a, STATIC, ONEOF, MESSAGE, (message,answer,message.answer), 2) \ +X(a, STATIC, ONEOF, MESSAGE, (message,offer,message.offer), 3) \ +X(a, STATIC, ONEOF, MESSAGE, (message,trickle,message.trickle), 4) \ +X(a, STATIC, ONEOF, MESSAGE, (message,update,message.update), 5) \ +X(a, STATIC, ONEOF, MESSAGE, (message,track_published,message.track_published), 6) \ +X(a, STATIC, ONEOF, MESSAGE, (message,leave,message.leave), 8) \ +X(a, STATIC, ONEOF, MESSAGE, (message,mute,message.mute), 9) \ +X(a, STATIC, ONEOF, MESSAGE, (message,speakers_changed,message.speakers_changed), 10) \ +X(a, STATIC, ONEOF, MESSAGE, (message,room_update,message.room_update), 11) \ +X(a, STATIC, ONEOF, MESSAGE, (message,connection_quality,message.connection_quality), 12) \ +X(a, STATIC, ONEOF, MESSAGE, (message,stream_state_update,message.stream_state_update), 13) \ +X(a, STATIC, ONEOF, MESSAGE, (message,subscribed_quality_update,message.subscribed_quality_update), 14) \ +X(a, STATIC, ONEOF, MESSAGE, (message,subscription_permission_update,message.subscription_permission_update), 15) \ +X(a, CALLBACK, ONEOF, STRING, (message,refresh_token,message.refresh_token), 16) \ +X(a, STATIC, ONEOF, MESSAGE, (message,track_unpublished,message.track_unpublished), 17) \ +X(a, STATIC, ONEOF, INT64, (message,pong,message.pong), 18) \ +X(a, STATIC, ONEOF, MESSAGE, (message,reconnect,message.reconnect), 19) \ +X(a, STATIC, ONEOF, MESSAGE, (message,pong_resp,message.pong_resp), 20) \ +X(a, STATIC, ONEOF, MESSAGE, (message,subscription_response,message.subscription_response), 21) \ +X(a, STATIC, ONEOF, MESSAGE, (message,request_response,message.request_response), 22) \ +X(a, STATIC, ONEOF, MESSAGE, (message,track_subscribed,message.track_subscribed), 23) \ +X(a, STATIC, ONEOF, MESSAGE, (message,room_moved,message.room_moved), 24) +#define LIVEKIT_PB_SIGNAL_RESPONSE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SIGNAL_RESPONSE_DEFAULT NULL +#define livekit_pb_signal_response_t_message_join_MSGTYPE livekit_pb_join_response_t +#define livekit_pb_signal_response_t_message_answer_MSGTYPE livekit_pb_session_description_t +#define livekit_pb_signal_response_t_message_offer_MSGTYPE livekit_pb_session_description_t +#define livekit_pb_signal_response_t_message_trickle_MSGTYPE livekit_pb_trickle_request_t +#define livekit_pb_signal_response_t_message_update_MSGTYPE livekit_pb_participant_update_t +#define livekit_pb_signal_response_t_message_track_published_MSGTYPE livekit_pb_track_published_response_t +#define livekit_pb_signal_response_t_message_leave_MSGTYPE livekit_pb_leave_request_t +#define livekit_pb_signal_response_t_message_mute_MSGTYPE livekit_pb_mute_track_request_t +#define livekit_pb_signal_response_t_message_speakers_changed_MSGTYPE livekit_pb_speakers_changed_t +#define livekit_pb_signal_response_t_message_room_update_MSGTYPE livekit_pb_room_update_t +#define livekit_pb_signal_response_t_message_connection_quality_MSGTYPE livekit_pb_connection_quality_update_t +#define livekit_pb_signal_response_t_message_stream_state_update_MSGTYPE livekit_pb_stream_state_update_t +#define livekit_pb_signal_response_t_message_subscribed_quality_update_MSGTYPE livekit_pb_subscribed_quality_update_t +#define livekit_pb_signal_response_t_message_subscription_permission_update_MSGTYPE livekit_pb_subscription_permission_update_t +#define livekit_pb_signal_response_t_message_track_unpublished_MSGTYPE livekit_pb_track_unpublished_response_t +#define livekit_pb_signal_response_t_message_reconnect_MSGTYPE livekit_pb_reconnect_response_t +#define livekit_pb_signal_response_t_message_pong_resp_MSGTYPE livekit_pb_pong_t +#define livekit_pb_signal_response_t_message_subscription_response_MSGTYPE livekit_pb_subscription_response_t +#define livekit_pb_signal_response_t_message_request_response_MSGTYPE livekit_pb_request_response_t +#define livekit_pb_signal_response_t_message_track_subscribed_MSGTYPE livekit_pb_track_subscribed_t +#define livekit_pb_signal_response_t_message_room_moved_MSGTYPE livekit_pb_room_moved_response_t + +#define LIVEKIT_PB_SIMULCAST_CODEC_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, codec, 1) \ +X(a, CALLBACK, SINGULAR, STRING, cid, 2) +#define LIVEKIT_PB_SIMULCAST_CODEC_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SIMULCAST_CODEC_DEFAULT NULL + +#define LIVEKIT_PB_ADD_TRACK_REQUEST_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, cid, 1) \ +X(a, STATIC, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, UENUM, type, 3) \ +X(a, STATIC, SINGULAR, BOOL, muted, 6) \ +X(a, STATIC, SINGULAR, UENUM, source, 8) \ +X(a, STATIC, REPEATED, MESSAGE, layers, 9) \ +X(a, STATIC, REPEATED, UENUM, audio_features, 17) +#define LIVEKIT_PB_ADD_TRACK_REQUEST_CALLBACK NULL +#define LIVEKIT_PB_ADD_TRACK_REQUEST_DEFAULT NULL +#define livekit_pb_add_track_request_t_layers_MSGTYPE livekit_pb_video_layer_t + +#define LIVEKIT_PB_TRICKLE_REQUEST_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, STRING, candidate_init, 1) \ +X(a, STATIC, SINGULAR, UENUM, target, 2) \ +X(a, STATIC, SINGULAR, BOOL, final, 3) +#define LIVEKIT_PB_TRICKLE_REQUEST_CALLBACK NULL +#define LIVEKIT_PB_TRICKLE_REQUEST_DEFAULT NULL + +#define LIVEKIT_PB_MUTE_TRACK_REQUEST_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, sid, 1) \ +X(a, STATIC, SINGULAR, BOOL, muted, 2) +#define LIVEKIT_PB_MUTE_TRACK_REQUEST_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_MUTE_TRACK_REQUEST_DEFAULT NULL + +#define LIVEKIT_PB_JOIN_RESPONSE_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, room, 1) \ +X(a, STATIC, REQUIRED, MESSAGE, participant, 2) \ +X(a, POINTER, REPEATED, MESSAGE, other_participants, 3) \ +X(a, STATIC, REPEATED, MESSAGE, ice_servers, 5) \ +X(a, STATIC, SINGULAR, BOOL, subscriber_primary, 6) \ +X(a, STATIC, OPTIONAL, MESSAGE, client_configuration, 8) \ +X(a, STATIC, SINGULAR, INT32, ping_timeout, 10) \ +X(a, STATIC, SINGULAR, INT32, ping_interval, 11) +#define LIVEKIT_PB_JOIN_RESPONSE_CALLBACK NULL +#define LIVEKIT_PB_JOIN_RESPONSE_DEFAULT NULL +#define livekit_pb_join_response_t_room_MSGTYPE livekit_pb_room_t +#define livekit_pb_join_response_t_participant_MSGTYPE livekit_pb_participant_info_t +#define livekit_pb_join_response_t_other_participants_MSGTYPE livekit_pb_participant_info_t +#define livekit_pb_join_response_t_ice_servers_MSGTYPE livekit_pb_ice_server_t +#define livekit_pb_join_response_t_client_configuration_MSGTYPE livekit_pb_client_configuration_t + +#define LIVEKIT_PB_RECONNECT_RESPONSE_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, ice_servers, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, client_configuration, 2) \ +X(a, STATIC, OPTIONAL, MESSAGE, server_info, 3) \ +X(a, STATIC, SINGULAR, UINT32, last_message_seq, 4) +#define LIVEKIT_PB_RECONNECT_RESPONSE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_RECONNECT_RESPONSE_DEFAULT NULL +#define livekit_pb_reconnect_response_t_ice_servers_MSGTYPE livekit_pb_ice_server_t +#define livekit_pb_reconnect_response_t_client_configuration_MSGTYPE livekit_pb_client_configuration_t +#define livekit_pb_reconnect_response_t_server_info_MSGTYPE livekit_pb_server_info_t + +#define LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, STRING, cid, 1) \ +X(a, STATIC, REQUIRED, MESSAGE, track, 2) +#define LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE_CALLBACK NULL +#define LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE_DEFAULT NULL +#define livekit_pb_track_published_response_t_track_MSGTYPE livekit_pb_track_info_t + +#define LIVEKIT_PB_TRACK_UNPUBLISHED_RESPONSE_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, track_sid, 1) +#define LIVEKIT_PB_TRACK_UNPUBLISHED_RESPONSE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_TRACK_UNPUBLISHED_RESPONSE_DEFAULT NULL + +#define LIVEKIT_PB_SESSION_DESCRIPTION_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, type, 1) \ +X(a, POINTER, SINGULAR, STRING, sdp, 2) \ +X(a, STATIC, SINGULAR, UINT32, id, 3) +#define LIVEKIT_PB_SESSION_DESCRIPTION_CALLBACK NULL +#define LIVEKIT_PB_SESSION_DESCRIPTION_DEFAULT NULL + +#define LIVEKIT_PB_PARTICIPANT_UPDATE_FIELDLIST(X, a) \ +X(a, POINTER, REPEATED, MESSAGE, participants, 1) +#define LIVEKIT_PB_PARTICIPANT_UPDATE_CALLBACK NULL +#define LIVEKIT_PB_PARTICIPANT_UPDATE_DEFAULT NULL +#define livekit_pb_participant_update_t_participants_MSGTYPE livekit_pb_participant_info_t + +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_FIELDLIST(X, a) \ +X(a, POINTER, REPEATED, STRING, track_sids, 1) \ +X(a, STATIC, SINGULAR, BOOL, subscribe, 2) +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_CALLBACK NULL +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_DEFAULT NULL + +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, STRING, track_sids, 1) \ +X(a, STATIC, SINGULAR, BOOL, disabled, 3) \ +X(a, STATIC, SINGULAR, UENUM, quality, 4) \ +X(a, STATIC, SINGULAR, UINT32, width, 5) \ +X(a, STATIC, SINGULAR, UINT32, height, 6) \ +X(a, STATIC, SINGULAR, UINT32, fps, 7) \ +X(a, STATIC, SINGULAR, UINT32, priority, 8) +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_DEFAULT NULL + +#define LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, track_sid, 1) \ +X(a, CALLBACK, REPEATED, UENUM, features, 2) +#define LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK_DEFAULT NULL + +#define LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, track_sid, 1) \ +X(a, STATIC, SINGULAR, UINT32, width, 2) \ +X(a, STATIC, SINGULAR, UINT32, height, 3) +#define LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_DEFAULT NULL + +#define LIVEKIT_PB_LEAVE_REQUEST_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, reason, 2) \ +X(a, STATIC, SINGULAR, UENUM, action, 3) +#define LIVEKIT_PB_LEAVE_REQUEST_CALLBACK NULL +#define LIVEKIT_PB_LEAVE_REQUEST_DEFAULT NULL + +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, metadata, 1) \ +X(a, CALLBACK, SINGULAR, STRING, name, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, attributes, 3) \ +X(a, STATIC, SINGULAR, UINT32, request_id, 4) +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_DEFAULT NULL +#define livekit_pb_update_participant_metadata_t_attributes_MSGTYPE livekit_pb_update_participant_metadata_attributes_entry_t + +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, key, 1) \ +X(a, CALLBACK, SINGULAR, STRING, value, 2) +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_DEFAULT NULL + +#define LIVEKIT_PB_ICE_SERVER_FIELDLIST(X, a) \ +X(a, POINTER, REPEATED, STRING, urls, 1) \ +X(a, POINTER, SINGULAR, STRING, username, 2) \ +X(a, POINTER, SINGULAR, STRING, credential, 3) +#define LIVEKIT_PB_ICE_SERVER_CALLBACK NULL +#define LIVEKIT_PB_ICE_SERVER_DEFAULT NULL + +#define LIVEKIT_PB_SPEAKERS_CHANGED_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, speakers, 1) +#define LIVEKIT_PB_SPEAKERS_CHANGED_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SPEAKERS_CHANGED_DEFAULT NULL +#define livekit_pb_speakers_changed_t_speakers_MSGTYPE livekit_pb_speaker_info_t + +#define LIVEKIT_PB_ROOM_UPDATE_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, room, 1) +#define LIVEKIT_PB_ROOM_UPDATE_CALLBACK NULL +#define LIVEKIT_PB_ROOM_UPDATE_DEFAULT NULL +#define livekit_pb_room_update_t_room_MSGTYPE livekit_pb_room_t + +#define LIVEKIT_PB_CONNECTION_QUALITY_INFO_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, participant_sid, 1) \ +X(a, STATIC, SINGULAR, UENUM, quality, 2) \ +X(a, STATIC, SINGULAR, FLOAT, score, 3) +#define LIVEKIT_PB_CONNECTION_QUALITY_INFO_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_CONNECTION_QUALITY_INFO_DEFAULT NULL + +#define LIVEKIT_PB_CONNECTION_QUALITY_UPDATE_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, updates, 1) +#define LIVEKIT_PB_CONNECTION_QUALITY_UPDATE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_CONNECTION_QUALITY_UPDATE_DEFAULT NULL +#define livekit_pb_connection_quality_update_t_updates_MSGTYPE livekit_pb_connection_quality_info_t + +#define LIVEKIT_PB_STREAM_STATE_INFO_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, participant_sid, 1) \ +X(a, CALLBACK, SINGULAR, STRING, track_sid, 2) \ +X(a, STATIC, SINGULAR, UENUM, state, 3) +#define LIVEKIT_PB_STREAM_STATE_INFO_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_STREAM_STATE_INFO_DEFAULT NULL + +#define LIVEKIT_PB_STREAM_STATE_UPDATE_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, stream_states, 1) +#define LIVEKIT_PB_STREAM_STATE_UPDATE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_STREAM_STATE_UPDATE_DEFAULT NULL +#define livekit_pb_stream_state_update_t_stream_states_MSGTYPE livekit_pb_stream_state_info_t + +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, quality, 1) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 2) +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_CALLBACK NULL +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_DEFAULT NULL + +#define LIVEKIT_PB_SUBSCRIBED_CODEC_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, codec, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, qualities, 2) +#define LIVEKIT_PB_SUBSCRIBED_CODEC_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SUBSCRIBED_CODEC_DEFAULT NULL +#define livekit_pb_subscribed_codec_t_qualities_MSGTYPE livekit_pb_subscribed_quality_t + +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, track_sid, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, subscribed_codecs, 3) +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE_DEFAULT NULL +#define livekit_pb_subscribed_quality_update_t_subscribed_codecs_MSGTYPE livekit_pb_subscribed_codec_t + +#define LIVEKIT_PB_TRACK_PERMISSION_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, participant_sid, 1) \ +X(a, STATIC, SINGULAR, BOOL, all_tracks, 2) \ +X(a, CALLBACK, REPEATED, STRING, track_sids, 3) \ +X(a, CALLBACK, SINGULAR, STRING, participant_identity, 4) +#define LIVEKIT_PB_TRACK_PERMISSION_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_TRACK_PERMISSION_DEFAULT NULL + +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, all_participants, 1) \ +X(a, CALLBACK, REPEATED, MESSAGE, track_permissions, 2) +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_DEFAULT NULL +#define livekit_pb_subscription_permission_t_track_permissions_MSGTYPE livekit_pb_track_permission_t + +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, participant_sid, 1) \ +X(a, CALLBACK, SINGULAR, STRING, track_sid, 2) \ +X(a, STATIC, SINGULAR, BOOL, allowed, 3) +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_DEFAULT NULL + +#define LIVEKIT_PB_ROOM_MOVED_RESPONSE_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, room, 1) \ +X(a, CALLBACK, SINGULAR, STRING, token, 2) \ +X(a, STATIC, OPTIONAL, MESSAGE, participant, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, other_participants, 4) +#define LIVEKIT_PB_ROOM_MOVED_RESPONSE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_ROOM_MOVED_RESPONSE_DEFAULT NULL +#define livekit_pb_room_moved_response_t_room_MSGTYPE livekit_pb_room_t +#define livekit_pb_room_moved_response_t_participant_MSGTYPE livekit_pb_participant_info_t +#define livekit_pb_room_moved_response_t_other_participants_MSGTYPE livekit_pb_participant_info_t + +#define LIVEKIT_PB_SYNC_STATE_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, answer, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, subscription, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, publish_tracks, 3) \ +X(a, CALLBACK, REPEATED, MESSAGE, data_channels, 4) \ +X(a, STATIC, OPTIONAL, MESSAGE, offer, 5) \ +X(a, CALLBACK, REPEATED, STRING, track_sids_disabled, 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, datachannel_receive_states, 7) +#define LIVEKIT_PB_SYNC_STATE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SYNC_STATE_DEFAULT NULL +#define livekit_pb_sync_state_t_answer_MSGTYPE livekit_pb_session_description_t +#define livekit_pb_sync_state_t_subscription_MSGTYPE livekit_pb_update_subscription_t +#define livekit_pb_sync_state_t_publish_tracks_MSGTYPE livekit_pb_track_published_response_t +#define livekit_pb_sync_state_t_data_channels_MSGTYPE livekit_pb_data_channel_info_t +#define livekit_pb_sync_state_t_offer_MSGTYPE livekit_pb_session_description_t +#define livekit_pb_sync_state_t_datachannel_receive_states_MSGTYPE livekit_pb_data_channel_receive_state_t + +#define LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, publisher_sid, 1) \ +X(a, STATIC, SINGULAR, UINT32, last_seq, 2) +#define LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE_DEFAULT NULL + +#define LIVEKIT_PB_DATA_CHANNEL_INFO_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, label, 1) \ +X(a, STATIC, SINGULAR, UINT32, id, 2) \ +X(a, STATIC, SINGULAR, UENUM, target, 3) +#define LIVEKIT_PB_DATA_CHANNEL_INFO_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_DATA_CHANNEL_INFO_DEFAULT NULL + +#define LIVEKIT_PB_SIMULATE_SCENARIO_FIELDLIST(X, a) \ +X(a, STATIC, ONEOF, INT32, (scenario,speaker_update,scenario.speaker_update), 1) \ +X(a, STATIC, ONEOF, BOOL, (scenario,node_failure,scenario.node_failure), 2) \ +X(a, STATIC, ONEOF, BOOL, (scenario,migration,scenario.migration), 3) \ +X(a, STATIC, ONEOF, BOOL, (scenario,server_leave,scenario.server_leave), 4) \ +X(a, STATIC, ONEOF, UENUM, (scenario,switch_candidate_protocol,scenario.switch_candidate_protocol), 5) \ +X(a, STATIC, ONEOF, INT64, (scenario,subscriber_bandwidth,scenario.subscriber_bandwidth), 6) \ +X(a, STATIC, ONEOF, BOOL, (scenario,disconnect_signal_on_resume,scenario.disconnect_signal_on_resume), 7) \ +X(a, STATIC, ONEOF, BOOL, (scenario,disconnect_signal_on_resume_no_messages,scenario.disconnect_signal_on_resume_no_messages), 8) \ +X(a, STATIC, ONEOF, BOOL, (scenario,leave_request_full_reconnect,scenario.leave_request_full_reconnect), 9) +#define LIVEKIT_PB_SIMULATE_SCENARIO_CALLBACK NULL +#define LIVEKIT_PB_SIMULATE_SCENARIO_DEFAULT NULL + +#define LIVEKIT_PB_PING_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT64, timestamp, 1) \ +X(a, STATIC, SINGULAR, INT64, rtt, 2) +#define LIVEKIT_PB_PING_CALLBACK NULL +#define LIVEKIT_PB_PING_DEFAULT NULL + +#define LIVEKIT_PB_PONG_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT64, last_ping_timestamp, 1) \ +X(a, STATIC, SINGULAR, INT64, timestamp, 2) +#define LIVEKIT_PB_PONG_CALLBACK NULL +#define LIVEKIT_PB_PONG_DEFAULT NULL + +#define LIVEKIT_PB_REGION_SETTINGS_FIELDLIST(X, a) \ +X(a, CALLBACK, REPEATED, MESSAGE, regions, 1) +#define LIVEKIT_PB_REGION_SETTINGS_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_REGION_SETTINGS_DEFAULT NULL +#define livekit_pb_region_settings_t_regions_MSGTYPE livekit_pb_region_info_t + +#define LIVEKIT_PB_REGION_INFO_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, region, 1) \ +X(a, CALLBACK, SINGULAR, STRING, url, 2) \ +X(a, STATIC, SINGULAR, INT64, distance, 3) +#define LIVEKIT_PB_REGION_INFO_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_REGION_INFO_DEFAULT NULL + +#define LIVEKIT_PB_SUBSCRIPTION_RESPONSE_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, track_sid, 1) \ +X(a, STATIC, SINGULAR, UENUM, err, 2) +#define LIVEKIT_PB_SUBSCRIPTION_RESPONSE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_SUBSCRIPTION_RESPONSE_DEFAULT NULL + +#define LIVEKIT_PB_REQUEST_RESPONSE_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, request_id, 1) \ +X(a, STATIC, SINGULAR, UENUM, reason, 2) \ +X(a, CALLBACK, SINGULAR, STRING, message, 3) +#define LIVEKIT_PB_REQUEST_RESPONSE_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_REQUEST_RESPONSE_DEFAULT NULL + +#define LIVEKIT_PB_TRACK_SUBSCRIBED_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, track_sid, 1) +#define LIVEKIT_PB_TRACK_SUBSCRIBED_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_TRACK_SUBSCRIBED_DEFAULT NULL + +extern const pb_msgdesc_t livekit_pb_signal_request_t_msg; +extern const pb_msgdesc_t livekit_pb_signal_response_t_msg; +extern const pb_msgdesc_t livekit_pb_simulcast_codec_t_msg; +extern const pb_msgdesc_t livekit_pb_add_track_request_t_msg; +extern const pb_msgdesc_t livekit_pb_trickle_request_t_msg; +extern const pb_msgdesc_t livekit_pb_mute_track_request_t_msg; +extern const pb_msgdesc_t livekit_pb_join_response_t_msg; +extern const pb_msgdesc_t livekit_pb_reconnect_response_t_msg; +extern const pb_msgdesc_t livekit_pb_track_published_response_t_msg; +extern const pb_msgdesc_t livekit_pb_track_unpublished_response_t_msg; +extern const pb_msgdesc_t livekit_pb_session_description_t_msg; +extern const pb_msgdesc_t livekit_pb_participant_update_t_msg; +extern const pb_msgdesc_t livekit_pb_update_subscription_t_msg; +extern const pb_msgdesc_t livekit_pb_update_track_settings_t_msg; +extern const pb_msgdesc_t livekit_pb_update_local_audio_track_t_msg; +extern const pb_msgdesc_t livekit_pb_update_local_video_track_t_msg; +extern const pb_msgdesc_t livekit_pb_leave_request_t_msg; +extern const pb_msgdesc_t livekit_pb_update_participant_metadata_t_msg; +extern const pb_msgdesc_t livekit_pb_update_participant_metadata_attributes_entry_t_msg; +extern const pb_msgdesc_t livekit_pb_ice_server_t_msg; +extern const pb_msgdesc_t livekit_pb_speakers_changed_t_msg; +extern const pb_msgdesc_t livekit_pb_room_update_t_msg; +extern const pb_msgdesc_t livekit_pb_connection_quality_info_t_msg; +extern const pb_msgdesc_t livekit_pb_connection_quality_update_t_msg; +extern const pb_msgdesc_t livekit_pb_stream_state_info_t_msg; +extern const pb_msgdesc_t livekit_pb_stream_state_update_t_msg; +extern const pb_msgdesc_t livekit_pb_subscribed_quality_t_msg; +extern const pb_msgdesc_t livekit_pb_subscribed_codec_t_msg; +extern const pb_msgdesc_t livekit_pb_subscribed_quality_update_t_msg; +extern const pb_msgdesc_t livekit_pb_track_permission_t_msg; +extern const pb_msgdesc_t livekit_pb_subscription_permission_t_msg; +extern const pb_msgdesc_t livekit_pb_subscription_permission_update_t_msg; +extern const pb_msgdesc_t livekit_pb_room_moved_response_t_msg; +extern const pb_msgdesc_t livekit_pb_sync_state_t_msg; +extern const pb_msgdesc_t livekit_pb_data_channel_receive_state_t_msg; +extern const pb_msgdesc_t livekit_pb_data_channel_info_t_msg; +extern const pb_msgdesc_t livekit_pb_simulate_scenario_t_msg; +extern const pb_msgdesc_t livekit_pb_ping_t_msg; +extern const pb_msgdesc_t livekit_pb_pong_t_msg; +extern const pb_msgdesc_t livekit_pb_region_settings_t_msg; +extern const pb_msgdesc_t livekit_pb_region_info_t_msg; +extern const pb_msgdesc_t livekit_pb_subscription_response_t_msg; +extern const pb_msgdesc_t livekit_pb_request_response_t_msg; +extern const pb_msgdesc_t livekit_pb_track_subscribed_t_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define LIVEKIT_PB_SIGNAL_REQUEST_FIELDS &livekit_pb_signal_request_t_msg +#define LIVEKIT_PB_SIGNAL_RESPONSE_FIELDS &livekit_pb_signal_response_t_msg +#define LIVEKIT_PB_SIMULCAST_CODEC_FIELDS &livekit_pb_simulcast_codec_t_msg +#define LIVEKIT_PB_ADD_TRACK_REQUEST_FIELDS &livekit_pb_add_track_request_t_msg +#define LIVEKIT_PB_TRICKLE_REQUEST_FIELDS &livekit_pb_trickle_request_t_msg +#define LIVEKIT_PB_MUTE_TRACK_REQUEST_FIELDS &livekit_pb_mute_track_request_t_msg +#define LIVEKIT_PB_JOIN_RESPONSE_FIELDS &livekit_pb_join_response_t_msg +#define LIVEKIT_PB_RECONNECT_RESPONSE_FIELDS &livekit_pb_reconnect_response_t_msg +#define LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE_FIELDS &livekit_pb_track_published_response_t_msg +#define LIVEKIT_PB_TRACK_UNPUBLISHED_RESPONSE_FIELDS &livekit_pb_track_unpublished_response_t_msg +#define LIVEKIT_PB_SESSION_DESCRIPTION_FIELDS &livekit_pb_session_description_t_msg +#define LIVEKIT_PB_PARTICIPANT_UPDATE_FIELDS &livekit_pb_participant_update_t_msg +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_FIELDS &livekit_pb_update_subscription_t_msg +#define LIVEKIT_PB_UPDATE_TRACK_SETTINGS_FIELDS &livekit_pb_update_track_settings_t_msg +#define LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK_FIELDS &livekit_pb_update_local_audio_track_t_msg +#define LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_FIELDS &livekit_pb_update_local_video_track_t_msg +#define LIVEKIT_PB_LEAVE_REQUEST_FIELDS &livekit_pb_leave_request_t_msg +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_FIELDS &livekit_pb_update_participant_metadata_t_msg +#define LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_FIELDS &livekit_pb_update_participant_metadata_attributes_entry_t_msg +#define LIVEKIT_PB_ICE_SERVER_FIELDS &livekit_pb_ice_server_t_msg +#define LIVEKIT_PB_SPEAKERS_CHANGED_FIELDS &livekit_pb_speakers_changed_t_msg +#define LIVEKIT_PB_ROOM_UPDATE_FIELDS &livekit_pb_room_update_t_msg +#define LIVEKIT_PB_CONNECTION_QUALITY_INFO_FIELDS &livekit_pb_connection_quality_info_t_msg +#define LIVEKIT_PB_CONNECTION_QUALITY_UPDATE_FIELDS &livekit_pb_connection_quality_update_t_msg +#define LIVEKIT_PB_STREAM_STATE_INFO_FIELDS &livekit_pb_stream_state_info_t_msg +#define LIVEKIT_PB_STREAM_STATE_UPDATE_FIELDS &livekit_pb_stream_state_update_t_msg +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_FIELDS &livekit_pb_subscribed_quality_t_msg +#define LIVEKIT_PB_SUBSCRIBED_CODEC_FIELDS &livekit_pb_subscribed_codec_t_msg +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE_FIELDS &livekit_pb_subscribed_quality_update_t_msg +#define LIVEKIT_PB_TRACK_PERMISSION_FIELDS &livekit_pb_track_permission_t_msg +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_FIELDS &livekit_pb_subscription_permission_t_msg +#define LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_FIELDS &livekit_pb_subscription_permission_update_t_msg +#define LIVEKIT_PB_ROOM_MOVED_RESPONSE_FIELDS &livekit_pb_room_moved_response_t_msg +#define LIVEKIT_PB_SYNC_STATE_FIELDS &livekit_pb_sync_state_t_msg +#define LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE_FIELDS &livekit_pb_data_channel_receive_state_t_msg +#define LIVEKIT_PB_DATA_CHANNEL_INFO_FIELDS &livekit_pb_data_channel_info_t_msg +#define LIVEKIT_PB_SIMULATE_SCENARIO_FIELDS &livekit_pb_simulate_scenario_t_msg +#define LIVEKIT_PB_PING_FIELDS &livekit_pb_ping_t_msg +#define LIVEKIT_PB_PONG_FIELDS &livekit_pb_pong_t_msg +#define LIVEKIT_PB_REGION_SETTINGS_FIELDS &livekit_pb_region_settings_t_msg +#define LIVEKIT_PB_REGION_INFO_FIELDS &livekit_pb_region_info_t_msg +#define LIVEKIT_PB_SUBSCRIPTION_RESPONSE_FIELDS &livekit_pb_subscription_response_t_msg +#define LIVEKIT_PB_REQUEST_RESPONSE_FIELDS &livekit_pb_request_response_t_msg +#define LIVEKIT_PB_TRACK_SUBSCRIBED_FIELDS &livekit_pb_track_subscribed_t_msg + +/* Maximum encoded size of messages (where known) */ +/* livekit_pb_SignalRequest_size depends on runtime parameters */ +/* livekit_pb_SignalResponse_size depends on runtime parameters */ +/* livekit_pb_SimulcastCodec_size depends on runtime parameters */ +/* livekit_pb_TrickleRequest_size depends on runtime parameters */ +/* livekit_pb_MuteTrackRequest_size depends on runtime parameters */ +/* livekit_pb_JoinResponse_size depends on runtime parameters */ +/* livekit_pb_ReconnectResponse_size depends on runtime parameters */ +/* livekit_pb_TrackPublishedResponse_size depends on runtime parameters */ +/* livekit_pb_TrackUnpublishedResponse_size depends on runtime parameters */ +/* livekit_pb_SessionDescription_size depends on runtime parameters */ +/* livekit_pb_ParticipantUpdate_size depends on runtime parameters */ +/* livekit_pb_UpdateSubscription_size depends on runtime parameters */ +/* livekit_pb_UpdateTrackSettings_size depends on runtime parameters */ +/* livekit_pb_UpdateLocalAudioTrack_size depends on runtime parameters */ +/* livekit_pb_UpdateLocalVideoTrack_size depends on runtime parameters */ +/* livekit_pb_UpdateParticipantMetadata_size depends on runtime parameters */ +/* livekit_pb_UpdateParticipantMetadata_AttributesEntry_size depends on runtime parameters */ +/* livekit_pb_ICEServer_size depends on runtime parameters */ +/* livekit_pb_SpeakersChanged_size depends on runtime parameters */ +/* livekit_pb_ConnectionQualityInfo_size depends on runtime parameters */ +/* livekit_pb_ConnectionQualityUpdate_size depends on runtime parameters */ +/* livekit_pb_StreamStateInfo_size depends on runtime parameters */ +/* livekit_pb_StreamStateUpdate_size depends on runtime parameters */ +/* livekit_pb_SubscribedCodec_size depends on runtime parameters */ +/* livekit_pb_SubscribedQualityUpdate_size depends on runtime parameters */ +/* livekit_pb_TrackPermission_size depends on runtime parameters */ +/* livekit_pb_SubscriptionPermission_size depends on runtime parameters */ +/* livekit_pb_SubscriptionPermissionUpdate_size depends on runtime parameters */ +/* livekit_pb_RoomMovedResponse_size depends on runtime parameters */ +/* livekit_pb_SyncState_size depends on runtime parameters */ +/* livekit_pb_DataChannelReceiveState_size depends on runtime parameters */ +/* livekit_pb_DataChannelInfo_size depends on runtime parameters */ +/* livekit_pb_RegionSettings_size depends on runtime parameters */ +/* livekit_pb_RegionInfo_size depends on runtime parameters */ +/* livekit_pb_SubscriptionResponse_size depends on runtime parameters */ +/* livekit_pb_RequestResponse_size depends on runtime parameters */ +/* livekit_pb_TrackSubscribed_size depends on runtime parameters */ +#define LIVEKIT_LIVEKIT_RTC_PB_H_MAX_SIZE LIVEKIT_PB_ADD_TRACK_REQUEST_SIZE +#define LIVEKIT_PB_ADD_TRACK_REQUEST_SIZE 81 +#define LIVEKIT_PB_LEAVE_REQUEST_SIZE 4 +#define LIVEKIT_PB_PING_SIZE 22 +#define LIVEKIT_PB_PONG_SIZE 22 +#define LIVEKIT_PB_SIMULATE_SCENARIO_SIZE 11 +#define LIVEKIT_PB_SUBSCRIBED_QUALITY_SIZE 4 +#if defined(livekit_pb_Room_size) +#define LIVEKIT_PB_ROOM_UPDATE_SIZE (6 + livekit_pb_Room_size) +#endif + +/* Mapping from canonical names (mangle_names or overridden package name) */ +#define livekit_SignalTarget livekit_pb_SignalTarget +#define livekit_StreamState livekit_pb_StreamState +#define livekit_CandidateProtocol livekit_pb_CandidateProtocol +#define livekit_SignalRequest livekit_pb_SignalRequest +#define livekit_SignalResponse livekit_pb_SignalResponse +#define livekit_SimulcastCodec livekit_pb_SimulcastCodec +#define livekit_AddTrackRequest livekit_pb_AddTrackRequest +#define livekit_TrickleRequest livekit_pb_TrickleRequest +#define livekit_MuteTrackRequest livekit_pb_MuteTrackRequest +#define livekit_JoinResponse livekit_pb_JoinResponse +#define livekit_ReconnectResponse livekit_pb_ReconnectResponse +#define livekit_TrackPublishedResponse livekit_pb_TrackPublishedResponse +#define livekit_TrackUnpublishedResponse livekit_pb_TrackUnpublishedResponse +#define livekit_SessionDescription livekit_pb_SessionDescription +#define livekit_ParticipantUpdate livekit_pb_ParticipantUpdate +#define livekit_UpdateSubscription livekit_pb_UpdateSubscription +#define livekit_UpdateTrackSettings livekit_pb_UpdateTrackSettings +#define livekit_UpdateLocalAudioTrack livekit_pb_UpdateLocalAudioTrack +#define livekit_UpdateLocalVideoTrack livekit_pb_UpdateLocalVideoTrack +#define livekit_LeaveRequest livekit_pb_LeaveRequest +#define livekit_LeaveRequest_Action livekit_pb_LeaveRequest_Action +#define livekit_UpdateVideoLayers livekit_pb_UpdateVideoLayers +#define livekit_UpdateParticipantMetadata livekit_pb_UpdateParticipantMetadata +#define livekit_UpdateParticipantMetadata_AttributesEntry livekit_pb_UpdateParticipantMetadata_AttributesEntry +#define livekit_ICEServer livekit_pb_ICEServer +#define livekit_SpeakersChanged livekit_pb_SpeakersChanged +#define livekit_RoomUpdate livekit_pb_RoomUpdate +#define livekit_ConnectionQualityInfo livekit_pb_ConnectionQualityInfo +#define livekit_ConnectionQualityUpdate livekit_pb_ConnectionQualityUpdate +#define livekit_StreamStateInfo livekit_pb_StreamStateInfo +#define livekit_StreamStateUpdate livekit_pb_StreamStateUpdate +#define livekit_SubscribedQuality livekit_pb_SubscribedQuality +#define livekit_SubscribedCodec livekit_pb_SubscribedCodec +#define livekit_SubscribedQualityUpdate livekit_pb_SubscribedQualityUpdate +#define livekit_TrackPermission livekit_pb_TrackPermission +#define livekit_SubscriptionPermission livekit_pb_SubscriptionPermission +#define livekit_SubscriptionPermissionUpdate livekit_pb_SubscriptionPermissionUpdate +#define livekit_RoomMovedResponse livekit_pb_RoomMovedResponse +#define livekit_SyncState livekit_pb_SyncState +#define livekit_DataChannelReceiveState livekit_pb_DataChannelReceiveState +#define livekit_DataChannelInfo livekit_pb_DataChannelInfo +#define livekit_SimulateScenario livekit_pb_SimulateScenario +#define livekit_Ping livekit_pb_Ping +#define livekit_Pong livekit_pb_Pong +#define livekit_RegionSettings livekit_pb_RegionSettings +#define livekit_RegionInfo livekit_pb_RegionInfo +#define livekit_SubscriptionResponse livekit_pb_SubscriptionResponse +#define livekit_RequestResponse livekit_pb_RequestResponse +#define livekit_RequestResponse_Reason livekit_pb_RequestResponse_Reason +#define livekit_TrackSubscribed livekit_pb_TrackSubscribed +#define _LIVEKIT_SIGNAL_TARGET_MIN _LIVEKIT_PB_SIGNAL_TARGET_MIN +#define _LIVEKIT_SIGNAL_TARGET_MAX _LIVEKIT_PB_SIGNAL_TARGET_MAX +#define _LIVEKIT_SIGNAL_TARGET_ARRAYSIZE _LIVEKIT_PB_SIGNAL_TARGET_ARRAYSIZE +#define _LIVEKIT_STREAM_STATE_MIN _LIVEKIT_PB_STREAM_STATE_MIN +#define _LIVEKIT_STREAM_STATE_MAX _LIVEKIT_PB_STREAM_STATE_MAX +#define _LIVEKIT_STREAM_STATE_ARRAYSIZE _LIVEKIT_PB_STREAM_STATE_ARRAYSIZE +#define _LIVEKIT_CANDIDATE_PROTOCOL_MIN _LIVEKIT_PB_CANDIDATE_PROTOCOL_MIN +#define _LIVEKIT_CANDIDATE_PROTOCOL_MAX _LIVEKIT_PB_CANDIDATE_PROTOCOL_MAX +#define _LIVEKIT_CANDIDATE_PROTOCOL_ARRAYSIZE _LIVEKIT_PB_CANDIDATE_PROTOCOL_ARRAYSIZE +#define _LIVEKIT_LEAVE_REQUEST_ACTION_MIN _LIVEKIT_PB_LEAVE_REQUEST_ACTION_MIN +#define _LIVEKIT_LEAVE_REQUEST_ACTION_MAX _LIVEKIT_PB_LEAVE_REQUEST_ACTION_MAX +#define _LIVEKIT_LEAVE_REQUEST_ACTION_ARRAYSIZE _LIVEKIT_PB_LEAVE_REQUEST_ACTION_ARRAYSIZE +#define _LIVEKIT_REQUEST_RESPONSE_REASON_MIN _LIVEKIT_PB_REQUEST_RESPONSE_REASON_MIN +#define _LIVEKIT_REQUEST_RESPONSE_REASON_MAX _LIVEKIT_PB_REQUEST_RESPONSE_REASON_MAX +#define _LIVEKIT_REQUEST_RESPONSE_REASON_ARRAYSIZE _LIVEKIT_PB_REQUEST_RESPONSE_REASON_ARRAYSIZE +#define LIVEKIT_SIGNAL_REQUEST_INIT_DEFAULT LIVEKIT_PB_SIGNAL_REQUEST_INIT_DEFAULT +#define LIVEKIT_SIGNAL_RESPONSE_INIT_DEFAULT LIVEKIT_PB_SIGNAL_RESPONSE_INIT_DEFAULT +#define LIVEKIT_SIMULCAST_CODEC_INIT_DEFAULT LIVEKIT_PB_SIMULCAST_CODEC_INIT_DEFAULT +#define LIVEKIT_ADD_TRACK_REQUEST_INIT_DEFAULT LIVEKIT_PB_ADD_TRACK_REQUEST_INIT_DEFAULT +#define LIVEKIT_TRICKLE_REQUEST_INIT_DEFAULT LIVEKIT_PB_TRICKLE_REQUEST_INIT_DEFAULT +#define LIVEKIT_MUTE_TRACK_REQUEST_INIT_DEFAULT LIVEKIT_PB_MUTE_TRACK_REQUEST_INIT_DEFAULT +#define LIVEKIT_JOIN_RESPONSE_INIT_DEFAULT LIVEKIT_PB_JOIN_RESPONSE_INIT_DEFAULT +#define LIVEKIT_RECONNECT_RESPONSE_INIT_DEFAULT LIVEKIT_PB_RECONNECT_RESPONSE_INIT_DEFAULT +#define LIVEKIT_TRACK_PUBLISHED_RESPONSE_INIT_DEFAULT LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE_INIT_DEFAULT +#define LIVEKIT_TRACK_UNPUBLISHED_RESPONSE_INIT_DEFAULT LIVEKIT_PB_TRACK_UNPUBLISHED_RESPONSE_INIT_DEFAULT +#define LIVEKIT_SESSION_DESCRIPTION_INIT_DEFAULT LIVEKIT_PB_SESSION_DESCRIPTION_INIT_DEFAULT +#define LIVEKIT_PARTICIPANT_UPDATE_INIT_DEFAULT LIVEKIT_PB_PARTICIPANT_UPDATE_INIT_DEFAULT +#define LIVEKIT_UPDATE_SUBSCRIPTION_INIT_DEFAULT LIVEKIT_PB_UPDATE_SUBSCRIPTION_INIT_DEFAULT +#define LIVEKIT_UPDATE_TRACK_SETTINGS_INIT_DEFAULT LIVEKIT_PB_UPDATE_TRACK_SETTINGS_INIT_DEFAULT +#define LIVEKIT_UPDATE_LOCAL_AUDIO_TRACK_INIT_DEFAULT LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK_INIT_DEFAULT +#define LIVEKIT_UPDATE_LOCAL_VIDEO_TRACK_INIT_DEFAULT LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_INIT_DEFAULT +#define LIVEKIT_LEAVE_REQUEST_INIT_DEFAULT LIVEKIT_PB_LEAVE_REQUEST_INIT_DEFAULT +#define LIVEKIT_UPDATE_PARTICIPANT_METADATA_INIT_DEFAULT LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_INIT_DEFAULT +#define LIVEKIT_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_INIT_DEFAULT LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_INIT_DEFAULT +#define LIVEKIT_ICE_SERVER_INIT_DEFAULT LIVEKIT_PB_ICE_SERVER_INIT_DEFAULT +#define LIVEKIT_SPEAKERS_CHANGED_INIT_DEFAULT LIVEKIT_PB_SPEAKERS_CHANGED_INIT_DEFAULT +#define LIVEKIT_ROOM_UPDATE_INIT_DEFAULT LIVEKIT_PB_ROOM_UPDATE_INIT_DEFAULT +#define LIVEKIT_CONNECTION_QUALITY_INFO_INIT_DEFAULT LIVEKIT_PB_CONNECTION_QUALITY_INFO_INIT_DEFAULT +#define LIVEKIT_CONNECTION_QUALITY_UPDATE_INIT_DEFAULT LIVEKIT_PB_CONNECTION_QUALITY_UPDATE_INIT_DEFAULT +#define LIVEKIT_STREAM_STATE_INFO_INIT_DEFAULT LIVEKIT_PB_STREAM_STATE_INFO_INIT_DEFAULT +#define LIVEKIT_STREAM_STATE_UPDATE_INIT_DEFAULT LIVEKIT_PB_STREAM_STATE_UPDATE_INIT_DEFAULT +#define LIVEKIT_SUBSCRIBED_QUALITY_INIT_DEFAULT LIVEKIT_PB_SUBSCRIBED_QUALITY_INIT_DEFAULT +#define LIVEKIT_SUBSCRIBED_CODEC_INIT_DEFAULT LIVEKIT_PB_SUBSCRIBED_CODEC_INIT_DEFAULT +#define LIVEKIT_SUBSCRIBED_QUALITY_UPDATE_INIT_DEFAULT LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE_INIT_DEFAULT +#define LIVEKIT_TRACK_PERMISSION_INIT_DEFAULT LIVEKIT_PB_TRACK_PERMISSION_INIT_DEFAULT +#define LIVEKIT_SUBSCRIPTION_PERMISSION_INIT_DEFAULT LIVEKIT_PB_SUBSCRIPTION_PERMISSION_INIT_DEFAULT +#define LIVEKIT_SUBSCRIPTION_PERMISSION_UPDATE_INIT_DEFAULT LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_INIT_DEFAULT +#define LIVEKIT_ROOM_MOVED_RESPONSE_INIT_DEFAULT LIVEKIT_PB_ROOM_MOVED_RESPONSE_INIT_DEFAULT +#define LIVEKIT_SYNC_STATE_INIT_DEFAULT LIVEKIT_PB_SYNC_STATE_INIT_DEFAULT +#define LIVEKIT_DATA_CHANNEL_RECEIVE_STATE_INIT_DEFAULT LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE_INIT_DEFAULT +#define LIVEKIT_DATA_CHANNEL_INFO_INIT_DEFAULT LIVEKIT_PB_DATA_CHANNEL_INFO_INIT_DEFAULT +#define LIVEKIT_SIMULATE_SCENARIO_INIT_DEFAULT LIVEKIT_PB_SIMULATE_SCENARIO_INIT_DEFAULT +#define LIVEKIT_PING_INIT_DEFAULT LIVEKIT_PB_PING_INIT_DEFAULT +#define LIVEKIT_PONG_INIT_DEFAULT LIVEKIT_PB_PONG_INIT_DEFAULT +#define LIVEKIT_REGION_SETTINGS_INIT_DEFAULT LIVEKIT_PB_REGION_SETTINGS_INIT_DEFAULT +#define LIVEKIT_REGION_INFO_INIT_DEFAULT LIVEKIT_PB_REGION_INFO_INIT_DEFAULT +#define LIVEKIT_SUBSCRIPTION_RESPONSE_INIT_DEFAULT LIVEKIT_PB_SUBSCRIPTION_RESPONSE_INIT_DEFAULT +#define LIVEKIT_REQUEST_RESPONSE_INIT_DEFAULT LIVEKIT_PB_REQUEST_RESPONSE_INIT_DEFAULT +#define LIVEKIT_TRACK_SUBSCRIBED_INIT_DEFAULT LIVEKIT_PB_TRACK_SUBSCRIBED_INIT_DEFAULT +#define LIVEKIT_SIGNAL_REQUEST_INIT_ZERO LIVEKIT_PB_SIGNAL_REQUEST_INIT_ZERO +#define LIVEKIT_SIGNAL_RESPONSE_INIT_ZERO LIVEKIT_PB_SIGNAL_RESPONSE_INIT_ZERO +#define LIVEKIT_SIMULCAST_CODEC_INIT_ZERO LIVEKIT_PB_SIMULCAST_CODEC_INIT_ZERO +#define LIVEKIT_ADD_TRACK_REQUEST_INIT_ZERO LIVEKIT_PB_ADD_TRACK_REQUEST_INIT_ZERO +#define LIVEKIT_TRICKLE_REQUEST_INIT_ZERO LIVEKIT_PB_TRICKLE_REQUEST_INIT_ZERO +#define LIVEKIT_MUTE_TRACK_REQUEST_INIT_ZERO LIVEKIT_PB_MUTE_TRACK_REQUEST_INIT_ZERO +#define LIVEKIT_JOIN_RESPONSE_INIT_ZERO LIVEKIT_PB_JOIN_RESPONSE_INIT_ZERO +#define LIVEKIT_RECONNECT_RESPONSE_INIT_ZERO LIVEKIT_PB_RECONNECT_RESPONSE_INIT_ZERO +#define LIVEKIT_TRACK_PUBLISHED_RESPONSE_INIT_ZERO LIVEKIT_PB_TRACK_PUBLISHED_RESPONSE_INIT_ZERO +#define LIVEKIT_TRACK_UNPUBLISHED_RESPONSE_INIT_ZERO LIVEKIT_PB_TRACK_UNPUBLISHED_RESPONSE_INIT_ZERO +#define LIVEKIT_SESSION_DESCRIPTION_INIT_ZERO LIVEKIT_PB_SESSION_DESCRIPTION_INIT_ZERO +#define LIVEKIT_PARTICIPANT_UPDATE_INIT_ZERO LIVEKIT_PB_PARTICIPANT_UPDATE_INIT_ZERO +#define LIVEKIT_UPDATE_SUBSCRIPTION_INIT_ZERO LIVEKIT_PB_UPDATE_SUBSCRIPTION_INIT_ZERO +#define LIVEKIT_UPDATE_TRACK_SETTINGS_INIT_ZERO LIVEKIT_PB_UPDATE_TRACK_SETTINGS_INIT_ZERO +#define LIVEKIT_UPDATE_LOCAL_AUDIO_TRACK_INIT_ZERO LIVEKIT_PB_UPDATE_LOCAL_AUDIO_TRACK_INIT_ZERO +#define LIVEKIT_UPDATE_LOCAL_VIDEO_TRACK_INIT_ZERO LIVEKIT_PB_UPDATE_LOCAL_VIDEO_TRACK_INIT_ZERO +#define LIVEKIT_LEAVE_REQUEST_INIT_ZERO LIVEKIT_PB_LEAVE_REQUEST_INIT_ZERO +#define LIVEKIT_UPDATE_PARTICIPANT_METADATA_INIT_ZERO LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_INIT_ZERO +#define LIVEKIT_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_INIT_ZERO LIVEKIT_PB_UPDATE_PARTICIPANT_METADATA_ATTRIBUTES_ENTRY_INIT_ZERO +#define LIVEKIT_ICE_SERVER_INIT_ZERO LIVEKIT_PB_ICE_SERVER_INIT_ZERO +#define LIVEKIT_SPEAKERS_CHANGED_INIT_ZERO LIVEKIT_PB_SPEAKERS_CHANGED_INIT_ZERO +#define LIVEKIT_ROOM_UPDATE_INIT_ZERO LIVEKIT_PB_ROOM_UPDATE_INIT_ZERO +#define LIVEKIT_CONNECTION_QUALITY_INFO_INIT_ZERO LIVEKIT_PB_CONNECTION_QUALITY_INFO_INIT_ZERO +#define LIVEKIT_CONNECTION_QUALITY_UPDATE_INIT_ZERO LIVEKIT_PB_CONNECTION_QUALITY_UPDATE_INIT_ZERO +#define LIVEKIT_STREAM_STATE_INFO_INIT_ZERO LIVEKIT_PB_STREAM_STATE_INFO_INIT_ZERO +#define LIVEKIT_STREAM_STATE_UPDATE_INIT_ZERO LIVEKIT_PB_STREAM_STATE_UPDATE_INIT_ZERO +#define LIVEKIT_SUBSCRIBED_QUALITY_INIT_ZERO LIVEKIT_PB_SUBSCRIBED_QUALITY_INIT_ZERO +#define LIVEKIT_SUBSCRIBED_CODEC_INIT_ZERO LIVEKIT_PB_SUBSCRIBED_CODEC_INIT_ZERO +#define LIVEKIT_SUBSCRIBED_QUALITY_UPDATE_INIT_ZERO LIVEKIT_PB_SUBSCRIBED_QUALITY_UPDATE_INIT_ZERO +#define LIVEKIT_TRACK_PERMISSION_INIT_ZERO LIVEKIT_PB_TRACK_PERMISSION_INIT_ZERO +#define LIVEKIT_SUBSCRIPTION_PERMISSION_INIT_ZERO LIVEKIT_PB_SUBSCRIPTION_PERMISSION_INIT_ZERO +#define LIVEKIT_SUBSCRIPTION_PERMISSION_UPDATE_INIT_ZERO LIVEKIT_PB_SUBSCRIPTION_PERMISSION_UPDATE_INIT_ZERO +#define LIVEKIT_ROOM_MOVED_RESPONSE_INIT_ZERO LIVEKIT_PB_ROOM_MOVED_RESPONSE_INIT_ZERO +#define LIVEKIT_SYNC_STATE_INIT_ZERO LIVEKIT_PB_SYNC_STATE_INIT_ZERO +#define LIVEKIT_DATA_CHANNEL_RECEIVE_STATE_INIT_ZERO LIVEKIT_PB_DATA_CHANNEL_RECEIVE_STATE_INIT_ZERO +#define LIVEKIT_DATA_CHANNEL_INFO_INIT_ZERO LIVEKIT_PB_DATA_CHANNEL_INFO_INIT_ZERO +#define LIVEKIT_SIMULATE_SCENARIO_INIT_ZERO LIVEKIT_PB_SIMULATE_SCENARIO_INIT_ZERO +#define LIVEKIT_PING_INIT_ZERO LIVEKIT_PB_PING_INIT_ZERO +#define LIVEKIT_PONG_INIT_ZERO LIVEKIT_PB_PONG_INIT_ZERO +#define LIVEKIT_REGION_SETTINGS_INIT_ZERO LIVEKIT_PB_REGION_SETTINGS_INIT_ZERO +#define LIVEKIT_REGION_INFO_INIT_ZERO LIVEKIT_PB_REGION_INFO_INIT_ZERO +#define LIVEKIT_SUBSCRIPTION_RESPONSE_INIT_ZERO LIVEKIT_PB_SUBSCRIPTION_RESPONSE_INIT_ZERO +#define LIVEKIT_REQUEST_RESPONSE_INIT_ZERO LIVEKIT_PB_REQUEST_RESPONSE_INIT_ZERO +#define LIVEKIT_TRACK_SUBSCRIBED_INIT_ZERO LIVEKIT_PB_TRACK_SUBSCRIBED_INIT_ZERO + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/components/livekit/protocol/protobufs/livekit_metrics.proto b/components/livekit/protocol/protobufs/livekit_metrics.proto new file mode 100644 index 0000000..5a3daa0 --- /dev/null +++ b/components/livekit/protocol/protobufs/livekit_metrics.proto @@ -0,0 +1,89 @@ +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +import "timestamp.proto"; + + +/* + Protocol used to record metrics for a specific session. + + Clients send their timestamp in their own monotonically increasing time (e.g `performance.now` on JS). + These timestamps are then augmented by the SFU to its time base. + + A metric can be linked to a specific track by setting `track_sid`. +*/ + + +// index from [0: MAX_LABEL_PREDEFINED_MAX_VALUE) are for predefined labels (`MetricLabel`) +enum MetricLabel { + AGENTS_LLM_TTFT = 0; // time to first token from LLM + AGENTS_STT_TTFT = 1; // time to final transcription + AGENTS_TTS_TTFB = 2; // time to first byte + + CLIENT_VIDEO_SUBSCRIBER_FREEZE_COUNT = 3; // Number of video freezes + CLIENT_VIDEO_SUBSCRIBER_TOTAL_FREEZE_DURATION = 4; // total duration of freezes + CLIENT_VIDEO_SUBSCRIBER_PAUSE_COUNT = 5; // number of video pauses + CLIENT_VIDEO_SUBSCRIBER_TOTAL_PAUSES_DURATION = 6; // total duration of pauses + CLIENT_AUDIO_SUBSCRIBER_CONCEALED_SAMPLES = 7; // number of concealed (synthesized) audio samples + CLIENT_AUDIO_SUBSCRIBER_SILENT_CONCEALED_SAMPLES = 8; // number of silent concealed samples + CLIENT_AUDIO_SUBSCRIBER_CONCEALMENT_EVENTS = 9; // number of concealment events + CLIENT_AUDIO_SUBSCRIBER_INTERRUPTION_COUNT = 10; // number of interruptions + CLIENT_AUDIO_SUBSCRIBER_TOTAL_INTERRUPTION_DURATION = 11; // total duration of interruptions + CLIENT_SUBSCRIBER_JITTER_BUFFER_DELAY = 12; // total time spent in jitter buffer + CLIENT_SUBSCRIBER_JITTER_BUFFER_EMITTED_COUNT = 13; // total time spent in jitter buffer + CLIENT_VIDEO_PUBLISHER_QUALITY_LIMITATION_DURATION_BANDWIDTH = 14; // total duration spent in bandwidth quality limitation + CLIENT_VIDEO_PUBLISHER_QUALITY_LIMITATION_DURATION_CPU = 15; // total duration spent in cpu quality limitation + CLIENT_VIDEO_PUBLISHER_QUALITY_LIMITATION_DURATION_OTHER = 16; // total duration spent in other quality limitation + + PUBLISHER_RTT = 17; // Publisher RTT (participant -> server) + SERVER_MESH_RTT = 18; // RTT between publisher node and subscriber node (could involve intermedia node(s)) + SUBSCRIBER_RTT = 19; // Subscribe RTT (server -> participant) + + METRIC_LABEL_PREDEFINED_MAX_VALUE = 4096; +} + +message MetricsBatch { + int64 timestamp_ms = 1; // time at which this batch is sent based on a monotonic clock (millisecond resolution) + google.protobuf.Timestamp normalized_timestamp = 2; + // To avoid repeating string values, we store them in a separate list and reference them by index + // This is useful for storing participant identities, track names, etc. + // There is also a predefined list of labels that can be used to reference common metrics. + // They have reserved indices from 0 to (METRIC_LABEL_PREDEFINED_MAX_VALUE - 1). + // Indexes pointing at str_data should start from METRIC_LABEL_PREDEFINED_MAX_VALUE, + // such that str_data[0] == index of METRIC_LABEL_PREDEFINED_MAX_VALUE. + repeated string str_data = 3; + repeated TimeSeriesMetric time_series = 4; + repeated EventMetric events = 5; +} + +message TimeSeriesMetric { + // Metric name e.g "speech_probablity". The string value is not directly stored in the message, but referenced by index + // in the `str_data` field of `MetricsBatch` + uint32 label = 1; + uint32 participant_identity = 2; // index into `str_data` + uint32 track_sid = 3; // index into `str_data` + repeated MetricSample samples = 4; + uint32 rid = 5; // index into 'str_data' +} + +message MetricSample { + int64 timestamp_ms = 1; // time of metric based on a monotonic clock (in milliseconds) + google.protobuf.Timestamp normalized_timestamp = 2; + float value = 3; +} + +message EventMetric { + uint32 label = 1; + uint32 participant_identity = 2; // index into `str_data` + uint32 track_sid = 3; // index into `str_data` + int64 start_timestamp_ms = 4; // start time of event based on a monotonic clock (in milliseconds) + optional int64 end_timestamp_ms = 5; // end time of event based on a monotonic clock (in milliseconds), if needed + google.protobuf.Timestamp normalized_start_timestamp = 6; + optional google.protobuf.Timestamp normalized_end_timestamp = 7; + string metadata = 8; + uint32 rid = 9; // index into 'str_data' +} diff --git a/components/livekit/protocol/protobufs/livekit_models.options b/components/livekit/protocol/protobufs/livekit_models.options new file mode 100644 index 0000000..c93b551 --- /dev/null +++ b/components/livekit/protocol/protobufs/livekit_models.options @@ -0,0 +1,101 @@ +livekit_pb.ClientConfiguration.video type:FT_IGNORE +livekit_pb.ClientConfiguration.screen type:FT_IGNORE +livekit_pb.ClientConfiguration.disabled_codecs type:FT_IGNORE + +livekit_pb.ParticipantInfo.sid type:FT_POINTER +livekit_pb.ParticipantInfo.identity type:FT_POINTER +livekit_pb.ParticipantInfo.tracks type:FT_POINTER +livekit_pb.ParticipantInfo.metadata type:FT_POINTER +livekit_pb.ParticipantInfo.joined_at type:FT_IGNORE +livekit_pb.ParticipantInfo.joined_at_ms type:FT_IGNORE +livekit_pb.ParticipantInfo.name type:FT_POINTER +livekit_pb.ParticipantInfo.version type:FT_IGNORE +livekit_pb.ParticipantInfo.permission label_override:LABEL_REQUIRED +livekit_pb.ParticipantInfo.region type:FT_IGNORE +livekit_pb.ParticipantInfo.is_publisher type:FT_IGNORE +livekit_pb.ParticipantInfo.kind type:FT_POINTER +livekit_pb.ParticipantInfo.attributes type:FT_IGNORE +livekit_pb.ParticipantInfo.disconnect_reason type:FT_IGNORE +livekit_pb.ParticipantInfo.kind_details type:FT_IGNORE + +livekit_pb.ParticipantPermission.hidden type:FT_IGNORE +livekit_pb.ParticipantPermission.recorder type:FT_IGNORE +livekit_pb.ParticipantPermission.can_publish_sources type:FT_IGNORE +livekit_pb.ParticipantPermission.can_update_metadata type:FT_IGNORE +livekit_pb.ParticipantPermission.can_subscribe_metrics type:FT_IGNORE + +livekit_pb.VideoLayer.bitrate type:FT_IGNORE +livekit_pb.VideoLayer.ssrc type:FT_IGNORE + +livekit_pb.DataPacket.metrics type:FT_IGNORE +livekit_pb.DataPacket.transcription type:FT_IGNORE +livekit_pb.DataPacket.chat_message type:FT_IGNORE +livekit_pb.DataPacket.participant_identity type:FT_POINTER +livekit_pb.DataPacket.destination_identities type:FT_POINTER +livekit_pb.DataPacket.participant_sid type:FT_POINTER + +livekit_pb.UserPacket.payload type:FT_POINTER +livekit_pb.UserPacket.topic type:FT_POINTER +livekit_pb.UserPacket.id type:FT_IGNORE +livekit_pb.UserPacket.start_time type:FT_IGNORE +livekit_pb.UserPacket.end_time type:FT_IGNORE +livekit_pb.UserPacket.nonce type:FT_IGNORE + +livekit_pb.RpcRequest.id max_length:36 +livekit_pb.RpcRequest.method type:FT_POINTER +livekit_pb.RpcRequest.payload type:FT_POINTER + +livekit_pb.RpcAck.request_id max_length:36 + +livekit_pb.RpcResponse.request_id max_length:36 +livekit_pb.RpcResponse.payload type:FT_POINTER + +livekit_pb.RpcError.message type:FT_IGNORE +livekit_pb.RpcError.data type:FT_POINTER + +livekit_pb.DataStream.Header.stream_id max_length:36 +livekit_pb.DataStream.Header.topic type:FT_POINTER +livekit_pb.DataStream.Header.mime_type type:FT_POINTER +livekit_pb.DataStream.Header.encryption_type type:FT_IGNORE +livekit_pb.DataStream.Header.attributes type:FT_IGNORE + +livekit_pb.DataStream.Chunk.stream_id max_length:36 +livekit_pb.DataStream.Chunk.content type:FT_POINTER +livekit_pb.DataStream.Chunk.iv type:FT_IGNORE + +livekit_pb.DataStream.Trailer.stream_id max_length:36 +livekit_pb.DataStream.Trailer.reason max_length:15 +livekit_pb.DataStream.Trailer.attributes type:FT_IGNORE + +livekit_pb.SipDTMF.digit max_length:1 + +livekit_pb.TrackInfo.sid type:FT_POINTER +livekit_pb.TrackInfo.name type:FT_IGNORE +livekit_pb.TrackInfo.width type:FT_IGNORE +livekit_pb.TrackInfo.height type:FT_IGNORE +livekit_pb.TrackInfo.simulcast type:FT_IGNORE +livekit_pb.TrackInfo.disable_dtx type:FT_IGNORE +livekit_pb.TrackInfo.source type:FT_IGNORE +livekit_pb.TrackInfo.layers type:FT_IGNORE +livekit_pb.TrackInfo.mime_type type:FT_POINTER +livekit_pb.TrackInfo.mid type:FT_IGNORE +livekit_pb.TrackInfo.codecs type:FT_IGNORE +livekit_pb.TrackInfo.disable_red type:FT_IGNORE +livekit_pb.TrackInfo.encryption type:FT_IGNORE +livekit_pb.TrackInfo.stream type:FT_IGNORE +livekit_pb.TrackInfo.version type:FT_IGNORE +livekit_pb.TrackInfo.audio_features max_count:8 +livekit_pb.TrackInfo.backup_codec_policy type:FT_IGNORE + +livekit_pb.Room.sid type:FT_POINTER +livekit_pb.Room.name type:FT_POINTER +livekit_pb.Room.empty_timeout type:FT_IGNORE +livekit_pb.Room.departure_timeout type:FT_IGNORE +livekit_pb.Room.max_participants type:FT_IGNORE +livekit_pb.Room.num_publishers type:FT_IGNORE +livekit_pb.Room.creation_time type:FT_IGNORE +livekit_pb.Room.creation_time_ms type:FT_IGNORE +livekit_pb.Room.turn_password type:FT_IGNORE +livekit_pb.Room.enabled_codecs type:FT_IGNORE +livekit_pb.Room.metadata type:FT_POINTER +livekit_pb.Room.version type:FT_IGNORE \ No newline at end of file diff --git a/components/livekit/protocol/protobufs/livekit_models.proto b/components/livekit/protocol/protobufs/livekit_models.proto new file mode 100644 index 0000000..f78895b --- /dev/null +++ b/components/livekit/protocol/protobufs/livekit_models.proto @@ -0,0 +1,714 @@ +// Copyright 2023 LiveKit, Inc. +// +// 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. + +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +import "timestamp.proto"; + +import "livekit_metrics.proto"; + +message Pagination { + string after_id = 1; // list entities which IDs are greater + int32 limit = 2; +} + +// ListUpdate is used for updated APIs where 'repeated string' field is modified. +message ListUpdate { + repeated string set = 1; // set the field to a new list +} + +message Room { + string sid = 1; + string name = 2; + uint32 empty_timeout = 3; + uint32 departure_timeout = 14; + uint32 max_participants = 4; + int64 creation_time = 5; + int64 creation_time_ms = 15; + string turn_password = 6; + repeated Codec enabled_codecs = 7; + string metadata = 8; + uint32 num_participants = 9; + uint32 num_publishers = 11; + bool active_recording = 10; + TimedVersion version = 13; + + // NEXT-ID: 16 +} + +message Codec { + string mime = 1; + string fmtp_line = 2; +} + +enum AudioCodec { + DEFAULT_AC = 0; + OPUS = 1; + AAC = 2; +} + +enum VideoCodec { + DEFAULT_VC = 0; + H264_BASELINE = 1; + H264_MAIN = 2; + H264_HIGH = 3; + VP8 = 4; +} + +enum ImageCodec { + IC_DEFAULT = 0; + IC_JPEG = 1; +} + +// Policy for publisher to handle subscribers that are unable to support the primary codec of a track +enum BackupCodecPolicy { + // default behavior, the track prefer to regress to backup codec and all subscribers will receive the backup codec, + // the sfu will try to regress codec if possible but not assured. + PREFER_REGRESSION = 0; + // encoding/send the primary and backup codec simultaneously + SIMULCAST = 1; + // force the track to regress to backup codec, this option can be used in video conference or the publisher has limited bandwidth/encoding power + REGRESSION = 2; +} + +message PlayoutDelay { + bool enabled = 1; + uint32 min = 2; + uint32 max = 3; +} + +message ParticipantPermission { + // allow participant to subscribe to other tracks in the room + bool can_subscribe = 1; + // allow participant to publish new tracks to room + bool can_publish = 2; + // allow participant to publish data + bool can_publish_data = 3; + // sources that are allowed to be published + repeated TrackSource can_publish_sources = 9; + // indicates that it's hidden to others + bool hidden = 7; + // indicates it's a recorder instance + // deprecated: use ParticipantInfo.kind instead + bool recorder = 8 [deprecated=true]; + // indicates that participant can update own metadata and attributes + bool can_update_metadata = 10; + // indicates that participant is an agent + // deprecated: use ParticipantInfo.kind instead + bool agent = 11 [deprecated=true]; + // if a participant can subscribe to metrics + bool can_subscribe_metrics = 12; + + // NEXT_ID: 13 +} + +message ParticipantInfo { + enum State { + // websocket' connected, but not offered yet + JOINING = 0; + // server received client offer + JOINED = 1; + // ICE connectivity established + ACTIVE = 2; + // WS disconnected + DISCONNECTED = 3; + } + enum Kind { + // standard participants, e.g. web clients + STANDARD = 0; + // only ingests streams + INGRESS = 1; + // only consumes streams + EGRESS = 2; + // SIP participants + SIP = 3; + // LiveKit agents + AGENT = 4; + + // NEXT_ID: 7 + } + enum KindDetail { + CLOUD_AGENT = 0; + FORWARDED = 1; + } + string sid = 1; + string identity = 2; + State state = 3; + repeated TrackInfo tracks = 4; + string metadata = 5; + // timestamp when participant joined room, in seconds + int64 joined_at = 6; + // timestamp when participant joined room, in milliseconds + int64 joined_at_ms = 17; + string name = 9; + uint32 version = 10; + ParticipantPermission permission = 11; + string region = 12; + // indicates the participant has an active publisher connection + // and can publish to the server + bool is_publisher = 13; + Kind kind = 14; + map attributes = 15; + DisconnectReason disconnect_reason = 16; + repeated KindDetail kind_details = 18; + + // NEXT_ID: 19 +} + +enum TrackType { + AUDIO = 0; + VIDEO = 1; + DATA = 2; +} + +enum TrackSource { + UNKNOWN = 0; + CAMERA = 1; + MICROPHONE = 2; + SCREEN_SHARE = 3; + SCREEN_SHARE_AUDIO = 4; +} + +message Encryption { + enum Type { + NONE=0; + GCM=1; + CUSTOM=2; + } +} + +message SimulcastCodecInfo { + string mime_type = 1; + string mid = 2; + string cid = 3; + repeated VideoLayer layers = 4; +} + +message TrackInfo { + string sid = 1; + TrackType type = 2; + string name = 3; + bool muted = 4; + // original width of video (unset for audio) + // clients may receive a lower resolution version with simulcast + uint32 width = 5; + // original height of video (unset for audio) + uint32 height = 6; + // true if track is simulcasted + bool simulcast = 7; + // true if DTX (Discontinuous Transmission) is disabled for audio + bool disable_dtx = 8; + // source of media + TrackSource source = 9; + repeated VideoLayer layers = 10; + // mime type of codec + string mime_type = 11; + string mid = 12; + repeated SimulcastCodecInfo codecs = 13; + bool stereo = 14; + // true if RED (Redundant Encoding) is disabled for audio + bool disable_red = 15; + Encryption.Type encryption = 16; + string stream = 17; + TimedVersion version = 18; + repeated AudioTrackFeature audio_features = 19; + BackupCodecPolicy backup_codec_policy = 20; +} + +enum VideoQuality { + LOW = 0; + MEDIUM = 1; + HIGH = 2; + OFF = 3; +} + +// provide information about available spatial layers +message VideoLayer { + // for tracks with a single layer, this should be HIGH + VideoQuality quality = 1; + uint32 width = 2; + uint32 height = 3; + // target bitrate in bit per second (bps), server will measure actual + uint32 bitrate = 4; + uint32 ssrc = 5; +} + +// new DataPacket API +message DataPacket { + enum Kind { + RELIABLE = 0; + LOSSY = 1; + } + Kind kind = 1 [deprecated=true]; + // participant identity of user that sent the message + string participant_identity = 4; + // identities of participants who will receive the message (sent to all by default) + repeated string destination_identities = 5; + oneof value { + UserPacket user = 2; + ActiveSpeakerUpdate speaker = 3 [deprecated=true]; + SipDTMF sip_dtmf = 6; + Transcription transcription = 7; + MetricsBatch metrics = 8; + ChatMessage chat_message = 9; + RpcRequest rpc_request = 10; + RpcAck rpc_ack = 11; + RpcResponse rpc_response = 12; + DataStream.Header stream_header = 13; + DataStream.Chunk stream_chunk = 14; + DataStream.Trailer stream_trailer = 15; + } + // sequence number of reliable packet + uint32 sequence = 16; + // sid of the user that sent the message + string participant_sid = 17; + + // NEXT_ID: 18 +} + +message ActiveSpeakerUpdate { + repeated SpeakerInfo speakers = 1; +} + +message SpeakerInfo { + string sid = 1; + // audio level, 0-1.0, 1 is loudest + float level = 2; + // true if speaker is currently active + bool active = 3; +} + +message UserPacket { + // participant ID of user that sent the message + string participant_sid = 1 [deprecated=true]; + string participant_identity = 5 [deprecated=true]; + // user defined payload + bytes payload = 2; + // the ID of the participants who will receive the message (sent to all by default) + repeated string destination_sids = 3 [deprecated=true]; + // identities of participants who will receive the message (sent to all by default) + repeated string destination_identities = 6 [deprecated=true]; + // topic under which the message was published + optional string topic = 4; + // Unique ID to indentify the message + optional string id = 8; + // start and end time allow relating the message to specific media time + optional uint64 start_time = 9; + optional uint64 end_time = 10; + // added by SDK to enable de-duping of messages, for INTERNAL USE ONLY + bytes nonce = 11; + + // NEXT_ID: 12 +} + +message SipDTMF { + uint32 code = 3; + string digit = 4; +} + +message Transcription { + // Participant that got its speech transcribed + string transcribed_participant_identity = 2; + string track_id = 3; + repeated TranscriptionSegment segments = 4; + + // NEXT_ID: 6 +} + +message TranscriptionSegment { + string id = 1; + string text = 2; + uint64 start_time = 3; + uint64 end_time = 4; + bool final = 5; + string language = 6; +} + +message ChatMessage { + string id = 1; // uuid + int64 timestamp = 2; + optional int64 edit_timestamp = 3; // populated only if the intent is to edit/update an existing message + string message = 4; + bool deleted = 5; // true to remove message + bool generated = 6; // true if the chat message has been generated by an agent from a participant's audio transcription +} + +message RpcRequest { + string id = 1; + string method = 2; + string payload = 3; + uint32 response_timeout_ms = 4; + uint32 version = 5; +} + +message RpcAck { + string request_id = 1; +} + +message RpcResponse { + string request_id = 1; + oneof value { + string payload = 2; + RpcError error = 3; + } +} + +message RpcError { + uint32 code = 1; + string message = 2; + string data = 3; +} + +enum ConnectionQuality { + POOR = 0; + GOOD = 1; + EXCELLENT = 2; + LOST = 3; +} + +message ParticipantTracks { + // participant ID of participant to whom the tracks belong + string participant_sid = 1; + repeated string track_sids = 2; +} + +// details about the server +message ServerInfo { + enum Edition { + Standard = 0; + Cloud = 1; + } + Edition edition = 1; + string version = 2; + int32 protocol = 3; + string region = 4; + string node_id = 5; + // additional debugging information. sent only if server is in development mode + string debug_info = 6; + int32 agent_protocol = 7; +} + +// details about the client +message ClientInfo { + enum SDK { + UNKNOWN = 0; + JS = 1; + SWIFT = 2; + ANDROID = 3; + FLUTTER = 4; + GO = 5; + UNITY = 6; + REACT_NATIVE = 7; + RUST = 8; + PYTHON = 9; + CPP = 10; + UNITY_WEB = 11; + NODE = 12; + UNREAL = 13; + } + + SDK sdk = 1; + string version = 2; + int32 protocol = 3; + string os = 4; + string os_version = 5; + string device_model = 6; + string browser = 7; + string browser_version = 8; + string address = 9; + // wifi, wired, cellular, vpn, empty if not known + string network = 10; + // comma separated list of additional LiveKit SDKs in use of this client, with versions + // e.g. "components-js:1.2.3,track-processors-js:1.2.3" + string other_sdks = 11; +} + +// server provided client configuration +message ClientConfiguration { + VideoConfiguration video = 1; + VideoConfiguration screen = 2; + + ClientConfigSetting resume_connection = 3; + DisabledCodecs disabled_codecs = 4; + ClientConfigSetting force_relay = 5; +} + +enum ClientConfigSetting { + UNSET = 0; + DISABLED = 1; + ENABLED = 2; +} + +message VideoConfiguration { + ClientConfigSetting hardware_encoder = 1; +} + +message DisabledCodecs { + // disabled for both publish and subscribe + repeated Codec codecs = 1; + // only disable for publish + repeated Codec publish = 2; +} + +enum DisconnectReason { + UNKNOWN_REASON = 0; + // the client initiated the disconnect + CLIENT_INITIATED = 1; + // another participant with the same identity has joined the room + DUPLICATE_IDENTITY = 2; + // the server instance is shutting down + SERVER_SHUTDOWN = 3; + // RoomService.RemoveParticipant was called + PARTICIPANT_REMOVED = 4; + // RoomService.DeleteRoom was called + ROOM_DELETED = 5; + // the client is attempting to resume a session, but server is not aware of it + STATE_MISMATCH = 6; + // client was unable to connect fully + JOIN_FAILURE = 7; + // Cloud-only, the server requested Participant to migrate the connection elsewhere + MIGRATION = 8; + // the signal websocket was closed unexpectedly + SIGNAL_CLOSE = 9; + // the room was closed, due to all Standard and Ingress participants having left + ROOM_CLOSED = 10; + // SIP callee did not respond in time + USER_UNAVAILABLE = 11; + // SIP callee rejected the call (busy) + USER_REJECTED = 12; + // SIP protocol failure or unexpected response + SIP_TRUNK_FAILURE = 13; + // server timed out a participant session + CONNECTION_TIMEOUT = 14; + // media stream failure or media timeout + MEDIA_FAILURE = 15; +} + +message RTPDrift { + google.protobuf.Timestamp start_time = 1; + google.protobuf.Timestamp end_time = 2; + double duration = 3; + + uint64 start_timestamp = 4; + uint64 end_timestamp = 5; + uint64 rtp_clock_ticks = 6; + int64 drift_samples = 7; + double drift_ms = 8; + double clock_rate = 9; +} + +message RTPStats { + google.protobuf.Timestamp start_time = 1; + google.protobuf.Timestamp end_time = 2; + double duration = 3; + + uint32 packets = 4; + double packet_rate = 5; + + uint64 bytes = 6; + uint64 header_bytes = 39; + double bitrate = 7; + + uint32 packets_lost = 8; + double packet_loss_rate = 9; + float packet_loss_percentage = 10; + + uint32 packets_duplicate = 11; + double packet_duplicate_rate = 12; + + uint64 bytes_duplicate = 13; + uint64 header_bytes_duplicate = 40; + double bitrate_duplicate = 14; + + uint32 packets_padding = 15; + double packet_padding_rate = 16; + + uint64 bytes_padding = 17; + uint64 header_bytes_padding = 41; + double bitrate_padding = 18; + + uint32 packets_out_of_order = 19; + + uint32 frames = 20; + double frame_rate = 21; + + double jitter_current = 22; + double jitter_max = 23; + + map gap_histogram = 24; + + uint32 nacks = 25; + uint32 nack_acks = 37; + uint32 nack_misses = 26; + uint32 nack_repeated = 38; + + uint32 plis = 27; + google.protobuf.Timestamp last_pli = 28; + + uint32 firs = 29; + google.protobuf.Timestamp last_fir = 30; + + uint32 rtt_current = 31; + uint32 rtt_max = 32; + + uint32 key_frames = 33; + google.protobuf.Timestamp last_key_frame = 34; + + uint32 layer_lock_plis = 35; + google.protobuf.Timestamp last_layer_lock_pli = 36; + + RTPDrift packet_drift = 44; + RTPDrift ntp_report_drift = 45; + RTPDrift rebased_report_drift = 46; + RTPDrift received_report_drift = 47; + // NEXT_ID: 48 +} + +message RTCPSenderReportState { + uint32 rtp_timestamp = 1; + uint64 rtp_timestamp_ext = 2; + uint64 ntp_timestamp = 3; + int64 at = 4; // time at which this happened + int64 at_adjusted = 5; + uint32 packets = 6; + uint64 octets = 7; +} + +message RTPForwarderState { + bool started = 1; + int32 reference_layer_spatial = 2; + int64 pre_start_time = 3; + uint64 ext_first_timestamp = 4; + uint64 dummy_start_timestamp_offset = 5; + RTPMungerState rtp_munger = 6; + oneof codec_munger { + VP8MungerState vp8_munger = 7; + } + repeated RTCPSenderReportState sender_report_state = 8; +} + +message RTPMungerState { + uint64 ext_last_sequence_number = 1; + uint64 ext_second_last_sequence_number = 2; + uint64 ext_last_timestamp = 3; + uint64 ext_second_last_timestamp = 4; + bool last_marker = 5; + bool second_last_marker = 6; +} + +message VP8MungerState { + int32 ext_last_picture_id = 1; + bool picture_id_used = 2; + uint32 last_tl0_pic_idx = 3; + bool tl0_pic_idx_used = 4; + bool tid_used = 5; + uint32 last_key_idx = 6; + bool key_idx_used = 7; +} + +message TimedVersion { + int64 unix_micro = 1; + int32 ticks = 2; +} + +enum ReconnectReason { + RR_UNKNOWN = 0; + RR_SIGNAL_DISCONNECTED = 1; + RR_PUBLISHER_FAILED = 2; + RR_SUBSCRIBER_FAILED = 3; + RR_SWITCH_CANDIDATE = 4; +} + +enum SubscriptionError { + SE_UNKNOWN = 0; + SE_CODEC_UNSUPPORTED = 1; + SE_TRACK_NOTFOUND = 2; +} + +enum AudioTrackFeature { + TF_STEREO = 0; + TF_NO_DTX = 1; + TF_AUTO_GAIN_CONTROL = 2; + TF_ECHO_CANCELLATION = 3; + TF_NOISE_SUPPRESSION = 4; + TF_ENHANCED_NOISE_CANCELLATION = 5; + TF_PRECONNECT_BUFFER = 6; // client will buffer audio once available and send it to the server via bytes stream once connected +} + +message DataStream { + + // enum for operation types (specific to TextHeader) + enum OperationType { + CREATE = 0; + UPDATE = 1; + DELETE = 2; + REACTION = 3; + } + + // header properties specific to text streams + message TextHeader { + OperationType operation_type = 1; + int32 version = 2; // Optional: Version for updates/edits + string reply_to_stream_id = 3; // Optional: Reply to specific message + repeated string attached_stream_ids = 4; // file attachments for text streams + bool generated = 5; // true if the text has been generated by an agent from a participant's audio transcription + + } + + // header properties specific to byte or file streams + message ByteHeader { + string name = 1; + } + + // main DataStream.Header that contains a oneof for specific headers + message Header { + string stream_id = 1; // unique identifier for this data stream + int64 timestamp = 2; // using int64 for Unix timestamp + string topic = 3; + string mime_type = 4; + optional uint64 total_length = 5; // only populated for finite streams, if it's a stream of unknown size this stays empty + Encryption.Type encryption_type = 7; // defaults to NONE + map attributes = 8; // user defined attributes map that can carry additional info + + // oneof to choose between specific header types + oneof content_header { + TextHeader text_header = 9; + ByteHeader byte_header = 10; + } + } + + message Chunk { + string stream_id = 1; // unique identifier for this data stream to map it to the correct header + uint64 chunk_index = 2; + bytes content = 3; // content as binary (bytes) + int32 version = 4; // a version indicating that this chunk_index has been retroactively modified and the original one needs to be replaced + optional bytes iv = 5; // optional, initialization vector for AES-GCM encryption + } + + message Trailer { + string stream_id = 1; // unique identifier for this data stream + string reason = 2; // reason why the stream was closed (could contain "error" / "interrupted" / empty for expected end) + map attributes = 3; // finalizing updates for the stream, can also include additional insights for errors or endTime for transcription + } +} + +message WebhookConfig { + string url = 1; + string signing_key = 2; +} diff --git a/components/livekit/protocol/protobufs/livekit_rtc.options b/components/livekit/protocol/protobufs/livekit_rtc.options new file mode 100644 index 0000000..bbf48cd --- /dev/null +++ b/components/livekit/protocol/protobufs/livekit_rtc.options @@ -0,0 +1,44 @@ +livekit_pb.JoinResponse.participant label_override:LABEL_REQUIRED +livekit_pb.JoinResponse.other_participants type:FT_POINTER +livekit_pb.JoinResponse.server_version type:FT_IGNORE +livekit_pb.JoinResponse.alternative_url type:FT_IGNORE +livekit_pb.JoinResponse.server_region type:FT_IGNORE +livekit_pb.JoinResponse.server_info type:FT_IGNORE +livekit_pb.JoinResponse.sif_trailer type:FT_IGNORE +livekit_pb.JoinResponse.enabled_publish_codecs type:FT_IGNORE +livekit_pb.JoinResponse.fast_publish type:FT_IGNORE +livekit_pb.JoinResponse.ice_servers max_count:4 + +livekit_pb.LeaveRequest.can_reconnect type:FT_IGNORE +livekit_pb.LeaveRequest.regions type:FT_IGNORE + +livekit_pb.ICEServer.urls type:FT_POINTER +livekit_pb.ICEServer.username type:FT_POINTER +livekit_pb.ICEServer.credential type:FT_POINTER + +livekit_pb.SessionDescription.type max_length:8 +livekit_pb.SessionDescription.sdp type:FT_POINTER + +livekit_pb.TrickleRequest.candidateInit type:FT_POINTER + +livekit_pb.AddTrackRequest.cid max_length:15 +livekit_pb.AddTrackRequest.name max_length:15 +livekit_pb.AddTrackRequest.width type:FT_IGNORE +livekit_pb.AddTrackRequest.height type:FT_IGNORE +livekit_pb.AddTrackRequest.layers max_count:1 +livekit_pb.AddTrackRequest.simulcast_codecs type:FT_IGNORE +livekit_pb.AddTrackRequest.sid type:FT_IGNORE +livekit_pb.AddTrackRequest.stereo type:FT_IGNORE +livekit_pb.AddTrackRequest.disable_red type:FT_IGNORE +livekit_pb.AddTrackRequest.encryption type:FT_IGNORE +livekit_pb.AddTrackRequest.stream type:FT_IGNORE +livekit_pb.AddTrackRequest.backup_codec_policy type:FT_IGNORE +livekit_pb.AddTrackRequest.audio_features max_count:8 + +livekit_pb.TrackPublishedResponse.cid type:FT_POINTER +livekit_pb.TrackPublishedResponse.track label_override:LABEL_REQUIRED + +livekit_pb.ParticipantUpdate.participants type:FT_POINTER + +livekit_pb.UpdateSubscription.track_sids type:FT_POINTER +livekit_pb.UpdateSubscription.participant_tracks type:FT_IGNORE \ No newline at end of file diff --git a/components/livekit/protocol/protobufs/livekit_rtc.proto b/components/livekit/protocol/protobufs/livekit_rtc.proto new file mode 100644 index 0000000..0ade62b --- /dev/null +++ b/components/livekit/protocol/protobufs/livekit_rtc.proto @@ -0,0 +1,473 @@ +// Copyright 2023 LiveKit, Inc. +// +// 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. + +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +import "livekit_models.proto"; + +message SignalRequest { + oneof message { + // initial join exchange, for publisher + SessionDescription offer = 1; + // participant answering publisher offer + SessionDescription answer = 2; + TrickleRequest trickle = 3; + AddTrackRequest add_track = 4; + // mute the participant's published tracks + MuteTrackRequest mute = 5; + // Subscribe or unsubscribe from tracks + UpdateSubscription subscription = 6; + // Update settings of subscribed tracks + UpdateTrackSettings track_setting = 7; + // Immediately terminate session + LeaveRequest leave = 8; + // Update published video layers + UpdateVideoLayers update_layers = 10 [deprecated = true]; + // Update subscriber permissions + SubscriptionPermission subscription_permission = 11; + // sync client's subscribe state to server during reconnect + SyncState sync_state = 12; + // Simulate conditions, for client validations + SimulateScenario simulate = 13; + // client triggered ping to server + int64 ping = 14; // deprecated by ping_req (message Ping) + // update a participant's own metadata, name, or attributes + // requires canUpdateOwnParticipantMetadata permission + UpdateParticipantMetadata update_metadata = 15; + Ping ping_req = 16; + // Update local audio track settings + UpdateLocalAudioTrack update_audio_track = 17; + // Update local video track settings + UpdateLocalVideoTrack update_video_track = 18; + } +} + +message SignalResponse { + oneof message { + // sent when join is accepted + JoinResponse join = 1; + // sent when server answers publisher + SessionDescription answer = 2; + // sent when server is sending subscriber an offer + SessionDescription offer = 3; + // sent when an ICE candidate is available + TrickleRequest trickle = 4; + // sent when participants in the room has changed + ParticipantUpdate update = 5; + // sent to the participant when their track has been published + TrackPublishedResponse track_published = 6; + // Immediately terminate session + LeaveRequest leave = 8; + // server initiated mute + MuteTrackRequest mute = 9; + // indicates changes to speaker status, including when they've gone to not speaking + SpeakersChanged speakers_changed = 10; + // sent when metadata of the room has changed + RoomUpdate room_update = 11; + // when connection quality changed + ConnectionQualityUpdate connection_quality = 12; + // when streamed tracks state changed, used to notify when any of the streams were paused due to + // congestion + StreamStateUpdate stream_state_update = 13; + // when max subscribe quality changed, used by dynamic broadcasting to disable unused layers + SubscribedQualityUpdate subscribed_quality_update = 14; + // when subscription permission changed + SubscriptionPermissionUpdate subscription_permission_update = 15; + // update the token the client was using, to prevent an active client from using an expired token + string refresh_token = 16; + // server initiated track unpublish + TrackUnpublishedResponse track_unpublished = 17; + // respond to ping + int64 pong = 18; // deprecated by pong_resp (message Pong) + // sent when client reconnects + ReconnectResponse reconnect = 19; + // respond to Ping + Pong pong_resp = 20; + // Subscription response, client should not expect any media from this subscription if it fails + SubscriptionResponse subscription_response = 21; + // Response relating to user inititated requests that carry a `request_id` + RequestResponse request_response = 22; + // notify to the publisher when a published track has been subscribed for the first time + TrackSubscribed track_subscribed = 23; + // notify to the participant when they have been moved to a new room + RoomMovedResponse room_moved = 24; + } +} + +enum SignalTarget { + PUBLISHER = 0; + SUBSCRIBER = 1; +} + +message SimulcastCodec { + string codec = 1; + string cid = 2; + + // NEXT-ID: 4 +} + +message AddTrackRequest { + // client ID of track, to match it when RTC track is received + string cid = 1; + string name = 2; + TrackType type = 3; + // to be deprecated in favor of layers + uint32 width = 4; + uint32 height = 5; + // true to add track and initialize to muted + bool muted = 6; + // true if DTX (Discontinuous Transmission) is disabled for audio + bool disable_dtx = 7 [deprecated = true]; // deprecated in favor of audio_features + TrackSource source = 8; + repeated VideoLayer layers = 9; + + repeated SimulcastCodec simulcast_codecs = 10; + + // server ID of track, publish new codec to exist track + string sid = 11; + + bool stereo = 12 [deprecated = true]; // deprecated in favor of audio_features + // true if RED (Redundant Encoding) is disabled for audio + bool disable_red = 13; + + Encryption.Type encryption = 14; + // which stream the track belongs to, used to group tracks together. + // if not specified, server will infer it from track source to bundle camera/microphone, screenshare/audio together + string stream = 15; + BackupCodecPolicy backup_codec_policy = 16; + + repeated AudioTrackFeature audio_features = 17; +} + +message TrickleRequest { + string candidateInit = 1; + SignalTarget target = 2; + bool final = 3; +} + +message MuteTrackRequest { + string sid = 1; + bool muted = 2; +} + +message JoinResponse { + Room room = 1; + ParticipantInfo participant = 2; + repeated ParticipantInfo other_participants = 3; + // deprecated. use server_info.version instead. + string server_version = 4; + repeated ICEServer ice_servers = 5; + // use subscriber as the primary PeerConnection + bool subscriber_primary = 6; + // when the current server isn't available, return alternate url to retry connection + // when this is set, the other fields will be largely empty + string alternative_url = 7; + ClientConfiguration client_configuration = 8; + // deprecated. use server_info.region instead. + string server_region = 9; + int32 ping_timeout = 10; + int32 ping_interval = 11; + ServerInfo server_info = 12; + // Server-Injected-Frame byte trailer, used to identify unencrypted frames when e2ee is enabled + bytes sif_trailer = 13; + repeated Codec enabled_publish_codecs = 14; + // when set, client should attempt to establish publish peer connection when joining room to speed up publishing + bool fast_publish = 15; +} + +message ReconnectResponse { + repeated ICEServer ice_servers = 1; + ClientConfiguration client_configuration = 2; + ServerInfo server_info = 3; + + // last sequence number of reliable message received before resuming + uint32 last_message_seq = 4; +} + +message TrackPublishedResponse { + string cid = 1; + TrackInfo track = 2; +} + +message TrackUnpublishedResponse { + string track_sid = 1; +} + +message SessionDescription { + string type = 1; // "answer" | "offer" | "pranswer" | "rollback" + string sdp = 2; + uint32 id = 3; +} + +message ParticipantUpdate { + repeated ParticipantInfo participants = 1; +} + +message UpdateSubscription { + repeated string track_sids = 1; + bool subscribe = 2; + repeated ParticipantTracks participant_tracks = 3; +} + +message UpdateTrackSettings { + repeated string track_sids = 1; + // when true, the track is placed in a paused state, with no new data returned + bool disabled = 3; + // deprecated in favor of width & height + VideoQuality quality = 4; + // for video, width to receive + uint32 width = 5; + // for video, height to receive + uint32 height = 6; + uint32 fps = 7; + // subscription priority. 1 being the highest (0 is unset) + // when unset, server sill assign priority based on the order of subscription + // server will use priority in the following ways: + // 1. when subscribed tracks exceed per-participant subscription limit, server will + // pause the lowest priority tracks + // 2. when the network is congested, server will assign available bandwidth to + // higher priority tracks first. lowest priority tracks can be paused + uint32 priority = 8; +} + + + +message UpdateLocalAudioTrack { + string track_sid = 1; + repeated AudioTrackFeature features = 2; +} + +message UpdateLocalVideoTrack { + string track_sid = 1; + uint32 width = 2; + uint32 height = 3; +} + +message LeaveRequest { + // indicates action clients should take on receiving this message + enum Action { + DISCONNECT = 0; // should disconnect + RESUME = 1; // should attempt a resume with `reconnect=1` in join URL + RECONNECT = 2; // should attempt a reconnect, i. e. no `reconnect=1` + } + + // sent when server initiates the disconnect due to server-restart + // indicates clients should attempt full-reconnect sequence + // NOTE: `can_reconnect` obsoleted by `action` starting in protocol version 13 + bool can_reconnect = 1; + DisconnectReason reason = 2; + Action action = 3; + RegionSettings regions = 4; +} + +// message to indicate published video track dimensions are changing +message UpdateVideoLayers { + option deprecated = true; + string track_sid = 1; + repeated VideoLayer layers = 2; +} + +message UpdateParticipantMetadata { + string metadata = 1; + string name = 2; + // attributes to update. it only updates attributes that have been set + // to delete attributes, set the value to an empty string + map attributes = 3; + uint32 request_id = 4; +} + +message ICEServer { + repeated string urls = 1; + string username = 2; + string credential = 3; +} + +message SpeakersChanged { + repeated SpeakerInfo speakers = 1; +} + +message RoomUpdate { + Room room = 1; +} + +message ConnectionQualityInfo { + string participant_sid = 1; + ConnectionQuality quality = 2; + float score = 3; +} + +message ConnectionQualityUpdate { + repeated ConnectionQualityInfo updates = 1; +} + +enum StreamState { + ACTIVE = 0; + PAUSED = 1; +} + +message StreamStateInfo { + string participant_sid = 1; + string track_sid = 2; + StreamState state = 3; +} + +message StreamStateUpdate { + repeated StreamStateInfo stream_states = 1; +} + +message SubscribedQuality { + VideoQuality quality = 1; + bool enabled = 2; +} + +message SubscribedCodec { + string codec = 1; + repeated SubscribedQuality qualities = 2; +} + +message SubscribedQualityUpdate { + string track_sid = 1; + repeated SubscribedQuality subscribed_qualities = 2 [deprecated = true]; + repeated SubscribedCodec subscribed_codecs = 3; +} + +message TrackPermission { + // permission could be granted either by participant sid or identity + string participant_sid = 1; + bool all_tracks = 2; + repeated string track_sids = 3; + string participant_identity = 4; +} + +message SubscriptionPermission { + bool all_participants = 1; + repeated TrackPermission track_permissions = 2; +} + +message SubscriptionPermissionUpdate { + string participant_sid = 1; + string track_sid = 2; + bool allowed = 3; +} + +message RoomMovedResponse { + // information about the new room + Room room = 1; + // new reconnect token that can be used to reconnect to the new room + string token = 2; + ParticipantInfo participant = 3; + repeated ParticipantInfo other_participants = 4; +} + +message SyncState { + // last subscribe answer before reconnecting + SessionDescription answer = 1; + UpdateSubscription subscription = 2; + repeated TrackPublishedResponse publish_tracks = 3; + repeated DataChannelInfo data_channels = 4; + // last received server side offer before reconnecting + SessionDescription offer = 5; + repeated string track_sids_disabled = 6; + repeated DataChannelReceiveState datachannel_receive_states = 7; +} + +message DataChannelReceiveState { + string publisher_sid = 1; + uint32 last_seq = 2; +} + +message DataChannelInfo { + string label = 1; + uint32 id = 2; + SignalTarget target = 3; +} + +enum CandidateProtocol { + UDP = 0; + TCP = 1; + TLS = 2; +} + +message SimulateScenario { + oneof scenario { + // simulate N seconds of speaker activity + int32 speaker_update = 1; + // simulate local node failure + bool node_failure = 2; + // simulate migration + bool migration = 3; + // server to send leave + bool server_leave = 4; + // switch candidate protocol to tcp + CandidateProtocol switch_candidate_protocol = 5; + // maximum bandwidth for subscribers, in bps + // when zero, clears artificial bandwidth limit + int64 subscriber_bandwidth = 6; + // disconnect signal on resume + bool disconnect_signal_on_resume = 7; + // disconnect signal on resume before sending any messages from server + bool disconnect_signal_on_resume_no_messages = 8; + // full reconnect leave request + bool leave_request_full_reconnect = 9; + } +} + +message Ping { + int64 timestamp = 1; + // rtt in milliseconds calculated by client + int64 rtt = 2; +} + +message Pong { + // timestamp field of last received ping request + int64 last_ping_timestamp = 1; + int64 timestamp = 2; +} + +message RegionSettings { + repeated RegionInfo regions = 1; +} + +message RegionInfo { + string region = 1; + string url = 2; + int64 distance = 3; +} + +message SubscriptionResponse { + string track_sid = 1; + SubscriptionError err = 2; +} + +message RequestResponse { + enum Reason { + OK = 0; + NOT_FOUND = 1; + NOT_ALLOWED = 2; + LIMIT_EXCEEDED = 3; + } + + uint32 request_id = 1; + Reason reason = 2; + string message = 3; +} + +message TrackSubscribed { + string track_sid = 1; +} diff --git a/components/livekit/protocol/protobufs/timestamp.options b/components/livekit/protocol/protobufs/timestamp.options new file mode 100644 index 0000000..650381e --- /dev/null +++ b/components/livekit/protocol/protobufs/timestamp.options @@ -0,0 +1 @@ +* package:'google.protobuf' \ No newline at end of file diff --git a/components/livekit/protocol/protobufs/timestamp.proto b/components/livekit/protocol/protobufs/timestamp.proto new file mode 100644 index 0000000..7b343dd --- /dev/null +++ b/components/livekit/protocol/protobufs/timestamp.proto @@ -0,0 +1,138 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "types"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "TimestampProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// A Timestamp represents a point in time independent of any time zone or local +// calendar, encoded as a count of seconds and fractions of seconds at +// nanosecond resolution. The count is relative to an epoch at UTC midnight on +// January 1, 1970, in the proleptic Gregorian calendar which extends the +// Gregorian calendar backwards to year one. +// +// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap +// second table is needed for interpretation, using a [24-hour linear +// smear](https://developers.google.com/time/smear). +// +// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By +// restricting to that range, we ensure that we can convert to and from [RFC +// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. +// +// # Examples +// +// Example 1: Compute Timestamp from POSIX `time()`. +// +// Timestamp timestamp; +// timestamp.set_seconds(time(NULL)); +// timestamp.set_nanos(0); +// +// Example 2: Compute Timestamp from POSIX `gettimeofday()`. +// +// struct timeval tv; +// gettimeofday(&tv, NULL); +// +// Timestamp timestamp; +// timestamp.set_seconds(tv.tv_sec); +// timestamp.set_nanos(tv.tv_usec * 1000); +// +// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. +// +// FILETIME ft; +// GetSystemTimeAsFileTime(&ft); +// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; +// +// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z +// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. +// Timestamp timestamp; +// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); +// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); +// +// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. +// +// long millis = System.currentTimeMillis(); +// +// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) +// .setNanos((int) ((millis % 1000) * 1000000)).build(); +// +// +// Example 5: Compute Timestamp from current time in Python. +// +// timestamp = Timestamp() +// timestamp.GetCurrentTime() +// +// # JSON Mapping +// +// In JSON format, the Timestamp type is encoded as a string in the +// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the +// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" +// where {year} is always expressed using four digits while {month}, {day}, +// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional +// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), +// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone +// is required. A proto3 JSON serializer should always use UTC (as indicated by +// "Z") when printing the Timestamp type and a proto3 JSON parser should be +// able to accept both UTC and other timezones (as indicated by an offset). +// +// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past +// 01:30 UTC on January 15, 2017. +// +// In JavaScript, one can convert a Date object to this format using the +// standard +// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) +// method. In Python, a standard `datetime.datetime` object can be converted +// to this format using +// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with +// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use +// the Joda Time's [`ISODateTimeFormat.dateTime()`]( +// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D +// ) to obtain a formatter capable of generating timestamps in this format. +// +// +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} \ No newline at end of file diff --git a/components/livekit/protocol/timestamp.pb.c b/components/livekit/protocol/timestamp.pb.c new file mode 100644 index 0000000..980b9f5 --- /dev/null +++ b/components/livekit/protocol/timestamp.pb.c @@ -0,0 +1,12 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.9.1 */ + +#include "timestamp.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(GOOGLE_PROTOBUF_TIMESTAMP, google_protobuf_timestamp_t, AUTO) + + + diff --git a/components/livekit/protocol/timestamp.pb.h b/components/livekit/protocol/timestamp.pb.h new file mode 100644 index 0000000..dc426aa --- /dev/null +++ b/components/livekit/protocol/timestamp.pb.h @@ -0,0 +1,140 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.9.1 */ + +#ifndef PB_GOOGLE_PROTOBUF_TIMESTAMP_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_TIMESTAMP_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +/* A Timestamp represents a point in time independent of any time zone or local + calendar, encoded as a count of seconds and fractions of seconds at + nanosecond resolution. The count is relative to an epoch at UTC midnight on + January 1, 1970, in the proleptic Gregorian calendar which extends the + Gregorian calendar backwards to year one. + + All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap + second table is needed for interpretation, using a [24-hour linear + smear](https://developers.google.com/time/smear). + + The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By + restricting to that range, we ensure that we can convert to and from [RFC + 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. + + # Examples + + Example 1: Compute Timestamp from POSIX `time()`. + + Timestamp timestamp; + timestamp.set_seconds(time(NULL)); + timestamp.set_nanos(0); + + Example 2: Compute Timestamp from POSIX `gettimeofday()`. + + struct timeval tv; + gettimeofday(&tv, NULL); + + Timestamp timestamp; + timestamp.set_seconds(tv.tv_sec); + timestamp.set_nanos(tv.tv_usec * 1000); + + Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. + + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + + // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z + // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. + Timestamp timestamp; + timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); + timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); + + Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. + + long millis = System.currentTimeMillis(); + + Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) + .setNanos((int) ((millis % 1000) * 1000000)).build(); + + + Example 5: Compute Timestamp from current time in Python. + + timestamp = Timestamp() + timestamp.GetCurrentTime() + + # JSON Mapping + + In JSON format, the Timestamp type is encoded as a string in the + [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the + format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" + where {year} is always expressed using four digits while {month}, {day}, + {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional + seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), + are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone + is required. A proto3 JSON serializer should always use UTC (as indicated by + "Z") when printing the Timestamp type and a proto3 JSON parser should be + able to accept both UTC and other timezones (as indicated by an offset). + + For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past + 01:30 UTC on January 15, 2017. + + In JavaScript, one can convert a Date object to this format using the + standard + [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) + method. In Python, a standard `datetime.datetime` object can be converted + to this format using + [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with + the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use + the Joda Time's [`ISODateTimeFormat.dateTime()`]( + http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D + ) to obtain a formatter capable of generating timestamps in this format. */ +typedef struct google_protobuf_timestamp { + /* Represents seconds of UTC time since Unix epoch + 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + 9999-12-31T23:59:59Z inclusive. */ + int64_t seconds; + /* Non-negative fractions of a second at nanosecond resolution. Negative + second values with fractions must still have non-negative nanos values + that count forward in time. Must be from 0 to 999,999,999 + inclusive. */ + int32_t nanos; +} google_protobuf_timestamp_t; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define GOOGLE_PROTOBUF_TIMESTAMP_INIT_DEFAULT {0, 0} +#define GOOGLE_PROTOBUF_TIMESTAMP_INIT_ZERO {0, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define GOOGLE_PROTOBUF_TIMESTAMP_SECONDS_TAG 1 +#define GOOGLE_PROTOBUF_TIMESTAMP_NANOS_TAG 2 + +/* Struct field encoding specification for nanopb */ +#define GOOGLE_PROTOBUF_TIMESTAMP_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT64, seconds, 1) \ +X(a, STATIC, SINGULAR, INT32, nanos, 2) +#define GOOGLE_PROTOBUF_TIMESTAMP_CALLBACK NULL +#define GOOGLE_PROTOBUF_TIMESTAMP_DEFAULT NULL + +extern const pb_msgdesc_t google_protobuf_timestamp_t_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define GOOGLE_PROTOBUF_TIMESTAMP_FIELDS &google_protobuf_timestamp_t_msg + +/* Maximum encoded size of messages (where known) */ +#define GOOGLE_PROTOBUF_TIMESTAMP_PB_H_MAX_SIZE GOOGLE_PROTOBUF_TIMESTAMP_SIZE +#define GOOGLE_PROTOBUF_TIMESTAMP_SIZE 22 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/components/livekit/protocol/update/update.py b/components/livekit/protocol/update/update.py new file mode 100644 index 0000000..24350ba --- /dev/null +++ b/components/livekit/protocol/update/update.py @@ -0,0 +1,98 @@ +import tempfile +import urllib.request +import zipfile +import os +import shutil +import subprocess +import configparser + +required_files = ["livekit_rtc.proto", "livekit_models.proto", "livekit_metrics.proto"] +protobuf_location = "../protobufs" +bindings_src_dest = "../" + +def main(): + version = read_version() + with tempfile.TemporaryDirectory() as temp_dir: + repo_archive = download_archive(version, temp_dir) + unzip_file(repo_archive, temp_dir) + repo_root = os.path.join(temp_dir, f"protocol--livekit-protocol-{version}") + patch_proto_imports(repo_root) + copy_proto_definitions(repo_root, protobuf_location) + generate_bindings() + +def read_version(): + config = configparser.ConfigParser() + config.read("./version.ini") + return config["download"]["version"] + +def download_archive(version,dest): + file_name = f"protocol@{version}.zip" + url = f"https://github.com/livekit/protocol/archive/refs/tags/@livekit/{file_name}" + download_path = os.path.join(dest, file_name) + print("Downloading " + url) + urllib.request.urlretrieve(url, download_path) + return download_path + +def patch_proto_imports(repo_root): + replacements = { + 'import "google/protobuf/timestamp.proto";': 'import "timestamp.proto";' + # Add more replacements here if needed + } + for fname in required_files: + src = os.path.join(repo_root, "protobufs", fname) + try: + with open(src, "r") as f: + content = f.read() + modified = False + new_content = content + for old, new in replacements.items(): + if old in new_content: + new_content = new_content.replace(old, new) + modified = True + print(f"Patched {fname}") + if modified: + with open(src, "w") as f: + f.write(new_content) + except IOError as e: + print(f"Error processing {fname}: {e}") + +def copy_proto_definitions(repo_root, dest): + for fname in required_files: + print("Copying " + fname) + src = os.path.join(repo_root, "protobufs", fname) + shutil.copy2(src, dest) + +def unzip_file(srcfile, dest): + with zipfile.ZipFile(srcfile, 'r') as zip_ref: + zip_ref.extractall(dest) + +def generate_bindings(verbose = False): + protoc_path = shutil.which("protoc") + if not protoc_path: + raise RuntimeError("Please install the Protobuf compiler") + + input_files = ["timestamp.proto"] + required_files + protoc_cmd = [ + protoc_path, + *(["--nanopb_opt=-v"] if verbose else []), # Verbose output + "--nanopb_opt=--c-style", + "--nanopb_opt=--error-on-unmatched", + "--nanopb_opt=-s discard_deprecated:true", + "--nanopb_opt=-s package:'livekit_pb'", # Prefixes generated types + f"--nanopb_out={os.path.abspath(bindings_src_dest)}", + ] + input_files + + print(f"Generating bindings: {" ".join(protoc_cmd)}") + result = subprocess.run( + protoc_cmd, + capture_output=True, + text=True, + cwd=os.path.abspath(protobuf_location) + ) + if verbose: + print(result.stderr) + if result.returncode != 0: + raise RuntimeError(f"protoc failed: {result.stderr}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/components/livekit/protocol/update/version.ini b/components/livekit/protocol/update/version.ini new file mode 100644 index 0000000..2a644cd --- /dev/null +++ b/components/livekit/protocol/update/version.ini @@ -0,0 +1,3 @@ +# See https://github.com/livekit/protocol/releases +[download] +version = 1.39.2 \ No newline at end of file diff --git a/components/livekit_sandbox/CMakeLists.txt b/components/livekit_sandbox/CMakeLists.txt new file mode 100644 index 0000000..176c9d8 --- /dev/null +++ b/components/livekit_sandbox/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRC_DIRS ./src + INCLUDE_DIRS ./include + PRIV_REQUIRES json mbedtls esp_http_client +) \ No newline at end of file diff --git a/components/livekit_sandbox/idf_component.yml b/components/livekit_sandbox/idf_component.yml new file mode 100644 index 0000000..f7c43b6 --- /dev/null +++ b/components/livekit_sandbox/idf_component.yml @@ -0,0 +1,4 @@ +description: LiveKit Sandbox Token Generator +version: 0.1.0 +dependencies: + idf: ">=5.4" \ No newline at end of file diff --git a/components/livekit_sandbox/include/livekit_sandbox.h b/components/livekit_sandbox/include/livekit_sandbox.h new file mode 100644 index 0000000..62bf602 --- /dev/null +++ b/components/livekit_sandbox/include/livekit_sandbox.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// Request options for generating a sandbox token. +typedef struct { + /// The sandbox ID. + char *sandbox_id; + + /// The room name the generated token will have. + /// @note If not provided, one will be generated. + char *room_name; + + /// The participant identity the generated token will have. + /// @note If not provided, one will be generated. + char *participant_name; +} livekit_sandbox_options_t; + +/// Response containing the generated token details. +typedef struct { + /// The LiveKit Cloud URL for the associated project. + char *server_url; + + /// The access token for the participant. Valid for 15 minutes. + char *token; + + /// Generated token's room name. + char *room_name; + + /// Generated token's participant identity. + char *participant_name; +} livekit_sandbox_res_t; + +/// Generate a sandbox token. +/// @param options[in] Options for generating the token. +/// @param res[out] The result to store the token details in. +/// @return True if the token was generated successfully, false otherwise. +/// @note If successful, the result must be freed using livekit_sandbox_res_free. +bool livekit_sandbox_generate(const livekit_sandbox_options_t *options, livekit_sandbox_res_t* res); + +/// Frees a sandbox result. +void livekit_sandbox_res_free(livekit_sandbox_res_t *result); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/livekit_sandbox/src/livekit_sandbox.c b/components/livekit_sandbox/src/livekit_sandbox.c new file mode 100644 index 0000000..c2d117b --- /dev/null +++ b/components/livekit_sandbox/src/livekit_sandbox.c @@ -0,0 +1,181 @@ +#include "cJSON.h" +#include "esp_log.h" +#include "esp_http_client.h" +#include +#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE +#include "esp_crt_bundle.h" +#endif + +#include "livekit_sandbox.h" + +static const char *TAG = "livekit_sandbox"; +static const char *SANDBOX_URL = "http://cloud-api.livekit.io/api/sandbox/connection-details"; + +#define MAX_HTTP_OUTPUT_BUFFER 2048 + +static esp_err_t _http_event_handler(esp_http_client_event_t *evt) +{ + // TODO: This should probably be made non-static. + static int output_len = 0; + char* res_buffer = (char *) evt->user_data; + + switch (evt->event_id) { + case HTTP_EVENT_ERROR: + case HTTP_EVENT_ON_FINISH: + case HTTP_EVENT_DISCONNECTED: + output_len = 0; + break; + case HTTP_EVENT_ON_DATA: + assert(evt->user_data != NULL); + if (output_len == 0) { + memset(res_buffer, 0, MAX_HTTP_OUTPUT_BUFFER); + } + if (esp_http_client_is_chunked_response(evt->client)) break; + + int copy_len = MIN(evt->data_len, (MAX_HTTP_OUTPUT_BUFFER - output_len)); + if (copy_len > 0) { + memcpy(res_buffer + output_len, evt->data, copy_len); + } + output_len += copy_len; + break; + case HTTP_EVENT_REDIRECT: + esp_http_client_set_redirection(evt->client); + break; + default: + break; + } + return ESP_OK; +} + +bool livekit_sandbox_generate(const livekit_sandbox_options_t *options, livekit_sandbox_res_t *res) +{ + if (options == NULL || options->sandbox_id == NULL) { + ESP_LOGE(TAG, "Missing required options"); + return false; + } + + char* res_buffer = calloc(MAX_HTTP_OUTPUT_BUFFER + 1, sizeof(char)); + if (res_buffer == NULL) { + ESP_LOGE(TAG, "Failed to allocate response buffer"); + return false; + } + + // Create JSON payload + cJSON *json_payload = cJSON_CreateObject(); + if (json_payload == NULL) { + ESP_LOGE(TAG, "Failed to create JSON payload"); + free(res_buffer); + return false; + } + if (options->room_name != NULL) { + cJSON_AddStringToObject(json_payload, "roomName", options->room_name); + } + if (options->participant_name != NULL) { + cJSON_AddStringToObject(json_payload, "participantName", options->participant_name); + } + char *json_string = cJSON_Print(json_payload); + if (json_string == NULL) { + ESP_LOGE(TAG, "Failed to serialize JSON payload"); + cJSON_Delete(json_payload); + free(res_buffer); + return false; + } + + esp_http_client_config_t http_config = { + .url = SANDBOX_URL, + .method = HTTP_METHOD_POST, + .timeout_ms = 10000, + .event_handler = _http_event_handler, + .user_data = res_buffer, +#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE + .crt_bundle_attach = esp_crt_bundle_attach +#endif + }; + + esp_http_client_handle_t client = esp_http_client_init(&http_config); + if (client == NULL) { + ESP_LOGE(TAG, "Failed to create HTTP client"); + free(json_string); + cJSON_Delete(json_payload); + free(res_buffer); + return false; + } + + // Set headers and POST data + esp_http_client_set_header(client, "Content-Type", "application/json"); + esp_http_client_set_header(client, "X-Sandbox-ID", options->sandbox_id); + esp_http_client_set_post_field(client, json_string, strlen(json_string)); + + bool success = false; + cJSON *res_json = NULL; + do { + esp_err_t perform_err = esp_http_client_perform(client); + if (perform_err != ESP_OK) { + ESP_LOGE(TAG, "Request failed: %s", esp_err_to_name(perform_err)); + break; + } + + int status_code = esp_http_client_get_status_code(client); + if (status_code != 200) { + ESP_LOGE(TAG, "Request failed with status %d", status_code); + if (strlen(res_buffer) > 0) { + ESP_LOGE(TAG, "Server response: %s", res_buffer); + } + break; + } + + res_json = cJSON_Parse(res_buffer); + if (res_json == NULL) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + ESP_LOGE(TAG, "Failed to parse response: %s", error_ptr); + break; + } + + cJSON *server_url = cJSON_GetObjectItemCaseSensitive(res_json, "serverUrl"); + if (!cJSON_IsString(server_url) || (server_url->valuestring == NULL)) { + ESP_LOGE(TAG, "Missing server URL in response"); + break; + } + cJSON *token = cJSON_GetObjectItemCaseSensitive(res_json, "participantToken"); + if (!cJSON_IsString(token) || (token->valuestring == NULL)) { + ESP_LOGE(TAG, "Missing token in response"); + break; + } + cJSON *room_name_resp = cJSON_GetObjectItemCaseSensitive(res_json, "roomName"); + if (!cJSON_IsString(room_name_resp) || (room_name_resp->valuestring == NULL)) { + ESP_LOGE(TAG, "Missing room name in response"); + break; + } + cJSON *participant_name_resp = cJSON_GetObjectItemCaseSensitive(res_json, "participantName"); + if (!cJSON_IsString(participant_name_resp) || (participant_name_resp->valuestring == NULL)) { + ESP_LOGE(TAG, "Missing participant name in response"); + break; + } + + res->server_url = strdup(server_url->valuestring); + res->token = strdup(token->valuestring); + res->room_name = strdup(room_name_resp->valuestring); + res->participant_name = strdup(participant_name_resp->valuestring); + success = true; + + ESP_LOGI(TAG, "Generated sandbox token\nroom_name=%s\nparticipant_name=%s", + res->room_name, res->participant_name); + } while (0); + + esp_http_client_cleanup(client); + cJSON_Delete(res_json); + cJSON_Delete(json_payload); + free(json_string); + free(res_buffer); + return success; +} + +void livekit_sandbox_res_free(livekit_sandbox_res_t *result) +{ + if (result == NULL) return; + if (result->server_url) free(result->server_url); + if (result->token) free(result->token); + if (result->room_name) free(result->room_name); + if (result->participant_name) free(result->participant_name); +} \ No newline at end of file diff --git a/components/third_party/esp-webrtc-solution b/components/third_party/esp-webrtc-solution new file mode 160000 index 0000000..7c670a5 --- /dev/null +++ b/components/third_party/esp-webrtc-solution @@ -0,0 +1 @@ +Subproject commit 7c670a52aa4d47dea240ba3610c33286b480940e diff --git a/components/third_party/khash/CMakeLists.txt b/components/third_party/khash/CMakeLists.txt new file mode 100644 index 0000000..e82a46b --- /dev/null +++ b/components/third_party/khash/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(INCLUDE_DIRS "include") \ No newline at end of file diff --git a/components/third_party/khash/LICENSE.txt b/components/third_party/khash/LICENSE.txt new file mode 100644 index 0000000..2de13e4 --- /dev/null +++ b/components/third_party/khash/LICENSE.txt @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2008- Attractive Chaos + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/components/third_party/khash/idf_component.yml b/components/third_party/khash/idf_component.yml new file mode 100644 index 0000000..38f7dbd --- /dev/null +++ b/components/third_party/khash/idf_component.yml @@ -0,0 +1,3 @@ +description: Khash (from klib) +repository: https://github.com/attractivechaos/klib +license: MIT \ No newline at end of file diff --git a/components/third_party/khash/include/khash.h b/components/third_party/khash/include/khash.h new file mode 100644 index 0000000..995e201 --- /dev/null +++ b/components/third_party/khash/include/khash.h @@ -0,0 +1,627 @@ +/* The MIT License + + Copyright (c) 2008, 2009, 2011 by Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + An example: + +#include "khash.h" +KHASH_MAP_INIT_INT(32, char) +int main() { + int ret, is_missing; + khiter_t k; + khash_t(32) *h = kh_init(32); + k = kh_put(32, h, 5, &ret); + kh_value(h, k) = 10; + k = kh_get(32, h, 10); + is_missing = (k == kh_end(h)); + k = kh_get(32, h, 5); + kh_del(32, h, k); + for (k = kh_begin(h); k != kh_end(h); ++k) + if (kh_exist(h, k)) kh_value(h, k) = 1; + kh_destroy(32, h); + return 0; +} +*/ + +/* + 2013-05-02 (0.2.8): + + * Use quadratic probing. When the capacity is power of 2, stepping function + i*(i+1)/2 guarantees to traverse each bucket. It is better than double + hashing on cache performance and is more robust than linear probing. + + In theory, double hashing should be more robust than quadratic probing. + However, my implementation is probably not for large hash tables, because + the second hash function is closely tied to the first hash function, + which reduce the effectiveness of double hashing. + + Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php + + 2011-12-29 (0.2.7): + + * Minor code clean up; no actual effect. + + 2011-09-16 (0.2.6): + + * The capacity is a power of 2. This seems to dramatically improve the + speed for simple keys. Thank Zilong Tan for the suggestion. Reference: + + - http://code.google.com/p/ulib/ + - http://nothings.org/computer/judy/ + + * Allow to optionally use linear probing which usually has better + performance for random input. Double hashing is still the default as it + is more robust to certain non-random input. + + * Added Wang's integer hash function (not used by default). This hash + function is more robust to certain non-random input. + + 2011-02-14 (0.2.5): + + * Allow to declare global functions. + + 2009-09-26 (0.2.4): + + * Improve portability + + 2008-09-19 (0.2.3): + + * Corrected the example + * Improved interfaces + + 2008-09-11 (0.2.2): + + * Improved speed a little in kh_put() + + 2008-09-10 (0.2.1): + + * Added kh_clear() + * Fixed a compiling error + + 2008-09-02 (0.2.0): + + * Changed to token concatenation which increases flexibility. + + 2008-08-31 (0.1.2): + + * Fixed a bug in kh_get(), which has not been tested previously. + + 2008-08-31 (0.1.1): + + * Added destructor +*/ + + +#ifndef __AC_KHASH_H +#define __AC_KHASH_H + +/*! + @header + + Generic hash table library. + */ + +#define AC_VERSION_KHASH_H "0.2.8" + +#include +#include +#include + +/* compiler specific configuration */ + +#if UINT_MAX == 0xffffffffu +typedef unsigned int khint32_t; +#elif ULONG_MAX == 0xffffffffu +typedef unsigned long khint32_t; +#endif + +#if ULONG_MAX == ULLONG_MAX +typedef unsigned long khint64_t; +#else +typedef unsigned long long khint64_t; +#endif + +#ifndef kh_inline +#ifdef _MSC_VER +#define kh_inline __inline +#else +#define kh_inline inline +#endif +#endif /* kh_inline */ + +#ifndef klib_unused +#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3) +#define klib_unused __attribute__ ((__unused__)) +#else +#define klib_unused +#endif +#endif /* klib_unused */ + +typedef khint32_t khint_t; +typedef khint_t khiter_t; + +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) + +#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) + +#ifndef kroundup32 +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#endif + +#ifndef kcalloc +#define kcalloc(N,Z) calloc(N,Z) +#endif +#ifndef kmalloc +#define kmalloc(Z) malloc(Z) +#endif +#ifndef krealloc +#define krealloc(P,Z) realloc(P,Z) +#endif +#ifndef kfree +#define kfree(P) free(P) +#endif + +static const double __ac_HASH_UPPER = 0.77; + +#define __KHASH_TYPE(name, khkey_t, khval_t) \ + typedef struct kh_##name##_s { \ + khint_t n_buckets, size, n_occupied, upper_bound; \ + khint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; + +#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ + extern kh_##name##_t *kh_init_##name(void); \ + extern void kh_destroy_##name(kh_##name##_t *h); \ + extern void kh_clear_##name(kh_##name##_t *h); \ + extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ + extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ + extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ + extern void kh_del_##name(kh_##name##_t *h, khint_t x); + +#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + SCOPE kh_##name##_t *kh_init_##name(void) { \ + return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ + } \ + SCOPE void kh_destroy_##name(kh_##name##_t *h) \ + { \ + if (h) { \ + kfree((void *)h->keys); kfree(h->flags); \ + kfree((void *)h->vals); \ + kfree(h); \ + } \ + } \ + SCOPE void kh_clear_##name(kh_##name##_t *h) \ + { \ + if (h && h->flags) { \ + memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ + { \ + if (h->n_buckets) { \ + khint_t k, i, last, mask, step = 0; \ + mask = h->n_buckets - 1; \ + k = __hash_func(key); i = k & mask; \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + i = (i + (++step)) & mask; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i)? h->n_buckets : i; \ + } else return 0; \ + } \ + SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ + khint32_t *new_flags = 0; \ + khint_t j = 1; \ + { \ + kroundup32(new_n_buckets); \ + if (new_n_buckets < 4) new_n_buckets = 4; \ + if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \ + else { /* hash table size to be changed (shrink or expand); rehash */ \ + new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (!new_flags) return -1; \ + memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (h->n_buckets < new_n_buckets) { /* expand */ \ + khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (!new_keys) { kfree(new_flags); return -1; } \ + h->keys = new_keys; \ + if (kh_is_map) { \ + khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + if (!new_vals) { kfree(new_flags); return -1; } \ + h->vals = new_vals; \ + } \ + } /* otherwise shrink */ \ + } \ + } \ + if (j) { /* rehashing is needed */ \ + for (j = 0; j != h->n_buckets; ++j) { \ + if (__ac_iseither(h->flags, j) == 0) { \ + khkey_t key = h->keys[j]; \ + khval_t val; \ + khint_t new_mask; \ + new_mask = new_n_buckets - 1; \ + if (kh_is_map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ + khint_t k, i, step = 0; \ + k = __hash_func(key); \ + i = k & new_mask; \ + while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ + { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ + if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ + __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ + } else { /* write the element and jump out of the loop */ \ + h->keys[i] = key; \ + if (kh_is_map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ + h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + kfree(h->flags); /* free the working space */ \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ + } \ + return 0; \ + } \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + { \ + khint_t x; \ + if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ + if (h->n_buckets > (h->size<<1)) { \ + if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ + *ret = -1; return h->n_buckets; \ + } \ + } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ + *ret = -1; return h->n_buckets; \ + } \ + } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ + { \ + khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ + x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ + if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ + else { \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (__ac_isdel(h->flags, i)) site = i; \ + i = (i + (++step)) & mask; \ + if (i == last) { x = site; break; } \ + } \ + if (x == h->n_buckets) { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ + else x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) { /* not present at all */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) { /* deleted */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ + return x; \ + } \ + SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +#define KHASH_DECLARE(name, khkey_t, khval_t) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_PROTOTYPES(name, khkey_t, khval_t) + +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +/*! @function + @abstract Integer hash function + @param key The integer [khint32_t] + @return The hash value [khint_t] + */ +#define kh_int_hash_func(key) (khint32_t)(key) +/*! @function + @abstract Integer comparison function + */ +#define kh_int_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract 64-bit integer hash function + @param key The integer [khint64_t] + @return The hash value [khint_t] + */ +#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) +/*! @function + @abstract 64-bit integer comparison function + */ +#define kh_int64_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract const char* hash function + @param s Pointer to a null terminated string + @return The hash value + */ +static kh_inline khint_t __ac_X31_hash_string(const char *s) +{ + khint_t h = (khint_t)*s; + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; + return h; +} +/*! @function + @abstract Another interface to const char* hash function + @param key Pointer to a null terminated string [const char*] + @return The hash value [khint_t] + */ +#define kh_str_hash_func(key) __ac_X31_hash_string(key) +/*! @function + @abstract Const char* comparison function + */ +#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) + +static kh_inline khint_t __ac_Wang_hash(khint_t key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} +#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other convenient macros... */ + +/*! + @abstract Type of the hash table. + @param name Name of the hash table [symbol] + */ +#define khash_t(name) kh_##name##_t + +/*! @function + @abstract Initiate a hash table. + @param name Name of the hash table [symbol] + @return Pointer to the hash table [khash_t(name)*] + */ +#define kh_init(name) kh_init_##name() + +/*! @function + @abstract Destroy a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_destroy(name, h) kh_destroy_##name(h) + +/*! @function + @abstract Reset a hash table without deallocating memory. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_clear(name, h) kh_clear_##name(h) + +/*! @function + @abstract Resize a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param s New size [khint_t] + */ +#define kh_resize(name, h, s) kh_resize_##name(h, s) + +/*! @function + @abstract Insert a key to the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @param r Extra return code: -1 if the operation failed; + 0 if the key is present in the hash table; + 1 if the bucket is empty (never used); 2 if the element in + the bucket has been deleted [int*] + @return Iterator to the inserted element [khint_t] + */ +#define kh_put(name, h, k, r) kh_put_##name(h, k, r) + +/*! @function + @abstract Retrieve a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] + */ +#define kh_get(name, h, k) kh_get_##name(h, k) + +/*! @function + @abstract Remove a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Iterator to the element to be deleted [khint_t] + */ +#define kh_del(name, h, k) kh_del_##name(h, k) + +/*! @function + @abstract Test whether a bucket contains data. + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return 1 if containing data; 0 otherwise [int] + */ +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) + +/*! @function + @abstract Get key given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Key [type of keys] + */ +#define kh_key(h, x) ((h)->keys[x]) + +/*! @function + @abstract Get value given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Value [type of values] + @discussion For hash sets, calling this results in segfault. + */ +#define kh_val(h, x) ((h)->vals[x]) + +/*! @function + @abstract Alias of kh_val() + */ +#define kh_value(h, x) ((h)->vals[x]) + +/*! @function + @abstract Get the start iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The start iterator [khint_t] + */ +#define kh_begin(h) (khint_t)(0) + +/*! @function + @abstract Get the end iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The end iterator [khint_t] + */ +#define kh_end(h) ((h)->n_buckets) + +/*! @function + @abstract Get the number of elements in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of elements in the hash table [khint_t] + */ +#define kh_size(h) ((h)->size) + +/*! @function + @abstract Get the number of buckets in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of buckets in the hash table [khint_t] + */ +#define kh_n_buckets(h) ((h)->n_buckets) + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_value(h, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/* More convenient interfaces */ + +/*! @function + @abstract Instantiate a hash set containing integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT(name) \ + KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT(name, khval_t) \ + KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash set containing 64-bit integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT64(name) \ + KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT64(name, khval_t) \ + KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef const char *kh_cstr_t; +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_STR(name) \ + KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_STR(name, khval_t) \ + KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#endif /* __AC_KHASH_H */ \ No newline at end of file diff --git a/components/third_party/nanopb/CMakeLists.txt b/components/third_party/nanopb/CMakeLists.txt new file mode 100644 index 0000000..8959b3a --- /dev/null +++ b/components/third_party/nanopb/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register(SRC_DIRS ./src + INCLUDE_DIRS ./include) + +target_compile_definitions(${COMPONENT_LIB} PRIVATE PB_BUFFER_ONLY=1) +target_compile_definitions(${COMPONENT_LIB} PRIVATE PB_VALIDATE_UTF8=1) +target_compile_definitions(${COMPONENT_LIB} PRIVATE PB_ENABLE_MALLOC=1) \ No newline at end of file diff --git a/components/third_party/nanopb/LICENSE.txt b/components/third_party/nanopb/LICENSE.txt new file mode 100644 index 0000000..d11c9af --- /dev/null +++ b/components/third_party/nanopb/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2011 Petteri Aimonen + +This software is provided 'as-is', without any express or +implied warranty. In no event will the authors be held liable +for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. diff --git a/components/third_party/nanopb/idf_component.yml b/components/third_party/nanopb/idf_component.yml new file mode 100644 index 0000000..679cc09 --- /dev/null +++ b/components/third_party/nanopb/idf_component.yml @@ -0,0 +1,6 @@ +description: Nanopb +url: https://jpa.kapsi.fi/nanopb/ +repository: https://github.com/nanopb/nanopb/ +documentation: https://jpa.kapsi.fi/nanopb/docs/ +version: 0.4.9 +license: Zlib \ No newline at end of file diff --git a/components/third_party/nanopb/include/pb.h b/components/third_party/nanopb/include/pb.h new file mode 100644 index 0000000..372f55f --- /dev/null +++ b/components/third_party/nanopb/include/pb.h @@ -0,0 +1,942 @@ +/* Common parts of the nanopb library. Most of these are quite low-level + * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. + */ + +#ifndef PB_H_INCLUDED +#define PB_H_INCLUDED + +/***************************************************************** + * Nanopb compilation time options. You can change these here by * + * uncommenting the lines, or on the compiler command line. * + *****************************************************************/ + +/* Enable support for dynamically allocated fields */ +/* #define PB_ENABLE_MALLOC 1 */ + +/* Define this if your CPU / compiler combination does not support + * unaligned memory access to packed structures. Note that packed + * structures are only used when requested in .proto options. */ +/* #define PB_NO_PACKED_STRUCTS 1 */ + +/* Increase the number of required fields that are tracked. + * A compiler warning will tell if you need this. */ +/* #define PB_MAX_REQUIRED_FIELDS 256 */ + +/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ +/* #define PB_FIELD_32BIT 1 */ + +/* Disable support for error messages in order to save some code space. */ +/* #define PB_NO_ERRMSG 1 */ + +/* Disable checks to ensure sub-message encoded size is consistent when re-run. */ +/* #define PB_NO_ENCODE_SIZE_CHECK 1 */ + +/* Disable support for custom streams (support only memory buffers). */ +/* #define PB_BUFFER_ONLY 1 */ + +/* Disable support for 64-bit datatypes, for compilers without int64_t + or to save some code space. */ +/* #define PB_WITHOUT_64BIT 1 */ + +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + +/* Enable conversion of doubles to floats for platforms that do not + * support 64-bit doubles. Most commonly AVR. */ +/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ + +/* Check whether incoming strings are valid UTF-8 sequences. Slows down + * the string processing slightly and slightly increases code size. */ +/* #define PB_VALIDATE_UTF8 1 */ + +/* This can be defined if the platform is little-endian and has 8-bit bytes. + * Normally it is automatically detected based on __BYTE_ORDER__ macro. */ +/* #define PB_LITTLE_ENDIAN_8BIT 1 */ + +/* Configure static assert mechanism. Instead of changing these, set your + * compiler to C11 standard mode if possible. */ +/* #define PB_C99_STATIC_ASSERT 1 */ +/* #define PB_NO_STATIC_ASSERT 1 */ + +/****************************************************************** + * You usually don't need to change anything below this line. * + * Feel free to look around and use the defined macros, though. * + ******************************************************************/ + + +/* Version of the nanopb library. Just in case you want to check it in + * your own program. */ +#define NANOPB_VERSION "nanopb-1.0.0-dev" + +/* Include all the system headers needed by nanopb. You will need the + * definitions of the following: + * - strlen, memcpy, memset functions + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t + * - size_t + * - bool + * + * If you don't have the standard header files, you can instead provide + * a custom header that defines or includes all this. In that case, + * define PB_SYSTEM_HEADER to the path of this file. + */ +#ifdef PB_SYSTEM_HEADER +#include PB_SYSTEM_HEADER +#else +#include +#include +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro for defining packed structures (compiler dependent). + * This just reduces memory requirements, but is not required. + */ +#if defined(PB_NO_PACKED_STRUCTS) + /* Disable struct packing */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#elif defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") +# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") +# define pb_packed +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) + /* For Microsoft Visual C++ */ +# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) +# define PB_PACKED_STRUCT_END __pragma(pack(pop)) +# define pb_packed +#else + /* Unknown compiler */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#endif + +/* Define for explicitly not inlining a given function */ +#if defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define pb_noinline __attribute__((noinline)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define pb_noinline +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) +# define pb_noinline __declspec(noinline) +#else +# define pb_noinline +#endif + +/* Detect endianness */ +#if !defined(CHAR_BIT) && defined(__CHAR_BIT__) +#define CHAR_BIT __CHAR_BIT__ +#endif + +#ifndef PB_LITTLE_ENDIAN_8BIT +#if ((defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || \ + defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || \ + defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)) \ + && defined(CHAR_BIT) && CHAR_BIT == 8 +#define PB_LITTLE_ENDIAN_8BIT 1 +#endif +#endif + +/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ +#ifndef PB_UNUSED +#define PB_UNUSED(x) (void)(x) +#endif + +/* Harvard-architecture processors may need special attributes for storing + * field information in program memory. */ +#ifndef PB_PROGMEM +#ifdef __AVR__ +#include +#define PB_PROGMEM PROGMEM +#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) +#else +#define PB_PROGMEM +#define PB_PROGMEM_READU32(x) (x) +#endif +#endif + +/* Compile-time assertion, used for checking compatible compilation options. + * If this does not work properly on your compiler, use + * #define PB_NO_STATIC_ASSERT to disable it. + * + * But before doing that, check carefully the error message / place where it + * comes from to see if the error has a real cause. Unfortunately the error + * message is not always very clear to read, but you can see the reason better + * in the place where the PB_STATIC_ASSERT macro was called. + */ +#ifndef PB_NO_STATIC_ASSERT +# ifndef PB_STATIC_ASSERT +# if defined(__ICCARM__) + /* IAR has static_assert keyword but no _Static_assert */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(_MSC_VER) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) + /* MSVC in C89 mode supports static_assert() keyword anyway */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# elif defined(PB_C99_STATIC_ASSERT) + /* Classic negative-size-array static assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; +# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) +# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER +# elif defined(__cplusplus) + /* C++11 standard static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) static_assert(COND,#MSG); +# else + /* C11 standard _Static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); +# endif +# endif +#else + /* Static asserts disabled by PB_NO_STATIC_ASSERT */ +# define PB_STATIC_ASSERT(COND,MSG) +#endif + +/* Test that PB_STATIC_ASSERT works + * If you get errors here, you may need to do one of these: + * - Enable C11 standard support in your compiler + * - Define PB_C99_STATIC_ASSERT to enable C99 standard support + * - Define PB_NO_STATIC_ASSERT to disable static asserts altogether + */ +PB_STATIC_ASSERT(1, STATIC_ASSERT_IS_NOT_WORKING) + +/* Number of required fields to keep track of. */ +#ifndef PB_MAX_REQUIRED_FIELDS +#define PB_MAX_REQUIRED_FIELDS 64 +#endif + +#if PB_MAX_REQUIRED_FIELDS < 64 +#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). +#endif + +#ifdef PB_WITHOUT_64BIT +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Cannot use doubles without 64-bit types */ +#undef PB_CONVERT_DOUBLE_FLOAT +#endif +#endif + +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +#if defined(PB_BYTE_T_OVERRIDE) +typedef PB_BYTE_T_OVERRIDE pb_byte_t; +#elif defined(UINT8_MAX) +typedef uint8_t pb_byte_t; +#else +typedef uint_least8_t pb_byte_t; +#endif + +/* List of possible field types. These are used in the autogenerated code. + * Least-significant 4 bits tell the scalar type + * Most-significant 4 bits specify repeated/required/packed etc. + */ +typedef pb_byte_t pb_type_t; + +/**** Field data types ****/ + +/* Numeric types */ +#define PB_LTYPE_BOOL 0x00U /* bool */ +#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x05U + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x06U + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x07U + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x08U + +/* Submessage with pre-decoding callback + * The pre-decoding callback is stored as pb_callback_t right before pSize. + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMSG_W_CB 0x09U + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x0AU + +/* Byte array with inline, pre-allocated byffer. + * data_size is the length of the inline, allocated buffer. + * This differs from PB_LTYPE_BYTES by defining the element as + * pb_byte_t[data_size] rather than pb_bytes_array_t. */ +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 0x0CU +#define PB_LTYPE_MASK 0x0FU + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00U +#define PB_HTYPE_OPTIONAL 0x10U +#define PB_HTYPE_SINGULAR 0x10U +#define PB_HTYPE_REPEATED 0x20U +#define PB_HTYPE_FIXARRAY 0x20U +#define PB_HTYPE_ONEOF 0x30U +#define PB_HTYPE_MASK 0x30U + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00U +#define PB_ATYPE_POINTER 0x80U +#define PB_ATYPE_CALLBACK 0x40U +#define PB_ATYPE_MASK 0xC0U + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) +#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ + PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) + +/* Data type used for storing sizes of struct fields + * and array counts. + */ +#if defined(PB_FIELD_32BIT) + typedef uint32_t pb_size_t; + typedef int32_t pb_ssize_t; +#else + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; +#endif +#define PB_SIZE_MAX ((pb_size_t)-1) + +/* Forward declaration of struct types */ +typedef struct pb_istream_s pb_istream_t; +typedef struct pb_ostream_s pb_ostream_t; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* This structure is used in auto-generated constants + * to specify struct fields. + */ +typedef struct pb_msgdesc_s pb_msgdesc_t; +struct pb_msgdesc_s { + const uint32_t *field_info; + const pb_msgdesc_t * const * submsg_info; + const pb_byte_t *default_value; + + bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); + + pb_size_t field_count; + pb_size_t required_field_count; + pb_size_t largest_tag; +}; + +/* Iterator for message descriptor */ +struct pb_field_iter_s { + const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ + void *message; /* Pointer to start of the structure */ + + pb_size_t index; /* Index of the field */ + pb_size_t field_info_index; /* Index to descriptor->field_info array */ + pb_size_t required_field_index; /* Index that counts only the required fields */ + pb_size_t submessage_index; /* Index that counts only submessages */ + + pb_size_t tag; /* Tag of current field */ + pb_size_t data_size; /* sizeof() of a single item */ + pb_size_t array_size; /* Number of array entries */ + pb_type_t type; /* Type of current field */ + + void *pField; /* Pointer to current field in struct */ + void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ + void *pSize; /* Pointer to count/has field */ + + const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ +}; + +/* For compatibility with legacy code */ +typedef pb_field_iter_t pb_field_t; + +/* Make sure that the standard integer types are of the expected sizes. + * Otherwise fixed32/fixed64 fields can break. + * + * If you get errors here, it probably means that your stdint.h is not + * correct for your platform. + */ +#ifndef PB_WITHOUT_64BIT +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) +#endif + +/* This structure is used for 'bytes' arrays. + * It has the number of bytes in the beginning, and after that an array. + * Note that actual structs used will have a different length of bytes array. + */ +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } +#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) + +struct pb_bytes_array_s { + pb_size_t size; + pb_byte_t bytes[1]; +}; +typedef struct pb_bytes_array_s pb_bytes_array_t; + +/* This structure is used for giving the callback function. + * It is stored in the message structure and filled in by the method that + * calls pb_decode. + * + * The decoding callback will be given a limited-length stream + * If the wire type was string, the length is the length of the string. + * If the wire type was a varint/fixed32/fixed64, the length is the length + * of the actual value. + * The function may be called multiple times (especially for repeated types, + * but also otherwise if the message happens to contain the field multiple + * times.) + * + * The encoding callback will receive the actual output stream. + * It should write all the data in one call, including the field tag and + * wire type. It can write multiple fields. + * + * The callback can be null if you want to skip a field. + */ +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { + /* Callback functions receive a pointer to the arg field. + * You can access the value of the field as *arg, and modify it if needed. + */ + union { + bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); + bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); + } funcs; + + /* Free arg for use by callback */ + void *arg; +}; + +extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); + +/* Wire types. Library user needs these only in encoder callbacks. */ +typedef enum { + PB_WT_VARINT = 0, + PB_WT_64BIT = 1, + PB_WT_STRING = 2, + PB_WT_32BIT = 5, + PB_WT_PACKED = 255 /* PB_WT_PACKED is internal marker for packed arrays. */ +} pb_wire_type_t; + +/* Structure for defining the handling of unknown/extension fields. + * Usually the pb_extension_type_t structure is automatically generated, + * while the pb_extension_t structure is created by the user. However, + * if you want to catch all unknown fields, you can also create a custom + * pb_extension_type_t with your own callback. + */ +typedef struct pb_extension_type_s pb_extension_type_t; +typedef struct pb_extension_s pb_extension_t; +struct pb_extension_type_s { + /* Called for each unknown field in the message. + * If you handle the field, read off all of its data and return true. + * If you do not handle the field, do not read anything and return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, + uint32_t tag, pb_wire_type_t wire_type); + + /* Called once after all regular fields have been encoded. + * If you have something to write, do so and return true. + * If you do not have anything to write, just return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); + + /* Free field for use by the callback. */ + const void *arg; +}; + +struct pb_extension_s { + /* Type describing the extension field. Usually you'll initialize + * this to a pointer to the automatically generated structure. */ + const pb_extension_type_t *type; + + /* Destination for the decoded data. This must match the datatype + * of the extension field. */ + void *dest; + + /* Pointer to the next extension handler, or NULL. + * If this extension does not match a field, the next handler is + * automatically called. */ + pb_extension_t *next; + + /* The decoder sets this to true if the extension was found. + * Ignored for encoding. */ + bool found; +}; + +#define pb_extension_init_zero {NULL,NULL,NULL,false} + +/* Memory allocation functions to use. You can define pb_realloc and + * pb_free to custom functions if you want. */ +#ifdef PB_ENABLE_MALLOC +# ifndef pb_realloc +# define pb_realloc(ptr, size) realloc(ptr, size) +# endif +# ifndef pb_free +# define pb_free(ptr) free(ptr) +# endif +#endif + +/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ +#define PB_PROTO_HEADER_VERSION 40 + +/* These macros are used to declare pb_field_t's in the constant array. */ +/* Size of a structure member, in bytes. */ +#define pb_membersize(st, m) (sizeof ((st*)0)->m) +/* Number of entries in an array. */ +#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) +/* Delta from start of one member to the start of another member. */ +#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) + +/* Force expansion of macro value */ +#define PB_EXPAND(x) x + +/* Binding of a message field set into a specific structure */ +#define PB_BIND(msgname, structname, width) \ + const uint32_t structname ## _field_info[] PB_PROGMEM = \ + { \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ + 0 \ + }; \ + const pb_msgdesc_t* const structname ## _submsg_info[] = \ + { \ + msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ + NULL \ + }; \ + const pb_msgdesc_t structname ## _msg = \ + { \ + structname ## _field_info, \ + structname ## _submsg_info, \ + msgname ## _DEFAULT, \ + msgname ## _CALLBACK, \ + 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ + }; \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) + +#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 +#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) +#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ + * 0 + tag + +/* X-macro for generating the entries in struct_field_info[] array. */ +#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +/* X-macro for generating asserts that entries fit in struct_field_info[] array. + * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), + * but it is not easily reused because of how macro substitutions work. */ +#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) + +#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) +#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) +#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) +#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) +#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) +#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) +#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 + +#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) + +#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) +#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) +#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) +#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) +#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) +#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) + +#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) +#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname +#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername +#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname + +#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ + PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) + +#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) +#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) +#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) +#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SI_PB_LTYPE_BOOL(t) +#define PB_SI_PB_LTYPE_BYTES(t) +#define PB_SI_PB_LTYPE_DOUBLE(t) +#define PB_SI_PB_LTYPE_ENUM(t) +#define PB_SI_PB_LTYPE_UENUM(t) +#define PB_SI_PB_LTYPE_FIXED32(t) +#define PB_SI_PB_LTYPE_FIXED64(t) +#define PB_SI_PB_LTYPE_FLOAT(t) +#define PB_SI_PB_LTYPE_INT32(t) +#define PB_SI_PB_LTYPE_INT64(t) +#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_SFIXED32(t) +#define PB_SI_PB_LTYPE_SFIXED64(t) +#define PB_SI_PB_LTYPE_SINT32(t) +#define PB_SI_PB_LTYPE_SINT64(t) +#define PB_SI_PB_LTYPE_STRING(t) +#define PB_SI_PB_LTYPE_UINT32(t) +#define PB_SI_PB_LTYPE_UINT64(t) +#define PB_SI_PB_LTYPE_EXTENSION(t) +#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) +#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), + +/* The field descriptors use a variable width format, with width of either + * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always + * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits + * of the field type. + * + * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. + * + * Formats, listed starting with the least significant bit of the first word. + * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] + * + * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] + * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] + * + * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * + * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * [32-bit array_size] + * [32-bit reserved] + * [32-bit reserved] + * [32-bit reserved] + */ + +#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ + (0 | (((uint32_t)(tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ + (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), + +#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ + (1 | (((uint32_t)(tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ + (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), + +#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ + (2 | (((uint32_t)(tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), + +#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ + (3 | (((uint32_t)(tag) << 2) & 0xFF) | ((type) << 8)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), (array_size), 0, 0, 0, + +/* These assertions verify that the field information fits in the allocated space. + * The generator tries to automatically determine the correct width that can fit all + * data associated with a message. These asserts will fail only if there has been a + * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, + * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting + * descriptorsize option in .options file. + */ +#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. + */ +#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) + +#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) +#endif + + +/* Automatic picking of FIELDINFO width: + * Uses width 1 when possible, otherwise resorts to width 2. + * This is used when PB_BIND() is called with "AUTO" as the argument. + * The generator will give explicit size argument when it knows that a message + * structure grows beyond 1-word format limits. + */ +#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) +#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 +#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 +#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 +#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 +#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 +#define PB_FI_WIDTH_PB_LTYPE_INT32 1 +#define PB_FI_WIDTH_PB_LTYPE_INT64 1 +#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 +#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_STRING 2 +#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL +#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES +#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT +#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE +#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB +#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING +#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION +#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES + +/* These macros are used for giving out error messages. + * They are mostly a debugging aid; the main error information + * is the true/false return value from functions. + * Some code space can be saved by disabling the error + * messages if not used. + * + * PB_SET_ERROR() sets the error message if none has been set yet. + * msg must be a constant string literal. + * PB_GET_ERROR() always returns a pointer to a string. + * PB_RETURN_ERROR() sets the error and returns false from current + * function. + */ +#ifdef PB_NO_ERRMSG +#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) +#define PB_GET_ERROR(stream) "(errmsg disabled)" +#else +#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) +#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") +#endif + +#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +#if __cplusplus >= 201103L +#define PB_CONSTEXPR constexpr +#else // __cplusplus >= 201103L +#define PB_CONSTEXPR +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR inline constexpr +#else // __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR PB_CONSTEXPR +#endif // __cplusplus >= 201703L + +extern "C++" +{ +namespace nanopb { +// Each type will be partially specialized by the generator. +template struct MessageDescriptor; +} // namespace nanopb +} +#endif /* __cplusplus */ + +#endif diff --git a/components/third_party/nanopb/include/pb_common.h b/components/third_party/nanopb/include/pb_common.h new file mode 100644 index 0000000..58aa90f --- /dev/null +++ b/components/third_party/nanopb/include/pb_common.h @@ -0,0 +1,49 @@ +/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. + * These functions are rarely needed by applications directly. + */ + +#ifndef PB_COMMON_H_INCLUDED +#define PB_COMMON_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize the field iterator structure to beginning. + * Returns false if the message type is empty. */ +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); + +/* Get a field iterator for extension field. */ +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); + +/* Same as pb_field_iter_begin(), but for const message pointer. + * Note that the pointers in pb_field_iter_t will be non-const but shouldn't + * be written to when using these functions. */ +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); + +/* Advance the iterator to the next field. + * Returns false when the iterator wraps back to the first field. */ +bool pb_field_iter_next(pb_field_iter_t *iter); + +/* Advance the iterator until it points at a field with the given tag. + * Returns false if no such field exists. */ +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); + +/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. + * There can be only one extension range field per message. */ +bool pb_field_iter_find_extension(pb_field_iter_t *iter); + +#ifdef PB_VALIDATE_UTF8 +/* Validate UTF-8 text string */ +bool pb_validate_utf8(const char *s); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/components/third_party/nanopb/include/pb_decode.h b/components/third_party/nanopb/include/pb_decode.h new file mode 100644 index 0000000..3f392b2 --- /dev/null +++ b/components/third_party/nanopb/include/pb_decode.h @@ -0,0 +1,204 @@ +/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. + * The main function is pb_decode. You also need an input stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_DECODE_H_INCLUDED +#define PB_DECODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom input streams. You will need to provide + * a callback function to read the bytes from your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause decoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer), + * and rely on pb_read to verify that no-body reads past bytes_left. + * 3) Your callback may be used with substreams, in which case bytes_left + * is different than from the main stream. Don't use bytes_left to compute + * any pointers. + */ +struct pb_istream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + */ + int *callback; +#else + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_istream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Maximum number of bytes left in this stream. Callback can report + * EOF before this limit is reached. Setting a limit is recommended + * when decoding directly from file or network streams to avoid + * denial-of-service by excessively long messages. + */ + size_t bytes_left; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +#ifndef PB_NO_ERRMSG +#define PB_ISTREAM_EMPTY {0,0,0,0} +#else +#define PB_ISTREAM_EMPTY {0,0,0} +#endif + +/*************************** + * Main decoding functions * + ***************************/ + +/* Decode a single protocol buffers message from input stream into a C structure. + * Returns true on success, false on any failure. + * The actual struct pointed to by dest must match the description in fields. + * Callback fields of the destination structure must be initialized by caller. + * All other fields will be initialized by this function. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_istream_t stream; + * + * // ... read some data into buffer ... + * + * stream = pb_istream_from_buffer(buffer, count); + * pb_decode(&stream, MyMessage_fields, &msg); + */ +bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); + +/* Extended version of pb_decode, with several options to control + * the decoding process: + * + * PB_DECODE_NOINIT: Do not initialize the fields to default values. + * This is slightly faster if you do not need the default + * values and instead initialize the structure to 0 using + * e.g. memset(). This can also be used for merging two + * messages, i.e. combine already existing data with new + * values. + * + * PB_DECODE_DELIMITED: Input message starts with the message size as varint. + * Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + * + * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows + * reading null terminated messages. + * NOTE: Until nanopb-0.4.0, pb_decode() also allows + * null-termination. This behaviour is not supported in + * most other protobuf implementations, so PB_DECODE_DELIMITED + * is a better option for compatibility. + * + * Multiple flags can be combined with bitwise or (| operator) + */ +#define PB_DECODE_NOINIT 0x01U +#define PB_DECODE_DELIMITED 0x02U +#define PB_DECODE_NULLTERMINATED 0x04U +bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) +#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) +#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) +#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) + +/* Release any allocated pointer fields. If you use dynamic allocation, you should + * call this for any successfully decoded message when you are done with it. If + * pb_decode() returns with an error, the message is already released. + */ +void pb_release(const pb_msgdesc_t *fields, void *dest_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory buffer. + * + * msglen should be the actual length of the message, not the full size of + * allocated buffer. + * + * Alternatively, you can use a custom stream that reads directly from e.g. + * a file or a network socket. + */ +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); + +/* Function to read from a pb_istream_t. You can use this if you need to + * read some custom header data, or to read data in field callbacks. + */ +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Decode the tag for the next field in the stream. Gives the wire type and + * field tag. At end of the message, returns false and sets eof to true. */ +bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); + +/* Skip the field payload data, given the wire type. */ +bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); + +/* Decode an integer in the varint format. This works for enum, int32, + * int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); +#else +#define pb_decode_varint pb_decode_varint32 +#endif + +/* Decode an integer in the varint format. This works for enum, int32, + * and uint32 field types. */ +bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); + +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); +#else +bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); +#endif + +/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to + * a 4-byte wide C variable. */ +bool pb_decode_fixed32(pb_istream_t *stream, void *dest); + +#ifndef PB_WITHOUT_64BIT +/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to + * a 8-byte wide C variable. */ +bool pb_decode_fixed64(pb_istream_t *stream, void *dest); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Decode a double value into float variable. */ +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); +#endif + +/* Make a limited-length substream for reading a PB_WT_STRING field. */ +bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); +bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/components/third_party/nanopb/include/pb_encode.h b/components/third_party/nanopb/include/pb_encode.h new file mode 100644 index 0000000..6dc089d --- /dev/null +++ b/components/third_party/nanopb/include/pb_encode.h @@ -0,0 +1,195 @@ +/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. + * The main function is pb_encode. You also need an output stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_ENCODE_H_INCLUDED +#define PB_ENCODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom output streams. You will need to provide + * a callback function to write the bytes to your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause encoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer). + * 3) pb_write will update bytes_written after your callback runs. + * 4) Substreams will modify max_size and bytes_written. Don't use them + * to calculate any pointers. + */ +struct pb_ostream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + * Also, NULL pointer marks a 'sizing stream' that does not + * write anything. + */ + const int *callback; +#else + bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +#endif + + /* state is a free field for use of the callback function defined above. + * Note that when pb_ostream_from_buffer() is used, it reserves this field + * for its own use. + */ + void *state; + + /* Limit number of output bytes written. Can be set to SIZE_MAX. */ + size_t max_size; + + /* Number of bytes written so far. */ + size_t bytes_written; + +#ifndef PB_NO_ERRMSG + /* Pointer to constant (ROM) string when decoding function returns error */ + const char *errmsg; +#endif +}; + +/*************************** + * Main encoding functions * + ***************************/ + +/* Encode a single protocol buffers message from C structure into a stream. + * Returns true on success, false on any failure. + * The actual struct pointed to by src_struct must match the description in fields. + * All required fields in the struct are assumed to have been filled in. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_ostream_t stream; + * + * msg.field1 = 42; + * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + * pb_encode(&stream, MyMessage_fields, &msg); + */ +bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +/* Extended version of pb_encode, with several options to control the + * encoding process: + * + * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. + * Corresponds to writeDelimitedTo() in Google's + * protobuf API. + * + * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. + * NOTE: This behaviour is not supported in most other + * protobuf implementations, so PB_ENCODE_DELIMITED + * is a better option for compatibility. + */ +#define PB_ENCODE_DELIMITED 0x02U +#define PB_ENCODE_NULLTERMINATED 0x04U +bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) +#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) + +/* Encode the message to get the size of the encoded data, but do not store + * the data. */ +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an output stream for writing into a memory buffer. + * The number of bytes written can be found in stream.bytes_written after + * encoding the message. + * + * Alternatively, you can use a custom stream that writes directly to e.g. + * a file or a network socket. + */ +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); + +/* Pseudo-stream for measuring the size of a message without actually storing + * the encoded data. + * + * Example usage: + * MyMessage msg = {}; + * pb_ostream_t stream = PB_OSTREAM_SIZING; + * pb_encode(&stream, MyMessage_fields, &msg); + * printf("Message size is %d\n", stream.bytes_written); + */ +#ifndef PB_NO_ERRMSG +#define PB_OSTREAM_SIZING {0,0,0,0,0} +#else +#define PB_OSTREAM_SIZING {0,0,0,0} +#endif + +/* Function to write into a pb_ostream_t stream. You can use this if you need + * to append or prepend some custom headers to the message. + */ +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Encode field header based on type and field number defined in the field + * structure. Call this from the callback before writing out field contents. */ +bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); + +/* Encode field header by manually specifying wire type. You need to use this + * if you want to write out packed arrays from a callback field. */ +bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); + +/* Encode an integer in the varint format. + * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); +#else +bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); +#endif + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); +#else +bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); +#endif + +/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); + +/* Encode a fixed32, sfixed32 or float value. + * You need to pass a pointer to a 4-byte wide C variable. */ +bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); + +#ifndef PB_WITHOUT_64BIT +/* Encode a fixed64, sfixed64 or double value. + * You need to pass a pointer to a 8-byte wide C variable. */ +bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Encode a float value so that it appears like a double in the encoded + * message. */ +bool pb_encode_float_as_double(pb_ostream_t *stream, float value); +#endif + +/* Encode a submessage field. + * You need to pass the pb_field_t array and pointer to struct, just like + * with pb_encode(). This internally encodes the submessage twice, first to + * calculate message size and then to actually write it out. + */ +bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/components/third_party/nanopb/src/pb_common.c b/components/third_party/nanopb/src/pb_common.c new file mode 100644 index 0000000..6aee76b --- /dev/null +++ b/components/third_party/nanopb/src/pb_common.c @@ -0,0 +1,388 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +static bool load_descriptor_values(pb_field_iter_t *iter) +{ + uint32_t word0; + uint32_t data_offset; + int_least8_t size_offset; + + if (iter->index >= iter->descriptor->field_count) + return false; + + word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + iter->type = (pb_type_t)((word0 >> 8) & 0xFF); + + switch(word0 & 3) + { + case 0: { + /* 1-word format */ + iter->array_size = 1; + iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); + size_offset = (int_least8_t)((word0 >> 24) & 0x0F); + data_offset = (word0 >> 16) & 0xFF; + iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); + break; + } + + case 1: { + /* 2-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + + iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); + size_offset = (int_least8_t)((word0 >> 28) & 0x0F); + data_offset = word1 & 0xFFFF; + iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); + break; + } + + case 2: { + /* 4-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + + iter->array_size = (pb_size_t)(word0 >> 16); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + + default: { + /* 8-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); + + iter->array_size = (pb_size_t)word4; + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + } + + if (!iter->message) + { + /* Avoid doing arithmetic on null pointers, it is undefined */ + iter->pField = NULL; + iter->pSize = NULL; + } + else + { + iter->pField = (char*)iter->message + data_offset; + + if (size_offset) + { + iter->pSize = (char*)iter->pField - size_offset; + } + else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && + (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || + PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) + { + /* Fixed count array */ + iter->pSize = &iter->array_size; + } + else + { + iter->pSize = NULL; + } + + if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) + { + iter->pData = *(void**)iter->pField; + } + else + { + iter->pData = iter->pField; + } + } + + if (PB_LTYPE_IS_SUBMSG(iter->type)) + { + iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; + } + else + { + iter->submsg_desc = NULL; + } + + return true; +} + +static void advance_iterator(pb_field_iter_t *iter) +{ + iter->index++; + + if (iter->index >= iter->descriptor->field_count) + { + /* Restart */ + iter->index = 0; + iter->field_info_index = 0; + iter->submessage_index = 0; + iter->required_field_index = 0; + } + else + { + /* Increment indexes based on previous field type. + * All field info formats have the following fields: + * - lowest 2 bits tell the amount of words in the descriptor (2^n words) + * - bits 2..7 give the lowest bits of tag number. + * - bits 8..15 give the field type. + */ + uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; + pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); + + /* Add to fields. + * The cast to pb_size_t is needed to avoid -Wconversion warning. + * Because the data is is constants from generator, there is no danger of overflow. + */ + iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); + iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); + iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); + } +} + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) +{ + memset(iter, 0, sizeof(*iter)); + + iter->descriptor = desc; + iter->message = message; + + return load_descriptor_values(iter); +} + +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; + bool status; + + uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); + if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + status = pb_field_iter_begin(iter, msg, &extension->dest); + } + else + { + status = pb_field_iter_begin(iter, msg, extension->dest); + } + + iter->pSize = &extension->found; + return status; +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + advance_iterator(iter); + (void)load_descriptor_values(iter); + return iter->index != 0; +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + if (iter->tag == tag) + { + return true; /* Nothing to do, correct field already. */ + } + else if (tag > iter->descriptor->largest_tag) + { + return false; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + if (tag < iter->tag) + { + /* Fields are in tag number order, so we know that tag is between + * 0 and our start position. Setting index to end forces + * advance_iterator() call below to restart from beginning. */ + iter->index = iter->descriptor->field_count; + } + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for tag number match */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) + { + /* Good candidate, check further */ + (void)load_descriptor_values(iter); + + if (iter->tag == tag && + PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) + { + /* Found it */ + return true; + } + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +bool pb_field_iter_find_extension(pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) + { + return true; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for field type */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) + { + return load_descriptor_values(iter); + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +static void *pb_const_cast(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. The cast is done using union + * to avoid spurious compiler warnings. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) +{ + return pb_field_iter_begin(iter, desc, pb_const_cast(message)); +} + +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) +{ + return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); +} + +bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) +{ + if (field->data_size == sizeof(pb_callback_t)) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + + if (pCallback != NULL) + { + if (istream != NULL && pCallback->funcs.decode != NULL) + { + return pCallback->funcs.decode(istream, field, &pCallback->arg); + } + + if (ostream != NULL && pCallback->funcs.encode != NULL) + { + return pCallback->funcs.encode(ostream, field, &pCallback->arg); + } + } + } + + return true; /* Success, but didn't do anything */ + +} + +#ifdef PB_VALIDATE_UTF8 + +/* This function checks whether a string is valid UTF-8 text. + * + * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Original copyright: Markus Kuhn 2005-03-30 + * Licensed under "Short code license", which allows use under MIT license or + * any compatible with it. + */ + +bool pb_validate_utf8(const char *str) +{ + const pb_byte_t *s = (const pb_byte_t*)str; + while (*s) + { + if (*s < 0x80) + { + /* 0xxxxxxx */ + s++; + } + else if ((s[0] & 0xe0) == 0xc0) + { + /* 110XXXXx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) /* overlong? */ + return false; + else + s += 2; + } + else if ((s[0] & 0xf0) == 0xe0) + { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ + return false; + else + s += 3; + } + else if ((s[0] & 0xf8) == 0xf0) + { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ + return false; + else + s += 4; + } + else + { + return false; + } + } + + return true; +} + +#endif + diff --git a/components/third_party/nanopb/src/pb_decode.c b/components/third_party/nanopb/src/pb_decode.c new file mode 100644 index 0000000..82affc6 --- /dev/null +++ b/components/third_party/nanopb/src/pb_decode.c @@ -0,0 +1,1763 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); +static bool pb_field_set_to_default(pb_field_iter_t *field); +static bool pb_message_set_to_defaults(pb_field_iter_t *iter); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); +static void pb_release_single_field(pb_field_iter_t *field); +#endif + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +typedef struct { + uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; +} pb_fields_seen_t; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + memcpy(buf, source, count * sizeof(pb_byte_t)); + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + if (count == 0) + return true; + +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + if (stream->bytes_left < count) + stream->bytes_left = 0; + else + stream->bytes_left -= count; + + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = msglen; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + + +/******************** + * Helper functions * + ********************/ + +bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + { + return false; + } + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 32) + { + /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ + pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; + bool valid_extension = ((byte & 0x7F) == 0x00 || + ((result >> 31) != 0 && byte == sign_extension)); + + if (bitpos >= 64 || !valid_extension) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + else if (bitpos == 28) + { + if ((byte & 0x70) != 0 && (byte & 0x78) != 0x78) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + result |= (uint32_t)(byte & 0x0F) << bitpos; + } + else + { + result |= (uint32_t)(byte & 0x7F) << bitpos; + } + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 63 && (byte & 0xFE) != 0) + PB_RETURN_ERROR(stream, "varint overflow"); + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} +#endif + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + if ((size_t)length != length) + { + PB_RETURN_ERROR(stream, "size too large"); + } + + return pb_read(stream, NULL, (size_t)length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (stream->bytes_left == 0) + { + *eof = true; + return false; + } + + if (!pb_decode_varint32(stream, &temp)) + { +#ifndef PB_BUFFER_ONLY + /* Workaround for issue #1017 + * + * Callback streams don't set bytes_left to 0 on eof until after being called by pb_decode_varint32, + * which results in "io error" being raised. This contrasts the behavior of buffer streams who raise + * no error on eof as bytes_left is already 0 on entry. This causes legitimate errors (e.g. missing + * required fields) to be incorrectly reported by callback streams. + */ + if (stream->callback != buf_read && stream->bytes_left == 0) + { +#ifndef PB_NO_ERRMSG + if (strcmp(stream->errmsg, "io error") == 0) + stream->errmsg = NULL; +#endif + *eof = true; + } +#endif + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + case PB_WT_PACKED: + /* Calling pb_skip_field with a PB_WT_PACKED is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_read(stream, buf, 1)) + return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + case PB_WT_STRING: + /* Calling read_raw_value with a PB_WT_STRING is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + case PB_WT_PACKED: + /* Calling read_raw_value with a PB_WT_PACKED is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = (size_t)size; + stream->bytes_left -= (size_t)size; + return true; +} + +bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + if (substream->bytes_left) { + if (!pb_read(substream, NULL, substream->bytes_left)) + return false; + } + + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif + return true; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_varint(stream, field); + + case PB_LTYPE_FIXED32: + if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_decode_fixed32(stream, field->pData); + + case PB_LTYPE_FIXED64: + if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float)) + { + return pb_decode_double_as_float(stream, (float*)field->pData); + } +#endif + +#ifdef PB_WITHOUT_64BIT + PB_RETURN_ERROR(stream, "invalid data_size"); +#else + return pb_decode_fixed64(stream, field->pData); +#endif + + case PB_LTYPE_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bytes(stream, field); + + case PB_LTYPE_STRING: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_OPTIONAL: + if (field->pSize != NULL) + *(bool*)field->pSize = true; + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_istream_t substream; + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < field->array_size) + { + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + (*size)++; + field->pData = (char*)field->pData + field->data_size; + } + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if ((*size)++ >= field->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && + *(pb_size_t*)field->pSize != field->tag) + { + /* We memset to zero so that any callbacks are set to NULL. + * This is because the callbacks might otherwise have values + * from some other union field. + * If callbacks are needed inside oneof field, use .proto + * option submsg_callback to have a separate callback function + * that can set the fields before submessage is decoded. + * pb_dec_submessage() will set any default values. */ + memset(field->pData, 0, (size_t)field->data_size); + + /* Set default values for the submessage fields. */ + if (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL) + { + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + } + *(pb_size_t*)field->pSize = field->tag; + + return decode_basic_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * Default values will be set by pb_dec_submessage(). */ + memset(pItem, 0, field->data_size); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(field); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + /* FIXME: Does this work correctly for oneofs? */ + pb_release_single_field(field); + } + + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = field->tag; + } + + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + /* pb_dec_string and pb_dec_bytes handle allocation themselves */ + field->pData = field->pField; + return decode_basic_field(stream, wire_type, field); + } + else + { + if (!allocate_field(stream, field->pField, field->data_size, 1)) + return false; + + field->pData = *(void**)field->pField; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)field->pSize; + size_t allocated_size = *size; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + size_t remain = (substream.bytes_left - 1) / field->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; + + if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + field->pData = *(char**)field->pField + field->data_size * (*size); + if (field->pData == NULL) + { + /* Shouldn't happen, but satisfies static analyzers */ + status = false; + break; + } + initialize_pointer_field(field->pData, field); + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + + (*size)++; + } + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)field->pSize; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) + return false; + + field->pData = *(char**)field->pField + field->data_size * (*size); + (*size)++; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + if (!field->descriptor->field_callback) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + size_t prev_bytes_left; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + /* If the callback field is inside a submsg, first call the submsg_callback which + * should set the decoder for the callback field. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) { + pb_callback_t* callback; + *(pb_size_t*)field->pSize = field->tag; + callback = (pb_callback_t*)field->pSize - 1; + + if (callback->funcs.decode) + { + if (!callback->funcs.decode(&substream, field, &callback->arg)) { + PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "submsg callback failed"); + return false; + } + } + } + + do + { + prev_bytes_left = substream.bytes_left; + if (!field->descriptor->field_callback(&substream, NULL, field)) + { + PB_SET_ERROR(stream, substream.errmsg ? substream.errmsg : "callback failed"); + return false; + } + } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return field->descriptor->field_callback(&substream, NULL, field); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, field)) + return false; + } +#endif + + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, field); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, field); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + if (iter.tag != tag || !iter.message) + return true; + + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) +{ + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Initialize message fields to default values, recursively */ +static bool pb_field_set_to_default(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + ext->found = false; + if (!pb_message_set_to_defaults(&ext_iter)) + return false; + } + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)field->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)field->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE_IS_SUBMSG(field->type) && + (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL)) + { + /* Initialize submessage to defaults. + * Only needed if it has default values + * or callback/submessage fields. */ + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + return false; + } + } + else + { + /* Initialize to zeros */ + memset(field->pData, 0, (size_t)field->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)field->pField = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + + return true; +} + +static bool pb_message_set_to_defaults(pb_field_iter_t *iter) +{ + pb_istream_t defstream = PB_ISTREAM_EMPTY; + uint32_t tag = 0; + pb_wire_type_t wire_type = PB_WT_VARINT; + bool eof; + + if (iter->descriptor->default_value) + { + defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + } + + do + { + if (!pb_field_set_to_default(iter)) + return false; + + if (tag != 0 && iter->tag == tag) + { + /* We have a default value for this field in the defstream */ + if (!decode_field(&defstream, wire_type, iter)) + return false; + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + + if (iter->pSize) + *(bool*)iter->pSize = false; + } + } while (pb_field_iter_next(iter)); + + return true; +} + +/********************* + * Decode all fields * + *********************/ + +static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + /* If the message contains extension fields, the extension handlers + * are called when tag number is >= extension_range_start. This precheck + * is just for speed, and the handlers will check for precise match. + */ + uint32_t extension_range_start = 0; + pb_extension_t *extensions = NULL; + + /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed + * count field. This can only handle _one_ repeated fixed count field that + * is unpacked and unordered among other (non repeated fixed count) fields. + */ + pb_size_t fixed_count_field = PB_SIZE_MAX; + pb_size_t fixed_count_size = 0; + pb_size_t fixed_count_total_size = 0; + + /* Tag and wire type of next field from the input stream */ + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + /* Track presence of required fields */ + pb_fields_seen_t fields_seen = {{0, 0}}; + const uint32_t allbits = ~(uint32_t)0; + + /* Descriptor for the structure field matching the tag decoded from stream */ + pb_field_iter_t iter; + + if (pb_field_iter_begin(&iter, fields, dest_struct)) + { + if ((flags & PB_DECODE_NOINIT) == 0) + { + if (!pb_message_set_to_defaults(&iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + + while (pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (tag == 0) + { + if (flags & PB_DECODE_NULLTERMINATED) + { + eof = true; + break; + } + else + { + PB_RETURN_ERROR(stream, "zero tag"); + } + } + + if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* No match found, check if it matches an extension. */ + if (extension_range_start == 0) + { + if (pb_field_iter_find_extension(&iter)) + { + extensions = *(pb_extension_t* const *)iter.pData; + extension_range_start = iter.tag; + } + + if (!extensions) + { + extension_range_start = (uint32_t)-1; + } + } + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, extensions)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + /* If a repeated fixed count field was found, get size from + * 'fixed_count_field' as there is no counter contained in the struct. + */ + if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) + { + if (fixed_count_field != iter.index) { + /* If the new fixed count field does not match the previous one, + * check that the previous one is NULL or that it finished + * receiving all the expected data. + */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + fixed_count_field = iter.index; + fixed_count_size = 0; + fixed_count_total_size = iter.array_size; + } + + iter.pSize = &fixed_count_size; + } + + if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + if (!eof) + { + /* pb_decode_tag() returned error before end of stream */ + return false; + } + + /* Check that all elements of the last decoded fixed count field were present. */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + /* Check that all required fields were present. */ + { + pb_size_t req_field_count = iter.descriptor->required_field_count; + + if (req_field_count > 0) + { + pb_size_t i; + + if (req_field_count > PB_MAX_REQUIRED_FIELDS) + req_field_count = PB_MAX_REQUIRED_FIELDS; + + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen.bitfield[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits (if any) */ + if ((req_field_count & 31) != 0) + { + if (fields_seen.bitfield[req_field_count >> 5] != + (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) + { + PB_RETURN_ERROR(stream, "missing required field"); + } + } + } + } + + return true; +} + +bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + bool status; + + if ((flags & PB_DECODE_DELIMITED) == 0) + { + status = pb_decode_inner(stream, fields, dest_struct, flags); + } + else + { + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode_inner(&substream, fields, dest_struct, flags); + + if (!pb_close_string_substream(stream, &substream)) + status = false; + } + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) +{ + return pb_decode_ex(stream, fields, dest_struct, 0); +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) +{ + pb_field_iter_t old_field = *field; + pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ + pb_size_t new_tag = field->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(&old_field, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(&old_field); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL to make sure it is valid + * even in case of error return. */ + *(void**)field->pField = NULL; + field->pData = NULL; + } + + return true; +} + +static void pb_release_single_field(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)field->pSize != field->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + pb_release_single_field(&ext_iter); + } + ext = ext->next; + } + } + else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) + { + /* Release fields in submessage or submsg array */ + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + field->pData = *(void**)field->pField; + } + else + { + field->pData = field->pField; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)field->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) + { + /* Protect against corrupted _count fields */ + count = field->array_size; + } + } + + if (field->pData) + { + for (; count > 0; count--) + { + pb_release(field->submsg_desc, field->pData); + field->pData = (char*)field->pData + field->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)field->pField; + pb_size_t count = *(pb_size_t*)field->pSize; + for (; count > 0; count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)field->pSize = 0; + } + + /* Release main pointer */ + pb_free(*(void**)field->pField); + *(void**)field->pField = NULL; + } +} + +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#else +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + /* Nothing to release without PB_ENABLE_MALLOC. */ + PB_UNUSED(fields); + PB_UNUSED(dest_struct); +} +#endif + +/* Field decoders */ + +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + uint32_t value; + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + +bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) +{ + pb_uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (pb_int64_t)(~(value >> 1)); + else + *dest = (pb_int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + union { + uint32_t fixed32; + pb_byte_t bytes[4]; + } u; + + if (!pb_read(stream, u.bytes, 4)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint32_t*)dest = u.fixed32; +#else + *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | + ((uint32_t)u.bytes[1] << 8) | + ((uint32_t)u.bytes[2] << 16) | + ((uint32_t)u.bytes[3] << 24); +#endif + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + union { + uint64_t fixed64; + pb_byte_t bytes[8]; + } u; + + if (!pb_read(stream, u.bytes, 8)) + return false; + +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint64_t*)dest = u.fixed64; +#else + *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | + ((uint64_t)u.bytes[1] << 8) | + ((uint64_t)u.bytes[2] << 16) | + ((uint64_t)u.bytes[3] << 24) | + ((uint64_t)u.bytes[4] << 32) | + ((uint64_t)u.bytes[5] << 40) | + ((uint64_t)u.bytes[6] << 48) | + ((uint64_t)u.bytes[7] << 56); +#endif + return true; +} +#endif + +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) +{ + return pb_decode_bool(stream, (bool*)field->pData); +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + pb_uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_uint64_t)) + clamped = *(pb_uint64_t*)field->pData = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)field->pData = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } + else + { + pb_uint64_t value; + pb_int64_t svalue; + pb_int64_t clamped; + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + { + if (!pb_decode_svarint(stream, &svalue)) + return false; + } + else + { + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(pb_int64_t)) + svalue = (pb_int64_t)value; + else + svalue = (int32_t)value; + } + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_int64_t)) + clamped = *(pb_int64_t*)field->pData = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)field->pData = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *dest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_bytes_array_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + dest = (pb_bytes_array_t*)field->pData; + } + + dest->size = (pb_size_t)size; + return pb_read(stream, dest->bytes, (size_t)size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_byte_t *dest = (pb_byte_t*)field->pData; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size == (uint32_t)-1) + PB_RETURN_ERROR(stream, "size too large"); + + /* Space for null terminator */ + alloc_size = (size_t)(size + 1); + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_byte_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + dest[size] = 0; + + if (!pb_read(stream, dest, (size_t)size)) + return false; + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8((const char*)dest)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return true; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) +{ + bool status = true; + bool submsg_consumed = false; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* Submessages can have a separate message-level callback that is called + * before decoding the message. Typically it is used to set callback fields + * inside oneofs. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.decode) + { + status = callback->funcs.decode(&substream, field, &callback->arg); + + if (substream.bytes_left == 0) + { + submsg_consumed = true; + } + } + } + + /* Now decode the submessage contents */ + if (status && !submsg_consumed) + { + unsigned int flags = 0; + + /* Static required/optional fields are already initialized by top-level + * pb_decode(), no need to initialize them again. */ + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_HTYPE(field->type) != PB_HTYPE_REPEATED) + { + flags = PB_DECODE_NOINIT; + } + + status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); + } + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; +} + +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + if (size == 0) + { + /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ + memset(field->pData, 0, (size_t)field->data_size); + return true; + } + + if (size != field->data_size) + PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); + + return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) +{ + uint_least8_t sign; + int exponent; + uint32_t mantissa; + uint64_t value; + union { float f; uint32_t i; } out; + + if (!pb_decode_fixed64(stream, &value)) + return false; + + /* Decompose input value */ + sign = (uint_least8_t)((value >> 63) & 1); + exponent = (int)((value >> 52) & 0x7FF) - 1023; + mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ + + /* Figure if value is in range representable by floats. */ + if (exponent == 1024) + { + /* Special value */ + exponent = 128; + mantissa >>= 1; + } + else + { + if (exponent > 127) + { + /* Too large, convert to infinity */ + exponent = 128; + mantissa = 0; + } + else if (exponent < -150) + { + /* Too small, convert to zero */ + exponent = -127; + mantissa = 0; + } + else if (exponent < -126) + { + /* Denormalized */ + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + /* Round off mantissa */ + mantissa = (mantissa + 1) >> 1; + + /* Check if mantissa went over 2.0 */ + if (mantissa & 0x800000) + { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + } + + /* Combine fields */ + out.i = mantissa; + out.i |= (uint32_t)(exponent + 127) << 23; + out.i |= (uint32_t)sign << 31; + + *dest = out.f; + return true; +} +#endif diff --git a/components/third_party/nanopb/src/pb_encode.c b/components/third_party/nanopb/src/pb_encode.c new file mode 100644 index 0000000..4a6f49c --- /dev/null +++ b/components/third_party/nanopb/src/pb_encode.c @@ -0,0 +1,1006 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#include "pb.h" +#include "pb_encode.h" +#include "pb_common.h" + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers, gcc before 3.4.0 and iar + * before 9.40.1 just ignore the annotation. + */ +#if (defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))) || \ + (defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)) + #define checkreturn __attribute__((warn_unused_result)) +#else + #define checkreturn +#endif + +/************************************** + * Declarations internal to this file * + **************************************/ +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); +static pb_noinline bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + memcpy(dest, buf, count * sizeof(pb_byte_t)); + + return true; +} + +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) +{ + pb_ostream_t stream; +#ifdef PB_BUFFER_ONLY + /* In PB_BUFFER_ONLY configuration the callback pointer is just int*. + * NULL pointer marks a sizing field, so put a non-NULL value to mark a buffer stream. + */ + static const int marker = 0; + stream.callback = ▮ +#else + stream.callback = &buf_write; +#endif + stream.state = buf; + stream.max_size = bufsize; + stream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + if (count > 0 && stream->callback != NULL) + { + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { + PB_RETURN_ERROR(stream, "stream full"); + } + +#ifdef PB_BUFFER_ONLY + if (!buf_write(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#endif + } + + stream->bytes_written += count; + return true; +} + +/************************* + * Encode a single field * + *************************/ + +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) +{ + pb_size_t i; + pb_size_t count; +#ifndef PB_ENCODE_ARRAYS_UNPACKED + size_t size; +#endif + + count = *(pb_size_t*)field->pSize; + + if (count == 0) + return true; + + if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) + PB_RETURN_ERROR(stream, "array max size exceeded"); + +#ifndef PB_ENCODE_ARRAYS_UNPACKED + /* We always pack arrays if the datatype allows it. */ + if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) + return false; + + /* Determine the total size of packed array. */ + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) + { + size = 4 * (size_t)count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * (size_t)count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + void *pData_orig = field->pData; + for (i = 0; i < count; i++) + { + if (!pb_enc_varint(&sizestream, field)) + PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); + field->pData = (char*)field->pData + field->data_size; + } + field->pData = pData_orig; + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + for (i = 0; i < count; i++) + { + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + if (!pb_enc_fixed(stream, field)) + return false; + } + else + { + if (!pb_enc_varint(stream, field)) + return false; + } + + field->pData = (char*)field->pData + field->data_size; + } + } + else /* Unpacked fields */ +#endif + { + for (i = 0; i < count; i++) + { + /* Normally the data is stored directly in the array entries, but + * for pointer-type string and bytes fields, the array entries are + * actually pointers themselves also. So we have to dereference once + * more to get to the actual data. */ + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && + (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES)) + { + bool status; + void *pData_orig = field->pData; + field->pData = *(void* const*)field->pData; + + if (!field->pData) + { + /* Null pointer in array is treated as empty string / bytes */ + status = pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0); + } + else + { + status = encode_basic_field(stream, field); + } + + field->pData = pData_orig; + + if (!status) + return false; + } + else + { + if (!encode_basic_field(stream, field)) + return false; + } + field->pData = (char*)field->pData + field->data_size; + } + } + + return true; +} + +/* In proto3, all fields are optional and are only encoded if their value is "non-zero". + * This function implements the check for the zero value. */ +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) +{ + pb_type_t type = field->type; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) + { + /* Required proto2 fields inside proto3 submessage, pretty rare case */ + return false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Repeated fields inside proto3 submessage: present if count != 0 */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* Oneof fields */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Proto2 optional fields inside proto3 message, or proto3 + * submessage fields. */ + return safe_read_bool(field->pSize) == false; + } + else if (field->descriptor->default_value) + { + /* Proto3 messages do not have default values, but proto2 messages + * can contain optional fields without has_fields (generator option 'proto3'). + * In this case they must always be encoded, to make sure that the + * non-zero default value is overwritten. + */ + return false; + } + + /* Rest is proto3 singular fields */ + if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Simple integer / float fields */ + pb_size_t i; + const char *p = (const char*)field->pData; + for (i = 0; i < field->data_size; i++) + { + if (p[i] != 0) + { + return false; + } + } + + return true; + } + else if (PB_LTYPE(type) == PB_LTYPE_BYTES) + { + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; + return bytes->size == 0; + } + else if (PB_LTYPE(type) == PB_LTYPE_STRING) + { + return *(const char*)field->pData == '\0'; + } + else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) + { + /* Fixed length bytes is only empty if its length is fixed + * as 0. Which would be pretty strange, but we can check + * it anyway. */ + return field->data_size == 0; + } + else if (PB_LTYPE_IS_SUBMSG(type)) + { + /* Check all fields in the submessage to find if any of them + * are non-zero. The comparison cannot be done byte-per-byte + * because the C struct may contain padding bytes that must + * be skipped. Note that usually proto3 submessages have + * a separate has_field that is checked earlier in this if. + */ + pb_field_iter_t iter; + if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) + { + do + { + if (!pb_check_proto3_default_value(&iter)) + { + return false; + } + } while (pb_field_iter_next(&iter)); + } + return true; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + return field->pData == NULL; + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + return extension == NULL; + } + else if (field->descriptor->field_callback == pb_default_field_callback) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + return pCallback->funcs.encode == NULL; + } + else + { + return field->descriptor->field_callback == NULL; + } + } + + return false; /* Not typically reached, safe default for weird special cases. */ +} + +/* Encode a field with static or pointer allocation, i.e. one whose data + * is available to the encoder directly. */ +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (!field->pData) + { + /* Missing pointer field */ + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + return pb_enc_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + return pb_enc_varint(stream, field); + + case PB_LTYPE_FIXED32: + case PB_LTYPE_FIXED64: + return pb_enc_fixed(stream, field); + + case PB_LTYPE_BYTES: + return pb_enc_bytes(stream, field); + + case PB_LTYPE_STRING: + return pb_enc_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + return pb_enc_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + return pb_enc_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Encode a field with callback semantics. This means that a user function is + * called to provide and encode the actual data. */ +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->descriptor->field_callback != NULL) + { + if (!field->descriptor->field_callback(NULL, stream, field)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback, pointer or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) +{ + /* Check field presence */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (*(const pb_size_t*)field->pSize != field->tag) + { + /* Different type oneof field */ + return true; + } + } + else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) + { + if (field->pSize) + { + if (safe_read_bool(field->pSize) == false) + { + /* Missing optional field */ + return true; + } + } + else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) + { + /* Proto3 singular field */ + if (pb_check_proto3_default_value(field)) + return true; + } + } + + if (!field->pData) + { + if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) + PB_RETURN_ERROR(stream, "missing required field"); + + /* Pointer field set to NULL */ + return true; + } + + /* Then encode field contents */ + if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) + { + return encode_callback_field(stream, field); + } + else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + { + return encode_array(stream, field); + } + else + { + return encode_basic_field(stream, field); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension_const(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + return encode_field(stream, &iter); +} + + +/* Walk through all the registered extensions and give them a chance + * to encode themselves. */ +static pb_noinline bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + + while (extension) + { + bool status; + if (extension->type->encode) + status = extension->type->encode(stream, extension); + else + status = default_extension_encoder(stream, extension); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/********************* + * Encode all fields * + *********************/ + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin_const(&iter, fields, src_struct)) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, &iter)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, &iter)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) +{ + if ((flags & PB_ENCODE_DELIMITED) != 0) + { + return pb_encode_submessage(stream, fields, src_struct); + } + else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) + { + const pb_byte_t zero = 0; + + if (!pb_encode(stream, fields, src_struct)) + return false; + + return pb_write(stream, &zero, 1); + } + else + { + return pb_encode(stream, fields, src_struct); + } +} + +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_ostream_t stream = PB_OSTREAM_SIZING; + + if (!pb_encode(&stream, fields, src_struct)) + return false; + + *size = stream.bytes_written; + return true; +} + +/******************** + * Helper functions * + ********************/ + +/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) +{ + size_t i = 0; + pb_byte_t buffer[10]; + pb_byte_t byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(high & 0x7F); + high >>= 7; + } + } + + buffer[i++] = byte; + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) +{ + if (value <= 0x7F) + { + /* Fast path: single byte */ + pb_byte_t byte = (pb_byte_t)value; + return pb_write(stream, &byte, 1); + } + else + { +#ifdef PB_WITHOUT_64BIT + return pb_encode_varint_32(stream, value, 0); +#else + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); +#endif + } +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) +{ + pb_uint64_t zigzagged; + pb_uint64_t mask = ((pb_uint64_t)-1) >> 1; /* Satisfy clang -fsanitize=integer */ + if (value < 0) + zigzagged = ~(((pb_uint64_t)value & mask) << 1); + else + zigzagged = (pb_uint64_t)value << 1; + + return pb_encode_varint(stream, zigzagged); +} + +bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 4); +#else + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); +#endif +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) +{ +#if defined(PB_LITTLE_ENDIAN_8BIT) && PB_LITTLE_ENDIAN_8BIT == 1 + /* Fast path if we know that we're on little endian */ + return pb_write(stream, (const pb_byte_t*)value, 8); +#else + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); +#endif +} +#endif + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + wiretype = PB_WT_VARINT; + break; + + case PB_LTYPE_FIXED32: + wiretype = PB_WT_32BIT; + break; + + case PB_LTYPE_FIXED64: + wiretype = PB_WT_64BIT; + break; + + case PB_LTYPE_BYTES: + case PB_LTYPE_STRING: + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + case PB_LTYPE_FIXED_LENGTH_BYTES: + wiretype = PB_WT_STRING; + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return pb_encode_tag(stream, wiretype, field->tag); +} + +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) +{ + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + /* First calculate the message size using a non-writing substream. */ + pb_ostream_t substream = PB_OSTREAM_SIZING; +#if !defined(PB_NO_ENCODE_SIZE_CHECK) || PB_NO_ENCODE_SIZE_CHECK == 0 + bool status; + size_t size; +#endif + + if (!pb_encode(&substream, fields, src_struct)) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + return false; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)substream.bytes_written)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, substream.bytes_written); /* Just sizing */ + + if (stream->bytes_written + substream.bytes_written > stream->max_size) + PB_RETURN_ERROR(stream, "stream full"); + +#if defined(PB_NO_ENCODE_SIZE_CHECK) && PB_NO_ENCODE_SIZE_CHECK == 1 + return pb_encode(stream, fields, src_struct); +#else + size = substream.bytes_written; + /* Use a substream to verify that a callback doesn't write more than + * what it did the first time. */ + substream.callback = stream->callback; + substream.state = stream->state; + substream.max_size = size; + substream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + substream.errmsg = NULL; +#endif + + status = pb_encode(&substream, fields, src_struct); + + stream->bytes_written += substream.bytes_written; + stream->state = substream.state; +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + + if (substream.bytes_written != size) + PB_RETURN_ERROR(stream, "submsg size changed"); + + return status; +#endif +} + +/* Field encoders */ + +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + uint32_t value = safe_read_bool(field->pData) ? 1 : 0; + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + /* Perform unsigned integer extension */ + pb_uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)field->pData; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)field->pData; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)field->pData; + else if (field->data_size == sizeof(pb_uint64_t)) + value = *(const pb_uint64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); + } + else + { + /* Perform signed integer extension */ + pb_int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)field->pData; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)field->pData; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)field->pData; + else if (field->data_size == sizeof(pb_int64_t)) + value = *(const pb_int64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + return pb_encode_svarint(stream, value); +#ifdef PB_WITHOUT_64BIT + else if (value < 0) + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); +#endif + else + return pb_encode_varint(stream, (pb_uint64_t)value); + + } +} + +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) +{ +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + return pb_encode_float_as_double(stream, *(float*)field->pData); + } +#endif + + if (field->data_size == sizeof(uint32_t)) + { + return pb_encode_fixed32(stream, field->pData); + } +#ifndef PB_WITHOUT_64BIT + else if (field->data_size == sizeof(uint64_t)) + { + return pb_encode_fixed64(stream, field->pData); + } +#endif + else + { + PB_RETURN_ERROR(stream, "invalid data_size"); + } +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_bytes_array_t *bytes = NULL; + + bytes = (const pb_bytes_array_t*)field->pData; + + if (bytes == NULL) + { + /* Treat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + size_t size = 0; + size_t max_size = (size_t)field->data_size; + const char *str = (const char*)field->pData; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + max_size = (size_t)-1; + } + else + { + /* pb_dec_string() assumes string fields end with a null + * terminator when the type isn't PB_ATYPE_POINTER, so we + * shouldn't allow more than max-1 bytes to be written to + * allow space for the null terminator. + */ + if (max_size == 0) + PB_RETURN_ERROR(stream, "zero-length string"); + + max_size -= 1; + } + + + if (str == NULL) + { + size = 0; /* Treat null pointer as an empty string */ + } + else + { + const char *p = str; + + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + + if (*p != '\0') + { + PB_RETURN_ERROR(stream, "unterminated string"); + } + } + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8(str)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return pb_encode_string(stream, (const pb_byte_t*)str, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.encode) + { + if (!callback->funcs.encode(stream, field, &callback->arg)) + return false; + } + } + + return pb_encode_submessage(stream, field->submsg_desc, field->pData); +} + +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_encode_float_as_double(pb_ostream_t *stream, float value) +{ + union { float f; uint32_t i; } in; + uint_least8_t sign; + int exponent; + uint64_t mantissa; + + in.f = value; + + /* Decompose input value */ + sign = (uint_least8_t)((in.i >> 31) & 1); + exponent = (int)((in.i >> 23) & 0xFF) - 127; + mantissa = in.i & 0x7FFFFF; + + if (exponent == 128) + { + /* Special value (NaN etc.) */ + exponent = 1024; + } + else if (exponent == -127) + { + if (!mantissa) + { + /* Zero */ + exponent = -1023; + } + else + { + /* Denormalized */ + mantissa <<= 1; + while (!(mantissa & 0x800000)) + { + mantissa <<= 1; + exponent--; + } + mantissa &= 0x7FFFFF; + } + } + + /* Combine fields */ + mantissa <<= 29; + mantissa |= (uint64_t)(exponent + 1023) << 52; + mantissa |= (uint64_t)sign << 63; + + return pb_encode_fixed64(stream, &mantissa); +} +#endif diff --git a/docs/Doxyfile b/docs/Doxyfile new file mode 100644 index 0000000..0bcbdd2 --- /dev/null +++ b/docs/Doxyfile @@ -0,0 +1,20 @@ +PROJECT_NAME = LiveKit ESP-32 SDK +OUTPUT_DIRECTORY = ./output +OPTIMIZE_OUTPUT_FOR_C = YES +WARN_IF_UNDOCUMENTED = YES +INPUT = ../README.md \ + ../docs \ + ../components/livekit/include \ + ../components/livekit_sandbox/include +USE_MDFILE_AS_MAINPAGE = ../README.md +FILE_PATTERNS = *.h *.md +EXTRACT_ALL = YES +RECURSIVE = YES +JAVADOC_AUTOBRIEF = YES +GENERATE_LATEX = NO +GENERATE_HTML = YES +TIMESTAMP = YES +INLINE_SIMPLE_STRUCTS = YES +TYPEDEF_HIDES_STRUCT = YES +SHOW_FILES = NO +LAYOUT_FILE = ./layout.xml \ No newline at end of file diff --git a/docs/layout.xml b/docs/layout.xml new file mode 100644 index 0000000..ae6ed39 --- /dev/null +++ b/docs/layout.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..54db58f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,95 @@ +# Examples + +This directory contains example applications using the LiveKit ESP-32 SDK, demonstrating various features and +use cases. For setup instructions specific to each example, see the example's README. + +## Configuration + +This guide lists the settings available for all examples. From within the example's root directory, you can set these options in two ways: + +1. **Using *menuconfig***: run `idf.py menuconfig`, navigate to the *LiveKit Example* submenu, and configure the +list of available settings. + +2. **In an *sdkconfig* file**: place setting key-value pairs in an *sdkconfig* file in the format shown below. + +### Codec board type + +For examples that support multiple development boards, you can set the model of the board you are using as follows: + +```ini +CONFIG_CODEC_BOARD_TYPE="S3_Korvo_V2" +``` + +See the README for each example for information about supported boards. + +### Network + +All examples require a network connection in order to connect to a LiveKit server. Choose one of the following connection options: + +#### Wi-Fi + +Connect using Wi-Fi by specifying your network SSID and password as follows: + +```ini +CONFIG_NETWORK_MODE_WIFI=y +CONFIG_WIFI_SSID="" +CONFIG_WIFI_PASSWORD="" +``` + +#### Ethernet + +You can choose to connect using Ethernet on supported boards such as the [ESP32-P4-Function-EV-Board](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html). + +TODO: + +### Room connection + +In production, your backend server is responsible for [generating tokens](https://docs.livekit.io/home/server/generating-tokens/) for users to connect to a room. + +However, for the purposes of demonstration, each example supports two methods to supply the required tokens without +creating a custom backend. Choose one of the following methods: + +#### Option A: Sandbox token server (recommended) + +A Sandbox Token Server is simple hosted component that exposes an HTTP endpoint to generate tokens—much like your own +server would in production. To use this option, create a [Sandbox Token Server](https://cloud.livekit.io/projects/p_/sandbox/templates/token-server) for your LiveKit Cloud project, and set its ID in _sdkconfig_: + +```ini +CONFIG_LK_USE_SANDBOX=y +CONFIG_LK_SANDBOX_ID="" +``` + +(Optional) If you would like the token to be generated with a specific room or participant name, you can also add the following keys: + +```ini +CONFIG_LK_SANDBOX_ROOM_NAME="robot-control" +CONFIG_LK_SANDBOX_PARTICIPANT_NAME="esp-32" +``` + +#### Option B: Pre-generated token + +Connect to a room by proving a manually generated token and custom LiveKit server URL: + +```ini +CONFIG_LK_USE_PREGENERATED=y +CONFIG_LK_TOKEN="your-jwt-token" +CONFIG_LK_SERVER_URL="ws://localhost:7880" +``` + +This option can be useful for local development or troubleshooting. Tokens can be generated using the *token* subcommand on the [LiveKit CLI](https://docs.livekit.io/home/cli/cli-setup/#generate-access-token), and you can follow [this guide](https://docs.livekit.io/home/self-hosting/local/) to install LiveKit server to run from your local machine. + +## Troubleshooting + +### No serial ports found + +If your board's serial port cannot be detected, check to see if it is recognized by your operating system: + +- macOS: Run `ls /dev/cu.*` and look for `/dev/cu.usbserial-*` or similar. +- Linux: Run `ls /dev/ttyUSB*` or `ls /dev/ttyACM*`. +- Windows: Check Device Manager under "Ports (COM & LPT)" for the COM port (e.g. _COM3_). + +If you see your board listed, you can manually specify its port to *idf.py* as follows: + +``` +idf.py -P /dev/cu.usbserial-xyz flash monitor +``` \ No newline at end of file diff --git a/examples/common/CMakeLists.txt b/examples/common/CMakeLists.txt new file mode 100755 index 0000000..e26ad4a --- /dev/null +++ b/examples/common/CMakeLists.txt @@ -0,0 +1,13 @@ + +set(component_srcdirs "./") +set(component_requires esp_netif nvs_flash esp_wifi) + +if("${IDF_TARGET}" STREQUAL "esp32p4") + list(APPEND component_requires "ethernet_init esp_eth") +endif() + +idf_component_register( + SRC_DIRS ./ + INCLUDE_DIRS ./ + REQUIRES ${component_requires} +) diff --git a/examples/common/Kconfig.projbuild b/examples/common/Kconfig.projbuild new file mode 100644 index 0000000..13e3ba7 --- /dev/null +++ b/examples/common/Kconfig.projbuild @@ -0,0 +1,88 @@ +menu "LiveKit Example" + + menu "Codec Board Type" + config CODEC_BOARD_TYPE + string "Codec board type" + default "DUMMY_CODEC_BOARD" + help + The model of dev board you are using. See board_cfg.txt from the codec + board component for a list of supported boards. + endmenu + + menu "Network" + choice NETWORK_MODE + prompt "Choose network mode" + config NETWORK_MODE_WIFI + bool "Wi-Fi" + help + Connect to the internet using Wi-Fi. + # TODO: Add Ethernet support + endchoice + + config WIFI_SSID + depends on NETWORK_MODE_WIFI + string "Wi-Fi SSID" + help + The SSID of the Wi-Fi network to connect to. + config WIFI_PASSWORD + depends on NETWORK_MODE_WIFI + string "Wi-Fi password" + help + The password of the Wi-Fi network to connect to. + endmenu + + menu "Room connection" + choice LK_ROOM_CONNECTION_METHOD + prompt "Choose room connection method" + help + Choose how to connect to the room in the example. + + config LK_USE_SANDBOX + bool "Sandbox token" + help + Use a sandbox token server for room authentication. + + config LK_USE_PREGENERATED + bool "Pre-generated token" + help + Use a pre-generated token and server URL for room connection. + endchoice + + config LK_SERVER_URL + depends on LK_USE_PREGENERATED + string "Server URL" + default "ws://localhost:7880" + help + The server URL to use for room connection. + config LK_TOKEN + depends on LK_USE_PREGENERATED + string "Token" + help + The token to use for room connection. + + config LK_SANDBOX_ID + depends on LK_USE_SANDBOX + string "Sandbox ID" + help + The ID of the sandbox token server to use. + config LK_SANDBOX_ROOM_NAME + depends on LK_USE_SANDBOX + string "Room name (optional)" + help + Specific room name sandbox tokens will be generated with. + config LK_SANDBOX_PARTICIPANT_NAME + depends on LK_USE_SANDBOX + string "Participant name (optional)" + help + Specific participant name sandbox tokens will be generated with. + endmenu + + menu "Media" + config DEFAULT_PLAYBACK_VOL + int "Default playback volume" + default 85 + range 0 100 + help + The default playback volume for speaker output. + endmenu +endmenu diff --git a/examples/common/idf_component.yml b/examples/common/idf_component.yml new file mode 100644 index 0000000..90def96 --- /dev/null +++ b/examples/common/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_wifi_remote: + version: "~0.10.0" + rules: + - if: "target in [esp32p4]" diff --git a/examples/common/network.c b/examples/common/network.c new file mode 100644 index 0000000..e4db206 --- /dev/null +++ b/examples/common/network.c @@ -0,0 +1,306 @@ +/* Network + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include +#include +#include "esp_netif.h" +#include "esp_event.h" +#include "network.h" + +#ifdef CONFIG_NETWORK_USE_ETHERNET +#include "esp_eth.h" +#include "ethernet_init.h" +#else +#include +#endif + +#define TAG "NETWORK" + +static bool network_connected = false; +static network_connect_cb connect_cb; + +static void network_set_connected(bool connected) +{ + if (network_connected != connected) { + network_connected = connected; + if (connect_cb) { + connect_cb(connected); + } + } +} + +bool network_is_connected(void) +{ + return network_connected; +} + +#ifndef CONFIG_NETWORK_USE_ETHERNET + +static wifi_config_t wifi_config; + +#define PART_NAME "wifi-set" +#define WIFI_SSID_KEY "ssid" +#define WIFI_PSW_KEY "psw" + +static bool load_from_nvs(void) +{ + nvs_handle_t wifi_nvs = 0; + bool load_ok = false; + do { + esp_err_t ret = nvs_open(PART_NAME, NVS_READWRITE, &wifi_nvs); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Fail to open nvs ret %d", ret); + break; + } + size_t size = sizeof(wifi_config.sta.ssid); + ret = nvs_get_str(wifi_nvs, WIFI_SSID_KEY, (char*)(wifi_config.sta.ssid), &size); + if (ret != ESP_OK) { + break; + } + wifi_config.sta.ssid[size] = '\0'; + size = sizeof(wifi_config.sta.password); + ret = nvs_get_str(wifi_nvs, WIFI_PSW_KEY, (char*)(wifi_config.sta.password), &size); + if (ret != ESP_OK) { + break; + } + wifi_config.sta.password[size] = '\0'; + load_ok = true; + } while (0); + if (wifi_nvs) { + nvs_close(wifi_nvs); + } + return load_ok; +} + +static void store_to_nvs(void) +{ + nvs_handle_t wifi_nvs = 0; + do { + esp_err_t ret = nvs_open(PART_NAME, NVS_READWRITE, &wifi_nvs); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Fail to open nvs ret %d", ret); + break; + } + ret = nvs_set_str(wifi_nvs, WIFI_SSID_KEY, (char*)(wifi_config.sta.ssid)); + if (ret != ESP_OK) { + break; + } + ret = nvs_set_str(wifi_nvs, WIFI_PSW_KEY, (char*)(wifi_config.sta.password)); + if (ret != ESP_OK) { + break; + } + } while (0); + if (wifi_nvs) { + nvs_close(wifi_nvs); + } +} + +static void event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + network_set_connected(false); + esp_wifi_connect(); + ESP_LOGI(TAG, "retry to connect to the AP"); + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + network_set_connected(true); + store_to_nvs(); + } +} + +int network_init(const char *ssid, const char *password, network_connect_cb cb) +{ + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + ESP_EVENT_ANY_ID, + &event_handler, + NULL, + &instance_any_id)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, + IP_EVENT_STA_GOT_IP, + &event_handler, + NULL, + &instance_got_ip)); + wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; + if (load_from_nvs()) { + ESP_LOGI(TAG, "Force to use wifi config from nvs"); + } else { + if (ssid) { + memcpy(wifi_config.sta.ssid, ssid, strlen(ssid) + 1); + } + if (password) { + memcpy(wifi_config.sta.password, password, strlen(password) + 1); + } + } + connect_cb = cb; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + esp_wifi_set_ps(WIFI_PS_NONE); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_LOGI(TAG, "wifi_init_sta finished."); + return 0; +} + +int network_get_mac(uint8_t mac[6]) +{ + esp_wifi_get_mac(WIFI_IF_STA, mac); + return 0; +} + +int network_connect_wifi(const char *ssid, const char *password) +{ + wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; + if (ssid) { + memcpy(wifi_config.sta.ssid, ssid, strlen(ssid) + 1); + } + if (password) { + memcpy(wifi_config.sta.password, password, strlen(password) + 1); + } + network_connected = false; + esp_wifi_disconnect(); + esp_wifi_stop(); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + return 0; +} + +#else +static esp_eth_handle_t *eth_handles = NULL; +static void eth_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + uint8_t mac_addr[6] = { 0 }; + /* we can get the ethernet driver handle from event data */ + esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; + + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + ESP_LOGI(TAG, "Ethernet Link Up"); + ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + break; + case ETHERNET_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "Ethernet Link Down"); + network_set_connected(false); + break; + case ETHERNET_EVENT_START: + ESP_LOGI(TAG, "Ethernet Started"); + break; + case ETHERNET_EVENT_STOP: + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + default: + break; + } +} + +/** Event handler for IP_EVENT_ETH_GOT_IP */ +static void got_ip_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + const esp_netif_ip_info_t *ip_info = &event->ip_info; + + ESP_LOGI(TAG, "Ethernet Got IP Address"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + network_set_connected(true); +} + +int network_init(const char *ssid, const char *password, network_connect_cb cb) +{ + // Initialize Ethernet driver + uint8_t eth_port_cnt = 0; + ESP_ERROR_CHECK(example_eth_init(ð_handles, ð_port_cnt)); + + // Initialize TCP/IP network interface aka the esp-netif (should be called only once in application) + ESP_ERROR_CHECK(esp_netif_init()); + // Create default event loop that running in background + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + // Create instance(s) of esp-netif for Ethernet(s) + if (eth_port_cnt == 1) { + // Use ESP_NETIF_DEFAULT_ETH when just one Ethernet interface is used and you don't need to modify + // default esp-netif configuration parameters. + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); + esp_netif_t *eth_netif = esp_netif_new(&cfg); + // Attach Ethernet driver to TCP/IP stack + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0]))); + } else { + // Use ESP_NETIF_INHERENT_DEFAULT_ETH when multiple Ethernet interfaces are used and so you need to modify + // esp-netif configuration parameters for each interface (name, priority, etc.). + esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH(); + esp_netif_config_t cfg_spi = { + .base = &esp_netif_config, + .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH + }; + char if_key_str[10]; + char if_desc_str[10]; + char num_str[3]; + for (int i = 0; i < eth_port_cnt; i++) { + itoa(i, num_str, 10); + strcat(strcpy(if_key_str, "ETH_"), num_str); + strcat(strcpy(if_desc_str, "eth"), num_str); + esp_netif_config.if_key = if_key_str; + esp_netif_config.if_desc = if_desc_str; + esp_netif_config.route_prio -= i * 5; + esp_netif_t *eth_netif = esp_netif_new(&cfg_spi); + + // Attach Ethernet driver to TCP/IP stack + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[i]))); + } + } + + // Register user defined event handers + ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL)); + connect_cb = cb; + // Start Ethernet driver state machine + for (int i = 0; i < eth_port_cnt; i++) { + ESP_ERROR_CHECK(esp_eth_start(eth_handles[i])); + } + return 0; +} + +int network_get_mac(uint8_t mac[6]) +{ + if (eth_handles) { + esp_eth_ioctl(eth_handles[0], ETH_CMD_G_MAC_ADDR, mac); + } + return 0; +} + +int network_connect_wifi(const char *ssid, const char *password) +{ + ESP_LOGE(TAG, "Using ethernet now not support wifi config"); + return 0; +} + +#endif diff --git a/examples/common/network.h b/examples/common/network.h new file mode 100644 index 0000000..1fbc522 --- /dev/null +++ b/examples/common/network.h @@ -0,0 +1,70 @@ +/* Network + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Network connect callback + */ +typedef int (*network_connect_cb)(bool connected); + +/** + * @brief Initialize network + * + * @param[in] ssid Wifi ssid + * @param[in] password Wifi password + * @param[in] cb Network connect callback + * + * @return + * - 0 On success + * - Others Fail to initialized + */ +int network_init(const char *ssid, const char *password, network_connect_cb cb); + +/** + * @brief Get current network mac + * + * @param[out] mac Network mac to store + * + * @return + * - 0 On success + * - Others Fail to initialized + */ +int network_get_mac(uint8_t mac[6]); + +/** + * @brief Check network connected or not + * + * @return + * - true Network connected + * - false Network disconnected + */ +bool network_is_connected(void); + +/** + * @brief Connect wifi manually + * + * @param[in] ssid Wifi ssid + * @param[in] password Wifi password + * + * @return + * - 0 On success + * - Others Fail to connect + */ +int network_connect_wifi(const char *ssid, const char *password); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/examples/common/sys_state.c b/examples/common/sys_state.c new file mode 100644 index 0000000..7639589 --- /dev/null +++ b/examples/common/sys_state.c @@ -0,0 +1,135 @@ +/* Sys state + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_heap_trace.h" +#include "esp_heap_caps.h" +#include "sys_state.h" + +#define TAG "SYS_STATE" + +#define NUM_RECORDS 500 + +#if (CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID && CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS) +#include "esp_memory_utils.h" +#ifndef configRUN_TIME_COUNTER_TYPE +#define configRUN_TIME_COUNTER_TYPE uint32_t +#endif +static int get_task_info(TaskStatus_t **array, int *array_size, configRUN_TIME_COUNTER_TYPE *run_time) +{ + int size = uxTaskGetNumberOfTasks() + 8; + TaskStatus_t *new_array = realloc(*array, sizeof(TaskStatus_t) * size); + if (new_array == NULL) { + return -1; + } + size = uxTaskGetSystemState(new_array, size, run_time); + *array_size = size; + *array = new_array; + return 0; +} + +static void show_threads() +{ + static TaskStatus_t *start_array = NULL; + static TaskStatus_t *end_array = NULL; + const char *task_stack[] = { "Extr", "Intr" }; + const char *task_state[] = { "Running", "Ready", "Blocked", "Suspend", "Deleted" }; + int start_array_size, end_array_size; + configRUN_TIME_COUNTER_TYPE start_run_time = 0, end_run_time = 0; + if (get_task_info(&start_array, &start_array_size, &start_run_time) != 0) { + return; + } + vTaskDelay(pdMS_TO_TICKS(1000)); + if (get_task_info(&end_array, &end_array_size, &end_run_time) != 0) { + return; + } + uint32_t total_elapsed_time = (uint32_t)(end_run_time - start_run_time); + if (total_elapsed_time) { + ESP_LOGI(TAG, "| Task | Run Time | Per | Prio | HWM | State | CoreId | Stack "); + // Match each task in start_array to those in the end_array + for (int i = 0; i < start_array_size; i++) { + for (int j = 0; j < end_array_size; j++) { + if (start_array[i].xHandle == end_array[j].xHandle) { + uint32_t task_elapsed_time = (uint32_t)(end_array[j].ulRunTimeCounter - start_array[i].ulRunTimeCounter); + uint32_t percentage_time = (task_elapsed_time * 100UL) / (total_elapsed_time); + ESP_LOGI(TAG, "| %-17s | %-11d |%2d%% | %-4u | %-9u | %-7s | %-8x | %s", start_array[i].pcTaskName, + (int)task_elapsed_time, (int)percentage_time, start_array[i].uxCurrentPriority, + (int)start_array[i].usStackHighWaterMark, task_state[(start_array[i].eCurrentState)], + start_array[i].xCoreID, + task_stack[esp_ptr_internal(pxTaskGetStackStart(start_array[i].xHandle))]); + + start_array[i].xHandle = NULL; + end_array[j].xHandle = NULL; + break; + } + } + } + for (int i = 0; i < start_array_size; i++) { + if (start_array[i].xHandle != NULL) { + ESP_LOGI(TAG, "| %s | Deleted", start_array[i].pcTaskName); + } + } + for (int i = 0; i < end_array_size; i++) { + if (end_array[i].xHandle != NULL) { + ESP_LOGI(TAG, "| %s | Created", end_array[i].pcTaskName); + } + } + printf("\n"); + } +} +#endif + +void show_mem() +{ +#ifdef CONFIG_SPIRAM_BOOT_INIT + ESP_LOGI(TAG, "MEM Avail:%d, IRam:%d, PSRam:%d\n", (int)esp_get_free_heap_size(), + (int)heap_caps_get_free_size(MALLOC_CAP_INTERNAL), (int)heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); +#else + ESP_LOGI(TAG, "MEM Avail:%d\n", (int)esp_get_free_heap_size()); +#endif +} + +void sys_state_show() +{ + show_mem(); +#if (CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID && CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS) + show_threads(); +#endif +} + +void sys_state_heap_trace(bool start) +{ +#if CONFIG_IDF_TARGET_ESP32S3 + static heap_trace_record_t *trace_record; + if (trace_record == NULL) { + trace_record = heap_caps_malloc(NUM_RECORDS * sizeof(heap_trace_record_t), MALLOC_CAP_SPIRAM); + heap_trace_init_standalone(trace_record, NUM_RECORDS); + } + if (trace_record == NULL) { + ESP_LOGE(TAG, "No memory to start trace"); + return; + } + static bool started = false; + if (start) { + ESP_LOGI(TAG, "Start to trace"); + if (started == false) { + heap_trace_start(HEAP_TRACE_LEAKS); + started = true; + } else { + heap_trace_resume(); + } + } else { + heap_trace_alloc_pause(); + heap_trace_dump(); + } +#endif +} diff --git a/examples/common/sys_state.h b/examples/common/sys_state.h new file mode 100644 index 0000000..bcc6bc8 --- /dev/null +++ b/examples/common/sys_state.h @@ -0,0 +1,32 @@ +/* System state + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Show current system memory usage and cpu usage + */ +void sys_state_show(void); + +/** + * @brief Trace for heap memory + * + * @param[in] start Start or stop trace + */ +void sys_state_heap_trace(bool start); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/examples/voice_agent/CMakeLists.txt b/examples/voice_agent/CMakeLists.txt new file mode 100755 index 0000000..30e083f --- /dev/null +++ b/examples/voice_agent/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) +set(COMPONENTS main) # Trim build +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(voice_agent) \ No newline at end of file diff --git a/examples/voice_agent/README.md b/examples/voice_agent/README.md new file mode 100644 index 0000000..ae7ed36 --- /dev/null +++ b/examples/voice_agent/README.md @@ -0,0 +1,86 @@ +# Voice Agent + +Example application combining this SDK with [LiveKit Agents](https://docs.livekit.io/agents/), enabling bidirectional voice communication with an AI agent. The agent can interact with hardware in response to user requests. Below is an example of a conversation between a user and the agent: + +> **User:** What is the current CPU temperature? \ +> **Agent:** The CPU temperature is currently 33°C. + +> **User:** Turn on the blue LED. \ +> **Agent:** *[turns blue LED on]* + +> **User:** Turn on the yellow LED. \ +> **Agent:** I'm sorry, the board does not have a yellow LED. + +## Requirements + +- Software: + - [IDF](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/index.html) release v5.4 or later + - Python 3.9 or later + - LiveKit Cloud Project + - Sandbox Token Server (created from your cloud project) + - API keys for OpenAI, Deepgram, and Cartesia. +- Hardware + - Dev board: [ESP32-S3-Korvo-2](https://docs.espressif.com/projects/esp-adf/en/latest/design-guide/dev-boards/user-guide-esp32-s3-korvo-2.html) + - Two micro USB cables: one for power, one for flashing + - Mono enclosed speaker (example from [Adafruit](https://www.adafruit.com/product/3351)) + +## Run example + +To run the example on your board, begin in your terminal by navigating to the example's root directory: *[examples/voice_agent](./examples/voice_agent/)*. + +### 1. Configuration + +The example requires a network connection and Sandbox ID from your [LiveKit Cloud Project](https://cloud.livekit.io/projects/p_/sandbox/templates/token-server). To configure these settings from your terminal, launch *menuconfig*: +```sh +idf.py menuconfig +``` + +With *menuconfig* open, navigate to the *LiveKit Example* menu and configure the following settings: + +- Network → Wi-Fi SSID +- Network → Wi-Fi password +- Room connection → Sandbox ID + +For more information about available options, please refer to [this guide](../README.md#configuration). + +### 2. Build & flash + +Begin by connecting your dev board via USB. With the board connected, use the following command +to build the example, flash it to your board, and monitor serial output: + +```sh +idf.py flash monitor +``` + +Once running on device, the example will establish a network connection and then connect to a LiveKit room. Once connected, you will see the following log message: + +```sh +I (19508) livekit_example: Room state: connected +``` + +If you encounter any issues during this process, please refer to the example [troubleshooting guide](../README.md/#troubleshooting). + +## Run agent + +With the example running on your board, the next step is to run the agent so it can join the room. +Begin by navigating to the agent's source directory in your terminal: *[examples/voice_agent/agent](../voice_agent/agent)*. + +In this directory, create a *.env* file containing the required API keys: + +```sh +DEEPGRAM_API_KEY= +OPENAI_API_KEY= +CARTESIA_API_KEY= +LIVEKIT_API_KEY= +LIVEKIT_API_SECRET= +LIVEKIT_URL= +``` + +With the API keys in place, download the required files and run the agent in development mode as follows: + +```sh +python agent.py download-files +python agent.py dev +``` + +With the agent running, it will discover and join the room, and you will now be able to engage in two-way conversation. Try asking some of the questions shown above. \ No newline at end of file diff --git a/examples/voice_agent/agent/.gitignore b/examples/voice_agent/agent/.gitignore new file mode 100644 index 0000000..a681a85 --- /dev/null +++ b/examples/voice_agent/agent/.gitignore @@ -0,0 +1,13 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv + +# Environment variables +.env \ No newline at end of file diff --git a/examples/voice_agent/agent/.python-version b/examples/voice_agent/agent/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/examples/voice_agent/agent/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/examples/voice_agent/agent/agent.py b/examples/voice_agent/agent/agent.py new file mode 100644 index 0000000..a5acafa --- /dev/null +++ b/examples/voice_agent/agent/agent.py @@ -0,0 +1,109 @@ +import json +from enum import Enum +from dotenv import load_dotenv +from livekit import agents +from livekit.agents import ( + AgentSession, + Agent, + RunContext, + RoomInputOptions, + function_tool, + get_job_context, + ToolError, +) +from livekit.plugins import ( + openai, + cartesia, + deepgram, + silero, +) +from livekit.plugins.turn_detector.multilingual import MultilingualModel + +# If enabled, RPC calls will not be performed. +TEST_MODE = False + +load_dotenv() + +class LEDColor(str, Enum): + RED = "red" + BLUE = "blue" + +class Assistant(Agent): + def __init__(self) -> None: + super().__init__( + instructions="""You are a helpful voice AI assistant running on an ESP-32 dev board. + You answer user's questions about the hardware state and control the hardware based on their requests. + The board has discrete LEDs that can be controlled independently. Each LED has a static color + that cannot be changed. While you are able to set the state of the LEDs, you are not able to read the + state which could be changed without your knowledge. No markdown is allowed in your responses. + """ + ) + async def on_enter(self) -> None: + await self.session.say( + "Hi, how can I help you today?", + allow_interruptions=False + ) + + @function_tool() + async def set_led_state(self, _: RunContext, led: LEDColor, state: bool) -> None: + """Set the state of an on-board LED. + + Args: + led: Which LED to set the state of. + state: The state to set the LED to (i.e. on or off). + """ + if TEST_MODE: return + try: + room = get_job_context().room + participant_identity = next(iter(room.remote_participants)) + await room.local_participant.perform_rpc( + destination_identity=participant_identity, + method="set_led_state", + payload=json.dumps({ "color": led.value, "state": state }) + ) + except Exception: + raise ToolError("Unable to set LED state") + + @function_tool() + async def get_cpu_temp(self, _: RunContext) -> float: + """Get the current temperature of the CPU. + + Returns: + The temperature reading in degrees Celsius. + """ + if TEST_MODE: return 25.0 + try: + room = get_job_context().room + participant_identity = next(iter(room.remote_participants)) + response = await room.local_participant.perform_rpc( + destination_identity=participant_identity, + method="get_cpu_temp", + response_timeout=10, + payload="" + ) + if isinstance(response, str): + try: + response = float(response) + except ValueError: + raise ToolError("Received invalid temperature value") + return response + except Exception: + raise ToolError("Unable to retrieve CPU temperature") + +async def entrypoint(ctx: agents.JobContext): + session = AgentSession( + stt=deepgram.STT(model="nova-3", language="multi"), + llm=openai.LLM(model="gpt-4o-mini"), + tts=cartesia.TTS(model="sonic-2", voice="c99d36f3-5ffd-4253-803a-535c1bc9c306"), + vad=silero.VAD.load(), + turn_detection=MultilingualModel(), + ) + await session.start( + room=ctx.room, + agent=Assistant(), + room_input_options=RoomInputOptions() + ) + await ctx.connect() + +if __name__ == "__main__": + agents.cli.run_app(agents.WorkerOptions(entrypoint_fnc=entrypoint)) \ No newline at end of file diff --git a/examples/voice_agent/agent/pyproject.toml b/examples/voice_agent/agent/pyproject.toml new file mode 100644 index 0000000..320a8da --- /dev/null +++ b/examples/voice_agent/agent/pyproject.toml @@ -0,0 +1,11 @@ +[project] +name = "esp-example-agent" +version = "0.1.0" +description = "Example agent for interacting with the ESP-32 voice chat example." +readme = "README.md" +license = {text = "MIT"} +requires-python = ">=3.13" +dependencies = [ + "livekit-agents[cartesia,deepgram,openai,silero,turn-detector]~=1.0", + "python-dotenv>=1.1.1" +] diff --git a/examples/voice_agent/agent/uv.lock b/examples/voice_agent/agent/uv.lock new file mode 100644 index 0000000..0f5fc98 --- /dev/null +++ b/examples/voice_agent/agent/uv.lock @@ -0,0 +1,1334 @@ +version = 1 +revision = 1 +requires-python = ">=3.13" + +[[package]] +name = "aiofiles" +version = "24.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c", size = 30247 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5", size = 15896 }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, +] + +[[package]] +name = "aiohttp" +version = "3.12.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/6e/ab88e7cb2a4058bed2f7870276454f85a7c56cd6da79349eb314fc7bbcaa/aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce", size = 7819160 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/0f/db19abdf2d86aa1deec3c1e0e5ea46a587b97c07a16516b6438428b3a3f8/aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938", size = 694910 }, + { url = "https://files.pythonhosted.org/packages/d5/81/0ab551e1b5d7f1339e2d6eb482456ccbe9025605b28eed2b1c0203aaaade/aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace", size = 472566 }, + { url = "https://files.pythonhosted.org/packages/34/3f/6b7d336663337672d29b1f82d1f252ec1a040fe2d548f709d3f90fa2218a/aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb", size = 464856 }, + { url = "https://files.pythonhosted.org/packages/26/7f/32ca0f170496aa2ab9b812630fac0c2372c531b797e1deb3deb4cea904bd/aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7", size = 1703683 }, + { url = "https://files.pythonhosted.org/packages/ec/53/d5513624b33a811c0abea8461e30a732294112318276ce3dbf047dbd9d8b/aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b", size = 1684946 }, + { url = "https://files.pythonhosted.org/packages/37/72/4c237dd127827b0247dc138d3ebd49c2ded6114c6991bbe969058575f25f/aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177", size = 1737017 }, + { url = "https://files.pythonhosted.org/packages/0d/67/8a7eb3afa01e9d0acc26e1ef847c1a9111f8b42b82955fcd9faeb84edeb4/aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef", size = 1786390 }, + { url = "https://files.pythonhosted.org/packages/48/19/0377df97dd0176ad23cd8cad4fd4232cfeadcec6c1b7f036315305c98e3f/aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103", size = 1708719 }, + { url = "https://files.pythonhosted.org/packages/61/97/ade1982a5c642b45f3622255173e40c3eed289c169f89d00eeac29a89906/aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da", size = 1622424 }, + { url = "https://files.pythonhosted.org/packages/99/ab/00ad3eea004e1d07ccc406e44cfe2b8da5acb72f8c66aeeb11a096798868/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d", size = 1675447 }, + { url = "https://files.pythonhosted.org/packages/3f/fe/74e5ce8b2ccaba445fe0087abc201bfd7259431d92ae608f684fcac5d143/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041", size = 1707110 }, + { url = "https://files.pythonhosted.org/packages/ef/c4/39af17807f694f7a267bd8ab1fbacf16ad66740862192a6c8abac2bff813/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1", size = 1649706 }, + { url = "https://files.pythonhosted.org/packages/38/e8/f5a0a5f44f19f171d8477059aa5f28a158d7d57fe1a46c553e231f698435/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1", size = 1725839 }, + { url = "https://files.pythonhosted.org/packages/fd/ac/81acc594c7f529ef4419d3866913f628cd4fa9cab17f7bf410a5c3c04c53/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911", size = 1759311 }, + { url = "https://files.pythonhosted.org/packages/38/0d/aabe636bd25c6ab7b18825e5a97d40024da75152bec39aa6ac8b7a677630/aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3", size = 1708202 }, + { url = "https://files.pythonhosted.org/packages/1f/ab/561ef2d8a223261683fb95a6283ad0d36cb66c87503f3a7dde7afe208bb2/aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd", size = 420794 }, + { url = "https://files.pythonhosted.org/packages/9d/47/b11d0089875a23bff0abd3edb5516bcd454db3fefab8604f5e4b07bd6210/aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706", size = 446735 }, +] + +[[package]] +name = "aiosignal" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anyio" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, +] + +[[package]] +name = "av" +version = "14.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/f6/0b473dab52dfdea05f28f3578b1c56b6c796ce85e76951bab7c4e38d5a74/av-14.4.0.tar.gz", hash = "sha256:3ecbf803a7fdf67229c0edada0830d6bfaea4d10bfb24f0c3f4e607cd1064b42", size = 3892203 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/4c/b0205f77352312ff457ecdf31723dbf4403b7a03fc1659075d6d32f23ef7/av-14.4.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:3d2aea7c602b105363903e4017103bc4b60336e7aff80e1c22e8b4ec09fd125f", size = 19917341 }, + { url = "https://files.pythonhosted.org/packages/e1/c4/9e783bd7d47828e9c67f9c773c99de45c5ae01b3e942f1abf6cbaf530267/av-14.4.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:38c18f036aeb6dc9abf5e867d998c867f9ec93a5f722b60721fdffc123bbb2ae", size = 23715363 }, + { url = "https://files.pythonhosted.org/packages/b5/26/b2b406a676864d06b1c591205782d8527e7c99e5bc51a09862c3576e0087/av-14.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58c1e18c8be73b6eada2d9ec397852ec74ebe51938451bdf83644a807189d6c8", size = 33496968 }, + { url = "https://files.pythonhosted.org/packages/89/09/0a032bbe30c7049fca243ec8cf01f4be49dd6e7f7b9c3c7f0cc13f83c9d3/av-14.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4c32ff03a357feb030634f093089a73cb474b04efe7fbfba31f229cb2fab115", size = 32075498 }, + { url = "https://files.pythonhosted.org/packages/0b/1f/0fee20f74c1f48086366e59dbd37fa0684cd0f3c782a65cbb719d26c7acd/av-14.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af31d16ae25964a6a02e09cc132b9decd5ee493c5dcb21bcdf0d71b2d6adbd59", size = 35224910 }, + { url = "https://files.pythonhosted.org/packages/9e/19/1c4a201c75a2a431a85a43fd15d1fad55a28c22d596461d861c8d70f9b92/av-14.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e9fb297009e528f4851d25f3bb2781b2db18b59b10aed10240e947b77c582fb7", size = 36172918 }, + { url = "https://files.pythonhosted.org/packages/00/48/26b7e5d911c807f5f017a285362470ba16f44e8ea46f8b09ab5e348dd15b/av-14.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:573314cb9eafec2827dc98c416c965330dc7508193adbccd281700d8673b9f0a", size = 34414492 }, + { url = "https://files.pythonhosted.org/packages/6d/26/2f4badfa5b5b7b8f5f83d562b143a83ed940fa458eea4cad495ce95c9741/av-14.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f82ab27ee57c3b80eb50a5293222307dfdc02f810ea41119078cfc85ea3cf9a8", size = 37245826 }, + { url = "https://files.pythonhosted.org/packages/f4/02/88dbb6f5a05998b730d2e695b05060297af127ac4250efbe0739daa446d5/av-14.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f682003bbcaac620b52f68ff0e85830fff165dea53949e217483a615993ca20", size = 27898395 }, +] + +[[package]] +name = "certifi" +version = "2025.6.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650 }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622 }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435 }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653 }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231 }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243 }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442 }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147 }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057 }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454 }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174 }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166 }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, +] + +[[package]] +name = "click" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "coloredlogs" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "humanfriendly" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018 }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, +] + +[[package]] +name = "docstring-parser" +version = "0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/12/9c22a58c0b1e29271051222d8906257616da84135af9ed167c9e28f85cb3/docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e", size = 26565 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/7c/e9fcff7623954d86bdc17782036cbf715ecab1bec4847c008557affe1ca8/docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637", size = 36533 }, +] + +[[package]] +name = "esp-example-agent" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "livekit-agents", extra = ["cartesia", "deepgram", "openai", "silero", "turn-detector"] }, + { name = "python-dotenv" }, +] + +[package.metadata] +requires-dist = [ + { name = "livekit-agents", extras = ["cartesia", "deepgram", "openai", "silero", "turn-detector"], specifier = "~=1.0" }, + { name = "python-dotenv", specifier = ">=1.1.1" }, +] + +[[package]] +name = "eval-type-backport" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/ea/8b0ac4469d4c347c6a385ff09dc3c048c2d021696664e26c7ee6791631b5/eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1", size = 9079 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/31/55cd413eaccd39125368be33c46de24a1f639f2e12349b0361b4678f3915/eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a", size = 5830 }, +] + +[[package]] +name = "filelock" +version = "3.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215 }, +] + +[[package]] +name = "flatbuffers" +version = "25.2.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/30/eb5dce7994fc71a2f685d98ec33cc660c0a5887db5610137e60d8cbc4489/flatbuffers-25.2.10.tar.gz", hash = "sha256:97e451377a41262f8d9bd4295cc836133415cc03d8cb966410a4af92eb00d26e", size = 22170 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/25/155f9f080d5e4bc0082edfda032ea2bc2b8fab3f4d25d46c1e9dd22a1a89/flatbuffers-25.2.10-py2.py3-none-any.whl", hash = "sha256:ebba5f4d5ea615af3f7fd70fc310636fbb2bbd1f566ac0a23d98dd412de50051", size = 30953 }, +] + +[[package]] +name = "frozenlist" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/b1/b64018016eeb087db503b038296fd782586432b9c077fc5c7839e9cb6ef6/frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f", size = 45078 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/90/6b2cebdabdbd50367273c20ff6b57a3dfa89bd0762de02c3a1eb42cb6462/frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee", size = 79791 }, + { url = "https://files.pythonhosted.org/packages/83/2e/5b70b6a3325363293fe5fc3ae74cdcbc3e996c2a11dde2fd9f1fb0776d19/frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d", size = 47165 }, + { url = "https://files.pythonhosted.org/packages/f4/25/a0895c99270ca6966110f4ad98e87e5662eab416a17e7fd53c364bf8b954/frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43", size = 45881 }, + { url = "https://files.pythonhosted.org/packages/19/7c/71bb0bbe0832793c601fff68cd0cf6143753d0c667f9aec93d3c323f4b55/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d", size = 232409 }, + { url = "https://files.pythonhosted.org/packages/c0/45/ed2798718910fe6eb3ba574082aaceff4528e6323f9a8570be0f7028d8e9/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee", size = 225132 }, + { url = "https://files.pythonhosted.org/packages/ba/e2/8417ae0f8eacb1d071d4950f32f229aa6bf68ab69aab797b72a07ea68d4f/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb", size = 237638 }, + { url = "https://files.pythonhosted.org/packages/f8/b7/2ace5450ce85f2af05a871b8c8719b341294775a0a6c5585d5e6170f2ce7/frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f", size = 233539 }, + { url = "https://files.pythonhosted.org/packages/46/b9/6989292c5539553dba63f3c83dc4598186ab2888f67c0dc1d917e6887db6/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60", size = 215646 }, + { url = "https://files.pythonhosted.org/packages/72/31/bc8c5c99c7818293458fe745dab4fd5730ff49697ccc82b554eb69f16a24/frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00", size = 232233 }, + { url = "https://files.pythonhosted.org/packages/59/52/460db4d7ba0811b9ccb85af996019f5d70831f2f5f255f7cc61f86199795/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b", size = 227996 }, + { url = "https://files.pythonhosted.org/packages/ba/c9/f4b39e904c03927b7ecf891804fd3b4df3db29b9e487c6418e37988d6e9d/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c", size = 242280 }, + { url = "https://files.pythonhosted.org/packages/b8/33/3f8d6ced42f162d743e3517781566b8481322be321b486d9d262adf70bfb/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949", size = 217717 }, + { url = "https://files.pythonhosted.org/packages/3e/e8/ad683e75da6ccef50d0ab0c2b2324b32f84fc88ceee778ed79b8e2d2fe2e/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca", size = 236644 }, + { url = "https://files.pythonhosted.org/packages/b2/14/8d19ccdd3799310722195a72ac94ddc677541fb4bef4091d8e7775752360/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b", size = 238879 }, + { url = "https://files.pythonhosted.org/packages/ce/13/c12bf657494c2fd1079a48b2db49fa4196325909249a52d8f09bc9123fd7/frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e", size = 232502 }, + { url = "https://files.pythonhosted.org/packages/d7/8b/e7f9dfde869825489382bc0d512c15e96d3964180c9499efcec72e85db7e/frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1", size = 39169 }, + { url = "https://files.pythonhosted.org/packages/35/89/a487a98d94205d85745080a37860ff5744b9820a2c9acbcdd9440bfddf98/frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba", size = 43219 }, + { url = "https://files.pythonhosted.org/packages/56/d5/5c4cf2319a49eddd9dd7145e66c4866bdc6f3dbc67ca3d59685149c11e0d/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d", size = 84345 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/ec2c1e1dc16b85bc9d526009961953df9cec8481b6886debb36ec9107799/frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d", size = 48880 }, + { url = "https://files.pythonhosted.org/packages/69/86/f9596807b03de126e11e7d42ac91e3d0b19a6599c714a1989a4e85eeefc4/frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b", size = 48498 }, + { url = "https://files.pythonhosted.org/packages/5e/cb/df6de220f5036001005f2d726b789b2c0b65f2363b104bbc16f5be8084f8/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146", size = 292296 }, + { url = "https://files.pythonhosted.org/packages/83/1f/de84c642f17c8f851a2905cee2dae401e5e0daca9b5ef121e120e19aa825/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74", size = 273103 }, + { url = "https://files.pythonhosted.org/packages/88/3c/c840bfa474ba3fa13c772b93070893c6e9d5c0350885760376cbe3b6c1b3/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1", size = 292869 }, + { url = "https://files.pythonhosted.org/packages/a6/1c/3efa6e7d5a39a1d5ef0abeb51c48fb657765794a46cf124e5aca2c7a592c/frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1", size = 291467 }, + { url = "https://files.pythonhosted.org/packages/4f/00/d5c5e09d4922c395e2f2f6b79b9a20dab4b67daaf78ab92e7729341f61f6/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384", size = 266028 }, + { url = "https://files.pythonhosted.org/packages/4e/27/72765be905619dfde25a7f33813ac0341eb6b076abede17a2e3fbfade0cb/frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb", size = 284294 }, + { url = "https://files.pythonhosted.org/packages/88/67/c94103a23001b17808eb7dd1200c156bb69fb68e63fcf0693dde4cd6228c/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c", size = 281898 }, + { url = "https://files.pythonhosted.org/packages/42/34/a3e2c00c00f9e2a9db5653bca3fec306349e71aff14ae45ecc6d0951dd24/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65", size = 290465 }, + { url = "https://files.pythonhosted.org/packages/bb/73/f89b7fbce8b0b0c095d82b008afd0590f71ccb3dee6eee41791cf8cd25fd/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3", size = 266385 }, + { url = "https://files.pythonhosted.org/packages/cd/45/e365fdb554159462ca12df54bc59bfa7a9a273ecc21e99e72e597564d1ae/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657", size = 288771 }, + { url = "https://files.pythonhosted.org/packages/00/11/47b6117002a0e904f004d70ec5194fe9144f117c33c851e3d51c765962d0/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104", size = 288206 }, + { url = "https://files.pythonhosted.org/packages/40/37/5f9f3c3fd7f7746082ec67bcdc204db72dad081f4f83a503d33220a92973/frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf", size = 282620 }, + { url = "https://files.pythonhosted.org/packages/0b/31/8fbc5af2d183bff20f21aa743b4088eac4445d2bb1cdece449ae80e4e2d1/frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81", size = 43059 }, + { url = "https://files.pythonhosted.org/packages/bb/ed/41956f52105b8dbc26e457c5705340c67c8cc2b79f394b79bffc09d0e938/frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e", size = 47516 }, + { url = "https://files.pythonhosted.org/packages/ee/45/b82e3c16be2182bff01179db177fe144d58b5dc787a7d4492c6ed8b9317f/frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e", size = 13106 }, +] + +[[package]] +name = "fsspec" +version = "2025.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/00/f7/27f15d41f0ed38e8fcc488584b57e902b331da7f7c6dcda53721b15838fc/fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475", size = 303033 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/61/78c7b3851add1481b048b5fdc29067397a1784e2910592bc81bb3f608635/fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462", size = 199052 }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, +] + +[[package]] +name = "hf-xet" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/d4/7685999e85945ed0d7f0762b686ae7015035390de1161dcea9d5276c134c/hf_xet-1.1.5.tar.gz", hash = "sha256:69ebbcfd9ec44fdc2af73441619eeb06b94ee34511bbcf57cd423820090f5694", size = 495969 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/89/a1119eebe2836cb25758e7661d6410d3eae982e2b5e974bcc4d250be9012/hf_xet-1.1.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:f52c2fa3635b8c37c7764d8796dfa72706cc4eded19d638331161e82b0792e23", size = 2687929 }, + { url = "https://files.pythonhosted.org/packages/de/5f/2c78e28f309396e71ec8e4e9304a6483dcbc36172b5cea8f291994163425/hf_xet-1.1.5-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9fa6e3ee5d61912c4a113e0708eaaef987047616465ac7aa30f7121a48fc1af8", size = 2556338 }, + { url = "https://files.pythonhosted.org/packages/6d/2f/6cad7b5fe86b7652579346cb7f85156c11761df26435651cbba89376cd2c/hf_xet-1.1.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc874b5c843e642f45fd85cda1ce599e123308ad2901ead23d3510a47ff506d1", size = 3102894 }, + { url = "https://files.pythonhosted.org/packages/d0/54/0fcf2b619720a26fbb6cc941e89f2472a522cd963a776c089b189559447f/hf_xet-1.1.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dbba1660e5d810bd0ea77c511a99e9242d920790d0e63c0e4673ed36c4022d18", size = 3002134 }, + { url = "https://files.pythonhosted.org/packages/f3/92/1d351ac6cef7c4ba8c85744d37ffbfac2d53d0a6c04d2cabeba614640a78/hf_xet-1.1.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ab34c4c3104133c495785d5d8bba3b1efc99de52c02e759cf711a91fd39d3a14", size = 3171009 }, + { url = "https://files.pythonhosted.org/packages/c9/65/4b2ddb0e3e983f2508528eb4501288ae2f84963586fbdfae596836d5e57a/hf_xet-1.1.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:83088ecea236d5113de478acb2339f92c95b4fb0462acaa30621fac02f5a534a", size = 3279245 }, + { url = "https://files.pythonhosted.org/packages/f0/55/ef77a85ee443ae05a9e9cba1c9f0dd9241eb42da2aeba1dc50f51154c81a/hf_xet-1.1.5-cp37-abi3-win_amd64.whl", hash = "sha256:73e167d9807d166596b4b2f0b585c6d5bd84a26dea32843665a8b58f6edba245", size = 2738931 }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "huggingface-hub" +version = "0.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fa/42/8a95c5632080ae312c0498744b2b852195e10b05a20b1be11c5141092f4c/huggingface_hub-0.33.2.tar.gz", hash = "sha256:84221defaec8fa09c090390cd68c78b88e3c4c2b7befba68d3dc5aacbc3c2c5f", size = 426637 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/f4/5f3f22e762ad1965f01122b42dae5bf0e009286e2dba601ce1d0dba72424/huggingface_hub-0.33.2-py3-none-any.whl", hash = "sha256:3749498bfa91e8cde2ddc2c1db92c79981f40e66434c20133b39e5928ac9bcc5", size = 515373 }, +] + +[[package]] +name = "humanfriendly" +version = "10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyreadline3", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "jiter" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/9d/ae7ddb4b8ab3fb1b51faf4deb36cb48a4fbbd7cb36bad6a5fca4741306f7/jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500", size = 162759 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/b0/279597e7a270e8d22623fea6c5d4eeac328e7d95c236ed51a2b884c54f70/jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644", size = 311617 }, + { url = "https://files.pythonhosted.org/packages/91/e3/0916334936f356d605f54cc164af4060e3e7094364add445a3bc79335d46/jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a", size = 318947 }, + { url = "https://files.pythonhosted.org/packages/6a/8e/fd94e8c02d0e94539b7d669a7ebbd2776e51f329bb2c84d4385e8063a2ad/jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6", size = 344618 }, + { url = "https://files.pythonhosted.org/packages/6f/b0/f9f0a2ec42c6e9c2e61c327824687f1e2415b767e1089c1d9135f43816bd/jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3", size = 368829 }, + { url = "https://files.pythonhosted.org/packages/e8/57/5bbcd5331910595ad53b9fd0c610392ac68692176f05ae48d6ce5c852967/jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2", size = 491034 }, + { url = "https://files.pythonhosted.org/packages/9b/be/c393df00e6e6e9e623a73551774449f2f23b6ec6a502a3297aeeece2c65a/jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25", size = 388529 }, + { url = "https://files.pythonhosted.org/packages/42/3e/df2235c54d365434c7f150b986a6e35f41ebdc2f95acea3036d99613025d/jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041", size = 350671 }, + { url = "https://files.pythonhosted.org/packages/c6/77/71b0b24cbcc28f55ab4dbfe029f9a5b73aeadaba677843fc6dc9ed2b1d0a/jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca", size = 390864 }, + { url = "https://files.pythonhosted.org/packages/6a/d3/ef774b6969b9b6178e1d1e7a89a3bd37d241f3d3ec5f8deb37bbd203714a/jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4", size = 522989 }, + { url = "https://files.pythonhosted.org/packages/0c/41/9becdb1d8dd5d854142f45a9d71949ed7e87a8e312b0bede2de849388cb9/jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e", size = 513495 }, + { url = "https://files.pythonhosted.org/packages/9c/36/3468e5a18238bdedae7c4d19461265b5e9b8e288d3f86cd89d00cbb48686/jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d", size = 211289 }, + { url = "https://files.pythonhosted.org/packages/7e/07/1c96b623128bcb913706e294adb5f768fb7baf8db5e1338ce7b4ee8c78ef/jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4", size = 205074 }, + { url = "https://files.pythonhosted.org/packages/54/46/caa2c1342655f57d8f0f2519774c6d67132205909c65e9aa8255e1d7b4f4/jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca", size = 318225 }, + { url = "https://files.pythonhosted.org/packages/43/84/c7d44c75767e18946219ba2d703a5a32ab37b0bc21886a97bc6062e4da42/jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070", size = 350235 }, + { url = "https://files.pythonhosted.org/packages/01/16/f5a0135ccd968b480daad0e6ab34b0c7c5ba3bc447e5088152696140dcb3/jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca", size = 207278 }, + { url = "https://files.pythonhosted.org/packages/1c/9b/1d646da42c3de6c2188fdaa15bce8ecb22b635904fc68be025e21249ba44/jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522", size = 310866 }, + { url = "https://files.pythonhosted.org/packages/ad/0e/26538b158e8a7c7987e94e7aeb2999e2e82b1f9d2e1f6e9874ddf71ebda0/jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8", size = 318772 }, + { url = "https://files.pythonhosted.org/packages/7b/fb/d302893151caa1c2636d6574d213e4b34e31fd077af6050a9c5cbb42f6fb/jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216", size = 344534 }, + { url = "https://files.pythonhosted.org/packages/01/d8/5780b64a149d74e347c5128d82176eb1e3241b1391ac07935693466d6219/jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4", size = 369087 }, + { url = "https://files.pythonhosted.org/packages/e8/5b/f235a1437445160e777544f3ade57544daf96ba7e96c1a5b24a6f7ac7004/jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426", size = 490694 }, + { url = "https://files.pythonhosted.org/packages/85/a9/9c3d4617caa2ff89cf61b41e83820c27ebb3f7b5fae8a72901e8cd6ff9be/jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12", size = 388992 }, + { url = "https://files.pythonhosted.org/packages/68/b1/344fd14049ba5c94526540af7eb661871f9c54d5f5601ff41a959b9a0bbd/jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9", size = 351723 }, + { url = "https://files.pythonhosted.org/packages/41/89/4c0e345041186f82a31aee7b9d4219a910df672b9fef26f129f0cda07a29/jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a", size = 392215 }, + { url = "https://files.pythonhosted.org/packages/55/58/ee607863e18d3f895feb802154a2177d7e823a7103f000df182e0f718b38/jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853", size = 522762 }, + { url = "https://files.pythonhosted.org/packages/15/d0/9123fb41825490d16929e73c212de9a42913d68324a8ce3c8476cae7ac9d/jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86", size = 513427 }, + { url = "https://files.pythonhosted.org/packages/d8/b3/2bd02071c5a2430d0b70403a34411fc519c2f227da7b03da9ba6a956f931/jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357", size = 210127 }, + { url = "https://files.pythonhosted.org/packages/03/0c/5fe86614ea050c3ecd728ab4035534387cd41e7c1855ef6c031f1ca93e3f/jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00", size = 318527 }, + { url = "https://files.pythonhosted.org/packages/b3/4a/4175a563579e884192ba6e81725fc0448b042024419be8d83aa8a80a3f44/jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5", size = 354213 }, +] + +[[package]] +name = "livekit" +version = "1.0.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiofiles" }, + { name = "numpy" }, + { name = "protobuf" }, + { name = "types-protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/28/9eefa7ddf5dbe0664d9503b21274509c2edc97fba7001e2deec7b270a159/livekit-1.0.11.tar.gz", hash = "sha256:f3a38f6e67067b0807c79acd00e6877214aeaeb5628f84e44543103f7848ef90", size = 311200 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/aa/52181d0d98bad649893683a63965954b87a50a3a07373fde8d884d206cdb/livekit-1.0.11-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:b0acbed93eb1d152f9a03bd576f94d9750335d5e4bba32cdc07dfb8ea5c9da0c", size = 10805274 }, + { url = "https://files.pythonhosted.org/packages/8a/a8/52d2bf123e3c219248a8e54d7e40f3a91de51945e9c51b909fa154f2a346/livekit-1.0.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:615e0a1ffc57c424a57331c9051229dce2d4c6760484ae7ee968d5ba1d2163bc", size = 9516631 }, + { url = "https://files.pythonhosted.org/packages/59/eb/9790883b38d6df2618176f2686aafa57ca0cd18d9c3dbe31bc840a730d6a/livekit-1.0.11-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:c9d21de6fc8cd722da8b3db960023da5f1719edf9236c44d5e756127d1afc6b9", size = 10561388 }, + { url = "https://files.pythonhosted.org/packages/30/8e/1543d99b66449f6719c0250d761e4a62c243fb547ac59bcc5f3f8ed35af6/livekit-1.0.11-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:5c6ef90fb038f87ec5f85d1cf59f9e5e2a01dc7ea8c2ebe6a83750d291f9ce69", size = 12056402 }, + { url = "https://files.pythonhosted.org/packages/fb/c4/0dbb85176a12f5451bc0d7c8d356f774f9f72435bb354b76e8b3342f5df0/livekit-1.0.11-py3-none-win_amd64.whl", hash = "sha256:e118954092b3a11e601d0a246614e554bc93ef0aa5f18ee2b63957f1fb5fe38d", size = 11418672 }, +] + +[[package]] +name = "livekit-agents" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "av" }, + { name = "click" }, + { name = "colorama" }, + { name = "docstring-parser" }, + { name = "eval-type-backport" }, + { name = "livekit" }, + { name = "livekit-api" }, + { name = "livekit-protocol" }, + { name = "nest-asyncio" }, + { name = "numpy" }, + { name = "protobuf" }, + { name = "psutil" }, + { name = "pydantic" }, + { name = "pyjwt" }, + { name = "sounddevice" }, + { name = "types-protobuf" }, + { name = "typing-extensions" }, + { name = "watchfiles" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b6/72/8220d9047c96d5bb194292ac5076dc04898830031afaf672aab6c5a62452/livekit_agents-1.1.5.tar.gz", hash = "sha256:e0f7850cbf99f91e2edaf4f1c8423ef526b56696f63fdc0efdde5e0469ba846b", size = 482216 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/08/38d3e9efc1a13eec17377a4e4b07944a4c60f94c23b5cd06d3d24ab92e7d/livekit_agents-1.1.5-py3-none-any.whl", hash = "sha256:cccb79fc9e4cbaede529bb83dcbd8e6ce531baeaaddbcdc541f6955fafa693d2", size = 540232 }, +] + +[package.optional-dependencies] +cartesia = [ + { name = "livekit-plugins-cartesia" }, +] +codecs = [ + { name = "av" }, + { name = "numpy" }, +] +deepgram = [ + { name = "livekit-plugins-deepgram" }, +] +images = [ + { name = "pillow" }, +] +openai = [ + { name = "livekit-plugins-openai" }, +] +silero = [ + { name = "livekit-plugins-silero" }, +] +turn-detector = [ + { name = "livekit-plugins-turn-detector" }, +] + +[[package]] +name = "livekit-api" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "livekit-protocol" }, + { name = "protobuf" }, + { name = "pyjwt" }, + { name = "types-protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/cf/2476359a6eab73fab0e58ca0053887a9344db3ba7caca5a29913e270a86f/livekit_api-1.0.3.tar.gz", hash = "sha256:24ffd1f0a92fd91f1d9977034e317951259d0ec9d053c6315c1562ba699d4cc8", size = 14940 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/b3/ff1c47a553d8773281aef531fb660e296e811176390b887901e1bcd351ab/livekit_api-1.0.3-py3-none-any.whl", hash = "sha256:38eee0ba44e9bba7eb95d53d29a6ac56b89e32e6f90dfa11d2f65463db8f06c5", size = 17413 }, +] + +[[package]] +name = "livekit-plugins-cartesia" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "livekit-agents" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/51/b1fd9501ea9e4c3fce21f7eb05f94ee3b3a37054109d69535294833f5113/livekit_plugins_cartesia-1.1.5.tar.gz", hash = "sha256:e415719fa408a971244769bb91c294e6b5047dc3a56a6cf052c30d174424ad3c", size = 9951 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/44/fd7d3831e19b92f19bb1c3373ec81fd51c6a81759db668fecd1e41327227/livekit_plugins_cartesia-1.1.5-py3-none-any.whl", hash = "sha256:3cb35455c5261f26b6f88c99b32778046abf84555fc4e1803e23ef7f83edefbb", size = 12453 }, +] + +[[package]] +name = "livekit-plugins-deepgram" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "livekit-agents", extra = ["codecs"] }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/f5/61c8567396523aac84de2381e573ebe0130b256d3261f5444f54728302c4/livekit_plugins_deepgram-1.1.5.tar.gz", hash = "sha256:b46f7e231eb6bddd852d0e6f9b73b534fab55cf3ed7ee66bbc67dfc49cff773b", size = 12956 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/3e/585795606da64d4f113e650c52c6926ee2eefdee4a317eea7d11af0eafa6/livekit_plugins_deepgram-1.1.5-py3-none-any.whl", hash = "sha256:bfe00c6df58c5da34a166fd2c97f4b8e69a26afebe56e59c8f2f995a550158d0", size = 15099 }, +] + +[[package]] +name = "livekit-plugins-openai" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "livekit-agents", extra = ["codecs", "images"] }, + { name = "openai", extra = ["realtime"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d8/9c/23ff7db39842bc82da62106fd90842c21d4d7f290639685570e42a63f255/livekit_plugins_openai-1.1.5.tar.gz", hash = "sha256:dbee556aa419d1b16ba88623f6ea81299447f4c93baab7aa02048834ab05eca5", size = 29288 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/4d/5c485051166fdf2fbbb0e633c2a09a13d91a187cdd727a50e1ccad862374/livekit_plugins_openai-1.1.5-py3-none-any.whl", hash = "sha256:c955d51c69c2406886af88335f0798cf695c43da4f35306e7fe8538c70ed1ab9", size = 34548 }, +] + +[[package]] +name = "livekit-plugins-silero" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "livekit-agents" }, + { name = "numpy" }, + { name = "onnxruntime" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/fe/a4a0fdc97f99c40b9b902457c828bc3d87b409e980d3b65d247d69e57b3a/livekit_plugins_silero-1.1.5.tar.gz", hash = "sha256:ac2dfeaa0c06bf7bcf73a76bd27e0214141ee997b8ab46e9cbaca8c220f19f5d", size = 1952538 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/76/3d2d4c9b3f835b2cde126c89629e1a2bde3657d66562ba55473b97f51d2a/livekit_plugins_silero-1.1.5-py3-none-any.whl", hash = "sha256:1853abdb24989906fda958533c16d1be413e2a0f40637a10b6dd7abb8aa6d6a0", size = 3898410 }, +] + +[[package]] +name = "livekit-plugins-turn-detector" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "livekit-agents" }, + { name = "numpy" }, + { name = "onnxruntime" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b8/8a/fb3bcdea489f298c65cbb7c1ee9c17fef0c84d65a44f6321fa286018d542/livekit_plugins_turn_detector-1.1.5.tar.gz", hash = "sha256:92eb7216165aa0ea8402ea41d8dfa16ce97904f8f74a2d4d185d7f5dad830e4e", size = 7199 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/f8/1a366e2c21d8435d62ee2df251819872bfcdfc4713dd32dfbf53570878ed/livekit_plugins_turn_detector-1.1.5-py3-none-any.whl", hash = "sha256:e532f408813b42a070c2463ed0b3bdd6d8ee5609e272ac55e545c0aaf63ca538", size = 8758 }, +] + +[[package]] +name = "livekit-protocol" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, + { name = "types-protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/75/ab0439ba2ce999e6212b32250ff8f0c93b56b0b3e95859c92052784b9d66/livekit_protocol-1.0.4.tar.gz", hash = "sha256:370477f7af376bbeb59ae3b26a052c1b93375abf1a69eb71c50b78bf73c70194", size = 56503 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/eb/88bc73fa434cb9cba85c7011bc40289205eb45784931226c9357f130796b/livekit_protocol-1.0.4-py3-none-any.whl", hash = "sha256:8359b3c7a3ebe1aa89c15db9e0128c3e2e95f3a49796941b04acf0b44310c4a0", size = 66823 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 }, +] + +[[package]] +name = "multidict" +version = "6.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/2c/5dad12e82fbdf7470f29bff2171484bf07cb3b16ada60a6589af8f376440/multidict-6.6.3.tar.gz", hash = "sha256:798a9eb12dab0a6c2e29c1de6f3468af5cb2da6053a20dfa3344907eed0937cc", size = 101006 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/1d/0bebcbbb4f000751fbd09957257903d6e002943fc668d841a4cf2fb7f872/multidict-6.6.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:540d3c06d48507357a7d57721e5094b4f7093399a0106c211f33540fdc374d55", size = 75843 }, + { url = "https://files.pythonhosted.org/packages/07/8f/cbe241b0434cfe257f65c2b1bcf9e8d5fb52bc708c5061fb29b0fed22bdf/multidict-6.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9c19cea2a690f04247d43f366d03e4eb110a0dc4cd1bbeee4d445435428ed35b", size = 45053 }, + { url = "https://files.pythonhosted.org/packages/32/d2/0b3b23f9dbad5b270b22a3ac3ea73ed0a50ef2d9a390447061178ed6bdb8/multidict-6.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7af039820cfd00effec86bda5d8debef711a3e86a1d3772e85bea0f243a4bd65", size = 43273 }, + { url = "https://files.pythonhosted.org/packages/fd/fe/6eb68927e823999e3683bc49678eb20374ba9615097d085298fd5b386564/multidict-6.6.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:500b84f51654fdc3944e936f2922114349bf8fdcac77c3092b03449f0e5bc2b3", size = 237124 }, + { url = "https://files.pythonhosted.org/packages/e7/ab/320d8507e7726c460cb77117848b3834ea0d59e769f36fdae495f7669929/multidict-6.6.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3fc723ab8a5c5ed6c50418e9bfcd8e6dceba6c271cee6728a10a4ed8561520c", size = 256892 }, + { url = "https://files.pythonhosted.org/packages/76/60/38ee422db515ac69834e60142a1a69111ac96026e76e8e9aa347fd2e4591/multidict-6.6.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:94c47ea3ade005b5976789baaed66d4de4480d0a0bf31cef6edaa41c1e7b56a6", size = 240547 }, + { url = "https://files.pythonhosted.org/packages/27/fb/905224fde2dff042b030c27ad95a7ae744325cf54b890b443d30a789b80e/multidict-6.6.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dbc7cf464cc6d67e83e136c9f55726da3a30176f020a36ead246eceed87f1cd8", size = 266223 }, + { url = "https://files.pythonhosted.org/packages/76/35/dc38ab361051beae08d1a53965e3e1a418752fc5be4d3fb983c5582d8784/multidict-6.6.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:900eb9f9da25ada070f8ee4a23f884e0ee66fe4e1a38c3af644256a508ad81ca", size = 267262 }, + { url = "https://files.pythonhosted.org/packages/1f/a3/0a485b7f36e422421b17e2bbb5a81c1af10eac1d4476f2ff92927c730479/multidict-6.6.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c6df517cf177da5d47ab15407143a89cd1a23f8b335f3a28d57e8b0a3dbb884", size = 254345 }, + { url = "https://files.pythonhosted.org/packages/b4/59/bcdd52c1dab7c0e0d75ff19cac751fbd5f850d1fc39172ce809a74aa9ea4/multidict-6.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ef421045f13879e21c994b36e728d8e7d126c91a64b9185810ab51d474f27e7", size = 252248 }, + { url = "https://files.pythonhosted.org/packages/bb/a4/2d96aaa6eae8067ce108d4acee6f45ced5728beda55c0f02ae1072c730d1/multidict-6.6.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6c1e61bb4f80895c081790b6b09fa49e13566df8fbff817da3f85b3a8192e36b", size = 250115 }, + { url = "https://files.pythonhosted.org/packages/25/d2/ed9f847fa5c7d0677d4f02ea2c163d5e48573de3f57bacf5670e43a5ffaa/multidict-6.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e5e8523bb12d7623cd8300dbd91b9e439a46a028cd078ca695eb66ba31adee3c", size = 249649 }, + { url = "https://files.pythonhosted.org/packages/1f/af/9155850372563fc550803d3f25373308aa70f59b52cff25854086ecb4a79/multidict-6.6.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ef58340cc896219e4e653dade08fea5c55c6df41bcc68122e3be3e9d873d9a7b", size = 261203 }, + { url = "https://files.pythonhosted.org/packages/36/2f/c6a728f699896252cf309769089568a33c6439626648843f78743660709d/multidict-6.6.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc9dc435ec8699e7b602b94fe0cd4703e69273a01cbc34409af29e7820f777f1", size = 258051 }, + { url = "https://files.pythonhosted.org/packages/d0/60/689880776d6b18fa2b70f6cc74ff87dd6c6b9b47bd9cf74c16fecfaa6ad9/multidict-6.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9e864486ef4ab07db5e9cb997bad2b681514158d6954dd1958dfb163b83d53e6", size = 249601 }, + { url = "https://files.pythonhosted.org/packages/75/5e/325b11f2222a549019cf2ef879c1f81f94a0d40ace3ef55cf529915ba6cc/multidict-6.6.3-cp313-cp313-win32.whl", hash = "sha256:5633a82fba8e841bc5c5c06b16e21529573cd654f67fd833650a215520a6210e", size = 41683 }, + { url = "https://files.pythonhosted.org/packages/b1/ad/cf46e73f5d6e3c775cabd2a05976547f3f18b39bee06260369a42501f053/multidict-6.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:e93089c1570a4ad54c3714a12c2cef549dc9d58e97bcded193d928649cab78e9", size = 45811 }, + { url = "https://files.pythonhosted.org/packages/c5/c9/2e3fe950db28fb7c62e1a5f46e1e38759b072e2089209bc033c2798bb5ec/multidict-6.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:c60b401f192e79caec61f166da9c924e9f8bc65548d4246842df91651e83d600", size = 43056 }, + { url = "https://files.pythonhosted.org/packages/3a/58/aaf8114cf34966e084a8cc9517771288adb53465188843d5a19862cb6dc3/multidict-6.6.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:02fd8f32d403a6ff13864b0851f1f523d4c988051eea0471d4f1fd8010f11134", size = 82811 }, + { url = "https://files.pythonhosted.org/packages/71/af/5402e7b58a1f5b987a07ad98f2501fdba2a4f4b4c30cf114e3ce8db64c87/multidict-6.6.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f3aa090106b1543f3f87b2041eef3c156c8da2aed90c63a2fbed62d875c49c37", size = 48304 }, + { url = "https://files.pythonhosted.org/packages/39/65/ab3c8cafe21adb45b24a50266fd747147dec7847425bc2a0f6934b3ae9ce/multidict-6.6.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e924fb978615a5e33ff644cc42e6aa241effcf4f3322c09d4f8cebde95aff5f8", size = 46775 }, + { url = "https://files.pythonhosted.org/packages/49/ba/9fcc1b332f67cc0c0c8079e263bfab6660f87fe4e28a35921771ff3eea0d/multidict-6.6.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b9fe5a0e57c6dbd0e2ce81ca66272282c32cd11d31658ee9553849d91289e1c1", size = 229773 }, + { url = "https://files.pythonhosted.org/packages/a4/14/0145a251f555f7c754ce2dcbcd012939bbd1f34f066fa5d28a50e722a054/multidict-6.6.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b24576f208793ebae00280c59927c3b7c2a3b1655e443a25f753c4611bc1c373", size = 250083 }, + { url = "https://files.pythonhosted.org/packages/9e/d4/d5c0bd2bbb173b586c249a151a26d2fb3ec7d53c96e42091c9fef4e1f10c/multidict-6.6.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:135631cb6c58eac37d7ac0df380294fecdc026b28837fa07c02e459c7fb9c54e", size = 228980 }, + { url = "https://files.pythonhosted.org/packages/21/32/c9a2d8444a50ec48c4733ccc67254100c10e1c8ae8e40c7a2d2183b59b97/multidict-6.6.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:274d416b0df887aef98f19f21578653982cfb8a05b4e187d4a17103322eeaf8f", size = 257776 }, + { url = "https://files.pythonhosted.org/packages/68/d0/14fa1699f4ef629eae08ad6201c6b476098f5efb051b296f4c26be7a9fdf/multidict-6.6.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e252017a817fad7ce05cafbe5711ed40faeb580e63b16755a3a24e66fa1d87c0", size = 256882 }, + { url = "https://files.pythonhosted.org/packages/da/88/84a27570fbe303c65607d517a5f147cd2fc046c2d1da02b84b17b9bdc2aa/multidict-6.6.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4cc8d848cd4fe1cdee28c13ea79ab0ed37fc2e89dd77bac86a2e7959a8c3bc", size = 247816 }, + { url = "https://files.pythonhosted.org/packages/1c/60/dca352a0c999ce96a5d8b8ee0b2b9f729dcad2e0b0c195f8286269a2074c/multidict-6.6.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9e236a7094b9c4c1b7585f6b9cca34b9d833cf079f7e4c49e6a4a6ec9bfdc68f", size = 245341 }, + { url = "https://files.pythonhosted.org/packages/50/ef/433fa3ed06028f03946f3993223dada70fb700f763f70c00079533c34578/multidict-6.6.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e0cb0ab69915c55627c933f0b555a943d98ba71b4d1c57bc0d0a66e2567c7471", size = 235854 }, + { url = "https://files.pythonhosted.org/packages/1b/1f/487612ab56fbe35715320905215a57fede20de7db40a261759690dc80471/multidict-6.6.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:81ef2f64593aba09c5212a3d0f8c906a0d38d710a011f2f42759704d4557d3f2", size = 243432 }, + { url = "https://files.pythonhosted.org/packages/da/6f/ce8b79de16cd885c6f9052c96a3671373d00c59b3ee635ea93e6e81b8ccf/multidict-6.6.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:b9cbc60010de3562545fa198bfc6d3825df430ea96d2cc509c39bd71e2e7d648", size = 252731 }, + { url = "https://files.pythonhosted.org/packages/bb/fe/a2514a6aba78e5abefa1624ca85ae18f542d95ac5cde2e3815a9fbf369aa/multidict-6.6.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70d974eaaa37211390cd02ef93b7e938de564bbffa866f0b08d07e5e65da783d", size = 247086 }, + { url = "https://files.pythonhosted.org/packages/8c/22/b788718d63bb3cce752d107a57c85fcd1a212c6c778628567c9713f9345a/multidict-6.6.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3713303e4a6663c6d01d648a68f2848701001f3390a030edaaf3fc949c90bf7c", size = 243338 }, + { url = "https://files.pythonhosted.org/packages/22/d6/fdb3d0670819f2228f3f7d9af613d5e652c15d170c83e5f1c94fbc55a25b/multidict-6.6.3-cp313-cp313t-win32.whl", hash = "sha256:639ecc9fe7cd73f2495f62c213e964843826f44505a3e5d82805aa85cac6f89e", size = 47812 }, + { url = "https://files.pythonhosted.org/packages/b6/d6/a9d2c808f2c489ad199723197419207ecbfbc1776f6e155e1ecea9c883aa/multidict-6.6.3-cp313-cp313t-win_amd64.whl", hash = "sha256:9f97e181f344a0ef3881b573d31de8542cc0dbc559ec68c8f8b5ce2c2e91646d", size = 53011 }, + { url = "https://files.pythonhosted.org/packages/f2/40/b68001cba8188dd267590a111f9661b6256debc327137667e832bf5d66e8/multidict-6.6.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ce8b7693da41a3c4fde5871c738a81490cea5496c671d74374c8ab889e1834fb", size = 45254 }, + { url = "https://files.pythonhosted.org/packages/d8/30/9aec301e9772b098c1f5c0ca0279237c9766d94b97802e9888010c64b0ed/multidict-6.6.3-py3-none-any.whl", hash = "sha256:8db10f29c7541fc5da4defd8cd697e1ca429db743fa716325f236079b96f775a", size = 12313 }, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, +] + +[[package]] +name = "numpy" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/19/d7c972dfe90a353dbd3efbbe1d14a5951de80c99c9dc1b93cd998d51dc0f/numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", size = 20390372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/bd/35ad97006d8abff8631293f8ea6adf07b0108ce6fec68da3c3fcca1197f2/numpy-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", size = 20889381 }, + { url = "https://files.pythonhosted.org/packages/f1/4f/df5923874d8095b6062495b39729178eef4a922119cee32a12ee1bd4664c/numpy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", size = 14152726 }, + { url = "https://files.pythonhosted.org/packages/8c/0f/a1f269b125806212a876f7efb049b06c6f8772cf0121139f97774cd95626/numpy-2.3.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", size = 5105145 }, + { url = "https://files.pythonhosted.org/packages/6d/63/a7f7fd5f375b0361682f6ffbf686787e82b7bbd561268e4f30afad2bb3c0/numpy-2.3.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", size = 6639409 }, + { url = "https://files.pythonhosted.org/packages/bf/0d/1854a4121af895aab383f4aa233748f1df4671ef331d898e32426756a8a6/numpy-2.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", size = 14257630 }, + { url = "https://files.pythonhosted.org/packages/50/30/af1b277b443f2fb08acf1c55ce9d68ee540043f158630d62cef012750f9f/numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", size = 16627546 }, + { url = "https://files.pythonhosted.org/packages/6e/ec/3b68220c277e463095342d254c61be8144c31208db18d3fd8ef02712bcd6/numpy-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", size = 15562538 }, + { url = "https://files.pythonhosted.org/packages/77/2b/4014f2bcc4404484021c74d4c5ee8eb3de7e3f7ac75f06672f8dcf85140a/numpy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", size = 18360327 }, + { url = "https://files.pythonhosted.org/packages/40/8d/2ddd6c9b30fcf920837b8672f6c65590c7d92e43084c25fc65edc22e93ca/numpy-2.3.1-cp313-cp313-win32.whl", hash = "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", size = 6312330 }, + { url = "https://files.pythonhosted.org/packages/dd/c8/beaba449925988d415efccb45bf977ff8327a02f655090627318f6398c7b/numpy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", size = 12731565 }, + { url = "https://files.pythonhosted.org/packages/0b/c3/5c0c575d7ec78c1126998071f58facfc124006635da75b090805e642c62e/numpy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", size = 10190262 }, + { url = "https://files.pythonhosted.org/packages/ea/19/a029cd335cf72f79d2644dcfc22d90f09caa86265cbbde3b5702ccef6890/numpy-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", size = 20987593 }, + { url = "https://files.pythonhosted.org/packages/25/91/8ea8894406209107d9ce19b66314194675d31761fe2cb3c84fe2eeae2f37/numpy-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", size = 14300523 }, + { url = "https://files.pythonhosted.org/packages/a6/7f/06187b0066eefc9e7ce77d5f2ddb4e314a55220ad62dd0bfc9f2c44bac14/numpy-2.3.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", size = 5227993 }, + { url = "https://files.pythonhosted.org/packages/e8/ec/a926c293c605fa75e9cfb09f1e4840098ed46d2edaa6e2152ee35dc01ed3/numpy-2.3.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", size = 6736652 }, + { url = "https://files.pythonhosted.org/packages/e3/62/d68e52fb6fde5586650d4c0ce0b05ff3a48ad4df4ffd1b8866479d1d671d/numpy-2.3.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", size = 14331561 }, + { url = "https://files.pythonhosted.org/packages/fc/ec/b74d3f2430960044bdad6900d9f5edc2dc0fb8bf5a0be0f65287bf2cbe27/numpy-2.3.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", size = 16693349 }, + { url = "https://files.pythonhosted.org/packages/0d/15/def96774b9d7eb198ddadfcbd20281b20ebb510580419197e225f5c55c3e/numpy-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", size = 15642053 }, + { url = "https://files.pythonhosted.org/packages/2b/57/c3203974762a759540c6ae71d0ea2341c1fa41d84e4971a8e76d7141678a/numpy-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", size = 18434184 }, + { url = "https://files.pythonhosted.org/packages/22/8a/ccdf201457ed8ac6245187850aff4ca56a79edbea4829f4e9f14d46fa9a5/numpy-2.3.1-cp313-cp313t-win32.whl", hash = "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", size = 6440678 }, + { url = "https://files.pythonhosted.org/packages/f1/7e/7f431d8bd8eb7e03d79294aed238b1b0b174b3148570d03a8a8a8f6a0da9/numpy-2.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", size = 12870697 }, + { url = "https://files.pythonhosted.org/packages/d4/ca/af82bf0fad4c3e573c6930ed743b5308492ff19917c7caaf2f9b6f9e2e98/numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", size = 10260376 }, +] + +[[package]] +name = "onnxruntime" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coloredlogs" }, + { name = "flatbuffers" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "sympy" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/65/5cb5018d5b0b7cba820d2c4a1d1b02d40df538d49138ba36a509457e4df6/onnxruntime-1.22.0-cp313-cp313-macosx_13_0_universal2.whl", hash = "sha256:fe7c051236aae16d8e2e9ffbfc1e115a0cc2450e873a9c4cb75c0cc96c1dae07", size = 34298715 }, + { url = "https://files.pythonhosted.org/packages/e1/89/1dfe1b368831d1256b90b95cb8d11da8ab769febd5c8833ec85ec1f79d21/onnxruntime-1.22.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a6bbed10bc5e770c04d422893d3045b81acbbadc9fb759a2cd1ca00993da919", size = 14443266 }, + { url = "https://files.pythonhosted.org/packages/1e/70/342514ade3a33ad9dd505dcee96ff1f0e7be6d0e6e9c911fe0f1505abf42/onnxruntime-1.22.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9fe45ee3e756300fccfd8d61b91129a121d3d80e9d38e01f03ff1295badc32b8", size = 16406707 }, + { url = "https://files.pythonhosted.org/packages/3e/89/2f64e250945fa87140fb917ba377d6d0e9122e029c8512f389a9b7f953f4/onnxruntime-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:5a31d84ef82b4b05d794a4ce8ba37b0d9deb768fd580e36e17b39e0b4840253b", size = 12691777 }, + { url = "https://files.pythonhosted.org/packages/9f/48/d61d5f1ed098161edd88c56cbac49207d7b7b149e613d2cd7e33176c63b3/onnxruntime-1.22.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2ac5bd9205d831541db4e508e586e764a74f14efdd3f89af7fd20e1bf4a1ed", size = 14454003 }, + { url = "https://files.pythonhosted.org/packages/c3/16/873b955beda7bada5b0d798d3a601b2ff210e44ad5169f6d405b93892103/onnxruntime-1.22.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64845709f9e8a2809e8e009bc4c8f73b788cee9c6619b7d9930344eae4c9cd36", size = 16427482 }, +] + +[[package]] +name = "openai" +version = "1.93.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/d7/e91c6a9cf71726420cddf539852ee4c29176ebb716a702d9118d0409fd8e/openai-1.93.0.tar.gz", hash = "sha256:988f31ade95e1ff0585af11cc5a64510225e4f5cd392698c675d0a9265b8e337", size = 486573 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/46/a10d9df4673df56f71201d129ba1cb19eaff3366d08c8664d61a7df52e65/openai-1.93.0-py3-none-any.whl", hash = "sha256:3d746fe5498f0dd72e0d9ab706f26c91c0f646bf7459e5629af8ba7c9dbdf090", size = 755038 }, +] + +[package.optional-dependencies] +realtime = [ + { name = "websockets" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, +] + +[[package]] +name = "pillow" +version = "11.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328 }, + { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652 }, + { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443 }, + { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474 }, + { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038 }, + { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407 }, + { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094 }, + { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503 }, + { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574 }, + { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060 }, + { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407 }, + { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841 }, + { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450 }, + { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055 }, + { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110 }, + { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547 }, + { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554 }, + { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132 }, + { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001 }, + { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814 }, + { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124 }, + { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186 }, + { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546 }, + { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102 }, + { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803 }, + { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520 }, + { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116 }, + { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597 }, + { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246 }, + { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336 }, + { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699 }, + { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789 }, + { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386 }, + { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911 }, + { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383 }, + { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385 }, + { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129 }, + { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580 }, + { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860 }, + { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694 }, + { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888 }, + { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330 }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089 }, + { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206 }, + { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370 }, + { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500 }, + { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835 }, +] + +[[package]] +name = "propcache" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/16/43264e4a779dd8588c21a70f0709665ee8f611211bdd2c87d952cfa7c776/propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168", size = 44139 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/d1/8c747fafa558c603c4ca19d8e20b288aa0c7cda74e9402f50f31eb65267e/propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945", size = 71286 }, + { url = "https://files.pythonhosted.org/packages/61/99/d606cb7986b60d89c36de8a85d58764323b3a5ff07770a99d8e993b3fa73/propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252", size = 42425 }, + { url = "https://files.pythonhosted.org/packages/8c/96/ef98f91bbb42b79e9bb82bdd348b255eb9d65f14dbbe3b1594644c4073f7/propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f", size = 41846 }, + { url = "https://files.pythonhosted.org/packages/5b/ad/3f0f9a705fb630d175146cd7b1d2bf5555c9beaed54e94132b21aac098a6/propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33", size = 208871 }, + { url = "https://files.pythonhosted.org/packages/3a/38/2085cda93d2c8b6ec3e92af2c89489a36a5886b712a34ab25de9fbca7992/propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e", size = 215720 }, + { url = "https://files.pythonhosted.org/packages/61/c1/d72ea2dc83ac7f2c8e182786ab0fc2c7bd123a1ff9b7975bee671866fe5f/propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1", size = 215203 }, + { url = "https://files.pythonhosted.org/packages/af/81/b324c44ae60c56ef12007105f1460d5c304b0626ab0cc6b07c8f2a9aa0b8/propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3", size = 206365 }, + { url = "https://files.pythonhosted.org/packages/09/73/88549128bb89e66d2aff242488f62869014ae092db63ccea53c1cc75a81d/propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1", size = 196016 }, + { url = "https://files.pythonhosted.org/packages/b9/3f/3bdd14e737d145114a5eb83cb172903afba7242f67c5877f9909a20d948d/propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6", size = 205596 }, + { url = "https://files.pythonhosted.org/packages/0f/ca/2f4aa819c357d3107c3763d7ef42c03980f9ed5c48c82e01e25945d437c1/propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387", size = 200977 }, + { url = "https://files.pythonhosted.org/packages/cd/4a/e65276c7477533c59085251ae88505caf6831c0e85ff8b2e31ebcbb949b1/propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4", size = 197220 }, + { url = "https://files.pythonhosted.org/packages/7c/54/fc7152e517cf5578278b242396ce4d4b36795423988ef39bb8cd5bf274c8/propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88", size = 210642 }, + { url = "https://files.pythonhosted.org/packages/b9/80/abeb4a896d2767bf5f1ea7b92eb7be6a5330645bd7fb844049c0e4045d9d/propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206", size = 212789 }, + { url = "https://files.pythonhosted.org/packages/b3/db/ea12a49aa7b2b6d68a5da8293dcf50068d48d088100ac016ad92a6a780e6/propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43", size = 205880 }, + { url = "https://files.pythonhosted.org/packages/d1/e5/9076a0bbbfb65d1198007059c65639dfd56266cf8e477a9707e4b1999ff4/propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02", size = 37220 }, + { url = "https://files.pythonhosted.org/packages/d3/f5/b369e026b09a26cd77aa88d8fffd69141d2ae00a2abaaf5380d2603f4b7f/propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05", size = 40678 }, + { url = "https://files.pythonhosted.org/packages/a4/3a/6ece377b55544941a08d03581c7bc400a3c8cd3c2865900a68d5de79e21f/propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b", size = 76560 }, + { url = "https://files.pythonhosted.org/packages/0c/da/64a2bb16418740fa634b0e9c3d29edff1db07f56d3546ca2d86ddf0305e1/propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0", size = 44676 }, + { url = "https://files.pythonhosted.org/packages/36/7b/f025e06ea51cb72c52fb87e9b395cced02786610b60a3ed51da8af017170/propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e", size = 44701 }, + { url = "https://files.pythonhosted.org/packages/a4/00/faa1b1b7c3b74fc277f8642f32a4c72ba1d7b2de36d7cdfb676db7f4303e/propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28", size = 276934 }, + { url = "https://files.pythonhosted.org/packages/74/ab/935beb6f1756e0476a4d5938ff44bf0d13a055fed880caf93859b4f1baf4/propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a", size = 278316 }, + { url = "https://files.pythonhosted.org/packages/f8/9d/994a5c1ce4389610838d1caec74bdf0e98b306c70314d46dbe4fcf21a3e2/propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c", size = 282619 }, + { url = "https://files.pythonhosted.org/packages/2b/00/a10afce3d1ed0287cef2e09506d3be9822513f2c1e96457ee369adb9a6cd/propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725", size = 265896 }, + { url = "https://files.pythonhosted.org/packages/2e/a8/2aa6716ffa566ca57c749edb909ad27884680887d68517e4be41b02299f3/propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892", size = 252111 }, + { url = "https://files.pythonhosted.org/packages/36/4f/345ca9183b85ac29c8694b0941f7484bf419c7f0fea2d1e386b4f7893eed/propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44", size = 268334 }, + { url = "https://files.pythonhosted.org/packages/3e/ca/fcd54f78b59e3f97b3b9715501e3147f5340167733d27db423aa321e7148/propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe", size = 255026 }, + { url = "https://files.pythonhosted.org/packages/8b/95/8e6a6bbbd78ac89c30c225210a5c687790e532ba4088afb8c0445b77ef37/propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81", size = 250724 }, + { url = "https://files.pythonhosted.org/packages/ee/b0/0dd03616142baba28e8b2d14ce5df6631b4673850a3d4f9c0f9dd714a404/propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba", size = 268868 }, + { url = "https://files.pythonhosted.org/packages/c5/98/2c12407a7e4fbacd94ddd32f3b1e3d5231e77c30ef7162b12a60e2dd5ce3/propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770", size = 271322 }, + { url = "https://files.pythonhosted.org/packages/35/91/9cb56efbb428b006bb85db28591e40b7736847b8331d43fe335acf95f6c8/propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330", size = 265778 }, + { url = "https://files.pythonhosted.org/packages/9a/4c/b0fe775a2bdd01e176b14b574be679d84fc83958335790f7c9a686c1f468/propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394", size = 41175 }, + { url = "https://files.pythonhosted.org/packages/a4/ff/47f08595e3d9b5e149c150f88d9714574f1a7cbd89fe2817158a952674bf/propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198", size = 44857 }, + { url = "https://files.pythonhosted.org/packages/cc/35/cc0aaecf278bb4575b8555f2b137de5ab821595ddae9da9d3cd1da4072c7/propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f", size = 12663 }, +] + +[[package]] +name = "protobuf" +version = "6.31.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/f3/b9655a711b32c19720253f6f06326faf90580834e2e83f840472d752bc8b/protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a", size = 441797 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/6f/6ab8e4bf962fd5570d3deaa2d5c38f0a363f57b4501047b5ebeb83ab1125/protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9", size = 423603 }, + { url = "https://files.pythonhosted.org/packages/44/3a/b15c4347dd4bf3a1b0ee882f384623e2063bb5cf9fa9d57990a4f7df2fb6/protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447", size = 435283 }, + { url = "https://files.pythonhosted.org/packages/6a/c9/b9689a2a250264a84e66c46d8862ba788ee7a641cdca39bccf64f59284b7/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402", size = 425604 }, + { url = "https://files.pythonhosted.org/packages/76/a1/7a5a94032c83375e4fe7e7f56e3976ea6ac90c5e85fac8576409e25c39c3/protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39", size = 322115 }, + { url = "https://files.pythonhosted.org/packages/fa/b1/b59d405d64d31999244643d88c45c8241c58f17cc887e73bcb90602327f8/protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6", size = 321070 }, + { url = "https://files.pythonhosted.org/packages/f7/af/ab3c51ab7507a7325e98ffe691d9495ee3d3aa5f589afad65ec920d39821/protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e", size = 168724 }, +] + +[[package]] +name = "psutil" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051 }, + { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535 }, + { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004 }, + { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986 }, + { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544 }, + { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053 }, + { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885 }, +] + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, +] + +[[package]] +name = "pydantic" +version = "2.11.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782 }, +] + +[[package]] +name = "pydantic-core" +version = "2.33.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ad/88/5f2260bdfae97aabf98f1778d43f69574390ad787afb646292a638c923d4/pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", size = 435195 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/8c/99040727b41f56616573a28771b1bfa08a3d3fe74d3d513f01251f79f172/pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", size = 2015688 }, + { url = "https://files.pythonhosted.org/packages/3a/cc/5999d1eb705a6cefc31f0b4a90e9f7fc400539b1a1030529700cc1b51838/pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", size = 1844808 }, + { url = "https://files.pythonhosted.org/packages/6f/5e/a0a7b8885c98889a18b6e376f344da1ef323d270b44edf8174d6bce4d622/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", size = 1885580 }, + { url = "https://files.pythonhosted.org/packages/3b/2a/953581f343c7d11a304581156618c3f592435523dd9d79865903272c256a/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", size = 1973859 }, + { url = "https://files.pythonhosted.org/packages/e6/55/f1a813904771c03a3f97f676c62cca0c0a4138654107c1b61f19c644868b/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", size = 2120810 }, + { url = "https://files.pythonhosted.org/packages/aa/c3/053389835a996e18853ba107a63caae0b9deb4a276c6b472931ea9ae6e48/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", size = 2676498 }, + { url = "https://files.pythonhosted.org/packages/eb/3c/f4abd740877a35abade05e437245b192f9d0ffb48bbbbd708df33d3cda37/pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", size = 2000611 }, + { url = "https://files.pythonhosted.org/packages/59/a7/63ef2fed1837d1121a894d0ce88439fe3e3b3e48c7543b2a4479eb99c2bd/pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", size = 2107924 }, + { url = "https://files.pythonhosted.org/packages/04/8f/2551964ef045669801675f1cfc3b0d74147f4901c3ffa42be2ddb1f0efc4/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", size = 2063196 }, + { url = "https://files.pythonhosted.org/packages/26/bd/d9602777e77fc6dbb0c7db9ad356e9a985825547dce5ad1d30ee04903918/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", size = 2236389 }, + { url = "https://files.pythonhosted.org/packages/42/db/0e950daa7e2230423ab342ae918a794964b053bec24ba8af013fc7c94846/pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", size = 2239223 }, + { url = "https://files.pythonhosted.org/packages/58/4d/4f937099c545a8a17eb52cb67fe0447fd9a373b348ccfa9a87f141eeb00f/pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", size = 1900473 }, + { url = "https://files.pythonhosted.org/packages/a0/75/4a0a9bac998d78d889def5e4ef2b065acba8cae8c93696906c3a91f310ca/pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", size = 1955269 }, + { url = "https://files.pythonhosted.org/packages/f9/86/1beda0576969592f1497b4ce8e7bc8cbdf614c352426271b1b10d5f0aa64/pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", size = 1893921 }, + { url = "https://files.pythonhosted.org/packages/a4/7d/e09391c2eebeab681df2b74bfe6c43422fffede8dc74187b2b0bf6fd7571/pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", size = 1806162 }, + { url = "https://files.pythonhosted.org/packages/f1/3d/847b6b1fed9f8ed3bb95a9ad04fbd0b212e832d4f0f50ff4d9ee5a9f15cf/pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", size = 1981560 }, + { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 }, +] + +[[package]] +name = "pyjwt" +version = "2.10.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997 }, +] + +[[package]] +name = "pyreadline3" +version = "3.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/49/4cea918a08f02817aabae639e3d0ac046fef9f9180518a3ad394e22da148/pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7", size = 99839 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/dc/491b7661614ab97483abf2056be1deee4dc2490ecbf7bff9ab5cdbac86e1/pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6", size = 83178 }, +] + +[[package]] +name = "python-dotenv" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, +] + +[[package]] +name = "regex" +version = "2024.11.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, + { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, + { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, + { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, + { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, + { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, + { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, + { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, + { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, + { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, + { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, + { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, + { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, + { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, + { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, +] + +[[package]] +name = "requests" +version = "2.32.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847 }, +] + +[[package]] +name = "safetensors" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/ae/88f6c49dbd0cc4da0e08610019a3c78a7d390879a919411a410a1876d03a/safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073", size = 436917 }, + { url = "https://files.pythonhosted.org/packages/b8/3b/11f1b4a2f5d2ab7da34ecc062b0bc301f2be024d110a6466726bec8c055c/safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7", size = 418419 }, + { url = "https://files.pythonhosted.org/packages/5d/9a/add3e6fef267658075c5a41573c26d42d80c935cdc992384dfae435feaef/safetensors-0.5.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11bce6164887cd491ca75c2326a113ba934be596e22b28b1742ce27b1d076467", size = 459493 }, + { url = "https://files.pythonhosted.org/packages/df/5c/bf2cae92222513cc23b3ff85c4a1bb2811a2c3583ac0f8e8d502751de934/safetensors-0.5.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4a243be3590bc3301c821da7a18d87224ef35cbd3e5f5727e4e0728b8172411e", size = 472400 }, + { url = "https://files.pythonhosted.org/packages/58/11/7456afb740bd45782d0f4c8e8e1bb9e572f1bf82899fb6ace58af47b4282/safetensors-0.5.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8bd84b12b1670a6f8e50f01e28156422a2bc07fb16fc4e98bded13039d688a0d", size = 522891 }, + { url = "https://files.pythonhosted.org/packages/57/3d/fe73a9d2ace487e7285f6e157afee2383bd1ddb911b7cb44a55cf812eae3/safetensors-0.5.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:391ac8cab7c829452175f871fcaf414aa1e292b5448bd02620f675a7f3e7abb9", size = 537694 }, + { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642 }, + { url = "https://files.pythonhosted.org/packages/ce/20/1fbe16f9b815f6c5a672f5b760951e20e17e43f67f231428f871909a37f6/safetensors-0.5.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1077f3e94182d72618357b04b5ced540ceb71c8a813d3319f1aba448e68a770d", size = 502241 }, + { url = "https://files.pythonhosted.org/packages/5f/18/8e108846b506487aa4629fe4116b27db65c3dde922de2c8e0cc1133f3f29/safetensors-0.5.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:799021e78287bac619c7b3f3606730a22da4cda27759ddf55d37c8db7511c74b", size = 638001 }, + { url = "https://files.pythonhosted.org/packages/82/5a/c116111d8291af6c8c8a8b40628fe833b9db97d8141c2a82359d14d9e078/safetensors-0.5.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df26da01aaac504334644e1b7642fa000bfec820e7cef83aeac4e355e03195ff", size = 734013 }, + { url = "https://files.pythonhosted.org/packages/7d/ff/41fcc4d3b7de837963622e8610d998710705bbde9a8a17221d85e5d0baad/safetensors-0.5.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:32c3ef2d7af8b9f52ff685ed0bc43913cdcde135089ae322ee576de93eae5135", size = 670687 }, + { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147 }, + { url = "https://files.pythonhosted.org/packages/0a/0c/95aeb51d4246bd9a3242d3d8349c1112b4ee7611a4b40f0c5c93b05f001d/safetensors-0.5.3-cp38-abi3-win32.whl", hash = "sha256:cfc0ec0846dcf6763b0ed3d1846ff36008c6e7290683b61616c4b040f6a54ace", size = 296677 }, + { url = "https://files.pythonhosted.org/packages/69/e2/b011c38e5394c4c18fb5500778a55ec43ad6106126e74723ffaee246f56e/safetensors-0.5.3-cp38-abi3-win_amd64.whl", hash = "sha256:836cbbc320b47e80acd40e44c8682db0e8ad7123209f69b093def21ec7cafd11", size = 308878 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "sounddevice" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/a6/91e9f08ed37c7c9f56b5227c6aea7f2ae63ba2d59520eefb24e82cbdd589/sounddevice-0.5.2.tar.gz", hash = "sha256:c634d51bd4e922d6f0fa5e1a975cc897c947f61d31da9f79ba7ea34dff448b49", size = 53150 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/2d/582738fc01352a5bc20acac9221e58538365cecb3bb264838f66419df219/sounddevice-0.5.2-py3-none-any.whl", hash = "sha256:82375859fac2e73295a4ab3fc60bd4782743157adc339561c1f1142af472f505", size = 32450 }, + { url = "https://files.pythonhosted.org/packages/3f/6f/e3dd751face4fcb5be25e8abba22f25d8e6457ebd7e9ed79068b768dc0e5/sounddevice-0.5.2-py3-none-macosx_10_6_x86_64.macosx_10_6_universal2.whl", hash = "sha256:943f27e66037d41435bdd0293454072cdf657b594c9cde63cd01ee3daaac7ab3", size = 108088 }, + { url = "https://files.pythonhosted.org/packages/45/0b/bfad79af0b380aa7c0bfe73e4b03e0af45354a48ad62549489bd7696c5b0/sounddevice-0.5.2-py3-none-win32.whl", hash = "sha256:3a113ce614a2c557f14737cb20123ae6298c91fc9301eb014ada0cba6d248c5f", size = 312665 }, + { url = "https://files.pythonhosted.org/packages/e1/3e/61d88e6b0a7383127cdc779195cb9d83ebcf11d39bc961de5777e457075e/sounddevice-0.5.2-py3-none-win_amd64.whl", hash = "sha256:e18944b767d2dac3771a7771bdd7ff7d3acd7d334e72c4bedab17d1aed5dbc22", size = 363808 }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353 }, +] + +[[package]] +name = "tokenizers" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/2d/b0fce2b8201635f60e8c95990080f58461cc9ca3d5026de2e900f38a7f21/tokenizers-0.21.2.tar.gz", hash = "sha256:fdc7cffde3e2113ba0e6cc7318c40e3438a4d74bbc62bf04bcc63bdfb082ac77", size = 351545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/cc/2936e2d45ceb130a21d929743f1e9897514691bec123203e10837972296f/tokenizers-0.21.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:342b5dfb75009f2255ab8dec0041287260fed5ce00c323eb6bab639066fef8ec", size = 2875206 }, + { url = "https://files.pythonhosted.org/packages/6c/e6/33f41f2cc7861faeba8988e7a77601407bf1d9d28fc79c5903f8f77df587/tokenizers-0.21.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:126df3205d6f3a93fea80c7a8a266a78c1bd8dd2fe043386bafdd7736a23e45f", size = 2732655 }, + { url = "https://files.pythonhosted.org/packages/33/2b/1791eb329c07122a75b01035b1a3aa22ad139f3ce0ece1b059b506d9d9de/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a32cd81be21168bd0d6a0f0962d60177c447a1aa1b1e48fa6ec9fc728ee0b12", size = 3019202 }, + { url = "https://files.pythonhosted.org/packages/05/15/fd2d8104faa9f86ac68748e6f7ece0b5eb7983c7efc3a2c197cb98c99030/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8bd8999538c405133c2ab999b83b17c08b7fc1b48c1ada2469964605a709ef91", size = 2934539 }, + { url = "https://files.pythonhosted.org/packages/a5/2e/53e8fd053e1f3ffbe579ca5f9546f35ac67cf0039ed357ad7ec57f5f5af0/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e9944e61239b083a41cf8fc42802f855e1dca0f499196df37a8ce219abac6eb", size = 3248665 }, + { url = "https://files.pythonhosted.org/packages/00/15/79713359f4037aa8f4d1f06ffca35312ac83629da062670e8830917e2153/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:514cd43045c5d546f01142ff9c79a96ea69e4b5cda09e3027708cb2e6d5762ab", size = 3451305 }, + { url = "https://files.pythonhosted.org/packages/38/5f/959f3a8756fc9396aeb704292777b84f02a5c6f25c3fc3ba7530db5feb2c/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1b9405822527ec1e0f7d8d2fdb287a5730c3a6518189c968254a8441b21faae", size = 3214757 }, + { url = "https://files.pythonhosted.org/packages/c5/74/f41a432a0733f61f3d21b288de6dfa78f7acff309c6f0f323b2833e9189f/tokenizers-0.21.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed9a4d51c395103ad24f8e7eb976811c57fbec2af9f133df471afcd922e5020", size = 3121887 }, + { url = "https://files.pythonhosted.org/packages/3c/6a/bc220a11a17e5d07b0dfb3b5c628621d4dcc084bccd27cfaead659963016/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c41862df3d873665ec78b6be36fcc30a26e3d4902e9dd8608ed61d49a48bc19", size = 9091965 }, + { url = "https://files.pythonhosted.org/packages/6c/bd/ac386d79c4ef20dc6f39c4706640c24823dca7ebb6f703bfe6b5f0292d88/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed21dc7e624e4220e21758b2e62893be7101453525e3d23264081c9ef9a6d00d", size = 9053372 }, + { url = "https://files.pythonhosted.org/packages/63/7b/5440bf203b2a5358f074408f7f9c42884849cd9972879e10ee6b7a8c3b3d/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:0e73770507e65a0e0e2a1affd6b03c36e3bc4377bd10c9ccf51a82c77c0fe365", size = 9298632 }, + { url = "https://files.pythonhosted.org/packages/a4/d2/faa1acac3f96a7427866e94ed4289949b2524f0c1878512516567d80563c/tokenizers-0.21.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:106746e8aa9014a12109e58d540ad5465b4c183768ea96c03cbc24c44d329958", size = 9470074 }, + { url = "https://files.pythonhosted.org/packages/d8/a5/896e1ef0707212745ae9f37e84c7d50269411aef2e9ccd0de63623feecdf/tokenizers-0.21.2-cp39-abi3-win32.whl", hash = "sha256:cabda5a6d15d620b6dfe711e1af52205266d05b379ea85a8a301b3593c60e962", size = 2330115 }, + { url = "https://files.pythonhosted.org/packages/13/c3/cc2755ee10be859c4338c962a35b9a663788c0c0b50c0bdd8078fb6870cf/tokenizers-0.21.2-cp39-abi3-win_amd64.whl", hash = "sha256:58747bb898acdb1007f37a7bbe614346e98dc28708ffb66a3fd50ce169ac6c98", size = 2509918 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "transformers" +version = "4.53.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/40/f2d2c3bcf5c6135027cab0fd7db52f6149a1c23acc4e45f914c43d362386/transformers-4.53.0.tar.gz", hash = "sha256:f89520011b4a73066fdc7aabfa158317c3934a22e3cd652d7ffbc512c4063841", size = 9177265 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/0c/68d03a38f6ab2ba2b2829eb11b334610dd236e7926787f7656001b68e1f2/transformers-4.53.0-py3-none-any.whl", hash = "sha256:7d8039ff032c01a2d7f8a8fe0066620367003275f023815a966e62203f9f5dd7", size = 10821970 }, +] + +[[package]] +name = "types-protobuf" +version = "4.25.0.20240417" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/21/757620113af23233496c04b8a66e0201e78695495b1db8e676672608588b/types-protobuf-4.25.0.20240417.tar.gz", hash = "sha256:c34eff17b9b3a0adb6830622f0f302484e4c089f533a46e3f147568313544352", size = 53340 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/78/6f0351f80a682c4005775c7e1fd2b17235b88d87c85ef59214bd1f60ff60/types_protobuf-4.25.0.20240417-py3-none-any.whl", hash = "sha256:e9b613227c2127e3d4881d75d93c93b4d6fd97b5f6a099a0b654a05351c8685d", size = 67896 }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839 }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7609f121aaa6b609744687f1d158b3c3a5bf4cc94238/typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28", size = 75726 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552 }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 }, +] + +[[package]] +name = "watchfiles" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004 }, + { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671 }, + { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772 }, + { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789 }, + { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551 }, + { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420 }, + { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950 }, + { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706 }, + { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814 }, + { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820 }, + { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194 }, + { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349 }, + { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836 }, + { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343 }, + { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916 }, + { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582 }, + { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752 }, + { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436 }, + { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016 }, + { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727 }, + { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864 }, + { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626 }, + { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744 }, + { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114 }, + { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879 }, + { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026 }, + { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917 }, + { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602 }, + { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758 }, + { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601 }, + { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936 }, + { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243 }, + { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073 }, + { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872 }, + { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877 }, + { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645 }, + { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424 }, + { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584 }, + { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675 }, + { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363 }, + { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240 }, + { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607 }, + { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315 }, +] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 }, +] + +[[package]] +name = "yarl" +version = "1.20.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/fb/efaa23fa4e45537b827620f04cf8f3cd658b76642205162e072703a5b963/yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac", size = 186428 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/e1/2411b6d7f769a07687acee88a062af5833cf1966b7266f3d8dfb3d3dc7d3/yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a", size = 131811 }, + { url = "https://files.pythonhosted.org/packages/b2/27/584394e1cb76fb771371770eccad35de400e7b434ce3142c2dd27392c968/yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3", size = 90078 }, + { url = "https://files.pythonhosted.org/packages/bf/9a/3246ae92d4049099f52d9b0fe3486e3b500e29b7ea872d0f152966fc209d/yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7", size = 88748 }, + { url = "https://files.pythonhosted.org/packages/a3/25/35afe384e31115a1a801fbcf84012d7a066d89035befae7c5d4284df1e03/yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691", size = 349595 }, + { url = "https://files.pythonhosted.org/packages/28/2d/8aca6cb2cabc8f12efcb82749b9cefecbccfc7b0384e56cd71058ccee433/yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31", size = 342616 }, + { url = "https://files.pythonhosted.org/packages/0b/e9/1312633d16b31acf0098d30440ca855e3492d66623dafb8e25b03d00c3da/yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28", size = 361324 }, + { url = "https://files.pythonhosted.org/packages/bc/a0/688cc99463f12f7669eec7c8acc71ef56a1521b99eab7cd3abb75af887b0/yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653", size = 359676 }, + { url = "https://files.pythonhosted.org/packages/af/44/46407d7f7a56e9a85a4c207724c9f2c545c060380718eea9088f222ba697/yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5", size = 352614 }, + { url = "https://files.pythonhosted.org/packages/b1/91/31163295e82b8d5485d31d9cf7754d973d41915cadce070491778d9c9825/yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02", size = 336766 }, + { url = "https://files.pythonhosted.org/packages/b4/8e/c41a5bc482121f51c083c4c2bcd16b9e01e1cf8729e380273a952513a21f/yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53", size = 364615 }, + { url = "https://files.pythonhosted.org/packages/e3/5b/61a3b054238d33d70ea06ebba7e58597891b71c699e247df35cc984ab393/yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc", size = 360982 }, + { url = "https://files.pythonhosted.org/packages/df/a3/6a72fb83f8d478cb201d14927bc8040af901811a88e0ff2da7842dd0ed19/yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04", size = 369792 }, + { url = "https://files.pythonhosted.org/packages/7c/af/4cc3c36dfc7c077f8dedb561eb21f69e1e9f2456b91b593882b0b18c19dc/yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4", size = 382049 }, + { url = "https://files.pythonhosted.org/packages/19/3a/e54e2c4752160115183a66dc9ee75a153f81f3ab2ba4bf79c3c53b33de34/yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b", size = 384774 }, + { url = "https://files.pythonhosted.org/packages/9c/20/200ae86dabfca89060ec6447649f219b4cbd94531e425e50d57e5f5ac330/yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1", size = 374252 }, + { url = "https://files.pythonhosted.org/packages/83/75/11ee332f2f516b3d094e89448da73d557687f7d137d5a0f48c40ff211487/yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7", size = 81198 }, + { url = "https://files.pythonhosted.org/packages/ba/ba/39b1ecbf51620b40ab402b0fc817f0ff750f6d92712b44689c2c215be89d/yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c", size = 86346 }, + { url = "https://files.pythonhosted.org/packages/43/c7/669c52519dca4c95153c8ad96dd123c79f354a376346b198f438e56ffeb4/yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d", size = 138826 }, + { url = "https://files.pythonhosted.org/packages/6a/42/fc0053719b44f6ad04a75d7f05e0e9674d45ef62f2d9ad2c1163e5c05827/yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf", size = 93217 }, + { url = "https://files.pythonhosted.org/packages/4f/7f/fa59c4c27e2a076bba0d959386e26eba77eb52ea4a0aac48e3515c186b4c/yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3", size = 92700 }, + { url = "https://files.pythonhosted.org/packages/2f/d4/062b2f48e7c93481e88eff97a6312dca15ea200e959f23e96d8ab898c5b8/yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d", size = 347644 }, + { url = "https://files.pythonhosted.org/packages/89/47/78b7f40d13c8f62b499cc702fdf69e090455518ae544c00a3bf4afc9fc77/yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c", size = 323452 }, + { url = "https://files.pythonhosted.org/packages/eb/2b/490d3b2dc66f52987d4ee0d3090a147ea67732ce6b4d61e362c1846d0d32/yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1", size = 346378 }, + { url = "https://files.pythonhosted.org/packages/66/ad/775da9c8a94ce925d1537f939a4f17d782efef1f973039d821cbe4bcc211/yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce", size = 353261 }, + { url = "https://files.pythonhosted.org/packages/4b/23/0ed0922b47a4f5c6eb9065d5ff1e459747226ddce5c6a4c111e728c9f701/yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3", size = 335987 }, + { url = "https://files.pythonhosted.org/packages/3e/49/bc728a7fe7d0e9336e2b78f0958a2d6b288ba89f25a1762407a222bf53c3/yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be", size = 329361 }, + { url = "https://files.pythonhosted.org/packages/93/8f/b811b9d1f617c83c907e7082a76e2b92b655400e61730cd61a1f67178393/yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16", size = 346460 }, + { url = "https://files.pythonhosted.org/packages/70/fd/af94f04f275f95da2c3b8b5e1d49e3e79f1ed8b6ceb0f1664cbd902773ff/yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513", size = 334486 }, + { url = "https://files.pythonhosted.org/packages/84/65/04c62e82704e7dd0a9b3f61dbaa8447f8507655fd16c51da0637b39b2910/yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f", size = 342219 }, + { url = "https://files.pythonhosted.org/packages/91/95/459ca62eb958381b342d94ab9a4b6aec1ddec1f7057c487e926f03c06d30/yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390", size = 350693 }, + { url = "https://files.pythonhosted.org/packages/a6/00/d393e82dd955ad20617abc546a8f1aee40534d599ff555ea053d0ec9bf03/yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458", size = 355803 }, + { url = "https://files.pythonhosted.org/packages/9e/ed/c5fb04869b99b717985e244fd93029c7a8e8febdfcffa06093e32d7d44e7/yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e", size = 341709 }, + { url = "https://files.pythonhosted.org/packages/24/fd/725b8e73ac2a50e78a4534ac43c6addf5c1c2d65380dd48a9169cc6739a9/yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d", size = 86591 }, + { url = "https://files.pythonhosted.org/packages/94/c3/b2e9f38bc3e11191981d57ea08cab2166e74ea770024a646617c9cddd9f6/yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f", size = 93003 }, + { url = "https://files.pythonhosted.org/packages/b4/2d/2345fce04cfd4bee161bf1e7d9cdc702e3e16109021035dbb24db654a622/yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77", size = 46542 }, +] diff --git a/examples/voice_agent/main/CMakeLists.txt b/examples/voice_agent/main/CMakeLists.txt new file mode 100755 index 0000000..6e75b98 --- /dev/null +++ b/examples/voice_agent/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" "example.c" "board.c" "media.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/examples/voice_agent/main/board.c b/examples/voice_agent/main/board.c new file mode 100755 index 0000000..e28b4df --- /dev/null +++ b/examples/voice_agent/main/board.c @@ -0,0 +1,43 @@ +#include "esp_log.h" +#include "codec_init.h" +#include "codec_board.h" +#include "driver/temperature_sensor.h" +#include "bsp/esp-bsp.h" + +#include "board.h" + +static const char *TAG = "board"; + +static temperature_sensor_handle_t temp_sensor = NULL; + +void board_init() +{ + ESP_LOGI(TAG, "Initializing board"); + + // Initialize board support package and LEDs + bsp_i2c_init(); + bsp_leds_init(); + bsp_led_set(BSP_LED_RED, true); + bsp_led_set(BSP_LED_BLUE, true); + + // Initialize temperature sensor + temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(10, 50); + ESP_ERROR_CHECK(temperature_sensor_install(&temp_sensor_config, &temp_sensor)); + ESP_ERROR_CHECK(temperature_sensor_enable(temp_sensor)); + + // Initialize codec board + set_codec_board_type(CONFIG_CODEC_BOARD_TYPE); + codec_init_cfg_t cfg = { + .in_mode = CODEC_I2S_MODE_TDM, + .in_use_tdm = true, + .reuse_dev = false + }; + init_codec(&cfg); +} + +float board_get_temp(void) +{ + float temp_out; + ESP_ERROR_CHECK(temperature_sensor_get_celsius(temp_sensor, &temp_out)); + return temp_out; +} \ No newline at end of file diff --git a/examples/voice_agent/main/board.h b/examples/voice_agent/main/board.h new file mode 100644 index 0000000..dee616e --- /dev/null +++ b/examples/voice_agent/main/board.h @@ -0,0 +1,15 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/// Initialize board. +void board_init(void); + +/// Read the chip's internal temperature in degrees Celsius. +float board_get_temp(void); + +#ifdef __cplusplus +} +#endif diff --git a/examples/voice_agent/main/example.c b/examples/voice_agent/main/example.c new file mode 100644 index 0000000..3faeb65 --- /dev/null +++ b/examples/voice_agent/main/example.c @@ -0,0 +1,173 @@ +#include "esp_log.h" +#include "cJSON.h" +#include "bsp/esp-bsp.h" +#include "livekit.h" +#include "livekit_sandbox.h" +#include "media.h" +#include "board.h" +#include "example.h" + +static const char *TAG = "livekit_example"; + +static livekit_room_handle_t room_handle; + +/// Invoked when the room's connection state changes. +static void on_state_changed(livekit_connection_state_t state, void* ctx) +{ + ESP_LOGI(TAG, "Room state: %s", livekit_connection_state_str(state)); +} + +/// Invoked when participant information is received. +static void on_participant_info(const livekit_participant_info_t* info, void* ctx) +{ + if (info->kind != LIVEKIT_PARTICIPANT_KIND_AGENT) { + // Only handle agent participants for this example. + return; + } + char* verb; + switch (info->state) { + case LIVEKIT_PARTICIPANT_STATE_ACTIVE: + verb = "joined"; + break; + case LIVEKIT_PARTICIPANT_STATE_DISCONNECTED: + verb = "left"; + break; + default: + return; + } + ESP_LOGI(TAG, "Agent has %s the room", verb); +} + +/// Invoked by a remote participant to set the state of an on-board LED. +static void set_led_state(const livekit_rpc_invocation_t* invocation, void* ctx) +{ + if (invocation->payload == NULL) { + livekit_rpc_return_error("Missing payload"); + return; + } + cJSON *root = cJSON_Parse(invocation->payload); + if (!root) { + livekit_rpc_return_error("Invalid JSON"); + return; + } + + char* error = NULL; + do { + const cJSON *color_entry = cJSON_GetObjectItemCaseSensitive(root, "color"); + const cJSON *state_entry = cJSON_GetObjectItemCaseSensitive(root, "state"); + if (!cJSON_IsString(color_entry) || !cJSON_IsBool(state_entry)) { + error = "Unexpected JSON format"; + break; + } + + const char *color = color_entry->valuestring; + bool state = cJSON_IsTrue(state_entry); + + bsp_led_t led; + if (strncmp(color, "red", 3) == 0) { + led = BSP_LED_RED; + } else if (strncmp(color, "blue", 4) == 0) { + led = BSP_LED_BLUE; + } else { + error = "Unsupported color"; + break; + } + // There is a known bug in the BSP component, so we need to invert the state for now. + // See https://github.com/espressif/esp-bsp/pull/610. + if (bsp_led_set(led, !state) != ESP_OK) { + error = "Failed to set LED state"; + break; + } + } while (0); + + if (!error) { + livekit_rpc_return_ok(NULL); + } else { + livekit_rpc_return_error(error); + } + // Perform necessary cleanup after returning an RPC result. + cJSON_Delete(root); +} + +/// Invoked by a remote participant to get the current CPU temperature. +static void get_cpu_temp(const livekit_rpc_invocation_t* invocation, void* ctx) +{ + float temp = board_get_temp(); + char temp_string[16]; + snprintf(temp_string, sizeof(temp_string), "%.2f", temp); + livekit_rpc_return_ok(temp_string); +} + +void join_room() +{ + if (room_handle != NULL) { + ESP_LOGE(TAG, "Room already created"); + return; + } + + livekit_room_options_t room_options = { + .publish = { + .kind = LIVEKIT_MEDIA_TYPE_AUDIO, + .audio_encode = { + .codec = LIVEKIT_AUDIO_CODEC_OPUS, + .sample_rate = 16000, + .channel_count = 1 + }, + .capturer = media_get_capturer() + }, + .subscribe = { + .kind = LIVEKIT_MEDIA_TYPE_AUDIO, + .renderer = media_get_renderer() + }, + .on_state_changed = on_state_changed, + .on_participant_info = on_participant_info + }; + if (livekit_room_create(&room_handle, &room_options) != LIVEKIT_ERR_NONE) { + ESP_LOGE(TAG, "Failed to create room"); + return; + } + + // Register RPC handlers so they can be invoked by remote participants. + livekit_room_rpc_register(room_handle, "set_led_state", set_led_state); + livekit_room_rpc_register(room_handle, "get_cpu_temp", get_cpu_temp); + + livekit_err_t connect_res; +#ifdef CONFIG_LK_USE_SANDBOX + // Option A: Sandbox token server. + livekit_sandbox_res_t res = {}; + livekit_sandbox_options_t gen_options = { + .sandbox_id = CONFIG_LK_SANDBOX_ID, + .room_name = CONFIG_LK_SANDBOX_ROOM_NAME, + .participant_name = CONFIG_LK_SANDBOX_PARTICIPANT_NAME + }; + if (!livekit_sandbox_generate(&gen_options, &res)) { + ESP_LOGE(TAG, "Failed to generate sandbox token"); + return; + } + connect_res = livekit_room_connect(room_handle, res.server_url, res.token); + livekit_sandbox_res_free(&res); +#else + // Option B: Pre-generated token. + connect_res = livekit_room_connect(room_handle, CONFIG_LK_SERVER_URL, CONFIG_LK_TOKEN); +#endif + + if (connect_res != LIVEKIT_ERR_NONE) { + ESP_LOGE(TAG, "Failed to connect to room"); + } +} + +void leave_room() +{ + if (room_handle == NULL) { + ESP_LOGE(TAG, "Room not created"); + return; + } + if (livekit_room_close(room_handle) != LIVEKIT_ERR_NONE) { + ESP_LOGE(TAG, "Failed to leave room"); + } + if (livekit_room_destroy(room_handle) != LIVEKIT_ERR_NONE) { + ESP_LOGE(TAG, "Failed to destroy room"); + return; + } + room_handle = NULL; +} \ No newline at end of file diff --git a/examples/voice_agent/main/example.h b/examples/voice_agent/main/example.h new file mode 100644 index 0000000..f576f95 --- /dev/null +++ b/examples/voice_agent/main/example.h @@ -0,0 +1,13 @@ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void join_room(); +void leave_room(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/examples/voice_agent/main/idf_component.yml b/examples/voice_agent/main/idf_component.yml new file mode 100644 index 0000000..e30c15d --- /dev/null +++ b/examples/voice_agent/main/idf_component.yml @@ -0,0 +1,16 @@ +dependencies: + idf: ">=5.4" + livekit: + path: ../../../components/livekit + codec_board: + path: ../../../components/third_party/esp-webrtc-solution/components/codec_board + # Eventually, the BSP will perform all the functions of the codec_board component. + # It currently is used because codec_board is required to support AEC. + esp32_s3_korvo_2: + version: ">=0.1" + render_impl: + path: ../../../components/third_party/esp-webrtc-solution/components/av_render/render_impl + livekit_sandbox: + path: ../../../components/livekit_sandbox + common: + path: ../../common diff --git a/examples/voice_agent/main/main.c b/examples/voice_agent/main/main.c new file mode 100644 index 0000000..d19c58d --- /dev/null +++ b/examples/voice_agent/main/main.c @@ -0,0 +1,31 @@ +#include "esp_log.h" +#include "media_lib_adapter.h" +#include "media_lib_os.h" +#include "livekit.h" +#include "network.h" +#include "media.h" +#include "board.h" +#include "example.h" + +static void run_async_join_room(void *arg) +{ + join_room(); // See example.c + media_lib_thread_destroy(NULL); +} + +static int network_event_handler(bool connected) +{ + // Create and join the room once network is connected. + if (!connected) return 0; + media_lib_thread_create_from_scheduler(NULL, "join", run_async_join_room, NULL); + return 0; +} + +void app_main(void) +{ + esp_log_level_set("*", ESP_LOG_INFO); + livekit_system_init(); + board_init(); + media_init(); + network_init(CONFIG_WIFI_SSID, CONFIG_WIFI_PASSWORD, network_event_handler); +} diff --git a/examples/voice_agent/main/media.c b/examples/voice_agent/main/media.c new file mode 100644 index 0000000..b1e42f1 --- /dev/null +++ b/examples/voice_agent/main/media.c @@ -0,0 +1,123 @@ +#include "esp_check.h" +#include "esp_log.h" +#include "codec_init.h" +#include "esp_capture_path_simple.h" +#include "esp_capture_audio_enc.h" +#include "av_render_default.h" +#include "esp_audio_dec_default.h" +#include "esp_audio_enc_default.h" +#include "esp_capture_defaults.h" + +#include "media.h" + +static const char *TAG = "media"; + +#define NULL_CHECK(pointer, message) \ + ESP_RETURN_ON_FALSE(pointer != NULL, -1, TAG, message) + +typedef struct { + esp_capture_aenc_if_t *audio_encoder; + esp_capture_audio_src_if_t *audio_source; + esp_capture_path_if_t *capture_path; + esp_capture_path_handle_t capturer_handle; +} capture_system_t; + +typedef struct { + audio_render_handle_t audio_renderer; + av_render_handle_t av_renderer_handle; +} renderer_system_t; + +static capture_system_t capturer_system; +static renderer_system_t renderer_system; + +static int build_capturer_system(void) +{ + // 1. Create audio encoder + capturer_system.audio_encoder = esp_capture_new_audio_encoder(); + NULL_CHECK(capturer_system.audio_encoder, "Failed to create audio encoder"); + + // 2. Create audio source + esp_codec_dev_handle_t record_handle = get_record_handle(); + NULL_CHECK(record_handle, "Failed to get record handle"); + + esp_capture_audio_aec_src_cfg_t codec_cfg = { + .record_handle = record_handle, + .channel = 4, + .channel_mask = 1 | 2 + }; + capturer_system.audio_source = esp_capture_new_audio_aec_src(&codec_cfg); + NULL_CHECK(capturer_system.audio_source, "Failed to create audio source"); + + // 3. Create capture path + esp_capture_simple_path_cfg_t path_cfg = { + .aenc = capturer_system.audio_encoder, + }; + capturer_system.capture_path = esp_capture_build_simple_path(&path_cfg); + NULL_CHECK(capturer_system.capture_path, "Failed to create capture path"); + + // 4. Create capture system + esp_capture_cfg_t cfg = { + .sync_mode = ESP_CAPTURE_SYNC_MODE_AUDIO, + .audio_src = capturer_system.audio_source, + .capture_path = capturer_system.capture_path, + }; + esp_capture_open(&cfg, &capturer_system.capturer_handle); + NULL_CHECK(capturer_system.capturer_handle, "Failed to open capture system"); + return 0; +} + +static int build_renderer_system(void) +{ + // 1. Create audio renderer + i2s_render_cfg_t i2s_cfg = { + .play_handle = get_playback_handle() + }; + renderer_system.audio_renderer = av_render_alloc_i2s_render(&i2s_cfg); + NULL_CHECK(renderer_system.audio_renderer, "Failed to create I2S renderer"); + + // Set initial speaker volume + esp_codec_dev_set_out_vol(i2s_cfg.play_handle, CONFIG_DEFAULT_PLAYBACK_VOL); + + // 2. Create AV renderer + // For this example, this only includes an audio renderer. + av_render_cfg_t render_cfg = { + .audio_render = renderer_system.audio_renderer, + .audio_raw_fifo_size = 8 * 4096, + .audio_render_fifo_size = 100 * 1024, + .allow_drop_data = false, + }; + renderer_system.av_renderer_handle = av_render_open(&render_cfg); + NULL_CHECK(renderer_system.av_renderer_handle, "Failed to create AV renderer"); + + // 3. Set frame info + av_render_audio_frame_info_t frame_info = { + .sample_rate = 16000, + .channel = 2, + .bits_per_sample = 16, + }; + av_render_set_fixed_frame_info(renderer_system.av_renderer_handle, &frame_info); + + return 0; +} + +int media_init(void) +{ + // Register default audio encoder and decoder + esp_audio_enc_register_default(); + esp_audio_dec_register_default(); + + // Build capturer and renderer systems + build_capturer_system(); + build_renderer_system(); + return 0; +} + +esp_capture_handle_t media_get_capturer(void) +{ + return capturer_system.capturer_handle; +} + +av_render_handle_t media_get_renderer(void) +{ + return renderer_system.av_renderer_handle; +} \ No newline at end of file diff --git a/examples/voice_agent/main/media.h b/examples/voice_agent/main/media.h new file mode 100644 index 0000000..3b07788 --- /dev/null +++ b/examples/voice_agent/main/media.h @@ -0,0 +1,39 @@ + +#pragma once + +#include "esp_capture.h" +#include "av_render.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// Initializes the capturer and renderer systems. +int media_init(void); + +/// Returns the capturer handle. +/// +/// This handle is provided to a LiveKit room when initialized to enable +/// publishing tracks from captured media (i.e. audio from a microphone and/or +/// video from a camera). +/// +/// How the capturer is configured is determined by the requirements of +/// your application and the hardware you are using. +/// +esp_capture_handle_t media_get_capturer(void); + +/// Returns the renderer handle. +/// +/// This handle is provided to a LiveKit room when initialized to enable +/// rendering media from subscribed tracks (i.e. playing audio through a +/// speaker and/or displaying video to a screen). +/// +/// How the renderer is configured is determined by the requirements of +/// your application and the hardware you are using. +/// +av_render_handle_t media_get_renderer(void); + +#ifdef __cplusplus +} +#endif + diff --git a/examples/voice_agent/partitions.csv b/examples/voice_agent/partitions.csv new file mode 100755 index 0000000..111c125 --- /dev/null +++ b/examples/voice_agent/partitions.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 3M, diff --git a/examples/voice_agent/sdkconfig.defaults b/examples/voice_agent/sdkconfig.defaults new file mode 100644 index 0000000..b10f30d --- /dev/null +++ b/examples/voice_agent/sdkconfig.defaults @@ -0,0 +1,39 @@ +CONFIG_IDF_TARGET="esp32s3" + +# Enable SPIRAM +CONFIG_SPIRAM=y + +# Enable GDBStub +CONFIG_ESP_SYSTEM_PANIC_GDBSTUB=y + +# Enable FreeRTOS trace +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_HZ=1000 + +# Support 2 SNTP +CONFIG_LWIP_SNTP_MAX_SERVERS=2 + +# Enable DTLS SRTP +CONFIG_MBEDTLS_SSL_PROTO_DTLS=y +CONFIG_MBEDTLS_SSL_DTLS_SRTP=y + +# Use new I2C master +CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE=n + +# Enable experimental features +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +# Use customer partition table +CONFIG_PARTITION_TABLE_CUSTOM=y + +# Allocate LWIP and MbedTLS on SPIRAM +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=256 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y + +CONFIG_IDF_TARGET_ESP32P4=1 + +# Board support package +CONFIG_BSP_I2C_NUM=0 \ No newline at end of file diff --git a/examples/voice_agent/sdkconfig.defaults.esp32s3 b/examples/voice_agent/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000..ba407f1 --- /dev/null +++ b/examples/voice_agent/sdkconfig.defaults.esp32s3 @@ -0,0 +1,21 @@ +CONFIG_CODEC_BOARD_TYPE="S3_Korvo_V2" + +CONFIG_ESPTOOLPY_FLASHFREQ_120M=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y + + +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_120M=y +CONFIG_ESP32S3_SPIRAM_SUPPORT=y + +CONFIG_COMPILER_OPTIMIZATION_PERF=y + +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +CONFIG_ESP32S3_DATA_CACHE_64KB=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y + +CONFIG_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y + +CONFIG_ESP_WS_CLIENT_ENABLE_DYNAMIC_BUFFER=y