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/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..35eb79b --- /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 + 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/README.md b/README.md new file mode 100644 index 0000000..1a80a41 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ + + +# 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 in early development and may undergo breaking API changes and contain bugs. + + \ 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..a48a18b --- /dev/null +++ b/components/livekit/core/common.h @@ -0,0 +1,25 @@ + +#pragma once + +#include "esp_peer.h" +#include "esp_capture.h" +#include "av_render.h" + +#ifdef __cplusplus +extern "C" { +#endif + +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..0e60410 --- /dev/null +++ b/components/livekit/core/engine.c @@ -0,0 +1,631 @@ +#include "esp_log.h" +#include "webrtc_utils_time.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; +} engine_t; + +/// @brief Performs one-time system initialization. +static void sys_init(void) +{ + static bool is_initialized = false; + if (is_initialized) { + ESP_LOGI(TAG, "System already initialized"); + return; + } + is_initialized = webrtc_utils_time_sync_init() == ESP_OK; + if (!is_initialized) { + ESP_LOGE(TAG, "System initialization failed"); + return; + } +} + +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; + ESP_LOGI(TAG, "Media stream task started"); + 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); + } + ESP_LOGI(TAG, "Media stream task ended"); + 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, "lk_stream", media_stream_task, eng) != ESP_OK) { + ESP_LOGE(TAG, "Failed to create media stream thread"); + eng->is_media_streaming = false; + return ENGINE_ERR_MEDIA; + } + ESP_LOGI(TAG, "Media stream started"); + 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); + ESP_LOGI(TAG, "Media stream ended"); + 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; +} + +/// @brief 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; + } + ESP_LOGI(TAG, "Published media tracks"); + return ENGINE_ERR_NONE; + } while (0); + + media_stream_end(eng); + return ret; +} + +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; +} + +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(peer_state_t state, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + if (state == PEER_STATE_CONNECTED) { + publish_tracks(eng); + } +} + +static void on_peer_sub_state_changed(peer_state_t state, void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + if (state == PEER_STATE_CONNECTED) { + // TODO: Subscribe + } +} + +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) +{ + engine_t *eng = (engine_t *)ctx; + ESP_LOGI(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_LOGI(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_connect(void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + ESP_LOGI(TAG, "Signaling connected"); + // TODO: Implement +} + +static void on_sig_disconnect(void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + ESP_LOGI(TAG, "Signaling disconnected"); + // TODO: Implement +} + +static void on_sig_error(void *ctx) +{ + engine_t *eng = (engine_t *)ctx; + ESP_LOGI(TAG, "Signaling error"); + // TODO: Implement +} + +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) { + 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; + } +} + +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); +} + +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_data_packet == 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_connect = on_sig_connect, + .on_disconnect = on_sig_disconnect, + .on_error = on_sig_error, + .on_join = on_sig_join, + .on_leave = on_sig_leave, + .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; + + sys_init(); + + 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; + + 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..c00d1ba --- /dev/null +++ b/components/livekit/core/engine.h @@ -0,0 +1,82 @@ + +#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" + +#ifdef __cplusplus +extern "C" { +#endif + +/// @brief 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; + +/// @brief 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 { + // This is an alternative to RtcEngine's async connect method + livekit_pb_join_response_t join_response; +} engine_event_connected_t; + +typedef struct { + livekit_pb_disconnect_reason_t reason; +} engine_event_disconnected_t; + +typedef struct { + // This is an alternative to RtcEngine's async connect method + // returning a error result. + // TODO: add error details +} engine_event_error_t; + +typedef struct { + void *ctx; + + void (*on_connected)(engine_event_connected_t detail, void *ctx); + void (*on_disconnected)(engine_event_disconnected_t detail, void *ctx); + void (*on_error)(engine_event_error_t detail, void *ctx); + void (*on_data_packet)(livekit_pb_data_packet_t* packet, void *ctx); + engine_media_options_t media; +} engine_options_t; + +/// @brief 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); + +/// @brief Destroys an instance. +/// @param[in] handle The handle to the instance to destroy. +engine_err_t engine_destroy(engine_handle_t handle); + +/// @brief Connect the engine. +engine_err_t engine_connect(engine_handle_t handle, const char* server_url, const char* token); + +/// @brief Close the engine. +engine_err_t engine_close(engine_handle_t handle); + +/// @brief 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..3b66705 --- /dev/null +++ b/components/livekit/core/livekit.c @@ -0,0 +1,297 @@ +#include +#include +#include "esp_peer.h" +#include "engine.h" +#include "rpc_manager.h" + +#include "livekit.h" + +static const char *TAG = "livekit"; + +typedef struct { + rpc_manager_handle_t rpc_manager; + engine_handle_t engine; + void (*on_rpc_result)(const livekit_rpc_result_t* result, void* ctx); + void* ctx; +} 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->on_rpc_result != NULL) { + room->on_rpc_result(result, room->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_connected(engine_event_connected_t detail, void *ctx) +{ + ESP_LOGI(TAG, "Received engine connected event"); + // TODO: Implement +} + +static void on_eng_disconnected(engine_event_disconnected_t detail, void *ctx) +{ + ESP_LOGI(TAG, "Received engine disconnected event"); + // TODO: Implement +} + +static void on_eng_error(engine_event_error_t detail, void *ctx) +{ + ESP_LOGE(TAG, "Received engine error event"); + // TODO: Implement +} + +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: + // TODO: Dispatch + 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; + } +} + +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; + } + + // 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->on_rpc_result = options->on_rpc_result; + room->ctx = options->ctx; + + engine_media_options_t media_options = {}; + populate_media_options(&media_options, &options->publish, &options->subscribe); + + engine_options_t eng_options = { + .media = media_options, + .on_connected = on_eng_connected, + .on_disconnected = on_eng_disconnected, + .on_error = on_eng_error, + .on_data_packet = on_eng_data_packet, + .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_err_t livekit_room_publish_data(livekit_room_handle_t handle, livekit_payload_t *payload, livekit_publish_data_options_t *options) +{ + if (handle == NULL || payload == NULL || options == 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(payload->size)); + if (bytes_array == NULL) { + return LIVEKIT_ERR_NO_MEM; + } + bytes_array->size = payload->size; + memcpy(bytes_array->bytes, payload->bytes, 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; +} \ 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..490886e --- /dev/null +++ b/components/livekit/core/peer.c @@ -0,0 +1,487 @@ +#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 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; + + peer_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; + ESP_LOGI(TAG(peer), "Task started"); + 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 channel_cfg = {}; + ESP_LOGI(TAG(peer), "Creating data channels"); + + // TODO: This is a temporary solution to create data channels. This is NOT + // actually reliable. Update once necessary options are exposed. + channel_cfg.label = RELIABLE_CHANNEL_LABEL; + if (esp_peer_create_data_channel(peer->connection, &channel_cfg) != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG(peer), "Failed to create reliable data channel"); + } + channel_cfg.label = LOSSY_CHANNEL_LABEL; + if (esp_peer_create_data_channel(peer->connection, &channel_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_LOGI(TAG(peer), "RTC state changed to %d", rtc_state); + + peer_state_t state = peer->state; + switch (rtc_state) { + case ESP_PEER_STATE_CONNECT_FAILED: + state = PEER_STATE_FAILED; + break; + case ESP_PEER_STATE_DISCONNECTED: + state = PEER_STATE_DISCONNECTED; + break; + case ESP_PEER_STATE_PAIRING: + state = PEER_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; + state = PEER_STATE_CONNECTED; + break; + default: + break; + } + if (state != peer->state) { + ESP_LOGI(TAG(peer), "State changed to %d", state); + peer->state = state; + peer->options.on_state_changed(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_LOGI(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_LOGI(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; + } + + ESP_LOGI(TAG(peer), "Decoded data packet: type=%d", packet.which_value); + 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 = PEER_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_LOGI(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 ? + "lk_sub_task" : "lk_pub_task"; + if (media_lib_thread_create_from_scheduler(&thread, thread_name, peer_task, peer) != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG(peer), "Failed to create task"); + 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..51e3101 --- /dev/null +++ b/components/livekit/core/peer.h @@ -0,0 +1,105 @@ + +#pragma once + +#include "common.h" +#include "engine.h" +#include "protocol.h" + +#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; + +typedef enum { + PEER_STATE_DISCONNECTED = 0, /*!< Disconnected */ + PEER_STATE_CONNECTING = 1, /*!< Establishing peer connection */ + PEER_STATE_CONNECTED = 2, /*!< Connected to peer & data channels open */ + PEER_STATE_FAILED = 3 /*!< Connection failed */ +} peer_state_t; + +/// @brief Options for creating a peer. +typedef struct { + /// @brief Whether the peer is a publisher or subscriber. + livekit_pb_signal_target_t target; + + /// @brief ICE server list. + esp_peer_ice_server_cfg_t* server_list; + + /// @brief Number of servers in the list. + int server_count; + + /// @brief Weather to force the use of relay ICE candidates. + bool force_relay; + + /// @brief Whether the peer is the primary peer. + /// @note This determines which peer controls the data channels. + bool is_primary; + + /// @brief Media options used for creating SDP messages. + engine_media_options_t* media; + + /// @brief Invoked when the peer's connection state changes. + void (*on_state_changed)(peer_state_t state, void *ctx); + + /// @brief 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); + + /// @brief Invoked when a new ICE candidate is available. + void (*on_ice_candidate)(const char *candidate, void *ctx); + + /// @brief Invoked when a data packet is received over the data channel. + void (*on_packet_received)(livekit_pb_data_packet_t* packet, void *ctx); + + /// @brief Invoked when information about an incoming audio stream is available. + void (*on_audio_info)(esp_peer_audio_stream_info_t* info, void *ctx); + + /// @brief Invoked when an audio frame is received. + void (*on_audio_frame)(esp_peer_audio_frame_t* frame, void *ctx); + + /// @brief Invoked when information about an incoming video stream is available. + void (*on_video_info)(esp_peer_video_stream_info_t* info, void *ctx); + + /// @brief Invoked when a video frame is received. + void (*on_video_frame)(esp_peer_video_frame_t* frame, void *ctx); + + /// @brief 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); + +/// @brief Handles an SDP message from the remote peer. +peer_err_t peer_handle_sdp(peer_handle_t handle, const char *sdp); + +/// @brief Handles an ICE candidate from the remote peer. +peer_err_t peer_handle_ice_candidate(peer_handle_t handle, const char *candidate); + +/// @brief 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); + +/// @brief 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); + +/// @brief 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.c b/components/livekit/core/protocol.c new file mode 100644 index 0000000..9f47896 --- /dev/null +++ b/components/livekit/core/protocol.c @@ -0,0 +1,31 @@ +#include "protocol.h" + +const char* livekit_protocol_sig_res_name(pb_size_t which_message) +{ + switch (which_message) { + case LIVEKIT_PB_SIGNAL_RESPONSE_JOIN_TAG: return "Join"; + case LIVEKIT_PB_SIGNAL_RESPONSE_ANSWER_TAG: return "Answer"; + case LIVEKIT_PB_SIGNAL_RESPONSE_OFFER_TAG: return "Offer"; + case LIVEKIT_PB_SIGNAL_RESPONSE_TRICKLE_TAG: return "Trickle"; + case LIVEKIT_PB_SIGNAL_RESPONSE_UPDATE_TAG: return "Update"; + case LIVEKIT_PB_SIGNAL_RESPONSE_TRACK_PUBLISHED_TAG: return "TrackPublished"; + case LIVEKIT_PB_SIGNAL_RESPONSE_LEAVE_TAG: return "Leave"; + case LIVEKIT_PB_SIGNAL_RESPONSE_MUTE_TAG: return "Mute"; + case LIVEKIT_PB_SIGNAL_RESPONSE_SPEAKERS_CHANGED_TAG: return "SpeakersChanged"; + case LIVEKIT_PB_SIGNAL_RESPONSE_ROOM_UPDATE_TAG: return "RoomUpdate"; + case LIVEKIT_PB_SIGNAL_RESPONSE_CONNECTION_QUALITY_TAG: return "ConnectionQuality"; + case LIVEKIT_PB_SIGNAL_RESPONSE_STREAM_STATE_UPDATE_TAG: return "StreamStateUpdate"; + case LIVEKIT_PB_SIGNAL_RESPONSE_SUBSCRIBED_QUALITY_UPDATE_TAG: return "SubscribedQualityUpdate"; + case LIVEKIT_PB_SIGNAL_RESPONSE_SUBSCRIPTION_PERMISSION_UPDATE_TAG: return "SubscriptionPermissionUpdate"; + case LIVEKIT_PB_SIGNAL_RESPONSE_REFRESH_TOKEN_TAG: return "RefreshToken"; + case LIVEKIT_PB_SIGNAL_RESPONSE_TRACK_UNPUBLISHED_TAG: return "TrackUnpublished"; + case LIVEKIT_PB_SIGNAL_RESPONSE_PONG_TAG: return "Pong"; + case LIVEKIT_PB_SIGNAL_RESPONSE_RECONNECT_TAG: return "Reconnect"; + case LIVEKIT_PB_SIGNAL_RESPONSE_PONG_RESP_TAG: return "PongResp"; + case LIVEKIT_PB_SIGNAL_RESPONSE_SUBSCRIPTION_RESPONSE_TAG: return "SubscriptionResponse"; + case LIVEKIT_PB_SIGNAL_RESPONSE_REQUEST_RESPONSE_TAG: return "RequestResponse"; + case LIVEKIT_PB_SIGNAL_RESPONSE_TRACK_SUBSCRIBED_TAG: return "TrackSubscribed"; + case LIVEKIT_PB_SIGNAL_RESPONSE_ROOM_MOVED_TAG: return "RoomMoved"; + default: return "Unknown"; + } +} \ 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..67aef15 --- /dev/null +++ b/components/livekit/core/protocol.h @@ -0,0 +1,21 @@ + +#pragma once + +#include +#include + +#include "livekit_rtc.pb.h" +#include "livekit_models.pb.h" +#include "livekit_metrics.pb.h" +#include "timestamp.pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/// @brief Gets the name of the signaling response type. +const char* livekit_protocol_sig_res_name(pb_size_t which_message); + +#ifdef __cplusplus +} +#endif \ 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..8783938 --- /dev/null +++ b/components/livekit/core/rpc_manager.c @@ -0,0 +1,241 @@ +#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_LOGE(TAG, "Invalid request packet"); + return RPC_MANAGER_ERR_NONE; + } + ESP_LOGI(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)) { + ESP_LOGE(TAG, "** Failed to send ack packet"); + return RPC_MANAGER_ERR_SEND_FAILED; + } + + if (request->version != 1) { + ESP_LOGW(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)) { + ESP_LOGE(TAG, "** Failed to send ack packet"); + 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_LOGE(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_LOGI(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_LOGE(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..e649662 --- /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; + +/// @brief Creates a new RPC manager. +rpc_manager_err_t rpc_manager_create(rpc_manager_handle_t *handle, const rpc_manager_options_t *options); + +/// @brief Destroys an RPC manager. +rpc_manager_err_t rpc_manager_destroy(rpc_manager_handle_t handle); + +/// @brief 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); + +/// @brief Unregisters a handler for an RPC method. +rpc_manager_err_t rpc_manager_unregister(rpc_manager_handle_t handle, const char* method); + +/// @brief 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..11fcf36 --- /dev/null +++ b/components/livekit/core/signaling.c @@ -0,0 +1,408 @@ +#include +#include +#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 { + char* url; + 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_LOGI(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_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_LOGI(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; + } + + ESP_LOGI(TAG, "Decoded res: type=%s(%d)", + livekit_protocol_sig_res_name(res.which_message), + res.which_message); + 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_LOGI(TAG, "Signaling connected"); + sg->options.on_connect(sg->options.ctx); + break; + case WEBSOCKET_EVENT_DISCONNECTED: + ESP_LOGI(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_disconnect(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_error(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_connect == NULL || + options->on_disconnect == NULL || + options->on_error == NULL || + options->on_join == NULL || + options->on_leave == 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; + + if (!url_build(server_url, token, &sg->url)) { + return SIGNAL_ERR_INVALID_URL; + } + + if (esp_websocket_client_set_uri(sg->ws, sg->url) != ESP_OK) { + ESP_LOGE(TAG, "Failed to set WebSocket URI"); + return SIGNAL_ERR_WEBSOCKET; + } + 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; + } + if (sg->url != NULL) { + free(sg->url); + sg->url = NULL; + } + 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); +} \ 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..b5b45a0 --- /dev/null +++ b/components/livekit/core/signaling.h @@ -0,0 +1,51 @@ + +#pragma once + +#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_connect)(void *ctx); + void (*on_disconnect)(void *ctx); + void (*on_error)(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_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); + +/// @brief 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); + +/// @brief 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); + +#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..f378811 --- /dev/null +++ b/components/livekit/core/url.c @@ -0,0 +1,75 @@ +#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=true" \ + "&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 = snprintf(NULL, 0, URL_FORMAT, + server_url, + separator, + idf_version, + model_code, + token + ); + *out_url = (char *)malloc(final_len + 1); + if (*out_url == NULL) { + return false; + } + sprintf(*out_url, URL_FORMAT, + server_url, + separator, + idf_version, + model_code, + token + ); + + // Token is redacted from logging for security + size_t token_len = strlen(token); + ESP_LOGI(TAG, "Built signaling URL: %.*s[REDACTED]", (int)(final_len - token_len), *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..0927306 --- /dev/null +++ b/components/livekit/core/url.h @@ -0,0 +1,20 @@ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// @brief 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..3fb4dbd --- /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.0" + espressif/esp_websocket_client: ^1.4.0 + media_lib_sal: + path: ../third_party/media_lib_sal + esp_webrtc: + path: ../third_party/esp_webrtc + peer_default: + path: ../third_party/esp_webrtc/impl/peer_default + public: true # Required to prevent linker error + webrtc_utils: + path: ../third_party/webrtc_utils + capture_audio_enc: + path: ../third_party/esp_capture/src/impl/capture_audio_enc + capture_video_enc: + path: ../third_party/esp_capture/src/impl/capture_video_enc + capture_audio_src: + path: ../third_party/esp_capture/src/impl/capture_audio_src + capture_video_src: + path: ../third_party/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..57c5d2e --- /dev/null +++ b/components/livekit/include/livekit.h @@ -0,0 +1,207 @@ + +#pragma once + +#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_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; + +/// Payload containing a pointer to data and its size. +typedef struct { + uint8_t *bytes; ///< Pointer to data + size_t size; ///< Size of the data +} livekit_payload_t; + +/// @brief 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; + +/// @brief 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; + +/// Options for a room 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; + + /// Callback for RPC results. + /// @see RPC + void (*on_rpc_result)(const livekit_rpc_result_t* result, void* ctx); + + /// User context passed to all handlers. + void* ctx; +} livekit_room_options_t; + +/// @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. +/// @{ + +/// 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. +/// @note Handle room events to get notified once the connection is established or fails. +/// @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. +/// @note Handle room events to get notified once the disconnection is complete. +/// @return @ref LIVEKIT_ERR_NONE if successful, otherwise an error code. +livekit_err_t livekit_room_close(livekit_room_handle_t handle); + +/// @} + +/// @defgroup DataPackets Data packets +/// +/// Low-level API for high-frequency data exchange. +/// +/// For more information about this feature, see the +/// [data packets documentation](https://docs.livekit.io/home/client/data/packets/). +/// @{ + +/// Options passed to @ref livekit_room_publish_data. +typedef struct { + /// 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_publish_data_options_t; + +/// Publishes a data packet to participants in a room asynchronously. +/// @ingroup DataPackets +/// @param handle[in] Room handle. +/// @param payload[in] Data to publish and its size. +/// @param options[in] Options for sending the data packet (e.g. reliability, topic, etc.). +/// @return @ref LIVEKIT_ERR_NONE if successful, otherwise an error code. +livekit_err_t livekit_room_publish_data(livekit_room_handle_t handle, livekit_payload_t *payload, livekit_publish_data_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 +/// [RPC documentation](https://docs.livekit.io/home/client/data/rpc/). +/// @{ + +/// Registers a handler for an RPC method. +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. +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..9ea5a26 --- /dev/null +++ b/components/livekit/include/livekit_rpc.h @@ -0,0 +1,121 @@ + +#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 + +/// @brief 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. + /// @note The payload must be less than or equal to @ref LIVEKIT_RPC_MAX_PAYLOAD_BYTES bytes. + 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..39e6488 --- /dev/null +++ b/components/livekit/protocol/livekit_models.pb.h @@ -0,0 +1,1862 @@ +/* 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_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 { + livekit_pb_participant_permission_t permission; +} 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; + 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_room { + pb_callback_t sid; + pb_callback_t name; + uint32_t empty_timeout; + uint32_t max_participants; + int64_t creation_time; + pb_callback_t turn_password; + pb_callback_t enabled_codecs; + pb_callback_t metadata; + uint32_t num_participants; + bool active_recording; + uint32_t num_publishers; + bool has_version; + livekit_pb_timed_version_t version; + uint32_t departure_timeout; + int64_t creation_time_ms; +} livekit_pb_room_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_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}, NULL}, 0, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, 0, 0, false, LIVEKIT_PB_TIMED_VERSION_INIT_DEFAULT, 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 {LIVEKIT_PB_PARTICIPANT_PERMISSION_INIT_DEFAULT} +#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, 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}, NULL}, 0, 0, 0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0, 0, 0, false, LIVEKIT_PB_TIMED_VERSION_INIT_ZERO, 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 {LIVEKIT_PB_PARTICIPANT_PERMISSION_INIT_ZERO} +#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, 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_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_PERMISSION_TAG 11 +#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_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_ROOM_SID_TAG 1 +#define LIVEKIT_PB_ROOM_NAME_TAG 2 +#define LIVEKIT_PB_ROOM_EMPTY_TIMEOUT_TAG 3 +#define LIVEKIT_PB_ROOM_MAX_PARTICIPANTS_TAG 4 +#define LIVEKIT_PB_ROOM_CREATION_TIME_TAG 5 +#define LIVEKIT_PB_ROOM_TURN_PASSWORD_TAG 6 +#define LIVEKIT_PB_ROOM_ENABLED_CODECS_TAG 7 +#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_ROOM_NUM_PUBLISHERS_TAG 11 +#define LIVEKIT_PB_ROOM_VERSION_TAG 13 +#define LIVEKIT_PB_ROOM_DEPARTURE_TIMEOUT_TAG 14 +#define LIVEKIT_PB_ROOM_CREATION_TIME_MS_TAG 15 +#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, CALLBACK, SINGULAR, STRING, sid, 1) \ +X(a, CALLBACK, SINGULAR, STRING, name, 2) \ +X(a, STATIC, SINGULAR, UINT32, empty_timeout, 3) \ +X(a, STATIC, SINGULAR, UINT32, max_participants, 4) \ +X(a, STATIC, SINGULAR, INT64, creation_time, 5) \ +X(a, CALLBACK, SINGULAR, STRING, turn_password, 6) \ +X(a, CALLBACK, REPEATED, MESSAGE, enabled_codecs, 7) \ +X(a, CALLBACK, SINGULAR, STRING, metadata, 8) \ +X(a, STATIC, SINGULAR, UINT32, num_participants, 9) \ +X(a, STATIC, SINGULAR, BOOL, active_recording, 10) \ +X(a, STATIC, SINGULAR, UINT32, num_publishers, 11) \ +X(a, STATIC, OPTIONAL, MESSAGE, version, 13) \ +X(a, STATIC, SINGULAR, UINT32, departure_timeout, 14) \ +X(a, STATIC, SINGULAR, INT64, creation_time_ms, 15) +#define LIVEKIT_PB_ROOM_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_ROOM_DEFAULT NULL +#define livekit_pb_room_t_enabled_codecs_MSGTYPE livekit_pb_codec_t +#define livekit_pb_room_t_version_MSGTYPE livekit_pb_timed_version_t + +#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, STATIC, REQUIRED, MESSAGE, permission, 11) +#define LIVEKIT_PB_PARTICIPANT_INFO_CALLBACK NULL +#define LIVEKIT_PB_PARTICIPANT_INFO_DEFAULT NULL +#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, 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_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_INFO_SIZE 8 +#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..d1e4d51 --- /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, AUTO) + + +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, AUTO) + + +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..dcf157a --- /dev/null +++ b/components/livekit/protocol/livekit_rtc.pb.h @@ -0,0 +1,1469 @@ +/* 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_callback_t participants; +} livekit_pb_participant_update_t; + +typedef struct livekit_pb_update_subscription { + pb_callback_t track_sids; + bool subscribe; + pb_callback_t participant_tracks; +} 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 { + livekit_pb_participant_info_t participant; + 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 {LIVEKIT_PB_PARTICIPANT_INFO_INIT_DEFAULT, 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 {{{NULL}, NULL}} +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_INIT_DEFAULT {{{NULL}, NULL}, 0, {{NULL}, NULL}} +#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 {LIVEKIT_PB_PARTICIPANT_INFO_INIT_ZERO, 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 {{{NULL}, NULL}} +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_INIT_ZERO {{{NULL}, NULL}, 0, {{NULL}, NULL}} +#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_SUBSCRIPTION_PARTICIPANT_TRACKS_TAG 3 +#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_PARTICIPANT_TAG 2 +#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, REQUIRED, MESSAGE, participant, 2) \ +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_participant_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, CALLBACK, REPEATED, MESSAGE, participants, 1) +#define LIVEKIT_PB_PARTICIPANT_UPDATE_CALLBACK pb_default_field_callback +#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, CALLBACK, REPEATED, STRING, track_sids, 1) \ +X(a, STATIC, SINGULAR, BOOL, subscribe, 2) \ +X(a, CALLBACK, REPEATED, MESSAGE, participant_tracks, 3) +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_CALLBACK pb_default_field_callback +#define LIVEKIT_PB_UPDATE_SUBSCRIPTION_DEFAULT NULL +#define livekit_pb_update_subscription_t_participant_tracks_MSGTYPE livekit_pb_participant_tracks_t + +#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..fab96cd --- /dev/null +++ b/components/livekit/protocol/protobufs/livekit_models.options @@ -0,0 +1,89 @@ +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_IGNORE +livekit_pb.ParticipantInfo.identity type:FT_IGNORE +livekit_pb.ParticipantInfo.state type:FT_IGNORE +livekit_pb.ParticipantInfo.tracks type:FT_IGNORE +livekit_pb.ParticipantInfo.metadata type:FT_IGNORE +livekit_pb.ParticipantInfo.joined_at type:FT_IGNORE +livekit_pb.ParticipantInfo.joined_at_ms type:FT_IGNORE +livekit_pb.ParticipantInfo.name type:FT_IGNORE +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_IGNORE +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_IGNORE +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 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..b67c002 --- /dev/null +++ b/components/livekit/protocol/protobufs/livekit_rtc.options @@ -0,0 +1,40 @@ +livekit_pb.JoinResponse.room type:FT_IGNORE +livekit_pb.JoinResponse.participant label_override:LABEL_REQUIRED +livekit_pb.JoinResponse.other_participants type:FT_IGNORE +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 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..ce4e9ad --- /dev/null +++ b/components/livekit_sandbox/idf_component.yml @@ -0,0 +1,2 @@ +description: LiveKit Sandbox Token Generator +version: 0.1.0 \ 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..4a0ddbc --- /dev/null +++ b/components/livekit_sandbox/include/livekit_sandbox.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// @brief Request options for generating a sandbox token. +typedef struct { + /// @brief The sandbox ID. + char *sandbox_id; + + /// @brief The room name the generated token will have. + /// @note If not provided, one will be generated. + char *room_name; + + /// @brief The participant identity the generated token will have. + /// @note If not provided, one will be generated. + char *participant_name; +} livekit_sandbox_options_t; + +/// @brief Response containing the generated token details. +typedef struct { + /// @brief The LiveKit Cloud URL for the associated project. + char *server_url; + + /// @brief The access token for the participant. Valid for 15 minutes. + char *token; + + /// @brief Generated token's room name. + char *room_name; + + /// @brief Generated token's participant identity. + char *participant_name; +} livekit_sandbox_res_t; + +/// @brief 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); + +/// @brief 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/av_render/CMakeLists.txt b/components/third_party/av_render/CMakeLists.txt new file mode 100644 index 0000000..eec1378 --- /dev/null +++ b/components/third_party/av_render/CMakeLists.txt @@ -0,0 +1,14 @@ + +list (APPEND COMPONENT_SRCDIRS ./src) + +set(COMPONENT_ADD_INCLUDEDIRS ./include) + +set(COMPONENT_PRIV_REQUIRES media_lib_sal esp_timer esp_audio_codec esp_video_codec) + +register_component() + +# Add color convert ASM optimized lib +IF (${IDF_TARGET} STREQUAL "esp32p4") +set(TARGET_LIB_NAME "${CMAKE_CURRENT_SOURCE_DIR}/libs/${IDF_TARGET}/libesp_image_i420_2_rgb565le.a") +target_link_libraries(${COMPONENT_TARGET} "-Wl,--start-group" ${TARGET_LIB_NAME} "-Wl,--end-group") +ENDIF () diff --git a/components/third_party/av_render/README.md b/components/third_party/av_render/README.md new file mode 100644 index 0000000..752a085 --- /dev/null +++ b/components/third_party/av_render/README.md @@ -0,0 +1,36 @@ +# AV_Render + +`AV_Render` is a simple player designed for video and audio playback. It primarily supports "push" playback model, where the user first provides stream information and then pushes the stream data to `av_render`. The player checks the input stream's codec and uses appropriate audio and video decoders for processing. The decoded output frame data is sent to the corresponding renderer for final output (e.g., audio playback via codec, video playback on LCD). + +## Abstraction + +### Render Implementations + +Users can select the appropriate renderer based on their specific use case. We also provide default implementations for common scenarios, such as: + +- **Audio Rendering:** `av_render_alloc_i2s_render` — outputs audio through I2S. +- **Video Rendering:** `av_render_alloc_lcd_render` — renders video through `esp_lcd`. + +## Simple Usage + +The overall flow for audio and video playback is as follows: + +1. `av_render_open` +2. `av_render_add_audio_stream` +3. `av_render_add_video_stream` +4. `av_render_add_audio_data` +5. `av_render_add_video_data` +6. `av_render_close` + +### Resetting the Playback + +If you want to clear the current stream and start a new one, call `av_render_reset`. + +### Resource usage + +Users can configure resource usage more precisely to meet their specific needs. This includes options such as whether an additional thread is required for decoding, as well as how much data can be buffered by the decoder and renderer. + +To customize these settings, you can use the `av_render_cfg_t` configuration structure or the following API functions: + +- `av_render_config_audio_fifo` — Configure the audio buffer size for the decoder and renderer. +- `av_render_config_video_fifo` — Configure the video buffer size for the decoder and renderer. diff --git a/components/third_party/av_render/idf_component.yml b/components/third_party/av_render/idf_component.yml new file mode 100644 index 0000000..3d08470 --- /dev/null +++ b/components/third_party/av_render/idf_component.yml @@ -0,0 +1,5 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_audio_codec: "~2.3.0" + espressif/esp_video_codec: "~0.5.2" + espressif/esp_audio_effects: "~1.1.0" diff --git a/components/third_party/av_render/include/audio_decoder.h b/components/third_party/av_render/include/audio_decoder.h new file mode 100644 index 0000000..eee3e4c --- /dev/null +++ b/components/third_party/av_render/include/audio_decoder.h @@ -0,0 +1,106 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#pragma once + +#include "av_render_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Audio decoder frame callback + * + * @param[in] frame Decoded audio frame + * @param[in] ctx Decoder context set by user + * + * @return + * - 0 On success + * - Others Fail to decode + */ +typedef int (*adec_frame_cb)(av_render_audio_frame_t *frame, void *ctx); + +/** + * @brief Audio decoder configuration + */ +typedef struct { + av_render_audio_info_t audio_info; /*!< Audio information */ + adec_frame_cb frame_cb; /*!< Decoded frame callback */ + void *ctx; /*!< Decoder context */ +} adec_cfg_t; + +/** + * @brief Audio decoder handle + */ +typedef void *adec_handle_t; + +/** + * @brief Open audio decoder + * + * @param[in] cfg Audio decoder configuration + * + * @return + * - NULL No memory for decoder instance + * - Others On success + */ +adec_handle_t adec_open(adec_cfg_t *cfg); + +/** + * @brief Decode audio + * + * @param[in] h Audio decoder handle + * @param[in] data Audio data to be decoded + * + * @return + * - 0 On success + * - Others Fail to decode + */ +int adec_decode(adec_handle_t h, av_render_audio_data_t *data); + +/** + * @brief Get decoded audio frame information + * + * @param[in] h Audio decoder handle + * @param[out] frame_info Audio frame information + * + * @return + * - 0 On success + * - Others Fail to decode + */ +int adec_get_frame_info(adec_handle_t h, av_render_audio_frame_info_t *frame_info); + +/** + * @brief Close audio decoder + * + * @param[in] h Audio decoder handle + * + * @return + * - 0 On success + * - Others Fail to decode + */ +int adec_close(adec_handle_t h); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/av_render/include/audio_render.h b/components/third_party/av_render/include/audio_render.h new file mode 100644 index 0000000..83eba08 --- /dev/null +++ b/components/third_party/av_render/include/audio_render.h @@ -0,0 +1,248 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "av_render_types.h" + +/** + * @brief Audio render handle + */ +typedef void *audio_render_handle_t; + +/** + * @brief Initialize audio render callback + * + * @param[in] cfg Audio render configuration + * @param[in] cfg_size Audio render configuration size + * + * @return + * - NULL Not enough memory + * - Others On success + */ +typedef audio_render_handle_t (*audio_render_int_func)(void *cfg, int cfg_size); + +/** + * @brief Open audio render callback + * + * @param[in] render Audio render handle + * @param[in] info Audio frame information + * + * @return + * - 0 On success + * - Others Fail to open audio render + */ +typedef int (*audio_render_open_func)(audio_render_handle_t render, av_render_audio_frame_info_t *info); + +/** + * @brief Write to audio render callback + * + * @param[in] render Audio render handle + * @param[in] audio_data Audio data to be written + * + * @return + * - 0 On success + * - Others Fail to write + */ +typedef int (*audio_render_write_func)(audio_render_handle_t render, av_render_audio_frame_t *audio_data); + +/** + * @brief Get audio render latency callback + * + * @param[in] render Audio render handle + * @param[out] latency Audio render latency + * + * @return + * - 0 On success + * - Others Fail to get latency + */ +typedef int (*audio_render_latency_func)(audio_render_handle_t render, uint32_t *latency); + +/** + * @brief Get audio render frame information + * + * @param[in] render Audio render handle + * @param[out] info Audio frame information + * + * @return + * - 0 On success + * - Others Fail to get + */ +typedef int (*audio_render_get_frame_info_func)(audio_render_handle_t render, av_render_audio_frame_info_t *info); + +/** + * @brief Set audio render speed + * + * @param[in] render Audio render handle + * @param[in] speed Audio render speed + * + * @return + * - 0 On success + * - Others Fail to set speed + */ +typedef int (*audio_render_set_speed_func)(audio_render_handle_t render, float speed); + +/** + * @brief Close audio render callback + * + * @param[in] render Audio render handle + * + * @return + * - 0 On success + * - Others Fail to close + */ +typedef int (*audio_render_close_func)(audio_render_handle_t render); + +/** + * @brief Deinitialize audio render callback + * + * @param[in] render Audio render handle + * + * @return + * - 0 On success + * - Others Fail to deinitialize + */ +typedef void (*audio_render_deinit_func)(audio_render_handle_t render); + +/** + * @brief Audio render operations + */ +typedef struct { + audio_render_int_func init; /*!< Initialize audio render callback */ + audio_render_open_func open; /*!< Open audio render callback */ + audio_render_write_func write; /*!< Write to audio render callback */ + audio_render_latency_func get_latency; /*!< Get audio render latency callback */ + audio_render_get_frame_info_func get_frame_info; /*!< Get audio frame information callback */ + audio_render_set_speed_func set_speed; /*!< Set audio render speed callback */ + audio_render_close_func close; /*!< Close audio render callback */ + audio_render_deinit_func deinit; /*!< Deinitialize audio render callback */ +} audio_render_ops_t; + +/** + * @brief Audio render configuration + */ +typedef struct { + audio_render_ops_t ops; /*!< Audio render implementation */ + void *cfg; /*!< Audio render configuration */ + int cfg_size; /*!< Audio render configuration size */ +} audio_render_cfg_t; + +/** + * @brief Allocate audio render handle + * + * @param[in] cfg Audio render configuration + * + * @return + * - NULL Not enough memory + * - Others On success + */ +audio_render_handle_t audio_render_alloc_handle(audio_render_cfg_t *cfg); + +/** + * @brief Open audio render + * + * @param[in] render Audio render handle + * @param[in] info Audio frame information + * + * @return + * - 0 On success + * - Others Fail to open audio render + */ +int audio_render_open(audio_render_handle_t render, av_render_audio_frame_info_t *info); + +/** + * @brief Write data to audio render + * + * @param[in] render Audio render handle + * @param[in] audio_data Data to be written + * + * @return + * - 0 On success + * - Others Fail to write + */ +int audio_render_write(audio_render_handle_t render, av_render_audio_frame_t *audio_data); + +/** + * @brief Get audio render latency + * + * @param[in] render Audio render handle + * @param[out] latency Audio latency (unit ms) + * + * @return + * - 0 On success + * - Others Fail to get latency + */ +int audio_render_get_latency(audio_render_handle_t render, uint32_t *latency); + +/** + * @brief Set audio render speed + * + * @note Speed 1.0 means normal speed, 0.0 means pause + * + * @param[in] render Audio render handle + * @param[in] speed Audio render speed + * + * @return + * - 0 On success + * - Others Fail to set speed + */ +int audio_render_set_speed(audio_render_handle_t render, float speed); + +/** + * @brief Get audio frame information from audio render + * + * @param[in] render Audio render handle + * @param[out] info Audio frame information + * + * @return + * - 0 On success + * - Others Fail to get + */ +int audio_render_get_frame_info(audio_render_handle_t render, av_render_audio_frame_info_t *info); + +/** + * @brief Close audio render + * + * @param[in] render Audio render handle + * + * @return + * - 0 On success + * - Others Fail to close + */ +int audio_render_close(audio_render_handle_t render); + +/** + * @brief Free audio render + * + * @param[in] render Audio render handle + * + */ +void audio_render_free_handle(audio_render_handle_t render); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/av_render/include/audio_resample.h b/components/third_party/av_render/include/audio_resample.h new file mode 100644 index 0000000..688f620 --- /dev/null +++ b/components/third_party/av_render/include/audio_resample.h @@ -0,0 +1,84 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "av_render_types.h" + +/** + * @brief Audio resample handle + */ +typedef void *audio_resample_handle_t; + +/** + * @brief Audio resample frame callback + */ +typedef int (*audio_resample_frame_cb)(av_render_audio_frame_t *frame, void *ctx); + +/** + * @brief Audio resample configuration + */ +typedef struct { + av_render_audio_frame_info_t input_info; /*!< Input frame information */ + av_render_audio_frame_info_t output_info; /*!< Output frame information */ + audio_resample_frame_cb resample_cb; /*!< Resample output callback */ + void *ctx; /*!< User context */ +} audio_resample_cfg_t; + +/** + * @brief Open audio resample + * + * @param[in] cfg Audio resample configuration + * + * @return + * - NULL No memory for resample instance + * - Others Audio resample instance + */ +audio_resample_handle_t audio_resample_open(audio_resample_cfg_t *cfg); + +/** + * @brief Write data to audio resample + * + * @param[in] h Audio resample handle + * @param[in] data Data to be written + * + * @return + * - ESP_MEDIA_ERR_OK On success + * - ESP_MEDIA_ERR_NO_MEM No enough memory + */ +int audio_resample_write(audio_resample_handle_t h, av_render_audio_frame_t *data); + +/** + * @brief Close audio resample + * + * @param[in] h Audio resample handle + */ +void audio_resample_close(audio_resample_handle_t h); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/av_render/include/av_render.h b/components/third_party/av_render/include/av_render.h new file mode 100644 index 0000000..18cb2af --- /dev/null +++ b/components/third_party/av_render/include/av_render.h @@ -0,0 +1,412 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#pragma once + +#include "av_render_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief AV render handle + */ +typedef void *av_render_handle_t; + +/** + * @brief AV render sync mode + */ +typedef enum { + AV_RENDER_SYNC_NONE, /*!< No sync, audio video run separately */ + AV_RENDER_SYNC_FOLLOW_AUDIO, /*!< Sync according audio position */ + AV_RENDER_SYNC_FOLLOW_TIME, /*!< Sync according system time */ +} av_render_sync_mode_t; + +/** + * @brief AV render configuration + */ +typedef struct { + audio_render_handle_t audio_render; /*!< Audio render handle */ + video_render_handle_t video_render; /*!< Video render handle */ + av_render_sync_mode_t sync_mode; /*!< AV sync mode */ + uint32_t audio_raw_fifo_size; /*!< Audio Decoder fifo size setting. if set, will create audio decode thread to decode data */ + uint32_t video_raw_fifo_size; /*!< Video Decoder fifo size setting. If set, will create video decode thread to decode data */ + uint32_t audio_render_fifo_size; /*!< Audio Render fifo size setting. If set, will create audio render thread to render audio */ + uint32_t video_render_fifo_size; /*!< Video Render fifo size setting. If set, will create video render thread to render audio */ + bool quit_when_eos; /*!< When received stream eos, quit thread or just wait for new data */ + bool allow_drop_data; /*!< Allow drop data when decode or render too slow */ + bool pause_render_only; /*!< If call `av_render_pause` only pause render thread or pause all thread */ + bool pause_on_first_frame; /*!< Whether automatically pause when render receive first frame */ + void *ctx; /*!< User context */ + bool video_cvt_in_render; /*!< Convert color in render*/ +} av_render_cfg_t; + +/** + * @brief AV render event type + */ +typedef enum { + AV_RENDER_EVENT_NONE, /*!< No event happen */ + AV_RENDER_EVENT_AUDIO_RENDERED, /*!< Audio is rendered */ + AV_RENDER_EVENT_VIDEO_RENDERED, /*!< Video is rendered */ + AV_RENDER_EVENT_AUDIO_EOS, /*!< Audio stream eos */ + AV_RENDER_EVENT_VIDEO_EOS, /*!< Video stream eos */ + AV_RENDER_EVENT_AUDIO_DECODE_ERR, /*!< Audio decode error */ + AV_RENDER_EVENT_VIDEO_DECODE_ERR, /*!< Video decode error */ +} av_render_event_t; + +/** + * @brief AV render fifo statistics + */ +typedef struct { + uint32_t duration; /*!< Fifo duration */ + int q_num; /*!< Queue number */ + int data_size; /*!< Data size */ + int render_q_num; /*!< Render queue number */ + int render_data_size; /*!< Render queue data number */ +} av_render_fifo_stat_t; + +/** + * @brief AV render fifo configuration + */ +typedef struct { + uint32_t raw_fifo_size; /*!< Decoder fifo size, if set to 0 will not create decode thread */ + uint32_t render_fifo_size; /*!< Render fifo size, if set to 0 will not create render thread */ +} av_render_fifo_cfg_t; + +/** + * @brief AV render event callback + * + * @param[in] event AV render event + * @param[in] ctx User context + * + * @return + * - 0 On success + * - Others Fail to process event + */ +typedef int (*av_render_event_cb)(av_render_event_t event, void *ctx); + +/** + * @brief Data pool free callback + * + * @param[in] data Data to be freed + * @param[in] ctx Data pool context + */ +typedef void (*av_render_pool_data_free)(void *data, void *ctx); + +/** + * @brief open AV render + * + * @param[in] cfg AV render configuration + * @param[in] ctx User context + * + * @return + * - NULL No resource + * - Others AV render handle + */ +av_render_handle_t av_render_open(av_render_cfg_t *cfg); + +/** + * @brief Set event callback for AV render + * + * @param[in] render AV render handle + * @param[in] cb AV render event callback + * @param[in] ctx User context + * + * @return + * - 0 On success + * - Others Fail to set event callback + */ +int av_render_set_event_cb(av_render_handle_t render, av_render_event_cb cb, void *ctx); + +/** + * @brief Configuration for audio fifo for AV render + * + * @param[in] render AV render handle + * @param[in] fifo_cfg Audio fifo configuration + * + * @return + * - 0 On success + * - Others Fail to config audio fifo + */ +int av_render_config_audio_fifo(av_render_handle_t render, av_render_fifo_cfg_t *fifo_cfg); + +/** + * @brief Add audio stream for AV render + * + * @param[in] render AV render handle + * @param[in] audio_info Audio stream information + * + * @return + * - 0 On success + * - Others Fail to add audio stream + */ +int av_render_add_audio_stream(av_render_handle_t render, av_render_audio_info_t *audio_info); + +/** + * @brief Set audio fixed frame info + * + * @note This API need set after add audio stream + * + * @param[in] render AV render handle + * @param[in] frame_info Audio stream information + * + * @return + * - 0 On success + * - Others Fail to set fixed frame info + */ +int av_render_set_fixed_frame_info(av_render_handle_t render, av_render_audio_frame_info_t *frame_info); + +/** + * @brief Configuration for video fifo for AV render + * + * @param[in] render AV render handle + * @param[in] fifo_cfg Video fifo configuration + * + * @return + * - 0 On success + * - Others Fail to config video fifo + */ +int av_render_config_video_fifo(av_render_handle_t render, av_render_fifo_cfg_t *fifo_cfg); + +/** + * @brief Add video stream for AV render + * + * @param[in] render AV render handle + * @param[in] video_info Video stream information + * + * @return + * - 0 On success + * - Others Fail to add video stream + */ +int av_render_add_video_stream(av_render_handle_t render, av_render_video_info_t *video_info); + +/** + * @brief Data pool setting for AV render + * + * @note When input audio and video data is in data pool, to avoid extra copy, need need provide pool free API + * When AV render not use it, it will call provided free API to release the pool data + * + * @param[in] render AV render handle + * @param[in] free API to free data pool + * @param[in] pool_ctx Data pool context + * + * @return + * - 0 On success + * - Others Fail to set data pool + */ +int av_render_use_data_pool(av_render_handle_t render, av_render_pool_data_free free, void *pool_ctx); + +/** + * @brief Add audio data for AV render + * + * @param[in] render AV render handle + * @param[in] audio_data Audio data + * + * @return + * - 0 On success + * - Others Fail to add audio data + */ +int av_render_add_audio_data(av_render_handle_t render, av_render_audio_data_t *audio_data); + +/** + * @brief Add video data for AV render + * + * @param[in] render AV render handle + * @param[in] video_data Video data + * + * @return + * - 0 On success + * - Others Fail to add video data + */ +int av_render_add_video_data(av_render_handle_t render, av_render_video_data_t *video_data); + +/** + * @brief Check audio fifo enough + * + * @param[in] render AV render handle + * @param[in] audio_data Audio data + * + * @return + * - true Fifo is enough + * - false Not enough fifo for input audio data + */ +bool av_render_audio_fifo_enough(av_render_handle_t render, av_render_audio_data_t *audio_data); + +/** + * @brief Check video fifo enough + * + * @param[in] render AV render handle + * @param[in] video_data Video data + * + * @return + * - true Fifo is enough + * - false Not enough fifo for input video data + */ +bool av_render_video_fifo_enough(av_render_handle_t render, av_render_video_data_t *video_data); + +/** + * @brief Get audio fifo level + * + * @param[in] render AV render handle + * @param[in] fifo_stat Audio fifo status + * + * @return + * - 0 On success + * - Others Fail to get audio fifo level + */ +int av_render_get_audio_fifo_level(av_render_handle_t render, av_render_fifo_stat_t *fifo_stat); + +/** + * @brief Get video fifo level + * + * @param[in] render AV render handle + * @param[in] fifo_stat Video fifo status + * + * @return + * - 0 On success + * - Others Fail to get video fifo level + */ +int av_render_get_video_fifo_level(av_render_handle_t render, av_render_fifo_stat_t *fifo_stat); + +/** + * @brief Pause for AV render + * + * @param[in] render AV render handle + * + * @return + * - 0 On success + * - Others Fail to pause + */ +int av_render_pause(av_render_handle_t render, bool pause); + +/** + * @brief Set playback speed for AV render + * + * @note Currently only allow to set for sync mode is `AV_RENDER_SYNC_FOLLOW_AUDIO` and have audio stream + * + * @param[in] render AV render handle + * @param[in] speed Playback speed + * + * @return + * - 0 On success + * - Others Fail to pause + */ +int av_render_set_speed(av_render_handle_t render, float speed); + +/** + * @brief Flush for AV render + * + * @note Flush will flush all pending data in decoder or render fifo + * Use can call `av_render_add_audio_data` or `av_render_add_video_data` to play new data + * + * @param[in] render AV render handle + * + * @return + * - 0 On success + * - Others Fail to pause + */ +int av_render_flush(av_render_handle_t render); + +/** + * @brief Set video start pts + * + * @note When video start pts is set, if decoded pts less than video start pts, data will de dropped + * + * @param[in] render AV render handle + * @param[in] video_pts Video start pts + * + * @return + * - 0 On success + * - Others Fail to set + */ +int av_render_set_video_start_pts(av_render_handle_t render, uint32_t video_pts); + +/** + * @brief Get current render presentation time + * + * @param[in] render AV render handle + * @param[in] video_pts Video start pts + * + * @return + * - 0 On success + * - Others Fail to set + */ +int av_render_get_render_pts(av_render_handle_t h, uint32_t *pts); + +/** + * @brief Query for AV render + * + * @note This API is used for debug only + * + * @param[in] render AV render handle + * + * @return + * - ESP_MEDIA_ERR_INVALID_ARG Invalid argument + * - ESP_MEDIA_ERR_OK On success + */ +int av_render_query(av_render_handle_t h); + +/** + * @brief Dump data for AV render + * + * @note This API is used for debug only + * + * @param[in] h AV render handle + * @param[in] mask Dump mask + * + * @return + * - ESP_MEDIA_ERR_INVALID_ARG Invalid argument + * - ESP_MEDIA_ERR_OK On success + */ +void av_render_dump(av_render_handle_t h, uint8_t mask); + +/** + * @brief Reset AV render + * + * @note When do reset it will stop the audio and video stream + * After reset, user can call `av_render_add_audio_stream` or `av_render_add_video_stream` to play new stream + * + * @param[in] h AV render handle + * @param[in] mask Dump mask + * + * @return + * - ESP_MEDIA_ERR_INVALID_ARG Invalid argument + * - ESP_MEDIA_ERR_OK On success + */ +int av_render_reset(av_render_handle_t render); + +/** + * @brief Close AV render + * + * @param[in] render AV render handle + * + * @return + * - ESP_MEDIA_ERR_INVALID_ARG Invalid argument + * - ESP_MEDIA_ERR_OK On success + */ +int av_render_close(av_render_handle_t render); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/av_render/include/av_render_default.h b/components/third_party/av_render/include/av_render_default.h new file mode 100644 index 0000000..4736d7f --- /dev/null +++ b/components/third_party/av_render/include/av_render_default.h @@ -0,0 +1,86 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "av_render_types.h" +#include "audio_render.h" +#include "video_render.h" +#include "esp_lcd_panel_ops.h" +#include "esp_codec_dev.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Audio render reference callback + */ +typedef int (*audio_render_ref_cb)(uint8_t *data, int size, void *ctx); + +/** + * @brief I2S render configuration + */ +typedef struct { + esp_codec_dev_handle_t play_handle; /*!< Playback handle */ + audio_render_ref_cb cb; /*!< Reference output callback */ + bool fixed_clock; /*!< Fixed clock mode */ + void *ctx; /*!< User context */ +} i2s_render_cfg_t; + +/** + * @brief LCD render configuration + */ +typedef struct { + esp_lcd_panel_handle_t lcd_handle; /*!< LCD display handle */ + bool rgb_panel; /*!< Whether RGB panel */ + bool dsi_panel; /*!< Whether DSI panel */ + bool use_frame_buffer; /*!< Use display frame buffer */ +} lcd_render_cfg_t; + +/** + * @brief Allocate I2S render + * + * @param[in] cfg I2S render configuration + * + * @return + * - NULL No memory for I2S render instance + * - Others Audio render instance + */ +audio_render_handle_t av_render_alloc_i2s_render(i2s_render_cfg_t *cfg); + +/** + * @brief Allocate LCD render + * + * @param[in] cfg LCD render configuration + * + * @return + * - NULL No memory for LCD render instance + * - Others video render instance + */ +video_render_handle_t av_render_alloc_lcd_render(lcd_render_cfg_t *cfg); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/av_render/include/av_render_types.h b/components/third_party/av_render/include/av_render_types.h new file mode 100644 index 0000000..bb963ea --- /dev/null +++ b/components/third_party/av_render/include/av_render_types.h @@ -0,0 +1,196 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#pragma once + +#include +#include +#include +#include +#include "media_lib_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Audio render stream type + */ +typedef enum { + AV_RENDER_STREAM_TYPE_NONE, /*!< Invalid stream type */ + AV_RENDER_STREAM_TYPE_AUDIO, /*!< Audio stream type */ + AV_RENDER_STREAM_TYPE_VIDEO /*!< Video stream type */ +} av_render_stream_type_t; + +/** + * @brief Video codec type + */ +typedef enum { + AV_RENDER_VIDEO_CODEC_NONE, /*!< Invalid video type */ + AV_RENDER_VIDEO_CODEC_H264, /*!< H264 video type */ + AV_RENDER_VIDEO_CODEC_MJPEG, /*!< MJPEG video type */ + AV_RENDER_VIDEO_CODEC_YUV420, /*!< YUV420 video type */ + AV_RENDER_VIDEO_CODEC_YUV422, /*!< YUV422 video type */ + AV_RENDER_VIDEO_CODEC_RGB565, /*!< RGB565 video type */ +} av_render_video_codec_t; + +/** + * @brief Audio codec type + */ +typedef enum { + AV_RENDER_AUDIO_CODEC_NONE, /*!< Invalid audio type */ + AV_RENDER_AUDIO_CODEC_AAC, /*!< AAC audio type */ + AV_RENDER_AUDIO_CODEC_MP3, /*!< MP3 audio type */ + AV_RENDER_AUDIO_CODEC_PCM, /*!< PCM audio type */ + AV_RENDER_AUDIO_CODEC_ADPCM, /*!< IMA-ADPCM audio type */ + AV_RENDER_AUDIO_CODEC_G711A, /*!< G711 alaw audio type */ + AV_RENDER_AUDIO_CODEC_G711U, /*!< G711 ulaw audio type */ + AV_RENDER_AUDIO_CODEC_AMRNB, /*!< AMRNB audio type */ + AV_RENDER_AUDIO_CODEC_AMRWB, /*!< AMRWB audio type */ + AV_RENDER_AUDIO_CODEC_OPUS, /*!< OPUS audio type */ + AV_RENDER_AUDIO_CODEC_FLAC, /*!< FLAC audio type */ + AV_RENDER_AUDIO_CODEC_VORBIS, /*!< VORBIS audio type */ + AV_RENDER_AUDIO_CODEC_ALAC, /*!< ALAC audio type */ +} av_render_audio_codec_t; + +/** + * @brief Audio stream information + */ +typedef struct { + av_render_audio_codec_t codec; /*!< Audio codec type */ + uint8_t channel; /*!< Audio channel */ + uint8_t bits_per_sample; /*!< Audio bits per sample */ + uint32_t sample_rate; /*!< Audio sample rate */ + uint32_t aac_no_adts : 1; /*!< AAC no adts */ + void *codec_spec_info; /*!< Audio codec specified information */ + int spec_info_len; /*!< Audio codec specified information length */ +} av_render_audio_info_t; + +/** + * @brief Audio frame information + */ +typedef struct { + uint8_t channel; /*!< Audio channel */ + uint8_t bits_per_sample; /*!< Audio bits per sample */ + uint32_t sample_rate; /*!< Audio sample rate */ +} av_render_audio_frame_info_t; + +/** + * @brief Audio frame + */ +typedef struct { + uint32_t pts; /*!< Audio PTS (unit ms) */ + uint8_t *data; /*!< Audio data */ + int size; /*!< Audio data size */ + bool eos; /*!< End of stream data */ +} av_render_audio_frame_t; + +/** + * @brief video render frame type + */ +typedef enum { + AV_RENDER_VIDEO_RAW_TYPE_NONE, /*!< Invalid video render frame type */ + AV_RENDER_VIDEO_RAW_TYPE_YUV422, /*!< YUV422 frame type */ + AV_RENDER_VIDEO_RAW_TYPE_YUV420, /*!< YUV420 frame type */ + AV_RENDER_VIDEO_RAW_TYPE_RGB565, /*!< RGB565 frame type */ + AV_RENDER_VIDEO_RAW_TYPE_RGB565_BE, /*!< RGB565 bigedian frame type */ + AV_RENDER_VIDEO_RAW_TYPE_MAX, /*!< Maximum of video render frame type */ +} av_render_video_frame_type_t; + +/** + * @brief video render frame information + */ +typedef struct { + av_render_video_frame_type_t type; /*!< Frame type */ + uint16_t width; /*!< Video width */ + uint16_t height; /*!< Video height */ + uint8_t fps; /*!< Video framerate per second */ +} av_render_video_frame_info_t; + +/** + * @brief video render frame data + */ +typedef struct { + uint32_t pts; /*!< PTS of frame data */ + uint8_t *data; /*!< Frame data */ + int size; /*!< Frame data size */ + bool eos; /*!< End of stream data */ +} av_render_video_frame_t; + +/** + * @brief video render frame buffer + * + * @note When use frame buffer, means decode output directly put to frame buffer without extra buffering + */ +typedef struct { + uint8_t *data; /*!< Frame buffer data */ + int size; /*!< Frame buffer size */ +} av_render_frame_buffer_t; + +/** + * @brief Video information + */ +typedef struct { + av_render_video_codec_t codec; /*!< Video codec type */ + uint16_t width; /*!< Video width */ + uint16_t height; /*!< Video height */ + uint8_t fps; /*!< Video framerate per second */ + void *codec_spec_info; /*!< Video codec specified info, + For H264 need to provide SPS and PPS information*/ + int spec_info_len; /*!< Video codec specified information length */ +} av_render_video_info_t; + +/** + * @brief Audio data + */ +typedef struct { + uint32_t pts; /*!< PTS of audio data */ + uint8_t *data; /*!< Audio data pointer */ + uint32_t size; /*!< Audio data size */ + bool eos; /*!< End of stream data*/ +} av_render_audio_data_t; + +/** + * @brief Video data + */ +typedef struct { + uint32_t pts; /*!< PTS of video data */ + bool key_frame; /*!< Whether it is key frame */ + uint8_t *data; /*!< Video data pointer */ + uint32_t size; /*!< Video data size */ + bool eos; /*!< End of stream data */ +} av_render_video_data_t; + +/** + * @brief audio render handle + */ +typedef void *audio_render_handle_t; + +/** + * @brief video render handle + */ +typedef void *video_render_handle_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/av_render/include/video_decoder.h b/components/third_party/av_render/include/video_decoder.h new file mode 100644 index 0000000..53925e3 --- /dev/null +++ b/components/third_party/av_render/include/video_decoder.h @@ -0,0 +1,148 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#pragma once + +#include "av_render_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Video decoder frame callback + */ +typedef int (*vdec_frame_cb)(av_render_video_frame_t *frame, void *ctx); + +/** + * @brief Video decoder configuration + */ +typedef struct { + av_render_video_info_t video_info; /*!< Video basic information */ + av_render_video_frame_type_t out_type; /*!< Output frame type */ + vdec_frame_cb frame_cb; /*!< Video decoded frame callback */ + void *ctx; /*!< Decoder context */ +} vdec_cfg_t; + +/** + * @brief Video decoder frame buffer callback configuration + */ +typedef struct { + uint8_t *(*fb_fetch)(int align, int size, void *ctx); /*!< Fetch frame buffer */ + int (*fb_return)(uint8_t *addr, bool drop, void *ctx); /*!< Return frame buffer */ + void *ctx; /*!< Context */ +} vdec_fb_cb_cfg_t; + +/** + * @brief Video decoder handle + */ +typedef void *vdec_handle_t; + +/** + * @brief Get video decoder output formats according decode type + * + * @param[in] codec Decode type + * @param[out] fmts Output formats to be filled + * @param[in,out] fmts Output formats number + * @return + * - NULL No resource for decoder + * - Others Video decoder handle + */ +int vdec_get_output_formats(av_render_video_codec_t codec, av_render_video_frame_type_t* fmts, uint8_t* num); + +/** + * @brief Open video decoder + * + * @param[in] cfg Video decoder configuration + * + * @return + * - NULL No resource for decoder + * - Others Video decoder handle + */ +vdec_handle_t vdec_open(vdec_cfg_t *cfg); + +/** + * @brief Set frame buffer callback + * + * @param[in] h Video decoder handle + * @param[in] cfg Frame buffer callback configuration + * + * @return + * - ESP_MEDIA_ERR_OK On success + * - ESP_MEDIA_ERR_INVALID_ARG Invalid argument + */ +int vdec_set_fb_cb(vdec_handle_t h, vdec_fb_cb_cfg_t *cfg); + +/** + * @brief Do video decode + * + * @param[in] h Video decoder handle + * @param[in] data Video data to be decoded + * + * @return + * - ESP_MEDIA_ERR_OK On success + * - ESP_MEDIA_ERR_INVALID_ARG Invalid argument + * - Other Fail to decode + */ +int vdec_decode(vdec_handle_t h, av_render_video_data_t *data); + +/** + * @brief Set frame buffer + * + * @param[in] h Video decoder handle + * @param[in] buffer Frame buffer to put decoded data + * + * @return + * - ESP_MEDIA_ERR_OK On success + * - ESP_MEDIA_ERR_INVALID_ARG Invalid argument + * - Other Fail to decode + */ +int vdec_set_frame_buffer(vdec_handle_t h, av_render_frame_buffer_t *buffer); + +/** + * @brief Get frame information from video decoder + * + * @param[in] h Video decoder handle + * @param[out] frame_info Video frame information + * + * @return + * - ESP_MEDIA_ERR_OK On success + * - ESP_MEDIA_ERR_INVALID_ARG Invalid argument + * - Other Fail to decode + */ +int vdec_get_frame_info(vdec_handle_t h, av_render_video_frame_info_t *frame_info); + +/** + * @brief Close video decoder + * + * @param[in] h Video decoder handle + * + * @return + * - ESP_MEDIA_ERR_OK On success + * - ESP_MEDIA_ERR_INVALID_ARG Invalid argument + */ +int vdec_close(vdec_handle_t h); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/av_render/include/video_render.h b/components/third_party/av_render/include/video_render.h new file mode 100644 index 0000000..8cc8d80 --- /dev/null +++ b/components/third_party/av_render/include/video_render.h @@ -0,0 +1,204 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#pragma once + +#include "av_render_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Video render open callback + */ +typedef video_render_handle_t (*video_render_open_func)(void *cfg, int size); + +/** + * @brief Video render check format supported callback + */ +typedef bool (*video_render_format_supported_func)(video_render_handle_t render, av_render_video_frame_type_t type); + +/** + * @brief Set frame information for video render callback + */ +typedef int (*video_render_set_frame_info_func)(video_render_handle_t render, av_render_video_frame_info_t *info); + +/** + * @brief Write data video render callback + */ +typedef int (*video_render_write_func)(video_render_handle_t render, av_render_video_frame_t *video_data); + +/** + * @brief Get video render latency callback + */ +typedef int (*video_render_latency_func)(video_render_handle_t render, uint32_t *latency); + +/** + * @brief Get frame buffer from video render callback + */ +typedef int (*video_render_get_frame_buffer_func)(video_render_handle_t render, av_render_frame_buffer_t *frame_buffer); + +/** + * @brief Get frame information from video render callback + */ +typedef int (*video_render_get_frame_info_func)(video_render_handle_t render, av_render_video_frame_info_t *info); + +/** + * @brief Clear of video render callback + */ +typedef int (*video_render_clear_func)(video_render_handle_t render); + +/** + * @brief Close of video render callback + */ +typedef int (*video_render_close_func)(video_render_handle_t render); + +/** + * @brief Video render operations + */ +typedef struct _render_video_ops { + video_render_open_func open; /*!< Video render open */ + video_render_format_supported_func format_support; /*!< Video render check format supported */ + video_render_set_frame_info_func set_frame_info; /*!< Set frame information */ + video_render_get_frame_buffer_func get_frame_buffer; /*!< Get frame buffer */ + video_render_write_func write; /*!< Write data to video render */ + video_render_latency_func get_latency; /*!< Get video render latency */ + video_render_get_frame_info_func get_frame_info; /*!< Get frame information */ + video_render_clear_func clear; /*!< Clear of video render */ + video_render_close_func close; /*!< Close of video render */ +} video_render_ops_t; + +/** + * @brief Video render configuration + */ +typedef struct { + video_render_ops_t ops; /*!< Video render implementation */ + void *cfg; /*!< Specified render configuration */ + int cfg_size; /*!< Specified render configuration size */ +} video_render_cfg_t; + +/** + * @brief Allocate video render + * + * @param[in] cfg Video render configuration + * + * @return + * - NULL Not enough memory or invalid argument + * - Others Video render instance + */ +video_render_handle_t video_render_alloc_handle(video_render_cfg_t *cfg); + +/** + * @brief Check whether video format supported + * + * @param[in] render Video render handle + * @param[in] frame_type Video frame type to be checked + * + * @return + * - true Format is supported + * - false Format not supported + */ +bool video_render_format_supported(video_render_handle_t render, av_render_video_frame_type_t frame_type); + +/** + * @brief Open video render + * + * @param[in] render Video render handle + * @param[in] info Video frame information + * + * @return + * - 0 On success + * - Others Fail to open + */ +int video_render_open(video_render_handle_t render, av_render_video_frame_info_t *info); + +/** + * @brief Write frame data to video render + * + * @param[in] render Video render handle + * @param[in] video_data Frame data to wrote + * + * @return + * - 0 On success + * - Others Fail to write + */ +int video_render_write(video_render_handle_t render, av_render_video_frame_t *video_data); + +/** + * @brief Get latency of video render + * + * @param[in] render Video render handle + * @param[out] latency Latency to get + * + * @return + * - 0 On success + * - Others Fail to get + */ +int video_render_get_latency(video_render_handle_t render, uint32_t *latency); + +/** + * @brief Get frame buffer from video render + * + * @param[in] render Video render handle + * @param[out] buffer Frame buffer + * + * @return + * - 0 On success + * - Others Fail to get + */ +int video_render_get_frame_buffer(video_render_handle_t render, av_render_frame_buffer_t *buffer); + +/** + * @brief Get frame information from video render + * + * @param[in] render Video render handle + * @param[out] info Video frame information + * + * @return + * - 0 On success + * - Others Fail to get + */ +int video_render_get_frame_info(video_render_handle_t render, av_render_video_frame_info_t *info); + +/** + * @brief Close of video render + * + * @param[in] render Video render handle + * + * @return + * - 0 On success + * - Others Fail to close + */ +int video_render_close(video_render_handle_t render); + +/** + * @brief Free of video render + * + * @param[in] render Video render handle + */ +void video_render_free_handle(video_render_handle_t render); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/av_render/libs/esp32p4/libesp_image_i420_2_rgb565le.a b/components/third_party/av_render/libs/esp32p4/libesp_image_i420_2_rgb565le.a new file mode 100644 index 0000000..1a0bfea Binary files /dev/null and b/components/third_party/av_render/libs/esp32p4/libesp_image_i420_2_rgb565le.a differ diff --git a/components/third_party/av_render/render_impl/CMakeLists.txt b/components/third_party/av_render/render_impl/CMakeLists.txt new file mode 100644 index 0000000..42a5d4e --- /dev/null +++ b/components/third_party/av_render/render_impl/CMakeLists.txt @@ -0,0 +1,5 @@ + +list (APPEND COMPONENT_SRCDIRS ./) +set(COMPONENT_PRIV_REQUIRES esp_timer media_lib_sal esp_lcd driver) + +register_component() diff --git a/components/third_party/av_render/render_impl/i2s_render.c b/components/third_party/av_render/render_impl/i2s_render.c new file mode 100644 index 0000000..09edebd --- /dev/null +++ b/components/third_party/av_render/render_impl/i2s_render.c @@ -0,0 +1,223 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#include +#include "av_render_default.h" +#include "media_lib_os.h" +#include "esp_codec_dev.h" +#include "esp_log.h" +#include "esp_ae_sonic.h" + +#define TAG "I2S_RENDER" + +#define SAMPLE_SIZE(info) (info.channel * info.bits_per_sample >> 3) + +#define DEFALUT_SONIC_OUT_SAMPLES (1024 * 4) + +typedef struct { + audio_render_ref_cb ref_cb; + av_render_audio_frame_info_t info; + void *ref_ctx; + bool fixed_clock; + esp_codec_dev_handle_t play_handle; + void *sonic_handle; + bool sonic_enable; + char *sonic_out; + int sonic_out_size; +} i2s_render_t; + +static audio_render_handle_t i2s_render_init(void *cfg, int cfg_size) +{ + i2s_render_cfg_t *i2s_cfg = (i2s_render_cfg_t *)cfg; + if (cfg == NULL || cfg_size != sizeof(i2s_render_cfg_t) || i2s_cfg->play_handle == NULL) { + return NULL; + } + i2s_render_t *i2s = (i2s_render_t *)media_lib_calloc(1, sizeof(i2s_render_t)); + if (i2s == NULL) { + return NULL; + } + i2s->ref_cb = i2s_cfg->cb; + i2s->ref_ctx = i2s_cfg->ctx; + i2s->fixed_clock = i2s_cfg->fixed_clock; + i2s->play_handle = i2s_cfg->play_handle; + if (i2s->play_handle == NULL) { + ESP_LOGE(TAG, "Play device not exists"); + media_lib_free(i2s); + return NULL; + } + return i2s; +} + +static int i2s_render_open(audio_render_handle_t render, av_render_audio_frame_info_t *info) +{ + if (render == NULL || info == NULL) { + return -1; + } + i2s_render_t *i2s = (i2s_render_t *)render; + // TODO, notes when set clk i2s should not in reading or writing status or else it wil hangup + ESP_LOGI(TAG, "open channel:%d sample_rate:%d bits:%d", info->channel, (int)info->sample_rate, info->bits_per_sample); + esp_codec_dev_sample_info_t fs = { + .bits_per_sample = info->bits_per_sample, + .sample_rate = info->sample_rate, + .channel = info->channel, + }; + int ret = esp_codec_dev_open(i2s->play_handle, &fs); + if (ret == 0) { + memcpy(&i2s->info, info, sizeof(av_render_audio_frame_info_t)); + } + return ret; +} + +static int i2s_render_write(audio_render_handle_t render, av_render_audio_frame_t *audio_data) +{ + if (render == NULL || audio_data == NULL) { + return -1; + } + int ret = ESP_OK; + i2s_render_t *i2s = (i2s_render_t *)render; + if (i2s->sonic_enable) { + int sample_num = audio_data->size / SAMPLE_SIZE(i2s->info); + esp_ae_sonic_in_data_t in_samples = { + .num = sample_num, + .samples = audio_data->data, + }; + esp_ae_sonic_out_data_t out_samples = { + .samples = i2s->sonic_out, + .needed_num = DEFALUT_SONIC_OUT_SAMPLES, + }; + while (sample_num > 0) { + ret = esp_ae_sonic_process(i2s->sonic_handle, &in_samples, &out_samples); + if (ret != ESP_AE_ERR_OK) { + printf("sonic process error\n"); + return -1; + } + if (out_samples.out_num == 0) { + break; + } + ret = esp_codec_dev_write(i2s->play_handle, out_samples.samples, out_samples.out_num * SAMPLE_SIZE(i2s->info)); + if (ret == 0 && i2s->ref_cb) { + i2s->ref_cb(audio_data->data, audio_data->size, i2s->ref_ctx); + } + sample_num -= in_samples.consume_num; + in_samples.num = sample_num; + in_samples.samples = audio_data->data + in_samples.consume_num * SAMPLE_SIZE(i2s->info); + } + } else { + int ret = esp_codec_dev_write(i2s->play_handle, audio_data->data, audio_data->size); + if (ret == 0 && i2s->ref_cb) { + i2s->ref_cb(audio_data->data, audio_data->size, i2s->ref_ctx); + } + } + return ret; +} + +static int i2s_render_get_latency(audio_render_handle_t render, uint32_t *latency) +{ + if (render == NULL) { + return -1; + } + *latency = 0; + return 0; +} + +static int i2s_render_get_frame_info(audio_render_handle_t render, av_render_audio_frame_info_t *info) +{ + if (render == NULL || info == NULL) { + return -1; + } + i2s_render_t *i2s = (i2s_render_t *)render; + memcpy(info, &i2s->info, sizeof(av_render_audio_frame_info_t)); + return 0; +} + +static int i2s_render_set_speed(audio_render_handle_t render, float speed) +{ + if (render == NULL) { + return -1; + } + i2s_render_t *i2s = (i2s_render_t *)render; + if (speed != 1.0) { + if (i2s->sonic_handle == NULL) { + esp_ae_sonic_cfg_t cfg = { + .sample_rate = i2s->info.sample_rate, + .channel = i2s->info.channel, + .bits_per_sample = i2s->info.bits_per_sample, + }; + esp_ae_sonic_open(&cfg, &i2s->sonic_handle); + i2s->sonic_out = media_lib_malloc(DEFALUT_SONIC_OUT_SAMPLES * SAMPLE_SIZE(i2s->info)); + } + if (i2s->sonic_handle && i2s->sonic_out) { + i2s->sonic_enable = true; + } + } + if (i2s->sonic_enable) { + esp_ae_sonic_set_speed(i2s->sonic_handle, speed); + } + return 0; +} + +static int i2s_render_close(audio_render_handle_t render) +{ + if (render == NULL) { + return -1; + } + i2s_render_t *i2s = (i2s_render_t *)render; + esp_codec_dev_close(i2s->play_handle); + if (i2s->sonic_handle) { + esp_ae_sonic_close(i2s->sonic_handle); + i2s->sonic_handle = NULL; + } + if (i2s->sonic_out) { + media_lib_free(i2s->sonic_out); + i2s->sonic_out = NULL; + } + i2s->sonic_enable = false; + return 0; +} + +static void i2s_render_deinit(audio_render_handle_t render) +{ + if (render == NULL) { + return; + } + media_lib_free(render); +} + +audio_render_handle_t av_render_alloc_i2s_render(i2s_render_cfg_t *i2s_cfg) +{ + audio_render_cfg_t cfg = { + .ops = { + .init = i2s_render_init, + .open = i2s_render_open, + .write = i2s_render_write, + .get_latency = i2s_render_get_latency, + .set_speed = i2s_render_set_speed, + .get_frame_info = i2s_render_get_frame_info, + .close = i2s_render_close, + .deinit = i2s_render_deinit, + }, + .cfg = i2s_cfg, + .cfg_size = sizeof(i2s_render_cfg_t), + }; + return audio_render_alloc_handle(&cfg); +} diff --git a/components/third_party/av_render/render_impl/idf_component.yml b/components/third_party/av_render/render_impl/idf_component.yml new file mode 100644 index 0000000..691fdf9 --- /dev/null +++ b/components/third_party/av_render/render_impl/idf_component.yml @@ -0,0 +1,7 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_codec_dev: "~1.3.4" + espressif/esp_audio_effects: "~1.1.0" + av_render: + path: ../ + diff --git a/components/third_party/av_render/render_impl/lcd_render.c b/components/third_party/av_render/render_impl/lcd_render.c new file mode 100644 index 0000000..6248c4e --- /dev/null +++ b/components/third_party/av_render/render_impl/lcd_render.c @@ -0,0 +1,297 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#include +#include +#include "video_render.h" +#include "av_render_default.h" +#include "media_lib_os.h" +#include "esp_log.h" +#include "esp_lcd_panel_ops.h" +#if SOC_LCD_RGB_SUPPORTED +#include "esp_lcd_panel_rgb.h" +#endif +#if CONFIG_IDF_TARGET_ESP32P4 +#include "esp_lcd_mipi_dsi.h" +#endif +#include "esp_timer.h" + +#define TAG "LCD_RENDER" + +typedef struct { + av_render_video_frame_info_t info; + esp_lcd_panel_handle_t handle; + bool rgb_panel; + bool dsi_panel; + uint8_t *frame_buffer[2]; + bool sel; + uint32_t start_time; + uint8_t frame_num; + bool drawing; +} lcd_render_t; + +static int lcd_render_close(video_render_handle_t h); + +#if CONFIG_IDF_TARGET_ESP32P4 +static bool draw_finished(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *data, void *ctx) +{ + lcd_render_t *lcd = (lcd_render_t *)ctx; + lcd->drawing = false; + return true; +} +#endif + +static video_render_handle_t lcd_render_open(void *cfg, int size) +{ + lcd_render_cfg_t *lcd_cfg = (lcd_render_cfg_t *)cfg; + if (cfg == NULL || size != sizeof(lcd_render_cfg_t) || lcd_cfg->lcd_handle == NULL) { + ESP_LOGE(TAG, "Invalid argument to open"); + return NULL; + } + lcd_render_t *lcd = (lcd_render_t *)media_lib_calloc(1, sizeof(lcd_render_t)); + if (lcd == NULL) { + return NULL; + } + lcd->handle = lcd_cfg->lcd_handle; // lcd_cfg->lcd_handle; + lcd->rgb_panel = lcd_cfg->rgb_panel; + lcd->dsi_panel = lcd_cfg->dsi_panel; + if (lcd_cfg->use_frame_buffer) { + if (lcd->rgb_panel) { +#if SOC_LCD_RGB_SUPPORTED + esp_lcd_rgb_panel_get_frame_buffer(lcd->handle, 2, (void **)&lcd->frame_buffer[0], + (void **)&lcd->frame_buffer[1]); +#endif + } + if (lcd->dsi_panel) { +#if CONFIG_IDF_TARGET_ESP32P4 + esp_lcd_dpi_panel_get_frame_buffer(lcd->handle, 2, (void **)&lcd->frame_buffer[0], + (void **)&lcd->frame_buffer[1]); +#endif + } + if (lcd->frame_buffer[0] == NULL) { + ESP_LOGE(TAG, "Fail to get frame buffer"); + lcd_render_close(lcd); + return NULL; + } + } +#if CONFIG_IDF_TARGET_ESP32P4 + esp_lcd_dpi_panel_event_callbacks_t dpi_cb = { + .on_color_trans_done = draw_finished, + }; + esp_lcd_dpi_panel_register_event_callbacks(lcd->handle, &dpi_cb, lcd); +#endif + return lcd; +} + +static bool lcd_render_format_supported(video_render_handle_t h, av_render_video_frame_type_t frame_type) +{ + if (h == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + lcd_render_t *lcd = (lcd_render_t *)h; + if (lcd->rgb_panel) { + if (frame_type == AV_RENDER_VIDEO_RAW_TYPE_RGB565) { + return true; + } + } else { +#if CONFIG_IDF_TARGET_ESP32P4 + if (frame_type == AV_RENDER_VIDEO_RAW_TYPE_RGB565) { + return true; + } +#else + if (frame_type == AV_RENDER_VIDEO_RAW_TYPE_RGB565_BE) { + return true; + } +#endif + } + return false; +} + +static int lcd_render_set_frame_info(video_render_handle_t h, av_render_video_frame_info_t *info) +{ + if (h == NULL || info == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + if (lcd_render_format_supported(h, info->type) == false) { + ESP_LOGE(TAG, "Only support format %d", info->type); + return ESP_MEDIA_ERR_NOT_SUPPORT; + } + lcd_render_t *lcd = (lcd_render_t *)h; + memcpy(&lcd->info, info, sizeof(av_render_video_frame_info_t)); + ESP_LOGI(TAG, "Render started %dx%d", info->width, info->height); + return 0; +} + +static int lcd_render_write(video_render_handle_t h, av_render_video_frame_t *video_data) +{ + lcd_render_t *lcd = (lcd_render_t *)h; + if (lcd == NULL || video_data == NULL || video_data->size == 0) { + ESP_LOGE(TAG, "lcd %p data %p size %d", lcd, video_data, video_data->size); + return ESP_MEDIA_ERR_INVALID_ARG; + } + if (lcd->start_time == 0 || lcd->frame_num == 0) { + lcd->start_time = esp_timer_get_time(); + } + lcd->frame_num++; + uint32_t cur_time = esp_timer_get_time(); + if (cur_time > lcd->start_time + 1000000) { + uint32_t elapse = cur_time - lcd->start_time; + ESP_LOGI(TAG, "fps: %" PRIu32, lcd->frame_num * 1000000 / elapse); + lcd->start_time = cur_time; + lcd->frame_num = 0; + } +#if CONFIG_IDF_TARGET_ESP32P4 + while (lcd->drawing) { + media_lib_thread_sleep(10); + } + lcd->drawing = true; +#endif + if (lcd->info.type == AV_RENDER_VIDEO_RAW_TYPE_RGB565 || lcd->info.type == AV_RENDER_VIDEO_RAW_TYPE_RGB565_BE) { + uint8_t *rgb_data = video_data->data; + ESP_LOGD(TAG, "pts:%d size %d", (int)video_data->pts, (int)video_data->size); + if (video_data->size < lcd->info.width * lcd->info.height * 2) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + if (lcd->frame_buffer[0]) { + // Check whether input ptr is frame buffer or not + lcd->sel = !lcd->sel; + return esp_lcd_panel_draw_bitmap(lcd->handle, 0, 0, lcd->info.width, lcd->info.height, rgb_data); + } +#if CONFIG_IDF_TARGET_ESP32P4 + return esp_lcd_panel_draw_bitmap(lcd->handle, 0, 0, lcd->info.width, lcd->info.height, rgb_data); +#endif + int w = lcd->info.width; + int h = lcd->info.height; + int i = 0; + int n = 40; + int ret = 0; + while (i < h) { + if (i + n > h) { + n = h - i; + } + ret = esp_lcd_panel_draw_bitmap(lcd->handle, 0, i, w, i + n, rgb_data); + rgb_data += n * w * 2; + i += n; + } + return ret; + } + return ESP_MEDIA_ERR_NOT_SUPPORT; +} + +static int lcd_render_get_frame_buffer(video_render_handle_t h, av_render_frame_buffer_t *buffer) +{ + lcd_render_t *lcd = (lcd_render_t *)h; + if (lcd == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + if (lcd->frame_buffer[0] == NULL) { + return ESP_MEDIA_ERR_NOT_SUPPORT; + } + uint8_t *frame_buffer = lcd->frame_buffer[lcd->sel]; + if (frame_buffer == NULL) { + frame_buffer = lcd->frame_buffer[0]; + } + // Only support RGB565 currently + buffer->data = frame_buffer; + buffer->size = lcd->info.width * lcd->info.height * 2; + return 0; +} + +static int lcd_render_get_latency(video_render_handle_t h, uint32_t *latency) +{ + lcd_render_t *lcd = (lcd_render_t *)h; + if (lcd == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + *latency = 0; + return 0; +} + +static int lcd_render_get_frame_info(video_render_handle_t h, av_render_video_frame_info_t *info) +{ + lcd_render_t *lcd = (lcd_render_t *)h; + if (lcd == NULL || info == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + memcpy(info, &lcd->info, sizeof(av_render_video_frame_info_t)); + return 0; +} + +static int lcd_render_clear(video_render_handle_t h) +{ + lcd_render_t *lcd = (lcd_render_t *)h; + if (lcd == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + if (lcd->handle && lcd->info.width) { + uint8_t *frame_buffer = lcd->frame_buffer[lcd->sel]; + if (frame_buffer == NULL) { +#ifndef CONFIG_IDF_TARGET_ESP32P4 + uint8_t *line_buffer = (uint8_t *)media_lib_malloc(lcd->info.width * 2); + if (line_buffer) { + memset(line_buffer, 0, lcd->info.width * 2); + for (int i = 0; i < lcd->info.height; i++) { + esp_lcd_panel_draw_bitmap(lcd->handle, 0, i, lcd->info.width, i + 1, line_buffer); + } + media_lib_free(line_buffer); + } +#endif + } else { + int len = lcd->info.width * lcd->info.height * 2; + memset(frame_buffer, 0, len); + esp_lcd_panel_draw_bitmap(lcd->handle, 0, 0, 1, 1, frame_buffer); + } + } + return 0; +} + +static int lcd_render_close(video_render_handle_t h) +{ + lcd_render_t *lcd = (lcd_render_t *)h; + if (lcd == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + lcd_render_clear(h); + media_lib_free(lcd); + return 0; +} + +video_render_handle_t av_render_alloc_lcd_render(lcd_render_cfg_t *lcd_cfg) +{ + video_render_cfg_t cfg = { + .ops = { + .open = lcd_render_open, + .format_support = lcd_render_format_supported, + .get_frame_buffer = lcd_render_get_frame_buffer, + .set_frame_info = lcd_render_set_frame_info, + .write = lcd_render_write, + .get_latency = lcd_render_get_latency, + .get_frame_info = lcd_render_get_frame_info, + .clear = lcd_render_clear, + .close = lcd_render_close, + }, + .cfg = lcd_cfg, + .cfg_size = sizeof(lcd_render_cfg_t), + }; + return video_render_alloc_handle(&cfg); +} diff --git a/components/third_party/av_render/src/audio_decoder.c b/components/third_party/av_render/src/audio_decoder.c new file mode 100644 index 0000000..e541c6b --- /dev/null +++ b/components/third_party/av_render/src/audio_decoder.c @@ -0,0 +1,305 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include "audio_decoder.h" +#include "esp_audio_dec.h" +#include "esp_audio_dec_default.h" +#include "media_lib_os.h" +#include "esp_log.h" + +#define TAG "AUDIO_DEC" + +#define ADEC_DEFAULT_OUTPUT_SIZE (4096) + +typedef struct { + av_render_audio_codec_t codec; + av_render_audio_frame_info_t frame_info; + adec_frame_cb frame_cb; + esp_audio_dec_handle_t dec_handle; + bool header_parsed; + void *ctx; + uint8_t *frame_data; + int frame_size; +} adec_t; + +static esp_audio_type_t get_audio_decoder_type(av_render_audio_codec_t audio_format) +{ + switch (audio_format) { + case AV_RENDER_AUDIO_CODEC_MP3: + return ESP_AUDIO_TYPE_MP3; + case AV_RENDER_AUDIO_CODEC_AAC: + return ESP_AUDIO_TYPE_AAC; + case AV_RENDER_AUDIO_CODEC_AMRNB: + return ESP_AUDIO_TYPE_AMRNB; + case AV_RENDER_AUDIO_CODEC_AMRWB: + return ESP_AUDIO_TYPE_AMRWB; + case AV_RENDER_AUDIO_CODEC_OPUS: + return ESP_AUDIO_TYPE_OPUS; + case AV_RENDER_AUDIO_CODEC_FLAC: + return ESP_AUDIO_TYPE_FLAC; + case AV_RENDER_AUDIO_CODEC_VORBIS: + return ESP_AUDIO_TYPE_VORBIS; + case AV_RENDER_AUDIO_CODEC_G711A: + return ESP_AUDIO_TYPE_G711A; + case AV_RENDER_AUDIO_CODEC_G711U: + return ESP_AUDIO_TYPE_G711U; + case AV_RENDER_AUDIO_CODEC_ADPCM: + return ESP_AUDIO_TYPE_ADPCM; + case AV_RENDER_AUDIO_CODEC_ALAC: + return ESP_AUDIO_TYPE_ALAC; + default: + return ESP_AUDIO_TYPE_UNSUPPORT; + } +} + +static int _open_audio_dec(adec_t *adec, av_render_audio_info_t *stream_info) +{ + esp_audio_dec_cfg_t dec_cfg = { + .type = get_audio_decoder_type(stream_info->codec), + }; + if (dec_cfg.type == ESP_AUDIO_TYPE_UNSUPPORT) { + return -1; + } + uint8_t *frame_data = (uint8_t *)media_lib_realloc(adec->frame_data, ADEC_DEFAULT_OUTPUT_SIZE); + if (frame_data == NULL) { + return ESP_MEDIA_ERR_NO_MEM; + } + adec->frame_size = ADEC_DEFAULT_OUTPUT_SIZE; + adec->frame_data = frame_data; + + switch (dec_cfg.type) { + case ESP_AUDIO_TYPE_VORBIS: { +#if 0 + ogg_vorbis_spec_info_t* vorbis_info = (ogg_vorbis_spec_info_t*)stream_info->codec_spec_info; + esp_vorbis_dec_cfg_t vorbis_cfg = { + .setup_header = vorbis_info->setup_header, + .setup_size = vorbis_info->setup_size, + .info_header = vorbis_info->info_header, + .info_size = vorbis_info->info_size, + }; + dec_cfg.cfg = &vorbis_cfg; + dec_cfg.cfg_sz = sizeof(esp_vorbis_dec_cfg_t); + esp_audio_dec_open(&dec_cfg, &adec->dec_handle); +#endif + } break; + case ESP_AUDIO_TYPE_OPUS: { + esp_opus_dec_cfg_t opus_cfg = { + .sample_rate = stream_info->sample_rate, + .channel = stream_info->channel, + }; + dec_cfg.cfg = &opus_cfg; + dec_cfg.cfg_sz = sizeof(esp_opus_dec_cfg_t); + esp_audio_dec_open(&dec_cfg, &adec->dec_handle); + } break; + case ESP_AUDIO_TYPE_ADPCM: { + esp_adpcm_dec_cfg_t adpcm_cfg = { + .sample_rate = stream_info->sample_rate, + .channel = stream_info->channel, + .bits_per_sample = stream_info->bits_per_sample, + }; + dec_cfg.cfg = &adpcm_cfg; + dec_cfg.cfg_sz = sizeof(esp_adpcm_dec_cfg_t); + esp_audio_dec_open(&dec_cfg, &adec->dec_handle); + } break; + case ESP_AUDIO_TYPE_G711A: + case ESP_AUDIO_TYPE_G711U: { + esp_g711_dec_cfg_t g711_cfg = { + .channel = stream_info->channel, + }; + if (stream_info->channel) { + dec_cfg.cfg = &g711_cfg; + dec_cfg.cfg_sz = sizeof(esp_g711_dec_cfg_t); + } + esp_audio_dec_open(&dec_cfg, &adec->dec_handle); + } break; + case ESP_AUDIO_TYPE_ALAC: { + esp_alac_dec_cfg_t alac_cfg = { + .codec_spec_info = stream_info->codec_spec_info, + .spec_info_len = stream_info->spec_info_len, + }; + dec_cfg.cfg = &alac_cfg; + dec_cfg.cfg_sz = sizeof(esp_alac_dec_cfg_t); + esp_audio_dec_open(&dec_cfg, &adec->dec_handle); + } break; + case ESP_AUDIO_TYPE_AAC: { + esp_aac_dec_cfg_t aac_cfg = { + .channel = stream_info->channel, + .sample_rate = stream_info->sample_rate, + .bits_per_sample = stream_info->bits_per_sample, + .no_adts_header = stream_info->aac_no_adts, + }; + if (stream_info->aac_no_adts) { + dec_cfg.cfg = &aac_cfg; + dec_cfg.cfg_sz = sizeof(esp_alac_dec_cfg_t); + } + esp_audio_dec_open(&dec_cfg, &adec->dec_handle); + } break; + default: + esp_audio_dec_open(&dec_cfg, &adec->dec_handle); + break; + } + if (adec->dec_handle == NULL) { + return -1; + } + return 0; +} + +static int _close_audio_dec(adec_t *adec) +{ + if (adec->dec_handle) { + esp_audio_dec_close(adec->dec_handle); + adec->dec_handle = NULL; + return 0; + } + return -1; +} + +static int decoder_one_frame(adec_t *adec, uint8_t *data, int size, av_render_audio_frame_t *frame_data) +{ + esp_audio_dec_in_raw_t raw = { + .buffer = data, + .len = size, + }; + esp_audio_dec_out_frame_t frame = { + .buffer = adec->frame_data, + .len = adec->frame_size, + }; +RETRY: + frame.decoded_size = 0; + esp_audio_err_t ret = esp_audio_dec_process(adec->dec_handle, &raw, &frame); + if (ret == ESP_AUDIO_ERR_BUFF_NOT_ENOUGH) { + ESP_LOGI(TAG, "Enlarge PCM buffer to %" PRIu32, frame.needed_size); + uint8_t *output_fifo = (uint8_t *)media_lib_realloc(adec->frame_data, frame.needed_size); + if (output_fifo == NULL) { + return ESP_MEDIA_ERR_NO_MEM; + } + frame.buffer = output_fifo; + frame.len = frame.needed_size; + adec->frame_data = output_fifo; + adec->frame_size = frame.needed_size; + goto RETRY; + } + if (ret != ESP_AUDIO_ERR_OK) { + ESP_LOGE(TAG, "Audio decode error %d", ret); + return ret; + } + if (adec->header_parsed == false && frame.decoded_size > 0) { + esp_audio_dec_info_t header = {}; + esp_audio_dec_get_info(adec->dec_handle, &header); + printf("Get samplerate %d chanel %d\n", (int)header.sample_rate, header.channel); + if (header.sample_rate && header.channel && header.bits_per_sample) { + adec->frame_info.sample_rate = header.sample_rate; + adec->frame_info.channel = header.channel; + adec->frame_info.bits_per_sample = header.bits_per_sample; + } + adec->header_parsed = true; + } + frame_data->data = adec->frame_data; + frame_data->size = frame.decoded_size; + if (adec->frame_cb) { + adec->frame_cb(frame_data, adec->ctx); + } + if (raw.consumed < raw.len) { + raw.buffer += raw.consumed; + raw.len -= raw.consumed; + raw.consumed = 0; + goto RETRY; + } + return ESP_MEDIA_ERR_OK; +} + +static int _start_audio_dec(adec_t *adec, av_render_audio_data_t *frame, av_render_audio_frame_t *frame_data) +{ + if (frame->size == 0) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + uint8_t *dec_buffer = frame->data; + frame_data->pts = frame->pts; + return decoder_one_frame(adec, dec_buffer, frame->size, frame_data); +} + +adec_handle_t adec_open(adec_cfg_t *cfg) +{ + adec_t *adec = (adec_t *)media_lib_calloc(1, sizeof(adec_t)); + if (adec == NULL) { + return NULL; + } + adec->frame_cb = cfg->frame_cb; + adec->ctx = cfg->ctx; + adec->codec = cfg->audio_info.codec; + adec->frame_info.sample_rate = cfg->audio_info.sample_rate; + adec->frame_info.channel = cfg->audio_info.channel; + adec->frame_info.bits_per_sample = cfg->audio_info.bits_per_sample; + int ret = _open_audio_dec(adec, &cfg->audio_info); + if (ret == 0) { + return adec; + } + adec_close(adec); + return NULL; +} + +int adec_decode(adec_handle_t h, av_render_audio_data_t *data) +{ + if (h == NULL || data == NULL || (data->size == 0 && data->eos == false)) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + adec_t *adec = (adec_t *)h; + av_render_audio_frame_t frame_data = { + .pts = data->pts, + .eos = data->eos, + }; + if (data->size == 0 && data->eos) { + // Not decode and send EOS directly + if (adec->frame_cb) { + adec->frame_cb(&frame_data, adec->ctx); + } + return ESP_MEDIA_ERR_OK; + } + return _start_audio_dec(adec, data, &frame_data); +} + +int adec_get_frame_info(adec_handle_t h, av_render_audio_frame_info_t *frame_info) +{ + if (h == NULL || frame_info == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + adec_t *adec = (adec_t *)h; + memcpy(frame_info, &adec->frame_info, sizeof(av_render_audio_frame_info_t)); + return 0; +} + +int adec_close(adec_handle_t h) +{ + if (h == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + adec_t *adec = (adec_t *)h; + _close_audio_dec(adec); + if (adec->frame_data) { + media_lib_free(adec->frame_data); + } + media_lib_free(adec); + return 0; +} diff --git a/components/third_party/av_render/src/audio_render.c b/components/third_party/av_render/src/audio_render.c new file mode 100644 index 0000000..d978ce9 --- /dev/null +++ b/components/third_party/av_render/src/audio_render.c @@ -0,0 +1,139 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include "audio_render.h" +#include "media_lib_os.h" + +typedef struct { + audio_render_ops_t render_ops; + audio_render_handle_t render_handle; + bool is_open; + int ref_count; +} audio_render_t; + +static void try_free(audio_render_t *a_render) +{ + if (a_render->ref_count) { + a_render->ref_count--; + } + if (a_render->ref_count == 0) { + if (a_render->render_handle) { + a_render->render_ops.deinit(a_render->render_handle); + a_render->render_handle = NULL; + } + media_lib_free(a_render); + } +} + +audio_render_handle_t audio_render_alloc_handle(audio_render_cfg_t *cfg) +{ + if (cfg == NULL || cfg->ops.open == NULL || cfg->ops.close == NULL) { + return NULL; + } + audio_render_t *a_render = (audio_render_t *)media_lib_calloc(1, sizeof(audio_render_t)); + if (a_render == NULL) { + return NULL; + } + memcpy(&a_render->render_ops, &cfg->ops, sizeof(audio_render_ops_t)); + a_render->render_handle = a_render->render_ops.init(cfg->cfg, cfg->cfg_size); + if (a_render->render_handle) { + a_render->ref_count++; + return a_render; + } + audio_render_free_handle(a_render); + return NULL; +} + +int audio_render_open(audio_render_handle_t render, av_render_audio_frame_info_t *info) +{ + audio_render_t *a_render = render; + if (info == NULL || a_render == NULL || a_render->render_handle == NULL) { + return -1; + } + int ret = a_render->render_ops.open(a_render->render_handle, info); + if (ret != 0) { + return ret; + } + a_render->is_open = true; + a_render->ref_count++; + return ret; +} + +int audio_render_write(audio_render_handle_t render, av_render_audio_frame_t *audio_data) +{ + audio_render_t *a_render = render; + if (audio_data == NULL || a_render == NULL || a_render->is_open == false) { + return -1; + } + return a_render->render_ops.write(a_render->render_handle, audio_data); +} + +int audio_render_get_latency(audio_render_handle_t render, uint32_t *latency) +{ + audio_render_t *a_render = render; + if (latency == NULL || a_render == NULL || a_render->is_open == false) { + return -1; + } + return a_render->render_ops.get_latency(a_render->render_handle, latency); +} + +int audio_render_set_speed(audio_render_handle_t render, float speed) +{ + audio_render_t *a_render = render; + if (a_render == NULL || a_render->is_open == false) { + return -1; + } + return a_render->render_ops.set_speed(a_render->render_handle, speed); +} + +int audio_render_get_frame_info(audio_render_handle_t render, av_render_audio_frame_info_t *info) +{ + audio_render_t *a_render = render; + if (info == NULL || a_render == NULL || a_render->is_open == false) { + return -1; + } + return a_render->render_ops.get_frame_info(a_render->render_handle, info); +} + +int audio_render_close(audio_render_handle_t render) +{ + audio_render_t *a_render = render; + if (a_render == NULL || a_render->is_open == false) { + return -1; + } + a_render->is_open = false; + int ret = a_render->render_ops.close(a_render->render_handle); + try_free(a_render); + return ret; +} + +void audio_render_free_handle(audio_render_handle_t render) +{ + audio_render_t *a_render = render; + if (a_render == NULL) { + return; + } + try_free(a_render); +} diff --git a/components/third_party/av_render/src/audio_resample.c b/components/third_party/av_render/src/audio_resample.c new file mode 100644 index 0000000..39529b3 --- /dev/null +++ b/components/third_party/av_render/src/audio_resample.c @@ -0,0 +1,292 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#include "audio_resample.h" +#include "esp_ae_ch_cvt.h" +#include "esp_ae_rate_cvt.h" +#include "esp_ae_bit_cvt.h" +#include "media_lib_os.h" +#include "esp_log.h" + +#define TAG "RESAMPLE" + +#define ELEMS(a) (sizeof(a) / sizeof(a[0])) + +#define SAMPLE_SIZE(info) (info.channel * (info.bits_per_sample >> 3)) + +typedef struct { + uint8_t *data; + int size; + bool used; +} work_buf_t; + +typedef enum { + RESAMPLE_OPS_NONE, + RESAMPLE_OPS_CH_CVT, + RESAMPLE_OPS_BIT_CVT, + RESAMPLE_OPS_RATE_CVT, +} resample_ops_t; + +typedef struct { + audio_resample_cfg_t cfg; + esp_ae_ch_cvt_handle_t ch_cvt_handle; + esp_ae_rate_cvt_handle_t rate_cvt_handle; + esp_ae_bit_cvt_handle_t bit_cvt_handle; + resample_ops_t ops[3]; + work_buf_t work_buf[2]; +} resample_t; + +static int add_bits_resample(resample_t *resample, audio_resample_cfg_t *cfg, int i) +{ + if (cfg->input_info.bits_per_sample > cfg->output_info.bits_per_sample) { + resample->ops[i++] = RESAMPLE_OPS_BIT_CVT; + } + if (cfg->input_info.sample_rate != cfg->output_info.sample_rate) { + resample->ops[i++] = RESAMPLE_OPS_RATE_CVT; + } + if (cfg->input_info.bits_per_sample < cfg->output_info.bits_per_sample) { + resample->ops[i++] = RESAMPLE_OPS_BIT_CVT; + } + return i; +} + +static void sort_resample_ops(resample_t *resample, audio_resample_cfg_t *cfg) +{ + int i = 0; + if (cfg->input_info.channel > cfg->output_info.channel) { + resample->ops[i++] = RESAMPLE_OPS_CH_CVT; + } + i = add_bits_resample(resample, cfg, i); + if (cfg->input_info.channel < cfg->output_info.channel) { + resample->ops[i++] = RESAMPLE_OPS_CH_CVT; + } +} + +static work_buf_t *alloc_work_buf(resample_t *resample, int size) +{ + for (int i = 0; i < ELEMS(resample->work_buf); i++) { + if (resample->work_buf[i].used == false) { + if (size > resample->work_buf[i].size) { + uint8_t *new_buf = media_lib_realloc(resample->work_buf[i].data, size); + if (new_buf == NULL) { + return NULL; + } + resample->work_buf[i].data = new_buf; + resample->work_buf[i].size = size; + } + resample->work_buf[i].used = true; + return &resample->work_buf[i]; + } + } + return NULL; +} + +static void release_work_buf(work_buf_t *buf) +{ + if (buf) { + buf->used = false; + } +} + +static int get_need_size(resample_t *resample, resample_ops_t op, uint32_t *sample, av_render_audio_frame_info_t *info) +{ + if (op == RESAMPLE_OPS_CH_CVT) { + return (*sample * resample->cfg.output_info.channel * (info->bits_per_sample >> 3)); + } + if (op == RESAMPLE_OPS_RATE_CVT) { + uint32_t out_sample = 0; + esp_ae_rate_cvt_get_max_out_sample_num(resample->rate_cvt_handle, *sample, &out_sample); + *sample = out_sample; + return out_sample * info->channel * (info->bits_per_sample >> 3); + } + if (op == RESAMPLE_OPS_BIT_CVT) { + return (*sample * info->channel * (resample->cfg.output_info.bits_per_sample >> 3)); + } + return 0; +} + +audio_resample_handle_t audio_resample_open(audio_resample_cfg_t *cfg) +{ + resample_t *resample = (resample_t *)media_lib_calloc(1, sizeof(resample_t)); + do { + if (resample == NULL) { + break; + } + sort_resample_ops(resample, cfg); + av_render_audio_frame_info_t cur_info = cfg->input_info; + esp_ae_err_t ret = ESP_AE_ERR_OK; + for (int i = 0; i < ELEMS(resample->ops); i++) { + if (resample->ops[i] == RESAMPLE_OPS_NONE) { + break; + } + if (resample->ops[i] == RESAMPLE_OPS_CH_CVT) { + esp_ae_ch_cvt_cfg_t ch_cfg = { + .sample_rate = cur_info.sample_rate, + .bits_per_sample = cur_info.bits_per_sample, + .src_ch = cur_info.channel, + .dest_ch = cfg->output_info.channel, + }; + ret = esp_ae_ch_cvt_open(&ch_cfg, &resample->ch_cvt_handle); + if (ret != ESP_AE_ERR_OK) { + break; + } + cur_info.channel = cfg->output_info.channel; + } else if (resample->ops[i] == RESAMPLE_OPS_RATE_CVT) { + esp_ae_rate_cvt_cfg_t rate_cfg = { + .src_rate = cur_info.sample_rate, + .dest_rate = cfg->output_info.sample_rate, + .channel = cur_info.channel, + .bits_per_sample = cur_info.bits_per_sample, + .complexity = 2, + .perf_type = ESP_AE_RATE_CVT_PERF_TYPE_SPEED, + }; + ret = esp_ae_rate_cvt_open(&rate_cfg, &resample->rate_cvt_handle); + if (ret != ESP_AE_ERR_OK) { + break; + } + cur_info.sample_rate = cfg->output_info.sample_rate; + } else if (resample->ops[i] == RESAMPLE_OPS_BIT_CVT) { + esp_ae_bit_cvt_cfg_t bit_cfg = { + .channel = cur_info.channel, + .sample_rate = cur_info.sample_rate, + .src_bits = cur_info.bits_per_sample, + .dest_bits = cfg->output_info.bits_per_sample, + }; + ret = esp_ae_bit_cvt_open(&bit_cfg, &resample->bit_cvt_handle); + if (ret != ESP_AE_ERR_OK) { + break; + } + cur_info.bits_per_sample = cfg->output_info.bits_per_sample; + } + } + if (ret != ESP_AE_ERR_OK) { + ESP_LOGE(TAG, "Fail to open AE for resample"); + break; + } + resample->cfg = *cfg; + return resample; + } while (0); + audio_resample_close(resample); + return NULL; +} + +int audio_resample_write(audio_resample_handle_t h, av_render_audio_frame_t *data) +{ + resample_t *resample = (resample_t *)h; + // Bypass or size is 0 + if (data->size == 0 || resample->ops[0] == RESAMPLE_OPS_NONE) { + resample->cfg.resample_cb(data, resample->cfg.ctx); + return ESP_MEDIA_ERR_OK; + } + av_render_audio_frame_info_t cur_info = resample->cfg.input_info; + work_buf_t *cur = NULL; + work_buf_t *last = NULL; + uint32_t sample_num = data->size / SAMPLE_SIZE(cur_info); + int need_size = 0; + for (int i = 0; i < ELEMS(resample->ops); i++) { + if (resample->ops[i] == RESAMPLE_OPS_NONE) { + break; + } + uint32_t out_sample = sample_num; + need_size = get_need_size(resample, resample->ops[i], &out_sample, &cur_info); + cur = alloc_work_buf(resample, need_size); + if (cur == NULL) { + release_work_buf(last); + return ESP_MEDIA_ERR_NO_MEM; + } + esp_ae_sample_t in_sample = (esp_ae_sample_t)(last ? last->data : data->data); + if (resample->ops[i] == RESAMPLE_OPS_CH_CVT) { + esp_ae_ch_cvt_process(resample->ch_cvt_handle, sample_num, in_sample, (esp_ae_sample_t)cur->data); + cur_info.channel = resample->cfg.output_info.channel; + } else if (resample->ops[i] == RESAMPLE_OPS_RATE_CVT) { + esp_ae_rate_cvt_process(resample->rate_cvt_handle, in_sample, sample_num, (esp_ae_sample_t)cur->data, &out_sample); + need_size = out_sample * SAMPLE_SIZE(cur_info); + cur_info.sample_rate = resample->cfg.output_info.sample_rate; + sample_num = out_sample; + } else if (resample->ops[i] == RESAMPLE_OPS_BIT_CVT) { + esp_ae_bit_cvt_process(resample->bit_cvt_handle, sample_num, in_sample, (esp_ae_sample_t)cur->data); + cur_info.bits_per_sample = resample->cfg.output_info.bits_per_sample; + } + if (last) { + release_work_buf(last); + } + last = cur; + } + release_work_buf(cur); + av_render_audio_frame_t new_frame = *data; + new_frame.data = cur->data; + new_frame.size = need_size; + resample->cfg.resample_cb(&new_frame, resample->cfg.ctx); + return ESP_MEDIA_ERR_OK; +} + +void audio_resample_close(audio_resample_handle_t h) +{ + resample_t *resample = (resample_t *)h; + if (resample == NULL) { + return; + } + if (resample->bit_cvt_handle) { + esp_ae_bit_cvt_close(resample->bit_cvt_handle); + resample->bit_cvt_handle = NULL; + } + if (resample->ch_cvt_handle) { + esp_ae_ch_cvt_close(resample->ch_cvt_handle); + resample->ch_cvt_handle = NULL; + } + if (resample->rate_cvt_handle) { + esp_ae_rate_cvt_close(resample->rate_cvt_handle); + resample->rate_cvt_handle = NULL; + } + for (int i = 0; i < ELEMS(resample->work_buf); i++) { + if (resample->work_buf[i].data) { + media_lib_free(resample->work_buf[i].data); + resample->work_buf[i].data = NULL; + } + } + media_lib_free(resample); +} diff --git a/components/third_party/av_render/src/av_render.c b/components/third_party/av_render/src/av_render.c new file mode 100644 index 0000000..f9dab9d --- /dev/null +++ b/components/third_party/av_render/src/av_render.c @@ -0,0 +1,2138 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include +#include "media_lib_os.h" +#include "data_queue.h" +#include "msg_q.h" +#include "av_render.h" +#include "audio_decoder.h" +#include "video_decoder.h" +#include "audio_render.h" +#include "video_render.h" +#include "audio_resample.h" +#include "esp_timer.h" +#include "color_convert.h" +#include "esp_log.h" + +#define TAG "AV_RENDER" + +#define ADEC_CLOSED_BITS (1 << 0) +#define VDEC_CLOSED_BITS (1 << 1) +#define A_RENDER_CLOSED_BITS (1 << 2) +#define V_RENDER_CLOSED_BITS (1 << 3) + +#define FLUSH_SHIFT_BITS (4) + +#define _SET_BITS(group, bit) media_lib_event_group_set_bits((media_lib_event_grp_handle_t)group, bit) +// Need manual clear bits +#define _WAIT_BITS(group, bit) \ + media_lib_event_group_wait_bits((media_lib_event_grp_handle_t)group, bit, MEDIA_LIB_MAX_LOCK_TIME); \ + media_lib_event_group_clr_bits(group, bit) + +#define VIDEO_ERR_FRAME_TOLERANCE (5) +#define AUDIO_ERR_FRAME_TOLERANCE (10) + +typedef enum { + AV_RENDER_MSG_NONE, + AV_RENDER_MSG_PAUSE, + AV_RENDER_MSG_RESUME, + AV_RENDER_MSG_FLUSH, + AV_RENDER_MSG_DATA, + AV_RENDER_MSG_CLOSE, +} av_render_msg_type_t; + +typedef struct { + av_render_msg_type_t type; + uint32_t data; +} av_render_msg_t; + +typedef struct _render_thread_res_t { + media_lib_thread_handle_t thread; + msg_q_handle_t msg_q; + data_queue_t *data_q; + bool use_pool; + const char *name; + uint8_t wait_bits; + uint8_t flushing; + struct _av_render *render; + bool paused; + int (*render_body)(struct _render_thread_res_t *res, bool drop); +} av_render_thread_res_t; + +typedef struct { + av_render_thread_res_t thread_res; + adec_handle_t adec; + int audio_err_cnt; +} av_render_adec_res_t; + +typedef struct { + av_render_thread_res_t thread_res; + vdec_handle_t vdec; + int video_err_cnt; + av_render_video_frame_t *fb_frame; + av_render_video_frame_type_t dec_out_fmt; + av_render_video_frame_type_t out_fmt; + color_convert_table_t *vid_convert; + uint8_t *vid_convert_out; + int vid_convert_out_size; +} av_render_vdec_res_t; + +struct _av_render; + +typedef struct { + av_render_thread_res_t thread_res; + bool audio_packet_reached; + bool audio_rendered; + av_render_audio_frame_info_t audio_frame_info; + av_render_audio_frame_info_t out_frame_info; + audio_resample_handle_t resample_handle; + bool need_resample; + uint32_t audio_send_pts; + bool decode_in_sync; + bool audio_is_pcm; + bool a_render_in_sync; +} av_render_audio_res_t; + +typedef struct { + av_render_thread_res_t thread_res; + bool video_packet_reached; + bool video_rendered; + av_render_video_frame_info_t video_frame_info; + bool decode_in_sync; + bool use_fb; + uint32_t video_send_pts; + bool v_render_in_sync; + bool video_is_raw; + uint32_t sent_frame_num; + uint32_t video_start_time; + uint32_t video_start_pts; + uint32_t video_last_pts; + int sync_tolerance; +} av_render_video_res_t; + +typedef struct _av_render { + av_render_cfg_t cfg; + + av_render_adec_res_t *adec_res; + av_render_vdec_res_t *vdec_res; + av_render_audio_res_t *a_render_res; + av_render_video_res_t *v_render_res; + av_render_audio_frame_info_t aud_fix_info; + + media_lib_event_grp_handle_t event_group; + media_lib_mutex_handle_t api_lock; + av_render_event_cb event_cb; + void *event_ctx; + av_render_pool_data_free pool_free; + void *pool; +} av_render_t; + +typedef enum { + AV_RENDER_DUMP_ADEC_DATA, + AV_RENDER_DUMP_ARENDER_DATA, + AV_RENDER_DUMP_VDEC_DATA, + AV_RENDER_DUMP_VRENDER_DATA, + AV_RENDER_DUMP_STOP_INDEX, +} av_render_dump_type_t; + +#define BREAK_ON_FAIL(ret) if (ret != 0) { \ + break; \ +} +#define RETURN_ON_FAIL(ret) if (ret != 0) { \ + return ret; \ +} + +#define RETURN_ON_NULL(ptr, ret) if (ptr == NULL) { \ + return ret; \ +} + +static uint8_t render_dump_mask; + +static int av_render_get_audio_pts(av_render_t *render, uint32_t *out_pts); + +static int av_render_get_video_pts(av_render_t *render, uint32_t *out_pts); + +static void render_thread(void *arg); + +static uint32_t get_cur_time() +{ + return esp_timer_get_time() / 1000; +} + +static int put_to_adec(data_queue_t *q, av_render_audio_data_t *data, bool use_pool) +{ + int head_size = sizeof(av_render_audio_data_t); + int size = head_size + (use_pool ? 0 : data->size); + uint8_t *b = (uint8_t *)data_queue_get_buffer(q, size); + if (b == NULL) { + ESP_LOGE(TAG, "Drop for no enough %d", size); + return -1; + } + memcpy(b, data, head_size); + if (use_pool == false && data->size) { + memcpy(b + head_size, data->data, data->size); + } + return data_queue_send_buffer(q, size); +} + +static int put_to_vdec(data_queue_t *q, av_render_video_data_t *data, bool use_pool) +{ + int head_size = sizeof(av_render_video_data_t); + int size = head_size + (use_pool ? 0 : data->size); + uint8_t *b = (uint8_t *)data_queue_get_buffer(q, size); + if (b == NULL) { + return -1; + } + memcpy(b, data, head_size); + if (use_pool == false && data->size) { + memcpy(b + head_size, data->data, data->size); + } + return data_queue_send_buffer(q, size); +} + +static int put_to_a_render(data_queue_t *q, av_render_audio_frame_t *data) +{ + int head_size = sizeof(av_render_audio_frame_t); + int size = head_size + data->size; + uint8_t *b = (uint8_t *)data_queue_get_buffer(q, size); + if (b == NULL) { + return -1; + } + memcpy(b, data, head_size); + if (data->size) { + memcpy(b + head_size, data->data, data->size); + } + return data_queue_send_buffer(q, size); +} + +static int put_to_v_render(data_queue_t *q, av_render_video_frame_t *data) +{ + int head_size = sizeof(av_render_video_frame_t); + int size = head_size + data->size; + uint8_t *b = (uint8_t *)data_queue_get_buffer(q, size); + if (b == NULL) { + return -1; + } + memcpy(b, data, head_size); + if (data->size) { + memcpy(b + head_size, data->data, data->size); + } + return data_queue_send_buffer(q, size); +} + +static int read_for_adec(data_queue_t *q, av_render_audio_data_t *data, bool use_pool) +{ + uint8_t *b; + int size; + int ret = data_queue_read_lock(q, (void **)&b, &size); + RETURN_ON_FAIL(ret); + av_render_audio_data_t *r = (av_render_audio_data_t *)b; + if (use_pool) { + *data = *r; + return ret; + } + int head_size = sizeof(av_render_audio_data_t); + if (r->size + head_size != size) { + ret = -1; + } else { + *data = *r; + data->data = b + head_size; + } + return ret; +} + +static int read_for_vdec(data_queue_t *q, av_render_video_data_t *data, bool use_pool) +{ + uint8_t *b; + int size; + int ret = data_queue_read_lock(q, (void **)&b, &size); + RETURN_ON_FAIL(ret); + av_render_video_data_t *r = (av_render_video_data_t *)b; + if (use_pool) { + *data = *r; + return ret; + } + int head_size = sizeof(av_render_video_data_t); + if (r->size + head_size != size) { + ret = -1; + } else { + *data = *r; + data->data = b + head_size; + } + return ret; +} + +static int read_for_a_render(data_queue_t *q, av_render_audio_frame_t *data) +{ + uint8_t *b = NULL; + int size; + int ret = data_queue_read_lock(q, (void **)&b, &size); + RETURN_ON_FAIL(ret); + RETURN_ON_NULL(b, ESP_MEDIA_ERR_FAIL); + av_render_audio_frame_t *r = (av_render_audio_frame_t *)b; + int head_size = sizeof(av_render_audio_frame_t); + if (r->size + head_size != size) { + ret = -1; + } else { + *data = *r; + data->data = b + head_size; + } + return ret; +} + +static int read_for_v_render(data_queue_t *q, av_render_video_frame_t *data) +{ + uint8_t *b; + int size; + int ret = data_queue_read_lock(q, (void **)&b, &size); + RETURN_ON_FAIL(ret); + av_render_video_frame_t *r = (av_render_video_frame_t *)b; + if (r->data > b && r->data + r->size == b + size) { + *data = *r; + return ret; + } + int head_size = sizeof(av_render_video_frame_t); + if (r->size + head_size != size) { + ret = -1; + } else { + *data = *r; + data->data = b + head_size; + } + return ret; +} + +static void dump_data(av_render_dump_type_t type, uint8_t *data, int size) +{ + static FILE *fp[AV_RENDER_DUMP_STOP_INDEX]; + static uint8_t dump_count = 0; + if (type == AV_RENDER_DUMP_STOP_INDEX) { + for (int i = 0; i < AV_RENDER_DUMP_STOP_INDEX; i++) { + if (fp[i]) { + fclose(fp[i]); + fp[i] = NULL; + } + } + dump_count++; + if (dump_count > 9) { + dump_count = 0; + } + return; + } + if ((render_dump_mask & (1 << type)) == 0) { + return; + } + if (size == 0) { + return; + } + if (fp[type] == NULL) { + char *pre_name[] = { "ad", "ar", "vd", "vr" }; + char file_name[20]; + snprintf(file_name, sizeof(file_name), "/sdcard/%s%d.bin", pre_name[type], dump_count); + fp[type] = fopen(file_name, "wb"); + if (fp[type]) { + ESP_LOGI(TAG, "dump to %s", file_name); + } + } + if (fp[type]) { + fwrite(data, size, 1, fp[type]); + } +} + +static int decode_audio(av_render_adec_res_t *adec_res, av_render_audio_data_t *data) +{ + int ret = 0; + if (data->size || data->eos) { + dump_data(AV_RENDER_DUMP_ADEC_DATA, data->data, data->size); + ret = adec_decode(adec_res->adec, data); + if (ret != 0) { + av_render_t *render = adec_res->thread_res.render; + adec_res->audio_err_cnt++; + if (adec_res->audio_err_cnt == AUDIO_ERR_FRAME_TOLERANCE) { + adec_res->audio_err_cnt++; + if (render->event_cb) { + render->event_cb(AV_RENDER_EVENT_AUDIO_DECODE_ERR, render->event_ctx); + } + } + ESP_LOGE(TAG, "Fail to decode audio data ret %d size:%" PRIu32, ret, data->size); + } else { + adec_res->audio_err_cnt = 0; + } + } + return ret; +} + +static int adec_body(av_render_thread_res_t *res, bool drop) +{ + av_render_audio_data_t data; + av_render_adec_res_t *adec_res = (av_render_adec_res_t *)res; + int ret = read_for_adec(res->data_q, &data, res->use_pool); + RETURN_ON_FAIL(ret); + // EOS data may not contain size + if (drop == false) { + ret = decode_audio(adec_res, &data); + } + if (data.data && res->use_pool) { + res->render->pool_free(data.data, res->render->pool); + } + data_queue_read_unlock(res->data_q); + if (data.eos) { + ESP_LOGI(TAG, "Decode finished"); + if (res->render->cfg.quit_when_eos == false) { + res->paused = true; + } else { + return 1; + } + } + return 0; +} + +static int get_video_sync_time(av_render_t *render, av_render_video_res_t *v_render, uint32_t *cur_time, + uint32_t *ref_time) +{ + int ret = 0; + *ref_time = get_cur_time() - v_render->video_start_time + v_render->video_start_pts; + if (render->cfg.sync_mode == AV_RENDER_SYNC_FOLLOW_AUDIO) { + ret = av_render_get_audio_pts(render, cur_time); + } else { + *cur_time = *ref_time; + } + return ret; +} + +static int video_sync_control_before_decode(av_render_t *render, uint32_t video_pts, int q_num, bool *skip) +{ + av_render_video_res_t *v_render = render->v_render_res; + if (render->cfg.allow_drop_data == false) { + return 0; + } + // No need sync, return directly + if (render->cfg.sync_mode == AV_RENDER_SYNC_NONE) { + if (q_num >= 2) { + *skip = true; + } + return 0; + } + uint32_t now, ref_time; + int ret = get_video_sync_time(render, v_render, &now, &ref_time); + RETURN_ON_FAIL(ret); + if (v_render->sent_frame_num) { + // Do not skip data until first frame decoded + // TODO currently set 100 as tolerance + if (video_pts + 100 < now) { + printf("D %d < %d\n", (int)video_pts, (int)now); + *skip = true; + } + } + return 0; +} + +static int audio_drop_before_render(av_render_t *render, int size, bool *skip) +{ + if (render->cfg.allow_drop_data == false) { + return 0; + } + av_render_audio_frame_info_t *info = &render->a_render_res->out_frame_info; + // No need sync, return directly + if (render->cfg.sync_mode == AV_RENDER_SYNC_NONE && info->sample_rate) { + int sample_size = info->channel * info->bits_per_sample >> 3; + int latency = size / sample_size * 1000 / info->sample_rate; + if (latency >= 200) { + *skip = true; + } + } + return 0; +} + +static int decode_video(av_render_vdec_res_t *vdec_res, av_render_video_data_t *data) +{ + av_render_t *render = vdec_res->thread_res.render; + if (render->v_render_res->thread_res.thread == NULL) { + // Get frame buffer not support render in separate thread + av_render_frame_buffer_t frame_buffer = { 0 }; + int ret = video_render_get_frame_buffer(render->cfg.video_render, &frame_buffer); + if (ret == 0) { + vdec_set_frame_buffer(vdec_res->vdec, &frame_buffer); + } + } + int ret = 0; + if (data->size || data->eos) { + dump_data(AV_RENDER_DUMP_VDEC_DATA, data->data, data->size); + int ret = vdec_decode(vdec_res->vdec, data); + if (ret != 0) { + vdec_res->video_err_cnt++; + if (vdec_res->video_err_cnt == AUDIO_ERR_FRAME_TOLERANCE) { + vdec_res->video_err_cnt++; + if (render->event_cb) { + render->event_cb(AV_RENDER_EVENT_VIDEO_DECODE_ERR, render->event_ctx); + } + } + ESP_LOGE(TAG, "Fail to decode video data ret %d size:%" PRIu32, ret, data->size); + } else { + vdec_res->video_err_cnt = 0; + } + } + return ret; +} + +static int vdec_body(av_render_thread_res_t *res, bool drop) +{ + av_render_video_data_t data; + av_render_vdec_res_t *vdec_res = (av_render_vdec_res_t *)res; + int ret = read_for_vdec(res->data_q, &data, vdec_res->thread_res.render->pool_free != NULL); + RETURN_ON_FAIL(ret); + int q_num = 0, q_size = 0; + data_queue_query(res->data_q, &q_num, &q_size); + // EOS data may not contain size + if (data.size || data.eos) { + bool skip = false; + video_sync_control_before_decode(res->render, data.pts, q_num, &skip); + if (drop == false && (skip == false || data.eos)) { + decode_video(vdec_res, &data); + } + } + if (data.data && vdec_res->thread_res.use_pool) { + vdec_res->thread_res.render->pool_free(data.data, vdec_res->thread_res.render->pool); + } + data_queue_read_unlock(res->data_q); + if (data.eos) { + ESP_LOGI(TAG, "Decode finished"); + if (res->render->cfg.quit_when_eos == false) { + res->paused = true; + } else { + return 1; + } + } + return 0; +} + +static int video_sync_control_before_render(av_render_t *render, uint32_t video_pts, bool *skip) +{ + av_render_video_res_t *v_render = render->v_render_res; + // No need sync, return directly + if (render->cfg.sync_mode == AV_RENDER_SYNC_NONE) { + return 0; + } + // Decoder sync control only skip data to avoid decoder too slow + uint32_t now, ref_time, audio_pts; + int ret = get_video_sync_time(render, v_render, &audio_pts, &ref_time); + RETURN_ON_FAIL(ret); + now = audio_pts; + int fps = v_render->video_frame_info.fps; + if (fps == 0) { + fps = 20; + } + if (v_render->sent_frame_num) { + uint32_t video_cur_pts = 0; + av_render_get_video_pts(render, &video_cur_pts); + bool in_sync = false; + for (int i = 0; i < 2; i++) { + if (video_cur_pts < now + v_render->sync_tolerance && video_cur_pts + v_render->sync_tolerance > now) { + in_sync = true; + break; + } + if (now == ref_time) { + break; + } + now = ref_time; + continue; + } + if (in_sync) { + if (video_cur_pts > now) { + uint32_t sleep_time = video_cur_pts - now; + uint32_t max_frame_time = 2 * 1000 / fps; + if (sleep_time > max_frame_time) { + sleep_time = max_frame_time; + } + media_lib_thread_sleep(sleep_time); + } else { + // TODO need more accurate to control drop threshold + uint32_t frame_pts = 1000 / fps * 4; + // Only do drop if not drop data before decode + if (video_pts + frame_pts <= now && render->cfg.allow_drop_data == false) { + *skip = true; + } + } + if ((v_render->sent_frame_num % fps) == 0) { + ESP_LOGI(TAG, "Video pts:%d now:%d ref:%d", (int)video_cur_pts, (int)audio_pts, (int)ref_time); + } + } else { + // Lost sync need resync from now + v_render->sent_frame_num = 0; + ESP_LOGE(TAG, "Video lost sync now:%d audio:%d ref:%d", (int)video_cur_pts, (int)audio_pts, (int)ref_time); + } + } + if (v_render->sent_frame_num == 0) { + ESP_LOGI(TAG, "Video start pts set to %" PRIu32, video_pts); + v_render->video_start_time = get_cur_time(); + v_render->video_start_pts = video_pts; + } + v_render->sent_frame_num++; + return 0; +} + +static int _render_write_audio(av_render_thread_res_t *res, av_render_audio_frame_t *audio_frame) +{ + if (res->render->a_render_res->audio_rendered == false) { + if (res->render->event_cb) { + res->render->event_cb(AV_RENDER_EVENT_AUDIO_RENDERED, res->render->event_ctx); + } + if (res->render->cfg.pause_on_first_frame) { + res->paused = true; + } + ESP_LOGI(TAG, "%s Auto pause audio first frame at %" PRIu32, res->name, audio_frame->pts); + res->render->a_render_res->audio_rendered = true; + } + if (res->paused) { + return 0; + } + // Update send pts + res->render->a_render_res->audio_send_pts = audio_frame->pts; + int ret = 0; + if (res->flushing == false) { + ret = audio_render_write(res->render->cfg.audio_render, audio_frame); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to render audio ret %d", ret); + return ret; + } + } + if (audio_frame->eos) { + if (res->render->event_cb) { + res->render->event_cb(AV_RENDER_EVENT_AUDIO_EOS, res->render->event_ctx); + } + } + return ret; +} + +static void _video_check_first_frame_render_notify(av_render_thread_res_t *res, av_render_video_frame_t *video_frame) +{ + if (res->render->v_render_res->video_rendered == false) { + ESP_LOGI(TAG, "%s Auto pause video first frame at %" PRIu32, res->name, video_frame->pts); + if (res->render->event_cb) { + res->render->event_cb(AV_RENDER_EVENT_VIDEO_RENDERED, res->render->event_ctx); + } + if (res->render->cfg.pause_on_first_frame) { + res->paused = true; + if (res->render->v_render_res->v_render_in_sync && res->render->vdec_res) { + // Auto pause decoder if render in sync + res->render->vdec_res->thread_res.paused = true; + } + } + res->render->v_render_res->video_rendered = true; + // Do not return when pause on first frame, so that it can show the picture after seek + } +} + +static int _render_write_video(av_render_thread_res_t *res, av_render_video_frame_t *video_frame) +{ + int ret = 0; + // Only render when PTS exceed start PTS + res->render->v_render_res->video_send_pts = video_frame->pts; + if (video_frame->pts >= res->render->v_render_res->video_start_pts) { + _video_check_first_frame_render_notify(res, video_frame); + // During flush do not show picture + if (res->flushing == false) { + bool skip = false; + if (res->paused && res->render->v_render_res->v_render_in_sync && res->render->vdec_res && res->render->vdec_res->thread_res.paused == false) { + res->paused = false; + } + if (res->paused == false) { + // Do av sync logic + video_sync_control_before_render(res->render, video_frame->pts, &skip); + } + if (1 || skip == false) { + ret = video_render_write(res->render->cfg.video_render, video_frame); + } + if (ret != 0) { + ESP_LOGE(TAG, "Fail to render video ret %d", ret); + } + } + } + if (video_frame->eos) { + _video_check_first_frame_render_notify(res, video_frame); + if (res->render->event_cb) { + res->render->event_cb(AV_RENDER_EVENT_VIDEO_EOS, res->render->event_ctx); + } + } + return ret; +} + +static int a_render_body(av_render_thread_res_t *res, bool drop) +{ + av_render_audio_frame_t data; + int ret = read_for_a_render(res->data_q, &data); + RETURN_ON_FAIL(ret); + bool skip = false; + if (data.size) { + int q_num = 0, q_size = 0; + data_queue_query(res->data_q, &q_num, &q_size); + audio_drop_before_render(res->render, q_size, &skip); + } + if (drop == false && skip == false && (data.size || data.eos)) { + ret = _render_write_audio(res, &data); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to render audio"); + } + } + if (res->paused) { + data_queue_peek_unlock(res->data_q); + } else { + data_queue_read_unlock(res->data_q); + } + return 0; +} + +static int v_render_body(av_render_thread_res_t *res, bool drop) +{ + av_render_video_frame_t data; + int ret = read_for_v_render(res->data_q, &data); + RETURN_ON_FAIL(ret); + if (drop == false && (data.size || data.eos)) { + av_render_vdec_res_t *vdec_res = res->render->vdec_res; + if (vdec_res && vdec_res->vid_convert) { + // Do color convert firstly + ret = convert_color(vdec_res->vid_convert, + data.data, data.size, + vdec_res->vid_convert_out, vdec_res->vid_convert_out_size); + data.data = vdec_res->vid_convert_out; + data.size = vdec_res->vid_convert_out_size; + } + ret = _render_write_video(res, &data); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to render video"); + } + } + if (res->paused) { + data_queue_peek_unlock(res->data_q); + } else { + data_queue_read_unlock(res->data_q); + } + return 0; +} + +static int create_thread_res(av_render_thread_res_t *res, const char *name, + int (*body)(av_render_thread_res_t *res, bool drop), + int buffer_size, int wait_bits) +{ + do { + if (res->msg_q == NULL) { + res->msg_q = msg_q_create(4, sizeof(av_render_msg_t)); + } + if (res->msg_q == NULL) { + break; + } + res->name = name; + if (res->data_q == NULL) { + res->data_q = data_queue_init(buffer_size); + } + if (res->data_q == NULL) { + break; + } + res->wait_bits = wait_bits; + res->render_body = body; + int ret = media_lib_thread_create_from_scheduler(&res->thread, name, render_thread, res); + BREAK_ON_FAIL(ret); + return 0; + } while (0); + return -1; +} + +static void destroy_thread_res(av_render_thread_res_t *res) +{ + if (res->msg_q) { + msg_q_destroy(res->msg_q); + res->msg_q = NULL; + } + if (res->data_q) { + data_queue_deinit(res->data_q); + res->data_q = NULL; + } + // Thread will quit automatically +} + +static int send_msg_to_thread(av_render_thread_res_t *res, int head_size, av_render_msg_t *msg) +{ + int ret = -1; + if (res->msg_q) { + ret = msg_q_send(res->msg_q, msg, sizeof(av_render_msg_t)); + } + // Try to wakeup wait data_queue when fifo enough + if (res->data_q) { + int q_num = 0, q_size = 0; + data_queue_query(res->data_q, &q_num, &q_size); + if (q_num == 0) { + uint8_t *b = (uint8_t *)data_queue_get_buffer(res->data_q, head_size); + if (b) { + memset(b, 0, head_size); + data_queue_send_buffer(res->data_q, head_size); + } + } + } + return ret; +} + +static const char *msg_to_str(av_render_msg_type_t msg) +{ + switch (msg) { + case AV_RENDER_MSG_CLOSE: + return "Close"; + case AV_RENDER_MSG_PAUSE: + return "Pause"; + case AV_RENDER_MSG_RESUME: + return "Resume"; + case AV_RENDER_MSG_FLUSH: + return "Flush"; + default: + return ""; + } +} + +static void render_consume_all(av_render_thread_res_t *res) +{ + if (res->use_pool == false) { + data_queue_consume_all(res->data_q); + } else { + while (data_queue_have_data(res->data_q)) { + res->render_body(res, true); + } + } +} + +static void render_thread(void *arg) +{ + av_render_thread_res_t *res = (av_render_thread_res_t *)arg; + while (1) { + av_render_msg_t msg = { 0 }; + msg_q_recv(res->msg_q, &msg, sizeof(av_render_msg_t), !res->paused); + if (msg.type != AV_RENDER_MSG_NONE) { + ESP_LOGI(TAG, "%s got msg:%s", res->name, msg_to_str(msg.type)); + } + if (msg.type == AV_RENDER_MSG_CLOSE) { + break; + } + if (msg.type == AV_RENDER_MSG_FLUSH) { + render_consume_all(res); + _SET_BITS(res->render->event_group, res->wait_bits << FLUSH_SHIFT_BITS); + res->flushing = false; + } + if (msg.type == AV_RENDER_MSG_PAUSE) { + res->paused = true; + continue; + } + if (msg.type == AV_RENDER_MSG_RESUME) { + ESP_LOGI(TAG, "%s resumed", res->name); + res->paused = false; + } + if (res->paused) { + continue; + } + if (res->render_body) { + int ret = res->render_body(res, false); + if (ret != 0) { + ESP_LOGE(TAG, "Thread %s process fail %d", res->name, ret); + break; + } + } + } + res->thread = NULL; + ESP_LOGI(TAG, "Thread %s exited", res->name); + // Wakeup wait thread + // Consume all data + render_consume_all(res); + _SET_BITS(res->render->event_group, res->wait_bits); + media_lib_thread_destroy(NULL); +} + +static bool audio_need_decode_in_sync(av_render_t *render, av_render_audio_info_t *audio_info) +{ + if (audio_info->codec == AV_RENDER_AUDIO_CODEC_PCM || render->cfg.audio_raw_fifo_size == 0) { + return true; + } + return false; +} + +static bool video_is_raw(av_render_video_info_t *video_info) +{ + if (video_info->codec == AV_RENDER_VIDEO_CODEC_YUV420 || video_info->codec == AV_RENDER_VIDEO_CODEC_YUV422 || video_info->codec == AV_RENDER_VIDEO_CODEC_RGB565) { + return true; + } + return false; +} + +static bool video_need_decode_in_sync(av_render_t *render, av_render_video_info_t *video_info) +{ + if (video_is_raw(video_info) || render->cfg.video_raw_fifo_size == 0) { + return true; + } + return false; +} + +static bool audio_need_render_in_sync(av_render_t *render) +{ + if (render->cfg.audio_render_fifo_size == 0) { + return true; + } + return false; +} + +static bool audio_need_resample(av_render_audio_res_t *a_render) +{ + if (0 != memcmp(&a_render->out_frame_info, &a_render->audio_frame_info, sizeof(av_render_audio_frame_info_t))) { + return true; + } + return false; +} + +static bool video_need_render_in_sync(av_render_t *render) +{ + if (render->cfg.video_render_fifo_size == 0) { + return true; + } + return false; +} + +int audio_render_frame_reached(av_render_audio_frame_t *frame, void *ctx) +{ + av_render_audio_res_t *a_render = (av_render_audio_res_t *)ctx; + int ret = -1; + dump_data(AV_RENDER_DUMP_ARENDER_DATA, frame->data, frame->size); + if (a_render->audio_packet_reached) { + // Write to audio render queue or write to audio render directly + if (a_render->thread_res.thread) { + ret = put_to_a_render(a_render->thread_res.data_q, frame); + } else { + ret = _render_write_audio(&a_render->thread_res, frame); + } + } + return ret; +} + +static int av_render_audio_frame_reached(av_render_audio_frame_t *frame, void *ctx) +{ + av_render_t *render = (av_render_t *)ctx; + av_render_audio_res_t *a_render = render->a_render_res; + if (a_render == NULL) { + return 0; + } + av_render_adec_res_t *adec_res = render->adec_res; + int ret = 0; + // Open audio render when first packet reached + if (a_render->audio_packet_reached == false) { + if (a_render->audio_is_pcm == false) { + ret = -1; + // Try to correct frame information from codec + if (adec_res && adec_res->adec) { + ret = adec_get_frame_info(adec_res->adec, &a_render->audio_frame_info); + } + if (ret != 0) { + ESP_LOGE(TAG, "Fail to get audio frame information"); + return ret; + } + } + // Reopen audio render using new frame information + audio_render_close(render->cfg.audio_render); + ESP_LOGI(TAG, "Get need resample %d in:%d out:%d", a_render->need_resample, + (int)a_render->audio_frame_info.sample_rate, (int)a_render->out_frame_info.sample_rate); + if (a_render->need_resample && audio_need_resample(a_render)) { + ret = audio_render_open(render->cfg.audio_render, &a_render->out_frame_info); + audio_resample_cfg_t resample_cfg = { + .input_info = a_render->audio_frame_info, + .output_info = a_render->out_frame_info, + .resample_cb = audio_render_frame_reached, + .ctx = a_render, + }; + a_render->resample_handle = audio_resample_open(&resample_cfg); + if (a_render->resample_handle == NULL) { + ESP_LOGE(TAG, "Fail to create audio resample"); + ret = -1; + } + } else { + if (a_render->resample_handle) { + audio_resample_close(a_render->resample_handle); + a_render->resample_handle = NULL; + } + ret = audio_render_open(render->cfg.audio_render, &a_render->audio_frame_info); + } + if (ret != 0) { + ESP_LOGE(TAG, "Fail to create audio render"); + return ret; + } + a_render->audio_packet_reached = true; + a_render->a_render_in_sync = true; + a_render->thread_res.render = render; + if (audio_need_render_in_sync(render) == false && a_render->thread_res.thread == NULL) { + ret = create_thread_res(&a_render->thread_res, "ARender", a_render_body, render->cfg.audio_render_fifo_size, + A_RENDER_CLOSED_BITS); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to create audio render thread resource"); + } else { + a_render->a_render_in_sync = true; + } + } + } + if (a_render->resample_handle) { + // write to resample + ret = audio_resample_write(a_render->resample_handle, frame); + } else { + ret = audio_render_frame_reached(frame, a_render); + } + return ret; +} + +static void convert_to_audio_frame(av_render_audio_info_t *audio_info, av_render_audio_frame_info_t *frame_info) +{ + frame_info->bits_per_sample = audio_info->bits_per_sample; + frame_info->channel = audio_info->channel; + frame_info->sample_rate = audio_info->sample_rate; +} + +static uint8_t *av_render_fetch_vid_fb(int align, int size, void *ctx) +{ + av_render_t *render = (av_render_t *)ctx; + av_render_video_res_t *v_render = render->v_render_res; + if (v_render == NULL || size == 0) { + return NULL; + } + av_render_vdec_res_t *vdec_res = render->vdec_res; + size = sizeof(av_render_video_frame_t) + size + align; + uint8_t *b = (uint8_t *)data_queue_get_buffer(v_render->thread_res.data_q, size); + if (b == NULL) { + return NULL; + } + vdec_res->fb_frame = (av_render_video_frame_t *)b; + b += sizeof(av_render_video_frame_t); + align -= 1; + vdec_res->fb_frame->data = (uint8_t *)(((uint32_t)b + align) & (~align)); + vdec_res->fb_frame->size = 0; + return vdec_res->fb_frame->data; +} + +static int av_render_release_vid_fb(uint8_t *addr, bool drop, void *ctx) +{ + av_render_t *render = (av_render_t *)ctx; + av_render_video_res_t *v_render = render->v_render_res; + if (v_render == NULL) { + return 0; + } + av_render_vdec_res_t *vdec_res = render->vdec_res; + if (vdec_res->fb_frame == NULL || addr != vdec_res->fb_frame->data) { + ESP_LOGE(TAG, "Release wrong data"); + } + uint32_t size = 0; + if (drop == false) { + size = vdec_res->fb_frame->size + (uint32_t)(addr - (uint8_t *)vdec_res->fb_frame); + } + return data_queue_send_buffer(v_render->thread_res.data_q, size); +} + +static int av_render_video_frame_reached(av_render_video_frame_t *frame, void *ctx) +{ + av_render_t *render = (av_render_t *)ctx; + av_render_video_res_t *v_render = render->v_render_res; + if (v_render == NULL) { + return 0; + } + av_render_vdec_res_t *vdec_res = render->vdec_res; + int ret = 0; + dump_data(AV_RENDER_DUMP_VRENDER_DATA, frame->data, frame->size); + // Open video render when first packet reached + if (v_render->video_packet_reached == false) { + if (v_render->video_is_raw == false) { + ret = -1; + // Try to correct frame information from codec + int old_fps = v_render->video_frame_info.fps; + if (vdec_res && vdec_res->vdec) { + ret = vdec_get_frame_info(vdec_res->vdec, &v_render->video_frame_info); + } + if (ret != 0) { + ESP_LOGE(TAG, "Fail to get video frame information"); + return ret; + } + if (vdec_res->dec_out_fmt != vdec_res->out_fmt) { + color_convert_cfg_t convert_cfg = { + .from = vdec_res->dec_out_fmt, + .to = vdec_res->out_fmt, + .width = v_render->video_frame_info.width, + .height = v_render->video_frame_info.height, + }; + vdec_res->vid_convert = init_convert_table(&convert_cfg); + if (vdec_res->vid_convert == NULL) { + ESP_LOGE(TAG, "Fail to init video convert"); + return ESP_MEDIA_ERR_NO_MEM; + } + } + if (vdec_res && vdec_res->vid_convert) { + // Delay to malloc video convert output size + int image_size = convert_table_get_image_size(vdec_res->out_fmt, + v_render->video_frame_info.width, + v_render->video_frame_info.height); + uint8_t* vid_cvt_out = media_lib_realloc(vdec_res->vid_convert_out, image_size); + if (vid_cvt_out == NULL) { + ESP_LOGE(TAG, "Fail to allocate video convert output"); + return ESP_MEDIA_ERR_NO_MEM; + } + vdec_res->vid_convert_out = vid_cvt_out; + vdec_res->vid_convert_out_size = image_size; + v_render->video_frame_info.type = vdec_res->out_fmt; + } + if (v_render->video_frame_info.fps == 0) { + v_render->video_frame_info.fps = old_fps; + } + } + // Reopen video render using new frame information + video_render_close(render->cfg.video_render); + ret = video_render_open(render->cfg.video_render, &v_render->video_frame_info); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to create video render"); + return ret; + } + v_render->video_packet_reached = true; + v_render->v_render_in_sync = true; + // Tolerance for 3 frame later + v_render->sync_tolerance = 600; + v_render->thread_res.render = render; + if (v_render->use_fb == false && video_need_render_in_sync(render) == false && v_render->thread_res.thread == NULL) { + ret = create_thread_res(&v_render->thread_res, "VRender", v_render_body, render->cfg.video_render_fifo_size, + V_RENDER_CLOSED_BITS); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to create video render thread resource"); + } else { + v_render->v_render_in_sync = false; + } + } + v_render->thread_res.name = "VRender"; + } + if (v_render->video_packet_reached) { + // Write to video render queue or write to video render directly + if (v_render->thread_res.thread) { + if (v_render->use_fb) { + // Update frame information only + if (vdec_res->fb_frame) { + uint8_t *frame_data = vdec_res->fb_frame->data; + memcpy(vdec_res->fb_frame, frame, sizeof(av_render_video_frame_t)); + vdec_res->fb_frame->data = frame_data; + } + } else { + ret = put_to_v_render(v_render->thread_res.data_q, frame); + } + } else { + av_render_vdec_res_t *vdec_res = render->vdec_res; + if (vdec_res && vdec_res->vid_convert) { + // Do color convert firstly + ret = convert_color(vdec_res->vid_convert, + frame->data, frame->size, + vdec_res->vid_convert_out, vdec_res->vid_convert_out_size); + frame->data = vdec_res->vid_convert_out; + frame->size = vdec_res->vid_convert_out_size; + } + ret = _render_write_video(&v_render->thread_res, frame); + } + } + return ret; +} + +static void convert_to_video_frame(av_render_video_info_t *video_info, av_render_video_frame_info_t *frame_info) +{ + memset(frame_info, 0, sizeof(av_render_video_frame_info_t)); + switch (video_info->codec) { + case AV_RENDER_VIDEO_CODEC_YUV420: + frame_info->type = AV_RENDER_VIDEO_RAW_TYPE_YUV420; + break; + case AV_RENDER_VIDEO_CODEC_YUV422: + frame_info->type = AV_RENDER_VIDEO_RAW_TYPE_YUV422; + break; + case AV_RENDER_VIDEO_CODEC_RGB565: + frame_info->type = AV_RENDER_VIDEO_RAW_TYPE_RGB565; + break; + default: + break; + } + frame_info->width = video_info->width; + frame_info->height = video_info->height; + frame_info->fps = video_info->fps; +} + +static void correct_video_fps(av_render_video_res_t *v_render, uint32_t pts) +{ + // Considering minimum fps is 10 for file + if (v_render->video_last_pts && pts > v_render->video_last_pts && pts <= v_render->video_last_pts + 100) { + v_render->video_frame_info.fps = 1000 / (pts - v_render->video_last_pts); + ESP_LOGI(TAG, "Correct video fps to %d", v_render->video_frame_info.fps); + } else { + v_render->video_last_pts = pts; + } +} + +av_render_handle_t av_render_open(av_render_cfg_t *cfg) +{ + if (cfg == NULL || (cfg->audio_render == NULL && cfg->video_render == NULL)) { + ESP_LOGE(TAG, "Arg wrong %p %p\n", cfg, cfg ? cfg->audio_render : NULL); + return NULL; + } + av_render_t *render = (av_render_t *)calloc(1, sizeof(av_render_t)); + if (render == NULL) { + return NULL; + } + // Copy configuration + render->cfg = *cfg; + do { + int ret = media_lib_mutex_create(&render->api_lock); + BREAK_ON_FAIL(ret); + ret = media_lib_event_group_create(&render->event_group); + BREAK_ON_FAIL(ret); + return render; + } while (0); + av_render_close(render); + return NULL; +} + +int av_render_config_audio_fifo(av_render_handle_t h, av_render_fifo_cfg_t *fifo_cfg) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || fifo_cfg == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + render->cfg.audio_raw_fifo_size = fifo_cfg->raw_fifo_size; + render->cfg.audio_render_fifo_size = fifo_cfg->render_fifo_size; + media_lib_mutex_unlock(render->api_lock); + return ESP_MEDIA_ERR_OK; +} + +int av_render_config_video_fifo(av_render_handle_t h, av_render_fifo_cfg_t *fifo_cfg) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || fifo_cfg == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + render->cfg.video_raw_fifo_size = fifo_cfg->raw_fifo_size; + render->cfg.video_render_fifo_size = fifo_cfg->render_fifo_size; + media_lib_mutex_unlock(render->api_lock); + return ESP_MEDIA_ERR_OK; +} + +int av_render_add_audio_stream(av_render_handle_t h, av_render_audio_info_t *audio_info) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || audio_info == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = 0; + do { + if (render->cfg.audio_render == NULL) { + ESP_LOGE(TAG, "Audio render not set, stream is skipped"); + ret = ESP_MEDIA_ERR_NOT_SUPPORT; + break; + } + // Create audio render resource + if (render->a_render_res == NULL) { + render->a_render_res = (av_render_audio_res_t *)media_lib_calloc(1, sizeof(av_render_audio_res_t)); + if (render->a_render_res == NULL) { + ret = ESP_MEDIA_ERR_NO_MEM; + break; + } + } + if (render->aud_fix_info.sample_rate) { + memcpy(&render->a_render_res->out_frame_info, &render->aud_fix_info, sizeof(av_render_audio_frame_info_t)); + render->a_render_res->need_resample = true; + } + av_render_audio_res_t *a_render = render->a_render_res; + // Close old decoder + if (render->adec_res) { + av_render_adec_res_t *adec_res = render->adec_res; + if (adec_res->thread_res.thread) { + av_render_msg_t msg = { + .type = AV_RENDER_MSG_CLOSE, + }; + ret = send_msg_to_thread(&adec_res->thread_res, sizeof(av_render_audio_data_t), &msg); + _WAIT_BITS(render->event_group, adec_res->thread_res.wait_bits); + } + adec_close(adec_res->adec); + adec_res->adec = NULL; + } + // Clear frame number + a_render->audio_packet_reached = false; + a_render->audio_rendered = false; + // Create new decoder if needed + a_render->audio_is_pcm = (audio_info->codec == AV_RENDER_AUDIO_CODEC_PCM); + + a_render->decode_in_sync = true; + + if (a_render->audio_is_pcm == false) { + if (render->adec_res == NULL) { + // Create audio decoder resource + render->adec_res = (av_render_adec_res_t *)media_lib_calloc(1, sizeof(av_render_adec_res_t)); + if (render->adec_res == NULL) { + ret = ESP_MEDIA_ERR_NO_MEM; + break; + } + } + av_render_adec_res_t *adec_res = render->adec_res; + adec_cfg_t cfg = { + .audio_info = *audio_info, + .frame_cb = av_render_audio_frame_reached, + .ctx = render, + }; + adec_res->adec = adec_open(&cfg); + if (adec_res->adec == NULL) { + ESP_LOGE(TAG, "Fail to create audio decoder"); + ret = ESP_MEDIA_ERR_FAIL; + break; + } + adec_res->thread_res.render = render; + adec_res->thread_res.use_pool = (render->pool_free != NULL); + // Create thread for audio decoder + if (audio_need_decode_in_sync(render, audio_info) == false) { + ret = create_thread_res(&adec_res->thread_res, "Adec", adec_body, render->cfg.audio_raw_fifo_size, + ADEC_CLOSED_BITS); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to create thread for ADec"); + ret = ESP_MEDIA_ERR_FAIL; + break; + } + // Clear decode in sync flag + a_render->decode_in_sync = false; + } + } else { + // Save to stream info + convert_to_audio_frame(audio_info, &a_render->audio_frame_info); + ESP_LOGI(TAG, "Save pcm frame information"); + } + } while (0); + media_lib_mutex_unlock(render->api_lock); + if (ret != ESP_MEDIA_ERR_OK) { + ESP_LOGE(TAG, "Fail to add video stream %d", ret); + } + return ret; +} + +int av_render_set_event_cb(av_render_handle_t h, av_render_event_cb cb, void *ctx) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || cb == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = ESP_MEDIA_ERR_WRONG_STATE; + render->event_ctx = ctx; + render->event_cb = cb; + media_lib_mutex_unlock(render->api_lock); + return ret; +} + +int av_render_set_fixed_frame_info(av_render_handle_t h, av_render_audio_frame_info_t *frame_info) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || frame_info == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = ESP_MEDIA_ERR_WRONG_STATE; + render->aud_fix_info = *frame_info; + media_lib_mutex_unlock(render->api_lock); + return ret; +} + +static bool get_support_output_format(av_render_t *render, av_render_video_info_t *video_info, vdec_cfg_t *cfg) +{ + av_render_video_frame_type_t out_type; + av_render_video_frame_type_t out_fmts[6]; + av_render_vdec_res_t *vdec_res = render->vdec_res; + uint8_t num = sizeof(out_fmts)/sizeof(out_fmts[0]); + vdec_get_output_formats(video_info->codec, out_fmts, &num); + // Try to match decoder supported formats + for (int i = 0; i < num; i++) { + if (video_render_format_supported(render->cfg.video_render, out_fmts[i])) { + ESP_LOGI(TAG, "Set video decoder prefer output format %d", out_fmts[i]); + cfg->out_type = out_fmts[i]; + vdec_res->out_fmt = out_fmts[i]; + vdec_res->dec_out_fmt = out_fmts[i]; + return true; + } + } + for (out_type = AV_RENDER_VIDEO_RAW_TYPE_NONE + 1; out_type < AV_RENDER_VIDEO_RAW_TYPE_MAX; out_type++) { + if (video_render_format_supported(render->cfg.video_render, out_type) == false) { + continue; + } + ESP_LOGI(TAG, "Set video render output format %d", out_type); + // Let decoder do color convert + if (render->cfg.video_cvt_in_render == false) { + cfg->out_type = out_type; + vdec_res->out_fmt = out_type; + vdec_res->dec_out_fmt = out_type; + return true; + } + // User codec prefer one + cfg->out_type = out_fmts[0]; + vdec_res->out_fmt = out_type; + vdec_res->dec_out_fmt = out_fmts[0]; + return true; + } + return false; +} + +int av_render_add_video_stream(av_render_handle_t h, av_render_video_info_t *video_info) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || video_info == NULL) { + return -ESP_MEDIA_ERR_INVALID_ARG; + } + int ret = 0; + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + do { + if (render->cfg.video_render == NULL) { + ESP_LOGE(TAG, "Video render not set, stream is skipped"); + ret = ESP_MEDIA_ERR_NOT_SUPPORT; + break; + } + // Create video render resource + if (render->v_render_res == NULL) { + render->v_render_res = (av_render_video_res_t *)media_lib_calloc(1, sizeof(av_render_video_res_t)); + if (render->v_render_res == NULL) { + ret = ESP_MEDIA_ERR_NO_MEM; + break; + } + } + av_render_video_res_t *v_render = render->v_render_res; + // TODO here force to use fetch decode output data + v_render->use_fb = true; + // Close old decoder + if (render->vdec_res) { + av_render_vdec_res_t *vdec_res = render->vdec_res; + if (vdec_res->thread_res.thread) { + av_render_msg_t msg = { + .type = AV_RENDER_MSG_CLOSE, + }; + ret = send_msg_to_thread(&vdec_res->thread_res, sizeof(av_render_video_data_t), &msg); + if (ret == 0) { + _WAIT_BITS(render->event_group, vdec_res->thread_res.wait_bits); + } + } + vdec_close(vdec_res->vdec); + vdec_res->vdec = NULL; + if (vdec_res->vid_convert) { + deinit_convert_table(vdec_res->vid_convert); + vdec_res->vid_convert = NULL; + } + } + // Clear frame number + v_render->video_packet_reached = false; + v_render->video_rendered = false; + // Create new decoder if needed + v_render->video_is_raw = video_is_raw(video_info); + + // Set initial decoder in sync flag + v_render->decode_in_sync = true; + + if (v_render->video_is_raw == false) { + if (render->vdec_res == NULL) { + // Create audio decoder resource + render->vdec_res = (av_render_vdec_res_t *)media_lib_calloc(1, sizeof(av_render_vdec_res_t)); + if (render->vdec_res == NULL) { + ret = ESP_MEDIA_ERR_NO_MEM; + break; + } + } + av_render_vdec_res_t *vdec_res = render->vdec_res; + vdec_cfg_t cfg = { + .video_info = *video_info, + .frame_cb = av_render_video_frame_reached, + .ctx = render, + }; + if (get_support_output_format(render, video_info, &cfg) == false) { + break; + } + vdec_res->vdec = vdec_open(&cfg); + if (vdec_res->vdec == NULL) { + ESP_LOGE(TAG, "Fail to create video decoder"); + ret = ESP_MEDIA_ERR_FAIL; + break; + } + vdec_res->thread_res.render = render; + vdec_res->thread_res.use_pool = (render->pool_free != NULL); + v_render->thread_res.render = render; + // When use FB pre create render resource + if (v_render->use_fb && video_need_render_in_sync(render) == false && v_render->thread_res.thread == NULL) { + ret = create_thread_res(&v_render->thread_res, "VRender", v_render_body, render->cfg.video_render_fifo_size, + V_RENDER_CLOSED_BITS); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to create video render thread resource"); + } else { + v_render->v_render_in_sync = false; + vdec_fb_cb_cfg_t vdec_cfg = { + .fb_fetch = av_render_fetch_vid_fb, + .fb_return = av_render_release_vid_fb, + .ctx = render, + }; + vdec_set_fb_cb(vdec_res->vdec, &vdec_cfg); + } + } else { + v_render->use_fb = false; + } + // Create thread for audio decoder + if (video_need_decode_in_sync(render, video_info) == false) { + ret = create_thread_res(&vdec_res->thread_res, "Vdec", vdec_body, render->cfg.video_raw_fifo_size, + VDEC_CLOSED_BITS); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to create thread for VDec"); + break; + } + // Clear decode in sync flag + v_render->decode_in_sync = false; + } + } else { + // Convert to frame info + convert_to_video_frame(video_info, &v_render->video_frame_info); + } + } while (0); + media_lib_mutex_unlock(render->api_lock); + if (ret != ESP_MEDIA_ERR_OK) { + ESP_LOGE(TAG, "Fail to add video stream %d", ret); + } + return ret; +} + +int av_render_use_data_pool(av_render_handle_t h, av_render_pool_data_free free, void *pool) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || free == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + render->pool_free = free; + render->pool = pool; + return 0; +} + +int av_render_add_audio_data(av_render_handle_t h, av_render_audio_data_t *audio_data) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || audio_data == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = 0; + do { + av_render_audio_res_t *a_render = render->a_render_res; + if (a_render == NULL) { + ret = ESP_MEDIA_ERR_WRONG_STATE; + break; + } + // If no need decode, notify raw data reached directly + if (a_render->audio_is_pcm) { + av_render_audio_frame_t audio_frame = { + .pts = audio_data->pts, + .data = audio_data->data, + .size = audio_data->size, + .eos = audio_data->eos, + }; + ret = av_render_audio_frame_reached(&audio_frame, render); + break; + } + if (render->adec_res->adec == NULL) { + ret = ESP_MEDIA_ERR_WRONG_STATE; + break; + } + av_render_adec_res_t *adec = render->adec_res; + // If decode async send to decode queue + if (adec->thread_res.thread) { + media_lib_mutex_unlock(render->api_lock); + ret = put_to_adec(adec->thread_res.data_q, audio_data, adec->thread_res.use_pool); + if (ret != 0) { + if (render->pool_free && audio_data->data) { + render->pool_free(audio_data->data, render->pool); + } + } + return ret; + } else { + ret = decode_audio(adec, audio_data); + } + } while (0); + if (render->pool_free && audio_data->data) { + render->pool_free(audio_data->data, render->pool); + } + media_lib_mutex_unlock(render->api_lock); + return ret; +} + +int av_render_add_video_data(av_render_handle_t h, av_render_video_data_t *video_data) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || video_data == NULL) { + return -1; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + av_render_video_res_t *v_render = render->v_render_res; + int ret = 0; + do { + if (v_render == NULL) { + ret = ESP_MEDIA_ERR_WRONG_STATE; + break; + } + if (v_render->video_frame_info.fps == 0) { + correct_video_fps(v_render, video_data->pts); + } + // If no need decode, notify raw data reached directly + if (v_render->video_is_raw) { + av_render_video_frame_t video_frame = { + .pts = video_data->pts, + .data = video_data->data, + .size = video_data->size, + .eos = video_data->eos, + }; + ret = av_render_video_frame_reached(&video_frame, render); + break; + } + if (render->vdec_res->vdec == NULL) { + ret = ESP_MEDIA_ERR_WRONG_STATE; + break; + } + av_render_vdec_res_t *vdec = render->vdec_res; + // If decode async send to decode queue + if (vdec->thread_res.thread) { + media_lib_mutex_unlock(render->api_lock); + ret = put_to_vdec(vdec->thread_res.data_q, video_data, vdec->thread_res.use_pool); + if (ret != 0) { + if (render->pool_free && video_data->data) { + render->pool_free(video_data->data, render->pool); + } + } + return ret; + } else { + ret = decode_video(vdec, video_data); + } + } while (0); + if (render->pool_free && video_data->data) { + render->pool_free(video_data->data, render->pool); + } + media_lib_mutex_unlock(render->api_lock); + return ret; +} + +bool av_render_audio_fifo_enough(av_render_handle_t h, av_render_audio_data_t *audio_data) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || audio_data == NULL) { + return false; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + av_render_audio_res_t *a_render = render->a_render_res; + int need_size = sizeof(av_render_audio_data_t) + audio_data->size; + bool enough = false; + do { + if (a_render == NULL) { + break; + } + if (render->adec_res && render->adec_res->thread_res.data_q) { + if (render->adec_res->thread_res.use_pool) { + need_size -= audio_data->size; + } + int avail = data_queue_get_available(render->adec_res->thread_res.data_q); + enough = (need_size <= avail); + break; + } + if (a_render->thread_res.data_q) { + int avail = data_queue_get_available(a_render->thread_res.data_q); + enough = (need_size <= avail); + break; + } + // Data directly write to render + enough = true; + } while (0); + media_lib_mutex_unlock(render->api_lock); + return enough; +} + +bool av_render_video_fifo_enough(av_render_handle_t h, av_render_video_data_t *video_data) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || video_data == NULL) { + return false; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + av_render_video_res_t *v_render = render->v_render_res; + int need_size = sizeof(av_render_video_data_t) + video_data->size; + bool enough = false; + do { + if (v_render == NULL) { + break; + } + if (render->vdec_res && render->vdec_res->thread_res.data_q) { + if (render->vdec_res->thread_res.use_pool) { + need_size -= video_data->size; + } + int avail = data_queue_get_available(render->vdec_res->thread_res.data_q); + enough = (need_size <= avail); + break; + } + if (v_render->thread_res.data_q) { + int avail = data_queue_get_available(v_render->thread_res.data_q); + enough = (need_size <= avail); + break; + } + enough = true; + } while (0); + media_lib_mutex_unlock(render->api_lock); + return enough; +} + +int av_render_get_audio_fifo_level(av_render_handle_t h, av_render_fifo_stat_t *fifo_stat) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL) { + return -1; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + memset(fifo_stat, 0, sizeof(av_render_fifo_stat_t)); + av_render_audio_res_t *a_render = render->a_render_res; + if (a_render) { + if (render->adec_res && render->adec_res->thread_res.data_q) { + data_queue_query(render->adec_res->thread_res.data_q, &fifo_stat->q_num, &fifo_stat->data_size); + } + if (a_render->thread_res.data_q) { + data_queue_query(a_render->thread_res.data_q, &fifo_stat->render_q_num, &fifo_stat->render_data_size); + } + } + media_lib_mutex_unlock(render->api_lock); + return 0; +} + +int av_render_get_video_fifo_level(av_render_handle_t h, av_render_fifo_stat_t *fifo_stat) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || fifo_stat == NULL) { + return -1; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + memset(fifo_stat, 0, sizeof(av_render_fifo_stat_t)); + av_render_video_res_t *v_render = render->v_render_res; + if (v_render) { + if (render->vdec_res && render->vdec_res->thread_res.data_q) { + data_queue_query(render->vdec_res->thread_res.data_q, &fifo_stat->q_num, &fifo_stat->data_size); + } else if (v_render->thread_res.data_q) { + data_queue_query(v_render->thread_res.data_q, &fifo_stat->q_num, &fifo_stat->data_size); + } + } + media_lib_mutex_unlock(render->api_lock); + return 0; +} + +int render_pause(av_render_t *render, bool pause) +{ + av_render_msg_t msg = { + .type = pause ? AV_RENDER_MSG_PAUSE : AV_RENDER_MSG_RESUME, + }; + if (pause) { + // Only pause decoder when pause only render not set or render thread not exists + if (render->cfg.pause_render_only == false || (render->a_render_res && render->a_render_res->thread_res.thread == NULL)) { + if (render->adec_res && render->adec_res->thread_res.thread) { + send_msg_to_thread(&render->adec_res->thread_res, sizeof(av_render_audio_data_t), &msg); + } + } + if (render->cfg.pause_render_only == false || (render->v_render_res && render->v_render_res->thread_res.thread == NULL)) { + if (render->vdec_res && render->vdec_res->thread_res.thread) { + send_msg_to_thread(&render->vdec_res->thread_res, sizeof(av_render_video_data_t), &msg); + } + } + if (render->a_render_res && render->a_render_res->thread_res.thread) { + send_msg_to_thread(&render->a_render_res->thread_res, sizeof(av_render_audio_frame_t), &msg); + } + if (render->v_render_res && render->v_render_res->thread_res.thread) { + send_msg_to_thread(&render->v_render_res->thread_res, sizeof(av_render_video_frame_t), &msg); + } + } else { + if (render->a_render_res && render->a_render_res->thread_res.thread) { + send_msg_to_thread(&render->a_render_res->thread_res, sizeof(av_render_audio_frame_t), &msg); + } + if (render->v_render_res && render->v_render_res->thread_res.thread) { + send_msg_to_thread(&render->v_render_res->thread_res, sizeof(av_render_video_frame_t), &msg); + } + if (render->adec_res && render->adec_res->thread_res.thread) { + send_msg_to_thread(&render->adec_res->thread_res, sizeof(av_render_audio_data_t), &msg); + } + if (render->vdec_res && render->vdec_res->thread_res.thread) { + send_msg_to_thread(&render->vdec_res->thread_res, sizeof(av_render_video_data_t), &msg); + } + } + printf("Pause set to %d\n", pause); + return 0; +} + +static int render_flush(av_render_t *render) +{ + av_render_msg_t msg = { + .type = AV_RENDER_MSG_FLUSH, + }; + int wait_bits; + // Consume render data so that decode can output even render is paused + if (render->adec_res && render->adec_res->thread_res.thread) { + render->adec_res->thread_res.flushing = true; + if (render->a_render_res && render->a_render_res->thread_res.data_q) { + render->a_render_res->thread_res.flushing = true; + render_consume_all(&render->a_render_res->thread_res); + } + send_msg_to_thread(&render->adec_res->thread_res, sizeof(av_render_audio_data_t), &msg); + wait_bits = render->adec_res->thread_res.wait_bits << FLUSH_SHIFT_BITS; + _WAIT_BITS(render->event_group, wait_bits); + printf("Wait for %x finished\n", wait_bits); + } + if (render->vdec_res && render->vdec_res->thread_res.thread) { + render->vdec_res->thread_res.flushing = true; + if (render->v_render_res && render->v_render_res->thread_res.data_q) { + render->v_render_res->thread_res.flushing = true; + render_consume_all(&render->v_render_res->thread_res); + } + send_msg_to_thread(&render->vdec_res->thread_res, sizeof(av_render_video_data_t), &msg); + wait_bits = render->vdec_res->thread_res.wait_bits << FLUSH_SHIFT_BITS; + _WAIT_BITS(render->event_group, wait_bits); + printf("Wait for %x finished\n", wait_bits); + } + wait_bits = 0; + if (render->a_render_res && render->a_render_res->thread_res.thread) { + render->a_render_res->thread_res.flushing = true; + send_msg_to_thread(&render->a_render_res->thread_res, sizeof(av_render_audio_frame_t), &msg); + wait_bits = render->a_render_res->thread_res.wait_bits << FLUSH_SHIFT_BITS; + } + if (render->v_render_res && render->v_render_res->thread_res.thread) { + render->v_render_res->thread_res.flushing = true; + send_msg_to_thread(&render->v_render_res->thread_res, sizeof(av_render_video_frame_t), &msg); + wait_bits = render->v_render_res->thread_res.wait_bits << FLUSH_SHIFT_BITS; + } + _WAIT_BITS(render->event_group, wait_bits); + // Resend first frame pts + if (render->v_render_res) { + render->v_render_res->video_rendered = false; + render->v_render_res->sent_frame_num = 0; + } + if (render->a_render_res) { + render->a_render_res->audio_rendered = false; + } + return 0; +} + +int av_render_set_speed(av_render_handle_t h, float speed) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL) { + return -1; + } + if (render->cfg.audio_render == NULL) { + return 0; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = audio_render_set_speed(render->cfg.audio_render, speed); + media_lib_mutex_unlock(render->api_lock); + return ret; +} + +int av_render_pause(av_render_handle_t h, bool pause) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL) { + return -1; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = render_pause(render, pause); + media_lib_mutex_unlock(render->api_lock); + return ret; +} + +int av_render_flush(av_render_handle_t h) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL) { + return -1; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = render_flush(render); + media_lib_mutex_unlock(render->api_lock); + return ret; +} + +int av_render_set_video_start_pts(av_render_handle_t h, uint32_t video_pts) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL) { + return -1; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + if (render && render->v_render_res) { + render->v_render_res->video_start_pts = video_pts; + } + media_lib_mutex_unlock(render->api_lock); + return 0; +} + +static int av_render_get_audio_pts(av_render_t *render, uint32_t *out_pts) +{ + av_render_audio_res_t *a_render = render->a_render_res; + if (a_render && a_render->audio_packet_reached) { + uint32_t audio_pts = a_render->audio_send_pts; + uint32_t latency = 0; + int ret = audio_render_get_latency(render->cfg.audio_render, &latency); + if (ret == 0) { + if (audio_pts >= latency) { + audio_pts -= latency; + } + } + *out_pts = audio_pts; + return 0; + } + return -1; +} + +static int av_render_get_video_pts(av_render_t *render, uint32_t *out_pts) +{ + av_render_video_res_t *v_render = render->v_render_res; + + if (v_render && v_render->video_packet_reached) { + uint32_t video_pts = v_render->video_send_pts; + uint32_t latency = 0; + int ret = video_render_get_latency(render->cfg.video_render, &latency); + if (ret == 0) { + video_pts -= latency; + } + if (v_render->thread_res.data_q) { + int q_num = 0, q_size = 0; + data_queue_query(v_render->thread_res.data_q, &q_num, &q_size); + if (q_num) { + int fps = v_render->video_frame_info.fps; + if (fps == 0) { + fps = 20; + } + int q_pts = q_num * 1000 / fps; + video_pts -= q_pts; + } + } + *out_pts = video_pts; + return 0; + } + return -1; +} + +int av_render_query(av_render_handle_t h) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + if (render->adec_res) { + data_queue_t *q = render->adec_res->thread_res.data_q; + int q_num = 0, q_size = 0; + if (q) { + data_queue_query(q, &q_num, &q_size); + } + ESP_LOGI(TAG, "Audio decoder fifo size %d items %d err:%d", q_size, q_num, render->adec_res->audio_err_cnt); + ESP_LOGI(TAG, "Audio decoder status flashing: %d paused: %d", + render->adec_res->thread_res.flushing, render->adec_res->thread_res.paused); + } + if (render->a_render_res) { + data_queue_t *q = render->a_render_res->thread_res.data_q; + int q_num = 0, q_size = 0; + if (q) { + data_queue_query(q, &q_num, &q_size); + } + ESP_LOGI(TAG, "Audio render fifo size %d items %d", q_size, q_num); + ESP_LOGI(TAG, "Audio render status flashing: %d paused: %d", + render->a_render_res->thread_res.flushing, render->a_render_res->thread_res.paused); + ESP_LOGI(TAG, "Audio render pts %" PRIu32 " use resample %d", + render->a_render_res->audio_send_pts, render->a_render_res->need_resample); + } + if (render->vdec_res) { + data_queue_t *q = render->vdec_res->thread_res.data_q; + int q_num = 0, q_size = 0; + if (q) { + data_queue_query(q, &q_num, &q_size); + } + ESP_LOGI(TAG, "Video decoder fifo size %d items %d err:%d", q_size, q_num, render->vdec_res->video_err_cnt); + ESP_LOGI(TAG, "Video decoder status flashing: %d paused: %d", + render->vdec_res->thread_res.flushing, render->vdec_res->thread_res.paused); + } + if (render->v_render_res) { + data_queue_t *q = render->v_render_res->thread_res.data_q; + int q_num = 0, q_size = 0; + if (q) { + data_queue_query(q, &q_num, &q_size); + } + ESP_LOGI(TAG, "Video render fifo size %d items %d", q_size, q_num); + ESP_LOGI(TAG, "Video render status flashing: %d paused: %d", + render->v_render_res->thread_res.flushing, render->v_render_res->thread_res.paused); + ESP_LOGI(TAG, "Video render pts %" PRIu32, render->v_render_res->video_send_pts); + } + media_lib_mutex_unlock(render->api_lock); + return 0; +} + +void av_render_dump(av_render_handle_t h, uint8_t mask) +{ + render_dump_mask = mask; + ESP_LOGI(TAG, "Dump mask set to %x", mask); + if (mask == 0) { + dump_data(AV_RENDER_DUMP_STOP_INDEX, NULL, 0); + } +} + +int av_render_get_render_pts(av_render_handle_t h, uint32_t *out_pts) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL || out_pts == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + uint32_t pts = 0xFFFFFFFF; + int ret = 0; + av_render_audio_res_t *a_render = render->a_render_res; + av_render_video_res_t *v_render = render->v_render_res; + + if (a_render && a_render->audio_packet_reached) { + uint32_t audio_pts = 0; + ret = av_render_get_audio_pts(render, &audio_pts); + if (ret == 0) { + pts = audio_pts; + } + } + if (v_render && v_render->video_packet_reached) { + uint32_t video_pts; + ret = av_render_get_video_pts(render, &video_pts); + if (ret == 0 && video_pts < pts) { + pts = video_pts; + } + } + media_lib_mutex_unlock(render->api_lock); + *out_pts = pts; + return 0; +} + +int av_render_reset(av_render_handle_t h) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + media_lib_mutex_lock(render->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + // wait thread quit + av_render_msg_t msg = { + .type = AV_RENDER_MSG_CLOSE, + }; + int wait_bits = 0; + // Wait for all thread to quit + if (render->adec_res && render->adec_res->thread_res.thread) { + wait_bits |= render->adec_res->thread_res.wait_bits; + send_msg_to_thread(&render->adec_res->thread_res, sizeof(av_render_audio_data_t), &msg); + } + if (render->vdec_res && render->vdec_res->thread_res.thread) { + wait_bits |= render->vdec_res->thread_res.wait_bits; + send_msg_to_thread(&render->vdec_res->thread_res, sizeof(av_render_video_data_t), &msg); + } + if (render->a_render_res && render->a_render_res->thread_res.thread) { + wait_bits |= render->a_render_res->thread_res.wait_bits; + send_msg_to_thread(&render->a_render_res->thread_res, sizeof(av_render_audio_frame_t), &msg); + } + if (render->v_render_res && render->v_render_res->thread_res.thread) { + wait_bits |= render->v_render_res->thread_res.wait_bits; + send_msg_to_thread(&render->v_render_res->thread_res, sizeof(av_render_video_frame_t), &msg); + } + + if (render->adec_res && render->adec_res->thread_res.thread) { + wait_bits |= render->adec_res->thread_res.wait_bits; + } + if (wait_bits) { + media_lib_event_group_wait_bits(render->event_group, wait_bits, MEDIA_LIB_MAX_LOCK_TIME); + media_lib_event_group_clr_bits(render->event_group, wait_bits); + } + ESP_LOGI(TAG, "Close done"); + dump_data(AV_RENDER_DUMP_STOP_INDEX, NULL, 0); + // Close decoder + if (render->adec_res) { + if (render->adec_res->adec) { + adec_close(render->adec_res->adec); + render->adec_res->adec = NULL; + } + destroy_thread_res(&render->adec_res->thread_res); + media_lib_free(render->adec_res); + render->adec_res = NULL; + } + if (render->vdec_res) { + av_render_vdec_res_t *vdec_res = render->vdec_res; + if (vdec_res->vdec) { + vdec_close(vdec_res->vdec); + vdec_res->vdec = NULL; + } + if (vdec_res->vid_convert) { + deinit_convert_table(vdec_res->vid_convert); + vdec_res->vid_convert = NULL; + } + if (vdec_res->vid_convert_out) { + media_lib_free(vdec_res->vid_convert_out); + vdec_res->vid_convert_out = NULL; + } + destroy_thread_res(&render->vdec_res->thread_res); + media_lib_free(render->vdec_res); + render->vdec_res = NULL; + } + // close render resource + if (render->a_render_res) { + destroy_thread_res(&render->a_render_res->thread_res); + if (render->a_render_res->resample_handle) { + audio_resample_close(render->a_render_res->resample_handle); + render->a_render_res->resample_handle = NULL; + } + media_lib_free(render->a_render_res); + render->a_render_res = NULL; + } + if (render->v_render_res) { + destroy_thread_res(&render->v_render_res->thread_res); + media_lib_free(render->v_render_res); + render->v_render_res = NULL; + } + // Close for render + if (render->cfg.audio_render) { + audio_render_close(render->cfg.audio_render); + } + if (render->cfg.video_render) { + video_render_close(render->cfg.video_render); + } + media_lib_mutex_unlock(render->api_lock); + return 0; +} + +int av_render_close(av_render_handle_t h) +{ + av_render_t *render = (av_render_t *)h; + if (render == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + av_render_reset(h); + if (render->event_group) { + media_lib_event_group_destroy(render->event_group); + } + if (render->api_lock) { + media_lib_mutex_destroy(render->api_lock); + } + media_lib_free(render); + return ESP_MEDIA_ERR_OK; +} diff --git a/components/third_party/av_render/src/color_convert.c b/components/third_party/av_render/src/color_convert.c new file mode 100644 index 0000000..986c82b --- /dev/null +++ b/components/third_party/av_render/src/color_convert.c @@ -0,0 +1,228 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#include +#include "color_convert.h" +#include "esp_log.h" + +#if CONFIG_IDF_TARGET_ESP32P4 +extern void i420_to_rgb565le(uint8_t *in_image, uint8_t *out_image, int16_t width, int16_t height); +#endif + +#define TAG "CLR_CONVERT" + +#define COLOR_LIMIT(a) (a > 255 ? 255 : a < 0 ? 0 \ + : a) + +#define YUV2RO(C, D, E) COLOR_LIMIT((298 * (C) + 409 * (E) + 128) >> 8) +#define YUV2GO(C, D, E) COLOR_LIMIT((298 * (C)-100 * (D)-208 * (E) + 128) >> 8) +#define YUV2BO(C, D, E) COLOR_LIMIT((298 * (C) + 516 * (D) + 128) >> 8) +#define RGB565(r, g, b) (((((r) << 6) | (g)) << 5) | (b)) + +typedef struct { + uint8_t *table; + av_render_video_frame_type_t from; + av_render_video_frame_type_t to; + int width; + int height; +} color_convert_t; + +static int get_table_size(av_render_video_frame_type_t from, av_render_video_frame_type_t to) +{ + if (from == AV_RENDER_VIDEO_RAW_TYPE_YUV420 && to == AV_RENDER_VIDEO_RAW_TYPE_RGB565_BE) { + return 256 * 256 * 2; + } + if (from == AV_RENDER_VIDEO_RAW_TYPE_YUV420 && to == AV_RENDER_VIDEO_RAW_TYPE_RGB565) { + return 256 * 256 * 2; + } + return 0; +} + +static int init_table(color_convert_t *convert) +{ + uint16_t *table16 = (uint16_t *)convert->table; + if (convert->from == AV_RENDER_VIDEO_RAW_TYPE_YUV420 && convert->to == AV_RENDER_VIDEO_RAW_TYPE_RGB565_BE) { + for (int u0 = 0; u0 < 32; u0++) { + for (int v0 = 0; v0 < 32; v0++) { + for (int y0 = 0; y0 < 64; y0++) { + int idx = (y0 << 10) + (u0 << 5) + v0; + int y = (y0 << 2) + (y0 & 0x3); + int u = (u0 << 3) + (y0 & 0x7); + int v = (v0 << 3) + (v0 & 0x7); + y -= 16; + u -= 128; + v -= 128; + uint16_t r = (YUV2RO(y, u, v) >> 3) & 0x1f; + uint16_t g = (YUV2GO(y, u, v) >> 2) & 0x3f; + uint16_t b = (YUV2BO(y, u, v) >> 3) & 0x1f; + table16[idx] = RGB565(r, g, b); + table16[idx] = (table16[idx] >> 8) | (table16[idx] << 8); + } + } + } + } + if (convert->from == AV_RENDER_VIDEO_RAW_TYPE_YUV420 && convert->to == AV_RENDER_VIDEO_RAW_TYPE_RGB565) { + for (int u0 = 0; u0 < 32; u0++) { + for (int v0 = 0; v0 < 32; v0++) { + for (int y0 = 0; y0 < 64; y0++) { + int idx = (y0 << 10) + (u0 << 5) + v0; + int y = (y0 << 2) + (y0 & 0x3); + int u = (u0 << 3) + (y0 & 0x7); + int v = (v0 << 3) + (v0 & 0x7); + y -= 16; + u -= 128; + v -= 128; + uint16_t r = (YUV2RO(y, u, v) >> 3) & 0x1f; + uint16_t g = (YUV2GO(y, u, v) >> 2) & 0x3f; + uint16_t b = (YUV2BO(y, u, v) >> 3) & 0x1f; + table16[idx] = RGB565(r, g, b); + } + } + } + } + return 0; +} + +int convert_table_get_image_size(av_render_video_frame_type_t fmt, int width, int height) +{ + switch (fmt) { + case AV_RENDER_VIDEO_RAW_TYPE_YUV420: + return width * height * 3 / 2; + case AV_RENDER_VIDEO_RAW_TYPE_RGB565: + case AV_RENDER_VIDEO_RAW_TYPE_RGB565_BE: + return width * height * 2; + default: + ESP_LOGE(TAG, "Not supported format %d", fmt); + break; + } + return 0; +} + +color_convert_table_t init_convert_table(color_convert_cfg_t *cfg) +{ + color_convert_t *convert = (color_convert_t *)calloc(1, sizeof(color_convert_t)); + do { + if (convert == NULL) { + break; + } + convert->from = cfg->from; + convert->to = cfg->to; + convert->width = cfg->width; + convert->height = cfg->height; +#if CONFIG_IDF_TARGET_ESP32P4 + if (convert->from == AV_RENDER_VIDEO_RAW_TYPE_YUV420 && convert->to == AV_RENDER_VIDEO_RAW_TYPE_RGB565) { + return (color_convert_table_t)convert; + } +#endif + int table_size = get_table_size(cfg->from, cfg->to); + if (table_size) { + convert->table = (uint8_t *)malloc(table_size); + if (convert->table == NULL) { + break; + } + } + init_table(convert); + return (color_convert_table_t)convert; + } while (0); + deinit_convert_table(convert); + return NULL; +} + +static void yuv420_to_rgb565(color_convert_t *convert, uint8_t *src, uint8_t *dst) +{ + uint8_t *y_plane = src; + uint8_t *u_plane = src + (convert->width * convert->height); + uint8_t *v_plane = src + (convert->width * convert->height * 5 / 4); + int y_pos = 0, u_pos = 0, rgb_idx = 0; + uint16_t *rgb565 = (uint16_t *)dst; + uint16_t *table16 = (uint16_t *)convert->table; + for (int i = 0; i < convert->height; i++) { + for (int j = 0; j < convert->width; j += 2) { + int y = y_plane[y_pos]; + int u = u_plane[u_pos]; + int v = v_plane[u_pos]; + int y_idx = (y >> 2) << 10; + int uv_idx = ((u >> 3) << 5) + (v >> 3); + + rgb565[rgb_idx++] = table16[y_idx + uv_idx]; + + y = y_plane[y_pos + 1]; + y_idx = (y >> 2) << 10; + rgb565[rgb_idx++] = table16[y_idx + uv_idx]; + y_pos += 2; + u_pos++; + } + // odd line not increase + if ((i & 1) == 0) { + u_pos -= convert->width >> 1; + } + } +} + +int convert_color(color_convert_table_t table, uint8_t *src, int src_size, uint8_t *dst, int dst_size) +{ + color_convert_t *convert = (color_convert_t *)table; + if (convert->from == AV_RENDER_VIDEO_RAW_TYPE_YUV420 && convert->to == AV_RENDER_VIDEO_RAW_TYPE_RGB565) { +#if CONFIG_IDF_TARGET_ESP32P4 + i420_to_rgb565le(src, dst, convert->width, convert->height); + return 0; +#endif + } + + switch (convert->from) { + case AV_RENDER_VIDEO_RAW_TYPE_YUV420: { + int src_need = convert->width * convert->height * 3 / 2; + int dst_need = convert->width * convert->height * 2; + if (src_size != src_need || dst_size < dst_need) { + ESP_LOGE(TAG, "size dismatch"); + return -1; + } + switch (convert->to) { + case AV_RENDER_VIDEO_RAW_TYPE_RGB565: + case AV_RENDER_VIDEO_RAW_TYPE_RGB565_BE: + yuv420_to_rgb565(convert, src, dst); + break; + default: + ESP_LOGE(TAG, "Bad format to %d", convert->to); + return -1; + } + } break; + default: + ESP_LOGE(TAG, "Bad format from %d", convert->from); + *(int *)0 = 0; + return -1; + } + return 0; +} + +void deinit_convert_table(color_convert_table_t t) +{ + color_convert_t *convert = (color_convert_t *)t; + if (convert) { + if (convert->table) { + free(convert->table); + convert->table = NULL; + } + free(convert); + } +} diff --git a/components/third_party/av_render/src/color_convert.h b/components/third_party/av_render/src/color_convert.h new file mode 100644 index 0000000..5bba04c --- /dev/null +++ b/components/third_party/av_render/src/color_convert.h @@ -0,0 +1,47 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef COLOR_CONVERT_H +#define COLOR_CONVERT_H + +#include "av_render_types.h" + +typedef void *color_convert_table_t; + +typedef struct { + av_render_video_frame_type_t from; + av_render_video_frame_type_t to; + int width; + int height; +} color_convert_cfg_t; + +int convert_table_get_image_size(av_render_video_frame_type_t fmt, int width, int height); + +color_convert_table_t init_convert_table(color_convert_cfg_t *cfg); + +int convert_color(color_convert_table_t table, uint8_t *src, int src_size, uint8_t *dst, int dst_size); + +void deinit_convert_table(color_convert_table_t t); + +#endif diff --git a/components/third_party/av_render/src/video_decoder.c b/components/third_party/av_render/src/video_decoder.c new file mode 100644 index 0000000..f42cc3c --- /dev/null +++ b/components/third_party/av_render/src/video_decoder.c @@ -0,0 +1,376 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "video_decoder.h" +#include "media_lib_os.h" +#include "media_lib_err.h" +#include "esp_log.h" +#include "color_convert.h" +#include "esp_heap_caps.h" +#include "esp_video_dec.h" +#include "esp_video_codec_utils.h" + +#define TAG "VID_DEC" + +typedef struct { + av_render_video_frame_info_t frame_info; + esp_video_dec_handle_t dec_handle; + uint8_t *out_data; + uint8_t *frame_data; + int frame_data_size; + uint32_t out_size; + bool header_parsed; + vdec_frame_cb frame_cb; + void *ctx; + esp_video_codec_pixel_fmt_t dec_out_fmt; + uint8_t out_frame_align; + bool need_clr_convert; + color_convert_table_t convert_table; + uint8_t *raw_buffer; + int raw_buffer_size; + vdec_fb_cb_cfg_t fb_cb; +} vdec_t; + +static esp_video_codec_type_t get_codec_type(av_render_video_codec_t codec) +{ + switch (codec) { + case AV_RENDER_VIDEO_CODEC_MJPEG: + return ESP_VIDEO_CODEC_TYPE_MJPEG; + case AV_RENDER_VIDEO_CODEC_H264: + return ESP_VIDEO_CODEC_TYPE_H264; + default: + return ESP_VIDEO_CODEC_TYPE_NONE; + } +} + +static esp_video_codec_pixel_fmt_t get_out_fmt(av_render_video_frame_type_t out_type) +{ + switch (out_type) { + case AV_RENDER_VIDEO_RAW_TYPE_YUV420: + return ESP_VIDEO_CODEC_PIXEL_FMT_YUV420P; + case AV_RENDER_VIDEO_RAW_TYPE_RGB565: + return ESP_VIDEO_CODEC_PIXEL_FMT_RGB565_LE; + case AV_RENDER_VIDEO_RAW_TYPE_RGB565_BE: + return ESP_VIDEO_CODEC_PIXEL_FMT_RGB565_BE; + default: + return ESP_VIDEO_CODEC_TYPE_NONE; + } +} + +static av_render_video_frame_type_t get_frame_type(esp_video_codec_pixel_fmt_t fmt) +{ + switch (fmt) { + case ESP_VIDEO_CODEC_PIXEL_FMT_RGB565_LE: + return AV_RENDER_VIDEO_RAW_TYPE_RGB565; + case ESP_VIDEO_CODEC_PIXEL_FMT_YUV420P: + return AV_RENDER_VIDEO_RAW_TYPE_YUV420; + case ESP_VIDEO_CODEC_PIXEL_FMT_RGB565_BE: + return AV_RENDER_VIDEO_RAW_TYPE_RGB565_BE; + default: + return ESP_VIDEO_CODEC_TYPE_NONE; + } +} + +static int do_decode(vdec_t *vdec, av_render_video_data_t *data, av_render_video_frame_t *out_frame) +{ + int ret = -1; + esp_video_dec_in_frame_t in_frame = { + .pts = data->pts, + .data = data->data, + .size = data->size, + }; + esp_video_dec_out_frame_t decoded_frame = {}; + if (vdec->header_parsed == false) { + vdec->out_size = 64; + vdec->out_data = esp_video_codec_align_alloc(vdec->out_frame_align, vdec->out_size, &vdec->out_size); + decoded_frame.data = vdec->out_data; + decoded_frame.size = vdec->out_size; + ret = esp_video_dec_process(vdec->dec_handle, &in_frame, &decoded_frame); + if (ret != ESP_VC_ERR_BUF_NOT_ENOUGH) { + return ESP_MEDIA_ERR_BAD_DATA; + } + esp_video_codec_free(vdec->out_data); + vdec->out_data = NULL; + esp_video_codec_frame_info_t frame_info = {}; + ret = esp_video_dec_get_frame_info(vdec->dec_handle, &frame_info); + if (ret != ESP_VC_ERR_OK) { + return ESP_MEDIA_ERR_NOT_SUPPORT; + } + vdec->frame_info.width = frame_info.res.width; + vdec->frame_info.height = frame_info.res.height; + ESP_LOGI(TAG, "Video resolution %dx%d fmt:%d", (int)frame_info.res.width, (int)frame_info.res.height, vdec->dec_out_fmt); + vdec->out_size = esp_video_codec_get_image_size(vdec->dec_out_fmt, &frame_info.res); + if ((vdec->frame_data == NULL && vdec->fb_cb.fb_fetch == NULL) || vdec->need_clr_convert) { + // TODO this middle buffer not needed if can get decoder output frame directly + vdec->out_data = esp_video_codec_align_alloc(vdec->out_frame_align, vdec->out_size, &vdec->out_size); + if (vdec->out_data == NULL) { + ESP_LOGE(TAG, "No memory for decode output size %d", (int)vdec->out_size); + return ESP_MEDIA_ERR_NO_MEM; + } + } + if (vdec->need_clr_convert) { + color_convert_cfg_t color_cfg = { + .width = frame_info.res.width, + .height = frame_info.res.height, + .from = get_frame_type(vdec->dec_out_fmt), + .to = vdec->frame_info.type, + }; + vdec->convert_table = init_convert_table(&color_cfg); + if (vdec->convert_table == NULL) { + ESP_LOGE(TAG, "No memory for color convert from %d to %d", color_cfg.from, color_cfg.to); + return -1; + } + vdec->raw_buffer_size = esp_video_codec_get_image_size(get_out_fmt(vdec->frame_info.type), &frame_info.res); + if (vdec->fb_cb.fb_fetch == NULL && vdec->raw_buffer == NULL) { + vdec->raw_buffer = (uint8_t *)malloc(vdec->raw_buffer_size); + if (vdec->raw_buffer == NULL) { + ESP_LOGE(TAG, "Fail to allocate convert output"); + return ESP_MEDIA_ERR_NO_MEM; + } + } + } + vdec->header_parsed = true; + } + if (vdec->need_clr_convert == false) { + if (vdec->fb_cb.fb_fetch) { + decoded_frame.data = vdec->fb_cb.fb_fetch(vdec->out_frame_align, vdec->out_size, vdec->fb_cb.ctx); + } else { + decoded_frame.data = vdec->frame_data ? vdec->frame_data : vdec->out_data; + } + } else { + decoded_frame.data = vdec->out_data; + } + decoded_frame.size = vdec->out_size; + ret = esp_video_dec_process(vdec->dec_handle, &in_frame, &decoded_frame); + if (ret != ESP_VC_ERR_OK) { + printf("Data size %d ", (int)data->size); +#define CC(a) a[0], a[1], a[2], a[3] + printf("%02x %02x %02x %02x\n", CC(data->data)); +#define CC1(a, size) a[size - 4], a[size - 3], a[size - 2], a[size - 1] + printf("Tail %02x %02x %02x %02x\n", CC1(data->data, data->size)); + ESP_LOGE(TAG, "Fail to decode data ret %d", ret); + if (vdec->fb_cb.fb_fetch && vdec->need_clr_convert == false) { + vdec->fb_cb.fb_return(decoded_frame.data, true, vdec->fb_cb.ctx); + } + return ret; + } + if (vdec->need_clr_convert == false) { + out_frame->pts = data->pts; + out_frame->data = decoded_frame.data; + out_frame->size = vdec->out_size; + return ESP_MEDIA_ERR_OK; + } + + uint8_t *out_data = vdec->frame_data ? vdec->frame_data : vdec->raw_buffer; + if (vdec->fb_cb.fb_fetch) { + out_data = vdec->fb_cb.fb_fetch(vdec->out_frame_align, vdec->raw_buffer_size, vdec->fb_cb.ctx); + if (out_data == NULL) { + return ESP_MEDIA_ERR_NO_MEM; + } + } + ret = convert_color(vdec->convert_table, decoded_frame.data, decoded_frame.decoded_size, + out_data, vdec->raw_buffer_size); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to convert color"); + if (vdec->fb_cb.fb_fetch) { + vdec->fb_cb.fb_return(out_data, true, vdec->fb_cb.ctx); + } + return ret; + } + out_frame->pts = decoded_frame.pts; + out_frame->data = out_data; + out_frame->size = vdec->raw_buffer_size; + return ESP_MEDIA_ERR_OK; +} + +static int check_input_format_support(vdec_t *vdec, esp_video_dec_cfg_t *dec_cfg, av_render_video_frame_type_t out_fmt) +{ + // Check whether RGB565 supported by decoder + esp_video_codec_query_t query = { + .codec_type = dec_cfg->codec_type, + }; + esp_video_dec_caps_t caps = {}; + esp_video_dec_query_caps(&query, &caps); + if (caps.out_fmt_num == 0) { + return ESP_MEDIA_ERR_NOT_SUPPORT; + } + esp_video_codec_pixel_fmt_t dec_out_fmt = get_out_fmt(out_fmt); + for (int i = 0; i < caps.out_fmt_num; i++) { + if (caps.out_fmts[i] == dec_out_fmt) { + dec_cfg->out_fmt = dec_out_fmt; + return ESP_MEDIA_ERR_OK; + } + } + vdec->need_clr_convert = true; + // Prefer to use first one + dec_cfg->out_fmt = caps.out_fmts[0]; + ESP_LOGI(TAG, "Need color covert to covert from %d to %d", dec_cfg->out_fmt, get_out_fmt(out_fmt)); + return ESP_MEDIA_ERR_OK; +} + +int vdec_get_output_formats(av_render_video_codec_t codec, av_render_video_frame_type_t* fmts, uint8_t* num) +{ + esp_video_codec_query_t query = { + .codec_type = get_codec_type(codec), + }; + esp_video_dec_caps_t caps = {}; + esp_video_dec_query_caps(&query, &caps); + if (caps.out_fmt_num == 0) { + return ESP_MEDIA_ERR_NOT_SUPPORT; + } + if (*num < caps.out_fmt_num) { + return ESP_MEDIA_ERR_EXCEED_LIMIT; + } + for (int i = 0; i < caps.out_fmt_num; i++) { + fmts[i] = get_frame_type(caps.out_fmts[i]); + } + *num = caps.out_fmt_num; + return ESP_MEDIA_ERR_OK; +} + +vdec_handle_t vdec_open(vdec_cfg_t *cfg) +{ + if (cfg == NULL) { + return NULL; + } + vdec_t *vdec = (vdec_t *)media_lib_calloc(1, sizeof(vdec_t)); + if (vdec == NULL) { + return NULL; + } + // Save input arguments + vdec->frame_info.width = cfg->video_info.width; + vdec->frame_info.height = cfg->video_info.height; + vdec->frame_info.fps = cfg->video_info.fps; + vdec->frame_cb = cfg->frame_cb; + vdec->ctx = cfg->ctx; + + av_render_video_frame_type_t output_type = cfg->out_type; + if (output_type == AV_RENDER_VIDEO_RAW_TYPE_NONE) { + output_type = AV_RENDER_VIDEO_RAW_TYPE_RGB565; + } + esp_video_dec_cfg_t dec_cfg = { + .codec_type = get_codec_type(cfg->video_info.codec), + }; + do { + int ret = check_input_format_support(vdec, &dec_cfg, output_type); + if (ret != ESP_MEDIA_ERR_OK) { + ESP_LOGE(TAG, "Format %d not support by %d", get_out_fmt(output_type), dec_cfg.codec_type); + break; + } + ret = esp_video_dec_open(&dec_cfg, &vdec->dec_handle); + if (ret == 0 && vdec->dec_handle) { + uint8_t in_align = 0; + esp_video_dec_get_frame_align(vdec->dec_handle, &in_align, &vdec->out_frame_align); + vdec->frame_info.type = output_type; + vdec->dec_out_fmt = dec_cfg.out_fmt; + return vdec; + } + } while (0); + vdec_close(vdec); + return NULL; +} + +int vdec_set_fb_cb(vdec_handle_t h, vdec_fb_cb_cfg_t *cfg) +{ + vdec_t *vdec = (vdec_t *)h; + if (vdec == NULL || cfg->fb_fetch == NULL || cfg->fb_return == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + vdec->fb_cb = *cfg; + return ESP_MEDIA_ERR_OK; +} + +int vdec_decode(vdec_handle_t h, av_render_video_data_t *data) +{ + vdec_t *vdec = (vdec_t *)h; + if (vdec == NULL || data == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + av_render_video_frame_t out_frame = { + .eos = data->eos, + .pts = data->pts + }; + int ret = do_decode(vdec, data, &out_frame); + if (ret == 0 && vdec->frame_cb) { + vdec->header_parsed = true; + vdec->frame_cb(&out_frame, vdec->ctx); + if (vdec->fb_cb.fb_fetch && out_frame.data) { + vdec->fb_cb.fb_return(out_frame.data, false, vdec->fb_cb.ctx); + } + } + return ret; +} + +int vdec_set_frame_buffer(vdec_handle_t h, av_render_frame_buffer_t *buffer) +{ + vdec_t *vdec = (vdec_t *)h; + if (vdec == NULL || buffer == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + vdec->frame_data = buffer->data; + // Notes make sure frame buffer size big than JPEG image size + if (buffer->size > 0) { + vdec->frame_data_size = buffer->size; + } + return ESP_MEDIA_ERR_OK; +} + +int vdec_get_frame_info(vdec_handle_t h, av_render_video_frame_info_t *frame_info) +{ + vdec_t *vdec = (vdec_t *)h; + if (vdec == NULL || frame_info == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + if (vdec->header_parsed == false) { + return ESP_MEDIA_ERR_NOT_SUPPORT; + } + memcpy(frame_info, &vdec->frame_info, sizeof(av_render_video_frame_info_t)); + return ESP_MEDIA_ERR_OK; +} + +int vdec_close(vdec_handle_t h) +{ + vdec_t *vdec = (vdec_t *)h; + if (vdec == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + if (vdec->dec_handle) { + esp_video_dec_close(vdec->dec_handle); + vdec->dec_handle = NULL; + } + if (vdec->out_data) { + free(vdec->out_data); + vdec->out_data = NULL; + } + if (vdec->convert_table) { + deinit_convert_table(vdec->convert_table); + vdec->convert_table = NULL; + } + if (vdec->raw_buffer) { + free(vdec->raw_buffer); + vdec->raw_buffer = NULL; + } + media_lib_free(vdec); + return ESP_MEDIA_ERR_OK; +} diff --git a/components/third_party/av_render/src/video_render.c b/components/third_party/av_render/src/video_render.c new file mode 100644 index 0000000..969faf8 --- /dev/null +++ b/components/third_party/av_render/src/video_render.c @@ -0,0 +1,153 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include "video_render.h" +#include "media_lib_os.h" + +typedef struct { + video_render_ops_t render_ops; + video_render_handle_t render_handle; + bool is_open; + int ref_count; +} video_render_t; + +static void try_free(video_render_t *v_render) +{ + if (v_render->ref_count) { + v_render->ref_count--; + } + if (v_render->ref_count == 0) { + if (v_render->render_handle) { + v_render->render_ops.close(v_render->render_handle); + v_render->render_handle = NULL; + } + media_lib_free(v_render); + } +} + +video_render_handle_t video_render_alloc_handle(video_render_cfg_t *cfg) +{ + if (cfg == NULL || cfg->ops.open == NULL || cfg->ops.close == NULL) { + return NULL; + } + video_render_t *v_render = (video_render_t *)media_lib_calloc(1, sizeof(video_render_t)); + if (v_render == NULL) { + return NULL; + } + memcpy(&v_render->render_ops, &cfg->ops, sizeof(video_render_ops_t)); + v_render->render_handle = v_render->render_ops.open(cfg->cfg, cfg->cfg_size); + if (v_render->render_handle) { + v_render->ref_count++; + return v_render; + } + video_render_free_handle(v_render); + return NULL; +} + +bool video_render_format_supported(video_render_handle_t render, av_render_video_frame_type_t frame_type) +{ + video_render_t *v_render = render; + if (v_render == NULL || v_render->render_handle == NULL) { + return false; + } + return v_render->render_ops.format_support(v_render->render_handle, frame_type); +} + +int video_render_open(video_render_handle_t render, av_render_video_frame_info_t *info) +{ + video_render_t *v_render = render; + if (info == NULL || v_render == NULL || v_render->render_handle == NULL) { + return -1; + } + int ret = v_render->render_ops.set_frame_info(v_render->render_handle, info); + if (ret != 0) { + return ret; + } + v_render->is_open = true; + v_render->ref_count++; + return ret; +} + +int video_render_write(video_render_handle_t render, av_render_video_frame_t *video_data) +{ + video_render_t *v_render = render; + if (video_data == NULL || v_render == NULL || v_render->is_open == false) { + return -1; + } + return v_render->render_ops.write(v_render->render_handle, video_data); +} + +int video_render_get_latency(video_render_handle_t render, uint32_t *latency) +{ + video_render_t *v_render = render; + if (latency == NULL || v_render == NULL || v_render->is_open == false) { + return -1; + } + return v_render->render_ops.get_latency(v_render->render_handle, latency); +} + +int video_render_get_frame_buffer(video_render_handle_t render, av_render_frame_buffer_t *buffer) +{ + video_render_t *v_render = render; + if (buffer == NULL || v_render == NULL) { + return -1; + } + if (v_render->render_ops.get_frame_info == NULL) { + return ESP_MEDIA_ERR_NOT_SUPPORT; + } + return v_render->render_ops.get_frame_buffer(v_render->render_handle, buffer); +} + +int video_render_get_frame_info(video_render_handle_t render, av_render_video_frame_info_t *info) +{ + video_render_t *v_render = render; + if (info == NULL || v_render == NULL || v_render->is_open == false) { + return -1; + } + return v_render->render_ops.get_frame_info(v_render->render_handle, info); +} + +int video_render_close(video_render_handle_t render) +{ + video_render_t *v_render = render; + if (v_render == NULL || v_render->is_open == false) { + return -1; + } + if (v_render->render_ops.clear) { + v_render->render_ops.clear(v_render->render_handle); + } + v_render->is_open = false; + try_free(v_render); + return 0; +} + +void video_render_free_handle(video_render_handle_t render) +{ + video_render_t *v_render = render; + if (v_render == NULL) { + return; + } + try_free(v_render); +} diff --git a/components/third_party/codec_board/CMakeLists.txt b/components/third_party/codec_board/CMakeLists.txt new file mode 100644 index 0000000..9c233d7 --- /dev/null +++ b/components/third_party/codec_board/CMakeLists.txt @@ -0,0 +1,17 @@ + +set(COMPONENT_PRIV_REQUIRE driver esp_codec_dev fatfs media_lib_sal esp_lcd) + +if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3") +list(APPEND COMPONENT_PRIV_REQUIRE esp_driver_i2c) +endif() + +if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3") +list(APPEND COMPONENT_PRIV_REQUIRE esp_driver_i2c) +endif() + +idf_component_register(SRCS "cfg_parse.c" "codec_board.c" "codec_init.c" "dummy_codec.c" + "extend_io.c" "lcd_init.c" "led_init.c" "drv/tca9554.c" + INCLUDE_DIRS ./include + PRIV_INCLUDE_DIRS drv + REQUIRES "${COMPONENT_PRIV_REQUIRE}" + EMBED_TXTFILES "board_cfg.txt") diff --git a/components/third_party/codec_board/README.md b/components/third_party/codec_board/README.md new file mode 100644 index 0000000..bbe7149 --- /dev/null +++ b/components/third_party/codec_board/README.md @@ -0,0 +1,48 @@ +# Codec_board + +## Overview +`codec_board` is a simple implementation of board support for a variety of ESP32 series boards. +Its main purpose is to: +- Gather board-specific peripheral settings from user. +- Add drivers for the supported peripherals. +- Provide high-level APIs to interact with the peripherals or abstract the peripherals into manageable handles. + +## Supported Peripherals + +- **Codec** + Currently support `ES8311`, `ES8388`, `ES7210`, `ES7243`, user can add other codec also. + After `init_codec` is called, codec is abstracted into `esp_codec_dev` handle. + Then use can use `esp_codec_dev` API to do playback and record. + Simple configuration of codec as: +``` +out: {codec: ES8311, pa: 38, use_mclk: 0, pa_gain:6} +in: {codec: ES7210, i2s_port: 1} +``` + +- **SDcard** + Users can configure SDCard related GPIO then use API `mount_sdcard` to mount sdcard to use it. + SDcard configuration as: +``` +sdcard: {clk: 43, cmd: 44, d0: 39, d1: 40, d2: 41, d3: 42} +``` + +- **Camera** + Currently support DVP and MIPI camera, use can get camera settings use API `get_camera_cfg` + +- **LCD** + Currently only support LCD on `S3_Korvo_V2` and `ESP32_P4_FUNCTION_EV`. + User can get LCD handle through API `board_get_lcd_handle` after `board_lcd_init` + +## Customized a New Board +To reuse the pre-defined devices, follow these steps: +1. Add a new section to the [board_cfg.txt](board_cfg.txt) file and modify corresponding GPIO settings as needed. +2. Call `set_codec_board_type` with the name of the newly added section. +For new devices, you will need to manually modify the code. + +## Comparison with esp-bsp + +[esp-bsp](https://github.com/espressif/esp-bsp) supports almost all devices carried on board while this module only support media related devices. It provides a quick verification option if reusing the same device with different GPIO settings. Users can easily replace this module with `esp-bsp` by making the following substitutions: + +- `bsp_audio_codec_speaker_init`: Replaces `get_playback_handle` +- `bsp_audio_codec_microphone_init`: Replaces `get_record_handle` +- `bsp_display_new`: Replaces `board_get_lcd_handle` \ No newline at end of file diff --git a/components/third_party/codec_board/board_cfg.txt b/components/third_party/codec_board/board_cfg.txt new file mode 100644 index 0000000..2b3d78c --- /dev/null +++ b/components/third_party/codec_board/board_cfg.txt @@ -0,0 +1,86 @@ +# support in, out, in_out type +# support i2c_port, i2s_port settings +# support pa_gain, i2c_addr setting + +Board: S3_Korvo_V2 +i2c: {sda: 17, scl: 18} +i2s: {mclk: 16, bclk: 9, ws: 45, din: 10, dout: 8} +out: {codec: ES8311, pa: 48, pa_gain: 6, use_mclk: 1, pa_gain:6} +in: {codec: ES7210} +sdcard: {clk: 15, cmd: 7, d0: 4} +camera: {type: dvp, xclk: 40, pclk: 11, vsync: 21, href: 38, d0: 13, d1: 47, d2: 14, d3: 3, d4: 12, d5: 42, d6: 41, d7: 39} +lcd: { + bus: spi, extend_io: tca9554, controller: st7789, spi_bus: 2, + mirror_x: 1, mirror_y: 1, swap_xy: 0, color_inv: 0, + width: 320, height: 240, + ctrl: ext1, rst: ext2, + cs: ext3, dc: 2, clk: 1, mosi: 0, cmd_bits: 8, param_bits: 8 +} + +Board: DUMMY_CODEC_BOARD +i2s: {mclk: 5, bclk: 6, ws: 16, din: -1, dout: 8} +out: {codec: DUMMY, pa: 15, i2c_port: -1} + +Board: S3_Korvo_V4 +i2c: {sda: 1, scl: 2} +i2s: {mclk: 42, bclk: 40, ws: 41, dout: 39} +i2s: {mclk: 20, bclk: 10, ws: 9, din: 11} +out: {codec: ES8311, pa: 38, use_mclk: 0, pa_gain:6} +in: {codec: ES7210, i2s_port: 1} +sdcard: {clk: 18, cmd: 17, d0: 16} + +Board: LYRAT_MINI_V1 +i2c: {sda: 18, scl: 23} +i2s: {mclk: 0, bclk: 5, ws: 25, dout: 26, din: 35} +i2s: {mclk: -1, bclk: 32, ws: 33, din: 36} +out: {codec: ES8311, pa: 21, use_mclk: 1, pa_gain:20} +in: {codec: ES7243, i2s_port: 0} +sdcard: {power: 13, clk: 14, cmd: 15, d0: 2} + +Board: ESP32_KORVO_V1 +i2c: {sda: 19, scl: 32} +i2s: {mclk: 0, bclk: 25, ws: 22, dout: 13, din: -1} +i2s: {mclk: 0, bclk: 27, ws: 26, dout: -1, din: 36} +out: {codec: ES8311, pa: 12, use_mclk: 1, pa_gain:6} +in: {codec: ES7210, i2s_port: 1} +sdcard: {clk: 14, cmd: 15, d0: 2} + +Board: ESP32_LYRAT_V43 +i2c: {sda: 18, scl: 23} +i2s: {mclk: -1, bclk: 5, ws: 25, dout: 26, din: 35} +out: {codec: ES8388, pa: 21, use_mclk: 0, pa_gain:6} + +Board: ESP32S3_BOX +i2c: {sda: 8, scl: 18} +i2s: {mclk: 2, bclk: 17, ws: 47, dout: 15, din: 16} +out: {codec: ES8311, pa: 46, use_mclk: 1, pa_gain:6} +in: {codec: ES7210} + +Board: ESP32_S3_BOX_3 +i2c: {sda: 8, scl: 18, i2c_addr: 24} +i2s: {mclk: 2, bclk: 17, ws: 45, din: 16, dout: 15} +out: {codec: ES8311, pa: 46, use_mclk: 1, pa_gain: 6} +in: {codec: ES7210} + +Board: ESP32_P4_DEV_V14 +i2c: {sda: 7, scl: 8} +i2s: {mclk: 13, bclk: 12, ws: 10, dout: 9, din: 11} +in_out: {codec: ES8311, pa: 53, use_mclk: 1, pa_gain:6} +sdcard: {clk: 43, cmd: 44, d0: 39, d1: 40, d2: 41, d3: 42} +camera: {type: mipi} +lcd: { + bus: mipi, ldo_chan: 3, ldo_voltage: 2500, lane_num: 2, + lane_bitrate: 1000, dpi_clk: 80, bit_depth: 16, fb_num: 2 + dsi_hsync: 1344, dsi_vsync: 635, + dsi_hbp: 160, dsi_hfp: 160, + dsi_vbp: 23, dsi_vfp: 12, + rst: -1, + width: 1024, height: 600, +} + +Board: ESP32S3_EYE +i2c: {sda: 4, scl: 5} +i2s: {mclk: -1, bclk: 41, ws: 42, dout: -1, din: 2} +in: {codec: DUMMY, i2c_port: -1} +sdcard: {clk: 39, cmd: 38, d0: 40} +camera: {type: dvp, xclk: 15, pclk: 13, vsync: 6, href: 7, d0: 11, d1: 9, d2: 8, d3: 10, d4: 12, d5: 18, d6: 17, d7: 16} diff --git a/components/third_party/codec_board/cfg_parse.c b/components/third_party/codec_board/cfg_parse.c new file mode 100644 index 0000000..1f81a5a --- /dev/null +++ b/components/third_party/codec_board/cfg_parse.c @@ -0,0 +1,596 @@ +#include "codec_board.h" + +#define BOARD_SECTION "Board:" + +#define IN_STR(a, const_b, len) in_str(a, const_b, sizeof(const_b) - 1, len) + +typedef struct _board_cfg_attr { + const char *attr; + const char *value; + struct _board_cfg_attr *next; +} board_cfg_attr_t; + +typedef struct { + const char *type; + board_cfg_attr_t *attr; +} board_cfg_line_t; + +static board_section_t *codec_section; + +extern const char board_cfg_start[] asm("_binary_board_cfg_txt_start"); +extern const char board_cfg_end[] asm("_binary_board_cfg_txt_end"); + +static bool is_word(char s) +{ + return ((s >= 'A' && s <= 'Z') || (s >= 'a' && s <= 'z') || (s >= '0' && s <= '9') || s == '_' || s == '-' || + s == '.'); +} + +bool str_same(const char *a, const char *b) +{ + while (*b && *a == *b) { + a++; + b++; + } + if (*b == 0) { + if (is_word(*a) == false) { + return true; + } + } + return false; +} + +static const char *in_str(const char *s, const char *org, int org_len, int len) +{ + while (len > org_len) { + if (*s == *org && memcmp(s, org, org_len) == 0 && is_word(s[org_len]) == false) { + return s; + } + s++; + len--; + } + return NULL; +} + +const char *get_section_data(const char *data, int size, const char *section_name) +{ + const char *s = data; + while (1) { + int left = size - (s - data); + s = IN_STR(s, BOARD_SECTION, left); + if (s == NULL) { + break; + } + s += sizeof(BOARD_SECTION) - 1; + while (!is_word(*s)) { + s++; + } + if (str_same(s, section_name)) { + while (*(s++) != '\n') + ; + return s; + } + } + return NULL; +} + +static int str_len(const char *s) +{ + int len = 0; + while (is_word(*s)) { + len++; + s++; + } + return len; +} + +static void print_cfg_line(board_cfg_line_t *cfg_line) +{ + printf("%.*s: {", str_len(cfg_line->type), cfg_line->type); + board_cfg_attr_t *attr = cfg_line->attr; + while (attr) { + printf("%.*s: %.*s", str_len(attr->attr), attr->attr, str_len(attr->value), attr->value); + if (attr->next) { + printf(", "); + } + attr = attr->next; + } + printf("}\n"); +} + +static void free_cfg_line(board_cfg_line_t *cfg_line) +{ + if (cfg_line) { + board_cfg_attr_t *attr = cfg_line->attr; + while (attr) { + board_cfg_attr_t *nxt = attr->next; + free(attr); + attr = nxt; + } + free(cfg_line); + } +} + +static board_cfg_line_t *parse_section(const char *s, int size, int *consume) +{ + board_cfg_line_t *cfg_line; + const char *start = s; + const char *end = s + size; + const char *name = s; + bool is_comment = false; + while (size) { + if (*name == '#') { + is_comment = true; + } + if (is_comment) { + if (*name == '\n') { + is_comment = false; + } + } else { + if (is_word(*name)) { + break; + } + } + size--; + name++; + } + if (size == 0 || str_same(name, BOARD_SECTION)) { + return NULL; + } + cfg_line = calloc(1, sizeof(board_cfg_line_t)); + if (cfg_line == NULL) { + return NULL; + } + board_cfg_attr_t *tail = NULL; + cfg_line->type = name; + s = name; + while (*(s++) != '{') + ; + int word_len = 0; + const char *attr = NULL; + + while (s < end) { + if (*s == '}') { + *consume = (s + 1 - start); + break; + } + if (is_word(*s)) { + if (word_len == 0) { + if (attr == NULL) { + attr = s; + } else { + board_cfg_attr_t *cfg_attr = calloc(1, sizeof(board_cfg_attr_t)); + if (cfg_attr) { + cfg_attr->attr = attr; + cfg_attr->value = s; + if (tail == NULL) { + cfg_line->attr = cfg_attr; + } else { + tail->next = cfg_attr; + } + tail = cfg_attr; + } + attr = NULL; + } + } + word_len++; + } else { + word_len = 0; + } + s++; + } + return cfg_line; +} + +static int fill_i2c_cfg(board_cfg_attr_t *attr) +{ + if (codec_section->i2c_num >= MAX_I2C_NUM) { + return -1; + } + codec_i2c_pin_t *i2c_cfg = &codec_section->i2c_pin[codec_section->i2c_num++]; + while (attr) { + if (str_same(attr->attr, "sda")) { + i2c_cfg->sda = atoi(attr->value); + } else if (str_same(attr->attr, "scl")) { + i2c_cfg->scl = atoi(attr->value); + } + attr = attr->next; + } + return 0; +} + +static int fill_i2s_cfg(board_cfg_attr_t *attr) +{ + if (codec_section->i2s_num >= MAX_I2C_NUM) { + return -1; + } + codec_i2s_pin_t *i2s_cfg = &codec_section->i2s_pin[codec_section->i2s_num++]; + i2s_cfg->din = i2s_cfg->dout = -1; + while (attr) { + if (str_same(attr->attr, "bclk")) { + i2s_cfg->bclk = atoi(attr->value); + } else if (str_same(attr->attr, "din")) { + i2s_cfg->din = atoi(attr->value); + } else if (str_same(attr->attr, "dout")) { + i2s_cfg->dout = atoi(attr->value); + } else if (str_same(attr->attr, "ws")) { + i2s_cfg->ws = atoi(attr->value); + } else if (str_same(attr->attr, "mclk")) { + i2s_cfg->mclk = atoi(attr->value); + } + attr = attr->next; + } + return 0; +} + +static extend_io_type_t lcd_get_io_type(const char* s) +{ + if (str_same(s, "tca9554")) { + return EXTENT_IO_TYPE_TCA9554; + } + return EXTENT_IO_TYPE_NONE; +} + +static lcd_controller_type_t lcd_get_controller(const char* s) +{ + if (str_same(s, "st7789")) { + return LCD_CONTROLLER_TYPE_ST7789; + } + return LCD_CONTROLLER_TYPE_NONE; +} + +static lcd_bus_type_t lcd_get_bus(const char* s) +{ + if (str_same(s, "spi")) { + return LCD_BUS_TYPE_SPI; + } + if (str_same(s, "rgb")) { + return LCD_BUS_TYPE_RGB; + } + if (str_same(s, "i80")) { + return LCD_BUS_TYPE_I80; + } + if (str_same(s, "mipi")) { + return LCD_BUS_TYPE_MIPI; + } + return LCD_BUS_TYPE_NONE; +} + +static int16_t get_pin(const char* s) +{ + char* ext = strstr(s, "ext"); + if (ext) { + return 0x1000 + atoi(ext + 3); + } + return (int16_t)atoi(s); +} + +static int fill_lcd_cfg(board_cfg_attr_t *attr) +{ + if (codec_section->lcd_num >= 1) { + return -1; + } + lcd_cfg_t *lcd_cfg = &codec_section->lcd; + lcd_cfg->reset_pin = lcd_cfg->ctrl_pin = -1; + codec_section->lcd_num++; + while (attr) { + if (str_same(attr->attr, "bus")) { + lcd_cfg->bus_type = lcd_get_bus(attr->value); + if (lcd_cfg->bus_type == LCD_BUS_TYPE_SPI) { + for (int i = 0; i < sizeof(lcd_cfg->spi_cfg.d)/sizeof(lcd_cfg->spi_cfg.d[0]); i++) { + lcd_cfg->spi_cfg.d[i] = -1; + } + } + } else if (str_same(attr->attr, "controller")) { + lcd_cfg->controller = lcd_get_controller(attr->value); + } + else if (str_same(attr->attr, "extend_io")) { + lcd_cfg->io_type = lcd_get_io_type(attr->value); + } else if (str_same(attr->attr, "width")) { + lcd_cfg->width = atoi(attr->value); + } else if (str_same(attr->attr, "height")) { + lcd_cfg->height = atoi(attr->value); + } else if (str_same(attr->attr, "mirror_x")) { + lcd_cfg->mirror_x = atoi(attr->value) ? 1: 0; + } else if (str_same(attr->attr, "mirror_y")) { + lcd_cfg->mirror_y = atoi(attr->value) ? 1: 0; + } else if (str_same(attr->attr, "swap_xy")) { + lcd_cfg->swap_xy = atoi(attr->value) ? 1: 0; + } else if (str_same(attr->attr, "color_inv")) { + lcd_cfg->color_inv = atoi(attr->value) ? 1: 0; + } else if (str_same(attr->attr, "ctrl")) { + lcd_cfg->ctrl_pin = get_pin(attr->value); + } else if (str_same(attr->attr, "rst")) { + lcd_cfg->reset_pin = get_pin(attr->value); + } + else if (lcd_cfg->bus_type == LCD_BUS_TYPE_SPI) { + // Bus configuration + if (str_same(attr->attr, "spi_bus")) { + lcd_cfg->spi_cfg.spi_bus = atoi(attr->value); + } else if (str_same(attr->attr, "cs")) { + lcd_cfg->spi_cfg.cs = get_pin(attr->value); + } else if (str_same(attr->attr, "dc")) { + lcd_cfg->spi_cfg.dc = get_pin(attr->value); + } else if (str_same(attr->attr, "clk")) { + lcd_cfg->spi_cfg.clk = get_pin(attr->value); + } else if (str_same(attr->attr, "mosi")) { + lcd_cfg->spi_cfg.mosi = get_pin(attr->value); + } else if (str_same(attr->attr, "cmd_bits")) { + lcd_cfg->spi_cfg.cmd_bits = (uint8_t)atoi(attr->value); + } else if (str_same(attr->attr, "param_bits")) { + lcd_cfg->spi_cfg.param_bits = (uint8_t)atoi(attr->value); + } + } else if (lcd_cfg->bus_type == LCD_BUS_TYPE_MIPI) { + if (str_same(attr->attr, "ldo_chan")) { + lcd_cfg->mipi_cfg.ldo_chan = (uint8_t)atoi(attr->value); + } + else if (str_same(attr->attr, "ldo_voltage")) { + lcd_cfg->mipi_cfg.ldo_voltage = (uint16_t)atoi(attr->value); + } + else if (str_same(attr->attr, "lane_num")) { + lcd_cfg->mipi_cfg.lane_num = (uint8_t)atoi(attr->value); + } + else if (str_same(attr->attr, "fb_num")) { + lcd_cfg->mipi_cfg.fb_num = (uint8_t)atoi(attr->value); + } + else if (str_same(attr->attr, "bit_depth")) { + lcd_cfg->mipi_cfg.bit_depth = (uint16_t)atoi(attr->value); + } + else if (str_same(attr->attr, "lane_bitrate")) { + lcd_cfg->mipi_cfg.lane_bitrate = (uint32_t)atoi(attr->value); + } + else if (str_same(attr->attr, "dpi_clk")) { + lcd_cfg->mipi_cfg.dpi_clk = (uint32_t)atoi(attr->value); + } + else if (str_same(attr->attr, "dsi_hsync")) { + lcd_cfg->mipi_cfg.dsi_hsync = (uint8_t)atoi(attr->value); + } + else if (str_same(attr->attr, "dsi_vsync")) { + lcd_cfg->mipi_cfg.dsi_vsync = (uint8_t)atoi(attr->value); + } + else if (str_same(attr->attr, "dsi_hbp")) { + lcd_cfg->mipi_cfg.dsi_hbp = (uint8_t)atoi(attr->value); + } + else if (str_same(attr->attr, "dsi_hfp")) { + lcd_cfg->mipi_cfg.dsi_hfp = (uint8_t)atoi(attr->value); + } + else if (str_same(attr->attr, "dsi_vbp")) { + lcd_cfg->mipi_cfg.dsi_vbp = (uint8_t)atoi(attr->value); + } + else if (str_same(attr->attr, "dsi_vfp")) { + lcd_cfg->mipi_cfg.dsi_vfp = (uint8_t)atoi(attr->value); + } + } + attr = attr->next; + } + return 0; +} + +// TODO add other codec +static codec_type_t get_codec_type(const char *type) +{ + if (str_same(type, "ES8311")) { + return CODEC_TYPE_ES8311; + } + if (str_same(type, "ES8388")) { + return CODEC_TYPE_ES8388; + } + if (str_same(type, "ES7243")) { + return CODEC_TYPE_ES7243; + } + if (str_same(type, "ES7210")) { + return CODEC_TYPE_ES7210; + } + if (str_same(type, "DUMMY")) { + return CODEC_TYPE_DUMMY; + } + return CODEC_TYPE_NONE; +} + +static int fill_codec_cfg(board_cfg_attr_t *attr, uint8_t codec_dir) +{ + if (codec_section->codec_num >= MAX_CODEC_NUM) { + return -1; + } + codec_section->codec[codec_section->codec_num].codec_dir = codec_dir; + codec_cfg_t *codec_cfg = &codec_section->codec[codec_section->codec_num].codec_cfg; + while (attr) { + if (str_same(attr->attr, "i2c_port")) { + codec_cfg->i2c_port = atoi(attr->value); + if (codec_cfg->i2c_port >= codec_section->i2c_num) { + return -1; + } + } else if (str_same(attr->attr, "i2s_port")) { + codec_cfg->i2s_port = atoi(attr->value); + if (codec_cfg->i2s_port >= codec_section->i2s_num) { + return -1; + } + } else if (str_same(attr->attr, "pa")) { + codec_cfg->pa_pin = atoi(attr->value); + } else if (str_same(attr->attr, "pa_gain")) { + codec_cfg->pa_gain = atof(attr->value); + } else if (str_same(attr->attr, "use_mclk")) { + codec_cfg->use_mclk = (bool) atoi(attr->value); + } else if (str_same(attr->attr, "codec")) { + codec_cfg->codec_type = get_codec_type(attr->value); + if (codec_cfg->codec_type == CODEC_TYPE_NONE) { + return -1; + } + } else if (str_same(attr->attr, "i2c_addr")) { + codec_cfg->i2c_addr = (uint8_t) atoi(attr->value); + } + attr = attr->next; + } + printf("Codec %d dir %d type:%d\n", codec_section->codec_num, codec_dir, codec_cfg->codec_type); + codec_section->codec_num++; + return 0; +} + +static int fill_sdcard_cfg(board_cfg_attr_t *attr) +{ + if (codec_section->sdcard_num >= 1) { + return -1; + } + sdcard_cfg_t *sdcard_cfg = &codec_section->sdcard; + sdcard_cfg->power = -1; + while (attr) { + if (str_same(attr->attr, "clk")) { + sdcard_cfg->clk = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "cmd")) { + sdcard_cfg->cmd = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "d0")) { + sdcard_cfg->d0 = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "d1")) { + sdcard_cfg->d1 = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "d2")) { + sdcard_cfg->d2 = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "d3")) { + sdcard_cfg->d3 = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "power")) { + sdcard_cfg->power = (int16_t) atoi(attr->value); + } + attr = attr->next; + } + printf("Sdcard clk:%d cmd:%d d0:%d\n", sdcard_cfg->clk, sdcard_cfg->cmd, sdcard_cfg->d0); + codec_section->sdcard_num++; + return 0; +} + +static camera_type_t get_camera_type(const char* s) { + if (str_same(s, "dvp")) { + return CAMERA_TYPE_DVP; + } + if (str_same(s, "usb")) { + return CAMERA_TYPE_USB; + } + if (str_same(s, "mipi")) { + return CAMERA_TYPE_MIPI; + } + return CAMERA_TYPE_NONE; +} + +static int fill_camera_cfg(board_cfg_attr_t *attr) +{ + if (codec_section->camera_num >= 1) { + return -1; + } + camera_cfg_t *camera_cfg = &codec_section->camera; + for (int i = 0; i < sizeof(camera_cfg->data)/sizeof(camera_cfg->data[0]); i++) { + camera_cfg->data[i] = -1; + } + camera_cfg->pwr = -1; + camera_cfg->reset = -1; + while (attr) { + if (str_same(attr->attr, "type")) { + camera_cfg->type = get_camera_type(attr->value); + } + if (str_same(attr->attr, "xclk")) { + camera_cfg->xclk = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "pclk")) { + camera_cfg->pclk = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "pwr")) { + camera_cfg->pwr = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "reset")) { + camera_cfg->reset = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "vsync")) { + camera_cfg->vsync = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "href")) { + camera_cfg->href = (int16_t) atoi(attr->value); + } else if (str_same(attr->attr, "de")) { + camera_cfg->de = (int16_t) atoi(attr->value); + } else if (attr->attr[0] == 'd' && attr->attr[1] >= '0' && attr->attr[1] <= '9') { + int n = atoi(attr->attr + 1); + if (n < sizeof(camera_cfg->data)/sizeof(camera_cfg->data[0])) { + camera_cfg->data[n] = (int16_t) atoi(attr->value); + } + } + attr = attr->next; + } + codec_section->camera_num++; + return 0; +} + +static int fill_cfg(board_cfg_line_t *cfg_line) +{ + board_cfg_attr_t *attr = cfg_line->attr; + if (str_same(cfg_line->type, "i2c")) { + if (fill_i2c_cfg(attr) != 0) { + return -1; + } + } else if (str_same(cfg_line->type, "i2s")) { + if (fill_i2s_cfg(attr) != 0) { + return -1; + } + } else if (str_same(cfg_line->type, "sdcard")) { + if (fill_sdcard_cfg(attr) != 0) { + return -1; + } + } else if (str_same(cfg_line->type, "in_out")) { + if (fill_codec_cfg(attr, CODEC_DIR_IN_OUT) != 0) { + return -1; + } + } else if (str_same(cfg_line->type, "out")) { + if (fill_codec_cfg(attr, CODEC_DIR_OUT) != 0) { + return -1; + } + } else if (str_same(cfg_line->type, "in")) { + if (fill_codec_cfg(attr, CODEC_DIR_IN) != 0) { + return -1; + } + } else if (str_same(cfg_line->type, "lcd")) { + if (fill_lcd_cfg(attr) != 0) { + return -1; + } + } else if (str_same(cfg_line->type, "camera")) { + if (fill_camera_cfg(attr) != 0) { + return -1; + } + } + return 0; +} + +static int parse_cfg(const char *section, int size) +{ + int consume = 0; + while (1) { + board_cfg_line_t *cfg_line = parse_section(section, size, &consume); + if (cfg_line == NULL) { + break; + } + size -= consume; + section += consume; + print_cfg_line(cfg_line); + int ret = fill_cfg(cfg_line); + free_cfg_line(cfg_line); + if (ret != 0) { + printf("Fail to parse cfg line\n"); + return ret; + } + } + return 0; +} + +board_section_t *get_codec_section(const char *codec_type) +{ + if (codec_section) { + free(codec_section); + } + codec_section = calloc(1, sizeof(board_section_t)); + if (codec_type == NULL) { + return NULL; + } + int cfg_size = board_cfg_end - board_cfg_start; + do { + const char *section = get_section_data(board_cfg_start, cfg_size, codec_type); + if (section == NULL) { + break; + } + int left_size = cfg_size - (section - board_cfg_start); + if (parse_cfg(section, left_size) != 0) { + break; + } + return codec_section; + } while (0); + free(codec_section); + return NULL; +} diff --git a/components/third_party/codec_board/codec_board.c b/components/third_party/codec_board/codec_board.c new file mode 100644 index 0000000..841111f --- /dev/null +++ b/components/third_party/codec_board/codec_board.c @@ -0,0 +1,98 @@ +#include "codec_board.h" +#include "esp_log.h" + +#define TAG "BOARD" + +board_section_t *get_codec_section(const char *codec_type); + +static board_section_t *codec; + +#define RET_ON_NOT_INIT() if (codec == NULL) { \ + return -1; \ +} + +void set_codec_board_type(const char *codec_type) +{ + codec = get_codec_section(codec_type); +} + +int get_sdcard_config(sdcard_cfg_t *card_cfg) +{ + RET_ON_NOT_INIT(); + if (codec->sdcard_num == 0) { + ESP_LOGE(TAG, "Sdcard not exits on board"); + return -1; + } + memcpy(card_cfg, &codec->sdcard, sizeof(sdcard_cfg_t)); + return 0; +} + +int get_i2c_pin(uint8_t port, codec_i2c_pin_t *i2c_pin) +{ + RET_ON_NOT_INIT(); + if (port > codec->i2c_num) { + ESP_LOGE(TAG, "I2C %d not exits on board", port); + return -1; + } + memcpy(i2c_pin, &codec->i2c_pin[port], sizeof(codec_i2c_pin_t)); + return 0; +} + +int get_i2s_pin(uint8_t port, codec_i2s_pin_t *i2s_pin) +{ + RET_ON_NOT_INIT(); + if (port > codec->i2s_num) { + ESP_LOGE(TAG, "I2S %d not exits on board", port); + return -1; + } + memcpy(i2s_pin, &codec->i2s_pin[port], sizeof(codec_i2s_pin_t)); + return 0; +} + +int get_out_codec_cfg(codec_cfg_t *out_cfg) +{ + RET_ON_NOT_INIT(); + for (int i = 0; i < codec->codec_num; i++) { + if (codec->codec[i].codec_dir & CODEC_DIR_OUT) { + memcpy(out_cfg, &codec->codec[i].codec_cfg, sizeof(codec_cfg_t)); + return 0; + } + } + ESP_LOGE(TAG, "Output codec not exits on board"); + return -1; +} + +int get_in_codec_cfg(codec_cfg_t *in_cfg) +{ + RET_ON_NOT_INIT(); + for (int i = 0; i < codec->codec_num; i++) { + if (codec->codec[i].codec_dir & CODEC_DIR_IN) { + memcpy(in_cfg, &codec->codec[i].codec_cfg, sizeof(codec_cfg_t)); + return 0; + } + } + ESP_LOGE(TAG, "Input codec not exits on board"); + return -1; +} + +int get_camera_cfg(camera_cfg_t *cam_cfg) +{ + RET_ON_NOT_INIT(); + if (codec->camera_num == 0) { + ESP_LOGE(TAG, "Camera not exits on board"); + return -1; + } + memcpy(cam_cfg, &codec->camera, sizeof(camera_cfg_t)); + return 0; +} + +int get_lcd_cfg(lcd_cfg_t *lcd_cfg) +{ + RET_ON_NOT_INIT(); + if (codec->lcd_num) { + memcpy(lcd_cfg, &codec->lcd, sizeof(lcd_cfg_t)); + return 0; + } + ESP_LOGE(TAG, "LCD not exits on board"); + return -1; +} diff --git a/components/third_party/codec_board/codec_init.c b/components/third_party/codec_board/codec_init.c new file mode 100644 index 0000000..8f8c242 --- /dev/null +++ b/components/third_party/codec_board/codec_init.c @@ -0,0 +1,602 @@ +#include +#include +#include "driver/i2c.h" +#include "esp_idf_version.h" +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +#include "driver/i2s_std.h" +#include "driver/i2s_tdm.h" +#include "soc/soc_caps.h" +#else +#include "driver/i2s.h" +#endif +#include "esp_codec_dev.h" +#include "esp_codec_dev_defaults.h" +#include "esp_codec_dev_os.h" +#include "codec_board.h" +#include "codec_init.h" +#include "esp_log.h" +#include "dummy_codec.h" +#include "driver/i2c_master.h" +#include "esp_vfs_fat.h" +#include "driver/sdmmc_host.h" +#include "driver/sdmmc_defs.h" + +#define TAG "CODEC_INIT" + +typedef struct { + bool inited; + const audio_codec_data_if_t *data_if; + const audio_codec_data_if_t *data_in_if; + const audio_codec_gpio_if_t *gpio_if; + const audio_codec_ctrl_if_t *in_ctrl_if; + const audio_codec_ctrl_if_t *out_ctrl_if; + const audio_codec_if_t *out_codec_if; + const audio_codec_if_t *in_codec_if; + esp_codec_dev_handle_t play_dev; + esp_codec_dev_handle_t record_dev; +} codec_res_t; + +#define USE_I2C_MASTER +typedef struct { + bool inited; +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + i2s_chan_handle_t tx_handle; + i2s_chan_handle_t rx_handle; +#endif +} i2s_keep_t; + +static bool i2c_inited[MAX_I2C_NUM]; +static i2c_master_bus_handle_t i2c_bus_handle[MAX_I2C_NUM]; +static i2s_keep_t *i2s_keep[MAX_I2S_NUM]; +static codec_res_t codec_res; + +static int _i2c_init(uint8_t port) +{ + if (port >= MAX_I2C_NUM) { + return -1; + } + if (i2c_inited[port]) { + return 0; + } + codec_i2c_pin_t i2c_pin; + if (get_i2c_pin(port, &i2c_pin)) { + ESP_LOGE(TAG, "Fail to get i2c pin"); + return -1; + } + // workaround to check i2c initialized already + int ret = 0; +#ifdef USE_I2C_MASTER + i2c_master_bus_config_t i2c_bus_config = { 0 }; + i2c_bus_config.clk_source = I2C_CLK_SRC_DEFAULT; + i2c_bus_config.i2c_port = port; + i2c_bus_config.scl_io_num = i2c_pin.scl; + i2c_bus_config.sda_io_num = i2c_pin.sda; + i2c_bus_config.glitch_ignore_cnt = 7; + i2c_bus_config.flags.enable_internal_pullup = true; + ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle[port]); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "failed to initialize I2C master bus port %d", port); + return -1; + } + ESP_LOGI(TAG, "Set mater handle %d %p", port, i2c_bus_handle[port]); +#else + i2c_config_t i2c_cfg = { + .mode = I2C_MODE_MASTER, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .master.clk_speed = 100000, + }; + i2c_cfg.sda_io_num = i2c_pin.sda; + i2c_cfg.scl_io_num = i2c_pin.scl; + ret = i2c_param_config(port, &i2c_cfg); + if (ret != ESP_OK) { + return -1; + } + ret = i2c_driver_install(port, i2c_cfg.mode, 0, 0, 0); +#endif + if (ret == 0) { + i2c_inited[port] = true; + } + return ret; +} + +void *get_i2c_bus_handle(uint8_t port) +{ + ESP_LOGI(TAG, "Get mater handle %d %p", port, i2c_bus_handle[port]); + return i2c_bus_handle[port]; +} + +static int _i2c_deinit(uint8_t port) +{ + if (port >= MAX_I2C_NUM || i2c_inited[port] == false) { + return -1; + } +#ifdef USE_I2C_MASTER + i2c_del_master_bus(i2c_bus_handle[port]); +#else + i2c_driver_delete(port); +#endif + i2c_bus_handle[port] = NULL; + i2c_inited[port] = false; + return 0; +} + +static int _i2s_init(uint8_t port, esp_codec_dev_type_t dev_type, codec_init_cfg_t *init_cfg) +{ + if (port >= MAX_I2S_NUM) { + return -1; + } + if (i2s_keep[port]) { + return 0; + } + codec_i2s_pin_t i2s_cfg; + if (get_i2s_pin(port, &i2s_cfg)) { + ESP_LOGE(TAG, "Fail to get i2s pin"); + return -1; + } + ESP_LOGI(TAG, "Init i2s %d type: %d mclk:%d bclk:%d ws:%d din:%d dout:%d", + port, dev_type, i2s_cfg.mclk, + i2s_cfg.bclk, i2s_cfg.ws, i2s_cfg.din, i2s_cfg.dout); + i2s_keep[port] = (i2s_keep_t *)calloc(1, sizeof(i2s_keep_t)); + if (i2s_keep[port] == NULL) { + return -1; + } +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER); + chan_cfg.auto_clear = true; + i2s_std_config_t std_cfg = { + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000), + .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(32, I2S_SLOT_MODE_STEREO), + .gpio_cfg = { + .mclk = i2s_cfg.mclk, + .bclk = i2s_cfg.bclk, + .ws = i2s_cfg.ws, + .dout = i2s_cfg.dout, + .din = i2s_cfg.din, + }, + }; + bool output = (dev_type & ESP_CODEC_DEV_TYPE_OUT) > 0; + bool input = (dev_type & ESP_CODEC_DEV_TYPE_IN) > 0; + // Same I2S have tx and rx but use rx or tx only + if (output && init_cfg->out_mode == CODEC_I2S_MODE_NONE) { + output = false; + } + if (input && init_cfg->in_mode == CODEC_I2S_MODE_NONE) { + input = false; + } + if (input == false && output == false) { + return 0; + } +#ifdef SOC_I2S_SUPPORTS_TDM + i2s_tdm_slot_mask_t slot_mask = I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3; + i2s_tdm_config_t tdm_cfg = { + .slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(32, I2S_SLOT_MODE_STEREO, slot_mask), + .clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000), + .gpio_cfg = { + .mclk = i2s_cfg.mclk, + .bclk = i2s_cfg.bclk, + .ws = i2s_cfg.ws, + .dout = i2s_cfg.dout, + .din = i2s_cfg.din, + }, + }; + tdm_cfg.slot_cfg.total_slot = 4; +#endif + int ret = i2s_new_channel(&chan_cfg, output == false ? NULL : &i2s_keep[port]->tx_handle, + input == false ? NULL : &i2s_keep[port]->rx_handle); + ESP_LOGI(TAG, "tx:%p rx:%p", i2s_keep[port]->tx_handle, i2s_keep[port]->rx_handle); + if (i2s_keep[port]->tx_handle) { + if (init_cfg->out_mode == CODEC_I2S_MODE_STD) { + ret = i2s_channel_init_std_mode(i2s_keep[port]->tx_handle, &std_cfg); + ESP_LOGI(TAG, "output init std ret %d", ret); + } +#ifdef SOC_I2S_SUPPORTS_TDM + else if (init_cfg->out_mode == CODEC_I2S_MODE_TDM) { + ret = i2s_channel_init_tdm_mode(i2s_keep[port]->tx_handle, &tdm_cfg); + ESP_LOGI(TAG, "output init tdm ret %d", ret); + } +#endif + } + if (i2s_keep[port]->rx_handle) { + if (init_cfg->in_mode == CODEC_I2S_MODE_STD) { + ret = i2s_channel_init_std_mode(i2s_keep[port]->rx_handle, &std_cfg); + ESP_LOGI(TAG, "Input init std ret %d", ret); + } +#ifdef SOC_I2S_SUPPORTS_TDM + else if (init_cfg->in_mode == CODEC_I2S_MODE_TDM) { + ret = i2s_channel_init_tdm_mode(i2s_keep[port]->rx_handle, &tdm_cfg); + ESP_LOGI(TAG, "Input init tdm ret %d", ret); + } +#endif + } + // Enable I2S here for maybe some codec need I2S clock to set register correctly + if (i2s_keep[port]->tx_handle) { + i2s_channel_enable(i2s_keep[port]->tx_handle); + } + if (i2s_keep[port]->rx_handle) { + i2s_channel_enable(i2s_keep[port]->rx_handle); + } +#else + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), + .sample_rate = 44100, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM, + .dma_buf_count = 3, + .dma_buf_len = 300, + .use_apll = true, + .tx_desc_auto_clear = true, + .fixed_mclk = 0 + //.mclk_multiple = 256, + }; + int ret = i2s_driver_install(port, &i2s_config, 0, NULL); + i2s_pin_config_t i2s_pin_cfg = { + .mck_io_num = i2s_cfg.mclk, + .bck_io_num = i2s_cfg.bclk, + .ws_io_num = i2s_cfg.ws, + .data_out_num = i2s_cfg.dout, + .data_in_num = i2s_cfg.din, + }; + i2s_set_pin(port, &i2s_pin_cfg); +#endif + ESP_LOGI(TAG, "Init i2s %d ok", port); + i2s_keep[port]->inited = true; + return ret; +} + +static int _i2s_deinit(uint8_t port) +{ + if (port >= MAX_I2S_NUM) { + return -1; + } + if (i2s_keep[port] == NULL) { + return 0; + } +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + if (i2s_keep[port]->tx_handle) { + i2s_channel_disable(i2s_keep[port]->tx_handle); + } + if (i2s_keep[port]->rx_handle) { + i2s_channel_disable(i2s_keep[port]->rx_handle); + } + if (i2s_keep[port]->tx_handle) { + i2s_del_channel(i2s_keep[port]->tx_handle); + } + if (i2s_keep[port]->rx_handle) { + i2s_del_channel(i2s_keep[port]->rx_handle); + } +#else + i2s_driver_uninstall(port); +#endif + free(i2s_keep[port]); + i2s_keep[port] = NULL; + return 0; +} + +int init_codec(codec_init_cfg_t *cfg) +{ + if (cfg == NULL) { + return -1; + } + if (codec_res.inited) { + ESP_LOGI(TAG, "Already initialized"); + return 0; + } + codec_cfg_t out_cfg = { 0 }; + codec_cfg_t in_cfg = { 0 }; + bool has_out = false; + bool has_in = false; + // Get codec configuration + if (get_out_codec_cfg(&out_cfg) == 0) { + has_out = true; + } + if (get_in_codec_cfg(&in_cfg) == 0) { + has_in = true; + } + if (has_out == false && has_in == false) { + ESP_LOGE(TAG, "No codec device found"); + return -1; + } + // Force to init I2C firstly + _i2c_init(0); + // Init i2c and i2s + bool same_i2s = (has_in && has_out && out_cfg.i2s_port == in_cfg.i2s_port); + ESP_LOGI(TAG, "in:%d out:%d port: %d", has_in, has_out, out_cfg.i2s_port == in_cfg.i2s_port); + if (has_out) { + if (out_cfg.i2c_port >= 0 && _i2c_init(out_cfg.i2c_port)) { + ESP_LOGE(TAG, "Fail to int i2c: %d", out_cfg.i2c_port); + return -1; + } + ESP_LOGI(TAG, "Success to int i2c: %d", in_cfg.i2c_port); + if (_i2s_init(out_cfg.i2s_port, same_i2s ? ESP_CODEC_DEV_TYPE_IN_OUT : ESP_CODEC_DEV_TYPE_OUT, cfg)) { + ESP_LOGE(TAG, "Fail to init i2s: %d", out_cfg.i2s_port); + return -1; + } + ESP_LOGI(TAG, "Success to init i2s: %d", in_cfg.i2s_port); + } + if (has_in) { + if (in_cfg.i2c_port >= 0 && _i2c_init(in_cfg.i2c_port)) { + ESP_LOGE(TAG, "Fail to int i2c: %d", in_cfg.i2c_port); + return -1; + } + ESP_LOGI(TAG, "Success to int i2c: %d", in_cfg.i2c_port); + if (_i2s_init(in_cfg.i2s_port, same_i2s ? ESP_CODEC_DEV_TYPE_IN_OUT : ESP_CODEC_DEV_TYPE_IN, cfg)) { + ESP_LOGE(TAG, "Fail to init i2s: %d", in_cfg.i2s_port); + return -1; + } + ESP_LOGI(TAG, "Success to init i2s: %d", in_cfg.i2s_port); + } + // Create gpio interface + codec_res.gpio_if = audio_codec_new_gpio(); + + bool same_codec = same_i2s && (in_cfg.codec_type == out_cfg.codec_type); + if (has_out) { + audio_codec_i2s_cfg_t i2s_out_cfg = { + .port = out_cfg.i2s_port, +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + .tx_handle = i2s_keep[out_cfg.i2s_port]->tx_handle, + .rx_handle = i2s_keep[out_cfg.i2s_port]->rx_handle, +#endif + }; + ESP_LOGI(TAG, "Get out handle %p port %d", i2s_out_cfg.tx_handle, out_cfg.i2s_port); + codec_res.data_if = audio_codec_new_i2s_data(&i2s_out_cfg); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = out_cfg.i2c_port, +#ifdef USE_I2C_MASTER + .bus_handle = i2c_bus_handle[out_cfg.i2c_port], +#endif + }; + // TODO add other codec support + switch (out_cfg.codec_type) { + case CODEC_TYPE_ES8311: { + i2c_cfg.addr = out_cfg.i2c_addr ? out_cfg.i2c_addr : ES8311_CODEC_DEFAULT_ADDR; + codec_res.out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + es8311_codec_cfg_t es8311_cfg = { + .codec_mode = same_codec ? ESP_CODEC_DEV_WORK_MODE_BOTH : ESP_CODEC_DEV_WORK_MODE_DAC, + .ctrl_if = codec_res.out_ctrl_if, + .gpio_if = codec_res.gpio_if, + .pa_pin = out_cfg.pa_pin, + .use_mclk = out_cfg.use_mclk, + .hw_gain.pa_gain = out_cfg.pa_gain, + }; + codec_res.out_codec_if = es8311_codec_new(&es8311_cfg); + } break; + case CODEC_TYPE_ES8388: { + i2c_cfg.addr = out_cfg.i2c_addr ? out_cfg.i2c_addr : ES8388_CODEC_DEFAULT_ADDR; + codec_res.out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + es8388_codec_cfg_t es8388_cfg = { + .codec_mode = same_codec ? ESP_CODEC_DEV_WORK_MODE_BOTH : ESP_CODEC_DEV_WORK_MODE_DAC, + .ctrl_if = codec_res.out_ctrl_if, + .gpio_if = codec_res.gpio_if, + .pa_pin = out_cfg.pa_pin, + .hw_gain.pa_gain = out_cfg.pa_gain, + }; + codec_res.out_codec_if = es8388_codec_new(&es8388_cfg); + } break; + case CODEC_TYPE_DUMMY: { + dummy_codec_cfg_t dummy_cfg = { + .gpio_if = codec_res.gpio_if, + .enable_gpio = out_cfg.pa_pin, + }; + codec_res.out_codec_if = dummy_codec_new(&dummy_cfg); + } break; + default: + ESP_LOGE(TAG, "TODO not supported output codec type %d", out_cfg.codec_type); + break; + } + esp_codec_dev_cfg_t dev_cfg = { + .codec_if = codec_res.out_codec_if, + .data_if = codec_res.data_if, + .dev_type = ESP_CODEC_DEV_TYPE_OUT, + }; + if (same_codec && cfg->reuse_dev) { + dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT; + } + codec_res.play_dev = esp_codec_dev_new(&dev_cfg); + if (same_codec) { + if (cfg->reuse_dev) { + codec_res.record_dev = codec_res.play_dev; + } else { + // separate record from playback so that can set different fs + dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN; + codec_res.record_dev = esp_codec_dev_new(&dev_cfg); + } + } + } + if (same_codec == false && has_in) { + if (same_i2s == false) { + audio_codec_i2s_cfg_t i2s_in_cfg = { + .port = in_cfg.i2s_port, +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + .rx_handle = i2s_keep[in_cfg.i2s_port]->rx_handle, +#endif + }; + ESP_LOGI(TAG, "Get in handle %p port %d", i2s_in_cfg.rx_handle, i2s_in_cfg.port); + codec_res.data_in_if = audio_codec_new_i2s_data(&i2s_in_cfg); + } + audio_codec_i2c_cfg_t i2c_cfg = { + .port = in_cfg.i2c_port, +#ifdef USE_I2C_MASTER + .bus_handle = i2c_bus_handle[in_cfg.i2c_port], +#endif + }; + // TODO add other codec support + switch (in_cfg.codec_type) { + case CODEC_TYPE_ES7210: { + i2c_cfg.addr = in_cfg.i2c_addr ? in_cfg.i2c_addr : ES7210_CODEC_DEFAULT_ADDR; + codec_res.in_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + es7210_codec_cfg_t es7210_cfg = { + .ctrl_if = codec_res.in_ctrl_if, + .mic_selected = ES7120_SEL_MIC1 | ES7120_SEL_MIC3, + }; + if (cfg->in_use_tdm || (cfg->in_mode == CODEC_I2S_MODE_TDM)) { + es7210_cfg.mic_selected |= ES7120_SEL_MIC2 | ES7120_SEL_MIC4; + } + codec_res.in_codec_if = es7210_codec_new(&es7210_cfg); + } break; + + case CODEC_TYPE_ES7243: { + i2c_cfg.addr = in_cfg.i2c_addr ? in_cfg.i2c_addr : ES7243_CODEC_DEFAULT_ADDR; + codec_res.in_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + es7243_codec_cfg_t es7243_cfg = { + .ctrl_if = codec_res.in_ctrl_if, + }; + codec_res.in_codec_if = es7243_codec_new(&es7243_cfg); + } break; + + case CODEC_TYPE_DUMMY: { + dummy_codec_cfg_t dummy_cfg = { + .gpio_if = codec_res.gpio_if, + .enable_gpio = out_cfg.pa_pin, + }; + codec_res.in_codec_if = dummy_codec_new(&dummy_cfg); + } + default: + break; + } + esp_codec_dev_cfg_t dev_cfg = { + .codec_if = codec_res.in_codec_if, + .data_if = same_i2s ? codec_res.data_if : codec_res.data_in_if, + .dev_type = ESP_CODEC_DEV_TYPE_IN, + }; + codec_res.record_dev = esp_codec_dev_new(&dev_cfg); + } + int ret = esp_codec_dev_set_out_vol(codec_res.play_dev, 60.0); + ret = esp_codec_dev_set_in_gain(codec_res.record_dev, 30.0); + if (ret == 0) { + codec_res.inited = true; + } + return ret; +} + +esp_codec_dev_handle_t get_playback_handle(void) +{ + return codec_res.play_dev; +} + +esp_codec_dev_handle_t get_record_handle(void) +{ + return codec_res.record_dev; +} + +void deinit_codec(void) +{ + if (codec_res.play_dev) { + esp_codec_dev_delete(codec_res.play_dev); + if (codec_res.record_dev == codec_res.play_dev) { + codec_res.record_dev = NULL; + } + codec_res.play_dev = NULL; + } + if (codec_res.record_dev) { + esp_codec_dev_delete(codec_res.record_dev); + codec_res.record_dev = NULL; + } + // Delete codec interface + if (codec_res.in_codec_if) { + audio_codec_delete_codec_if(codec_res.in_codec_if); + codec_res.in_codec_if = NULL; + } + if (codec_res.out_codec_if) { + audio_codec_delete_codec_if(codec_res.out_codec_if); + codec_res.out_codec_if = NULL; + } + // Delete codec control interface + if (codec_res.in_ctrl_if) { + audio_codec_delete_ctrl_if(codec_res.in_ctrl_if); + codec_res.in_ctrl_if = NULL; + } + if (codec_res.out_ctrl_if) { + audio_codec_delete_ctrl_if(codec_res.out_ctrl_if); + codec_res.out_ctrl_if = NULL; + } + if (codec_res.data_if) { + audio_codec_delete_data_if(codec_res.data_if); + codec_res.data_if = NULL; + } + if (codec_res.data_in_if) { + audio_codec_delete_data_if(codec_res.data_in_if); + codec_res.data_in_if = NULL; + } + if (codec_res.gpio_if) { + audio_codec_delete_gpio_if(codec_res.gpio_if); + codec_res.gpio_if = NULL; + } + for (int i = 0; i < MAX_I2C_NUM; i++) { + _i2c_deinit(i); + _i2s_deinit(i); + } + codec_res.inited = false; +} + +static sdmmc_card_t *card = NULL; + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) +#if CONFIG_IDF_TARGET_ESP32P4 +#include "esp_ldo_regulator.h" +#endif +#endif + +static void enable_mmc_phy_power(void) +{ +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) +#if CONFIG_IDF_TARGET_ESP32P4 + esp_ldo_channel_config_t ldo_cfg = { + .chan_id = 4, + .voltage_mv = 3300, + }; + esp_ldo_channel_handle_t ldo_phy_chan; + esp_ldo_acquire_channel(&ldo_cfg, &ldo_phy_chan); +#endif +#endif +} + +int mount_sdcard(void) +{ + sdcard_cfg_t cfg = { 0 }; + enable_mmc_phy_power(); + int ret = get_sdcard_config(&cfg); + if (ret != 0) { + return ret; + } +#if SOC_SDMMC_HOST_SUPPORTED + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = false, + .max_files = 5, + }; + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); +#if CONFIG_IDF_TARGET_ESP32P4 + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; +#endif + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + slot_config.width = cfg.d3 ? 4 : 1; + slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; + slot_config.d4 = -1; + slot_config.d5 = -1; + slot_config.d6 = -1; + slot_config.d7 = -1; + slot_config.cd = -1; + slot_config.wp = -1; + slot_config.clk = cfg.clk; + slot_config.cmd = cfg.cmd; + slot_config.d0 = cfg.d0; + slot_config.d1 = cfg.d1 ? cfg.d1 : -1; + slot_config.d2 = cfg.d2 ? cfg.d2 : -1; + slot_config.d3 = cfg.d3 ? cfg.d3 : -1; + printf("use %d %d %d %d\n", cfg.d0, cfg.d1, cfg.d2, cfg.d3); + return esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card); +#else + return -1; +#endif +} + +void unmount_sdcard() +{ + if (card) { + esp_vfs_fat_sdcard_unmount("/sdcard", card); + card = NULL; + } +} diff --git a/components/third_party/codec_board/drv/tca9554.c b/components/third_party/codec_board/drv/tca9554.c new file mode 100644 index 0000000..d098a0e --- /dev/null +++ b/components/third_party/codec_board/drv/tca9554.c @@ -0,0 +1,178 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include "esp_log.h" +#include "tca9554.h" +#include "codec_init.h" +#include "audio_codec_ctrl_if.h" +#include "esp_codec_dev_defaults.h" + +static char *TAG = "TCA9554"; + +#define SET_BITS(_m, _s, _v) ((_v) ? (_m)|((_s)) : (_m)&~((_s))) +#define GET_BITS(_m, _s) (((_m) & (_s)) ? true : false) + +#define TCA9554_INPUT_PORT 0x00 +#define TCA9554_OUTPUT_PORT 0x01 +#define TCA9554_POLARITY_INVERSION_PORT 0x02 +#define TCA9554_CONFIGURATION_PORT 0x03 + +typedef struct { + uint8_t addr; + char *name; +} tca9554_dev_t; + +static tca9554_dev_t dev_list[] = { + { 0x70, "TCA9554A"}, + { 0x40, "TCA9554"}, +}; + +const static audio_codec_ctrl_if_t *i2c_ctrl; + +static esp_err_t expander_dev_prob(uint8_t port) +{ + audio_codec_i2c_cfg_t i2c_cfg = { + .port = port, + }; + for (size_t i = 0; i < sizeof(dev_list) / sizeof(dev_list[0]); i++) { + i2c_cfg.addr = dev_list[i].addr; + i2c_cfg.bus_handle = get_i2c_bus_handle(port); + i2c_ctrl = audio_codec_new_i2c_ctrl(&i2c_cfg); + uint8_t data = 0; + if (i2c_ctrl->read_reg(i2c_ctrl, TCA9554_OUTPUT_PORT, 1, &data, 1) == 0) { + ESP_LOGI(TAG, "Detected IO expander device at 0x%02X, name is: %s", + dev_list[i].addr, dev_list[i].name); + return ESP_OK; + } + audio_codec_delete_ctrl_if(i2c_ctrl); + i2c_ctrl = NULL; + } + ESP_LOGE(TAG, "IO expander device has not detected"); + return ESP_ERR_NOT_FOUND; +} + +static esp_err_t tca9554_write_reg(uint8_t reg_addr, uint8_t data) +{ + return i2c_ctrl->write_reg(i2c_ctrl, reg_addr, 1, &data, 1); +} + +static char tca9554_read_reg(uint8_t reg_addr) +{ + uint8_t data; + i2c_ctrl->read_reg(i2c_ctrl, reg_addr, 1, &data, 1); + return data; +} + + +esp_tca9554_io_level_t tca9554_get_input_state(esp_tca9554_gpio_num_t gpio_num) +{ + char data = 0; + if (gpio_num < TCA9554_GPIO_NUM_MAX) { + data = tca9554_read_reg(TCA9554_INPUT_PORT); + } else { + ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num); + return TCA9554_LEVEL_ERROR; + } + return GET_BITS(data, gpio_num); +} + +esp_tca9554_io_level_t tca9554_get_output_state(esp_tca9554_gpio_num_t gpio_num) +{ + char data = 0; + if (gpio_num < TCA9554_GPIO_NUM_MAX) { + data = tca9554_read_reg(TCA9554_OUTPUT_PORT); + } else { + ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num); + return TCA9554_LEVEL_ERROR; + } + + return GET_BITS(data, gpio_num); +} + +esp_err_t tca9554_set_output_state(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_level_t level) +{ + char data; + esp_err_t res = ESP_FAIL; + if (gpio_num < TCA9554_GPIO_NUM_MAX) { + data = tca9554_read_reg(TCA9554_OUTPUT_PORT); + res = tca9554_write_reg(TCA9554_OUTPUT_PORT, SET_BITS(data, gpio_num, level)); + } else { + ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num); + } + return res; +} + +esp_err_t tca9554_set_polarity_inversion(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_polarity_t polarity) +{ + char data; + esp_err_t res = ESP_FAIL; + if (gpio_num < TCA9554_GPIO_NUM_MAX) { + data = tca9554_read_reg(TCA9554_POLARITY_INVERSION_PORT); + res = tca9554_write_reg(TCA9554_POLARITY_INVERSION_PORT, SET_BITS(data, gpio_num, polarity)); + } else { + ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num); + } + return res; +} + +esp_tca9554_io_config_t tca9554_get_io_config(esp_tca9554_gpio_num_t gpio_num) +{ + char data = 0; + if (gpio_num < TCA9554_GPIO_NUM_MAX) { + data = tca9554_read_reg(TCA9554_CONFIGURATION_PORT); + } else { + ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num); + return TCA9554_LEVEL_ERROR; + } + + return GET_BITS(data, gpio_num); +} + +esp_err_t tca9554_set_io_config(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_config_t io_config) +{ + char data; + esp_err_t res = ESP_FAIL; + if (gpio_num < TCA9554_GPIO_NUM_MAX) { + data = tca9554_read_reg(TCA9554_CONFIGURATION_PORT); + res = tca9554_write_reg(TCA9554_CONFIGURATION_PORT, SET_BITS(data, gpio_num, io_config)); + } else { + ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num); + } + return res; +} + +esp_err_t tca9554_init(uint8_t port) +{ + return expander_dev_prob(port); +} + +esp_err_t tca9554_deinit() +{ + if (i2c_ctrl) { + audio_codec_delete_ctrl_if(i2c_ctrl); + i2c_ctrl = NULL; + } + return ESP_OK; +} diff --git a/components/third_party/codec_board/drv/tca9554.h b/components/third_party/codec_board/drv/tca9554.h new file mode 100644 index 0000000..14bfe07 --- /dev/null +++ b/components/third_party/codec_board/drv/tca9554.h @@ -0,0 +1,158 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef _TCA9554_H +#define _TCA9554_H + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + TCA9554_GPIO_NUM_0 = 1 << 0, + TCA9554_GPIO_NUM_1 = 1 << 1, + TCA9554_GPIO_NUM_2 = 1 << 2, + TCA9554_GPIO_NUM_3 = 1 << 3, + TCA9554_GPIO_NUM_4 = 1 << 4, + TCA9554_GPIO_NUM_5 = 1 << 5, + TCA9554_GPIO_NUM_6 = 1 << 6, + TCA9554_GPIO_NUM_7 = 1 << 7, + TCA9554_GPIO_NUM_MAX +} esp_tca9554_gpio_num_t; + +typedef enum { + TCA9554_IO_LOW, + TCA9554_IO_HIGH, + TCA9554_LEVEL_ERROR +} esp_tca9554_io_level_t; + +typedef enum { + TCA9554_IO_RETAINED, + TCA9554_IO_INVERTED +} esp_tca9554_io_polarity_t; + +typedef enum { + TCA9554_IO_OUTPUT, + TCA9554_IO_INPUT +} esp_tca9554_io_config_t; + +/* + * @brief Initialize TCA9554 chip + * + * @param codec_cfg configuration of TCA9554 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t tca9554_init(uint8_t i2c_port); + +/** + * @brief Deinitialize TCA9554 chip + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t tca9554_deinit(void); + +/* + * @brief Get TCA9554 input level + * + * @param gpio_num GPIO of TCA9554 + * + * @return + * - esp_tca9554_io_level_t + */ +esp_tca9554_io_level_t tca9554_get_input_state(esp_tca9554_gpio_num_t gpio_num); + +/* + * @brief Get PCA95xian39 output level + * + * @param gpio_num GPIO of TCA9554 + * + * @return + * - esp_tca9554_io_level_t + */ +esp_tca9554_io_level_t tca9554_get_output_state(esp_tca9554_gpio_num_t gpio_num); + +/* + * @brief Get TCA9554 output state + * + * @param gpio_num GPIO of TCA9554 + * + * @return + * - esp_tca9554_io_level_t + */ +esp_err_t tca9554_set_output_state(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_level_t level); + +/* + * @brief Set TCA9554 polarity + * + * @param gpio_num GPIO of TCA9554 + * polarity polarity + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t tca9554_set_polarity_inversion(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_polarity_t polarity); + +/* + * @brief Get TCA9554 output level + * + * @param gpio_num GPIO of TCA9554 + * + * @return + * - esp_tca9554_io_level_t + */ +esp_tca9554_io_config_t tca9554_get_io_config(esp_tca9554_gpio_num_t gpio_num); + +/* + * @brief Set TCA9554 io config + * + * @param gpio_num GPIO of TCA9554 + * io_config io config + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t tca9554_set_io_config(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_config_t io_config); + +/** + * @brief Print all TCA9554 registers + * + * @return + * - void + */ +void tca9554_read_all(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/codec_board/dummy_codec.c b/components/third_party/codec_board/dummy_codec.c new file mode 100644 index 0000000..aa04a06 --- /dev/null +++ b/components/third_party/codec_board/dummy_codec.c @@ -0,0 +1,72 @@ + +#include "dummy_codec.h" + +typedef struct { + audio_codec_if_t base; + const audio_codec_gpio_if_t *gpio_if; + bool is_open; + bool enable; +} dummy_codec_t; + +typedef struct { + audio_codec_ctrl_if_t base; + bool is_open; +} dummy_codec_ctrl_t; + +static int dummy_codec_open(const audio_codec_if_t *h, void *cfg, int cfg_size) +{ + dummy_codec_cfg_t *codec_cfg = (dummy_codec_cfg_t *)cfg; + if (cfg_size != sizeof(dummy_codec_cfg_t) || codec_cfg->gpio_if == NULL) { + return -1; + } + dummy_codec_t *codec = (dummy_codec_t *)h; + codec->gpio_if = codec_cfg->gpio_if; + codec->gpio_if->setup(codec_cfg->enable_gpio, AUDIO_GPIO_DIR_OUT, AUDIO_GPIO_MODE_FLOAT); + codec->gpio_if->set(codec_cfg->enable_gpio, true); + codec->is_open = true; + return 0; +} + +static bool dummy_codec_is_open(const audio_codec_if_t *h) +{ + dummy_codec_t *codec = (dummy_codec_t *)h; + return codec->is_open; +} + +static int dummy_codec_enable(const audio_codec_if_t *h, bool enable) +{ + dummy_codec_t *codec = (dummy_codec_t *)h; + codec->enable = enable; + return 0; +} + +static int dummy_codec_set_fs(const audio_codec_if_t *h, esp_codec_dev_sample_info_t *fs) +{ + return 0; +} + +static int dummy_codec_close(const audio_codec_if_t *h) +{ + dummy_codec_t *codec = (dummy_codec_t *)h; + // Auto disable when codec closed + if (codec->enable) { + dummy_codec_enable(h, false); + } + codec->is_open = false; + return 0; +} + +const audio_codec_if_t *dummy_codec_new(dummy_codec_cfg_t *codec_cfg) +{ + dummy_codec_t *codec = (dummy_codec_t *)calloc(1, sizeof(dummy_codec_t)); + if (codec == NULL) { + return NULL; + } + codec->base.open = dummy_codec_open; + codec->base.is_open = dummy_codec_is_open; + codec->base.enable = dummy_codec_enable; + codec->base.set_fs = dummy_codec_set_fs; + codec->base.close = dummy_codec_close; + codec->base.open(&codec->base, codec_cfg, sizeof(dummy_codec_cfg_t)); + return &codec->base; +} \ No newline at end of file diff --git a/components/third_party/codec_board/dummy_codec.h b/components/third_party/codec_board/dummy_codec.h new file mode 100644 index 0000000..0d89913 --- /dev/null +++ b/components/third_party/codec_board/dummy_codec.h @@ -0,0 +1,33 @@ +#include "esp_codec_dev.h" +#include "audio_codec_ctrl_if.h" +#include "audio_codec_gpio_if.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Dummy codec configuration + */ +typedef struct { + int16_t enable_gpio; /*!< Enable GPIO setting */ + const audio_codec_gpio_if_t *gpio_if; /*!< GPIO interface to control gpio */ +} dummy_codec_cfg_t; + +/** + * @brief Create a dummy codec + * + * @note Dummy codec means use I2S to transfer audio data with out I2C control interface + * + * @param[in] codec_cfg Dummy codec configuration + * + * @return + * - NULL No memory for dummy codec + * - Others Dummy codec instance + * + */ +const audio_codec_if_t *dummy_codec_new(dummy_codec_cfg_t *codec_cfg); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/codec_board/extend_io.c b/components/third_party/codec_board/extend_io.c new file mode 100644 index 0000000..18fc2b8 --- /dev/null +++ b/components/third_party/codec_board/extend_io.c @@ -0,0 +1,68 @@ +#include +#include "tca9554.h" +#include "codec_board.h" +#include "extend_io.h" +#include "driver/gpio.h" + +typedef struct { + int (*init)(uint8_t io_i2c_port); + int (*set_dir)(int16_t gpio, bool output); + int (*set_gpio)(int16_t gpio, bool high); +} extend_io_ops_t; + +static extend_io_ops_t extend_io_ops; + +static int tca9554_io_init(uint8_t io_i2c_port) +{ + return tca9554_init(io_i2c_port); +} + +static int tca9554_io_set_dir(int16_t gpio, bool output) +{ + gpio = (1 << gpio); + tca9554_set_io_config(gpio, output ? TCA9554_IO_OUTPUT : TCA9554_IO_INPUT); + return 0; +} + +static int tca9554_io_set(int16_t gpio, bool high) +{ + gpio = (1 << gpio); + return tca9554_set_output_state(gpio, high ? TCA9554_IO_HIGH : TCA9554_IO_LOW); +} + +static void register_tca9554(void) +{ + extend_io_ops.init = tca9554_io_init; + extend_io_ops.set_dir = tca9554_io_set_dir; + extend_io_ops.set_gpio = tca9554_io_set; +} + +int extend_io_init(uint8_t io_i2c_port) +{ + register_tca9554(); + return extend_io_ops.init(io_i2c_port); +} + +int extend_io_set_pin_dir(int16_t pin, bool output) +{ + pin &= ~BOARD_EXTEND_IO_START; + extend_io_ops.set_dir(pin, output); + return 0; +} + +int extend_io_set_pin_state(int16_t pin, bool high) +{ + extend_io_ops.set_gpio(pin, high); + return 0; +} + +int16_t extend_io_get_hw_gpio(int16_t pin) +{ + if (pin == -1) { + return pin; + } + if (pin & BOARD_EXTEND_IO_START) { + return -1; + } + return pin; +} \ No newline at end of file diff --git a/components/third_party/codec_board/extend_io.h b/components/third_party/codec_board/extend_io.h new file mode 100644 index 0000000..d12aa5b --- /dev/null +++ b/components/third_party/codec_board/extend_io.h @@ -0,0 +1,16 @@ +#include + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +int extend_io_init(uint8_t io_i2c_port); +int extend_io_set_pin_dir(int16_t pin, bool output); +int extend_io_set_pin_state(int16_t pin, bool high); +int16_t extend_io_get_hw_gpio(int16_t pin); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/third_party/codec_board/idf_component.yml b/components/third_party/codec_board/idf_component.yml new file mode 100755 index 0000000..5d34d37 --- /dev/null +++ b/components/third_party/codec_board/idf_component.yml @@ -0,0 +1,11 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_codec_dev: "~1.3.4" + esp_lcd_ek79007: + version: "~1.0.2" + rules: + - if: "target in [esp32p4]" + espressif/esp_lcd_ili9881c: + version: "~1.0.1" + rules: + - if: "target in [esp32p4]" diff --git a/components/third_party/codec_board/include/codec_board.h b/components/third_party/codec_board/include/codec_board.h new file mode 100644 index 0000000..e4ff7f3 --- /dev/null +++ b/components/third_party/codec_board/include/codec_board.h @@ -0,0 +1,346 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum of supported I2C number + */ +#define MAX_I2C_NUM (2) + +/** + * @brief Maximum of supported I2S number + */ +#define MAX_I2S_NUM (2) + +/** + * @brief Maximum of supported codec number + */ +#define MAX_CODEC_NUM (2) + +/** + * @brief Codec input and output direction + */ +#define CODEC_DIR_IN (1 << 0) +#define CODEC_DIR_OUT (1 << 1) +#define CODEC_DIR_IN_OUT (CODEC_DIR_IN | CODEC_DIR_OUT) + +/** + * @brief Start of extended GPIO start + */ +#define BOARD_EXTEND_IO_START (0x1000) + +/** + * @brief Codec type + */ +typedef enum { + CODEC_TYPE_NONE, /*!< Codec type none */ + CODEC_TYPE_ES8311, /*!< ES8311 codec type */ + CODEC_TYPE_ES7210, /*!< ES7210 codec type */ + CODEC_TYPE_ES7243, /*!< ES7243 codec type */ + CODEC_TYPE_ES8388, /*!< ES8388 codec type */ + CODEC_TYPE_DUMMY, /*!< Dummy codec type (which have I2S interface only) */ +} codec_type_t; + +/** + * @brief Camera type + */ +typedef enum { + CAMERA_TYPE_NONE, /*!< Camera type none */ + CAMERA_TYPE_DVP, /*!< DVP camera type */ + CAMERA_TYPE_USB, /*!< USB camera type */ + CAMERA_TYPE_MIPI /*!< MIPI camera type */ +} camera_type_t; + +/** + * @brief I2C pin setting + */ +typedef struct { + int16_t sda; /*!< GPIO for SDA */ + int16_t scl; /*!< GPIO for SCL */ +} codec_i2c_pin_t; + +/** + * @brief I2S pin setting + */ +typedef struct { + int16_t mclk; /*!< GPIO for MCLK */ + int16_t bclk; /*!< GPIO for BCLK */ + int16_t ws; /*!< GPIO for Word Selction */ + int16_t dout; /*!< GPIO for digital output */ + int16_t din; /*!< GPIO for digital input */ +} codec_i2s_pin_t; + +/** + * @brief Codec configuration + */ +typedef struct { + codec_type_t codec_type; /*!< Codec type */ + int16_t pa_pin; /*!< GPIO for PA control */ + float pa_gain; /*!< PA gain */ + uint8_t i2c_addr; /*!< I2C address */ + int8_t i2c_port; /*!< I2C port */ + int8_t i2s_port; /*!< I2S port */ + bool use_mclk; /*!< Whether codec need MCLK clock */ +} codec_cfg_t; + +/** + * @brief Sdcard configuration + */ +typedef struct { + int16_t clk; /*!< GPIO for clock */ + int16_t cmd; /*!< GPIO for command */ + int16_t d0; /*!< GPIO for d0 */ + int16_t d1; /*!< GPIO for d1 (if only one line need set to -1) */ + int16_t d2; /*!< GPIO for d2 (if only one line need set to -1) */ + int16_t d3; /*!< GPIO for d3 (if only one line need set to -1) */ + int16_t power; /*!< GPIO for power */ +} sdcard_cfg_t; + +/** + * @brief Camera configuration + */ +typedef struct { + camera_type_t type; /*!< Camera type */ + int16_t pwr; /*!< GPIO for power */ + int16_t reset; /*!< GPIO for reset */ + int16_t xclk; /*!< GPIO for XCLK */ + int16_t pclk; /*!< GPIO for PCLK */ + int16_t vsync; /*!< GPIO for VSYNC */ + int16_t de; /*!< GPIO for DE */ + int16_t href; /*!< GPIO for HREF */ + int16_t data[16]; /*!< GPIO for DATA */ +} camera_cfg_t; + +/** + * @brief Codec setting + */ +typedef struct { + codec_cfg_t codec_cfg; /*!< Codec configuration */ + uint8_t codec_dir; /*!< Codec direction */ +} codec_setting_t; + +/** + * @brief Extent IO board type + */ +typedef enum { + EXTENT_IO_TYPE_NONE, /*!< NOne extent IO */ + EXTENT_IO_TYPE_TCA9554, /*!< TCA9554 extent IO type */ +} extend_io_type_t; + +/** + * @brief LCD controller type + */ +typedef enum { + LCD_CONTROLLER_TYPE_NONE, /*!< None controller type */ + LCD_CONTROLLER_TYPE_ST7789, /*!< ST7789 controller type */ +} lcd_controller_type_t; + +/** + * @brief LCD bus type + */ +typedef enum { + LCD_BUS_TYPE_NONE, /*!< None LCD bus type */ + LCD_BUS_TYPE_SPI, /*!< SPI LCD bus type */ + LCD_BUS_TYPE_RGB, /*!< RGB LCD bus type */ + LCD_BUS_TYPE_I80, /*!< I80 LCD bus type */ + LCD_BUS_TYPE_MIPI, /*!< MIPI LCD bus type */ +} lcd_bus_type_t; + +/** + * @brief SPI LCD configuration + */ +typedef struct { + uint8_t spi_bus; /*!< SPI bus number */ + int pclk_clk; /*!< PCLK clock */ + uint8_t cmd_bits; /*!< Command bit width */ + uint8_t param_bits; /*!< Parameter bit width */ + int16_t cs; /*!< CS GPIO */ + int16_t dc; /*!< DC GPIO */ + int16_t clk; /*!< Clock GPIO */ + int16_t mosi; /*!< MOSI GPIO */ + int16_t d[7]; /*!< Data GPIOs */ +} lcd_spi_cfg_t; + +/** + * @brief MIPI LCD configuration + */ +typedef struct { + uint8_t ldo_chan; + uint16_t ldo_voltage; + uint8_t lane_num; + uint32_t lane_bitrate; // Mbps + uint32_t dpi_clk; // MHz + uint8_t bit_depth; + uint8_t fb_num; + uint8_t dsi_hsync; + uint8_t dsi_vsync; + uint8_t dsi_hbp; + uint8_t dsi_hfp; + uint8_t dsi_vbp; + uint8_t dsi_vfp; +} lcd_mipi_cfg_t; + +/** + * @brief LCD configuration + */ +typedef struct { + extend_io_type_t io_type; + uint8_t io_i2c_port; + lcd_bus_type_t bus_type; + lcd_controller_type_t controller; + int width; + int height; + uint8_t i2c_port; + uint8_t mirror_x : 1; + uint8_t mirror_y : 1; + uint8_t swap_xy : 1; + uint8_t color_inv : 1; + int16_t ctrl_pin; + int16_t reset_pin; + union { + lcd_spi_cfg_t spi_cfg; + lcd_mipi_cfg_t mipi_cfg; + }; +} lcd_cfg_t; + +/** + * @brief Board section + */ +typedef struct { + codec_i2c_pin_t i2c_pin[MAX_I2C_NUM]; + codec_i2s_pin_t i2s_pin[MAX_I2S_NUM]; + codec_setting_t codec[MAX_CODEC_NUM]; + lcd_cfg_t lcd; + sdcard_cfg_t sdcard; + camera_cfg_t camera; + uint8_t i2c_num; + uint8_t i2s_num; + uint8_t codec_num; + uint8_t sdcard_num; + uint8_t lcd_num; + uint8_t camera_num; +} board_section_t; + +/** + * @brief Set codec board type + * + * @param[in] board_type Board name selection + */ +void set_codec_board_type(const char *board_type); + +/** + * @brief Get SDCard configuration + * + * @param[out] card_cfg SDCard configuration to store + * + * @return + * - 0 On success + * - -1 Not exists + * + */ +int get_sdcard_config(sdcard_cfg_t *card_cfg); + +/** + * @brief Get I2C pin setting by port + * + * @param[in] port I2C port + * @param[out] i2c_pin I2C pin setting + * + * @return + * - 0 On success + * - -1 Not exists + * + */ +int get_i2c_pin(uint8_t port, codec_i2c_pin_t *i2c_pin); + +/** + * @brief Get I2S pin setting by port + * + * @param[in] port I2S port + * @param[out] i2s_pin I2S pin setting + * + * @return + * - 0 On success + * - -1 Not exists + */ +int get_i2s_pin(uint8_t port, codec_i2s_pin_t *i2s_pin); + +/** + * @brief Get output codec configuration + * + * @param[out] out_cfg Output codec configuration to store + * + * @return + * - 0 On success + * - -1 Not exists + */ +int get_out_codec_cfg(codec_cfg_t *out_cfg); + +/** + * @brief Get input codec configuration + * + * @param[out] in_cfg Input codec configuration to store + * + * @return + * - 0 On success + * - -1 Not exists + */ +int get_in_codec_cfg(codec_cfg_t *in_cfg); + +/** + * @brief Get LCD configuration + * + * @param[out] lcd_cfg LCD configuration to store + * + * @return + * - 0 On success + * - -1 Not exists + */ +int get_lcd_cfg(lcd_cfg_t *lcd_cfg); + +/** + * @brief Get camera configuration + * + * @param[out] cam_cfg Camera configuration to store + * + * @return + * - 0 On success + * - -1 Not exists + */ +int get_camera_cfg(camera_cfg_t *cam_cfg); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/codec_board/include/codec_init.h b/components/third_party/codec_board/include/codec_init.h new file mode 100644 index 0000000..7c6c406 --- /dev/null +++ b/components/third_party/codec_board/include/codec_init.h @@ -0,0 +1,149 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_codec_dev.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief I2S mode + */ +typedef enum { + CODEC_I2S_MODE_STD = 0, /*!< STD mode */ + CODEC_I2S_MODE_TDM, /*!< TDM mode */ + CODEC_I2S_MODE_NONE, /*!< NONE means not support */ +} codec_i2s_mode_t; + +/** + * @brief Codec initialize configuration + */ +typedef struct { + codec_i2s_mode_t in_mode; /*!< I2S input mode */ + codec_i2s_mode_t out_mode; /*!< I2S output mode */ + bool in_use_tdm; /*!< Whether codec use TDM mode for input (codec use TDM, I2S can use STD mode) */ + bool reuse_dev; /*!< Use same handle for both input and output */ +} codec_init_cfg_t; + +/** + * @brief Initialize codec + * + * @param[in] cfg Codec initialize configuration + * + * @return + * - 0 On success + * - Others Fail to initialize + */ +int init_codec(codec_init_cfg_t *cfg); + +/** + * @brief Get `esp_codec_dev` handle for playback + * + * @return + * - NULL Fail to get playback handle + * - Others Playback `esp_codec_dev` handle + */ +esp_codec_dev_handle_t get_playback_handle(void); + +/** + * @brief Get `esp_codec_dev` handle for record + * + * @return + * - NULL Fail to get record handle + * - Others Record `esp_codec_dev` handle + */ +esp_codec_dev_handle_t get_record_handle(void); + +/** + * @brief Get I2C master bus handle by port + * + * @param[in] port I2C bus port + * + * @return + * - NULL Fail to get I2C bus handle + * - Others I2C bus handle + */ +void *get_i2c_bus_handle(uint8_t port); + +/** + * @brief Mount sdcard + * + * @return + * - 0 On success + * - Others Fail to mount + */ +int mount_sdcard(void); + +/** + * @brief Unmount sdcard + */ +void unmount_sdcard(void); + +/** + * @brief Deinitialize codec + */ +void deinit_codec(void); + +/** + * @brief Initialized for LCD + * + * @return + * - 0 On success + * - Others Fail to init LCD + */ +int board_lcd_init(void); + +/** + * @brief Get LCD handle + * + * @return + * - NULL Fail to get LCD handle + * - Others LCD handle + */ +void *board_get_lcd_handle(void); + +/** + * @brief Initialize the onboard LEDs + * + * @return + * - 0 On success + * - Others Fail to init LEDs + */ +int board_led_init(void); + +/** + * @brief Set the state of an onboard LED + * + * @return + * - 0 On success + * - Others Fail to init LEDs + */ +int board_led_set(int led_number, const bool state); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/codec_board/lcd_init.c b/components/third_party/codec_board/lcd_init.c new file mode 100644 index 0000000..a7a300b --- /dev/null +++ b/components/third_party/codec_board/lcd_init.c @@ -0,0 +1,289 @@ +#include "media_lib_os.h" +#include "codec_board.h" +#include "extend_io.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_ops.h" +#include "esp_log.h" + +#include "driver/spi_common.h" +#include "esp_idf_version.h" + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) +#include "esp_lcd_panel_dev.h" +#include "esp_lcd_panel_st7789.h" +#else +#include "esp_lcd_panel_vendor.h" +#endif + +#if CONFIG_IDF_TARGET_ESP32P4 +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_ili9881c.h" +#include "esp_ldo_regulator.h" +#include "soc/mipi_dsi_bridge_struct.h" +#include "esp_lcd_ek79007.h" +#endif + +#define TAG "LCD_INIT" + +#define RETURN_ON_ERR(ret) if (ret != 0) { \ + ESP_LOGE(TAG, "Fail to run on %d ret %d", __LINE__, ret); \ + return ret; \ +} + +static esp_lcd_panel_handle_t panel_handle = NULL; + +static int _lcd_rest(lcd_cfg_t *cfg) +{ + if (cfg->reset_pin >= 0) { + extend_io_set_pin_state(cfg->reset_pin, false); + media_lib_thread_sleep(100); + extend_io_set_pin_state(cfg->reset_pin, true); + } + return 0; +} + +static int _init_spi_lcd(lcd_cfg_t *cfg) +{ + int ret = 0; + if (cfg->spi_cfg.cs & BOARD_EXTEND_IO_START) { + extend_io_set_pin_dir(cfg->spi_cfg.cs, true); + media_lib_thread_sleep(10); + extend_io_set_pin_dir(cfg->spi_cfg.cs, false); + media_lib_thread_sleep(10); + } + spi_bus_config_t buscfg = { + .sclk_io_num = cfg->spi_cfg.clk, + .mosi_io_num = cfg->spi_cfg.mosi, + .miso_io_num = -1, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = cfg->width * cfg->height * 2, + }; +#if SOC_SPI_SUPPORT_OCT + if (cfg->spi_cfg.d[6] >= 0) { + buscfg.data1_io_num = cfg->spi_cfg.d[0]; + buscfg.data2_io_num = cfg->spi_cfg.d[1]; + ; + buscfg.data3_io_num = cfg->spi_cfg.d[2]; + ; + buscfg.data4_io_num = cfg->spi_cfg.d[3]; + ; + buscfg.data5_io_num = cfg->spi_cfg.d[4]; + ; + buscfg.data6_io_num = cfg->spi_cfg.d[5]; + ; + buscfg.data7_io_num = cfg->spi_cfg.d[6]; + ; + buscfg.flags = SPICOMMON_BUSFLAG_OCTAL; + } +#endif + int bus_id = SPI1_HOST + (cfg->spi_cfg.spi_bus - 1); + ret = spi_bus_initialize(bus_id, &buscfg, SPI_DMA_CH_AUTO); + ESP_LOGI(TAG, "CLK %d MOSI %d CS:%d DC: %d Bus:%d", + cfg->spi_cfg.clk, cfg->spi_cfg.mosi, + extend_io_get_hw_gpio(cfg->spi_cfg.cs), cfg->spi_cfg.dc, + bus_id); + RETURN_ON_ERR(ret); + esp_lcd_panel_io_spi_config_t io_config = { + .dc_gpio_num = cfg->spi_cfg.dc, + .cs_gpio_num = extend_io_get_hw_gpio(cfg->spi_cfg.cs), + .pclk_hz = cfg->spi_cfg.pclk_clk ? cfg->spi_cfg.pclk_clk : 60 * 1000 * 1000, + .spi_mode = 0, + .trans_queue_depth = 10, + .lcd_cmd_bits = cfg->spi_cfg.cmd_bits ? cfg->spi_cfg.cmd_bits : 8, + .lcd_param_bits = cfg->spi_cfg.param_bits ? cfg->spi_cfg.param_bits : 8, + .on_color_trans_done = NULL, + .user_ctx = NULL, + }; +#if SOC_SPI_SUPPORT_OCT + if (cfg->spi_cfg.d[6] >= 0) { + io_config.flags.octal_mode = 1; + io_config.spi_mode = 3; + } +#endif + esp_lcd_panel_io_handle_t io_handle; + ret = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)bus_id, &io_config, &io_handle); + RETURN_ON_ERR(ret); + esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = extend_io_get_hw_gpio(cfg->reset_pin), +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + .rgb_ele_order = ESP_LCD_COLOR_SPACE_BGR, +#else + .rgb_endian = LCD_RGB_ENDIAN_BGR, +#endif + .bits_per_pixel = 16, + }; + switch (cfg->controller) { + default: + return -1; + case LCD_CONTROLLER_TYPE_ST7789: + ret = esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle); + RETURN_ON_ERR(ret); + ESP_LOGI(TAG, "Init driver ST7789 finished"); + break; + } + return ret; +} + +#if CONFIG_IDF_TARGET_ESP32P4 +static int power_on_dsi(lcd_mipi_cfg_t *mipi_cfg) +{ + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = mipi_cfg->ldo_chan, + .voltage_mv = mipi_cfg->ldo_voltage, + }; + return esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy); +} + +static int _init_mipi_lcd(lcd_cfg_t *cfg) +{ + int ret = 0; + lcd_mipi_cfg_t *mipi_cfg = &cfg->mipi_cfg; + power_on_dsi(mipi_cfg); + // create MIPI DSI bus first, it will initialize the DSI PHY as well + esp_lcd_dsi_bus_handle_t mipi_dsi_bus; + esp_lcd_dsi_bus_config_t bus_config = { + .bus_id = 0, + .num_data_lanes = mipi_cfg->lane_num, + .phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, + .lane_bit_rate_mbps = mipi_cfg->lane_bitrate, + }; + ret = esp_lcd_new_dsi_bus(&bus_config, &mipi_dsi_bus); + RETURN_ON_ERR(ret); + ESP_LOGI(TAG, "Install MIPI DSI LCD control panel"); + esp_lcd_panel_io_handle_t mipi_dbi_io; + // we use DBI interface to send LCD commands and parameters + esp_lcd_dbi_io_config_t dbi_config = { + .virtual_channel = 0, + .lcd_cmd_bits = 8, + .lcd_param_bits = 8, + }; + ret = esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &mipi_dbi_io); + RETURN_ON_ERR(ret); + esp_lcd_dpi_panel_config_t dpi_config = { + .num_fbs = mipi_cfg->fb_num, + .virtual_channel = 0, + .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, + .dpi_clock_freq_mhz = mipi_cfg->dpi_clk, + .pixel_format = mipi_cfg->bit_depth == 24 ? LCD_COLOR_PIXEL_FORMAT_RGB888 : LCD_COLOR_PIXEL_FORMAT_RGB565, + .video_timing = { + .h_size = cfg->width, + .v_size = cfg->height, + .hsync_back_porch = mipi_cfg->dsi_hbp, + .hsync_pulse_width = mipi_cfg->dsi_hsync, + .hsync_front_porch = mipi_cfg->dsi_hfp, + .vsync_back_porch = mipi_cfg->dsi_vbp, + .vsync_pulse_width = mipi_cfg->dsi_vsync, + .vsync_front_porch = mipi_cfg->dsi_vfp, + }, + .flags.use_dma2d = true, + }; + esp_lcd_panel_dev_config_t panel_config = { + .bits_per_pixel = mipi_cfg->bit_depth, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .reset_gpio_num = cfg->reset_pin, + }; + if (cfg->width == 1024 && cfg->height == 600) { + ESP_LOGI(TAG, "Install EK79007 LCD control panel"); + esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565); + ek79007_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + panel_config.vendor_config = &vendor_config; + ret = esp_lcd_new_panel_ek79007(mipi_dbi_io, &panel_config, &panel_handle); + RETURN_ON_ERR(ret); + } else { + ili9881c_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + .lane_num = mipi_cfg->lane_num, + }, + }; + panel_config.vendor_config = &vendor_config; + ret = esp_lcd_new_panel_ili9881c(mipi_dbi_io, &panel_config, &panel_handle); + } + RETURN_ON_ERR(ret); + esp_lcd_panel_reset(panel_handle); + ESP_LOGI(TAG, "Install MIPI DSI LCD data panel"); + return ret; +} +#else + +static int _init_mipi_lcd(lcd_cfg_t *cfg) +{ + return -1; +} + +#endif + +static int _init_lcd(lcd_cfg_t *cfg) +{ + int ret = 0; + if (cfg->io_type == EXTENT_IO_TYPE_TCA9554) { + ret = extend_io_init(cfg->io_i2c_port); + if (ret != 0) { + return ret; + } + } + // Config reset and ctrl gpio dir + if (cfg->reset_pin >= 0) { + extend_io_set_pin_dir(cfg->reset_pin, true); + } + if (cfg->ctrl_pin >= 0) { + extend_io_set_pin_dir(cfg->ctrl_pin, true); + } + if (cfg->bus_type == LCD_BUS_TYPE_SPI) { + if (cfg->spi_cfg.cs >= 0) { + extend_io_set_pin_dir(cfg->spi_cfg.cs, true); + } + } + _lcd_rest(cfg); + if (cfg->ctrl_pin >= 0) { + extend_io_set_pin_dir(cfg->ctrl_pin, true); + } + if (cfg->bus_type == LCD_BUS_TYPE_SPI) { + ret = _init_spi_lcd(cfg); + } else if (cfg->bus_type == LCD_BUS_TYPE_MIPI) { + ret = _init_mipi_lcd(cfg); + } + if (panel_handle) { + ret = esp_lcd_panel_init(panel_handle); + RETURN_ON_ERR(ret); + if (cfg->color_inv) { + ret = esp_lcd_panel_invert_color(panel_handle, cfg->color_inv); + } + // ret = esp_lcd_panel_set_gap(panel_handle, 0, 0); + if (cfg->swap_xy) { + ret = esp_lcd_panel_swap_xy(panel_handle, cfg->swap_xy); + } + if (cfg->mirror_x || cfg->mirror_y) { + ret = esp_lcd_panel_mirror(panel_handle, cfg->mirror_x, cfg->mirror_y); + } + ret = esp_lcd_panel_disp_on_off(panel_handle, true); + } + return ret; +} + +int board_lcd_init(void) +{ + lcd_cfg_t cfg = { 0 }; + int ret = get_lcd_cfg(&cfg); + if (ret != 0) { + return ret; + } + return _init_lcd(&cfg); +} + +void *board_get_lcd_handle(void) +{ + if (panel_handle) { + return panel_handle; + } + return NULL; +} diff --git a/components/third_party/codec_board/led_init.c b/components/third_party/codec_board/led_init.c new file mode 100644 index 0000000..979eb31 --- /dev/null +++ b/components/third_party/codec_board/led_init.c @@ -0,0 +1,26 @@ +#include "codec_init.h" +#include "extend_io.h" + +static int16_t get_pin_by_led_number(int led_number) +{ + switch (led_number) { + case 0: return 6; + case 1: return 7; + default: return -1; + } +} + +int board_led_init(void) +{ + return extend_io_init(0); +} + +int board_led_set(int led_number, const bool state) +{ + int16_t pin = get_pin_by_led_number(led_number); + if (pin == -1) return -1; + + extend_io_set_pin_dir(pin, true); + extend_io_set_pin_state(pin, state); + return 0; +} \ No newline at end of file diff --git a/components/third_party/esp_capture/CMakeLists.txt b/components/third_party/esp_capture/CMakeLists.txt new file mode 100755 index 0000000..61f4c7d --- /dev/null +++ b/components/third_party/esp_capture/CMakeLists.txt @@ -0,0 +1,12 @@ + +# Gather common sources +set(component_srcdirs "src" + "src/impl/capture_simple_path" + "src/impl/capture_file_src" +) + +idf_component_register( + SRC_DIRS ${component_srcdirs} + INCLUDE_DIRS ./include ./interface + REQUIRES media_lib_sal esp_timer +) diff --git a/components/third_party/esp_capture/README.md b/components/third_party/esp_capture/README.md new file mode 100644 index 0000000..a847ad2 --- /dev/null +++ b/components/third_party/esp_capture/README.md @@ -0,0 +1,99 @@ +# ESP_Capture + +`esp_capture` is an integrated capture module for capturing audio and video data. It supports various audio and video capture devices and codecs, allowing users to easily acquire audio/video frame data or container data. + +## Features + +- **Capture Devices**: Abstracted into audio or video sources. Users can either use existing capture devices or add custom ones via provided interfaces. +- **Audio/Video Codecs**: Support for a wide range of codecs。 Users can use `menuconfig` to remove unused codecs to reduce the final binary size. +- **Muxer Support**: Support Saving muxed container data to storage or send it through a network. +- **Capture Path**: Simple capture paths for single audio and video codec outputs, with multiple path support under testing. + +--- + +## Capture Devices + +Capture devices are abstracted into **audio sources** and **video sources**. Users can: + +1. Add custom capture devices using the provided interfaces. +2. Use existing supported devices: + +### Supported Audio Capture Devices: +- `esp_capture_new_audio_codec_src`: Supports I2S devices using the `esp_codec_dev` handle. +- `esp_capture_new_audio_aec_src`: Supports I2S devices with AEC using the `esp_codec_dev` handle. + +### Supported Video Capture Devices: +1. `esp_capture_new_video_v4l2_src`: Supports MIPI CSI cameras and DVP cameras on ESP32-P4. +2. `esp_capture_new_video_dvp_src`: Supports DVP cameras on ESP32-S3 and other ESP platforms. + +--- + +## Capture Codecs + +Users can register video and audio codecs via the following APIs: + +- `esp_video_enc_register_default` +- `esp_audio_enc_register_default` + +### Audio Codecs: +- `G711A` +- `G711U` +- `AAC` +- `OPUS` + +### Video Codecs: +- `MJPEG` +- `H.264` (Baseline profile only) + +Unused codecs can be deselected via `menuconfig` to reduce the final image size. + +--- + +## Muxer Support + +Muxers allow user mux audio and video data into container formats: + +- **Supported Formats**: + - `MP4`: Supports saving to storage only. + - `TS`: Supports saving to storage and streaming output both. + +--- + +## Capture Path + +Each capture path supports one audio and video codec or muxed data output. + +- **Simple Capture**: Currently supports one capture path. +- **Multiple Path Support**: Developped based on ESP-GMF (under testing, not released yet). + +--- + +## Simple Usage + +Below are the steps to use `esp_capture` for audio and video capture: + +1. **Register Codecs**: + - Register audio codecs using `esp_audio_enc_register_default`. + - Register video codecs using `esp_video_enc_register_default`. + +2. **Select Capture Devices**: + - Use `esp_capture_new_audio_codec_src` to create an audio source. + - Use `esp_capture_new_video_v4l2_src` to create a video source. + +3. **Build Capture Path**: + - Use `esp_capture_build_simple_path` to build a capture path. + - Use the path to create a capture handle with `esp_capture_open`. + +4. **Setup and Enable Path**: + - Configure codec settings using `esp_capture_setup_path`. + - Enable the capture path using `esp_capture_enable_path`. + +5. **Start Capture**: + - Call `esp_capture_start` to begin capturing. + +6. **Acquire and Release Frame Data**: + - Call `esp_capture_acquire_path_frame` to retrieve audio, video, or muxed data. + - Call `esp_capture_release_path_frame` to release the frame data when done. + +7. **Stop Capture**: + - Call `esp_capture_stop` to end the capture session. \ No newline at end of file diff --git a/components/third_party/esp_capture/idf_component.yml b/components/third_party/esp_capture/idf_component.yml new file mode 100644 index 0000000..28909c7 --- /dev/null +++ b/components/third_party/esp_capture/idf_component.yml @@ -0,0 +1,5 @@ +version: "0.0.1" +description: Espressif Capture Library +dependencies: + idf : ">=4.4" + espressif/esp_muxer: "~1.1.0" diff --git a/components/third_party/esp_capture/include/esp_capture.h b/components/third_party/esp_capture/include/esp_capture.h new file mode 100644 index 0000000..6989e30 --- /dev/null +++ b/components/third_party/esp_capture/include/esp_capture.h @@ -0,0 +1,310 @@ + +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_types.h" +#include "esp_capture_path_if.h" +#include "esp_capture_audio_src_if.h" +#include "esp_capture_video_src_if.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Capture handle + */ +typedef void *esp_capture_handle_t; + +/** + * @brief Capture path handle + * @note A capture system may contain multiple capture paths + * Each path can have its special audio and video settings and can be configured to connect to muxer or not. + */ +typedef void *esp_capture_path_handle_t; + +/** + * @brief Capture sync mode + */ +typedef enum { + ESP_CAPTURE_SYNC_MODE_NONE, /*!< Audio and video without sync */ + ESP_CAPTURE_SYNC_MODE_SYSTEM, /*!< Audio and video synced with system time */ + ESP_CAPTURE_SYNC_MODE_AUDIO, /*!< Video sync follow audio */ +} esp_capture_sync_mode_t; + +/** + * @brief Capture configuration + */ +typedef struct { + esp_capture_sync_mode_t sync_mode; /*!< Capture sync mode */ + esp_capture_audio_src_if_t *audio_src; /*!< Capture audio source interface */ + esp_capture_video_src_if_t *video_src; /*!< Capture video source interface */ + esp_capture_path_if_t *capture_path; /*!< Capture path interface */ +} esp_capture_cfg_t; + +/** + * @brief Capture run type + * @note Capture run type is used to control capture path behavior + */ +typedef enum { + ESP_CAPTURE_RUN_TYPE_DISABLE = 0, /*!< Disable capture path, not run anymore */ + ESP_CAPTURE_RUN_TYPE_ALWAYS = 1, /*!< Enable capture path, run always */ + ESP_CAPTURE_RUN_TYPE_ONCE = 2 /*!< Enable capture once, use scenario like capture one image */ +} esp_capture_run_type_t; + +/** + * @brief Capture muxer configuration + */ +typedef enum { + ESP_CAPTURE_MUXER_TYPE_NONE, /*!< None muxer type */ + ESP_CAPTURE_MUXER_TYPE_TS, /*!< TS muxer type */ + ESP_CAPTURE_MUXER_TYPE_MP4, /*!< MP4 muxer type */ +} esp_capture_muxer_type_t; + +/** + * @brief Capture muxer mask + * @note Capture muxer mask is used control whether enable audio or video muxer + */ +typedef enum { + ESP_CAPTURE_MUXER_MASK_ALL = 0, /*!< Mux for both audio and video */ + ESP_CAPTURE_MUXER_MASK_AUDIO = 1, /*!< Mux for audio stream only */ + ESP_CAPTURE_MUXER_MASK_VIDEO = 2, /*!< Mux for video stream only */ +} esp_capture_muxer_mask_t; + +/** + * @brief Capture muxer configuration + */ +typedef struct { + esp_capture_muxer_type_t muxer_type; /* Muxer type */ + esp_capture_muxer_mask_t muxer_mask; /* Muxer masks to muxer */ + int (*slice_cb)(char *file_path, int len, int slice_idx); + /* File path to save file */ + uint32_t muxer_cache_size; /* Cache size to hold muxer output data */ + bool capture_muxer_data; /* Whether capture muxer output data */ + bool muxer_only; + /* When set means user do not want to get audio sink or video sink output data + Data is feed to muxer and consumed by muxer only */ +} esp_capture_muxer_cfg_t; + +/** + * @brief Open capture + * + * @param[in] cfg Capture configuration + * @param[out] capture Pointer to output capture system handle + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * - ESP_CAPTURE_ERR_NO_MEM Not enough memory for capture instance + * - ESP_CAPTURE_ERR_NO_RESOURCES No related resources + * + */ +int esp_capture_open(esp_capture_cfg_t *cfg, esp_capture_handle_t *capture); + +/** + * @brief Setup capture path to use certain sink settings + * + * @note Only support setup path when path not existed, or path disabled or not started + * Setup to an existed path will get the existed path handle + * + * @param[in] capture Capture handle + * @param[in] path Path to be added + * @param[in] sink_info Sink setting for audio and video stream + * @param[out] path_handle Pointer to output capture path handle + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * - ESP_CAPTURE_ERR_NO_MEM Not enough memory for capture instance + * - ESP_CAPTURE_ERR_INVALID_STATE Path already added + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Not supported to do path add (path interface not provided) + */ +int esp_capture_setup_path(esp_capture_handle_t capture, esp_capture_path_type_t path, + esp_capture_sink_cfg_t *sink_info, esp_capture_path_handle_t *path_handle); + +/** + * @brief Add muxer to capture path + * + * @param[in] h Capture path handle + * @param[in] muxer_cfg Muxer configuration + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * - ESP_CAPTURE_ERR_INVALID_STATE Muxer already added or can not add after capture system started + */ +int esp_capture_add_muxer_to_path(esp_capture_path_handle_t h, esp_capture_muxer_cfg_t *muxer_cfg); + +/** + * @brief Add overlay to capture path + * + * @param[in] h Capture path handle + * @param[in] overlay Overlay interface + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Path interface not provided + */ +int esp_capture_add_overlay_to_path(esp_capture_path_handle_t h, esp_capture_overlay_if_t *overlay); + +/** + * @brief Enable muxer for capture path + * + * @param[in] h Capture path handle + * @param[in] enable Enable muxer or not + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Path interface not provided + */ +int esp_capture_enable_muxer(esp_capture_path_handle_t h, bool enable); + +/** + * @brief Enable overlay for capture path + * + * @note Use can enable overlay at any time, even after `esp_capture_start` + * + * @param[in] h Capture path handle + * @param[in] enable Enable overlay or not + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Path interface not provided + */ +int esp_capture_enable_overlay(esp_capture_path_handle_t h, bool enable); + +/** + * @brief Enable capture path + * + * @note Use can enable capture path at any time, even after `esp_capture_start` + * + * @param[in] h Capture path handle + * @param[in] run_type Run type for capture path + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Path interface not provided + */ +int esp_capture_enable_path(esp_capture_path_handle_t h, esp_capture_run_type_t run_type); + +/** + * @brief Start capture system + * + * @note If capture system contain multiple capture path, all paths will be started + * + * @param[in] capture Capture system handle + * + * @return + * - ESP_CAPTURE_ERR_OK Success to start capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + */ +int esp_capture_start(esp_capture_handle_t capture); + +/** + * @brief Set stream bitrate for capture path + * + * @note If capture system contain multiple capture path, all paths will be started + * + * @param[in] h Capture path handle + * @param[in] stream_type Capture stream type + * @param[in] bitrate Stream bitrate to set + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Path interface not provided + */ +int esp_capture_set_path_bitrate(esp_capture_path_handle_t h, esp_capture_stream_type_t stream_type, uint32_t bitrate); + +/** + * @brief Acquire stream data from capture path + * + * @note Stream data is internally managed by capture system, use need not provide memory to hold it + * Meanwhile after use done, must call `esp_capture_release_stream` to release stream data + * Use need set `frame->stream_type` to specify which stream type to acquire + * + * @param[in] h Capture path handle + * @param[in,out] frame Capture frame information + * @param[in] no_wait Whether need wait for frame data, set to `true` to not wait + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Path interface not provided + */ +int esp_capture_acquire_path_frame(esp_capture_path_handle_t h, esp_capture_stream_frame_t *frame, bool no_wait); + +/** + * @brief Release stream data from capture path + * + * @note Use need make sure frame data, size, stream_type is same as the one acquired from `esp_capture_acquire_path_frame` + * + * @param[in] h Capture path handle + * @param[in] frame Capture frame information + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Wrong stream type + * - ESP_CAPTURE_ERR_INVALID_STATE Capture path not enable yet + */ +int esp_capture_release_path_frame(esp_capture_path_handle_t h, esp_capture_stream_frame_t *frame); + +/** + * @brief Stop capture + * + * @note All capture paths will be stopped + * + * @param[in] capture Capture path handle + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * + */ +int esp_capture_stop(esp_capture_handle_t capture); + +/** + * @brief Close capture + * + * @note The whole capture system will be destroyed, all related capture paths will be destroyed also + * + * @param[in] capture Capture path handle + * + * @return + * - ESP_CAPTURE_ERR_OK Success to open capture + * - ESP_CAPTURE_ERR_INVALID_ARG Invalid input argument + * + */ +int esp_capture_close(esp_capture_handle_t capture); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_capture/include/esp_capture_audio_enc.h b/components/third_party/esp_capture/include/esp_capture_audio_enc.h new file mode 100644 index 0000000..7146c3d --- /dev/null +++ b/components/third_party/esp_capture/include/esp_capture_audio_enc.h @@ -0,0 +1,45 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_types.h" +#include "esp_capture_aenc_if.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create generic audio encoder instance + * + * @return + * - NULL Not enough memory to hold audio encoder instance + * - Others Audio encoder instance + */ +esp_capture_aenc_if_t *esp_capture_new_audio_encoder(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/third_party/esp_capture/include/esp_capture_defaults.h b/components/third_party/esp_capture/include/esp_capture_defaults.h new file mode 100644 index 0000000..f4ab845 --- /dev/null +++ b/components/third_party/esp_capture/include/esp_capture_defaults.h @@ -0,0 +1,133 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_types.h" +#include "esp_capture_video_src_if.h" +#include "esp_capture_audio_src_if.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief V4L2 video source configuration + */ +typedef struct { + uint8_t buf_count; /*!< Buffer count to buffer video frame */ + int16_t pwr_pin; /*!< Power pin */ + int16_t reset_pin; /*!< Reset pin */ + int16_t xclk_pin; /*!< XCLK pin */ + int16_t data[8]; /*!< Data pins */ + int16_t vsync_pin; /*!< VSYNC pin */ + int16_t href_pin; /*!< HREF pin */ + int16_t pclk_pin; /*!< PCLK pin */ + uint32_t xclk_freq; /*!< XCLK frequency */ + uint8_t i2c_port; /*!< I2C port for the initialized bus */ +} esp_capture_video_dvp_src_cfg_t; + +/** + * @brief Create an instance for DVP video source + * + * @param[in] cfg DVP video source configuration + * + * @return + * - NULL Not enough memory to hold DVP instance + * - Others DVP video source instance + * + */ +esp_capture_video_src_if_t *esp_capture_new_video_dvp_src(esp_capture_video_dvp_src_cfg_t *cfg); + +/** + * @brief V4L2 video source configuration + */ +typedef struct { + char dev_name[16]; /*!< Device name */ + uint8_t buf_count; /*!< Frame buffer count */ +} esp_capture_video_v4l2_src_cfg_t; + +/** + * @brief Create an instance for V4L2 video source + * + * @param[in] cfg V4L2 video source configuration + * + * @return + * - NULL Not enough memory to hold V4L2 instance + * - Others V4L2 video source instance + * + */ +esp_capture_video_src_if_t *esp_capture_new_video_v4l2_src(esp_capture_video_v4l2_src_cfg_t *cfg); + +/** + * @brief Audio codec source configuration + */ +typedef struct { + void *record_handle; /*!< Record handle of `esp_codec_dev` */ +} esp_capture_audio_codec_src_cfg_t; + +/** + * @brief Create audio source instance for codec + * + * @param[in] cfg Audio codec source configuration + * + * @return + * - NULL Not enough memory to hold audio codec source instance + * - Others Audio codec source instance + * + */ +esp_capture_audio_src_if_t *esp_capture_new_audio_codec_src(esp_capture_audio_codec_src_cfg_t *cfg); + +/** + * @brief Audio with AEC source configuration + */ +typedef struct { + void *record_handle; /*!< Record handle of `esp_codec_dev` */ + uint8_t channel; /*!< Audio channel */ + uint8_t channel_mask; /*!< Selected channel */ +} esp_capture_audio_aec_src_cfg_t; + +/** + * @brief Enable to dump AEC input and output data + * + * @param[in] enable Whether enable dump AEC of not + * + */ +void esp_capture_enable_aec_src_dump(bool enable); + +/** + * @brief Create audio source with AEC for codec + * + * @param[in] cfg Audio source with AEC configuration + * + * @return + * - NULL Not enough memory to hold audio codec source instance + * - Others Audio codec source instance + * + */ +esp_capture_audio_src_if_t *esp_capture_new_audio_aec_src(esp_capture_audio_aec_src_cfg_t *cfg); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_capture/include/esp_capture_path_simple.h b/components/third_party/esp_capture/include/esp_capture_path_simple.h new file mode 100644 index 0000000..0616a52 --- /dev/null +++ b/components/third_party/esp_capture/include/esp_capture_path_simple.h @@ -0,0 +1,64 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_path_if.h" +#include "esp_capture_aenc_if.h" +#include "esp_capture_venc_if.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Simple capture path configuration + * + * @note Simple capture path consisted by one audio encoder and one video encoder + * It also support bypass mode, during bypass encoder not work, + * audio source data and video source data is sent to user directly. + * + */ +typedef struct { + esp_capture_aenc_if_t *aenc; /*!< Audio encoder instance */ + esp_capture_venc_if_t *venc; /*!< Video encoder instance */ + uint32_t aenc_frame_count; /*!< Audio encoder output frame count */ + uint32_t venc_frame_count; /*!< Video encoder output frame count */ +} esp_capture_simple_path_cfg_t; + +/** + * @brief Create simple capture path instance + * + * @param[in] cfg Audio codec source configuration + * + * @return + * - NULL Not enough memory to hold simple capture path instance + * - Others Simple capture path instance + * + */ +esp_capture_path_if_t *esp_capture_build_simple_path(esp_capture_simple_path_cfg_t *cfg); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_capture/include/esp_capture_text_overlay.h b/components/third_party/esp_capture/include/esp_capture_text_overlay.h new file mode 100644 index 0000000..683ec1b --- /dev/null +++ b/components/third_party/esp_capture/include/esp_capture_text_overlay.h @@ -0,0 +1,170 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_overlay_if.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Color definition in RGB565 format + * + */ +#define COLOR_RGB565_BLACK (0x0000) +#define COLOR_RGB565_NAVY (0x000F) +#define COLOR_RGB565_DARKGREEN (0x03E0) +#define COLOR_RGB565_DARKCYAN (0x03EF) +#define COLOR_RGB565_MAROON (0x7800) +#define COLOR_RGB565_PURPLE (0x780F) +#define COLOR_RGB565_OLIVE (0x7BE0) +#define COLOR_RGB565_LIGHTGREY (0xC618) +#define COLOR_RGB565_DARKGREY (0x7BEF) +#define COLOR_RGB565_BLUE (0x001F) +#define COLOR_RGB565_GREEN (0x07E0) +#define COLOR_RGB565_CYAN (0x07FF) +#define COLOR_RGB565_RED (0xF800) +#define COLOR_RGB565_MAGENTA (0xF81F) +#define COLOR_RGB565_YELLOW (0xFFE0) +#define COLOR_RGB565_WHITE (0xFFFF) +#define COLOR_RGB565_ORANGE (0xFD20) +#define COLOR_RGB565_GREENYELLOW (0xAFE5) +#define COLOR_RGB565_PINK (0xF81F) +#define COLOR_RGB565_SILVER (0xC618) +#define COLOR_RGB565_GRAY (0x8410) +#define COLOR_RGB565_LIME (0x07E0) +#define COLOR_RGB565_TEAL (0x0410) +#define COLOR_RGB565_FUCHSIA (0xF81F) +#define COLOR_RGB565_ESP_BKGD (0xD185) +#define ESP_PAINGER_FORMAT_SIZE_MAX (128) + +/** + * @brief Capture text overlay draw information + */ +typedef struct { + uint16_t color; /*!< Draw color */ + uint16_t font_size; /*!< Draw font size, font for this size must be loaded use `menuconfig` */ + uint16_t x; /*!< Draw x position inside of the text overlay */ + uint16_t y; /*!< Draw y position inside of the text overlay */ +} esp_capture_text_overlay_draw_info_t; + +/** + * @brief Create text overlay instance + * + * @note The text overlay must be smaller than the main capture frame size. + * The region's (x, y) position represents the top-left corner of the text overlay + * relative to the capture frame. + * The region's width and height define the size of the text overlay, where the + * text is drawn within this region. + * + * @param[in] rgn Text overlay region setting + * + * @return + * - NULL Not enough memory to hold text overlay instance + * - Others Text overlay instance + * + */ +esp_capture_overlay_if_t *esp_capture_new_text_overlay(esp_capture_rgn_t *rgn); + +/** + * @brief Draw text start + * + * @note Drawing should occur between `esp_capture_text_overlay_draw_start` and `esp_capture_text_overlay_draw_finished`, + * to ensure the text overlay frame data is fully captured. + * Multiple draw actions can be performed between these two functions + * + * @param[in] h Text overlay instance + * + * @return + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Not supported action for not open yet + * - ESP_CAPTURE_ERR_OK Draw start success + * + */ +int esp_capture_text_overlay_draw_start(esp_capture_overlay_if_t *h); + +/** + * @brief Draw text on text overlay + * + * @note Supports drawing multiple lines of text separated by '\n'. + * Each subsequent line will be aligned to the x position of the first line. + * + * @param[in] h Text overlay instance + * @param[in] info Drawing settings + * @param[in] str String to be drawn + * + * @return + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Not supported action for not open yet or will overflow + * - ESP_CAPTURE_ERR_OK Draw text success + * + */ +int esp_capture_text_overlay_draw_text(esp_capture_overlay_if_t *h, esp_capture_text_overlay_draw_info_t *info, char *str); + +/** + * @brief Draw text with a format similar to `sprintf` on the text overlay. + * + * @note Behavior is the same as `esp_capture_text_overlay_draw_text`. + * + * @param[in] h Text overlay instance + * @param[in] info Drawing settings + * @param[in] fmt String format + * @param[in] Arguments to be formatted + * + * @return + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Not supported action, either not open yet or will overflow + * - ESP_CAPTURE_ERR_OK Drawn with format success + * + */ +int esp_capture_text_overlay_draw_text_fmt(esp_capture_overlay_if_t *h, esp_capture_text_overlay_draw_info_t *info, const char *fmt, ...); + +/** + * @brief Indicate draw text finished + * + * @param[in] h Text overlay instance + * + * @return + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Not supported action for not open yet + * - ESP_CAPTURE_ERR_OK Draw finished success + * + */ +int esp_capture_text_overlay_draw_finished(esp_capture_overlay_if_t *h); + +/** + * @brief Clear a region on the text overlay + * + * @param[in] h Text overlay instance + * @param[in] rgn Region to be cleared + * @param[in] color Color used to clear the region + * + * @return + * - ESP_CAPTURE_ERR_NOT_SUPPORTED Action not supported for not open yet + * - ESP_CAPTURE_ERR_INVALID_ARG Region exceeds text overlay boundaries + * - ESP_CAPTURE_ERR_OK Clear region success + */ +int esp_capture_text_overlay_clear(esp_capture_overlay_if_t *h, esp_capture_rgn_t *rgn, uint16_t color); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/third_party/esp_capture/include/esp_capture_types.h b/components/third_party/esp_capture/include/esp_capture_types.h new file mode 100644 index 0000000..3fe7b39 --- /dev/null +++ b/components/third_party/esp_capture/include/esp_capture_types.h @@ -0,0 +1,134 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Capture error code + */ +typedef enum { + ESP_CAPTURE_ERR_OK, + ESP_CAPTURE_ERR_NO_MEM = -1, + ESP_CAPTURE_ERR_INVALID_ARG = -2, + ESP_CAPTURE_ERR_NOT_SUPPORTED = -3, + ESP_CAPTURE_ERR_NOT_FOUND = -4, + ESP_CAPTURE_ERR_NOT_ENOUGH, + ESP_CAPTURE_ERR_TIMEOUT = -5, + ESP_CAPTURE_ERR_INVALID_STATE = -6, + ESP_CAPTURE_ERR_INTERNAL = -7, + ESP_CAPTURE_ERR_NO_RESOURCES = -8, +} esp_capture_err_t; + +/** + * @brief Capture codec types + */ +typedef enum { + ESP_CAPTURE_CODEC_TYPE_NONE, + ESP_CAPTURE_CODEC_TYPE_AUDIO = 0x20, + ESP_CAPTURE_CODEC_TYPE_PCM, /*!< Audio PCM codec */ + ESP_CAPTURE_CODEC_TYPE_G711A, /*!< Audio G711-ALaw codec */ + ESP_CAPTURE_CODEC_TYPE_G711U, /*!< Audio G711-ULaw codec */ + ESP_CAPTURE_CODEC_TYPE_OPUS, /*!< Audio OPUS codec */ + ESP_CAPTURE_CODEC_TYPE_AAC, /*!< Audio AAC codec */ + ESP_CAPTURE_CODEC_TYPE_VIDEO = 0x40, + ESP_CAPTURE_CODEC_TYPE_H264, /*!< Video H264 codec */ + ESP_CAPTURE_CODEC_TYPE_MJPEG, /*!< Video JPEG codec */ + ESP_CAPTURE_CODEC_TYPE_RGB565, /*!< Video RGB565 codec */ + ESP_CAPTURE_CODEC_TYPE_RGB8888, /*!< Video RGB8888 codec */ + ESP_CAPTURE_CODEC_TYPE_YUV420P, /*!< Video YUV420 progressive codec */ + ESP_CAPTURE_CODEC_TYPE_YUV422P, /*!< Video YUV422 progressive codec */ + ESP_CAPTURE_CODEC_TYPE_YUV420, /*!< Video YUV420 codec */ + ESP_CAPTURE_CODEC_TYPE_YUV422, /*!< Video YUV422 codec */ +} esp_capture_codec_type_t; + +/** + * @brief Capture stream type + */ +typedef enum { + ESP_CAPTURE_STREAM_TYPE_NONE, + ESP_CAPTURE_STREAM_TYPE_AUDIO, /*!< Audio stream type */ + ESP_CAPTURE_STREAM_TYPE_VIDEO, /*!< Video stream type */ + ESP_CAPTURE_STREAM_TYPE_MUXER, /*!< Mux stream type */ +} esp_capture_stream_type_t; + +/** + * @brief Capture frame information + */ +typedef struct { + esp_capture_stream_type_t stream_type; /*!< Capture stream type */ + uint32_t pts; /*!< Stream frame presentation timestamp (unit ms) */ + uint8_t *data; /*!< Stream frame data pointer */ + int size; /*!< Stream frame data size */ +} esp_capture_stream_frame_t; + +/** + * @brief Capture audio information + */ +typedef struct { + esp_capture_codec_type_t codec; /*!< Audio codec */ + uint32_t sample_rate; /*!< Audio sample rate */ + uint8_t channel; /*!< Audio channel */ + uint8_t bits_per_sample; /*!< Audio bits per sample */ +} esp_capture_audio_info_t; + +/** + * @brief Capture video information + */ +typedef struct { + esp_capture_codec_type_t codec; /*!< Video codec */ + uint32_t width; /*!< Video width */ + uint32_t height; /*!< Video height */ + uint8_t fps; /*!< Video frames per second */ + uint32_t bitrate; + uint32_t gop; +} esp_capture_video_info_t; + +/** + * @brief Capture sink configuration + */ +typedef struct { + esp_capture_audio_info_t audio_info; /*!< Audio sink information */ + esp_capture_video_info_t video_info; /*!< Video sink information */ +} esp_capture_sink_cfg_t; + +/** + * @brief Capture region + */ +typedef struct { + uint32_t x; /*!< Region x position */ + uint32_t y; /*!< Region y position */ + uint32_t width; /*!< Region width */ + uint32_t height; /*!< Region height */ +} esp_capture_rgn_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_capture/include/esp_capture_video_enc.h b/components/third_party/esp_capture/include/esp_capture_video_enc.h new file mode 100644 index 0000000..33ab1a4 --- /dev/null +++ b/components/third_party/esp_capture/include/esp_capture_video_enc.h @@ -0,0 +1,46 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_types.h" +#include "esp_capture_venc_if.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create an instance for general video encoder + * + * @return + * - NULL Not enough memory to hold video encoder instance + * - Others Video encoder instance + * + */ +esp_capture_venc_if_t *esp_capture_new_video_encoder(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_capture/interface/esp_capture_aenc_if.h b/components/third_party/esp_capture/interface/esp_capture_aenc_if.h new file mode 100644 index 0000000..bc2a632 --- /dev/null +++ b/components/third_party/esp_capture/interface/esp_capture_aenc_if.h @@ -0,0 +1,77 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Capture audio encoder interface + */ +typedef struct esp_capture_aenc_if_t esp_capture_aenc_if_t; + +struct esp_capture_aenc_if_t { + /** + * @brief Clone an existing audio encoder. + */ + esp_capture_aenc_if_t *(*clone)(esp_capture_aenc_if_t *enc); + + /** + * @brief Get the supported audio codecs. + */ + int (*get_support_codecs)(esp_capture_aenc_if_t *src, const esp_capture_codec_type_t **codecs, uint8_t *num); + + /** + * @brief Start the audio encoder. + */ + int (*start)(esp_capture_aenc_if_t *src, esp_capture_audio_info_t *info); + + /** + * @brief Get the input and output frame sizes for the audio encoder. + */ + int (*get_frame_size)(esp_capture_aenc_if_t *src, int *in_samples, int *out_frame_size); + + /** + * @brief Set the bitrate for the audio encoder. + */ + int (*set_bitrate)(esp_capture_aenc_if_t *src, int bitrate); + + /** + * @brief Encode an audio frame. + */ + int (*encode_frame)(esp_capture_aenc_if_t *src, esp_capture_stream_frame_t *raw, esp_capture_stream_frame_t *encoded); + + /** + * @brief Stop the audio encoder. + */ + int (*stop)(esp_capture_aenc_if_t *src); +}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/third_party/esp_capture/interface/esp_capture_audio_src_if.h b/components/third_party/esp_capture/interface/esp_capture_audio_src_if.h new file mode 100644 index 0000000..5657b37 --- /dev/null +++ b/components/third_party/esp_capture/interface/esp_capture_audio_src_if.h @@ -0,0 +1,77 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Capture audio source interface + */ +typedef struct esp_capture_audio_src_if_t esp_capture_audio_src_if_t; + +struct esp_capture_audio_src_if_t { + /** + * @brief Open the audio source for capturing. + */ + int (*open)(esp_capture_audio_src_if_t *src); + + /** + * @brief Get the supported audio codecs. + */ + int (*get_support_codecs)(esp_capture_audio_src_if_t *src, const esp_capture_codec_type_t **codecs, uint8_t *num); + + /** + * @brief Negotiate capabilities between the source and the sink. + */ + int (*negotiate_caps)(esp_capture_audio_src_if_t *src, esp_capture_audio_info_t *in_cap, esp_capture_audio_info_t *out_caps); + + /** + * @brief Start capturing audio from the source. + */ + int (*start)(esp_capture_audio_src_if_t *src); + + /** + * @brief Read a frame of audio data from the source. + */ + int (*read_frame)(esp_capture_audio_src_if_t *src, esp_capture_stream_frame_t *frame); + + /** + * @brief Stop capturing audio from the source. + */ + int (*stop)(esp_capture_audio_src_if_t *src); + + /** + * @brief Close the audio source and release resources. + */ + int (*close)(esp_capture_audio_src_if_t *src); +}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/third_party/esp_capture/interface/esp_capture_overlay_if.h b/components/third_party/esp_capture/interface/esp_capture_overlay_if.h new file mode 100644 index 0000000..309975d --- /dev/null +++ b/components/third_party/esp_capture/interface/esp_capture_overlay_if.h @@ -0,0 +1,77 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Capture overlay interface + */ +typedef struct esp_capture_overlay_if_t esp_capture_overlay_if_t; + +struct esp_capture_overlay_if_t { + /** + * @brief Open the overlay interface. + */ + int (*open)(esp_capture_overlay_if_t *src); + + /** + * @brief Get the overlay region and codec type. + */ + int (*get_overlay_region)(esp_capture_overlay_if_t *src, esp_capture_codec_type_t *codec, esp_capture_rgn_t *rgn); + + /** + * @brief Set the alpha value for the overlay. + */ + int (*set_alpha)(esp_capture_overlay_if_t *src, uint8_t alpha); + + /** + * @brief Get the current alpha value of the overlay. + */ + int (*get_alpha)(esp_capture_overlay_if_t *src, uint8_t *alpha); + + /** + * @brief Acquire a frame for the overlay. + */ + int (*acquire_frame)(esp_capture_overlay_if_t *src, esp_capture_stream_frame_t *frame); + + /** + * @brief Release a previously acquired frame. + */ + int (*release_frame)(esp_capture_overlay_if_t *src, esp_capture_stream_frame_t *frame); + + /** + * @brief Close the overlay interface. + */ + int (*close)(esp_capture_overlay_if_t *src); +}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/third_party/esp_capture/interface/esp_capture_path_if.h b/components/third_party/esp_capture/interface/esp_capture_path_if.h new file mode 100644 index 0000000..8bdf141 --- /dev/null +++ b/components/third_party/esp_capture/interface/esp_capture_path_if.h @@ -0,0 +1,174 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_types.h" +#include "esp_capture_overlay_if.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Capture path interface alias + */ +typedef struct esp_capture_path_if_t esp_capture_path_if_t; + +/** + * @brief Capture path type + */ +typedef enum { + ESP_CAPTURE_PATH_PRIMARY = 0, /*!< Primary capture path */ + ESP_CAPTURE_PATH_SECONDARY, /*!< Secondary capture path */ + ESP_CAPTURE_PATH_THIRD, /*!< Third capture path */ + ESP_CAPTURE_PATH_MAX, /*!< Maximum of capture path supported */ +} esp_capture_path_type_t; + +/** + * @brief Capture path set type + */ +typedef enum { + ESP_CAPTURE_PATH_SET_TYPE_NONE, /*!< Set type NONE */ + ESP_CAPTURE_PATH_SET_TYPE_AUDIO_BITRATE, /*!< Set for audio bitrate */ + ESP_CAPTURE_PATH_SET_TYPE_VIDEO_BITRATE, /*!< Set for video bitrate */ + ESP_CAPTURE_PATH_SET_TYPE_VIDEO_FPS, /*!< Set for video frame per second */ +} esp_capture_path_set_type_t; + +/** + * @brief Capture path event + */ +typedef enum { + ESP_CAPTURE_PATH_EVENT_TYPE_NONE = 0, + ESP_CAPTURE_PATH_EVENT_AUDIO_STARTED = 1, + ESP_CAPTURE_PATH_EVENT_AUDIO_NOT_SUPPORT = 2, + ESP_CAPTURE_PATH_EVENT_AUDIO_ERROR = 3, + ESP_CAPTURE_PATH_EVENT_VIDEO_STARTED = 4, + ESP_CAPTURE_PATH_EVENT_VIDEO_NOT_SUPPORT = 5, + ESP_CAPTURE_PATH_EVENT_VIDEO_ERROR = 6, +} esp_capture_path_event_type_t; + +/** + * @brief Capture path configuration + */ +typedef struct { + /** + * @brief Acquire a source frame for processing. + */ + int (*acquire_src_frame)(void *src, esp_capture_stream_frame_t *frame, bool no_wait); + + /** + * @brief Release a previously acquired source frame. + */ + int (*release_src_frame)(void *src, esp_capture_stream_frame_t *frame); + + /** + * @brief Negotiate video capabilities between source and destination. + */ + int (*nego_video)(void *src, esp_capture_video_info_t *in_cap, esp_capture_video_info_t *out_caps); + + /** + * @brief Negotiate audio capabilities between source and destination. + */ + int (*nego_audio)(void *src, esp_capture_audio_info_t *in_cap, esp_capture_audio_info_t *out_caps); + + /** + * @brief Notify that a frame has been processed. + */ + int (*frame_processed)(void *src, esp_capture_path_type_t path, esp_capture_stream_frame_t *frame); + + /** + * @brief Notify path event to capture + */ + int (*event_cb)(void *src, esp_capture_path_type_t path, esp_capture_path_event_type_t event); + + /** + * @brief Pointer to the source context. + */ + void *src_ctx; +} esp_capture_path_cfg_t; + +/** + * @brief Capture path interface + */ +struct esp_capture_path_if_t { + /** + * @brief Open a capture path interface with specified configuration. + */ + int (*open)(esp_capture_path_if_t *p, esp_capture_path_cfg_t *cfg); + + /** + * @brief Add a new path to the capture path interface. + */ + int (*add_path)(esp_capture_path_if_t *p, esp_capture_path_type_t path, esp_capture_sink_cfg_t *sink); + + /** + * @brief Add an overlay to a specific path. + */ + int (*add_overlay)(esp_capture_path_if_t *p, esp_capture_path_type_t path, esp_capture_overlay_if_t *overlay); + + /** + * @brief Enable or disable an overlay on a specific path. + */ + int (*enable_overlay)(esp_capture_path_if_t *p, esp_capture_path_type_t path, bool enable); + + /** + * @brief Enable or disable a specific path. + */ + int (*enable_path)(esp_capture_path_if_t *p, esp_capture_path_type_t path, bool enable); + + /** + * @brief Start the capture path interface. + */ + int (*start)(esp_capture_path_if_t *p); + + /** + * @brief Retrieve the number of audio frame samples for a specific path. + */ + int (*get_audio_frame_samples)(esp_capture_path_if_t *p, esp_capture_path_type_t path); + + /** + * @brief Configure a specific path with given settings. + */ + int (*set)(esp_capture_path_if_t *p, esp_capture_path_type_t path, esp_capture_path_set_type_t type, void *cfg, int cfg_size); + + /** + * @brief Return a frame back to the capture path interface. + */ + int (*return_frame)(esp_capture_path_if_t *p, esp_capture_path_type_t path, esp_capture_stream_frame_t *frame); + + /** + * @brief Stop the capture path interface. + */ + int (*stop)(esp_capture_path_if_t *p); + + /** + * @brief Close the capture path interface. + */ + int (*close)(esp_capture_path_if_t *p); +}; + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_capture/interface/esp_capture_venc_if.h b/components/third_party/esp_capture/interface/esp_capture_venc_if.h new file mode 100644 index 0000000..dbeae7f --- /dev/null +++ b/components/third_party/esp_capture/interface/esp_capture_venc_if.h @@ -0,0 +1,86 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Capture video encoder interface definition + */ +typedef struct esp_capture_venc_if_t esp_capture_venc_if_t; + +/** + * @brief Capture video encoder interface + */ +struct esp_capture_venc_if_t { + /** + * @brief Clone of video encoder instance + */ + esp_capture_venc_if_t *(*clone)(esp_capture_venc_if_t *enc); + + /** + * @brief Get supported codecs from video encoder + */ + int (*get_support_codecs)(esp_capture_venc_if_t *enc, const esp_capture_codec_type_t **codecs, uint8_t *num); + + /** + * @brief Get input codecs of special output codec from video encoder + */ + int (*get_input_codecs)(esp_capture_venc_if_t *enc, esp_capture_codec_type_t out_codec, + const esp_capture_codec_type_t **codecs, uint8_t *num); + + /** + * @brief Start video encoder + */ + int (*start)(esp_capture_venc_if_t *enc, esp_capture_codec_type_t src_codec, esp_capture_video_info_t *info); + + /** + * @brief Get frame size from video encoder + */ + int (*get_frame_size)(esp_capture_venc_if_t *enc, int *in_frame_size, int *out_frame_size); + + /** + * @brief Set bitrate for video encoder + */ + int (*set_bitrate)(esp_capture_venc_if_t *enc, int bitrate); + + /** + * @brief Encode video frame + */ + int (*encode_frame)(esp_capture_venc_if_t *enc, esp_capture_stream_frame_t *raw, esp_capture_stream_frame_t *encoded); + + /** + * @brief Stop of video encode + */ + int (*stop)(esp_capture_venc_if_t *enc); +}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/third_party/esp_capture/interface/esp_capture_video_src_if.h b/components/third_party/esp_capture/interface/esp_capture_video_src_if.h new file mode 100644 index 0000000..59cecdb --- /dev/null +++ b/components/third_party/esp_capture/interface/esp_capture_video_src_if.h @@ -0,0 +1,85 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_capture_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Video source interface definition + */ +typedef struct esp_capture_video_src_if_t esp_capture_video_src_if_t; + +/** + * @brief Video source interface + */ +struct esp_capture_video_src_if_t { + /** + * @brief Open video capture soruce + */ + int (*open)(esp_capture_video_src_if_t *src); + + /** + * @brief Get supported codecs from video source + */ + int (*get_support_codecs)(esp_capture_video_src_if_t *src, const esp_capture_codec_type_t **codecs, uint8_t *num); + + /** + * @brief Negotiate for video source capability + */ + int (*negotiate_caps)(esp_capture_video_src_if_t *src, esp_capture_video_info_t *in_cap, esp_capture_video_info_t *out_caps); + + /** + * @brief Start of video source + */ + int (*start)(esp_capture_video_src_if_t *src); + + /** + * @brief Acquire video frame from video source + */ + int (*acquire_frame)(esp_capture_video_src_if_t *src, esp_capture_stream_frame_t *frame); + + /** + * @brief Release video frame from video source + */ + int (*release_frame)(esp_capture_video_src_if_t *src, esp_capture_stream_frame_t *frame); + + /** + * @brief Stop of video source + */ + int (*stop)(esp_capture_video_src_if_t *src); + + /** + * @brief Close of video source + */ + int (*close)(esp_capture_video_src_if_t *src); +}; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/third_party/esp_capture/src/esp_capture.c b/components/third_party/esp_capture/src/esp_capture.c new file mode 100644 index 0000000..00bdee3 --- /dev/null +++ b/components/third_party/esp_capture/src/esp_capture.c @@ -0,0 +1,1617 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include +#include "esp_capture.h" +#include "esp_log.h" +#include "esp_muxer.h" +#include "mp4_muxer.h" +#include "ts_muxer.h" +#include "msg_q.h" +#include "media_lib_os.h" +#include "data_queue.h" +#include "share_q.h" +#include "esp_capture_sync.h" +#include "msg_q.h" + +#define TAG "ESP_CAPTURE" + +#define SLICE_DURATION 300000 +#define WRITE_CACHE_SIZE (16 * 1024) +#define MUXER_DEFAULT_POOL_SIZE (500 * 1024) +#define MIN_AUDIO_FRAME_DURATION 10 +#define MIN_VIDEO_FRAME_DURATION 30 + +#define EVENT_GROUP_AUDIO_SRC_EXITED (1) +#define EVENT_GROUP_VIDEO_SRC_EXITED (2) +#define EVENT_GROUP_MUXER_EXITED (4) + +#define MAX_Q_SIZE (5) + +// Here hacking to use stream type to indicate start/stop command +#define START_CMD_STREAM_TYPE (esp_capture_stream_type_t)0x10 +#define STOP_CMD_STREAM_TYPE (esp_capture_stream_type_t)0x11 +#define CAPTURE_SYNC_TOLERANCE 100 +#define BREAK_SET_RETURN(ret_val) \ + ret = ret_val; \ + break; + +typedef enum { + CAPTURE_SHARED_BY_USER = 0, + CAPTURE_SHARED_BY_MUXER = 1 +} capture_shared_type_t; + +struct capture_t; + +typedef struct { + esp_capture_path_type_t path_type; + esp_capture_sink_cfg_t sink_cfg; + esp_muxer_handle_t muxer; + esp_capture_muxer_cfg_t muxer_cfg; + data_queue_t *muxer_data_q; + bool muxer_enable; + esp_capture_overlay_if_t *overlay; + struct capture_t *parent; + bool run_once; + bool run_finished; + bool enable; + bool audio_path_disabled; + bool video_path_disabled; + bool sink_disabled; + bool muxing; + bool muxer_started; + msg_q_handle_t audio_q; + msg_q_handle_t video_q; + msg_q_handle_t muxer_q; + share_q_handle_t audio_share_q; + share_q_handle_t video_share_q; + uint32_t muxer_cur_pts; + int audio_stream_idx; + int video_stream_idx; +} capture_path_t; + +typedef struct capture_t { + esp_capture_cfg_t cfg; + uint32_t audio_frame_samples; + uint32_t audio_frame_size; + uint32_t audio_frames; + uint32_t video_frames; + esp_capture_audio_info_t audio_src_info; + esp_capture_video_info_t video_src_info; + data_queue_t *audio_src_q; + msg_q_handle_t video_src_q; + capture_path_t *path[ESP_CAPTURE_PATH_MAX]; + uint8_t path_num; + esp_capture_sync_handle_t sync_handle; + bool audio_nego_done; + bool video_nego_done; + bool fetching_audio; + bool fetching_video; + bool started; + media_lib_event_grp_handle_t event_group; + media_lib_mutex_handle_t api_lock; +} capture_t; + +static esp_muxer_type_t get_muxer_type(esp_capture_muxer_type_t muxer_type) +{ + switch (muxer_type) { + case ESP_CAPTURE_MUXER_TYPE_MP4: + return ESP_MUXER_TYPE_MP4; + case ESP_CAPTURE_MUXER_TYPE_TS: + return ESP_MUXER_TYPE_TS; + default: + return ESP_MUXER_TYPE_MAX; + } +} + +static esp_muxer_audio_codec_t get_muxer_acodec(esp_capture_codec_type_t codec_type) +{ + switch (codec_type) { + case ESP_CAPTURE_CODEC_TYPE_AAC: + return ESP_MUXER_ADEC_AAC; + case ESP_CAPTURE_CODEC_TYPE_PCM: + return ESP_MUXER_ADEC_PCM; + default: + return ESP_MUXER_ADEC_NONE; + } +} + +esp_muxer_video_codec_t get_muxer_vcodec(esp_capture_codec_type_t codec_type) +{ + switch (codec_type) { + case ESP_CAPTURE_CODEC_TYPE_H264: + return ESP_MUXER_VDEC_H264; + case ESP_CAPTURE_CODEC_TYPE_MJPEG: + return ESP_MUXER_VDEC_MJPEG; + default: + return ESP_MUXER_VDEC_NONE; + } +} + +static int muxer_data_reached(esp_muxer_data_info_t *muxer_data, void *ctx) +{ + capture_path_t *path = (capture_path_t *)ctx; + if (path->muxer_cfg.capture_muxer_data && muxer_data->size) { + // Add data to queue + int size = sizeof(uint32_t) + muxer_data->size; + void *data = data_queue_get_buffer(path->muxer_data_q, size); + if (data) { + *(uint32_t *)data = path->muxer_cur_pts; + memcpy(data + sizeof(uint32_t), muxer_data->data, muxer_data->size); + data_queue_send_buffer(path->muxer_data_q, size); + } + } + return 0; +} + +static bool muxer_support_streaming(esp_muxer_type_t muxer_type) +{ + if (muxer_type == ESP_MUXER_TYPE_TS || muxer_type == ESP_MUXER_TYPE_FLV) { + return true; + } + return false; +} + +static int open_muxer(capture_path_t *path) +{ + // TODO add other muxer types + union { + mp4_muxer_config_t mp4_cfg; + ts_muxer_config_t ts_cfg; + } muxer_cfg; + memset(&muxer_cfg, 0, sizeof(muxer_cfg)); + esp_muxer_config_t *base = &muxer_cfg.mp4_cfg.base_config; + base->muxer_type = get_muxer_type(path->muxer_cfg.muxer_type); + base->slice_duration = SLICE_DURATION; + base->url_pattern = path->muxer_cfg.slice_cb; + if (path->muxer_cfg.capture_muxer_data) { + if (muxer_support_streaming(base->muxer_type)) { + base->data_cb = muxer_data_reached; + } else { + ESP_LOGE(TAG, "Muxer type %d does not support streaming", base->muxer_type); + path->muxer_cfg.capture_muxer_data = false; + } + } + base->ctx = path; + // base->ram_cache_size = WRITE_CACHE_SIZE; + int cfg_size = 0; + if (base->muxer_type == ESP_MUXER_TYPE_MP4) { + cfg_size = sizeof(muxer_cfg.mp4_cfg); + } else if (base->muxer_type == ESP_MUXER_TYPE_TS) { + cfg_size = sizeof(muxer_cfg.ts_cfg); + } + path->audio_stream_idx = -1; + path->video_stream_idx = -1; + path->muxer = esp_muxer_open(base, cfg_size); + if (path->muxer == NULL) { + ESP_LOGE(TAG, "Fail to open muxer"); + return ESP_CAPTURE_ERR_NO_MEM; + } + int ret = 0; + esp_capture_sink_cfg_t *sink_cfg = &path->sink_cfg; + if (sink_cfg->audio_info.codec && (path->muxer_cfg.muxer_mask == ESP_CAPTURE_MUXER_MASK_ALL || path->muxer_cfg.muxer_mask == ESP_CAPTURE_MUXER_MASK_AUDIO)) { + esp_muxer_audio_stream_info_t audio_info = { + .codec = get_muxer_acodec(sink_cfg->audio_info.codec), + .sample_rate = sink_cfg->audio_info.sample_rate, + .bits_per_sample = sink_cfg->audio_info.bits_per_sample, + .channel = sink_cfg->audio_info.channel, + .min_packet_duration = MIN_AUDIO_FRAME_DURATION, + }; + ret = esp_muxer_add_audio_stream(path->muxer, &audio_info, &path->audio_stream_idx); + if (ret != ESP_MUXER_ERR_OK) { + ESP_LOGE(TAG, "Fail to add audio stream for muxer"); + } + } + if (sink_cfg->video_info.codec && (path->muxer_cfg.muxer_mask == ESP_CAPTURE_MUXER_MASK_ALL || path->muxer_cfg.muxer_mask == ESP_CAPTURE_MUXER_MASK_VIDEO)) { + esp_muxer_video_stream_info_t video_info = { + .codec = get_muxer_vcodec(sink_cfg->video_info.codec), + .fps = sink_cfg->video_info.fps, + .width = sink_cfg->video_info.width, + .height = sink_cfg->video_info.height, + .min_packet_duration = MIN_VIDEO_FRAME_DURATION, + }; + ret = esp_muxer_add_video_stream(path->muxer, &video_info, &path->video_stream_idx); + if (ret != ESP_MUXER_ERR_OK) { + ESP_LOGE(TAG, "Fail to add audio stream for muxer"); + } + } + ret = (path->audio_stream_idx >= 0 || path->video_stream_idx >= 0) ? ESP_CAPTURE_ERR_OK : ESP_CAPTURE_ERR_NOT_SUPPORTED; + if (ret != ESP_CAPTURE_ERR_OK) { + esp_muxer_close(path->muxer); + path->muxer = NULL; + path->muxer_cfg.muxer_type = ESP_CAPTURE_MUXER_TYPE_NONE; + } + return ret; +} + +static void muxer_thread(void *arg) +{ + capture_path_t *path = (capture_path_t *)arg; + esp_capture_stream_frame_t frame = { 0 }; + ESP_LOGI(TAG, "Enter muxer thread muxing %d", path->muxing); + while (path->muxing) { + int ret = msg_q_recv(path->muxer_q, &frame, sizeof(frame), false); + if (ret != 0) { + ESP_LOGI(TAG, "Quit muxer for recv ret %d", ret); + break; + } + if (frame.stream_type == STOP_CMD_STREAM_TYPE) { + ESP_LOGI(TAG, "Muxer receive stop"); + break; + } + if (frame.data == NULL || frame.size == 0) { + ESP_LOGE(TAG, "Receive quit frame"); + continue; + } + switch (frame.stream_type) { + case ESP_CAPTURE_STREAM_TYPE_AUDIO: { + esp_muxer_audio_packet_t audio_packet = { + .pts = frame.pts, + .data = frame.data, + .len = frame.size, + }; + path->muxer_cur_pts = frame.pts; + ret = esp_muxer_add_audio_packet(path->muxer, path->audio_stream_idx, &audio_packet); + share_q_release(path->audio_share_q, &frame); + } break; + case ESP_CAPTURE_STREAM_TYPE_VIDEO: { + esp_muxer_video_packet_t video_packet = { + .pts = frame.pts, + .data = frame.data, + .len = frame.size, + }; + path->muxer_cur_pts = frame.pts; + ret = esp_muxer_add_video_packet(path->muxer, path->video_stream_idx, &video_packet); + share_q_release(path->video_share_q, &frame); + } break; + default: + break; + } + } + ESP_LOGI(TAG, "Leave muxer thread"); + media_lib_event_group_set_bits(path->parent->event_group, EVENT_GROUP_MUXER_EXITED); + media_lib_thread_destroy(NULL); +} + +static uint32_t calc_audio_pts(capture_t *capture, uint32_t frames) +{ + if (capture->audio_src_info.sample_rate == 0) { + return 0; + } + return (uint32_t)((uint64_t)frames * capture->audio_frame_samples * 1000 / capture->audio_src_info.sample_rate); +} + +static bool has_active_path(capture_t *capture, esp_capture_stream_type_t type, bool check_finished) +{ + if (type == ESP_CAPTURE_STREAM_TYPE_AUDIO) { + for (int i = 0; i < capture->path_num; i++) { + capture_path_t *path = capture->path[i]; + if (path->enable && path->sink_cfg.audio_info.codec && path->audio_path_disabled == false) { + if (path->run_finished == false && check_finished) { + continue; + } + return true; + } + } + } else if (type == ESP_CAPTURE_STREAM_TYPE_VIDEO) { + for (int i = 0; i < capture->path_num; i++) { + capture_path_t *path = capture->path[i]; + if (path->enable && path->sink_cfg.video_info.codec && path->video_path_disabled == false) { + if (path->run_finished == false && check_finished) { + continue; + } + return true; + } + } + } + return false; +} + +static void audio_src_thread(void *arg) +{ + capture_t *capture = (capture_t *)arg; + ESP_LOGI(TAG, "Start to fetch audio src data now"); + while (capture->fetching_audio) { + // No active path drop data directly + if (has_active_path(capture, ESP_CAPTURE_STREAM_TYPE_AUDIO, false) == false) { + media_lib_thread_sleep(10); + continue; + } + // TODO how to calculate audio_frame_size + int frame_size = sizeof(esp_capture_stream_frame_t) + capture->audio_frame_size; + uint8_t *data = data_queue_get_buffer(capture->audio_src_q, frame_size); + if (data == NULL) { + ESP_LOGE(TAG, "Failed to get buffer from audio src queue"); + break; + } + esp_capture_stream_frame_t *frame = (esp_capture_stream_frame_t *)data; + frame->stream_type = ESP_CAPTURE_STREAM_TYPE_AUDIO; + frame->data = (data + sizeof(esp_capture_stream_frame_t)); + frame->size = capture->audio_frame_size; + int ret = capture->cfg.audio_src->read_frame(capture->cfg.audio_src, frame); + frame->pts = calc_audio_pts(capture, capture->audio_frames); + // printf("Audio Frame %d to pts:%d size:%d\n", (int)capture->audio_frames, (int)frame->pts, (int)capture->audio_frame_size); + if (ret != ESP_CAPTURE_ERR_OK) { + data_queue_send_buffer(capture->audio_src_q, 0); + ESP_LOGE(TAG, "Failed to read audio frame ret %d", ret); + break; + } + if (capture->sync_handle) { + esp_capture_sync_audio_update(capture->sync_handle, frame->pts); + if (capture->cfg.sync_mode != ESP_CAPTURE_SYNC_MODE_AUDIO) { + uint32_t cur_pts = 0; + esp_capture_sync_get_current(capture->sync_handle, &cur_pts); + if (frame->pts > cur_pts + CAPTURE_SYNC_TOLERANCE || frame->pts + CAPTURE_SYNC_TOLERANCE < cur_pts) { + frame->pts = cur_pts; + } + } + } + data_queue_send_buffer(capture->audio_src_q, frame_size); + capture->audio_frames++; + } + ESP_LOGI(TAG, "Audio src thread exited"); + media_lib_event_group_set_bits(capture->event_group, EVENT_GROUP_AUDIO_SRC_EXITED); + media_lib_thread_destroy(NULL); +} + +static uint32_t calc_video_pts(capture_t *capture, uint32_t frames) +{ + if (capture->video_src_info.fps == 0) { + return 0; + } + return (uint32_t)((uint64_t)frames * 1000 / capture->video_src_info.fps); +} + +static bool is_encoded_video(esp_capture_codec_type_t codec) +{ + return (codec == ESP_CAPTURE_CODEC_TYPE_H264 || codec == ESP_CAPTURE_CODEC_TYPE_MJPEG); +} + +static int capture_frame_processed(void *src, esp_capture_path_type_t sel, esp_capture_stream_frame_t *frame) +{ + capture_t *capture = (capture_t *)src; + if (capture == NULL || sel >= ESP_CAPTURE_PATH_MAX || capture->path[sel] == NULL) { + return ESP_CAPTURE_ERR_NOT_FOUND; + } + capture_path_t *path = capture->path[sel]; + // When path disable drop input data directly + if (path->sink_disabled) { + // can not release it here, let callback to handle it + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + int ret = ESP_CAPTURE_ERR_NOT_SUPPORTED; + switch (frame->stream_type) { + default: + break; + case ESP_CAPTURE_STREAM_TYPE_VIDEO: + if (path->video_share_q) { + ret = share_q_add(path->video_share_q, frame); + } + break; + case ESP_CAPTURE_STREAM_TYPE_AUDIO: + if (path->audio_share_q) { + ret = share_q_add(path->audio_share_q, frame); + } + break; + } + return ret; +} + +static int capture_path_event_reached(void *src, esp_capture_path_type_t sel, esp_capture_path_event_type_t event) +{ + capture_t *capture = (capture_t *)src; + if (capture == NULL || sel >= ESP_CAPTURE_PATH_MAX || capture->path[sel] == NULL) { + return ESP_CAPTURE_ERR_NOT_FOUND; + } + capture_path_t *path = capture->path[sel]; + switch (event) { + default: + break; + case ESP_CAPTURE_PATH_EVENT_AUDIO_NOT_SUPPORT: + case ESP_CAPTURE_PATH_EVENT_AUDIO_ERROR: { + path->audio_path_disabled = true; + // TODO send fake data into share queue can let user quit + // But will cause wrong share queue release not existed data + if (path->audio_share_q) { + esp_capture_stream_frame_t frame = { 0 }; + frame.stream_type = ESP_CAPTURE_STREAM_TYPE_AUDIO; + share_q_add(path->audio_share_q, &frame); + } + break; + } + case ESP_CAPTURE_PATH_EVENT_VIDEO_NOT_SUPPORT: + case ESP_CAPTURE_PATH_EVENT_VIDEO_ERROR: { + path->video_path_disabled = true; + if (path->video_share_q) { + esp_capture_stream_frame_t frame = { 0 }; + frame.stream_type = ESP_CAPTURE_STREAM_TYPE_VIDEO; + share_q_add(path->video_share_q, &frame); + } + break; + } + } + return ESP_CAPTURE_ERR_OK; +} + +static void video_src_thread(void *arg) +{ + capture_t *capture = (capture_t *)arg; + esp_capture_stream_frame_t frame = { + .stream_type = ESP_CAPTURE_STREAM_TYPE_VIDEO, + }; + ESP_LOGI(TAG, "Start to fetch video src data now"); + while (capture->fetching_video) { + if (has_active_path(capture, ESP_CAPTURE_STREAM_TYPE_VIDEO, false) == false) { + media_lib_thread_sleep(10); + continue; + } + int ret = capture->cfg.video_src->acquire_frame(capture->cfg.video_src, &frame); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Failed to acquire video frame"); + break; + } + uint32_t video_pts = calc_video_pts(capture, capture->video_frames); + // TODO not drop if is raw + if (capture->sync_handle) { + uint32_t cur_pts = 0; + esp_capture_sync_get_current(capture->sync_handle, &cur_pts); + if (video_pts > cur_pts && is_encoded_video(capture->video_src_info.codec) == false) { + // Drop current video + capture->cfg.video_src->release_frame(capture->cfg.video_src, &frame); + continue; + } else if (video_pts + CAPTURE_SYNC_TOLERANCE < cur_pts) { + // Video too slow force to use current pts + video_pts = cur_pts; + } + } + capture->video_frames++; + frame.pts = video_pts; + ret = msg_q_send(capture->video_src_q, &frame, sizeof(esp_capture_stream_frame_t)); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to send video frame to queue"); + capture->cfg.video_src->release_frame(capture->cfg.video_src, &frame); + break; + } + } + ESP_LOGI(TAG, "Video src thread exited"); + media_lib_event_group_set_bits(capture->event_group, EVENT_GROUP_VIDEO_SRC_EXITED); + media_lib_thread_destroy(NULL); +} + +static int capture_path_acquire_frame(void *src, esp_capture_stream_frame_t *frame, bool no_wait) +{ + capture_t *capture = (capture_t *)src; + int ret = ESP_CAPTURE_ERR_NOT_SUPPORTED; + switch (frame->stream_type) { + default: + break; + case ESP_CAPTURE_STREAM_TYPE_AUDIO: + if (capture->audio_src_q) { + void *data; + int size; + if (no_wait && data_queue_have_data(capture->audio_src_q) == false) { + ret = ESP_CAPTURE_ERR_NOT_FOUND; + break; + } + ret = data_queue_read_lock(capture->audio_src_q, &data, &size); + if (ret != 0 || data == NULL) { + ret = ESP_CAPTURE_ERR_INTERNAL; + break; + } + memcpy(frame, data, sizeof(esp_capture_stream_frame_t)); + ret = ESP_CAPTURE_ERR_OK; + } + break; + case ESP_CAPTURE_STREAM_TYPE_VIDEO: + if (capture->video_src_q) { + ret = msg_q_recv(capture->video_src_q, frame, sizeof(esp_capture_stream_frame_t), no_wait); + if (ret != 0) { + ret = ESP_CAPTURE_ERR_INTERNAL; + } + } + break; + } + return ret; +} + +static int capture_path_release_frame(void *src, esp_capture_stream_frame_t *frame) +{ + capture_t *capture = (capture_t *)src; + int ret = ESP_CAPTURE_ERR_NOT_SUPPORTED; + switch (frame->stream_type) { + default: + break; + case ESP_CAPTURE_STREAM_TYPE_AUDIO: + if (capture->audio_src_q) { + data_queue_read_unlock(capture->audio_src_q); + ret = ESP_CAPTURE_ERR_OK; + } + break; + case ESP_CAPTURE_STREAM_TYPE_VIDEO: + if (capture->video_src_q && frame->data) { + // Release video frame here + capture->cfg.video_src->release_frame(capture->cfg.video_src, frame); + ret = ESP_CAPTURE_ERR_OK; + } + break; + } + return ret; +} + +static int capture_path_nego_video_caps(void *src, esp_capture_video_info_t *in_cap, esp_capture_video_info_t *out_caps) +{ + capture_t *capture = (capture_t *)src; + if (capture->cfg.video_src == NULL) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + // TODO save out_caps to capture? + int ret = capture->cfg.video_src->negotiate_caps(capture->cfg.video_src, in_cap, out_caps); + if (ret == ESP_CAPTURE_ERR_OK) { + memcpy(&capture->video_src_info, out_caps, sizeof(esp_capture_video_info_t)); + capture->video_nego_done = true; + ESP_LOGI(TAG, "Video source caps negotiate done: %dx%d@%dfps", (int)out_caps->width, (int)out_caps->height, (int)out_caps->fps); + } + return ret; +} + +static int capture_path_nego_audio_caps(void *src, esp_capture_audio_info_t *in_cap, esp_capture_audio_info_t *out_caps) +{ + capture_t *capture = (capture_t *)src; + if (capture->cfg.audio_src == NULL) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + // TODO save out_caps to capture? + int ret = capture->cfg.audio_src->negotiate_caps(capture->cfg.audio_src, in_cap, out_caps); + if (ret == ESP_CAPTURE_ERR_OK) { + memcpy(&capture->audio_src_info, out_caps, sizeof(esp_capture_audio_info_t)); + ESP_LOGI(TAG, "Audio source caps negotiate done: %dHz, %d channels", (int)out_caps->sample_rate, (int)out_caps->channel); + capture->audio_nego_done = true; + } + return ret; +} + +static void *video_sink_get_q_data_ptr(void *item) +{ + esp_capture_stream_frame_t *frame = (esp_capture_stream_frame_t *)item; + return frame ? frame->data : NULL; +} + +static void *audio_sink_get_q_data_ptr(void *item) +{ + esp_capture_stream_frame_t *frame = (esp_capture_stream_frame_t *)item; + return frame ? frame->data : NULL; +} + +static int video_sink_release_frame(void *item, void *ctx) +{ + esp_capture_stream_frame_t *frame = (esp_capture_stream_frame_t *)item; + capture_path_t *path = (capture_path_t *)ctx; + esp_capture_path_if_t *capture_path = path->parent->cfg.capture_path; + int ret = capture_path->return_frame(capture_path, path->path_type, frame); + if (path->run_once) { + path->run_finished = true; + ESP_LOGI(TAG, "Capture once finished"); + } + return ret; +} + +static int audio_sink_release_frame(void *item, void *ctx) +{ + esp_capture_stream_frame_t *frame = (esp_capture_stream_frame_t *)item; + capture_path_t *path = (capture_path_t *)ctx; + esp_capture_path_if_t *capture_path = path->parent->cfg.capture_path; + ESP_LOGD(TAG, "Begin to return audio frame"); + return capture_path->return_frame(capture_path, path->path_type, frame); +} + +static int stop_muxer(capture_path_t *path) +{ + // Disable data sending firstly + if (path->video_share_q) { + share_q_enable(path->video_share_q, CAPTURE_SHARED_BY_MUXER, false); + } + if (path->audio_share_q) { + share_q_enable(path->audio_share_q, CAPTURE_SHARED_BY_MUXER, false); + } + // Wait for thread to quit + if (path->muxing) { + esp_capture_stream_frame_t frame = { 0 }; + frame.stream_type = STOP_CMD_STREAM_TYPE; + msg_q_send(path->muxer_q, &frame, sizeof(frame)); + media_lib_event_group_wait_bits(path->parent->event_group, EVENT_GROUP_MUXER_EXITED, 1000); + path->muxing = false; + } + // Close muxer + if (path->muxer) { + esp_muxer_close(path->muxer); + path->muxer = NULL; + } + path->muxer_started = false; + return ESP_CAPTURE_ERR_OK; +} + +static int start_muxer(capture_path_t *path) +{ + if (path->enable == false || path->parent->started == false) { + return ESP_CAPTURE_ERR_OK; + } + if (path->muxer_started) { + return ESP_CAPTURE_ERR_OK; + } + int ret = open_muxer(path); + do { + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Fail to open muxer"); + break; + } + path->muxing = true; + media_lib_thread_handle_t handle; + ret = media_lib_thread_create_from_scheduler(&handle, "Muxer", muxer_thread, path); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to create muxer thread"); + path->muxer_enable = false; + path->muxing = false; + ret = ESP_CAPTURE_ERR_NO_RESOURCES; + break; + } + } while (0); + // Enable muxer when muxer is ready + if (path->video_share_q) { + bool enable = path->muxer_enable && (path->video_stream_idx >= 0); + share_q_enable(path->video_share_q, CAPTURE_SHARED_BY_MUXER, enable); + } + if (path->audio_share_q) { + bool enable = path->muxer_enable && (path->audio_stream_idx >= 0); + share_q_enable(path->audio_share_q, CAPTURE_SHARED_BY_MUXER, enable); + } + if (ret != ESP_CAPTURE_ERR_OK) { + return ret; + } + path->muxer_started = true; + return ret; +} + +static int enable_muxer(capture_path_t *path, bool enable) +{ + if (path->muxer_enable == enable) { + return ESP_CAPTURE_ERR_OK; + } + path->muxer_enable = enable; + int ret; + if (enable) { + ret = start_muxer(path); + } else { + ret = stop_muxer(path); + } + return ret; +} + +static int prepare_src_queue(capture_path_t *path) +{ + capture_t *capture = path->parent; + if (path->sink_cfg.audio_info.codec != ESP_CAPTURE_CODEC_TYPE_NONE && capture->audio_src_q == NULL) { + // TODO need configure audio src q + capture->audio_src_q = data_queue_init(10 * 1024); + if (capture->audio_src_q == NULL) { + ESP_LOGE(TAG, "Failed to create audio src q"); + // Not support audio now + path->sink_cfg.audio_info.codec = ESP_CAPTURE_CODEC_TYPE_NONE; + } + } + + if (path->sink_cfg.video_info.codec != ESP_CAPTURE_CODEC_TYPE_NONE && capture->video_src_q == NULL) { + // TODO need refine queue number + capture->video_src_q = msg_q_create(5, sizeof(esp_capture_stream_frame_t)); + if (capture->video_src_q == NULL) { + ESP_LOGE(TAG, "Failed to create video src q"); + path->sink_cfg.video_info.codec = ESP_CAPTURE_CODEC_TYPE_NONE; + } + } + return (path->sink_cfg.video_info.codec || path->sink_cfg.audio_info.codec) ? ESP_CAPTURE_ERR_OK : ESP_CAPTURE_ERR_NO_RESOURCES; +} + +static int start_path(capture_path_t *path) +{ + capture_t *capture = path->parent; + // Do not prepare resource when not started yet + if (capture->started == false) { + return ESP_CAPTURE_ERR_OK; + } + // Prepare source queues firstly + int ret = prepare_src_queue(path); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Failed to prepare src queue"); + return ret; + } + if (capture->cfg.capture_path == NULL) { + return ESP_CAPTURE_ERR_OK; + } + // Clear path error status + path->audio_path_disabled = false; + path->video_path_disabled = false; + + if (path->muxer_cfg.muxer_type && path->muxer_q == NULL) { + path->muxer_q = msg_q_create(10, sizeof(esp_capture_stream_frame_t)); + if (path->muxer_q == NULL) { + ESP_LOGE(TAG, "Failed to create muxer q"); + path->muxer_cfg.muxer_type = ESP_CAPTURE_MUXER_TYPE_NONE; + } + } + // Create audio share queues and audio sink queue to hold sink output data + if (path->sink_cfg.audio_info.codec) { + if (path->muxer_cfg.muxer_only == false && path->audio_q == NULL) { + path->audio_q = msg_q_create(5, sizeof(esp_capture_stream_frame_t)); + if (path->audio_q == NULL) { + ESP_LOGE(TAG, "Failed to create audio q"); + } + } + // TODO support muxer when path_if not existed + if (capture->audio_src_q && capture->cfg.capture_path) { + // Create share queue to hold sink buffer + if (path->audio_share_q == NULL) { + uint8_t user_count = 1; + // Support muxer add user + if (path->muxer_cfg.muxer_type) { + user_count++; + } + share_q_cfg_t cfg = { + .user_count = user_count, + .q_count = 5, + .item_size = sizeof(esp_capture_stream_frame_t), + .get_frame_data = audio_sink_get_q_data_ptr, + .release_frame = audio_sink_release_frame, + .ctx = path, + .use_external_q = true, + }; + path->audio_share_q = share_q_create(&cfg); + } + if (path->audio_share_q == NULL) { + ESP_LOGE(TAG, "Failed to create share q for audio sink"); + } else { + if (path->audio_q) { + share_q_set_external(path->audio_share_q, CAPTURE_SHARED_BY_USER, path->audio_q); + share_q_enable(path->audio_share_q, CAPTURE_SHARED_BY_USER, path->enable); + } + if (path->muxer_q) { + share_q_set_external(path->audio_share_q, CAPTURE_SHARED_BY_MUXER, path->muxer_q); + share_q_enable(path->audio_share_q, CAPTURE_SHARED_BY_MUXER, path->muxer_enable); + } + } + } + } + // Create video share queues and video sink queue to hold sink output data + if (path->sink_cfg.video_info.codec) { + if (path->muxer_cfg.muxer_only == false && path->video_q == NULL) { + path->video_q = msg_q_create(5, sizeof(esp_capture_stream_frame_t)); + } + if (capture->video_src_q && capture->cfg.capture_path) { + // Create share queue to hold sink buffer + if (path->video_share_q == NULL) { + uint8_t user_count = 1; + if (path->muxer_cfg.muxer_type) { + user_count++; + } + share_q_cfg_t cfg = { + .user_count = user_count, + .q_count = 5, + .item_size = sizeof(esp_capture_stream_frame_t), + .get_frame_data = video_sink_get_q_data_ptr, + .release_frame = video_sink_release_frame, + .ctx = path, + .use_external_q = true, + }; + path->video_share_q = share_q_create(&cfg); + } + if (path->video_share_q == NULL) { + ESP_LOGE(TAG, "Failed to create share q for video sink"); + } else { + if (path->video_q) { + share_q_set_external(path->video_share_q, CAPTURE_SHARED_BY_USER, path->video_q); + share_q_enable(path->video_share_q, CAPTURE_SHARED_BY_USER, path->enable); + } + if (path->muxer_q) { + share_q_set_external(path->video_share_q, CAPTURE_SHARED_BY_MUXER, path->muxer_q); + share_q_enable(path->video_share_q, CAPTURE_SHARED_BY_MUXER, path->muxer_enable); + } + } + } + } + if (path->muxer_q) { + // Create muxer output queue if user want to fetch muxer data also + if (path->muxer_cfg.capture_muxer_data) { + int muxer_pool_size = MUXER_DEFAULT_POOL_SIZE; + if (path->muxer_cfg.muxer_cache_size) { + muxer_pool_size = path->muxer_cfg.muxer_cache_size; + } + if (path->muxer_data_q == NULL) { + path->muxer_data_q = data_queue_init(muxer_pool_size); + if (path->muxer_data_q == NULL) { + ESP_LOGE(TAG, "Fail to create output queue for muxer"); + } + } + } + if (path->muxer_data_q) { + start_muxer(path); + } + } + return (path->audio_share_q || path->video_share_q) ? ESP_CAPTURE_ERR_OK : ESP_CAPTURE_ERR_NO_MEM; +} + +static void release_path(capture_path_t *path) +{ + if (path->audio_q) { + msg_q_destroy(path->audio_q); + path->audio_q = NULL; + } + if (path->video_q) { + msg_q_destroy(path->video_q); + path->video_q = NULL; + } + if (path->muxer_q) { + msg_q_destroy(path->muxer_q); + path->muxer_q = NULL; + } + // Start to destroy queue + if (path->muxer_data_q) { + data_queue_deinit(path->muxer_data_q); + path->muxer_data_q = NULL; + } + if (path->audio_share_q) { + share_q_destroy(path->audio_share_q); + path->audio_share_q = NULL; + } + if (path->video_share_q) { + share_q_destroy(path->video_share_q); + path->video_share_q = NULL; + } +} + +static void consume_all_audio_src(capture_t *capture) +{ + if (capture->audio_src_q == NULL) { + return; + } + data_queue_consume_all(capture->audio_src_q); +} + +static void consume_all_video_src(capture_t *capture) +{ + if (capture->video_src_q == NULL) { + return; + } + esp_capture_stream_frame_t frame = {}; + while (msg_q_recv(capture->video_src_q, &frame, sizeof(frame), true) == 0) { + if (frame.size) { + capture->cfg.video_src->release_frame(capture->cfg.video_src, &frame); + } + } +} + +static int stop_path(capture_path_t *path) +{ + if (path->video_share_q) { + share_q_enable(path->video_share_q, CAPTURE_SHARED_BY_USER, false); + } + if (path->audio_share_q) { + share_q_enable(path->audio_share_q, CAPTURE_SHARED_BY_USER, false); + } + // Receive all data in src queue + if (!has_active_path(path->parent, ESP_CAPTURE_STREAM_TYPE_AUDIO, false)) { + consume_all_audio_src(path->parent); + } + if (!has_active_path(path->parent, ESP_CAPTURE_STREAM_TYPE_VIDEO, false)) { + consume_all_video_src(path->parent); + } + return ESP_CAPTURE_ERR_OK; +} + +static int capture_negotiate_directly(capture_t *capture, esp_capture_sink_cfg_t *sink_info) +{ + // Query src support it + int ret = ESP_CAPTURE_ERR_OK; + if (sink_info->audio_info.codec) { + ret = capture_path_nego_audio_caps(capture, &sink_info->audio_info, &capture->audio_src_info); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Audio src not support codec:%d sample_rate:%d channel:%d", + (int)sink_info->audio_info.codec, (int)sink_info->audio_info.sample_rate, (int)sink_info->audio_info.channel); + return ret; + } + } + if (sink_info->video_info.codec) { + ret = capture_path_nego_video_caps(capture, &sink_info->video_info, &capture->video_src_info); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Video src not support codec:%d resolution:%dx%d", + (int)sink_info->video_info.codec, (int)sink_info->video_info.width, (int)sink_info->video_info.height); + return ret; + } + } + return ret; +} + +static int capture_update_audio_frame_samples(capture_t *capture) +{ + esp_capture_audio_info_t *aud_info = &capture->audio_src_info; + int samples = 20 * aud_info->sample_rate / 1000; + int sample_size = aud_info->bits_per_sample * aud_info->channel >> 3; + capture->audio_frame_samples = samples; + capture->audio_frame_size = samples * sample_size; + + if (capture->cfg.capture_path == NULL) { + return ESP_CAPTURE_ERR_OK; + } + int path_samples = 0; + // Use min frame size for low latency output + for (int i = 0; i < capture->path_num; i++) { + capture_path_t *path = capture->path[i]; + if (path->enable == false || path->sink_cfg.audio_info.codec == ESP_CAPTURE_CODEC_TYPE_NONE) { + continue; + } + // Prepare queues and related resource + int need_sample = capture->cfg.capture_path->get_audio_frame_samples(capture->cfg.capture_path, path->path_type); + if (need_sample > 0) { + if (path_samples == 0) { + path_samples = need_sample; + } else if (need_sample < path_samples) { + path_samples = need_sample; + } + } + } + if (path_samples) { + capture->audio_frame_samples = path_samples; + capture->audio_frame_size = path_samples * sample_size; + } + return ESP_CAPTURE_ERR_OK; +} + +static int capture_start_src(capture_t *capture) +{ + int ret = ESP_CAPTURE_ERR_OK; + media_lib_thread_handle_t handle = NULL; + // Src always ready + if (capture->sync_handle) { + esp_capture_sync_start(capture->sync_handle); + } + if (capture->cfg.audio_src && capture->audio_nego_done && capture->fetching_audio == false) { + if (has_active_path(capture, ESP_CAPTURE_STREAM_TYPE_AUDIO, false) == false) { + return ESP_CAPTURE_ERR_OK; + } + // Try to get audio frame samples firstly + capture_update_audio_frame_samples(capture); + // Start audio src + capture->fetching_audio = true; + ret = capture->cfg.audio_src->start(capture->cfg.audio_src); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to start audio src"); + // TODO send empty data to capture path to let acquire frame quit?? + capture->fetching_audio = false; + } else { + // Create src thread to receive data + ret = media_lib_thread_create_from_scheduler(&handle, "AUD_SRC", audio_src_thread, capture); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to create audio src thread"); + capture->fetching_audio = false; + } + } + } + if (capture->cfg.video_src && capture->video_nego_done && capture->fetching_video == false) { + if (has_active_path(capture, ESP_CAPTURE_STREAM_TYPE_VIDEO, false) == false) { + return ESP_CAPTURE_ERR_OK; + } + capture->fetching_video = true; + ret = capture->cfg.video_src->start(capture->cfg.video_src); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to start video src"); + // TODO send empty data to capture path to let acquire frame quit?? + capture->fetching_video = false; + } else { + ret = media_lib_thread_create_from_scheduler(&handle, "VID_SRC", video_src_thread, capture); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to create video src thread"); + capture->fetching_video = false; + } + } + } + return (capture->fetching_video || capture->fetching_audio) ? ESP_CAPTURE_ERR_OK : ESP_CAPTURE_ERR_NO_RESOURCES; +} + +static void capture_send_src_leave_data(capture_t *capture) +{ + if (capture->fetching_audio && capture->audio_src_q && has_active_path(capture, ESP_CAPTURE_STREAM_TYPE_VIDEO, true) && data_queue_have_data(capture->audio_src_q) == false) { + int frame_size = sizeof(esp_capture_stream_frame_t); + uint8_t *data = data_queue_get_buffer(capture->audio_src_q, frame_size); + if (data) { + esp_capture_stream_frame_t *frame = (esp_capture_stream_frame_t *)data; + memset(data, 0, sizeof(esp_capture_stream_frame_t)); + frame->stream_type = STOP_CMD_STREAM_TYPE; + data_queue_send_buffer(capture->audio_src_q, frame_size); + } + } + if (capture->fetching_video && capture->video_src_q && msg_q_number(capture->video_src_q) == 0 && has_active_path(capture, ESP_CAPTURE_STREAM_TYPE_VIDEO, true)) { + esp_capture_stream_frame_t frame = { + .stream_type = STOP_CMD_STREAM_TYPE, + }; + msg_q_send(capture->video_src_q, &frame, sizeof(esp_capture_stream_frame_t)); + } +} + +static void capture_stop_src(capture_t *capture) +{ + bool fetching_video = capture->fetching_video; + bool fetching_audio = capture->fetching_audio; + capture->fetching_audio = false; + capture->fetching_video = false; + // Wait for fetch thread quit + if (fetching_video) { + consume_all_video_src(capture); + media_lib_event_group_wait_bits(capture->event_group, EVENT_GROUP_VIDEO_SRC_EXITED, 1000); + media_lib_event_group_clr_bits(capture->event_group, EVENT_GROUP_VIDEO_SRC_EXITED); + // Consume again to flush all input data + consume_all_video_src(capture); + capture->cfg.video_src->stop(capture->cfg.video_src); + } + if (fetching_audio) { + consume_all_audio_src(capture); + media_lib_event_group_wait_bits(capture->event_group, EVENT_GROUP_AUDIO_SRC_EXITED, 1000); + media_lib_event_group_clr_bits(capture->event_group, EVENT_GROUP_AUDIO_SRC_EXITED); + consume_all_audio_src(capture); + capture->cfg.audio_src->stop(capture->cfg.audio_src); + } +} + +int esp_capture_open(esp_capture_cfg_t *cfg, esp_capture_handle_t *h) +{ + if (cfg == NULL || h == NULL || (cfg->audio_src == NULL && cfg->video_src == NULL)) { + ESP_LOGE(TAG, "Invalid argument cfg:%p capture:%p audio src:%p video_src:%p", + cfg, h, cfg ? cfg->audio_src : NULL, cfg ? cfg->video_src : NULL); + return ESP_CAPTURE_ERR_INVALID_ARG; + } + capture_t *capture = media_lib_calloc(1, sizeof(capture_t)); + if (capture == NULL) { + ESP_LOGE(TAG, "Failed to allocate memory for capture"); + return ESP_CAPTURE_ERR_NO_MEM; + } + do { + media_lib_event_group_create(&capture->event_group); + if (capture->event_group == NULL) { + break; + } + media_lib_mutex_create(&capture->api_lock); + if (capture->api_lock == NULL) { + break; + } + if (cfg->sync_mode != ESP_CAPTURE_SYNC_MODE_NONE) { + esp_capture_sync_create(cfg->sync_mode, &capture->sync_handle); + } + if (cfg->capture_path) { + esp_capture_path_cfg_t path_cfg = { + .acquire_src_frame = capture_path_acquire_frame, + .release_src_frame = capture_path_release_frame, + .src_ctx = capture, + .nego_audio = capture_path_nego_audio_caps, + .nego_video = capture_path_nego_video_caps, + .frame_processed = capture_frame_processed, + .event_cb = capture_path_event_reached, + }; + if (cfg->capture_path->open(cfg->capture_path, &path_cfg) != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Failed to open capture path"); + break; + } + } + int ret = ESP_CAPTURE_ERR_OK; + if (cfg->audio_src) { + ret = cfg->audio_src->open(cfg->audio_src); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Failed to open audio source"); + break; + } + } + if (cfg->video_src) { + cfg->video_src->open(cfg->video_src); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Failed to open video source"); + break; + } + } + capture->cfg = *cfg; + *h = capture; + return ESP_CAPTURE_ERR_OK; + } while (0); + esp_capture_close(capture); + return ESP_CAPTURE_ERR_NO_RESOURCES; +} + +int esp_capture_setup_path(esp_capture_handle_t h, esp_capture_path_type_t type, esp_capture_sink_cfg_t *sink_info, esp_capture_path_handle_t *path) +{ + capture_t *capture = (capture_t *)h; + if (capture == NULL || sink_info == NULL || path == NULL || (sink_info->audio_info.codec == ESP_CAPTURE_CODEC_TYPE_NONE && sink_info->video_info.codec == ESP_CAPTURE_CODEC_TYPE_NONE)) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + media_lib_mutex_lock(capture->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = ESP_CAPTURE_ERR_OK; + do { + if (capture->path_num >= ESP_CAPTURE_PATH_MAX) { + ESP_LOGE(TAG, "Only support max path %d", ESP_CAPTURE_PATH_MAX); + BREAK_SET_RETURN(ESP_CAPTURE_ERR_NOT_ENOUGH); + } + if (capture->started) { + ESP_LOGE(TAG, "Not support add path after started"); + BREAK_SET_RETURN(ESP_CAPTURE_ERR_INVALID_STATE); + } + if (capture->cfg.capture_path == NULL) { + if (capture->path_num) { + ESP_LOGE(TAG, "Only support one path when path interface not set"); + BREAK_SET_RETURN(ESP_CAPTURE_ERR_NOT_SUPPORTED); + } + } + // Path already added + if (capture->path[type]) { + capture_path_t *cur = capture->path[type]; + if (cur->enable && capture->started) { + ESP_LOGW(TAG, "Not allowed to change sink during running"); + BREAK_SET_RETURN(ESP_CAPTURE_ERR_INVALID_STATE); + } + cur->sink_cfg = *sink_info; + *path = (esp_capture_path_handle_t)cur; + BREAK_SET_RETURN(ESP_CAPTURE_ERR_OK); + } + capture->path[capture->path_num] = (capture_path_t *)media_lib_calloc(1, sizeof(capture_path_t)); + if (path == NULL) { + BREAK_SET_RETURN(ESP_CAPTURE_ERR_NO_MEM); + } + capture_path_t *cur = capture->path[capture->path_num]; + cur->path_type = type; + cur->sink_cfg = *sink_info; + cur->parent = capture; + + if (capture->cfg.capture_path == NULL) { + ret = capture_negotiate_directly(capture, sink_info); + } else { + ret = capture->cfg.capture_path->add_path(capture->cfg.capture_path, cur->path_type, sink_info); + } + if (ret != ESP_CAPTURE_ERR_OK) { + media_lib_free(cur); + BREAK_SET_RETURN(ret); + } + capture->path_num++; + *path = (esp_capture_path_handle_t)cur; + ret = ESP_CAPTURE_ERR_OK; + } while (0); + media_lib_mutex_unlock(capture->api_lock); + return ret; +} + +int esp_capture_add_muxer_to_path(esp_capture_path_handle_t h, esp_capture_muxer_cfg_t *muxer_cfg) +{ + capture_path_t *path = (capture_path_t *)h; + + if (path == NULL || muxer_cfg == NULL || path->parent == NULL || muxer_cfg->muxer_type == ESP_CAPTURE_MUXER_TYPE_NONE) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + capture_t *capture = path->parent; + int ret = ESP_CAPTURE_ERR_OK; + media_lib_mutex_lock(capture->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + do { + if (capture->started) { + ESP_LOGE(TAG, "Not support add muxer after started"); + BREAK_SET_RETURN(ESP_CAPTURE_ERR_INVALID_STATE); + } + if (path->muxer) { + ESP_LOGE(TAG, "Muxer already added"); + BREAK_SET_RETURN(ESP_CAPTURE_ERR_INVALID_STATE); + } + path->muxer_cfg = *muxer_cfg; + } while (0); + media_lib_mutex_unlock(capture->api_lock); + return ret; +} + +int esp_capture_add_overlay_to_path(esp_capture_path_handle_t h, esp_capture_overlay_if_t *overlay) +{ + capture_path_t *path = (capture_path_t *)h; + if (path == NULL || path->parent == NULL || overlay == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + capture_t *capture = path->parent; + media_lib_mutex_lock(capture->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = ESP_CAPTURE_ERR_OK; + if (capture->cfg.capture_path == NULL || capture->cfg.capture_path->add_overlay == NULL) { + ESP_LOGE(TAG, "Capture path not added, not support overlay"); + ret = ESP_CAPTURE_ERR_NOT_SUPPORTED; + } else { + ret = capture->cfg.capture_path->add_overlay(capture->cfg.capture_path, path->path_type, overlay); + } + media_lib_mutex_unlock(capture->api_lock); + return ret; +} + +int esp_capture_enable_muxer(esp_capture_path_handle_t h, bool enable) +{ + capture_path_t *path = (capture_path_t *)h; + if (path == NULL || path->parent == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + media_lib_mutex_lock(path->parent->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = enable_muxer(path, enable); + media_lib_mutex_unlock(path->parent->api_lock); + return ret; +} + +int esp_capture_enable_overlay(esp_capture_path_handle_t h, bool enable) +{ + capture_path_t *path = (capture_path_t *)h; + if (path == NULL || path->parent == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + capture_t *capture = path->parent; + media_lib_mutex_lock(capture->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + int ret = ESP_CAPTURE_ERR_OK; + if (capture->cfg.capture_path == NULL || capture->cfg.capture_path->enable_overlay == NULL) { + ESP_LOGE(TAG, "Capture path not added, not support overlay"); + ret = ESP_CAPTURE_ERR_NOT_SUPPORTED; + } else { + ret = capture->cfg.capture_path->enable_overlay(capture->cfg.capture_path, path->path_type, enable); + } + media_lib_mutex_unlock(capture->api_lock); + return ret; +} + +int esp_capture_enable_path(esp_capture_path_handle_t h, esp_capture_run_type_t run_type) +{ + capture_path_t *path = (capture_path_t *)h; + if (path == NULL || path->parent == NULL) { + printf("Fail for path:%p\n", path); + return ESP_CAPTURE_ERR_INVALID_ARG; + } + capture_t *capture = path->parent; + media_lib_mutex_lock(capture->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + bool enable = run_type != ESP_CAPTURE_RUN_TYPE_DISABLE; + if (enable) { + // Clear capture once finish state, so that src will start to send data + path->run_finished = false; + } + if (path->enable == enable) { + media_lib_mutex_unlock(capture->api_lock); + return ESP_CAPTURE_ERR_OK; + } + int ret = ESP_CAPTURE_ERR_OK; + if (enable) { + path->enable = true; + path->sink_disabled = false; + path->run_once = (run_type == ESP_CAPTURE_RUN_TYPE_ONCE); + // Prepare so that data pushed to path queue + ret = start_path(path); + } else { + // Stop from last to first one src -> path -> muxer + path->sink_disabled = true; + stop_muxer(path); + esp_capture_stream_frame_t frame = { 0 }; + if (path->video_share_q) { + share_q_recv_all(path->video_share_q, &frame); + } + if (path->audio_share_q) { + share_q_recv_all(path->audio_share_q, &frame); + } + // Avoid enable once finished, not enable again + capture_send_src_leave_data(path->parent); + } + if (capture->cfg.capture_path) { + ret = capture->cfg.capture_path->enable_path(capture->cfg.capture_path, path->path_type, enable); + } + path->enable = enable; + if (enable == false) { + ret = stop_path(path); + if (has_active_path(capture, ESP_CAPTURE_STREAM_TYPE_VIDEO, false) == false) { + capture->audio_nego_done = false; + } + if (has_active_path(capture, ESP_CAPTURE_STREAM_TYPE_AUDIO, false) == false) { + capture->video_nego_done = false; + } + } else { + if (capture->started) { + ret = capture_start_src(capture); + } + } + media_lib_mutex_unlock(capture->api_lock); + return ret; +} + +int esp_capture_start(esp_capture_handle_t h) +{ + capture_t *capture = (capture_t *)h; + if (capture == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + media_lib_mutex_lock(capture->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + if (capture->started) { + media_lib_mutex_unlock(capture->api_lock); + ESP_LOGW(TAG, "Already started"); + return ESP_CAPTURE_ERR_OK; + } + int ret = ESP_CAPTURE_ERR_OK; + capture->started = true; + for (int i = 0; i < capture->path_num; i++) { + capture_path_t *path = capture->path[i]; + if (path == NULL) { + continue; + } + // Prepare queues and related resource + ret = start_path(path); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Failed to start capture path %d", i); + // When fail try to start next path + continue; + } + if (capture->cfg.capture_path) { + ret = capture->cfg.capture_path->start(capture->cfg.capture_path); + } + } + // Start source when path ready + ret = capture_start_src(capture); + media_lib_mutex_unlock(capture->api_lock); + return ret; +} + +int esp_capture_set_path_bitrate(esp_capture_path_handle_t h, esp_capture_stream_type_t stream_type, uint32_t bitrate) +{ + capture_path_t *path = (capture_path_t *)h; + if (path == NULL || path->parent == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + capture_t *capture = path->parent; + media_lib_mutex_lock(capture->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + if (capture->cfg.capture_path == NULL) { + ESP_LOGE(TAG, "Capture path not supported"); + media_lib_mutex_unlock(capture->api_lock); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + esp_capture_path_set_type_t type = ESP_CAPTURE_PATH_SET_TYPE_NONE; + if (stream_type == ESP_CAPTURE_STREAM_TYPE_VIDEO) { + type = ESP_CAPTURE_PATH_SET_TYPE_VIDEO_BITRATE; + } else if (stream_type == ESP_CAPTURE_STREAM_TYPE_VIDEO) { + type = ESP_CAPTURE_PATH_SET_TYPE_AUDIO_BITRATE; + } + int ret = capture->cfg.capture_path->set(capture->cfg.capture_path, path->path_type, type, &bitrate, sizeof(uint32_t)); + media_lib_mutex_unlock(capture->api_lock); + return ret; +} + +int esp_capture_acquire_path_frame(esp_capture_path_handle_t h, esp_capture_stream_frame_t *frame, bool no_wait) +{ + capture_path_t *path = (capture_path_t *)h; + if (path == NULL || path->parent == NULL || frame == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + capture_t *capture = path->parent; + // TODO not add lock user need care the timing + if (path->enable == false) { + ESP_LOGE(TAG, "Capture path %d is not enabled", path->path_type); + return ESP_CAPTURE_ERR_INVALID_STATE; + } + int ret = ESP_CAPTURE_ERR_NOT_SUPPORTED; + switch (frame->stream_type) { + case ESP_CAPTURE_STREAM_TYPE_VIDEO: + if (capture->cfg.capture_path == NULL) { + // Get from src directly + return capture_path_acquire_frame(capture, frame, no_wait); + } + if (path->video_path_disabled) { + if (path->video_share_q) { + share_q_recv_all(path->video_share_q, frame); + } + return ESP_CAPTURE_ERR_NOT_FOUND; + } + // TODO check whether share q send frame only + if (path->video_q) { + ret = msg_q_recv(path->video_q, frame, sizeof(esp_capture_stream_frame_t), no_wait); + ret = (ret == 0) ? ESP_CAPTURE_ERR_OK : ESP_CAPTURE_ERR_NOT_FOUND; + } + break; + case ESP_CAPTURE_STREAM_TYPE_AUDIO: + if (capture->cfg.capture_path == NULL) { + // Get from src directly + return capture_path_acquire_frame(capture, frame, no_wait); + } + if (path->audio_path_disabled) { + if (path->audio_share_q) { + share_q_recv_all(path->audio_share_q, frame); + } + return ESP_CAPTURE_ERR_NOT_FOUND; + } + if (path->audio_q) { + ret = msg_q_recv(path->audio_q, frame, sizeof(esp_capture_stream_frame_t), no_wait); + ret = (ret == 0) ? ESP_CAPTURE_ERR_OK : ESP_CAPTURE_ERR_NOT_FOUND; + } + break; + case ESP_CAPTURE_STREAM_TYPE_MUXER: + if (path->muxer_enable && path->muxer_data_q != NULL) { + int size = 0; + void *data = NULL; + if (no_wait == false) { + data_queue_read_lock(path->muxer_data_q, &data, &size); + } else if (data_queue_have_data(path->muxer_data_q)) { + data_queue_read_lock(path->muxer_data_q, &data, &size); + } + frame->size = 0; + if (data) { + frame->pts = *(uint32_t *)data; + frame->data = (uint8_t *)data + sizeof(uint32_t); + frame->size = size - sizeof(uint32_t); + } + ret = data ? ESP_CAPTURE_ERR_OK : ESP_CAPTURE_ERR_NOT_FOUND; + } + break; + default: + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + return ret; +} + +int esp_capture_release_path_frame(esp_capture_path_handle_t h, esp_capture_stream_frame_t *frame) +{ + capture_path_t *path = (capture_path_t *)h; + if (path == NULL || path->parent == NULL || frame == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + // TODO not add lock user need care the timing + capture_t *capture = path->parent; + if (path->enable == false) { + ESP_LOGE(TAG, "Capture path %d is not enabled", path->path_type); + return ESP_CAPTURE_ERR_INVALID_STATE; + } + int ret = ESP_CAPTURE_ERR_OK; + switch (frame->stream_type) { + case ESP_CAPTURE_STREAM_TYPE_VIDEO: + if (capture->cfg.capture_path == NULL) { + return capture_path_release_frame(capture, frame); + } + if (path->video_share_q) { + share_q_release(path->video_share_q, frame); + } + break; + case ESP_CAPTURE_STREAM_TYPE_AUDIO: + if (capture->cfg.capture_path == NULL) { + return capture_path_release_frame(capture, frame); + } + if (path->audio_share_q) { + share_q_release(path->audio_share_q, frame); + } + break; + case ESP_CAPTURE_STREAM_TYPE_MUXER: + if (path->muxer_enable && path->muxer_data_q != NULL) { + data_queue_read_unlock(path->muxer_data_q); + } + break; + default: + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + return ret; +} + +int esp_capture_stop(esp_capture_handle_t h) +{ + capture_t *capture = (capture_t *)h; + if (capture == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + media_lib_mutex_lock(capture->api_lock, MEDIA_LIB_MAX_LOCK_TIME); + if (capture->started == false) { + media_lib_mutex_unlock(capture->api_lock); + return ESP_CAPTURE_ERR_INVALID_STATE; + } + capture->started = false; + esp_capture_stream_frame_t frame = { 0 }; + + // Stop muxer before path for muxer may still keep capture path data + for (int i = 0; i < capture->path_num; i++) { + capture_path_t *path = capture->path[i]; + stop_muxer(path); + } + // Receive all output firstly to let capture path quit + for (int i = 0; i < capture->path_num; i++) { + capture_path_t *path = capture->path[i]; + // Disable path + path->sink_disabled = true; + if (path->video_share_q) { + share_q_recv_all(path->video_share_q, &frame); + } + if (path->audio_share_q) { + share_q_recv_all(path->audio_share_q, &frame); + } + } + capture_send_src_leave_data(capture); + // Stop path + if (capture->cfg.capture_path) { + capture->cfg.capture_path->stop(capture->cfg.capture_path); + } + // Send empty data to let user quit + for (int i = 0; i < capture->path_num; i++) { + capture_path_t *path = capture->path[i]; + if (path->video_share_q) { + share_q_recv_all(path->video_share_q, &frame); + frame.stream_type = ESP_CAPTURE_STREAM_TYPE_VIDEO; + share_q_add(path->video_share_q, &frame); + } + if (path->audio_share_q) { + share_q_recv_all(path->audio_share_q, &frame); + frame.stream_type = ESP_CAPTURE_STREAM_TYPE_AUDIO; + share_q_add(path->audio_share_q, &frame); + } + } + for (int i = 0; i < capture->path_num; i++) { + capture_path_t *path = capture->path[i]; + stop_path(path); + release_path(path); + path->sink_disabled = false; + } + capture_stop_src(capture); + if (capture->sync_handle) { + esp_capture_sync_stop(capture->sync_handle); + } + // Destroy src resources + if (capture->audio_src_q) { + data_queue_deinit(capture->audio_src_q); + capture->audio_src_q = NULL; + } + if (capture->video_src_q) { + msg_q_destroy(capture->video_src_q); + capture->video_src_q = NULL; + } + capture->audio_frames = 0; + capture->video_frames = 0; + media_lib_mutex_unlock(capture->api_lock); + return ESP_CAPTURE_ERR_OK; +} + +int esp_capture_close(esp_capture_handle_t h) +{ + if (h == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + capture_t *capture = (capture_t *)h; + esp_capture_stop(h); + if (capture->cfg.capture_path) { + capture->cfg.capture_path->close(capture->cfg.capture_path); + } + if (capture->cfg.audio_src) { + capture->cfg.audio_src->close(capture->cfg.audio_src); + } + if (capture->cfg.video_src) { + capture->cfg.video_src->close(capture->cfg.video_src); + } + if (capture->event_group) { + media_lib_event_group_destroy(capture->event_group); + capture->event_group = NULL; + } + if (capture->api_lock) { + media_lib_mutex_destroy(capture->api_lock); + capture->api_lock = NULL; + } + if (capture->sync_handle) { + esp_capture_sync_destroy(capture->sync_handle); + capture->sync_handle = NULL; + } + media_lib_free(capture); + return ESP_CAPTURE_ERR_OK; +} diff --git a/components/third_party/esp_capture/src/esp_capture_sync.c b/components/third_party/esp_capture/src/esp_capture_sync.c new file mode 100644 index 0000000..e533968 --- /dev/null +++ b/components/third_party/esp_capture/src/esp_capture_sync.c @@ -0,0 +1,106 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "esp_capture_sync.h" +#include "esp_timer.h" +#include +#include + +#define ELAPSE(cur, last) (cur > last ? cur - last : cur + (0xFFFFFFFF - last)) +#define CUR() (uint32_t)(esp_timer_get_time() / 1000) + +typedef struct { + esp_capture_sync_mode_t mode; + uint32_t last_update_time; + uint32_t last_update_pts; + uint32_t last_audio_pts; + bool started; +} sync_t; + +int esp_capture_sync_create(esp_capture_sync_mode_t mode, esp_capture_sync_handle_t *handle) +{ + sync_t *sync = (sync_t *)calloc(1, sizeof(sync_t)); + if (sync == NULL) { + return ESP_CAPTURE_ERR_NO_MEM; + } + sync->mode = mode; + *handle = sync; + return ESP_CAPTURE_ERR_OK; +} + +int esp_capture_sync_audio_update(esp_capture_sync_handle_t handle, uint32_t aud_pts) +{ + sync_t *sync = (sync_t *)handle; + if (sync->mode == ESP_CAPTURE_SYNC_MODE_AUDIO) { + sync->last_update_time = CUR(); + sync->last_update_pts = sync->last_audio_pts = aud_pts; + } + return ESP_CAPTURE_ERR_OK; +} + +int esp_capture_sync_start(esp_capture_sync_handle_t handle) +{ + if (handle == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + sync_t *sync = (sync_t *)handle; + sync->started = true; + sync->last_update_time = CUR(); + return ESP_CAPTURE_ERR_OK; +} + +int esp_capture_sync_stop(esp_capture_sync_handle_t handle) +{ + if (handle == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + sync_t *sync = (sync_t *)handle; + sync->started = false; + return ESP_CAPTURE_ERR_OK; +} + +int esp_capture_sync_get_current(esp_capture_sync_handle_t handle, uint32_t *pts) +{ + if (handle == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + sync_t *sync = (sync_t *)handle; + if (sync->started == false) { + *pts = sync->last_update_pts; + return ESP_CAPTURE_ERR_OK; + } + uint32_t cur = CUR(); + uint32_t elapse = ELAPSE(cur, sync->last_update_time); + *pts = sync->last_update_pts + elapse; + return ESP_CAPTURE_ERR_OK; +} + +int esp_capture_sync_destroy(esp_capture_sync_handle_t handle) +{ + if (handle == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + free(handle); + return ESP_CAPTURE_ERR_OK; +} diff --git a/components/third_party/esp_capture/src/esp_capture_sync.h b/components/third_party/esp_capture/src/esp_capture_sync.h new file mode 100644 index 0000000..e360a96 --- /dev/null +++ b/components/third_party/esp_capture/src/esp_capture_sync.h @@ -0,0 +1,42 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include +#include "esp_capture.h" + +typedef void *esp_capture_sync_handle_t; + +int esp_capture_sync_create(esp_capture_sync_mode_t type, esp_capture_sync_handle_t *handle); + +int esp_capture_sync_audio_update(esp_capture_sync_handle_t handle, uint32_t aud_pts); + +int esp_capture_sync_start(esp_capture_sync_handle_t handle); + +int esp_capture_sync_get_current(esp_capture_sync_handle_t handle, uint32_t *pts); + +int esp_capture_sync_stop(esp_capture_sync_handle_t handle); + +int esp_capture_sync_destroy(esp_capture_sync_handle_t handle); \ No newline at end of file diff --git a/components/third_party/esp_capture/src/impl/capture_audio_enc/CMakeLists.txt b/components/third_party/esp_capture/src/impl/capture_audio_enc/CMakeLists.txt new file mode 100755 index 0000000..30112a8 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_audio_enc/CMakeLists.txt @@ -0,0 +1,7 @@ + +set(component_srcdirs "./") + +idf_component_register( + SRC_DIRS ${component_srcdirs} + INCLUDE_DIRS ./ +) diff --git a/components/third_party/esp_capture/src/impl/capture_audio_enc/capture_audio_enc.c b/components/third_party/esp_capture/src/impl/capture_audio_enc/capture_audio_enc.c new file mode 100644 index 0000000..23a8e0d --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_audio_enc/capture_audio_enc.c @@ -0,0 +1,277 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include "esp_audio_enc_default.h" +#include "esp_capture_types.h" +#include "esp_capture_aenc_if.h" +#include "esp_log.h" + +#define TAG "CAPTURE_AENC" +#define CAPTURE_AENC_DEF_FRAME_DURATION (20) // Default audio frame duration + +typedef struct { + esp_capture_aenc_if_t base; + bool open; + bool started; + esp_capture_audio_info_t info; + int bitrate; + int in_frame_size; + int out_frame_size; + esp_audio_enc_handle_t aenc_handle; +} general_aenc_t; + +typedef union { + esp_aac_enc_config_t aac_cfg; + esp_alac_enc_config_t alac_cfg; + esp_adpcm_enc_config_t adpcm_cfg; + esp_amrnb_enc_config_t amrnb_cfg; + esp_amrwb_enc_config_t amrwb_cfg; + esp_g711_enc_config_t g711_cfg; + esp_opus_enc_config_t opus_cfg; +} enc_all_cfg_t; + +#define ASSIGN_BASIC_CFG(cfg) { \ + cfg->sample_rate = info->sample_rate; \ + cfg->bits_per_sample = info->bits_per_sample; \ + cfg->channel = info->channel; \ +} + +static int get_encoder_config(esp_audio_enc_config_t *enc_cfg, esp_capture_audio_info_t *info) +{ + enc_all_cfg_t *all_cfg = (enc_all_cfg_t *)(enc_cfg->cfg); + switch (info->codec) { + case ESP_CAPTURE_CODEC_TYPE_AAC: { + esp_aac_enc_config_t *cfg = &all_cfg->aac_cfg; + ASSIGN_BASIC_CFG(cfg); + enc_cfg->cfg_sz = sizeof(esp_aac_enc_config_t); + cfg->bitrate = 90000; + cfg->adts_used = true; + break; + } + case ESP_CAPTURE_CODEC_TYPE_G711A: + case ESP_CAPTURE_CODEC_TYPE_G711U: { + esp_g711_enc_config_t *cfg = &all_cfg->g711_cfg; + enc_cfg->cfg_sz = sizeof(esp_g711_enc_config_t); + cfg->frame_duration = CAPTURE_AENC_DEF_FRAME_DURATION; // Use default frame duration + ASSIGN_BASIC_CFG(cfg); + break; + } + case ESP_CAPTURE_CODEC_TYPE_OPUS: { + esp_opus_enc_config_t *cfg = &all_cfg->opus_cfg; + ASSIGN_BASIC_CFG(cfg); + enc_cfg->cfg_sz = sizeof(esp_opus_enc_config_t); + cfg->bitrate = 90000; + cfg->frame_duration = ESP_OPUS_ENC_FRAME_DURATION_20_MS; + cfg->application_mode = ESP_OPUS_ENC_APPLICATION_AUDIO; + break; + } + default: + ESP_LOGE(TAG, "Not supported encoder type %d", info->codec); + return -1; + } + return 0; +} + +static esp_audio_type_t get_audio_codec_type(esp_capture_codec_type_t codec) +{ + switch (codec) { + case ESP_CAPTURE_CODEC_TYPE_AAC: + return ESP_AUDIO_TYPE_AAC; + case ESP_CAPTURE_CODEC_TYPE_G711A: + return ESP_AUDIO_TYPE_G711A; + case ESP_CAPTURE_CODEC_TYPE_G711U: + return ESP_AUDIO_TYPE_G711U; + case ESP_CAPTURE_CODEC_TYPE_OPUS: + return ESP_AUDIO_TYPE_OPUS; + default: + return ESP_AUDIO_TYPE_UNSUPPORT; + } +} + +static int general_aenc_get_support_codecs(esp_capture_aenc_if_t *h, const esp_capture_codec_type_t **codecs, uint8_t *num) +{ + static esp_capture_codec_type_t acodecs[] = { + ESP_CAPTURE_CODEC_TYPE_AAC, + ESP_CAPTURE_CODEC_TYPE_G711A, + ESP_CAPTURE_CODEC_TYPE_G711U, + ESP_CAPTURE_CODEC_TYPE_OPUS, + }; + *codecs = acodecs; + *num = sizeof(acodecs) / sizeof(acodecs[0]); + return ESP_CAPTURE_ERR_OK; +} + +static bool is_aenc_supported(esp_capture_aenc_if_t *h, esp_capture_audio_info_t *info) +{ + const esp_capture_codec_type_t *codec = NULL; + uint8_t num = 0; + general_aenc_get_support_codecs(h, &codec, &num); + for (int i = 0; i < num; i++) { + if (codec[i] == info->codec) { + return true; + } + } + return false; +} + +static int general_aenc_get_frame_size(esp_capture_aenc_if_t *h, int *in_frame_size, int *out_frame_size) +{ + general_aenc_t *aenc = (general_aenc_t *)h; + if (aenc == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + if (aenc->started == false) { + return ESP_CAPTURE_ERR_INVALID_STATE; + } + switch (aenc->info.codec) { + case ESP_CAPTURE_CODEC_TYPE_G711A: + case ESP_CAPTURE_CODEC_TYPE_G711U: + case ESP_CAPTURE_CODEC_TYPE_AAC: + case ESP_CAPTURE_CODEC_TYPE_OPUS: + esp_audio_enc_get_frame_size(aenc->aenc_handle, in_frame_size, out_frame_size); + break; + default: + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + return ESP_CAPTURE_ERR_OK; +} + +static int general_aenc_start(esp_capture_aenc_if_t *h, esp_capture_audio_info_t *info) +{ + general_aenc_t *aenc = (general_aenc_t *)h; + if (aenc == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + if (!is_aenc_supported(h, info)) { + ESP_LOGE(TAG, "codec %d not supported", info->codec); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + aenc->info = *info; + enc_all_cfg_t all_cfg = { 0 }; + esp_audio_enc_config_t enc_cfg = { + .type = get_audio_codec_type(info->codec), + .cfg = &all_cfg, + }; + // Get encoder configuration + if (get_encoder_config(&enc_cfg, info) != 0) { + ESP_LOGE(TAG, "Fail to get encoder config"); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + // Open encoder + int ret = esp_audio_enc_open(&enc_cfg, &aenc->aenc_handle); + if (ret != ESP_AUDIO_ERR_OK) { + ESP_LOGE(TAG, "Fail to open encoder ret: %d", ret); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + aenc->started = true; + general_aenc_get_frame_size(h, &aenc->in_frame_size, &aenc->out_frame_size); + ESP_LOGD(TAG, "%p Get frame size in:%d out:%d", h, aenc->in_frame_size, aenc->out_frame_size); + return ESP_CAPTURE_ERR_OK; +} + +static int general_aenc_set_bitrate(esp_capture_aenc_if_t *h, int bitrate) +{ + general_aenc_t *aenc = (general_aenc_t *)h; + if (aenc == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + aenc->bitrate = bitrate; + return ESP_CAPTURE_ERR_OK; +} + +static int general_aenc_encode_frame(esp_capture_aenc_if_t *h, esp_capture_stream_frame_t *raw, esp_capture_stream_frame_t *encoded) +{ + general_aenc_t *aenc = (general_aenc_t *)h; + if (aenc == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + if (aenc->started == false) { + return ESP_CAPTURE_ERR_INVALID_STATE; + } + if (raw->size != aenc->in_frame_size || encoded->size < aenc->out_frame_size) { + ESP_LOGE(TAG, "Bad frame size need in:%d vs %d need out:%d but %d", + aenc->in_frame_size, raw->size, aenc->out_frame_size, encoded->size); + return ESP_CAPTURE_ERR_INVALID_ARG; + } + esp_audio_enc_in_frame_t in_frame = { + .buffer = raw->data, + .len = raw->size, + }; + esp_audio_enc_out_frame_t out_frame = { + .buffer = encoded->data, + .len = encoded->size, + }; + int ret = esp_audio_enc_process(aenc->aenc_handle, &in_frame, &out_frame); + if (ret != ESP_AUDIO_ERR_OK) { + ESP_LOGE(TAG, "Fail to encode audio frame return %d", ret); + return ESP_CAPTURE_ERR_INTERNAL; + } + encoded->size = out_frame.encoded_bytes; + return ESP_CAPTURE_ERR_OK; +} + +static int general_aenc_stop(esp_capture_aenc_if_t *h) +{ + general_aenc_t *aenc = (general_aenc_t *)h; + if (aenc == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + if (aenc->started == false) { + ESP_LOGE(TAG, "Aenc already stopped"); + return ESP_CAPTURE_ERR_OK; + } + if (aenc->aenc_handle) { + esp_audio_enc_close(aenc->aenc_handle); + aenc->aenc_handle = NULL; + } + aenc->started = false; + return ESP_CAPTURE_ERR_OK; +} + +static esp_capture_aenc_if_t *general_aenc_clone(esp_capture_aenc_if_t *aenc_if) +{ + general_aenc_t *aenc = (general_aenc_t *)calloc(1, sizeof(general_aenc_t)); + if (aenc == NULL) { + return NULL; + } + aenc->base = ((general_aenc_t *)aenc_if)->base; + return &aenc->base; +} + +esp_capture_aenc_if_t *esp_capture_new_audio_encoder(void) +{ + general_aenc_t *aenc = (general_aenc_t *)calloc(1, sizeof(general_aenc_t)); + if (aenc == NULL) { + return NULL; + } + aenc->base.clone = general_aenc_clone; + aenc->base.get_support_codecs = general_aenc_get_support_codecs; + aenc->base.start = general_aenc_start; + aenc->base.get_frame_size = general_aenc_get_frame_size; + aenc->base.set_bitrate = general_aenc_set_bitrate; + aenc->base.encode_frame = general_aenc_encode_frame; + aenc->base.stop = general_aenc_stop; + return &aenc->base; +} diff --git a/components/third_party/esp_capture/src/impl/capture_audio_enc/idf_component.yml b/components/third_party/esp_capture/src/impl/capture_audio_enc/idf_component.yml new file mode 100644 index 0000000..a407850 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_audio_enc/idf_component.yml @@ -0,0 +1,4 @@ +dependencies: + espressif/esp_audio_codec: "~2.3.0" + esp_capture: + path: ../../../../esp_capture diff --git a/components/third_party/esp_capture/src/impl/capture_audio_src/CMakeLists.txt b/components/third_party/esp_capture/src/impl/capture_audio_src/CMakeLists.txt new file mode 100755 index 0000000..4360a3c --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_audio_src/CMakeLists.txt @@ -0,0 +1,7 @@ + +set(component_srcdirs "./") + +idf_component_register( + SRC_DIRS ${component_srcdirs} + INCLUDE_DIRS ./ +) \ No newline at end of file diff --git a/components/third_party/esp_capture/src/impl/capture_audio_src/capture_aud_aec_src.c b/components/third_party/esp_capture/src/impl/capture_audio_src/capture_aud_aec_src.c new file mode 100644 index 0000000..9abab04 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_audio_src/capture_aud_aec_src.c @@ -0,0 +1,462 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 +#include "esp_capture_types.h" +#include "esp_capture_audio_src_if.h" +#include "esp_capture_defaults.h" +#include "esp_codec_dev.h" +#include "esp_log.h" +#include "esp_aec.h" +#include "data_queue.h" +#include +#include "media_lib_os.h" +#include "esp_afe_sr_iface.h" +#include "esp_afe_sr_models.h" + +#define TAG "AUD_AEC_SRC" + +// #define DISABLE_AEC + +typedef struct { + esp_capture_audio_src_if_t base; + uint8_t channel; + uint8_t channel_mask; + esp_codec_dev_handle_t handle; + esp_capture_audio_info_t info; + uint64_t samples; + bool start; + bool open; + + data_queue_t *in_q; + uint8_t *cached_frame; + int cached_read_pos; + int cache_size; + int cache_fill; + bool in_quit; + bool stopping; + const esp_afe_sr_iface_t *aec_if; + esp_afe_sr_data_t *aec_data; +} audio_aec_src_t; + +static int cal_frame_length(esp_capture_audio_info_t *info) +{ + // 16ms, 1channel, 16bit + return 16 * info->sample_rate / 1000 * (16 / 8); +} + +static int open_afe(audio_aec_src_t *src) +{ + afe_config_t afe_config = AFE_CONFIG_DEFAULT(); + afe_config.vad_init = false; + afe_config.wakenet_init = false; + afe_config.afe_perferred_core = 1; + afe_config.afe_perferred_priority = 20; + // afe_config.memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_INTERNAL; + afe_config.pcm_config.mic_num = 1; + afe_config.pcm_config.ref_num = 1; + afe_config.pcm_config.total_ch_num = 1 + 1; + afe_config.aec_init = true; + afe_config.se_init = false; +#if 0 + afe_config.voice_communication_agc_init = true; + afe_config.voice_communication_agc_gain = algo->agc_gain; +#endif + afe_config.pcm_config.sample_rate = src->info.sample_rate; + afe_config.voice_communication_init = true; + src->aec_if = &ESP_AFE_VC_HANDLE; + src->aec_data = src->aec_if->create_from_config(&afe_config); + return 0; +} + +static int audio_aec_src_open(esp_capture_audio_src_if_t *h) +{ + audio_aec_src_t *src = (audio_aec_src_t *)h; + if (src->handle == NULL) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + src->samples = 0; + src->open = true; + return ESP_CAPTURE_ERR_OK; +} + +static int audio_aec_src_get_support_codecs(esp_capture_audio_src_if_t *src, const esp_capture_codec_type_t **codecs, uint8_t *num) +{ + static esp_capture_codec_type_t support_codecs[] = { ESP_CAPTURE_CODEC_TYPE_PCM }; + *codecs = support_codecs; + *num = 1; + return ESP_CAPTURE_ERR_OK; +} + +static int audio_aec_src_negotiate_caps(esp_capture_audio_src_if_t *h, esp_capture_audio_info_t *in_cap, esp_capture_audio_info_t *out_caps) +{ + audio_aec_src_t *src = (audio_aec_src_t *)h; + // Only support 1 channel 16bits PCM + if (in_cap->channel != 1 || in_cap->bits_per_sample != 16 || in_cap->codec != ESP_CAPTURE_CODEC_TYPE_PCM) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + *out_caps = *in_cap; + src->info = *in_cap; + return ESP_CAPTURE_ERR_OK; +} + +static bool aec_dump_enabled = 0; +static uint8_t *origin_data = NULL; +static int origin_size; +static int origin_fill; +static uint8_t *aec_dump_data = NULL; +static int aec_dump_size = 0; +static int aec_dump_fill = 0; +static media_lib_mutex_handle_t dump_mutex; + +static void enable_aec_dump(bool enable) +{ + if (origin_data == NULL) { + origin_data = (uint8_t *)malloc(4096); + if (origin_data == NULL) { + ESP_LOGE(TAG, "Failed to malloc origin_data"); + return; + } + origin_size = 4096; + } + if (dump_mutex == NULL) { + media_lib_mutex_create(&dump_mutex); + if (dump_mutex == NULL) { + return; + } + } + if (aec_dump_data == NULL) { + aec_dump_data = (uint8_t *)malloc(800 * 1024); + if (aec_dump_data == NULL) { + ESP_LOGE(TAG, "Failed to malloc AEC dump data"); + return; + } + aec_dump_size = 800 * 1024; + } + if (enable) { + origin_fill = 0; + aec_dump_fill = 0; + } + aec_dump_enabled = enable; +} + +static void add_output_data(uint8_t *data, int size) +{ + if (aec_dump_enabled == false) { + return; + } + int need_size = size * 3; + if (aec_dump_fill + need_size > aec_dump_size || origin_fill < size * 2) { + media_lib_mutex_lock(dump_mutex, 1000); + origin_fill -= size * 2; + media_lib_mutex_unlock(dump_mutex); + return; + } + media_lib_mutex_lock(dump_mutex, 1000); + int16_t *lr = (int16_t *)origin_data; + int16_t *act = (int16_t *)data; + int16_t *out = (int16_t *)(aec_dump_data + aec_dump_fill); + int samples = size >> 1; + while (samples > 0) { + *(out++) = *(lr++); + *(out++) = *(lr++); + *(out++) = *(act++); + samples--; + } + origin_fill -= size * 2; + memmove(origin_data, origin_data + size * 2, origin_fill); + aec_dump_fill += need_size; + media_lib_mutex_unlock(dump_mutex); +} + +static void add_origin_data(uint8_t *data, int size) +{ + if (aec_dump_enabled == false) { + return; + } + if (origin_fill + size < origin_size) { + media_lib_mutex_lock(dump_mutex, 1000); + memcpy(origin_data + origin_fill, data, size); + origin_fill += size; + media_lib_mutex_unlock(dump_mutex); + } else { + ESP_LOGE(TAG, "Read too slow"); + } +} + +#ifdef AEC_ADD_READ_THREAD +static void audio_read_thread(void *arg) +{ + audio_aec_src_t *src = (audio_aec_src_t *)arg; + int read_size = src->cache_size * 2; + while (1) { + uint8_t *feed_data = (uint8_t *)data_queue_get_buffer(src->in_q, read_size); + if (feed_data == NULL) { + break; + } + int ret = esp_codec_dev_read(src->handle, feed_data, read_size); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to read data %d", ret); + data_queue_send_buffer(src->in_q, 0); + break; + } + data_queue_send_buffer(src->in_q, read_size); + } + media_lib_thread_destroy(NULL); +} + +static void audio_aec_src_buffer_in_thread(void *arg) +{ + audio_aec_src_t *src = (audio_aec_src_t *)arg; + int read_size = src->cache_size * 2; + src->in_q = data_queue_init(32 * 1024); + int ret = -1; + if (src->in_q) { + ret = media_lib_thread_create_from_scheduler(NULL, "SrcRead", audio_read_thread, src); + } + int time_size = 0; + while (!src->stopping && ret == 0) { + void *feed_data = NULL; + int ret = data_queue_read_lock(src->in_q, &feed_data, &read_size); + if (feed_data == NULL) { + break; + } + time_size += read_size; + if (time_size > 32000) { + time_size = 0; + int q_num = 0, q_size = 0; + data_queue_query(src->in_q, &q_num, &q_size); + printf("Cached %d\n", q_size); + } + ret = src->aec_if->feed(src->aec_data, (int16_t *)feed_data); + if (ret < 0) { + ESP_LOGE(TAG, "Fail to feed data %d", ret); + break; + } + add_origin_data(feed_data, read_size); + data_queue_read_unlock(src->in_q); + } + printf("wait for read quit\n"); + if (src->in_q) { + data_queue_wakeup(src->in_q); + data_queue_deinit(src->in_q); + src->in_q = NULL; + } + + src->in_quit = true; + printf("Quit done\n"); + media_lib_thread_destroy(NULL); +} + +#else +static void audio_aec_src_buffer_in_thread(void *arg) +{ + audio_aec_src_t *src = (audio_aec_src_t *)arg; + int read_size = src->cache_size * 2; + uint8_t *feed_data = malloc(read_size); + if (feed_data) { + while (!src->stopping) { + int ret = esp_codec_dev_read(src->handle, feed_data, read_size); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to read data %d", ret); + break; + } + ret = src->aec_if->feed(src->aec_data, (int16_t *)feed_data); + if (ret < 0) { + ESP_LOGE(TAG, "Fail to feed data %d", ret); + break; + } + add_origin_data(feed_data, read_size); + } + free(feed_data); + } + src->in_quit = true; + printf("Quit done\n"); + media_lib_thread_destroy(NULL); +} +#endif + +static int audio_aec_src_start(esp_capture_audio_src_if_t *h) +{ + audio_aec_src_t *src = (audio_aec_src_t *)h; + esp_codec_dev_sample_info_t fs = { + .sample_rate = src->info.sample_rate, + .bits_per_sample = 16, +#ifdef DISABLE_AEC + .channel = 1, + .channel_mask = 1, +#else + .channel = src->channel, + .channel_mask = src->channel_mask, +#endif + }; + src->in_quit = true; + printf("Start to open channel %d mask %x\n", fs.channel, fs.channel_mask); + int ret = esp_codec_dev_open(src->handle, &fs); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to open codec device, ret=%d", ret); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } +#ifndef DISABLE_AEC + src->cache_size = cal_frame_length(&src->info); + ret = open_afe(src); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to open AFE"); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + int audio_chunksize = src->aec_if->get_feed_chunksize(src->aec_data); + printf("Audio chunksize %d\n", audio_chunksize); + src->cache_size = audio_chunksize * (16 / 8); + + src->cached_frame = calloc(1, src->cache_size * 2); + if (src->cached_frame == NULL) { + ESP_LOGE(TAG, "Failed to allocate cache frame"); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + src->samples = 0; + src->cached_read_pos = src->cache_fill = 0; + src->stopping = false; + + media_lib_thread_handle_t thread = NULL; + media_lib_thread_create_from_scheduler(&thread, "buffer_in", audio_aec_src_buffer_in_thread, src); +#endif + src->start = true; + src->in_quit = false; + return ESP_CAPTURE_ERR_OK; +} + +static int audio_aec_src_read_frame(esp_capture_audio_src_if_t *h, esp_capture_stream_frame_t *frame) +{ + audio_aec_src_t *src = (audio_aec_src_t *)h; + if (src->start == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + frame->pts = (uint32_t)(src->samples * 1000 / src->info.sample_rate); +#ifndef DISABLE_AEC + int need_size = frame->size; + uint8_t *frame_data = frame->data; + while (need_size > 0) { + if (src->cached_read_pos < src->cache_fill) { + int left = src->cache_fill - src->cached_read_pos; + if (left > need_size) { + left = need_size; + } + memcpy(frame_data, src->cached_frame + src->cached_read_pos, left); + src->cached_read_pos += left; + need_size -= left; + frame_data += left; + continue; + } + if (src->in_quit) { + return ESP_CAPTURE_ERR_INTERNAL; + } + src->cache_fill = 0; + src->cached_read_pos = 0; + afe_fetch_result_t *res = src->aec_if->fetch(src->aec_data); + if (res->ret_value != ESP_OK) { + ESP_LOGE(TAG, "Fail to read from AEC"); + return -1; + } + if (res->data_size <= src->cache_size * 2) { + memcpy(src->cached_frame, res->data, res->data_size); + add_output_data(src->cached_frame, res->data_size); + src->cache_fill = res->data_size; + } else { + ESP_LOGE(TAG, "Why so huge %d", res->data_size); + } + } +#else + int ret = esp_codec_dev_read(src->handle, frame->data, frame->size); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to read data %d", ret); + return -1; + } + frame->pts = (uint32_t)(src->samples * 1000 / src->info.sample_rate); +#endif + src->samples += frame->size / 2; + return ESP_CAPTURE_ERR_OK; +} + +static int audio_aec_src_stop(esp_capture_audio_src_if_t *h) +{ + audio_aec_src_t *src = (audio_aec_src_t *)h; + printf("audio_aec_src_stop\n"); +#ifndef DISABLE_AEC + if (src->in_quit == false) { + // fetch once + src->aec_if->fetch(src->aec_data); + src->stopping = true; + while (src->in_quit == false) { + media_lib_thread_sleep(10); + } + } + if (src->aec_data) { + src->aec_if->destroy(src->aec_data); + src->aec_data = NULL; + } + if (src->cached_frame) { + free(src->cached_frame); + src->cached_frame = NULL; + } +#endif + if (src->handle) { + esp_codec_dev_close(src->handle); + } + src->start = false; + return ESP_CAPTURE_ERR_OK; +} + +static int audio_aec_src_close(esp_capture_audio_src_if_t *h) +{ + audio_aec_src_t *src = (audio_aec_src_t *)h; + src->handle = NULL; + return ESP_CAPTURE_ERR_OK; +} + +void esp_capture_enable_aec_src_dump(bool enable) +{ + enable_aec_dump(enable); +} + +esp_capture_audio_src_if_t *esp_capture_new_audio_aec_src(esp_capture_audio_aec_src_cfg_t *cfg) +{ + if (cfg == NULL || cfg->record_handle == NULL) { + return NULL; + } + audio_aec_src_t *src = calloc(1, sizeof(audio_aec_src_t)); + src->base.open = audio_aec_src_open; + src->base.get_support_codecs = audio_aec_src_get_support_codecs; + src->base.negotiate_caps = audio_aec_src_negotiate_caps; + src->base.start = audio_aec_src_start; + src->base.read_frame = audio_aec_src_read_frame; + src->base.stop = audio_aec_src_stop; + src->base.close = audio_aec_src_close; + src->handle = cfg->record_handle; + src->channel = cfg->channel ? cfg->channel : 2; + src->channel_mask = cfg->channel_mask; + return &src->base; +} + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_audio_src/capture_audio_codec_src.c b/components/third_party/esp_capture/src/impl/capture_audio_src/capture_audio_codec_src.c new file mode 100644 index 0000000..24db744 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_audio_src/capture_audio_codec_src.c @@ -0,0 +1,145 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "esp_capture_types.h" +#include "esp_capture_audio_src_if.h" +#include "esp_capture_defaults.h" +#include "esp_codec_dev.h" +#include "esp_log.h" +#include + +#define TAG "CODEC_DEV_SRC" + +typedef struct { + esp_capture_audio_src_if_t base; + esp_codec_dev_handle_t handle; + esp_capture_audio_info_t info; + int frame_num; + uint64_t frames; + bool start; + bool open; +} codec_dev_src_t; + +static int codec_dev_src_open(esp_capture_audio_src_if_t *h) +{ + codec_dev_src_t *src = (codec_dev_src_t *)h; + if (src->handle == NULL) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + src->frame_num = 0; + src->open = true; + return ESP_CAPTURE_ERR_OK; +} + +static int codec_dev_src_get_support_codecs(esp_capture_audio_src_if_t *src, const esp_capture_codec_type_t **codecs, uint8_t *num) +{ + static esp_capture_codec_type_t support_codecs[] = { ESP_CAPTURE_CODEC_TYPE_PCM }; + *codecs = support_codecs; + *num = 1; + return ESP_CAPTURE_ERR_OK; +} + +static int codec_dev_src_negotiate_caps(esp_capture_audio_src_if_t *h, esp_capture_audio_info_t *in_cap, esp_capture_audio_info_t *out_caps) +{ + codec_dev_src_t *src = (codec_dev_src_t *)h; + const esp_capture_codec_type_t *codecs = NULL; + uint8_t num = 0; + codec_dev_src_get_support_codecs(h, &codecs, &num); + for (int i = 0; i < num; i++) { + if (codecs[i] == in_cap->codec) { + memcpy(out_caps, in_cap, sizeof(esp_capture_audio_info_t)); + src->info = *in_cap; + return ESP_CAPTURE_ERR_OK; + } + } + return ESP_CAPTURE_ERR_NOT_SUPPORTED; +} + +static int codec_dev_src_start(esp_capture_audio_src_if_t *h) +{ + codec_dev_src_t *src = (codec_dev_src_t *)h; + esp_codec_dev_sample_info_t fs = { + .sample_rate = src->info.sample_rate, + .bits_per_sample = src->info.bits_per_sample, + .channel = src->info.channel, + }; + int ret = esp_codec_dev_open(src->handle, &fs); + if (ret != 0) { + ESP_LOGE(TAG, "Failed to open codec device, ret=%d", ret); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + src->start = true; + src->frame_num = 0; + src->frames = 0; + return ESP_CAPTURE_ERR_OK; +} + +static int codec_dev_src_read_frame(esp_capture_audio_src_if_t *h, esp_capture_stream_frame_t *frame) +{ + codec_dev_src_t *src = (codec_dev_src_t *)h; + if (src->start == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + int ret = esp_codec_dev_read(src->handle, frame->data, frame->size); + if (ret == 0) { + int samples = frame->size / (src->info.bits_per_sample / 8 * src->info.channel); + frame->pts = src->frame_num * samples * 1000 / src->info.sample_rate; + src->frame_num++; + } + return ret == 0 ? ESP_CAPTURE_ERR_OK : ESP_CAPTURE_ERR_INTERNAL; +} + +static int codec_dev_src_stop(esp_capture_audio_src_if_t *h) +{ + codec_dev_src_t *src = (codec_dev_src_t *)h; + if (src->handle) { + esp_codec_dev_close(src->handle); + } + src->start = false; + return ESP_CAPTURE_ERR_OK; +} + +static int codec_dev_src_close(esp_capture_audio_src_if_t *h) +{ + codec_dev_src_t *src = (codec_dev_src_t *)h; + src->handle = NULL; + return ESP_CAPTURE_ERR_OK; +} + +esp_capture_audio_src_if_t *esp_capture_new_audio_codec_src(esp_capture_audio_codec_src_cfg_t *cfg) +{ + if (cfg == NULL || cfg->record_handle == NULL) { + return NULL; + } + codec_dev_src_t *src = calloc(1, sizeof(codec_dev_src_t)); + src->base.open = codec_dev_src_open; + src->base.get_support_codecs = codec_dev_src_get_support_codecs; + src->base.negotiate_caps = codec_dev_src_negotiate_caps; + src->base.start = codec_dev_src_start; + src->base.read_frame = codec_dev_src_read_frame; + src->base.stop = codec_dev_src_stop; + src->base.close = codec_dev_src_close; + src->handle = cfg->record_handle; + return &src->base; +} \ No newline at end of file diff --git a/components/third_party/esp_capture/src/impl/capture_audio_src/idf_component.yml b/components/third_party/esp_capture/src/impl/capture_audio_src/idf_component.yml new file mode 100644 index 0000000..9a08913 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_audio_src/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + espressif/esp_codec_dev: "~1.3.4" + espressif/esp-sr: "1.9.5" + esp_capture: + path: ../../../../esp_capture diff --git a/components/third_party/esp_capture/src/impl/capture_file_src/capture_audio_file_src.c b/components/third_party/esp_capture/src/impl/capture_file_src/capture_audio_file_src.c new file mode 100644 index 0000000..862235c --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_file_src/capture_audio_file_src.c @@ -0,0 +1,179 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "esp_capture_types.h" +#include "esp_capture_audio_src_if.h" +#include +#include +#include "esp_log.h" + +#define TAG "AUD_FILE_SRC" + +typedef struct { + esp_capture_audio_src_if_t base; + esp_capture_audio_info_t aud_info; + char file_path[128]; + FILE *fp; + bool is_open; + bool is_start; + bool nego_ok; +} aud_file_src_t; + +static int get_aud_info_by_name(aud_file_src_t *src) +{ + char *ext = strrchr(src->file_path, '.'); + if (ext == NULL) { + return -1; + } + if (strcmp(ext, ".pcm") == 0) { + src->aud_info.codec = ESP_CAPTURE_CODEC_TYPE_PCM; + src->aud_info.sample_rate = 44100; + src->aud_info.bits_per_sample = 16; + src->aud_info.channel = 2; + } else if (strcmp(ext, ".opus") == 0) { + src->aud_info.codec = ESP_CAPTURE_CODEC_TYPE_OPUS; + src->aud_info.sample_rate = 44100; + src->aud_info.bits_per_sample = 16; + src->aud_info.channel = 2; + } else { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + return ESP_CAPTURE_ERR_OK; +} + +static int aud_file_src_close(esp_capture_audio_src_if_t *h) +{ + aud_file_src_t *src = (aud_file_src_t *)h; + if (src->fp) { + fclose(src->fp); + src->fp = NULL; + } + return 0; +} + +static int aud_file_src_open(esp_capture_audio_src_if_t *h) +{ + aud_file_src_t *src = (aud_file_src_t *)h; + src->fp = fopen(src->file_path, "rb"); + if (src->fp == NULL) { + ESP_LOGE(TAG, "open file failed"); + return ESP_CAPTURE_ERR_NOT_FOUND; + } + int ret = get_aud_info_by_name(src); + if (ret != ESP_CAPTURE_ERR_OK) { + aud_file_src_close(h); + return ret; + } + src->is_open = true; + return 0; +} + +static int aud_file_src_get_codec(esp_capture_audio_src_if_t *h, const esp_capture_codec_type_t **codecs, uint8_t *num) +{ + aud_file_src_t *src = (aud_file_src_t *)h; + if (src->is_open == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + *num = 1; + *codecs = &src->aud_info.codec; + return ESP_CAPTURE_ERR_OK; +} + +static int aud_file_src_nego(esp_capture_audio_src_if_t *h, esp_capture_audio_info_t *in_cap, esp_capture_audio_info_t *out_caps) +{ + aud_file_src_t *src = (aud_file_src_t *)h; + if (src->is_open == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + if (in_cap->codec == src->aud_info.codec) { + src->nego_ok = true; + *out_caps = src->aud_info; + return ESP_CAPTURE_ERR_OK; + } + return ESP_CAPTURE_ERR_NOT_SUPPORTED; +} + +static int aud_file_src_start(esp_capture_audio_src_if_t *h) +{ + aud_file_src_t *src = (aud_file_src_t *)h; + if (src->nego_ok == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + src->is_start = true; + return ESP_CAPTURE_ERR_OK; +} + +static int aud_file_src_read_frame(esp_capture_audio_src_if_t *h, esp_capture_stream_frame_t *frame) +{ + aud_file_src_t *src = (aud_file_src_t *)h; + if (src->is_start == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + if (src->aud_info.codec == ESP_CAPTURE_CODEC_TYPE_PCM) { + int ret = fread(frame->data, 1, frame->size, src->fp); + if (ret >= 0) { + frame->size = ret; + return ESP_CAPTURE_ERR_OK; + } + } else if (src->aud_info.codec == ESP_CAPTURE_CODEC_TYPE_OPUS) { + uint32_t payload_size = 0; + int ret = fread(&payload_size, 1, 4, src->fp); + if (payload_size && frame->size >= payload_size) { + ret = fread(frame->data, 1, payload_size, src->fp); + if (ret >= 0) { + frame->size = ret; + return ESP_CAPTURE_ERR_OK; + } + } + } + return ESP_CAPTURE_ERR_NOT_SUPPORTED; +} + +static int aud_file_src_stop(esp_capture_audio_src_if_t *h) +{ + aud_file_src_t *src = (aud_file_src_t *)h; + src->nego_ok = false; + src->is_start = false; + if (src->fp) { + fseek(src->fp, 0, SEEK_SET); + } + return ESP_CAPTURE_ERR_OK; +} + +esp_capture_audio_src_if_t *esp_capture_new_audio_file_src(const char *file_name) +{ + aud_file_src_t *src = (aud_file_src_t *)calloc(1, sizeof(aud_file_src_t)); + if (src == NULL) { + return NULL; + } + strncpy(src->file_path, file_name, sizeof(src->file_path) - 1); + src->base.open = aud_file_src_open; + src->base.get_support_codecs = aud_file_src_get_codec; + src->base.negotiate_caps = aud_file_src_nego; + src->base.start = aud_file_src_start; + src->base.read_frame = aud_file_src_read_frame; + src->base.stop = aud_file_src_stop; + src->base.close = aud_file_src_close; + return &src->base; +} diff --git a/components/third_party/esp_capture/src/impl/capture_file_src/capture_video_file_src.c b/components/third_party/esp_capture/src/impl/capture_file_src/capture_video_file_src.c new file mode 100644 index 0000000..20aeb98 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_file_src/capture_video_file_src.c @@ -0,0 +1,420 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "esp_capture_types.h" +#include "esp_capture_video_src_if.h" +#include +#include +#include "esp_log.h" +#include "data_queue.h" + +#define TAG "VID_FILE_SRC" + +#define NAL_UNIT_TYPE_SPS 7 +#define NAL_UNIT_TYPE_PPS 8 + +#define MAX_FRAME_SIZE 40 * 1024 +#define MAX_FRAME_NUM 2 +#define READ_SIZE 1024 +#define MAX_FILE_PATH_LEN 128 +typedef struct { + esp_capture_video_src_if_t base; + esp_capture_video_info_t vid_info; + char file_path[MAX_FILE_PATH_LEN]; + uint8_t frame_cache[READ_SIZE]; + int cached_size; + FILE *fp; + bool is_open; + bool is_start; + bool nego_ok; + data_queue_t *frame_q; +} vid_file_src_t; + +static int sps_bit_pos = 0; + +static inline uint32_t read_bits(uint8_t *buffer, uint32_t count) +{ + uint32_t res = 0; + uint8_t index = (sps_bit_pos >> 3); + uint8_t bit_num = sps_bit_pos & 0x7; + uint8_t out_bit_num = count - 1; + for (uint8_t c = 0; c < count; c++) { + if (buffer[index] << bit_num & 0x80) { + res |= (1 << out_bit_num); + } + if (++bit_num > 7) { + bit_num = 0; + index++; + } + out_bit_num--; + } + sps_bit_pos += count; + return res; +} + +static inline uint32_t read_ueg(uint8_t *buffer) +{ + uint32_t bits = 0; + while (read_bits(buffer, 1) == 0) { + bits++; + } + uint32_t res = 0; + if (bits) { + uint32_t val = read_bits(buffer, bits); + res = (uint32_t)((1 << bits) - 1 + val); + } + return res; +} + +static int32_t read_eg(uint8_t *buffer) +{ + uint32_t val = read_ueg(buffer); + if (val & 0x01) { + return (val + 1) / 2; + } else { + return -(val / 2); + } +} + +static inline void skip_scaling_list(uint8_t *buffer, uint8_t count) +{ + uint32_t delta_scale, last_scale = 8, next_scale = 8; + for (uint8_t j = 0; j < count; j++) { + if (next_scale != 0) { + delta_scale = read_eg(buffer); + next_scale = (last_scale + delta_scale + 256) & 0xFF; + } + last_scale = (next_scale == 0 ? last_scale : next_scale); + } +} + +static inline void parse_sps(uint8_t *buffer, size_t sps_size, esp_capture_video_info_t *info) +{ + sps_bit_pos = 8; + uint8_t profile = read_bits(buffer, 8); + sps_bit_pos += 16; + read_ueg(buffer); + if (profile == 100 || profile == 110 || profile == 122 || profile == 244 || profile == 44 || profile == 83 || profile == 86 || profile == 118 || profile == 128) { + uint32_t chroma_format = read_ueg(buffer); + if (chroma_format == 3) { + sps_bit_pos++; + } + read_ueg(buffer); + read_ueg(buffer); + sps_bit_pos++; + if (read_bits(buffer, 1)) { + for (int i = 0; i < (chroma_format != 3 ? 8 : 12); i++) { + if (read_bits(buffer, 1)) { + if (i < 6) { + skip_scaling_list(buffer, 16); + } else { + skip_scaling_list(buffer, 64); + } + } + } + } + } + read_ueg(buffer); + uint32_t pic_order_type = read_ueg(buffer); + if (pic_order_type == 0) { + read_ueg(buffer); + } else if (pic_order_type == 1) { + sps_bit_pos++; + read_eg(buffer); + read_eg(buffer); + for (int i = 0; i < read_ueg(buffer); i++) { + read_eg(buffer); + } + } + read_ueg(buffer); + sps_bit_pos++; + + uint32_t width_minus1 = read_ueg(buffer); + uint32_t height_minus1 = read_ueg(buffer); + uint8_t frame_mbs_only = read_bits(buffer, 1); + if (!frame_mbs_only) { + sps_bit_pos++; + } + sps_bit_pos++; + + uint32_t crop_left = 0; + uint32_t crop_right = 0; + uint32_t crop_top = 0; + uint32_t crop_bottom = 0; + if (read_bits(buffer, 1)) { + crop_left = read_ueg(buffer); + crop_right = read_ueg(buffer); + crop_top = read_ueg(buffer); + crop_bottom = read_ueg(buffer); + } + info->width = (((width_minus1 + 1) << 4) - crop_left * 2 - crop_right * 2); + info->height = ((2 - frame_mbs_only) * (height_minus1 + 1) << 4); + info->height -= ((frame_mbs_only ? 2 : 4) * (crop_top + crop_bottom)); + ESP_LOGI(TAG, "Parse sps ok %dx%d", (int)info->width, (int)info->height); +} + +static inline int is_start_code(vid_file_src_t *src, uint8_t *data) +{ + if (data[0] || data[1]) { + return 0; + ; + } + if (data[2] == 1) { + return 3; + } + if (data[2] == 0 && data[3] == 1) { + return 4; + } + return 0; +} + +static int try_parse_sps(vid_file_src_t *src, uint8_t *data, int size) +{ + for (int i = 0; i < size - 5; i++) { + int start_code_len = is_start_code(src, data + i); + if (start_code_len == 0) { + continue; + } + uint8_t nal_type = data[i + start_code_len] & 0x1F; + if (nal_type == NAL_UNIT_TYPE_SPS) { + parse_sps(data + i + start_code_len, size - (i + start_code_len + 1), &src->vid_info); + return 0; + } + i += start_code_len; + } + return -1; +} + +static int get_vid_info_by_name(vid_file_src_t *src) +{ + char *ext = strrchr(src->file_path, '.'); + if (ext == NULL) { + return -1; + } + if (strcmp(ext, ".h264") == 0) { + int ret = fread(src->frame_cache, 1, READ_SIZE, src->fp); + if (ret < 0) { + return ESP_CAPTURE_ERR_NOT_FOUND; + } + src->cached_size = ret; + ret = try_parse_sps(src, src->frame_cache, ret); + if (ret != 0) { + return ESP_CAPTURE_ERR_NOT_FOUND; + } + src->vid_info.codec = ESP_CAPTURE_CODEC_TYPE_H264; + return ESP_CAPTURE_ERR_OK; + } + return ESP_CAPTURE_ERR_NOT_SUPPORTED; +} + +static int vid_file_src_close(esp_capture_video_src_if_t *h) +{ + vid_file_src_t *src = (vid_file_src_t *)h; + if (src->fp != NULL) { + fclose(src->fp); + src->fp = NULL; + } + if (src->frame_q) { + data_queue_deinit(src->frame_q); + src->frame_q = NULL; + } + return ESP_CAPTURE_ERR_OK; +} + +static int vid_file_src_open(esp_capture_video_src_if_t *h) +{ + vid_file_src_t *src = (vid_file_src_t *)h; + src->fp = fopen(src->file_path, "rb"); + if (src->fp == NULL) { + ESP_LOGE(TAG, "open file failed"); + return ESP_CAPTURE_ERR_NOT_FOUND; + } + src->frame_q = data_queue_init(MAX_FRAME_NUM * MAX_FRAME_SIZE); + if (src->frame_q == NULL) { + vid_file_src_close(h); + return ESP_CAPTURE_ERR_NO_MEM; + } + int ret = get_vid_info_by_name(src); + if (ret != ESP_CAPTURE_ERR_OK) { + vid_file_src_close(h); + return ret; + } + src->is_open = true; + return 0; +} + +static int vid_file_src_get_support_codecs(esp_capture_video_src_if_t *h, const esp_capture_codec_type_t **codecs, + uint8_t *num) +{ + vid_file_src_t *src = (vid_file_src_t *)h; + *num = 1; + *codecs = &src->vid_info.codec; + return ESP_CAPTURE_ERR_OK; +} + +static int vid_file_src_negotiate_caps(esp_capture_video_src_if_t *h, esp_capture_video_info_t *in_cap, + esp_capture_video_info_t *out_caps) +{ + vid_file_src_t *src = (vid_file_src_t *)h; + if (src->is_open == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + if (in_cap->codec != src->vid_info.codec) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + *out_caps = src->vid_info; + out_caps->fps = in_cap->fps; + src->nego_ok = true; + return ESP_CAPTURE_ERR_OK; +} + +static int vid_file_src_start(esp_capture_video_src_if_t *h) +{ + vid_file_src_t *src = (vid_file_src_t *)h; + if (src->nego_ok == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + src->is_start = true; + return ESP_CAPTURE_ERR_OK; +} + +static int read_h264_frame(vid_file_src_t *src, uint8_t *data, int size) +{ + // Find frame in cache + int fill = 0; + bool find_frame = false; + bool use_cache = false; + if (src->cached_size) { + memcpy(data, src->frame_cache, src->cached_size); + fill = src->cached_size; + src->cached_size = 0; + use_cache = true; + } + while (fill + READ_SIZE < size) { + int ret = 0; + int last; + if (use_cache == false) { + ret = fread(data + fill, 1, READ_SIZE, src->fp); + if (ret < 0) { + return -1; + } + last = fill > 5 ? fill - 5 : 0; + } else { + last = 0; + } + fill += ret; + for (int i = last; i < fill - 5; i++) { + int start_code_len = is_start_code(src, data + i); + if (start_code_len == 0) { + continue; + } + // Already find a frame + if (find_frame) { + // Reach file end + if (use_cache == false && ret < READ_SIZE) { + return fill; + } + // Copy left buffer into cache + memcpy(src->frame_cache, data + i, fill - i); + src->cached_size = fill - i; + return i; + } + uint8_t nal_type = data[i + start_code_len] & 0x1F; + // Find IDR or non-IDR frame + if (nal_type == 5 || nal_type == 1) { + find_frame = true; + } + i += start_code_len; + } + if (use_cache) { + use_cache = false; + continue; + } + if (ret < READ_SIZE) { + break; + } + } + return -1; +} + +static int vid_file_src_acquire_frame(esp_capture_video_src_if_t *h, esp_capture_stream_frame_t *frame) +{ + vid_file_src_t *src = (vid_file_src_t *)h; + if (src->is_start == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + uint8_t *data = data_queue_get_buffer(src->frame_q, MAX_FRAME_SIZE); + if (data) { + int ret = read_h264_frame(src, data, MAX_FRAME_SIZE); + if (ret > 0) { + frame->data = data; + frame->size = ret; + data_queue_send_buffer(src->frame_q, ret); + return ESP_CAPTURE_ERR_OK; + } + data_queue_send_buffer(src->frame_q, 0); + } + return ESP_CAPTURE_ERR_NOT_FOUND; +} + +static int vid_file_src_release_frame(esp_capture_video_src_if_t *h, esp_capture_stream_frame_t *frame) +{ + vid_file_src_t *src = (vid_file_src_t *)h; + if (src->is_start == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + void *data = NULL; + int size = 0; + data_queue_read_lock(src->frame_q, &data, &size); + data_queue_read_unlock(src->frame_q); + return ESP_CAPTURE_ERR_OK; +} + +static int vid_file_src_stop(esp_capture_video_src_if_t *h) +{ + vid_file_src_t *src = (vid_file_src_t *)h; + if (src->fp) { + fseek(src->fp, 0, SEEK_SET); + } + src->is_start = false; + return ESP_CAPTURE_ERR_OK; +} + +esp_capture_video_src_if_t *esp_capture_new_video_file_src(const char *file_name) +{ + vid_file_src_t *src = (vid_file_src_t *)calloc(1, sizeof(vid_file_src_t)); + if (src == NULL) { + return NULL; + } + strncpy(src->file_path, file_name, sizeof(src->file_path) - 1); + src->base.open = vid_file_src_open; + src->base.get_support_codecs = vid_file_src_get_support_codecs; + src->base.negotiate_caps = vid_file_src_negotiate_caps; + src->base.start = vid_file_src_start; + src->base.acquire_frame = vid_file_src_acquire_frame; + src->base.release_frame = vid_file_src_release_frame; + src->base.stop = vid_file_src_stop; + src->base.close = vid_file_src_close; + return &src->base; +} diff --git a/components/third_party/esp_capture/src/impl/capture_simple_path/esp_capture_path_simple.c b/components/third_party/esp_capture/src/impl/capture_simple_path/esp_capture_path_simple.c new file mode 100644 index 0000000..6efea12 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_simple_path/esp_capture_path_simple.c @@ -0,0 +1,627 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include +#include "esp_capture_path_simple.h" +#include "esp_log.h" +#include "media_lib_os.h" +#include "data_queue.h" + +#define TAG "CAPTURE_SIMP" + +#define CAPTURE_AENC_EXITED (1) +#define CAPTURE_VENC_EXITED (2) + +#define VIDEO_ENC_OUT_ALIGNMENT (128) +#define ALIGN_UP(size, align) (((size) + (align)-1) & ~((align)-1)) + +typedef struct { + bool added; + bool enable; + bool started; + bool venc_bypass; + bool aenc_bypass; + bool video_enabled; + bool audio_enabled; + esp_capture_codec_type_t video_src_codec; + esp_capture_sink_cfg_t sink; + data_queue_t *audio_q; + data_queue_t *video_q; + int audio_frame_size; + int video_frame_size; + media_lib_event_grp_handle_t event_group; +} simple_capture_res_t; + +typedef struct { + esp_capture_path_if_t base; + esp_capture_simple_path_cfg_t enc_cfg; + esp_capture_path_cfg_t src_cfg; + esp_capture_audio_info_t aud_info; + esp_capture_audio_info_t vid_info; + simple_capture_res_t primary; +} simple_capture_t; + +int simple_capture_open(esp_capture_path_if_t *h, esp_capture_path_cfg_t *cfg) +{ + simple_capture_t *capture = (simple_capture_t *)h; + if (cfg->acquire_src_frame == NULL || cfg->release_src_frame == NULL || (cfg->nego_audio == NULL && cfg->nego_video == NULL) || cfg->frame_processed == NULL) { + return ESP_CAPTURE_ERR_INVALID_ARG; + } + capture->src_cfg = *cfg; + return ESP_CAPTURE_ERR_OK; +} + +static bool need_bypass_aenc(simple_capture_t *capture, esp_capture_audio_info_t *audio_info) +{ + esp_capture_audio_info_t in_info = {}; + int ret = capture->src_cfg.nego_audio(capture->src_cfg.src_ctx, audio_info, &in_info); + if (ret == ESP_CAPTURE_ERR_OK && in_info.codec == audio_info->codec) { + ESP_LOGI(TAG, "Bypass audio encoder for codec %d", audio_info->codec); + return true; + } + return false; +} + +static bool need_bypass_venc(simple_capture_t *capture, esp_capture_video_info_t *video_info) +{ + esp_capture_video_info_t in_info = {}; + int ret = capture->src_cfg.nego_video(capture->src_cfg.src_ctx, video_info, &in_info); + if (ret == ESP_CAPTURE_ERR_OK && in_info.codec == video_info->codec) { + ESP_LOGI(TAG, "Bypass video encoder for codec %d", video_info->codec); + return true; + } + return false; +} + +static bool check_audio_codec_support(simple_capture_t *capture, esp_capture_aenc_if_t *aenc, esp_capture_audio_info_t *audio_info) +{ + if (aenc == NULL) { + ESP_LOGE(TAG, "Not support audio encoder"); + return false; + } + if (need_bypass_aenc(capture, audio_info)) { + capture->primary.aenc_bypass = true; + return true; + } + const esp_capture_codec_type_t *acodecs = NULL; + uint8_t num = 0; + aenc->get_support_codecs(aenc, &acodecs, &num); + for (int i = 0; i < num; i++) { + if (acodecs[i] == audio_info->codec) { + esp_capture_audio_info_t pcm_info; + esp_capture_audio_info_t in_info; + pcm_info = *audio_info; + pcm_info.codec = ESP_CAPTURE_CODEC_TYPE_PCM; + capture->src_cfg.nego_audio(capture->src_cfg.src_ctx, &pcm_info, &in_info); + if (in_info.sample_rate != audio_info->sample_rate || in_info.channel != audio_info->channel || in_info.bits_per_sample != audio_info->bits_per_sample) { + ESP_LOGE(TAG, "Sample rate or channel not supported by source"); + break; + } + return true; + } + } + ESP_LOGE(TAG, "Audio encoder not support codec %d", audio_info->codec); + return false; +} + +static bool check_video_codec_support(simple_capture_t *capture, esp_capture_venc_if_t *venc, esp_capture_video_info_t *video_info) +{ + if (venc == NULL) { + ESP_LOGE(TAG, "Not support video encoder"); + return false; + } + if (need_bypass_venc(capture, video_info)) { + capture->primary.venc_bypass = true; + return true; + } + const esp_capture_codec_type_t *vcodecs = NULL; + uint8_t num = 0; + venc->get_support_codecs(venc, &vcodecs, &num); + for (int i = 0; i < num; i++) { + printf("cmp %d %d\n", vcodecs[i], video_info->codec); + if (vcodecs[i] == video_info->codec) { + esp_capture_video_info_t in_info = *video_info; + esp_capture_video_info_t out_info; + const esp_capture_codec_type_t *in_codecs = NULL; + num = 0; + venc->get_input_codecs(venc, video_info->codec, &in_codecs, &num); + for (i = 0; i < num; i++) { + in_info.codec = in_codecs[i]; + printf("Source to nego %d\n", in_codecs[i]); + int ret = capture->src_cfg.nego_video(capture->src_cfg.src_ctx, &in_info, &out_info); + if (ret == ESP_CAPTURE_ERR_OK) { + if (in_info.width != out_info.width || in_info.height != out_info.height) { + ESP_LOGE(TAG, "Resolution not supported by source"); + break; + } + capture->primary.video_src_codec = in_info.codec; + return true; + } + } + break; + } + } + ESP_LOGE(TAG, "video encoder not support codec %d", video_info->codec); + return false; +} + +int simple_capture_add_path(esp_capture_path_if_t *p, esp_capture_path_type_t path, esp_capture_sink_cfg_t *sink) +{ + simple_capture_t *capture = (simple_capture_t *)p; + if (path != ESP_CAPTURE_PATH_PRIMARY) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + simple_capture_res_t *res = &capture->primary; + if (res->added) { + return ESP_CAPTURE_ERR_INVALID_STATE; + } + media_lib_event_group_create(&res->event_group); + if (res->event_group == NULL) { + return ESP_CAPTURE_ERR_NO_MEM; + } + res->sink = *sink; + if (sink->audio_info.codec && check_audio_codec_support(capture, capture->enc_cfg.aenc, &sink->audio_info) == false) { + res->sink.audio_info.codec = ESP_CAPTURE_CODEC_TYPE_NONE; + } + if (sink->video_info.codec && check_video_codec_support(capture, capture->enc_cfg.venc, &sink->video_info) == false) { + res->sink.video_info.codec = ESP_CAPTURE_CODEC_TYPE_NONE; + } + res->added = true; + return ESP_CAPTURE_ERR_OK; +} + +static int get_frame_samples(esp_capture_audio_info_t *audio_info, int dur) +{ + return dur * audio_info->sample_rate / 1000; +} + +int simple_capture_get_frame_samples(esp_capture_path_if_t *p, esp_capture_path_type_t path) +{ + simple_capture_t *capture = (simple_capture_t *)p; + if (path != ESP_CAPTURE_PATH_PRIMARY) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + simple_capture_res_t *res = &capture->primary; + if (res->sink.audio_info.codec == ESP_CAPTURE_CODEC_TYPE_NONE) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + esp_capture_audio_info_t *aud_info = &res->sink.audio_info; + int frame_samples = get_frame_samples(aud_info, 20); + if (res->aenc_bypass == false && res->audio_enabled) { + int in_frame_size = 0, out_frame_size = 0; + capture->enc_cfg.aenc->get_frame_size(capture->enc_cfg.aenc, &in_frame_size, &out_frame_size); + if (in_frame_size) { + int frame_size = aud_info->channel * aud_info->bits_per_sample >> 3; + frame_samples = in_frame_size / frame_size; + } + } + ESP_LOGI(TAG, "Set audio frame size %d", frame_samples); + return frame_samples; +} + +int simple_capture_add_overlay(esp_capture_path_if_t *h, esp_capture_path_type_t path, esp_capture_overlay_if_t *overlay) +{ + return ESP_CAPTURE_ERR_NOT_SUPPORTED; +} + +int simple_capture_enable_overlay(esp_capture_path_if_t *p, esp_capture_path_type_t path, bool enable) +{ + return ESP_CAPTURE_ERR_NOT_SUPPORTED; +} + +static void simple_capture_aenc_thread(void *arg) +{ + simple_capture_t *capture = (simple_capture_t *)arg; + simple_capture_res_t *res = &capture->primary; + esp_capture_stream_frame_t out_frame; + out_frame.stream_type = ESP_CAPTURE_STREAM_TYPE_AUDIO; + ESP_LOGI(TAG, "Enter audio encoder thread"); + while (res->audio_enabled) { + // grab data from src + esp_capture_stream_frame_t frame; + frame.stream_type = ESP_CAPTURE_STREAM_TYPE_AUDIO; + int ret = capture->src_cfg.acquire_src_frame(capture->src_cfg.src_ctx, &frame, false); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Fail to acquire audio frame ret %d", ret); + break; + } + // TODO use original frame is OK? + if (res->aenc_bypass) { + capture->src_cfg.frame_processed(capture->src_cfg.src_ctx, ESP_CAPTURE_PATH_PRIMARY, &frame); + if (frame.data == NULL && frame.size == 0) { + ESP_LOGI(TAG, "Stop frame is received"); + break; + } + continue; + } + int frame_size = res->audio_frame_size + sizeof(esp_capture_stream_frame_t); + uint8_t *data = data_queue_get_buffer(res->audio_q, frame_size); + if (data == NULL) { + ESP_LOGE(TAG, "Fail to get audio fifo buffer"); + break; + } + out_frame.pts = frame.pts; + out_frame.data = data + sizeof(esp_capture_stream_frame_t); + out_frame.size = res->audio_frame_size; + memcpy(data, &out_frame, sizeof(esp_capture_stream_frame_t)); + if (frame.size > 0) { + ret = capture->enc_cfg.aenc->encode_frame(capture->enc_cfg.aenc, &frame, &out_frame); + } else { + out_frame.size = 0; + } + capture->src_cfg.release_src_frame(capture->src_cfg.src_ctx, &frame); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Fail to encode audio frame"); + data_queue_send_buffer(res->audio_q, 0); + continue; + } + data_queue_send_buffer(res->audio_q, out_frame.size + sizeof(esp_capture_stream_frame_t)); + capture->src_cfg.frame_processed(capture->src_cfg.src_ctx, ESP_CAPTURE_PATH_PRIMARY, &out_frame); + if (frame.data == NULL && frame.size == 0) { + ESP_LOGI(TAG, "Stop frame is received"); + break; + } + } + ESP_LOGI(TAG, "Audio encoder thread exit"); + media_lib_event_group_set_bits(res->event_group, CAPTURE_AENC_EXITED); + media_lib_thread_destroy(NULL); +} + +static void simple_capture_venc_thread(void *arg) +{ + simple_capture_t *capture = (simple_capture_t *)arg; + simple_capture_res_t *res = &capture->primary; + esp_capture_stream_frame_t out_frame = {}; + out_frame.stream_type = ESP_CAPTURE_STREAM_TYPE_VIDEO; + ESP_LOGI(TAG, "Enter video encoder thread"); + while (res->video_enabled) { + // grab data from src + esp_capture_stream_frame_t frame; + frame.stream_type = ESP_CAPTURE_STREAM_TYPE_VIDEO; + int ret = capture->src_cfg.acquire_src_frame(capture->src_cfg.src_ctx, &frame, false); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Fail to acquire video frame ret %d", ret); + break; + } + // TODO use original frame is OK? + if (res->venc_bypass) { + capture->src_cfg.frame_processed(capture->src_cfg.src_ctx, ESP_CAPTURE_PATH_PRIMARY, &frame); + if (frame.data == NULL && frame.size == 0) { + ESP_LOGI(TAG, "Stop frame is received"); + break; + } + continue; + } + // Get data for output + int size = sizeof(esp_capture_stream_frame_t) + res->video_frame_size + VIDEO_ENC_OUT_ALIGNMENT; + uint8_t *data = data_queue_get_buffer(res->video_q, size); + if (data == NULL) { + ESP_LOGE(TAG, "Fail to get video fifo buffer"); + capture->src_cfg.release_src_frame(capture->src_cfg.src_ctx, &frame); + break; + } + out_frame.pts = frame.pts; + out_frame.data = data + sizeof(esp_capture_stream_frame_t); + // Align frame + out_frame.data = (uint8_t *)ALIGN_UP((uintptr_t)out_frame.data, VIDEO_ENC_OUT_ALIGNMENT); + out_frame.size = res->video_frame_size; + memcpy(data, &out_frame, sizeof(esp_capture_stream_frame_t)); + if (frame.size) { + ret = capture->enc_cfg.venc->encode_frame(capture->enc_cfg.venc, &frame, &out_frame); + } else { + out_frame.size = 0; + } + capture->src_cfg.release_src_frame(capture->src_cfg.src_ctx, &frame); + if (ret != ESP_CAPTURE_ERR_OK) { + if (ret == ESP_CAPTURE_ERR_NOT_ENOUGH) { + ESP_LOGW(TAG, "Bad input maybe skipped size %d", (int)res->video_frame_size); + continue; + } + ESP_LOGE(TAG, "Fail to encode video frame"); + data_queue_send_buffer(res->video_q, 0); + capture->src_cfg.event_cb(capture->src_cfg.src_ctx, ESP_CAPTURE_PATH_PRIMARY, ESP_CAPTURE_PATH_EVENT_VIDEO_ERROR); + break; + } + size = (int)(intptr_t)(out_frame.data - (uint8_t *)data) + out_frame.size; + data_queue_send_buffer(res->video_q, size); + // Notify to use audio encoded frame + capture->src_cfg.frame_processed(capture->src_cfg.src_ctx, ESP_CAPTURE_PATH_PRIMARY, &out_frame); + if (frame.data == NULL && frame.size == 0) { + ESP_LOGI(TAG, "Stop frame is received"); + break; + } + } + ESP_LOGI(TAG, "Video encoder thread exit"); + media_lib_event_group_set_bits(res->event_group, CAPTURE_VENC_EXITED); + media_lib_thread_destroy(NULL); +} + +static int simple_capture_enable_audio(simple_capture_t *capture, bool enable) +{ + simple_capture_res_t *res = &capture->primary; + if (res->sink.audio_info.codec == ESP_CAPTURE_CODEC_TYPE_NONE) { + return ESP_CAPTURE_ERR_OK; + } + esp_capture_aenc_if_t *aenc = capture->enc_cfg.aenc; + if (enable == false) { + if (res->audio_enabled) { + ESP_LOGI(TAG, "Start disable audio"); + res->audio_enabled = false; + data_queue_consume_all(res->audio_q); + media_lib_event_group_wait_bits(res->event_group, CAPTURE_AENC_EXITED, 100000); + media_lib_event_group_clr_bits(res->event_group, CAPTURE_AENC_EXITED); + aenc->stop(aenc); + ESP_LOGI(TAG, "End to disable audio"); + } + return ESP_CAPTURE_ERR_OK; + } + if (res->aenc_bypass == false) { + int ret = aenc->start(aenc, &res->sink.audio_info); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Fail to start audio encoder"); + return ret; + } + int in_frame_size = 0, out_frame_size = 0; + aenc->get_frame_size(aenc, &in_frame_size, &out_frame_size); + res->audio_frame_size = out_frame_size; + int frame_count = capture->enc_cfg.aenc_frame_count ? capture->enc_cfg.aenc_frame_count : 5; + int fifo_size = frame_count * (out_frame_size + 64); + // Reuse queue if disable and enable again + if (res->audio_q == NULL) { + res->audio_q = data_queue_init(fifo_size); + } + if (res->audio_q == NULL) { + ESP_LOGE(TAG, "Fail to init audio encoder fifo"); + return ESP_CAPTURE_ERR_NO_MEM; + } + } + res->audio_enabled = true; + media_lib_thread_handle_t thread = NULL; + media_lib_thread_create_from_scheduler(&thread, "aenc", simple_capture_aenc_thread, capture); + if (thread == NULL) { + res->audio_enabled = false; + return ESP_CAPTURE_ERR_NO_RESOURCES; + } + return ESP_CAPTURE_ERR_OK; +} + +static int simple_capture_enable_video(simple_capture_t *capture, bool enable) +{ + simple_capture_res_t *res = &capture->primary; + if (res->sink.video_info.codec == ESP_CAPTURE_CODEC_TYPE_NONE) { + return ESP_CAPTURE_ERR_OK; + } + esp_capture_venc_if_t *venc = capture->enc_cfg.venc; + if (enable == false) { + if (res->video_enabled) { + ESP_LOGI(TAG, "Start to disable video"); + res->video_enabled = false; + data_queue_consume_all(res->video_q); + media_lib_event_group_wait_bits(res->event_group, CAPTURE_VENC_EXITED, 10000); + media_lib_event_group_clr_bits(res->event_group, CAPTURE_VENC_EXITED); + venc->stop(venc); + ESP_LOGI(TAG, "End to disable video"); + } + return ESP_CAPTURE_ERR_OK; + } + int ret = ESP_CAPTURE_ERR_OK; + if (res->venc_bypass == false) { + ret = venc->start(venc, res->video_src_codec, &res->sink.video_info); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGE(TAG, "Fail to start audio encoder"); + return ret; + } + int in_frame_size = 0, out_frame_size = 0; + venc->get_frame_size(venc, &in_frame_size, &out_frame_size); + res->video_frame_size = out_frame_size; + int frame_count = capture->enc_cfg.venc_frame_count ? capture->enc_cfg.venc_frame_count : 2; + int fifo_size = frame_count * (out_frame_size + 256); + if (res->video_q == NULL) { + res->video_q = data_queue_init(fifo_size); + } + if (res->video_q == NULL) { + ESP_LOGE(TAG, "Fail to init video encoder fifo"); + return ESP_CAPTURE_ERR_NO_MEM; + } + } + res->video_enabled = true; + media_lib_thread_handle_t thread = NULL; + media_lib_thread_create_from_scheduler(&thread, "venc", simple_capture_venc_thread, capture); + if (thread == NULL) { + res->video_enabled = false; + ret = ESP_CAPTURE_ERR_NO_RESOURCES; + } + return ret; +} + +int simple_capture_enable_path(esp_capture_path_if_t *p, esp_capture_path_type_t path, bool enable) +{ + simple_capture_t *capture = (simple_capture_t *)p; + if (path != ESP_CAPTURE_PATH_PRIMARY) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + simple_capture_res_t *res = &capture->primary; + if (res->enable == enable) { + return ESP_CAPTURE_ERR_OK; + } + if (res->added == false) { + ESP_LOGE(TAG, "Path not added yet"); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + res->enable = enable; + int ret = ESP_CAPTURE_ERR_OK; + if (res->started) { + ret = simple_capture_enable_audio(capture, enable); + if (ret != ESP_CAPTURE_ERR_OK) { + capture->src_cfg.event_cb(capture->src_cfg.src_ctx, ESP_CAPTURE_PATH_PRIMARY, ESP_CAPTURE_PATH_EVENT_AUDIO_ERROR); + } + ret = simple_capture_enable_video(capture, enable); + if (ret != ESP_CAPTURE_ERR_OK) { + capture->src_cfg.event_cb(capture->src_cfg.src_ctx, ESP_CAPTURE_PATH_PRIMARY, ESP_CAPTURE_PATH_EVENT_VIDEO_ERROR); + } + } + return ret; +} + +int simple_capture_start(esp_capture_path_if_t *p) +{ + simple_capture_t *capture = (simple_capture_t *)p; + simple_capture_res_t *res = &capture->primary; + if (res->started) { + return ESP_CAPTURE_ERR_OK; + } + if (res->sink.video_info.codec && check_video_codec_support(capture, capture->enc_cfg.venc, &res->sink.video_info) == false) { + res->sink.video_info.codec = ESP_CAPTURE_CODEC_TYPE_NONE; + } + int ret = ESP_CAPTURE_ERR_OK; + res->started = true; + if (res->enable) { + ret = simple_capture_enable_audio(capture, true); + if (ret != ESP_CAPTURE_ERR_OK) { + capture->src_cfg.event_cb(capture->src_cfg.src_ctx, ESP_CAPTURE_PATH_PRIMARY, ESP_CAPTURE_PATH_EVENT_AUDIO_ERROR); + } + ret = simple_capture_enable_video(capture, true); + if (ret != ESP_CAPTURE_ERR_OK) { + capture->src_cfg.event_cb(capture->src_cfg.src_ctx, ESP_CAPTURE_PATH_PRIMARY, ESP_CAPTURE_PATH_EVENT_VIDEO_ERROR); + } + } + return ret; +} + +int simple_capture_set(esp_capture_path_if_t *p, esp_capture_path_type_t path, esp_capture_path_set_type_t type, void *cfg, int cfg_size) +{ + simple_capture_t *capture = (simple_capture_t *)p; + simple_capture_res_t *res = &capture->primary; + int ret = ESP_CAPTURE_ERR_OK; + switch (type) { + case ESP_CAPTURE_PATH_SET_TYPE_AUDIO_BITRATE: + if (res->aenc_bypass == false && capture->enc_cfg.aenc != NULL && cfg_size == sizeof(int)) { + ret = capture->enc_cfg.aenc->set_bitrate(capture->enc_cfg.aenc, *(int *)cfg); + } + break; + case ESP_CAPTURE_PATH_SET_TYPE_VIDEO_BITRATE: + if (res->venc_bypass == false && capture->enc_cfg.venc != NULL && cfg_size == sizeof(int)) { + ret = capture->enc_cfg.venc->set_bitrate(capture->enc_cfg.venc, *(int *)cfg); + } + break; + case ESP_CAPTURE_PATH_SET_TYPE_VIDEO_FPS: + break; + default: + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + return ret; +} + +int simple_capture_return_frame(esp_capture_path_if_t *p, esp_capture_path_type_t path, esp_capture_stream_frame_t *frame) +{ + simple_capture_t *capture = (simple_capture_t *)p; + simple_capture_res_t *res = &capture->primary; + int ret = ESP_CAPTURE_ERR_NOT_SUPPORTED; + if (frame->stream_type == ESP_CAPTURE_STREAM_TYPE_AUDIO) { + if (res->audio_enabled) { + if (res->aenc_bypass) { + capture->src_cfg.release_src_frame(capture->src_cfg.src_ctx, frame); + } else { + esp_capture_stream_frame_t *read_frame = NULL; + if (data_queue_have_data(res->audio_q)) { + int read_size = 0; + data_queue_read_lock(res->audio_q, (void **)&read_frame, &read_size); + ESP_LOGD(TAG, "simple return audio data:%x frame:%x\n", frame->data[0], read_frame->data[0]); + ret = data_queue_read_unlock(res->audio_q); + } + } + } + } else if (frame->stream_type == ESP_CAPTURE_STREAM_TYPE_VIDEO) { + if (res->video_enabled) { + if (res->venc_bypass) { + capture->src_cfg.release_src_frame(capture->src_cfg.src_ctx, frame); + } else { + esp_capture_stream_frame_t *read_frame = NULL; + if (data_queue_have_data(res->video_q)) { + int read_size = 0; + data_queue_read_lock(res->video_q, (void **)&read_frame, &read_size); + ESP_LOGD(TAG, "simple return video data:%x frame:%x\n", frame->data[0], read_frame->data[0]); + ret = data_queue_read_unlock(res->video_q); + } + } + } + } + return ret; +} + +int simple_capture_stop(esp_capture_path_if_t *h) +{ + simple_capture_t *capture = (simple_capture_t *)h; + int ret = ESP_CAPTURE_ERR_OK; + simple_capture_res_t *res = &capture->primary; + if (res->started == false) { + return ESP_CAPTURE_ERR_OK; + } + simple_capture_enable_audio(capture, false); + simple_capture_enable_video(capture, false); + if (res->audio_q) { + data_queue_deinit(res->audio_q); + res->audio_q = NULL; + } + if (res->video_q) { + data_queue_deinit(res->video_q); + res->video_q = NULL; + } + res->started = false; + return ret; +} + +int simple_capture_close(esp_capture_path_if_t *h) +{ + simple_capture_t *capture = (simple_capture_t *)h; + simple_capture_res_t *res = &capture->primary; + simple_capture_stop(h); + if (res->event_group) { + media_lib_event_group_destroy(res->event_group); + res->event_group = NULL; + } + return ESP_CAPTURE_ERR_OK; +} + +esp_capture_path_if_t *esp_capture_build_simple_path(esp_capture_simple_path_cfg_t *cfg) +{ + simple_capture_t *capture = calloc(1, sizeof(simple_capture_t)); + if (capture == NULL) { + return NULL; + } + capture->base.open = simple_capture_open; + capture->base.add_path = simple_capture_add_path; + capture->base.add_overlay = simple_capture_add_overlay; + capture->base.enable_overlay = simple_capture_enable_overlay; + capture->base.enable_path = simple_capture_enable_path; + capture->base.get_audio_frame_samples = simple_capture_get_frame_samples; + capture->base.start = simple_capture_start; + capture->base.set = simple_capture_set; + capture->base.return_frame = simple_capture_return_frame; + capture->base.stop = simple_capture_stop; + capture->base.close = simple_capture_close; + capture->enc_cfg = *cfg; + return &capture->base; +} diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/CMakeLists.txt b/components/third_party/esp_capture/src/impl/capture_text_overlay/CMakeLists.txt new file mode 100644 index 0000000..0b45516 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/CMakeLists.txt @@ -0,0 +1,4 @@ +set(COMPONENT_ADD_INCLUDEDIRS "../../../include" ./ "../../../interface") +set(COMPONENT_SRCDIRS ./ font) +set(COMPONENT_REQUIRES media_lib_sal) +register_component() \ No newline at end of file diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/Kconfig b/components/third_party/esp_capture/src/impl/capture_text_overlay/Kconfig new file mode 100644 index 0000000..bc004ab --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/Kconfig @@ -0,0 +1,47 @@ +menu "Capture Text Overlay" + config ESP_PAINTER_FORMAT_SIZE_MAX + int "Size of format string buffer" + default 128 + + menu "fonts" + config ESP_PAINTER_BASIC_FONT_12 + bool "Enable basic_font_12" + default y + + config ESP_PAINTER_BASIC_FONT_16 + bool "Enable basic_font_16" + default n + + config ESP_PAINTER_BASIC_FONT_20 + bool "Enable basic_font_20" + default n + + config ESP_PAINTER_BASIC_FONT_24 + bool "Enable basic_font_24" + default y + + config ESP_PAINTER_BASIC_FONT_28 + bool "Enable basic_font_28" + default n + + config ESP_PAINTER_BASIC_FONT_32 + bool "Enable basic_font_32" + default n + + config ESP_PAINTER_BASIC_FONT_36 + bool "Enable basic_font_36" + default n + + config ESP_PAINTER_BASIC_FONT_40 + bool "Enable basic_font_40" + default n + + config ESP_PAINTER_BASIC_FONT_44 + bool "Enable basic_font_44" + default n + + config ESP_PAINTER_BASIC_FONT_48 + bool "Enable basic_font_48" + default n + endmenu +endmenu diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/esp_capture_text_overlay.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/esp_capture_text_overlay.c new file mode 100644 index 0000000..37fa941 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/esp_capture_text_overlay.c @@ -0,0 +1,332 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "esp_capture_text_overlay.h" +#include "media_lib_os.h" +#include "esp_painter_font.h" +#include "esp_log.h" +#include + +#define TAG "TEXT_OVERLAY" + +#define RGN_OVERFLOW(base, rgn) ((rgn)->x + (rgn)->width > base->width || (rgn)->y + (rgn)->height > base->height) + +typedef struct { + esp_capture_overlay_if_t base; + esp_capture_codec_type_t codec; + esp_capture_rgn_t rgn; + esp_capture_stream_frame_t frame; + media_lib_mutex_handle_t mutex; + bool opened; + uint8_t alpha; +} text_overlay_t; + +static int text_overlay_close(esp_capture_overlay_if_t *h); + +const esp_painter_basic_font_t *get_font(uint16_t font_size) +{ + switch (font_size) { + default: + return NULL; +#if CONFIG_ESP_PAINTER_BASIC_FONT_12 + case 12: + return &esp_painter_basic_font_12; +#endif +#if CONFIG_ESP_PAINTER_BASIC_FONT_16 + case 16: + return &esp_painter_basic_font_16; +#endif +#if CONFIG_ESP_PAINTER_BASIC_FONT_20 + case 20: + return &esp_painter_basic_font_20; +#endif +#if CONFIG_ESP_PAINTER_BASIC_FONT_24 + case 24: + return &esp_painter_basic_font_24; +#endif +#if CONFIG_ESP_PAINTER_BASIC_FONT_28 + case 28: + return &esp_painter_basic_font_28; +#endif +#if CONFIG_ESP_PAINTER_BASIC_FONT_32 + case 32: + return &esp_painter_basic_font_32; +#endif +#if CONFIG_ESP_PAINTER_BASIC_FONT_36 + case 36: + return &esp_painter_basic_font_36; +#endif +#if CONFIG_ESP_PAINTER_BASIC_FONT_40 + case 40: + return &esp_painter_basic_font_40; +#endif +#if CONFIG_ESP_PAINTER_BASIC_FONT_44 + case 44: + return &esp_painter_basic_font_44; +#endif +#if CONFIG_ESP_PAINTER_BASIC_FONT_48 + case 48: + return &esp_painter_basic_font_48; +#endif + } +} + +static int text_overlay_open(esp_capture_overlay_if_t *h) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + do { + media_lib_mutex_create(&text_overlay->mutex); + if (text_overlay->mutex == NULL) { + break; + } + text_overlay->codec = ESP_CAPTURE_CODEC_TYPE_RGB565; + text_overlay->frame.stream_type = ESP_CAPTURE_STREAM_TYPE_VIDEO; + text_overlay->frame.size = text_overlay->rgn.width * text_overlay->rgn.height * 2; + text_overlay->frame.data = calloc(1, text_overlay->frame.size); + if (text_overlay->frame.data == NULL) { + break; + } + text_overlay->opened = true; + return ESP_CAPTURE_ERR_OK; + } while (0); + text_overlay_close(h); + return ESP_CAPTURE_ERR_NO_RESOURCES; +} + +static int text_overlay_get_region(esp_capture_overlay_if_t *h, esp_capture_codec_type_t *codec, esp_capture_rgn_t *rgn) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + if (text_overlay->opened == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + *codec = text_overlay->codec; + *rgn = text_overlay->rgn; + return ESP_CAPTURE_ERR_OK; +} + +static int text_overlay_get_frame(esp_capture_overlay_if_t *h, esp_capture_stream_frame_t *frame) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + if (text_overlay->opened == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + media_lib_mutex_lock(text_overlay->mutex, 1000); + *frame = text_overlay->frame; + return ESP_CAPTURE_ERR_OK; +} + +static int text_overlay_release_frame(esp_capture_overlay_if_t *h, esp_capture_stream_frame_t *frame) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + if (text_overlay->opened == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + media_lib_mutex_unlock(text_overlay->mutex); + return ESP_CAPTURE_ERR_OK; +} + +int esp_capture_text_overlay_draw_start(esp_capture_overlay_if_t *h) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + if (text_overlay->opened == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + media_lib_mutex_lock(text_overlay->mutex, 1000); + return ESP_CAPTURE_ERR_OK; +} + +int esp_capture_text_overlay_clear(esp_capture_overlay_if_t *h, esp_capture_rgn_t *rgn, uint16_t color) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + esp_capture_rgn_t *base = &text_overlay->rgn; + if (text_overlay->opened == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + if (RGN_OVERFLOW(base, rgn)) { + ESP_LOGE(TAG, "Region overflow"); + return ESP_CAPTURE_ERR_INVALID_ARG; + } + bool pure_color = (color >> 8) == (color & 0xFF); + if (pure_color) { + uint8_t color_l = color & 0xFF; + uint16_t *v = (uint16_t *)text_overlay->frame.data; + v += (rgn->y * text_overlay->rgn.width + rgn->x); + for (int i = 0; i < rgn->height; i++) { + memset(v, color_l, rgn->width * 2); + v += text_overlay->rgn.width; + } + } else { + uint16_t *v = (uint16_t *)text_overlay->frame.data; + v += (rgn->y * text_overlay->rgn.width + rgn->x); + for (int i = 0; i < rgn->height; i++) { + uint16_t *col = v; + for (int j = 0; j < rgn->width; j++) { + *(col++) = color; + } + v += text_overlay->rgn.width; + } + } + return ESP_CAPTURE_ERR_OK; +} +int esp_capture_text_overlay_draw_text(esp_capture_overlay_if_t *h, esp_capture_text_overlay_draw_info_t *info, char *str) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + if (text_overlay->opened == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + + const esp_painter_basic_font_t *font = get_font(info->font_size); + if (font == NULL) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + uint32_t x = info->x; + uint32_t y = info->y; + uint16_t font_w = font->width; + uint16_t font_h = font->height; + uint16_t c_size = font->height * ((font->width + 7) / 8); + + if (x + font_w > text_overlay->rgn.width || y + font_h > text_overlay->rgn.height) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + + uint16_t *dst = (uint16_t *)(text_overlay->frame.data) + (y * text_overlay->rgn.width + x); + uint16_t *pixel = dst; + + while (*str) { + if (*str == '\n' || x + font_w > text_overlay->rgn.width) { + y += font_h; + if (y + font_h > text_overlay->rgn.height) { + break; + } + x = info->x; + dst = (uint16_t *)(text_overlay->frame.data) + (y * text_overlay->rgn.width + x); + str++; + continue; + } + pixel = dst; + uint16_t c_offset = (*str - ' ') * c_size; + const uint8_t *p_c = &font->bitmap[c_offset]; + uint16_t *cur = pixel; + int x0 = 0; + for (int i = 0; i < c_size; i++) { + uint8_t temp = p_c[i]; + for (int j = 0; j < 8; j++) { + if (temp & 0x80) { + cur[x0] = info->color; + } + temp <<= 1; + x0++; + if (x0 == font_w) { + x0 = 0; + pixel += text_overlay->rgn.width; + cur = pixel; + break; + } + } + } + dst += font_w; + x += font_w; + str++; + } + return ESP_CAPTURE_ERR_OK; +} + +int esp_capture_text_overlay_draw_text_fmt(esp_capture_overlay_if_t *h, esp_capture_text_overlay_draw_info_t *info, + const char *fmt, ...) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + if (text_overlay->opened == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + char buffer[CONFIG_ESP_PAINTER_FORMAT_SIZE_MAX]; + va_list args; + int ret; + va_start(args, fmt); + ret = vsnprintf(buffer, CONFIG_ESP_PAINTER_FORMAT_SIZE_MAX, fmt, args); + va_end(args); + if (ret < 0) { + return ESP_CAPTURE_ERR_NOT_ENOUGH; + } + return esp_capture_text_overlay_draw_text(h, info, buffer); +} + +int esp_capture_text_overlay_draw_finished(esp_capture_overlay_if_t *h) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + if (text_overlay->opened == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + media_lib_mutex_unlock(text_overlay->mutex); + return ESP_CAPTURE_ERR_OK; +} + +static int text_overlay_set_alpha(esp_capture_overlay_if_t *h, uint8_t alpha) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + text_overlay->alpha = alpha; + return ESP_CAPTURE_ERR_OK; +} + +static int text_overlay_get_alpha(esp_capture_overlay_if_t *h, uint8_t *alpha) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + *alpha = text_overlay->alpha; + return ESP_CAPTURE_ERR_OK; +} + +static int text_overlay_close(esp_capture_overlay_if_t *h) +{ + text_overlay_t *text_overlay = (text_overlay_t *)h; + if (text_overlay->mutex) { + media_lib_mutex_lock(text_overlay->mutex, 1000); + } + if (text_overlay->frame.data) { + free(text_overlay->frame.data); + text_overlay->frame.data = NULL; + } + if (text_overlay->mutex) { + media_lib_mutex_unlock(text_overlay->mutex); + media_lib_mutex_destroy(text_overlay->mutex); + text_overlay->mutex = NULL; + } + text_overlay->opened = false; + return ESP_CAPTURE_ERR_OK; +} + +esp_capture_overlay_if_t *esp_capture_new_text_overlay(esp_capture_rgn_t *rgn) +{ + text_overlay_t *text_overlay = calloc(1, sizeof(text_overlay_t)); + if (text_overlay == NULL) { + return NULL; + } + text_overlay->base.open = text_overlay_open; + text_overlay->base.get_overlay_region = text_overlay_get_region; + text_overlay->base.set_alpha = text_overlay_set_alpha; + text_overlay->base.get_alpha = text_overlay_get_alpha; + text_overlay->base.acquire_frame = text_overlay_get_frame; + text_overlay->base.release_frame = text_overlay_release_frame; + text_overlay->base.close = text_overlay_close; + text_overlay->rgn = *rgn; + return &text_overlay->base; +} \ No newline at end of file diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/esp_painter_font.h b/components/third_party/esp_capture/src/impl/capture_text_overlay/esp_painter_font.h new file mode 100644 index 0000000..b829868 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/esp_painter_font.h @@ -0,0 +1,65 @@ +/** + * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const uint8_t *bitmap; + uint16_t width; + uint16_t height; +} esp_painter_basic_font_t; + +#define esp_painter_FONT_DECLARE(font_name) extern const esp_painter_basic_font_t font_name; + +#if CONFIG_ESP_PAINTER_BASIC_FONT_12 +esp_painter_FONT_DECLARE(esp_painter_basic_font_12); +#endif + +#if CONFIG_ESP_PAINTER_BASIC_FONT_16 +esp_painter_FONT_DECLARE(esp_painter_basic_font_16); +#endif + +#if CONFIG_ESP_PAINTER_BASIC_FONT_20 +esp_painter_FONT_DECLARE(esp_painter_basic_font_20); +#endif + +#if CONFIG_ESP_PAINTER_BASIC_FONT_24 +esp_painter_FONT_DECLARE(esp_painter_basic_font_24); +#endif + +#if CONFIG_ESP_PAINTER_BASIC_FONT_28 +esp_painter_FONT_DECLARE(esp_painter_basic_font_28); +#endif + +#if CONFIG_ESP_PAINTER_BASIC_FONT_32 +esp_painter_FONT_DECLARE(esp_painter_basic_font_32); +#endif + +#if CONFIG_ESP_PAINTER_BASIC_FONT_36 +esp_painter_FONT_DECLARE(esp_painter_basic_font_36); +#endif + +#if CONFIG_ESP_PAINTER_BASIC_FONT_40 +esp_painter_FONT_DECLARE(esp_painter_basic_font_40); +#endif + +#if CONFIG_ESP_PAINTER_BASIC_FONT_44 +esp_painter_FONT_DECLARE(esp_painter_basic_font_44); +#endif + +#if CONFIG_ESP_PAINTER_BASIC_FONT_48 +esp_painter_FONT_DECLARE(esp_painter_basic_font_48); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_12.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_12.c new file mode 100644 index 0000000..513d14a --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_12.c @@ -0,0 +1,211 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "sdkconfig.h" + +#include "esp_painter_font.h" + +#if CONFIG_ESP_PAINTER_BASIC_FONT_12 + +static const uint8_t bitmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*" ",0*/ + + 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, /*"!",1*/ + + 0x28, 0x28, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*""",2*/ + + 0x00, 0x00, 0x50, 0x50, 0xF8, 0x50, 0x50, 0xF8, 0x50, 0x50, 0x00, 0x00, /*"#",3*/ + + 0x00, 0x20, 0x70, 0xA8, 0xA0, 0x60, 0x30, 0x28, 0xA8, 0x70, 0x20, 0x00, /*"$",4*/ + + 0x00, 0x00, 0x48, 0xA8, 0xB0, 0xA8, 0x74, 0x34, 0x54, 0x48, 0x00, 0x00, /*"%",5*/ + + 0x00, 0x00, 0x20, 0x50, 0x50, 0x6C, 0xA8, 0xA8, 0x94, 0x68, 0x00, 0x00, /*"&",6*/ + + 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"'",7*/ + + 0x08, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x10, 0x08, 0x00, /*"(",8*/ + + 0x40, 0x20, 0x20, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x40, 0x00, /*")",9*/ + + 0x00, 0x00, 0x00, 0x20, 0xA8, 0x70, 0x70, 0xA8, 0x20, 0x00, 0x00, 0x00, /*"*",10*/ + + 0x00, 0x00, 0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, /*"+",11*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x80, 0x00, /*",",12*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"-",13*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, /*".",14*/ + + 0x00, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x00, /*"/",15*/ + + 0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, /*"0",16*/ + + 0x00, 0x00, 0x20, 0x60, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, /*"1",17*/ + + 0x00, 0x00, 0x70, 0x88, 0x88, 0x10, 0x20, 0x40, 0x80, 0xF8, 0x00, 0x00, /*"2",18*/ + + 0x00, 0x00, 0x70, 0x88, 0x08, 0x30, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, /*"3",19*/ + + 0x00, 0x00, 0x10, 0x30, 0x30, 0x50, 0x90, 0xF8, 0x10, 0x38, 0x00, 0x00, /*"4",20*/ + + 0x00, 0x00, 0xF8, 0x80, 0x80, 0xF0, 0x88, 0x08, 0x88, 0x70, 0x00, 0x00, /*"5",21*/ + + 0x00, 0x00, 0x30, 0x48, 0x80, 0xB0, 0xC8, 0x88, 0x88, 0x70, 0x00, 0x00, /*"6",22*/ + + 0x00, 0x00, 0x78, 0x08, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, /*"7",23*/ + + 0x00, 0x00, 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, /*"8",24*/ + + 0x00, 0x00, 0x70, 0x88, 0x88, 0x98, 0x68, 0x08, 0x90, 0x60, 0x00, 0x00, /*"9",25*/ + + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, /*":",26*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x20, 0x00, /*";",27*/ + + 0x00, 0x00, 0x08, 0x10, 0x20, 0x40, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, /*"<",28*/ + + 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, /*"=",29*/ + + 0x00, 0x00, 0x40, 0x20, 0x10, 0x08, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, /*">",30*/ + + 0x00, 0x00, 0x70, 0x88, 0x88, 0x10, 0x20, 0x20, 0x00, 0x20, 0x00, 0x00, /*"?",31*/ + + 0x00, 0x00, 0x38, 0x44, 0x94, 0xB4, 0xB4, 0xB8, 0x44, 0x38, 0x00, 0x00, /*"@",32*/ + + 0x00, 0x00, 0x20, 0x20, 0x30, 0x50, 0x50, 0x78, 0x48, 0xCC, 0x00, 0x00, /*"A",33*/ + + 0x00, 0x00, 0xF0, 0x48, 0x48, 0x70, 0x48, 0x48, 0x48, 0xF0, 0x00, 0x00, /*"B",34*/ + + 0x00, 0x00, 0x78, 0x88, 0x80, 0x80, 0x80, 0x80, 0x88, 0x70, 0x00, 0x00, /*"C",35*/ + + 0x00, 0x00, 0xF0, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0xF0, 0x00, 0x00, /*"D",36*/ + + 0x00, 0x00, 0xF8, 0x48, 0x50, 0x70, 0x50, 0x40, 0x48, 0xF8, 0x00, 0x00, /*"E",37*/ + + 0x00, 0x00, 0xF8, 0x48, 0x50, 0x70, 0x50, 0x40, 0x40, 0xE0, 0x00, 0x00, /*"F",38*/ + + 0x00, 0x00, 0x38, 0x48, 0x80, 0x80, 0x9C, 0x88, 0x48, 0x30, 0x00, 0x00, /*"G",39*/ + + 0x00, 0x00, 0xCC, 0x48, 0x48, 0x78, 0x48, 0x48, 0x48, 0xCC, 0x00, 0x00, /*"H",40*/ + + 0x00, 0x00, 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xF8, 0x00, 0x00, /*"I",41*/ + + 0x00, 0x00, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x90, 0xE0, /*"J",42*/ + + 0x00, 0x00, 0xEC, 0x48, 0x50, 0x60, 0x50, 0x48, 0x48, 0xEC, 0x00, 0x00, /*"K",43*/ + + 0x00, 0x00, 0xE0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0xFC, 0x00, 0x00, /*"L",44*/ + + 0x00, 0x00, 0xDC, 0xD8, 0xD8, 0xD8, 0xA8, 0xA8, 0xA8, 0xAC, 0x00, 0x00, /*"M",45*/ + + 0x00, 0x00, 0xDC, 0x48, 0x68, 0x68, 0x58, 0x58, 0x48, 0xE8, 0x00, 0x00, /*"N",46*/ + + 0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, /*"O",47*/ + + 0x00, 0x00, 0xF0, 0x48, 0x48, 0x70, 0x40, 0x40, 0x40, 0xE0, 0x00, 0x00, /*"P",48*/ + + 0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0xE8, 0x98, 0x70, 0x18, 0x00, /*"Q",49*/ + + 0x00, 0x00, 0xF0, 0x48, 0x48, 0x70, 0x50, 0x48, 0x48, 0xEC, 0x00, 0x00, /*"R",50*/ + + 0x00, 0x00, 0x78, 0x88, 0x80, 0x60, 0x10, 0x08, 0x88, 0xF0, 0x00, 0x00, /*"S",51*/ + + 0x00, 0x00, 0xF8, 0xA8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, /*"T",52*/ + + 0x00, 0x00, 0xCC, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x30, 0x00, 0x00, /*"U",53*/ + + 0x00, 0x00, 0xCC, 0x48, 0x48, 0x50, 0x50, 0x30, 0x20, 0x20, 0x00, 0x00, /*"V",54*/ + + 0x00, 0x00, 0xA8, 0xA8, 0xA8, 0xA8, 0x70, 0x50, 0x50, 0x50, 0x00, 0x00, /*"W",55*/ + + 0x00, 0x00, 0xD8, 0x50, 0x50, 0x20, 0x20, 0x50, 0x50, 0xD8, 0x00, 0x00, /*"X",56*/ + + 0x00, 0x00, 0xD8, 0x50, 0x50, 0x50, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, /*"Y",57*/ + + 0x00, 0x00, 0xF8, 0x90, 0x10, 0x20, 0x20, 0x40, 0x48, 0xF8, 0x00, 0x00, /*"Z",58*/ + + 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, /*"[",59*/ + + 0x00, 0x40, 0x40, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, 0x08, 0x08, 0x00, /*"\",60*/ + + 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x70, 0x00, /*"]",61*/ + + 0x20, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"^",62*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, /*"_",63*/ + + 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"`",64*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x48, 0x38, 0x48, 0x3C, 0x00, 0x00, /*"a",65*/ + + 0x00, 0xC0, 0x40, 0x40, 0x40, 0x70, 0x48, 0x48, 0x48, 0x70, 0x00, 0x00, /*"b",66*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x48, 0x40, 0x48, 0x30, 0x00, 0x00, /*"c",67*/ + + 0x00, 0x18, 0x08, 0x08, 0x08, 0x38, 0x48, 0x48, 0x48, 0x3C, 0x00, 0x00, /*"d",68*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x48, 0x78, 0x40, 0x38, 0x00, 0x00, /*"e",69*/ + + 0x00, 0x18, 0x24, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x78, 0x00, 0x00, /*"f",70*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x48, 0x30, 0x40, 0x38, 0x44, 0x38, /*"g",71*/ + + 0x00, 0xC0, 0x40, 0x40, 0x40, 0x70, 0x48, 0x48, 0x48, 0xEC, 0x00, 0x00, /*"h",72*/ + + 0x00, 0x20, 0x20, 0x00, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, /*"i",73*/ + + 0x00, 0x10, 0x10, 0x00, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0xE0, /*"j",74*/ + + 0x00, 0xC0, 0x40, 0x40, 0x40, 0x58, 0x50, 0x60, 0x50, 0xC8, 0x00, 0x00, /*"k",75*/ + + 0x00, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xF8, 0x00, 0x00, /*"l",76*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xA8, 0xA8, 0xA8, 0xA8, 0x00, 0x00, /*"m",77*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x48, 0x48, 0x48, 0xEC, 0x00, 0x00, /*"n",78*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x48, 0x48, 0x48, 0x30, 0x00, 0x00, /*"o",79*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x48, 0x48, 0x48, 0x70, 0x40, 0xE0, /*"p",80*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x48, 0x48, 0x48, 0x38, 0x08, 0x1C, /*"q",81*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x60, 0x40, 0x40, 0xE0, 0x00, 0x00, /*"r",82*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x40, 0x30, 0x08, 0x78, 0x00, 0x00, /*"s",83*/ + + 0x00, 0x00, 0x00, 0x20, 0x20, 0x78, 0x20, 0x20, 0x20, 0x38, 0x00, 0x00, /*"t",84*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x48, 0x48, 0x48, 0x3C, 0x00, 0x00, /*"u",85*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x50, 0x50, 0x20, 0x20, 0x00, 0x00, /*"v",86*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xA8, 0x70, 0x50, 0x50, 0x00, 0x00, /*"w",87*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x50, 0x20, 0x50, 0xD8, 0x00, 0x00, /*"x",88*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x48, 0x48, 0x30, 0x10, 0x20, 0xC0, /*"y",89*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x10, 0x20, 0x20, 0x78, 0x00, 0x00, /*"z",90*/ + + 0x18, 0x10, 0x10, 0x10, 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x18, 0x00, /*"{",91*/ + + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, /*"|",92*/ + + 0x60, 0x20, 0x20, 0x20, 0x20, 0x10, 0x20, 0x20, 0x20, 0x20, 0x60, 0x00, /*"}",93*/ + + 0x68, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"~",94*/ +}; + +const esp_painter_basic_font_t esp_painter_basic_font_12 = { + .bitmap = bitmap, + .width = 6, + .height = 12, +}; + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_16.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_16.c new file mode 100644 index 0000000..4f336cc --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_16.c @@ -0,0 +1,211 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "sdkconfig.h" + +#include "esp_painter_font.h" + +#if CONFIG_ESP_PAINTER_BASIC_FONT_16 + +static const uint8_t bitmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*" ",0*/ + + 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, /*"!",1*/ + + 0x00, 0x12, 0x24, 0x24, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*""",2*/ + + 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x7E, 0x24, 0x24, 0x24, 0x7E, 0x24, 0x24, 0x24, 0x00, 0x00, /*"#",3*/ + + 0x00, 0x00, 0x08, 0x3C, 0x4A, 0x4A, 0x48, 0x38, 0x0C, 0x0A, 0x0A, 0x4A, 0x4A, 0x3C, 0x08, 0x08, /*"$",4*/ + + 0x00, 0x00, 0x00, 0x44, 0xA4, 0xA8, 0xA8, 0xB0, 0x54, 0x1A, 0x2A, 0x2A, 0x4A, 0x44, 0x00, 0x00, /*"%",5*/ + + 0x00, 0x00, 0x00, 0x30, 0x48, 0x48, 0x48, 0x50, 0x6E, 0xA4, 0x94, 0x98, 0x89, 0x76, 0x00, 0x00, /*"&",6*/ + + 0x00, 0x60, 0x20, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"'",7*/ + + 0x00, 0x02, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x02, 0x00, /*"(",8*/ + + 0x00, 0x40, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x20, 0x40, 0x00, /*")",9*/ + + 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0xD6, 0x38, 0x38, 0xD6, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, /*"*",10*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x7F, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, /*"+",11*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x20, 0x20, 0x40, /*",",12*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"-",13*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, /*".",14*/ + + 0x00, 0x00, 0x02, 0x04, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x00, /*"/",15*/ + + 0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00, /*"0",16*/ + + 0x00, 0x00, 0x00, 0x08, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, /*"1",17*/ + + 0x00, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x02, 0x04, 0x08, 0x10, 0x20, 0x42, 0x7E, 0x00, 0x00, /*"2",18*/ + + 0x00, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x02, 0x04, 0x18, 0x04, 0x02, 0x42, 0x42, 0x3C, 0x00, 0x00, /*"3",19*/ + + 0x00, 0x00, 0x00, 0x04, 0x0C, 0x0C, 0x14, 0x24, 0x24, 0x44, 0x7F, 0x04, 0x04, 0x1F, 0x00, 0x00, /*"4",20*/ + + 0x00, 0x00, 0x00, 0x7E, 0x40, 0x40, 0x40, 0x78, 0x44, 0x02, 0x02, 0x42, 0x44, 0x38, 0x00, 0x00, /*"5",21*/ + + 0x00, 0x00, 0x00, 0x18, 0x24, 0x40, 0x40, 0x5C, 0x62, 0x42, 0x42, 0x42, 0x22, 0x1C, 0x00, 0x00, /*"6",22*/ + + 0x00, 0x00, 0x00, 0x7E, 0x42, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, /*"7",23*/ + + 0x00, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x24, 0x18, 0x24, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00, /*"8",24*/ + + 0x00, 0x00, 0x00, 0x38, 0x44, 0x42, 0x42, 0x42, 0x46, 0x3A, 0x02, 0x02, 0x24, 0x18, 0x00, 0x00, /*"9",25*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, /*":",26*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, /*";",27*/ + + 0x00, 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, /*"<",28*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"=",29*/ + + 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, /*">",30*/ + + 0x00, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x62, 0x04, 0x08, 0x08, 0x08, 0x00, 0x18, 0x18, 0x00, 0x00, /*"?",31*/ + + 0x00, 0x00, 0x00, 0x38, 0x44, 0x5A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x5C, 0x42, 0x3C, 0x00, 0x00, /*"@",32*/ + + 0x00, 0x00, 0x00, 0x10, 0x10, 0x18, 0x28, 0x28, 0x24, 0x3C, 0x44, 0x42, 0x42, 0xE7, 0x00, 0x00, /*"A",33*/ + + 0x00, 0x00, 0x00, 0xF8, 0x44, 0x44, 0x44, 0x78, 0x44, 0x42, 0x42, 0x42, 0x44, 0xF8, 0x00, 0x00, /*"B",34*/ + + 0x00, 0x00, 0x00, 0x3E, 0x42, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0x42, 0x44, 0x38, 0x00, 0x00, /*"C",35*/ + + 0x00, 0x00, 0x00, 0xF8, 0x44, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x44, 0xF8, 0x00, 0x00, /*"D",36*/ + + 0x00, 0x00, 0x00, 0xFC, 0x42, 0x48, 0x48, 0x78, 0x48, 0x48, 0x40, 0x42, 0x42, 0xFC, 0x00, 0x00, /*"E",37*/ + + 0x00, 0x00, 0x00, 0xFC, 0x42, 0x48, 0x48, 0x78, 0x48, 0x48, 0x40, 0x40, 0x40, 0xE0, 0x00, 0x00, /*"F",38*/ + + 0x00, 0x00, 0x00, 0x3C, 0x44, 0x44, 0x80, 0x80, 0x80, 0x8E, 0x84, 0x44, 0x44, 0x38, 0x00, 0x00, /*"G",39*/ + + 0x00, 0x00, 0x00, 0xE7, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x42, 0xE7, 0x00, 0x00, /*"H",40*/ + + 0x00, 0x00, 0x00, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, /*"I",41*/ + + 0x00, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x88, 0xF0, /*"J",42*/ + + 0x00, 0x00, 0x00, 0xEE, 0x44, 0x48, 0x50, 0x70, 0x50, 0x48, 0x48, 0x44, 0x44, 0xEE, 0x00, 0x00, /*"K",43*/ + + 0x00, 0x00, 0x00, 0xE0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x42, 0xFE, 0x00, 0x00, /*"L",44*/ + + 0x00, 0x00, 0x00, 0xEE, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x54, 0x54, 0x54, 0x54, 0xD6, 0x00, 0x00, /*"M",45*/ + + 0x00, 0x00, 0x00, 0xC7, 0x62, 0x62, 0x52, 0x52, 0x4A, 0x4A, 0x4A, 0x46, 0x46, 0xE2, 0x00, 0x00, /*"N",46*/ + + 0x00, 0x00, 0x00, 0x38, 0x44, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x44, 0x38, 0x00, 0x00, /*"O",47*/ + + 0x00, 0x00, 0x00, 0xFC, 0x42, 0x42, 0x42, 0x42, 0x7C, 0x40, 0x40, 0x40, 0x40, 0xE0, 0x00, 0x00, /*"P",48*/ + + 0x00, 0x00, 0x00, 0x38, 0x44, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0xB2, 0x4C, 0x38, 0x06, 0x00, /*"Q",49*/ + + 0x00, 0x00, 0x00, 0xFC, 0x42, 0x42, 0x42, 0x7C, 0x48, 0x48, 0x44, 0x44, 0x42, 0xE3, 0x00, 0x00, /*"R",50*/ + + 0x00, 0x00, 0x00, 0x3E, 0x42, 0x42, 0x40, 0x20, 0x18, 0x04, 0x02, 0x42, 0x42, 0x7C, 0x00, 0x00, /*"S",51*/ + + 0x00, 0x00, 0x00, 0xFE, 0x92, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, /*"T",52*/ + + 0x00, 0x00, 0x00, 0xE7, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00, /*"U",53*/ + + 0x00, 0x00, 0x00, 0xE7, 0x42, 0x42, 0x44, 0x24, 0x24, 0x28, 0x28, 0x18, 0x10, 0x10, 0x00, 0x00, /*"V",54*/ + + 0x00, 0x00, 0x00, 0xD6, 0x54, 0x54, 0x54, 0x54, 0x54, 0x6C, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00, /*"W",55*/ + + 0x00, 0x00, 0x00, 0xE7, 0x42, 0x24, 0x24, 0x18, 0x18, 0x18, 0x24, 0x24, 0x42, 0xE7, 0x00, 0x00, /*"X",56*/ + + 0x00, 0x00, 0x00, 0xEE, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, /*"Y",57*/ + + 0x00, 0x00, 0x00, 0x7E, 0x84, 0x04, 0x08, 0x08, 0x10, 0x20, 0x20, 0x42, 0x42, 0xFC, 0x00, 0x00, /*"Z",58*/ + + 0x00, 0x1E, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1E, 0x00, /*"[",59*/ + + 0x00, 0x00, 0x40, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x04, 0x02, 0x02, /*"\",60*/ + + 0x00, 0x78, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x78, 0x00, /*"]",61*/ + + 0x00, 0x18, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"^",62*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, /*"_",63*/ + + 0x00, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"`",64*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x0C, 0x34, 0x44, 0x4C, 0x36, 0x00, 0x00, /*"a",65*/ + + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x40, 0x40, 0x58, 0x64, 0x42, 0x42, 0x42, 0x64, 0x58, 0x00, 0x00, /*"b",66*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x40, 0x40, 0x40, 0x22, 0x1C, 0x00, 0x00, /*"c",67*/ + + 0x00, 0x00, 0x00, 0x00, 0x06, 0x02, 0x02, 0x3E, 0x42, 0x42, 0x42, 0x42, 0x46, 0x3B, 0x00, 0x00, /*"d",68*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x7E, 0x40, 0x42, 0x3C, 0x00, 0x00, /*"e",69*/ + + 0x00, 0x00, 0x00, 0x00, 0x0C, 0x12, 0x10, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, /*"f",70*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x44, 0x44, 0x38, 0x40, 0x3C, 0x42, 0x42, 0x3C, /*"g",71*/ + + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x40, 0x40, 0x5C, 0x62, 0x42, 0x42, 0x42, 0x42, 0xE7, 0x00, 0x00, /*"h",72*/ + + 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, /*"i",73*/ + + 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x1C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x78, /*"j",74*/ + + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x40, 0x40, 0x4E, 0x48, 0x50, 0x70, 0x48, 0x44, 0xEE, 0x00, 0x00, /*"k",75*/ + + 0x00, 0x00, 0x00, 0x10, 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7C, 0x00, 0x00, /*"l",76*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x49, 0x49, 0x49, 0x49, 0x49, 0xED, 0x00, 0x00, /*"m",77*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x62, 0x42, 0x42, 0x42, 0x42, 0xE7, 0x00, 0x00, /*"n",78*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00, /*"o",79*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x64, 0x42, 0x42, 0x42, 0x64, 0x58, 0x40, 0xE0, /*"p",80*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x26, 0x42, 0x42, 0x42, 0x26, 0x1A, 0x02, 0x07, /*"q",81*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x32, 0x20, 0x20, 0x20, 0x20, 0xF8, 0x00, 0x00, /*"r",82*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x42, 0x40, 0x3C, 0x02, 0x42, 0x7C, 0x00, 0x00, /*"s",83*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x10, 0x10, 0x12, 0x0C, 0x00, 0x00, /*"t",84*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x42, 0x42, 0x42, 0x42, 0x46, 0x3B, 0x00, 0x00, /*"u",85*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x44, 0x44, 0x28, 0x28, 0x10, 0x10, 0x00, 0x00, /*"v",86*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0x89, 0x4A, 0x5A, 0x54, 0x24, 0x24, 0x00, 0x00, /*"w",87*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x24, 0x18, 0x18, 0x18, 0x24, 0x6E, 0x00, 0x00, /*"x",88*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x42, 0x24, 0x24, 0x18, 0x18, 0x10, 0x10, 0x60, /*"y",89*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x44, 0x08, 0x10, 0x10, 0x22, 0x7E, 0x00, 0x00, /*"z",90*/ + + 0x00, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x00, /*"{",91*/ + + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, /*"|",92*/ + + 0x00, 0xC0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0xC0, 0x00, /*"}",93*/ + + 0x20, 0x5A, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"~",94*/ +}; + +const esp_painter_basic_font_t esp_painter_basic_font_16 = { + .bitmap = bitmap, + .width = 8, + .height = 16, +}; + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_20.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_20.c new file mode 100644 index 0000000..6e1cc1c --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_20.c @@ -0,0 +1,401 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "sdkconfig.h" + +#include "esp_painter_font.h" + +#if CONFIG_ESP_PAINTER_BASIC_FONT_20 + +static const uint8_t bitmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*" ",0*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"!",1*/ + + 0x00, 0x00, 0x1B, 0x00, 0x1B, 0x00, 0x36, 0x00, 0x24, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*""",2*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x11, 0x00, 0x11, 0x00, 0x7F, 0x80, 0x7F, 0x80, + 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x7F, 0x80, 0x7F, 0x80, 0x22, 0x00, 0x22, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"#",3*/ + + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1E, 0x00, 0x29, 0x00, 0x49, 0x00, 0x49, 0x00, 0x48, 0x00, + 0x28, 0x00, 0x1C, 0x00, 0x0A, 0x00, 0x09, 0x00, 0x09, 0x00, 0x49, 0x00, 0x49, 0x00, 0x4A, 0x00, + 0x3C, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, /*"$",4*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x92, 0x00, 0x94, 0x00, 0x94, 0x00, 0x94, 0x00, + 0x98, 0x00, 0x9B, 0x00, 0x6C, 0x80, 0x14, 0x80, 0x14, 0x80, 0x14, 0x80, 0x24, 0x80, 0x24, 0x80, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"%",5*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x24, 0x00, 0x24, 0x00, 0x24, 0x00, 0x24, 0x00, + 0x28, 0x00, 0x33, 0x80, 0x51, 0x00, 0x91, 0x00, 0x89, 0x00, 0x8A, 0x00, 0x86, 0x00, 0x46, 0x40, + 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"&",6*/ + + 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"'",7*/ + + 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, /*"(",8*/ + + 0x00, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, + 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, /*")",9*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0xC9, 0x80, + 0xEB, 0x80, 0x1C, 0x00, 0x1C, 0x00, 0xEB, 0x80, 0xC9, 0x80, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"*",10*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x7F, 0xC0, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"+",11*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, /*",",12*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"-",13*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*".",14*/ + + 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x10, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, /*"/",15*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x21, 0x00, 0x21, 0x00, 0x40, 0x80, 0x40, 0x80, + 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x21, 0x00, 0x21, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"0",16*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"1",17*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x21, 0x00, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, + 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x80, 0x40, 0x80, + 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"2",18*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x42, 0x00, 0x41, 0x00, 0x41, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, 0x40, 0x80, 0x40, 0x80, 0x41, 0x00, + 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"3",19*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0A, 0x00, 0x0A, 0x00, + 0x12, 0x00, 0x22, 0x00, 0x22, 0x00, 0x42, 0x00, 0x7F, 0x80, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"4",20*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x2E, 0x00, 0x31, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x40, 0x80, 0x40, 0x80, 0x41, 0x00, + 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"5",21*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x11, 0x00, 0x21, 0x00, 0x40, 0x00, 0x40, 0x00, + 0x5E, 0x00, 0x61, 0x00, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x20, 0x80, 0x21, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"6",22*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x40, 0x80, 0x41, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"7",23*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x21, 0x00, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, + 0x21, 0x00, 0x1E, 0x00, 0x21, 0x00, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x21, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"8",24*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x21, 0x00, 0x41, 0x00, 0x40, 0x80, 0x40, 0x80, + 0x40, 0x80, 0x41, 0x80, 0x22, 0x80, 0x1C, 0x80, 0x00, 0x80, 0x01, 0x00, 0x21, 0x00, 0x22, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"9",25*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*":",26*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x0C, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, /*";",27*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, + 0x20, 0x00, 0x40, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"<",28*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"=",29*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*">",30*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x21, 0x00, 0x40, 0x80, 0x40, 0x80, + 0x60, 0x80, 0x01, 0x00, 0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"?",31*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x21, 0x00, 0x40, 0x80, 0x4E, 0x80, 0x92, 0x80, + 0x92, 0x80, 0xA4, 0x80, 0xA4, 0x80, 0xA4, 0x80, 0xA5, 0x00, 0x9E, 0x00, 0x40, 0x80, 0x21, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"@",32*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x14, 0x00, 0x14, 0x00, + 0x14, 0x00, 0x14, 0x00, 0x22, 0x00, 0x22, 0x00, 0x3E, 0x00, 0x22, 0x00, 0x41, 0x00, 0x41, 0x00, + 0xE3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"A",33*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x42, 0x00, 0x42, 0x00, 0x42, 0x00, + 0x44, 0x00, 0x7C, 0x00, 0x42, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x42, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"B",34*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x80, 0x21, 0x80, 0x40, 0x80, 0x40, 0x80, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0x40, 0x80, 0x61, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"C",35*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x42, 0x00, 0x41, 0x00, 0x40, 0x80, + 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x41, 0x00, 0x42, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"D",36*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x41, 0x00, 0x40, 0x80, 0x42, 0x00, + 0x42, 0x00, 0x7E, 0x00, 0x42, 0x00, 0x42, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x80, 0x41, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"E",37*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x41, 0x00, 0x40, 0x80, 0x42, 0x00, + 0x42, 0x00, 0x7E, 0x00, 0x42, 0x00, 0x42, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, + 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"F",38*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x80, 0x11, 0x80, 0x20, 0x80, 0x40, 0x80, + 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x41, 0xC0, 0x40, 0x80, 0x40, 0x80, 0x20, 0x80, 0x30, 0x80, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"G",39*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x80, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, + 0x41, 0x00, 0x41, 0x00, 0x7F, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, + 0xE3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"H",40*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"I",41*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x84, 0x00, 0x88, 0x00, 0x70, 0x00, /*"J",42*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x80, 0x42, 0x00, 0x44, 0x00, 0x44, 0x00, + 0x48, 0x00, 0x58, 0x00, 0x68, 0x00, 0x44, 0x00, 0x44, 0x00, 0x42, 0x00, 0x42, 0x00, 0x41, 0x00, + 0xE3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"K",43*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, + 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x80, 0x41, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"L",44*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x80, 0x63, 0x00, 0x63, 0x00, 0x63, 0x00, + 0x55, 0x00, 0x55, 0x00, 0x55, 0x00, 0x55, 0x00, 0x55, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, + 0xEB, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"M",45*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x80, 0x61, 0x00, 0x51, 0x00, 0x51, 0x00, + 0x51, 0x00, 0x49, 0x00, 0x49, 0x00, 0x45, 0x00, 0x45, 0x00, 0x43, 0x00, 0x43, 0x00, 0x43, 0x00, + 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"N",46*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x63, 0x00, 0x41, 0x00, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x41, 0x00, 0x63, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"O",47*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x41, 0x00, 0x40, 0x80, 0x40, 0x80, + 0x40, 0x80, 0x41, 0x00, 0x7E, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, + 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"P",48*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x63, 0x00, 0x41, 0x00, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xB8, 0x80, 0x45, 0x00, 0x63, 0x00, + 0x1E, 0x00, 0x02, 0x80, 0x01, 0x00, 0x00, 0x00, /*"Q",49*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x42, 0x00, 0x41, 0x00, 0x41, 0x00, + 0x42, 0x00, 0x7C, 0x00, 0x48, 0x00, 0x44, 0x00, 0x44, 0x00, 0x42, 0x00, 0x42, 0x00, 0x41, 0x00, + 0xE1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"R",50*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x23, 0x00, 0x41, 0x00, 0x40, 0x00, + 0x40, 0x00, 0x30, 0x00, 0x0C, 0x00, 0x02, 0x00, 0x01, 0x00, 0x41, 0x00, 0x41, 0x00, 0x62, 0x00, + 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"S",51*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x88, 0x80, 0x88, 0x80, 0x08, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"T",52*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x80, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, + 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x22, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"U",53*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x80, 0x41, 0x00, 0x41, 0x00, 0x22, 0x00, + 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x14, 0x00, 0x14, 0x00, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"V",54*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0x80, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, + 0x49, 0x00, 0x55, 0x00, 0x55, 0x00, 0x55, 0x00, 0x55, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"W",55*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x80, 0x21, 0x00, 0x12, 0x00, 0x12, 0x00, + 0x14, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x14, 0x00, 0x12, 0x00, 0x22, 0x00, 0x21, 0x00, + 0x73, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"X",56*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x80, 0x41, 0x00, 0x41, 0x00, 0x22, 0x00, + 0x22, 0x00, 0x14, 0x00, 0x14, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Y",57*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x21, 0x00, 0x41, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x10, 0x00, 0x20, 0x80, 0x21, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Z",58*/ + + 0x00, 0x00, 0x0F, 0x80, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, /*"[",59*/ + + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, /*"\",60*/ + + 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, /*"]",61*/ + + 0x00, 0x00, 0x1C, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"^",62*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, /*"_",63*/ + + 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"`",64*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3E, 0x00, 0x41, 0x00, 0x41, 0x00, 0x07, 0x00, 0x39, 0x00, 0x41, 0x00, 0x41, 0x00, 0x43, 0x40, + 0x3D, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"a",65*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x2E, 0x00, 0x31, 0x00, 0x20, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x80, 0x31, 0x00, + 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"b",66*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x21, 0x00, 0x41, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x80, 0x21, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"c",67*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x1D, 0x00, 0x23, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x23, 0x00, + 0x1D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"d",68*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x21, 0x00, 0x40, 0x80, 0x40, 0x80, 0x7F, 0x80, 0x40, 0x00, 0x40, 0x80, 0x21, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"e",69*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x08, 0x80, 0x08, 0x00, 0x08, 0x00, + 0x3F, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"f",70*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x80, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x1E, 0x00, 0x20, 0x00, 0x3F, 0x00, + 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x3F, 0x00, /*"g",71*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x2E, 0x00, 0x31, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, + 0x73, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"h",72*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"i",73*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3C, 0x00, /*"j",74*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x27, 0x00, 0x22, 0x00, 0x24, 0x00, 0x28, 0x00, 0x34, 0x00, 0x24, 0x00, 0x22, 0x00, 0x21, 0x00, + 0x73, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"k",75*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x78, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"l",76*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xDE, 0x00, 0x69, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, + 0xED, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"m",77*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6E, 0x00, 0x31, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, + 0x73, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"n",78*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1E, 0x00, 0x21, 0x00, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x40, 0x80, 0x21, 0x00, + 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"o",79*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6E, 0x00, 0x31, 0x00, 0x20, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x80, 0x31, 0x00, + 0x2E, 0x00, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, /*"p",80*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1D, 0x00, 0x23, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x41, 0x00, 0x23, 0x00, + 0x1D, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x80, /*"q",81*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x80, 0x14, 0x80, 0x18, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"r",82*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0x43, 0x00, 0x41, 0x00, 0x60, 0x00, 0x1C, 0x00, 0x03, 0x00, 0x41, 0x00, 0x61, 0x00, + 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"s",83*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x3F, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x80, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"t",84*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x21, 0x00, 0x23, 0x00, + 0x1D, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"u",85*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE3, 0x80, 0x41, 0x00, 0x41, 0x00, 0x22, 0x00, 0x22, 0x00, 0x14, 0x00, 0x14, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"v",86*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xDD, 0x80, 0x49, 0x00, 0x49, 0x00, 0x49, 0x00, 0x55, 0x00, 0x55, 0x00, 0x55, 0x00, 0x22, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"w",87*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x80, 0x21, 0x00, 0x12, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x12, 0x00, 0x21, 0x00, + 0x73, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"x",88*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x80, 0x21, 0x00, 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x0A, 0x00, 0x0C, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x28, 0x00, 0x30, 0x00, /*"y",89*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x42, 0x00, 0x44, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x80, 0x20, 0x80, 0x21, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"z",90*/ + + 0x00, 0x00, 0x01, 0x80, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, /*"{",91*/ + + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, /*"|",92*/ + + 0x00, 0x00, 0x60, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x10, 0x00, 0x10, 0x00, 0x0C, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x10, 0x00, 0x10, 0x00, 0x60, 0x00, 0x00, 0x00, /*"}",93*/ + + 0x30, 0x00, 0x48, 0x80, 0x44, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"~",94*/ +}; + +const esp_painter_basic_font_t esp_painter_basic_font_20 = { + .bitmap = bitmap, + .width = 10, + .height = 20, +}; + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_24.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_24.c new file mode 100644 index 0000000..36c1e77 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_24.c @@ -0,0 +1,401 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "sdkconfig.h" + +#include "esp_painter_font.h" + +#if CONFIG_ESP_PAINTER_BASIC_FONT_24 + +static const uint8_t bitmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*" ",0*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"!",1*/ + + 0x00, 0x00, 0x00, 0x00, 0x06, 0x60, 0x06, 0x60, 0x0C, 0xC0, 0x19, 0x80, 0x11, 0x00, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*""",2*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, 0x08, 0x40, + 0x7F, 0xE0, 0x7F, 0xE0, 0x10, 0x40, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x7F, 0xE0, + 0x7F, 0xE0, 0x20, 0x80, 0x20, 0x80, 0x20, 0x80, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"#",3*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x0F, 0x80, 0x1A, 0xC0, 0x32, 0xC0, + 0x33, 0xC0, 0x32, 0x00, 0x1A, 0x00, 0x0E, 0x00, 0x07, 0x00, 0x03, 0x80, 0x03, 0x80, 0x02, 0xC0, + 0x32, 0xC0, 0x3A, 0xC0, 0x32, 0xC0, 0x12, 0x80, 0x0F, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, /*"$",4*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x50, 0x80, 0x89, 0x00, 0x89, 0x00, + 0x89, 0x00, 0x8A, 0x00, 0x8A, 0x00, 0x5C, 0x00, 0x75, 0xC0, 0x05, 0x40, 0x0A, 0x20, 0x0A, 0x20, + 0x12, 0x20, 0x12, 0x20, 0x12, 0x20, 0x21, 0x40, 0x21, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"%",5*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x36, 0x00, 0x36, 0x00, 0x36, 0x00, + 0x36, 0x00, 0x36, 0x00, 0x34, 0x00, 0x19, 0xE0, 0x38, 0x80, 0x58, 0x80, 0xCC, 0x80, 0xCC, 0x80, + 0xC6, 0x80, 0xC7, 0x00, 0xC3, 0x10, 0x63, 0x90, 0x3C, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"&",6*/ + + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x38, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"'",7*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00, /*"(",8*/ + + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, /*")",9*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x62, 0x30, 0x72, 0xF0, 0x0B, 0x80, 0x0F, 0x80, 0x7A, 0xF0, 0x62, 0x30, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"*",10*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x7F, 0xF0, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"+",11*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x38, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, /*",",12*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"-",13*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*".",14*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x60, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x80, + 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x06, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x10, 0x00, 0x10, 0x00, 0x30, 0x00, 0x20, 0x00, 0x60, 0x00, 0x40, 0x00, 0x00, 0x00, /*"/",15*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x19, 0x80, 0x30, 0xC0, + 0x30, 0xC0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x30, 0xC0, 0x30, 0xC0, 0x19, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"0",16*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3E, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"1",17*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x21, 0x80, 0x40, 0xC0, + 0x60, 0xC0, 0x60, 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x10, 0x40, 0x20, 0x40, 0x60, 0x40, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"2",18*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x63, 0x00, 0x61, 0x80, + 0x61, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0x00, 0x0E, 0x00, 0x01, 0x80, 0x00, 0x80, 0x00, 0xC0, + 0x00, 0xC0, 0x60, 0xC0, 0x60, 0xC0, 0x61, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"3",19*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x03, 0x80, + 0x05, 0x80, 0x05, 0x80, 0x09, 0x80, 0x11, 0x80, 0x11, 0x80, 0x21, 0x80, 0x41, 0x80, 0x7F, 0xF0, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"4",20*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0x20, 0x00, 0x20, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x2F, 0x00, 0x31, 0x80, 0x20, 0xC0, 0x00, 0xC0, 0x00, 0xC0, + 0x60, 0xC0, 0x60, 0xC0, 0x41, 0x80, 0x21, 0x80, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"5",21*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x18, 0xC0, 0x30, 0xC0, + 0x30, 0x00, 0x20, 0x00, 0x60, 0x00, 0x67, 0x80, 0x68, 0xC0, 0x70, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x20, 0x60, 0x30, 0x40, 0x18, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"6",22*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x30, 0x60, 0x20, 0x40, + 0x20, 0x80, 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"7",23*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x30, 0xC0, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x30, 0x40, 0x38, 0xC0, 0x0F, 0x00, 0x13, 0x80, 0x30, 0xC0, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x30, 0xC0, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"8",24*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x30, 0x80, 0x30, 0xC0, + 0x60, 0x40, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xE0, 0x31, 0x60, 0x1E, 0x60, 0x00, 0x60, + 0x00, 0xC0, 0x00, 0xC0, 0x30, 0x80, 0x31, 0x80, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"9",25*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*":",26*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04, 0x00, /*";",27*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, + 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"<",28*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"=",29*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, + 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*">",30*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x18, 0x60, 0x20, 0x30, + 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0xE0, 0x01, 0x80, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"?",31*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x18, 0xC0, 0x30, 0x40, 0x33, 0xA0, + 0x26, 0xA0, 0x66, 0xA0, 0x65, 0xA0, 0x6D, 0x20, 0x6D, 0x20, 0x6D, 0x20, 0x6D, 0x20, 0x6D, 0x40, + 0x27, 0x80, 0x30, 0x20, 0x30, 0x40, 0x18, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"@",32*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0A, 0x00, + 0x0B, 0x00, 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x11, 0x80, 0x10, 0x80, 0x1F, 0x80, 0x20, 0xC0, + 0x20, 0xC0, 0x20, 0x40, 0x40, 0x40, 0x40, 0x60, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"A",33*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x31, 0xC0, 0x30, 0xC0, + 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x31, 0x80, 0x3F, 0x00, 0x30, 0xC0, 0x30, 0x40, 0x30, 0x60, + 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0xC0, 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"B",34*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x18, 0xC0, 0x30, 0x60, + 0x30, 0x20, 0x20, 0x20, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, + 0x60, 0x20, 0x30, 0x20, 0x30, 0x40, 0x18, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"C",35*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x31, 0x80, 0x30, 0xC0, + 0x30, 0xC0, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, + 0x30, 0x60, 0x30, 0xC0, 0x30, 0xC0, 0x31, 0x80, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"D",36*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x60, 0xC0, 0x60, 0x20, + 0x60, 0x20, 0x60, 0x00, 0x61, 0x00, 0x61, 0x00, 0x7F, 0x00, 0x61, 0x00, 0x61, 0x00, 0x61, 0x00, + 0x60, 0x00, 0x60, 0x20, 0x60, 0x20, 0x60, 0x40, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"E",37*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x60, 0xC0, 0x60, 0x20, + 0x60, 0x20, 0x60, 0x00, 0x61, 0x00, 0x61, 0x00, 0x7F, 0x00, 0x61, 0x00, 0x61, 0x00, 0x61, 0x00, + 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"F",38*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x18, 0x80, 0x30, 0x40, + 0x30, 0x40, 0x20, 0x40, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x63, 0xF0, 0x60, 0xC0, + 0x60, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x18, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"G",39*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7F, 0xE0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"H",40*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"I",41*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE0, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x63, 0x00, 0x66, 0x00, 0x3C, 0x00, /*"J",42*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xC0, 0x61, 0x80, 0x63, 0x00, + 0x62, 0x00, 0x64, 0x00, 0x68, 0x00, 0x6C, 0x00, 0x74, 0x00, 0x76, 0x00, 0x62, 0x00, 0x63, 0x00, + 0x61, 0x00, 0x61, 0x80, 0x60, 0x80, 0x60, 0xC0, 0xF1, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"K",43*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x60, 0x00, 0x60, 0x00, + 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, + 0x60, 0x00, 0x60, 0x20, 0x60, 0x20, 0x60, 0x40, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"L",44*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0, 0x61, 0xC0, 0x61, 0xC0, + 0x71, 0xC0, 0x71, 0xC0, 0x72, 0xC0, 0x72, 0xC0, 0x52, 0xC0, 0x5A, 0xC0, 0x5A, 0xC0, 0x5C, 0xC0, + 0x4C, 0xC0, 0x4C, 0xC0, 0x4C, 0xC0, 0x48, 0xC0, 0xE1, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"M",45*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0xF0, 0x30, 0x40, 0x38, 0x40, + 0x38, 0x40, 0x2C, 0x40, 0x2C, 0x40, 0x26, 0x40, 0x26, 0x40, 0x22, 0x40, 0x23, 0x40, 0x21, 0x40, + 0x21, 0xC0, 0x21, 0xC0, 0x20, 0xC0, 0x20, 0xC0, 0xF8, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"N",46*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x19, 0x80, 0x30, 0xC0, + 0x30, 0x40, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x30, 0x40, 0x30, 0xC0, 0x19, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"O",47*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x60, 0xC0, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0xC0, 0x7F, 0x00, 0x60, 0x00, 0x60, 0x00, + 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"P",48*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x19, 0x80, 0x30, 0xC0, + 0x30, 0x40, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x6E, 0x60, 0x32, 0x40, 0x33, 0xC0, 0x19, 0x80, 0x0F, 0x00, 0x01, 0xE0, 0x00, 0xC0, 0x00, 0x00, /*"Q",49*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x60, 0xC0, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xC0, 0x7F, 0x00, 0x62, 0x00, 0x63, 0x00, 0x61, 0x00, + 0x61, 0x80, 0x60, 0xC0, 0x60, 0xC0, 0x60, 0x60, 0xF0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"R",50*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x40, 0x30, 0xC0, 0x60, 0x40, + 0x60, 0x40, 0x60, 0x00, 0x70, 0x00, 0x3C, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xC0, 0x00, 0xE0, + 0x40, 0x60, 0x40, 0x60, 0x60, 0x60, 0x30, 0xC0, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"S",51*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xE0, 0x46, 0x20, 0x86, 0x10, + 0x86, 0x10, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"T",52*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xE0, 0x30, 0x40, 0x30, 0x40, + 0x30, 0x40, 0x30, 0x40, 0x30, 0x40, 0x30, 0x40, 0x30, 0x40, 0x30, 0x40, 0x30, 0x40, 0x30, 0x40, + 0x30, 0x40, 0x30, 0x40, 0x30, 0x40, 0x18, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"U",53*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xE0, 0x60, 0x40, 0x20, 0x80, + 0x20, 0x80, 0x30, 0x80, 0x30, 0x80, 0x11, 0x00, 0x11, 0x00, 0x19, 0x00, 0x19, 0x00, 0x0A, 0x00, + 0x0A, 0x00, 0x0E, 0x00, 0x0C, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"V",54*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x70, 0x46, 0x20, 0x42, 0x20, + 0x62, 0x20, 0x62, 0x20, 0x26, 0x40, 0x26, 0x40, 0x27, 0x40, 0x27, 0x40, 0x39, 0x40, 0x39, 0x80, + 0x19, 0x80, 0x19, 0x80, 0x11, 0x80, 0x11, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"W",55*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0xE0, 0x30, 0x80, 0x10, 0x80, + 0x18, 0x80, 0x09, 0x00, 0x0D, 0x00, 0x06, 0x00, 0x04, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0B, 0x00, + 0x09, 0x00, 0x11, 0x80, 0x10, 0x80, 0x20, 0xC0, 0x71, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"X",56*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x60, 0x20, 0x20, 0x40, + 0x30, 0x40, 0x10, 0x80, 0x18, 0x80, 0x1D, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Y",57*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x30, 0x40, 0x60, 0xC0, + 0x40, 0x80, 0x01, 0x80, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, + 0x18, 0x00, 0x10, 0x20, 0x30, 0x20, 0x20, 0x40, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Z",58*/ + + 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x07, 0xC0, 0x00, 0x00, /*"[",59*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x20, /*"\",60*/ + + 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x3E, 0x00, 0x00, 0x00, /*"]",61*/ + + 0x00, 0x00, 0x06, 0x00, 0x09, 0x00, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"^",62*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, /*"_",63*/ + + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"`",64*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x30, 0xC0, 0x30, 0xC0, 0x00, 0xC0, 0x07, 0xC0, 0x38, 0xC0, + 0x70, 0xC0, 0x60, 0xC0, 0x60, 0xC0, 0x71, 0xD0, 0x3E, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"a",65*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x70, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x33, 0x80, 0x3C, 0xC0, 0x38, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, + 0x30, 0x60, 0x30, 0x60, 0x30, 0x40, 0x38, 0xC0, 0x27, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"b",66*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x31, 0x80, 0x31, 0x80, 0x61, 0x80, 0x60, 0x00, 0x60, 0x00, + 0x60, 0x00, 0x60, 0x40, 0x30, 0x40, 0x30, 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"c",67*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0xC0, 0x00, 0xC0, 0x00, 0xC0, + 0x00, 0xC0, 0x00, 0xC0, 0x1E, 0xC0, 0x31, 0xC0, 0x30, 0xC0, 0x60, 0xC0, 0x60, 0xC0, 0x60, 0xC0, + 0x60, 0xC0, 0x60, 0xC0, 0x20, 0xC0, 0x31, 0xE0, 0x1E, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"d",68*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x18, 0xC0, 0x10, 0x40, 0x30, 0x60, 0x30, 0x60, 0x3F, 0xE0, + 0x30, 0x00, 0x30, 0x00, 0x18, 0x20, 0x18, 0x40, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"e",69*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x06, 0x60, 0x0C, 0x60, + 0x0C, 0x00, 0x0C, 0x00, 0x7F, 0x80, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, + 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"f",70*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x1B, 0x60, 0x31, 0x80, 0x31, 0x80, 0x31, 0x80, 0x19, 0x80, + 0x1F, 0x00, 0x30, 0x00, 0x3F, 0x00, 0x33, 0xC0, 0x60, 0xC0, 0x60, 0xC0, 0x71, 0xC0, 0x1F, 0x00, /*"g",71*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x70, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x37, 0x80, 0x38, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, + 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x79, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"h",72*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x3E, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"i",73*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x0F, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x33, 0x00, 0x3E, 0x00, /*"j",74*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x70, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x31, 0xC0, 0x31, 0x00, 0x31, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3A, 0x00, + 0x31, 0x00, 0x31, 0x80, 0x30, 0x80, 0x30, 0xC0, 0x79, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"k",75*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3E, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"l",76*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xEC, 0xE0, 0x77, 0x60, 0x66, 0x60, 0x66, 0x60, 0x66, 0x60, 0x66, 0x60, + 0x66, 0x60, 0x66, 0x60, 0x66, 0x60, 0x66, 0x60, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"m",77*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x38, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, + 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x79, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"n",78*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x19, 0x80, 0x30, 0xC0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x30, 0xC0, 0x30, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"o",79*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x77, 0x80, 0x38, 0xC0, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, + 0x30, 0x60, 0x30, 0x60, 0x30, 0xC0, 0x38, 0xC0, 0x37, 0x80, 0x30, 0x00, 0x30, 0x00, 0x7C, 0x00, /*"p",80*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x40, 0x31, 0xC0, 0x30, 0xC0, 0x60, 0xC0, 0x60, 0xC0, 0x60, 0xC0, + 0x60, 0xC0, 0x60, 0xC0, 0x20, 0xC0, 0x31, 0xC0, 0x1E, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x03, 0xE0, /*"q",81*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF9, 0xC0, 0x1A, 0x60, 0x1C, 0x60, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"r",82*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x38, 0xC0, 0x30, 0x40, 0x30, 0x40, 0x1C, 0x00, 0x0F, 0x00, + 0x03, 0xC0, 0x20, 0xC0, 0x20, 0xC0, 0x31, 0xC0, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"s",83*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x0C, 0x00, 0x7F, 0x80, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, + 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x40, 0x0C, 0x40, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"t",84*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x40, 0x71, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, + 0x30, 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x39, 0xE0, 0x1E, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"u",85*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0xE0, 0x30, 0x40, 0x10, 0x80, 0x10, 0x80, 0x18, 0x80, 0x09, 0x00, + 0x0D, 0x00, 0x0D, 0x00, 0x06, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"v",86*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xEF, 0x70, 0x46, 0x20, 0x62, 0x20, 0x26, 0x40, 0x26, 0x40, 0x37, 0x40, + 0x39, 0x80, 0x19, 0x80, 0x19, 0x80, 0x19, 0x80, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"w",87*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3D, 0xE0, 0x18, 0x80, 0x08, 0x80, 0x0D, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x07, 0x00, 0x09, 0x00, 0x10, 0x80, 0x10, 0xC0, 0x79, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"x",88*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x79, 0xE0, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x09, 0x00, 0x09, 0x00, + 0x0D, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x04, 0x00, 0x04, 0x00, 0x24, 0x00, 0x38, 0x00, /*"y",89*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x21, 0x80, 0x23, 0x00, 0x22, 0x00, 0x06, 0x00, 0x04, 0x00, + 0x0C, 0x00, 0x18, 0x40, 0x10, 0x40, 0x30, 0xC0, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"z",90*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00, /*"{",91*/ + + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, /*"|",92*/ + + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x30, 0x00, 0x00, 0x00, /*"}",93*/ + + 0x00, 0x00, 0x38, 0x00, 0x44, 0x20, 0x43, 0x20, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"~",94*/ +}; + +const esp_painter_basic_font_t esp_painter_basic_font_24 = { + .bitmap = bitmap, + .width = 12, + .height = 24, +}; + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_28.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_28.c new file mode 100644 index 0000000..96976ce --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_28.c @@ -0,0 +1,496 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "sdkconfig.h" + +#include "esp_painter_font.h" + +#if CONFIG_ESP_PAINTER_BASIC_FONT_28 + +static const uint8_t bitmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*" ",0*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, + 0x07, 0x00, 0x07, 0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"!",1*/ + + 0x00, 0x00, 0x00, 0x00, 0x06, 0x60, 0x0E, 0xE0, 0x0C, 0xC0, 0x19, 0x80, 0x19, 0x80, 0x33, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*""",2*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, + 0x08, 0x20, 0x7F, 0xF8, 0x7F, 0xF8, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, 0x10, 0x40, 0x10, 0x40, + 0x10, 0x40, 0x7F, 0xF8, 0x7F, 0xF8, 0x10, 0x40, 0x10, 0x40, 0x10, 0x40, 0x10, 0x40, 0x10, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"#",3*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x07, 0xC0, 0x0D, 0x60, 0x19, 0x30, + 0x19, 0x30, 0x19, 0x70, 0x19, 0x00, 0x0D, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x01, 0xC0, 0x01, 0xE0, + 0x01, 0x60, 0x01, 0x30, 0x31, 0x30, 0x39, 0x30, 0x31, 0x30, 0x31, 0x30, 0x19, 0x60, 0x07, 0x80, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, /*"$",4*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x20, 0x48, 0x20, 0x84, 0x40, + 0x84, 0x40, 0x84, 0x80, 0x84, 0x80, 0x85, 0x00, 0x85, 0x00, 0x4A, 0x00, 0x3A, 0x60, 0x02, 0x90, + 0x05, 0x08, 0x05, 0x08, 0x09, 0x08, 0x09, 0x08, 0x11, 0x08, 0x11, 0x08, 0x20, 0x90, 0x20, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"%",5*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x19, 0x80, 0x31, 0x80, + 0x31, 0x80, 0x31, 0x80, 0x31, 0x80, 0x33, 0x00, 0x3E, 0x00, 0x18, 0xF8, 0x38, 0x20, 0x4C, 0x20, + 0x4C, 0x20, 0xC6, 0x20, 0xC6, 0x40, 0xC3, 0x40, 0xC3, 0x80, 0x61, 0x84, 0x71, 0xCC, 0x3E, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"&",6*/ + + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x38, 0x00, 0x38, 0x00, 0x18, 0x00, 0x18, 0x00, 0x10, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"'",7*/ + + 0x00, 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, + 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, /*"(",8*/ + + 0x00, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, + 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, /*")",9*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, + 0x03, 0x80, 0x01, 0x80, 0x61, 0x0C, 0x79, 0x3C, 0x1D, 0x70, 0x03, 0x80, 0x03, 0x80, 0x1D, 0x70, + 0x79, 0x3C, 0x61, 0x0C, 0x03, 0x00, 0x03, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"*",10*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x3F, 0xF8, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"+",11*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x38, 0x00, 0x38, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, /*",",12*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"-",13*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*".",14*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x18, 0x00, 0x10, 0x00, 0x30, 0x00, 0x20, 0x00, 0x60, + 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x18, 0x00, 0x10, 0x00, 0x30, 0x00, 0x20, 0x00, + 0x60, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, /*"/",15*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0C, 0x60, 0x18, 0x20, + 0x30, 0x30, 0x30, 0x30, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, + 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x30, 0x30, 0x30, 0x30, 0x18, 0x20, 0x0C, 0x40, 0x07, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"0",16*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x1F, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x1F, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"1",17*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x30, 0xE0, 0x40, 0x70, + 0x40, 0x30, 0x60, 0x30, 0x60, 0x30, 0x00, 0x30, 0x00, 0x60, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, + 0x03, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x18, 0x10, 0x30, 0x10, 0x60, 0x30, 0x7F, 0xF0, 0x7F, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"2",18*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x10, 0xC0, 0x30, 0x60, + 0x30, 0x60, 0x30, 0x60, 0x00, 0x60, 0x00, 0x40, 0x00, 0xC0, 0x03, 0x80, 0x00, 0xC0, 0x00, 0x60, + 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0x10, 0xC0, 0x0F, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"3",19*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0xC0, 0x01, 0xC0, + 0x03, 0xC0, 0x02, 0xC0, 0x04, 0xC0, 0x04, 0xC0, 0x08, 0xC0, 0x10, 0xC0, 0x10, 0xC0, 0x20, 0xC0, + 0x40, 0xC0, 0x7F, 0xF8, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x07, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"4",20*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x1F, 0xF0, 0x10, 0x00, + 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x17, 0xC0, 0x18, 0x60, 0x10, 0x60, 0x00, 0x30, + 0x00, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x60, 0x10, 0xE0, 0x0F, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"5",21*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x0C, 0x30, 0x18, 0x30, + 0x30, 0x30, 0x30, 0x00, 0x20, 0x00, 0x60, 0x00, 0x67, 0xC0, 0x68, 0x70, 0x70, 0x30, 0x60, 0x18, + 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x20, 0x18, 0x30, 0x18, 0x30, 0x30, 0x1C, 0x60, 0x07, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"6",22*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x1F, 0xF0, 0x30, 0x20, + 0x20, 0x20, 0x20, 0x40, 0x00, 0x40, 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"7",23*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x30, 0x60, 0x30, 0x70, + 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x30, 0x20, 0x38, 0x60, 0x0F, 0x80, 0x0B, 0x80, 0x31, 0xC0, + 0x30, 0x60, 0x60, 0x70, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x30, 0x60, 0x18, 0x60, 0x0F, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"8",24*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x18, 0xC0, 0x30, 0x60, + 0x60, 0x20, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x70, 0x30, 0x70, 0x38, 0xB0, + 0x1F, 0x30, 0x00, 0x30, 0x00, 0x60, 0x00, 0x60, 0x30, 0x60, 0x30, 0xC0, 0x31, 0x80, 0x1F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"9",25*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*":",26*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, + 0x00, 0x80, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, /*";",27*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, + 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"<",28*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"=",29*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x20, + 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*">",30*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x10, 0xE0, 0x20, 0x60, + 0x40, 0x30, 0x40, 0x30, 0x70, 0x30, 0x70, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"?",31*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x0C, 0x60, 0x18, 0x10, + 0x31, 0xB0, 0x33, 0x68, 0x22, 0x68, 0x66, 0x68, 0x66, 0x48, 0x6C, 0x48, 0x6C, 0x48, 0x6C, 0x48, + 0x6C, 0x48, 0x6C, 0xD0, 0x6D, 0x50, 0x36, 0x60, 0x30, 0x08, 0x18, 0x10, 0x0C, 0x20, 0x07, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"@",32*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x07, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x09, 0x80, 0x09, 0x80, 0x08, 0x80, 0x08, 0x80, 0x10, 0xC0, 0x10, 0xC0, + 0x1F, 0xC0, 0x10, 0x40, 0x20, 0x60, 0x20, 0x60, 0x20, 0x60, 0x20, 0x20, 0x60, 0x30, 0xF0, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"A",33*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x30, 0x60, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0x30, 0xC0, 0x3F, 0x80, 0x30, 0x60, 0x30, 0x30, + 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x30, 0x30, 0x70, 0xFF, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"B",34*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x0C, 0x30, 0x18, 0x18, + 0x30, 0x08, 0x30, 0x08, 0x20, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, + 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x30, 0x08, 0x30, 0x08, 0x18, 0x10, 0x1C, 0x20, 0x07, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"C",35*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x30, 0xC0, 0x30, 0x60, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, + 0x30, 0x18, 0x30, 0x18, 0x30, 0x10, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0x30, 0xC0, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"D",36*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x30, 0x30, 0x30, 0x18, + 0x30, 0x08, 0x30, 0x00, 0x30, 0x40, 0x30, 0x40, 0x30, 0xC0, 0x3F, 0xC0, 0x30, 0xC0, 0x30, 0x40, + 0x30, 0x40, 0x30, 0x00, 0x30, 0x00, 0x30, 0x08, 0x30, 0x08, 0x30, 0x10, 0x30, 0x30, 0xFF, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"E",37*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x30, 0x30, 0x30, 0x10, + 0x30, 0x08, 0x30, 0x08, 0x30, 0x40, 0x30, 0x40, 0x30, 0x40, 0x3F, 0xC0, 0x30, 0xC0, 0x30, 0x40, + 0x30, 0x40, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0xFC, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"F",38*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0C, 0x60, 0x18, 0x30, + 0x30, 0x10, 0x30, 0x10, 0x20, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, + 0x60, 0xFC, 0x60, 0x30, 0x60, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x30, 0x0C, 0x70, 0x07, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"G",39*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3F, 0xF0, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"H",40*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x3F, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"I",41*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x00, 0xC0, 0x00, 0xC0, + 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, + 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, + 0x60, 0xC0, 0x61, 0x80, 0x63, 0x80, 0x3E, 0x00, /*"J",42*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xF0, 0x30, 0x40, 0x30, 0x80, + 0x30, 0x80, 0x31, 0x00, 0x32, 0x00, 0x32, 0x00, 0x36, 0x00, 0x36, 0x00, 0x3B, 0x00, 0x33, 0x00, + 0x31, 0x80, 0x31, 0x80, 0x30, 0xC0, 0x30, 0xC0, 0x30, 0x60, 0x30, 0x60, 0x30, 0x70, 0xFC, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"K",43*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x08, 0x18, 0x08, 0x18, 0x18, 0x18, 0x30, 0x7F, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"L",44*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x38, 0x30, 0x70, 0x30, 0x70, + 0x38, 0x70, 0x38, 0x70, 0x38, 0x70, 0x28, 0xB0, 0x28, 0xB0, 0x2C, 0xB0, 0x2C, 0xB0, 0x2D, 0x30, + 0x25, 0x30, 0x25, 0x30, 0x27, 0x30, 0x27, 0x30, 0x26, 0x30, 0x22, 0x30, 0x22, 0x30, 0x72, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"M",45*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7C, 0x38, 0x10, 0x38, 0x10, + 0x3C, 0x10, 0x2C, 0x10, 0x2E, 0x10, 0x26, 0x10, 0x26, 0x10, 0x23, 0x10, 0x23, 0x10, 0x21, 0x90, + 0x21, 0x90, 0x20, 0xD0, 0x20, 0xD0, 0x20, 0x70, 0x20, 0x70, 0x20, 0x30, 0x20, 0x30, 0xF8, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"N",46*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x08, 0x40, 0x10, 0x20, + 0x30, 0x30, 0x30, 0x30, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, + 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x30, 0x10, 0x30, 0x30, 0x10, 0x20, 0x08, 0x40, 0x07, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"O",47*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x30, 0x70, 0x30, 0x30, + 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x30, 0x30, 0x70, 0x3F, 0xC0, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0xFC, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"P",48*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x18, 0x60, 0x10, 0x20, + 0x30, 0x30, 0x30, 0x10, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, + 0x60, 0x18, 0x60, 0x18, 0x67, 0x18, 0x29, 0x10, 0x31, 0xB0, 0x18, 0xA0, 0x18, 0xE0, 0x07, 0xC0, + 0x00, 0x78, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, /*"Q",49*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x18, 0x60, 0x18, 0x30, + 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x60, 0x18, 0x60, 0x1F, 0xC0, 0x19, 0x80, + 0x18, 0x80, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0x40, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x7E, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"R",50*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x20, 0x30, 0xE0, 0x20, 0x60, + 0x60, 0x20, 0x60, 0x20, 0x60, 0x00, 0x70, 0x00, 0x3C, 0x00, 0x1F, 0x00, 0x07, 0xC0, 0x01, 0xE0, + 0x00, 0x60, 0x00, 0x70, 0x40, 0x30, 0x40, 0x30, 0x60, 0x30, 0x20, 0x60, 0x38, 0x60, 0x07, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"S",51*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x23, 0x10, 0x63, 0x08, + 0x43, 0x08, 0x43, 0x08, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x0F, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"T",52*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x7C, 0x30, 0x10, 0x30, 0x10, + 0x30, 0x10, 0x30, 0x10, 0x30, 0x10, 0x30, 0x10, 0x30, 0x10, 0x30, 0x10, 0x30, 0x10, 0x30, 0x10, + 0x30, 0x10, 0x30, 0x10, 0x30, 0x10, 0x30, 0x10, 0x30, 0x10, 0x30, 0x30, 0x18, 0x60, 0x07, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"U",53*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x78, 0x70, 0x30, 0x30, 0x20, + 0x30, 0x20, 0x30, 0x20, 0x10, 0x40, 0x18, 0x40, 0x18, 0x40, 0x18, 0x40, 0x08, 0x80, 0x0C, 0x80, + 0x0C, 0x80, 0x0D, 0x00, 0x05, 0x00, 0x07, 0x00, 0x07, 0x00, 0x06, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"V",54*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x9C, 0x63, 0x08, 0x63, 0x08, + 0x61, 0x10, 0x21, 0x10, 0x23, 0x10, 0x23, 0x90, 0x33, 0x90, 0x33, 0x90, 0x33, 0xA0, 0x34, 0xA0, + 0x14, 0xA0, 0x14, 0xA0, 0x14, 0xE0, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0x40, 0x08, 0x40, 0x08, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"W",55*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x78, 0x18, 0x20, 0x18, 0x20, + 0x08, 0x40, 0x0C, 0x40, 0x04, 0x80, 0x06, 0x80, 0x07, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x05, 0x80, 0x04, 0x80, 0x08, 0xC0, 0x08, 0xC0, 0x08, 0x60, 0x10, 0x60, 0x10, 0x30, 0x78, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"X",56*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x78, 0x30, 0x30, 0x18, 0x20, + 0x18, 0x20, 0x18, 0x40, 0x0C, 0x40, 0x0C, 0x40, 0x04, 0x80, 0x06, 0x80, 0x07, 0x80, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x0F, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Y",57*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x18, 0x10, 0x30, 0x30, + 0x20, 0x60, 0x00, 0x60, 0x00, 0xC0, 0x00, 0x80, 0x01, 0x80, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x18, 0x08, 0x18, 0x08, 0x30, 0x10, 0x20, 0x30, 0x7F, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Z",58*/ + + 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, /*"[",59*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x30, 0x00, 0x10, 0x00, 0x18, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x40, 0x00, 0x40, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x10, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, /*"\",60*/ + + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, /*"]",61*/ + + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x08, 0x80, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"^",62*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, /*"_",63*/ + + 0x00, 0x00, 0x3C, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"`",64*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x10, 0xE0, 0x30, 0x60, 0x30, 0x60, 0x00, 0x60, + 0x07, 0xE0, 0x18, 0x60, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x68, 0x31, 0xE8, 0x1E, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"a",65*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0xC0, 0x34, 0x60, 0x38, 0x60, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0x38, 0x60, 0x27, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"b",66*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0C, 0x60, 0x18, 0x60, 0x18, 0x60, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x10, 0x18, 0x20, 0x0C, 0x20, 0x07, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"c",67*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x30, 0x00, 0x30, + 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x07, 0xB0, 0x0C, 0x70, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x70, 0x18, 0xFC, 0x07, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"d",68*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x0C, 0x60, 0x18, 0x60, 0x10, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x3F, 0xF0, 0x30, 0x00, 0x30, 0x00, 0x30, 0x10, 0x18, 0x10, 0x0C, 0x20, 0x07, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"e",69*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x03, 0x18, 0x06, 0x18, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x3F, 0xE0, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x3F, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"f",70*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xB8, 0x0C, 0xD8, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, + 0x18, 0x60, 0x0C, 0xC0, 0x0F, 0x80, 0x18, 0x00, 0x18, 0x00, 0x0F, 0xC0, 0x19, 0xF0, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x18, 0x60, 0x0F, 0xC0, /*"g",71*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0xE0, 0x34, 0x70, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"h",72*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x07, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1F, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x1F, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"i",73*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x03, 0xE0, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, + 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, + 0x00, 0x60, 0x30, 0x40, 0x30, 0xC0, 0x1F, 0x00, /*"j",74*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x78, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0xF0, 0x18, 0x40, 0x18, 0x80, 0x18, 0x80, 0x19, 0x00, + 0x1B, 0x00, 0x1D, 0x80, 0x1C, 0x80, 0x18, 0xC0, 0x18, 0x60, 0x18, 0x60, 0x18, 0x30, 0x7E, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"k",75*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1F, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x3F, 0xF0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"l",76*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x70, 0x73, 0x98, 0x63, 0x18, 0x63, 0x18, 0x63, 0x18, + 0x63, 0x18, 0x63, 0x18, 0x63, 0x18, 0x63, 0x18, 0x63, 0x18, 0x63, 0x18, 0x63, 0x18, 0xF7, 0xBC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"m",77*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xC0, 0xF4, 0x60, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFC, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"n",78*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x18, 0x60, 0x30, 0x30, 0x30, 0x30, 0x60, 0x18, + 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x20, 0x10, 0x30, 0x30, 0x18, 0x60, 0x07, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"o",79*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xC0, 0xF4, 0x70, 0x38, 0x30, 0x30, 0x18, 0x30, 0x18, + 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x30, 0x38, 0x60, 0x37, 0xC0, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0xFC, 0x00, /*"p",80*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x20, 0x18, 0xE0, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x30, 0x60, 0x30, 0xE0, 0x0F, 0x60, + 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x01, 0xF8, /*"q",81*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x70, 0x7C, 0x98, 0x0D, 0x18, 0x0E, 0x00, 0x0C, 0x00, + 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x7F, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"r",82*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x90, 0x18, 0x70, 0x30, 0x30, 0x30, 0x10, 0x38, 0x00, + 0x1E, 0x00, 0x07, 0xC0, 0x01, 0xE0, 0x20, 0x70, 0x20, 0x30, 0x30, 0x30, 0x38, 0x60, 0x27, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"s",83*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x3F, 0xE0, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x10, 0x06, 0x10, 0x03, 0x20, 0x03, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"t",84*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x20, 0xF1, 0xE0, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, + 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x30, 0x60, 0x38, 0xF8, 0x1F, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"u",85*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x78, 0x10, 0x30, 0x18, 0x20, 0x18, 0x20, 0x08, 0x40, + 0x0C, 0x40, 0x0C, 0x40, 0x04, 0x80, 0x06, 0x80, 0x06, 0x80, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"v",86*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0xBC, 0x63, 0x08, 0x23, 0x10, 0x23, 0x10, 0x33, 0x10, + 0x33, 0x90, 0x37, 0xA0, 0x14, 0xA0, 0x1C, 0xA0, 0x1C, 0xC0, 0x18, 0xC0, 0x08, 0x40, 0x08, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"w",87*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0xF8, 0x0C, 0x20, 0x0C, 0x40, 0x06, 0x40, 0x06, 0x80, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x80, 0x04, 0xC0, 0x08, 0xC0, 0x08, 0x60, 0x10, 0x30, 0x78, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"x",88*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xF8, 0x10, 0x20, 0x18, 0x20, 0x18, 0x40, 0x08, 0x40, + 0x0C, 0x40, 0x0C, 0x80, 0x04, 0x80, 0x06, 0x80, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x34, 0x00, 0x38, 0x00, /*"y",89*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x30, 0x60, 0x20, 0xC0, 0x21, 0x80, 0x01, 0x80, + 0x03, 0x00, 0x02, 0x00, 0x06, 0x00, 0x0C, 0x10, 0x08, 0x10, 0x18, 0x10, 0x30, 0x20, 0x3F, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"z",90*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x40, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, + 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, + 0x00, 0x40, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, /*"{",91*/ + + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, /*"|",92*/ + + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, /*"}",93*/ + + 0x1C, 0x00, 0x26, 0x08, 0x43, 0x08, 0x41, 0x90, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"~",94*/ +}; + +const esp_painter_basic_font_t esp_painter_basic_font_28 = { + .bitmap = bitmap, + .width = 14, + .height = 28, +}; + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_32.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_32.c new file mode 100644 index 0000000..b5e3c03 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_32.c @@ -0,0 +1,496 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "sdkconfig.h" + +#include "esp_painter_font.h" + +#if CONFIG_ESP_PAINTER_BASIC_FONT_32 + +static const uint8_t bitmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*" ",0*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x03, 0xC0, + 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x01, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0xC0, 0x03, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"!",1*/ + + 0x00, 0x00, 0x00, 0x00, 0x03, 0x18, 0x07, 0x38, 0x07, 0x38, 0x0E, 0x70, 0x0C, 0x60, 0x18, 0xC0, + 0x31, 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*""",2*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x04, 0x08, + 0x04, 0x08, 0x04, 0x08, 0x04, 0x08, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x18, 0x30, + 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"#",3*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0xC0, 0x0D, 0x30, + 0x09, 0x18, 0x19, 0x18, 0x19, 0x38, 0x19, 0x38, 0x1D, 0x00, 0x0D, 0x00, 0x0F, 0x00, 0x07, 0x00, + 0x03, 0xC0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0x30, 0x01, 0x38, 0x01, 0x18, 0x39, 0x18, 0x39, 0x18, + 0x31, 0x18, 0x31, 0x30, 0x19, 0x60, 0x07, 0xC0, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, /*"$",4*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x10, 0x6C, 0x10, + 0x44, 0x20, 0xC6, 0x20, 0xC6, 0x40, 0xC6, 0x40, 0xC6, 0x40, 0xC6, 0x80, 0xC6, 0x80, 0x44, 0x80, + 0x6D, 0x38, 0x39, 0x6C, 0x02, 0x44, 0x02, 0xC6, 0x02, 0xC6, 0x04, 0xC6, 0x04, 0xC6, 0x08, 0xC6, + 0x08, 0xC6, 0x08, 0x44, 0x10, 0x6C, 0x10, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"%",5*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x19, 0x00, + 0x31, 0x80, 0x31, 0x80, 0x31, 0x80, 0x31, 0x80, 0x31, 0x00, 0x33, 0x00, 0x3A, 0x00, 0x1C, 0x00, + 0x38, 0x7C, 0x3C, 0x10, 0x4C, 0x10, 0xCE, 0x10, 0xC6, 0x20, 0xC7, 0x20, 0xC3, 0x20, 0xC1, 0xC0, + 0xC1, 0xC2, 0x60, 0xE6, 0x31, 0x7C, 0x1E, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"&",6*/ + + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x30, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"'",7*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x18, 0x00, 0x30, 0x00, 0x20, + 0x00, 0x60, 0x00, 0x40, 0x00, 0xC0, 0x00, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x40, + 0x00, 0x60, 0x00, 0x20, 0x00, 0x30, 0x00, 0x18, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, /*"(",8*/ + + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x00, 0x00, /*")",9*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC0, 0x01, 0xC0, 0x01, 0xC0, 0x30, 0xC6, 0x38, 0x8E, 0x1C, 0x9C, 0x06, 0xB0, 0x01, 0xC0, + 0x01, 0xC0, 0x06, 0xB0, 0x1C, 0x9C, 0x38, 0x8E, 0x31, 0x86, 0x01, 0xC0, 0x01, 0xC0, 0x01, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"*",10*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, + 0x3F, 0xFE, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"+",11*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x30, 0x00, 0x60, 0x00, /*",",12*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"-",13*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x3C, 0x00, 0x3C, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*".",14*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x08, + 0x00, 0x18, 0x00, 0x10, 0x00, 0x30, 0x00, 0x20, 0x00, 0x60, 0x00, 0x40, 0x00, 0xC0, 0x00, 0x80, + 0x01, 0x80, 0x01, 0x00, 0x03, 0x00, 0x02, 0x00, 0x06, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x08, 0x00, + 0x18, 0x00, 0x10, 0x00, 0x30, 0x00, 0x20, 0x00, 0x60, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, /*"/",15*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x06, 0x20, + 0x0C, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x08, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, + 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x18, 0x08, 0x18, 0x18, + 0x18, 0x18, 0x0C, 0x30, 0x06, 0x20, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"0",16*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x80, + 0x1F, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"1",17*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x08, 0x38, + 0x10, 0x18, 0x20, 0x0C, 0x20, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x18, + 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x02, 0x00, 0x04, 0x04, 0x08, 0x04, + 0x10, 0x04, 0x20, 0x0C, 0x3F, 0xF8, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"2",18*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x18, 0x60, + 0x30, 0x30, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, + 0x03, 0xC0, 0x00, 0x70, 0x00, 0x18, 0x00, 0x08, 0x00, 0x0C, 0x00, 0x0C, 0x30, 0x0C, 0x30, 0x0C, + 0x30, 0x08, 0x30, 0x18, 0x18, 0x30, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"3",19*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, + 0x00, 0xE0, 0x00, 0xE0, 0x01, 0x60, 0x01, 0x60, 0x02, 0x60, 0x04, 0x60, 0x04, 0x60, 0x08, 0x60, + 0x08, 0x60, 0x10, 0x60, 0x30, 0x60, 0x20, 0x60, 0x40, 0x60, 0x7F, 0xFC, 0x00, 0x60, 0x00, 0x60, + 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"4",20*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x0F, 0xFC, + 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x13, 0xE0, 0x14, 0x30, + 0x18, 0x18, 0x10, 0x08, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x30, 0x0C, 0x30, 0x0C, + 0x20, 0x18, 0x20, 0x18, 0x18, 0x30, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"5",21*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, 0x06, 0x18, + 0x0C, 0x18, 0x08, 0x18, 0x18, 0x00, 0x10, 0x00, 0x10, 0x00, 0x30, 0x00, 0x33, 0xE0, 0x36, 0x30, + 0x38, 0x18, 0x38, 0x08, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x18, 0x0C, + 0x18, 0x08, 0x0C, 0x18, 0x0E, 0x30, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"6",22*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x1F, 0xFC, + 0x10, 0x08, 0x30, 0x10, 0x20, 0x10, 0x20, 0x20, 0x00, 0x20, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, + 0x00, 0x80, 0x00, 0x80, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"7",23*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x0C, 0x30, + 0x18, 0x18, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x38, 0x0C, 0x38, 0x08, 0x1E, 0x18, 0x0F, 0x20, + 0x07, 0xC0, 0x18, 0xF0, 0x30, 0x78, 0x30, 0x38, 0x60, 0x1C, 0x60, 0x0C, 0x60, 0x0C, 0x60, 0x0C, + 0x60, 0x0C, 0x30, 0x18, 0x18, 0x30, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"8",24*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x18, 0x20, + 0x30, 0x10, 0x30, 0x18, 0x60, 0x08, 0x60, 0x0C, 0x60, 0x0C, 0x60, 0x0C, 0x60, 0x0C, 0x60, 0x0C, + 0x70, 0x1C, 0x30, 0x2C, 0x18, 0x6C, 0x0F, 0x8C, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x18, 0x00, 0x10, + 0x30, 0x30, 0x30, 0x60, 0x30, 0xC0, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"9",25*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xC0, 0x03, 0xC0, + 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0xC0, 0x03, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*":",26*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, + 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x01, 0x80, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, /*";",27*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, + 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, + 0x10, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, + 0x00, 0x20, 0x00, 0x10, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"<",28*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"=",29*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, + 0x08, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x10, + 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x01, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*">",30*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x0C, 0x18, + 0x18, 0x0C, 0x10, 0x06, 0x30, 0x06, 0x38, 0x06, 0x38, 0x06, 0x38, 0x06, 0x00, 0x0C, 0x00, 0x18, + 0x00, 0x70, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x80, 0x03, 0xC0, 0x03, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"?",31*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x06, 0x10, + 0x0C, 0x08, 0x18, 0x04, 0x30, 0xD4, 0x31, 0xB2, 0x21, 0x32, 0x63, 0x32, 0x63, 0x22, 0x66, 0x22, + 0x66, 0x22, 0x66, 0x22, 0x66, 0x62, 0x66, 0x64, 0x66, 0x64, 0x26, 0xE8, 0x33, 0x30, 0x30, 0x02, + 0x10, 0x04, 0x18, 0x0C, 0x0C, 0x18, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"@",32*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, + 0x03, 0x80, 0x03, 0x80, 0x04, 0xC0, 0x04, 0xC0, 0x04, 0xC0, 0x04, 0xC0, 0x0C, 0x40, 0x08, 0x60, + 0x08, 0x60, 0x08, 0x60, 0x18, 0x20, 0x1F, 0xF0, 0x10, 0x30, 0x10, 0x30, 0x10, 0x30, 0x20, 0x18, + 0x20, 0x18, 0x20, 0x18, 0x60, 0x1C, 0xF8, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"A",33*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xE0, 0x18, 0x38, + 0x18, 0x18, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x18, 0x18, 0x30, + 0x1F, 0xE0, 0x18, 0x18, 0x18, 0x0C, 0x18, 0x04, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, + 0x18, 0x06, 0x18, 0x0C, 0x18, 0x18, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"B",34*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x06, 0x1C, + 0x08, 0x0C, 0x18, 0x06, 0x30, 0x02, 0x30, 0x02, 0x30, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, + 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x30, 0x02, 0x30, 0x02, + 0x10, 0x04, 0x18, 0x08, 0x0C, 0x10, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"C",35*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x18, 0x70, + 0x18, 0x18, 0x18, 0x08, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, + 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x04, 0x18, 0x0C, 0x18, 0x0C, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x60, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"D",36*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFC, 0x18, 0x0C, + 0x18, 0x04, 0x18, 0x02, 0x18, 0x02, 0x18, 0x00, 0x18, 0x00, 0x18, 0x10, 0x18, 0x10, 0x18, 0x30, + 0x1F, 0xF0, 0x18, 0x30, 0x18, 0x10, 0x18, 0x10, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x02, + 0x18, 0x02, 0x18, 0x04, 0x18, 0x0C, 0x7F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"E",37*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFC, 0x18, 0x1C, + 0x18, 0x04, 0x18, 0x02, 0x18, 0x02, 0x18, 0x00, 0x18, 0x00, 0x18, 0x10, 0x18, 0x10, 0x18, 0x30, + 0x1F, 0xF0, 0x18, 0x30, 0x18, 0x10, 0x18, 0x10, 0x18, 0x10, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"F",38*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x0C, 0x30, + 0x08, 0x10, 0x18, 0x18, 0x30, 0x08, 0x30, 0x08, 0x20, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, + 0x60, 0x00, 0x60, 0x00, 0x60, 0x7E, 0x60, 0x18, 0x60, 0x18, 0x20, 0x18, 0x30, 0x18, 0x30, 0x18, + 0x10, 0x18, 0x18, 0x18, 0x0C, 0x20, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"G",39*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x3F, 0x30, 0x0C, + 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, + 0x3F, 0xFC, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, + 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0xFC, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"H",40*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"I",41*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x60, + 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, + 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, + 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x70, 0x60, 0x70, 0xC0, 0x71, 0x80, 0x3F, 0x00, /*"J",42*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7C, 0x18, 0x30, + 0x18, 0x20, 0x18, 0x60, 0x18, 0x40, 0x18, 0x80, 0x18, 0x80, 0x19, 0x00, 0x19, 0x00, 0x1B, 0x00, + 0x1D, 0x80, 0x1D, 0x80, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0x60, 0x18, 0x60, 0x18, 0x30, 0x18, 0x30, + 0x18, 0x30, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"K",43*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x02, + 0x18, 0x02, 0x18, 0x04, 0x18, 0x0C, 0x7F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"L",44*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x38, 0x1C, + 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x1C, 0x38, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x2C, 0x4C, + 0x2C, 0x4C, 0x26, 0x4C, 0x26, 0x4C, 0x26, 0x4C, 0x26, 0x8C, 0x22, 0x8C, 0x23, 0x8C, 0x23, 0x8C, + 0x23, 0x0C, 0x23, 0x0C, 0x21, 0x0C, 0xF1, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"M",45*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x1F, 0x38, 0x04, + 0x38, 0x04, 0x2C, 0x04, 0x2C, 0x04, 0x26, 0x04, 0x26, 0x04, 0x23, 0x04, 0x23, 0x04, 0x21, 0x84, + 0x21, 0x84, 0x20, 0xC4, 0x20, 0xC4, 0x20, 0x64, 0x20, 0x64, 0x20, 0x34, 0x20, 0x34, 0x20, 0x1C, + 0x20, 0x1C, 0x20, 0x0C, 0x20, 0x0C, 0xF8, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"N",46*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x0C, 0x30, + 0x18, 0x18, 0x10, 0x08, 0x30, 0x0C, 0x30, 0x0C, 0x60, 0x04, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, + 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x20, 0x06, 0x30, 0x0C, 0x30, 0x0C, + 0x10, 0x08, 0x18, 0x18, 0x0C, 0x30, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"O",47*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF0, 0x18, 0x18, + 0x18, 0x0C, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x0C, + 0x18, 0x18, 0x1F, 0xE0, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"P",48*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x0C, 0x30, + 0x18, 0x18, 0x10, 0x08, 0x30, 0x0C, 0x30, 0x0C, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, + 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x27, 0x84, 0x38, 0xCC, + 0x38, 0x6C, 0x18, 0x78, 0x0C, 0x70, 0x03, 0xE0, 0x00, 0x32, 0x00, 0x3C, 0x00, 0x1C, 0x00, 0x00, /*"Q",49*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xE0, 0x18, 0x38, + 0x18, 0x18, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x18, 0x18, 0x30, + 0x1F, 0xE0, 0x18, 0xC0, 0x18, 0xC0, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x30, 0x18, 0x30, + 0x18, 0x30, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"R",50*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC8, 0x18, 0x78, + 0x30, 0x18, 0x60, 0x18, 0x60, 0x08, 0x60, 0x08, 0x60, 0x00, 0x70, 0x00, 0x3C, 0x00, 0x1F, 0x00, + 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x78, 0x00, 0x18, 0x00, 0x1C, 0x40, 0x0C, 0x40, 0x0C, 0x60, 0x0C, + 0x20, 0x0C, 0x30, 0x18, 0x38, 0x30, 0x27, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"S",51*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFC, 0x31, 0x84, + 0x21, 0x86, 0x41, 0x82, 0x41, 0x82, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"T",52*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x3E, 0x30, 0x08, + 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, + 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, 0x30, 0x08, + 0x30, 0x08, 0x18, 0x10, 0x1C, 0x20, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"U",53*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x1E, 0x18, 0x0C, + 0x18, 0x08, 0x18, 0x08, 0x18, 0x08, 0x0C, 0x10, 0x0C, 0x10, 0x0C, 0x10, 0x0C, 0x10, 0x0C, 0x20, + 0x06, 0x20, 0x06, 0x20, 0x06, 0x20, 0x06, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x40, 0x03, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"V",54*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xCF, 0x61, 0x86, + 0x61, 0x84, 0x21, 0x84, 0x20, 0x84, 0x30, 0xC4, 0x31, 0xC4, 0x31, 0xC4, 0x31, 0xC8, 0x31, 0xC8, + 0x11, 0xC8, 0x12, 0x48, 0x1A, 0x68, 0x1A, 0x68, 0x1A, 0x70, 0x1C, 0x70, 0x0C, 0x70, 0x0C, 0x70, + 0x0C, 0x30, 0x0C, 0x20, 0x08, 0x20, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"W",55*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x3E, 0x18, 0x08, + 0x18, 0x10, 0x0C, 0x10, 0x0C, 0x20, 0x06, 0x20, 0x06, 0x40, 0x03, 0x40, 0x03, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x02, 0xC0, 0x02, 0x60, 0x04, 0x60, 0x04, 0x70, 0x08, 0x30, + 0x08, 0x30, 0x18, 0x18, 0x10, 0x1C, 0x7C, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"X",56*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x3E, 0x38, 0x08, + 0x18, 0x08, 0x18, 0x10, 0x0C, 0x10, 0x0C, 0x10, 0x0C, 0x20, 0x06, 0x20, 0x06, 0x20, 0x03, 0x40, + 0x03, 0x40, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Y",57*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFE, 0x1C, 0x0C, + 0x18, 0x0C, 0x30, 0x18, 0x20, 0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0x60, 0x00, 0xC0, 0x00, 0xC0, + 0x01, 0x80, 0x01, 0x80, 0x03, 0x00, 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x18, 0x02, + 0x18, 0x06, 0x30, 0x04, 0x30, 0x1C, 0x7F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Z",58*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFC, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, /*"[",59*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x08, 0x00, + 0x0C, 0x00, 0x04, 0x00, 0x06, 0x00, 0x06, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x80, + 0x01, 0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, 0x40, 0x00, 0x60, 0x00, 0x60, 0x00, 0x30, 0x00, 0x30, + 0x00, 0x30, 0x00, 0x18, 0x00, 0x18, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x00, /*"\",60*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, + 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, + 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, + 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x3F, 0xC0, 0x00, 0x00, 0x00, 0x00, /*"]",61*/ + + 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x03, 0xE0, 0x06, 0x20, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"^",62*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, /*"_",63*/ + + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"`",64*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x18, 0x30, 0x30, 0x18, + 0x30, 0x18, 0x30, 0x18, 0x00, 0x38, 0x07, 0xD8, 0x1C, 0x18, 0x30, 0x18, 0x60, 0x18, 0x60, 0x18, + 0x60, 0x18, 0x60, 0x19, 0x30, 0x79, 0x1F, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"a",65*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x78, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x19, 0xE0, 0x1A, 0x38, 0x1C, 0x18, + 0x1C, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, + 0x18, 0x08, 0x1C, 0x18, 0x1C, 0x30, 0x13, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"b",66*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x0E, 0x10, 0x0C, 0x18, + 0x18, 0x18, 0x30, 0x18, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x04, + 0x18, 0x04, 0x18, 0x08, 0x0C, 0x10, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"c",67*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x78, 0x00, 0x18, + 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x07, 0xD8, 0x0C, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, + 0x10, 0x18, 0x18, 0x38, 0x0C, 0x5E, 0x07, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"d",68*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x0C, 0x30, 0x08, 0x18, + 0x18, 0x08, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x3F, 0xFC, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x18, 0x04, 0x18, 0x08, 0x0E, 0x18, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"e",69*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x01, 0x86, + 0x01, 0x06, 0x03, 0x06, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x3F, 0xF8, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"f",70*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xEE, 0x0C, 0x36, 0x08, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x08, 0x18, 0x0C, 0x30, 0x0F, 0xE0, 0x18, 0x00, 0x18, 0x00, + 0x1F, 0xC0, 0x0F, 0xF8, 0x18, 0x1C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x18, 0x18, 0x07, 0xE0, /*"g",71*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x78, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x19, 0xE0, 0x1A, 0x30, 0x1C, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"h",72*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0xC0, + 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"i",73*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x78, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0xF0, 0x00, 0x30, 0x00, 0x30, + 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, + 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x18, 0x60, 0x18, 0x40, 0x0F, 0x80, /*"j",74*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x78, 0x00, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x7C, 0x18, 0x30, 0x18, 0x20, + 0x18, 0x40, 0x18, 0x80, 0x19, 0x80, 0x1B, 0x80, 0x1E, 0xC0, 0x1C, 0xC0, 0x18, 0x60, 0x18, 0x30, + 0x18, 0x30, 0x18, 0x18, 0x18, 0x1C, 0x7E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"k",75*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1F, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"l",76*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0xEF, 0x3C, 0x71, 0xC6, 0x61, 0x86, + 0x61, 0x86, 0x61, 0x86, 0x61, 0x86, 0x61, 0x86, 0x61, 0x86, 0x61, 0x86, 0x61, 0x86, 0x61, 0x86, + 0x61, 0x86, 0x61, 0x86, 0x61, 0x86, 0xF3, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"m",77*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xE0, 0x7A, 0x30, 0x1C, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"n",78*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x0C, 0x30, 0x08, 0x18, + 0x18, 0x18, 0x10, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, 0x30, 0x0C, + 0x18, 0x18, 0x18, 0x18, 0x0C, 0x30, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"o",79*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xE0, 0x7A, 0x30, 0x1C, 0x18, + 0x18, 0x08, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, 0x18, 0x0C, + 0x18, 0x18, 0x1C, 0x18, 0x1E, 0x30, 0x19, 0xE0, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x7E, 0x00, /*"p",80*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC8, 0x0C, 0x78, 0x18, 0x38, + 0x18, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, + 0x10, 0x18, 0x18, 0x38, 0x0C, 0x78, 0x07, 0x98, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x7E, /*"q",81*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1C, 0x7E, 0x66, 0x06, 0x86, + 0x07, 0x80, 0x07, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, + 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"r",82*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE4, 0x06, 0x1C, 0x0C, 0x0C, + 0x0C, 0x04, 0x0C, 0x04, 0x0E, 0x00, 0x07, 0xC0, 0x01, 0xF0, 0x00, 0x78, 0x00, 0x1C, 0x10, 0x0C, + 0x10, 0x0C, 0x18, 0x0C, 0x1C, 0x18, 0x13, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"s",83*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x07, 0x00, 0x3F, 0xF8, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x03, 0x04, 0x03, 0x04, 0x01, 0x88, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"t",84*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x78, 0x78, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x38, 0x0C, 0x5E, 0x07, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"u",85*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x3E, 0x18, 0x0C, 0x18, 0x08, + 0x18, 0x18, 0x0C, 0x10, 0x0C, 0x10, 0x04, 0x20, 0x06, 0x20, 0x06, 0x20, 0x03, 0x40, 0x03, 0x40, + 0x03, 0xC0, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"v",86*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xCF, 0x61, 0x86, 0x21, 0x84, + 0x31, 0x84, 0x31, 0x84, 0x31, 0xC8, 0x11, 0xC8, 0x1A, 0xC8, 0x1A, 0x48, 0x1A, 0x70, 0x0E, 0x70, + 0x0C, 0x70, 0x0C, 0x30, 0x0C, 0x20, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"w",87*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x7C, 0x0C, 0x10, 0x0E, 0x10, + 0x06, 0x20, 0x03, 0x40, 0x03, 0x40, 0x01, 0x80, 0x01, 0x80, 0x01, 0xC0, 0x02, 0x60, 0x04, 0x60, + 0x04, 0x30, 0x08, 0x18, 0x18, 0x18, 0x7C, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"x",88*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x3E, 0x18, 0x18, 0x18, 0x10, + 0x08, 0x10, 0x0C, 0x10, 0x04, 0x20, 0x06, 0x20, 0x06, 0x20, 0x02, 0x40, 0x03, 0x40, 0x01, 0x40, + 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x3E, 0x00, 0x3C, 0x00, /*"y",89*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x30, 0x38, 0x30, 0x30, + 0x20, 0x60, 0x20, 0xE0, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0E, 0x04, + 0x0C, 0x04, 0x18, 0x0C, 0x30, 0x18, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"z",90*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x10, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xC0, + 0x01, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x10, 0x00, 0x0C, 0x00, 0x00, /*"{",91*/ + + 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, + 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, + 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, + 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, /*"|",92*/ + + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x01, 0x80, + 0x00, 0xC0, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, /*"}",93*/ + + 0x00, 0x00, 0x1E, 0x00, 0x23, 0x00, 0x41, 0x82, 0x40, 0x82, 0x00, 0xE4, 0x00, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"~",94*/ +}; + +const esp_painter_basic_font_t esp_painter_basic_font_32 = { + .bitmap = bitmap, + .width = 16, + .height = 32, +}; + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_36.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_36.c new file mode 100644 index 0000000..186f214 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_36.c @@ -0,0 +1,781 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "sdkconfig.h" + +#include "esp_painter_font.h" + +#if CONFIG_ESP_PAINTER_BASIC_FONT_36 + +static const uint8_t bitmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*" ",0*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, + 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"!",1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC7, 0x00, 0x01, 0xC7, 0x00, 0x03, 0xCF, 0x00, 0x03, + 0x8E, 0x00, 0x07, 0x1C, 0x00, 0x06, 0x18, 0x00, 0x0C, 0x30, 0x00, 0x18, 0x60, 0x00, 0x10, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*""",2*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00, 0x04, 0x04, + 0x00, 0x04, 0x04, 0x00, 0x7F, 0xFF, 0x80, 0x7F, 0xFF, 0x80, 0x7F, 0xFF, 0x80, 0x0C, 0x0C, 0x00, + 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, + 0x08, 0x00, 0x7F, 0xFF, 0x80, 0x7F, 0xFF, 0x80, 0x7F, 0xFF, 0x80, 0x18, 0x18, 0x00, 0x18, 0x18, + 0x00, 0x18, 0x18, 0x00, 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"#",3*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x80, 0x00, 0x03, 0xF0, 0x00, 0x06, 0x88, 0x00, 0x0C, 0x84, 0x00, 0x18, 0x86, 0x00, 0x18, 0x86, + 0x00, 0x18, 0x8E, 0x00, 0x18, 0x8E, 0x00, 0x1C, 0x80, 0x00, 0x0E, 0x80, 0x00, 0x0F, 0x80, 0x00, + 0x07, 0x80, 0x00, 0x03, 0xC0, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xBC, 0x00, 0x00, + 0x9C, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x86, 0x00, 0x38, 0x86, 0x00, 0x38, 0x86, 0x00, 0x38, 0x86, + 0x00, 0x30, 0x84, 0x00, 0x10, 0x8C, 0x00, 0x08, 0x98, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, /*"$",4*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x04, 0x00, 0x66, 0x0C, 0x00, 0x42, 0x08, 0x00, 0xC3, 0x08, 0x00, 0xC3, 0x10, + 0x00, 0xC3, 0x10, 0x00, 0xC3, 0x20, 0x00, 0xC3, 0x20, 0x00, 0xC3, 0x20, 0x00, 0xC3, 0x40, 0x00, + 0x42, 0x40, 0x00, 0x66, 0x80, 0x00, 0x3C, 0x9E, 0x00, 0x00, 0xB3, 0x00, 0x01, 0x31, 0x00, 0x01, + 0x61, 0x80, 0x02, 0x61, 0x80, 0x02, 0x61, 0x80, 0x06, 0x61, 0x80, 0x04, 0x61, 0x80, 0x04, 0x61, + 0x80, 0x08, 0x61, 0x80, 0x08, 0x21, 0x00, 0x10, 0x33, 0x00, 0x10, 0x1E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"%",5*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x80, 0x00, 0x0C, 0x80, 0x00, 0x08, 0xC0, 0x00, 0x18, 0xC0, 0x00, 0x18, 0xC0, + 0x00, 0x18, 0xC0, 0x00, 0x18, 0xC0, 0x00, 0x18, 0x80, 0x00, 0x19, 0x80, 0x00, 0x1D, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0C, 0x3E, 0x00, 0x1E, 0x18, 0x00, 0x26, 0x08, 0x00, 0x67, 0x08, 0x00, 0x43, + 0x08, 0x00, 0xC3, 0x10, 0x00, 0xC3, 0x90, 0x00, 0xC1, 0xD0, 0x00, 0xC0, 0xD0, 0x00, 0xC0, 0xE0, + 0x00, 0x60, 0x70, 0x80, 0x60, 0x79, 0x80, 0x30, 0x9F, 0x00, 0x1F, 0x0E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"&",6*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"'",7*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x20, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"(",8*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*")",9*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x01, 0xC0, + 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x70, 0xC7, 0x00, 0x78, 0x8F, 0x00, 0x3C, 0x9E, 0x00, + 0x0E, 0xB8, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x0E, 0xB8, 0x00, 0x3C, 0x9E, 0x00, 0x78, + 0x8F, 0x00, 0x71, 0x87, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"*",10*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x3F, 0xFF, 0x80, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"+",11*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, /*",",12*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"-",13*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*".",14*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, + 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"/",15*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xE0, 0x00, 0x06, 0x18, 0x00, 0x04, 0x0C, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, + 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, + 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, + 0x00, 0x0C, 0x0C, 0x00, 0x04, 0x08, 0x00, 0x06, 0x18, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"0",16*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"1",17*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x0C, 0x1C, 0x00, 0x18, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x03, + 0x00, 0x38, 0x03, 0x00, 0x38, 0x03, 0x00, 0x38, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, + 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x01, 0x00, 0x08, 0x01, + 0x00, 0x10, 0x03, 0x00, 0x30, 0x07, 0x00, 0x3F, 0xFE, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"2",18*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0xE0, 0x00, 0x08, 0x38, 0x00, 0x10, 0x0C, 0x00, 0x20, 0x0E, 0x00, 0x30, 0x06, + 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x30, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x18, 0x03, 0x00, 0x38, 0x03, 0x00, 0x38, 0x03, + 0x00, 0x38, 0x06, 0x00, 0x18, 0x04, 0x00, 0x0C, 0x18, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"3",19*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x58, + 0x00, 0x00, 0xD8, 0x00, 0x00, 0x98, 0x00, 0x01, 0x18, 0x00, 0x03, 0x18, 0x00, 0x02, 0x18, 0x00, + 0x04, 0x18, 0x00, 0x0C, 0x18, 0x00, 0x08, 0x18, 0x00, 0x10, 0x18, 0x00, 0x10, 0x18, 0x00, 0x20, + 0x18, 0x00, 0x60, 0x18, 0x00, 0x7F, 0xFF, 0x80, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"4",20*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x0F, 0xFF, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x11, 0xF0, 0x00, + 0x16, 0x0C, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x18, 0x03, 0x00, 0x38, 0x03, 0x00, 0x38, 0x06, + 0x00, 0x30, 0x06, 0x00, 0x10, 0x0C, 0x00, 0x0C, 0x18, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"5",21*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xF8, 0x00, 0x03, 0x04, 0x00, 0x06, 0x06, 0x00, 0x0C, 0x06, 0x00, 0x08, 0x06, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x31, 0xF0, 0x00, + 0x36, 0x1C, 0x00, 0x34, 0x06, 0x00, 0x38, 0x06, 0x00, 0x38, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, + 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x02, + 0x00, 0x0C, 0x06, 0x00, 0x0C, 0x04, 0x00, 0x06, 0x08, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"6",22*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x1F, 0xFF, 0x00, 0x18, 0x02, 0x00, 0x30, 0x04, 0x00, 0x20, 0x04, + 0x00, 0x20, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, + 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"7",23*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xF0, 0x00, 0x06, 0x0C, 0x00, 0x0C, 0x06, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, + 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x1C, 0x03, 0x00, 0x0E, 0x06, 0x00, 0x0F, 0x06, 0x00, + 0x07, 0x88, 0x00, 0x03, 0xF0, 0x00, 0x06, 0xF8, 0x00, 0x0C, 0x3C, 0x00, 0x18, 0x1E, 0x00, 0x18, + 0x0E, 0x00, 0x30, 0x07, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, + 0x00, 0x18, 0x02, 0x00, 0x18, 0x06, 0x00, 0x0E, 0x0C, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"8",24*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x0E, 0x18, 0x00, 0x08, 0x0C, 0x00, 0x18, 0x04, 0x00, 0x10, 0x06, + 0x00, 0x30, 0x02, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, + 0x30, 0x07, 0x00, 0x18, 0x07, 0x00, 0x18, 0x0B, 0x00, 0x0E, 0x13, 0x00, 0x03, 0xE3, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x18, 0x0C, + 0x00, 0x18, 0x08, 0x00, 0x18, 0x18, 0x00, 0x08, 0x70, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"9",25*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x01, 0xE0, 0x00, + 0x01, 0xE0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*":",26*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, + 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, /*";",27*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"<",28*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"=",29*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*">",30*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x06, 0x0C, 0x00, 0x08, 0x06, 0x00, 0x10, 0x02, 0x00, 0x30, 0x03, + 0x00, 0x30, 0x03, 0x00, 0x3C, 0x03, 0x00, 0x3C, 0x03, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x78, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"?",31*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xF0, 0x00, 0x07, 0x0C, 0x00, 0x0C, 0x02, 0x00, 0x08, 0x01, 0x00, 0x18, 0x7D, + 0x00, 0x30, 0xCD, 0x00, 0x31, 0x8C, 0x80, 0x21, 0x8C, 0x80, 0x63, 0x08, 0x80, 0x63, 0x08, 0x80, + 0x63, 0x18, 0x80, 0x66, 0x18, 0x80, 0x66, 0x18, 0x80, 0x66, 0x18, 0x80, 0x66, 0x18, 0x80, 0x66, + 0x31, 0x00, 0x66, 0x31, 0x00, 0x22, 0x52, 0x00, 0x33, 0x9C, 0x00, 0x30, 0x00, 0x80, 0x10, 0x01, + 0x00, 0x18, 0x03, 0x00, 0x0C, 0x06, 0x00, 0x06, 0x0C, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"@",32*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x60, + 0x00, 0x02, 0x60, 0x00, 0x02, 0x60, 0x00, 0x02, 0x60, 0x00, 0x06, 0x30, 0x00, 0x04, 0x30, 0x00, + 0x04, 0x30, 0x00, 0x04, 0x30, 0x00, 0x0C, 0x38, 0x00, 0x08, 0x18, 0x00, 0x08, 0x18, 0x00, 0x0F, + 0xF8, 0x00, 0x18, 0x18, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x0C, 0x00, 0x10, 0x0C, 0x00, 0x30, 0x0C, + 0x00, 0x20, 0x0E, 0x00, 0x20, 0x06, 0x00, 0x30, 0x06, 0x00, 0xF8, 0x1F, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"A",33*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x18, 0x1E, 0x00, 0x18, 0x06, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, + 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, + 0x18, 0x18, 0x00, 0x1F, 0xF0, 0x00, 0x18, 0x0C, 0x00, 0x18, 0x06, 0x00, 0x18, 0x03, 0x00, 0x18, + 0x01, 0x00, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, + 0x80, 0x18, 0x03, 0x00, 0x18, 0x07, 0x00, 0x18, 0x0E, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"B",34*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xF9, 0x00, 0x02, 0x0F, 0x00, 0x0C, 0x03, 0x00, 0x08, 0x01, 0x00, 0x18, 0x01, + 0x80, 0x30, 0x00, 0x80, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x80, 0x30, 0x00, 0x80, 0x30, 0x01, + 0x00, 0x18, 0x01, 0x00, 0x0C, 0x02, 0x00, 0x06, 0x0C, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"C",35*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x18, 0x38, 0x00, 0x18, 0x0C, 0x00, 0x18, 0x06, 0x00, 0x18, 0x02, + 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, + 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, + 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x18, 0x0C, 0x00, 0x18, 0x38, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"D",36*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x18, 0x07, 0x00, 0x18, 0x03, 0x00, 0x18, 0x01, 0x80, 0x18, 0x00, + 0x80, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x08, 0x00, 0x18, 0x08, 0x00, 0x18, 0x08, 0x00, + 0x18, 0x18, 0x00, 0x1F, 0xF8, 0x00, 0x18, 0x18, 0x00, 0x18, 0x08, 0x00, 0x18, 0x08, 0x00, 0x18, + 0x08, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x80, 0x18, 0x00, + 0x80, 0x18, 0x01, 0x00, 0x18, 0x03, 0x00, 0x18, 0x07, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"E",37*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x18, 0x0F, 0x00, 0x18, 0x03, 0x00, 0x18, 0x01, 0x00, 0x18, 0x00, + 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x08, 0x00, 0x18, 0x08, 0x00, + 0x18, 0x18, 0x00, 0x1F, 0xF8, 0x00, 0x18, 0x18, 0x00, 0x18, 0x08, 0x00, 0x18, 0x08, 0x00, 0x18, + 0x08, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"F",38*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xE0, 0x00, 0x06, 0x1C, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x04, 0x00, 0x10, 0x06, + 0x00, 0x30, 0x02, 0x00, 0x30, 0x02, 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x3F, 0x80, 0x60, + 0x06, 0x00, 0x60, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x10, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x0C, 0x06, 0x00, 0x06, 0x08, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"G",39*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x1F, 0x80, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, + 0x18, 0x06, 0x00, 0x1F, 0xFE, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, + 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x7E, 0x1F, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"H",40*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"I",41*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x70, 0x18, 0x00, + 0x70, 0x30, 0x00, 0x70, 0x30, 0x00, 0x30, 0x40, 0x00, 0x1F, 0x80, 0x00, /*"J",42*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x3F, 0x00, 0x18, 0x0C, 0x00, 0x18, 0x08, 0x00, 0x18, 0x10, 0x00, 0x18, 0x30, + 0x00, 0x18, 0x20, 0x00, 0x18, 0x40, 0x00, 0x18, 0xC0, 0x00, 0x18, 0x80, 0x00, 0x19, 0x80, 0x00, + 0x19, 0x80, 0x00, 0x1B, 0xC0, 0x00, 0x1C, 0xC0, 0x00, 0x1C, 0xE0, 0x00, 0x18, 0x60, 0x00, 0x18, + 0x60, 0x00, 0x18, 0x30, 0x00, 0x18, 0x30, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x18, 0x1C, + 0x00, 0x18, 0x0C, 0x00, 0x18, 0x0E, 0x00, 0x18, 0x06, 0x00, 0x7E, 0x1F, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"K",43*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x80, 0x18, 0x00, + 0x80, 0x18, 0x01, 0x00, 0x18, 0x03, 0x00, 0x18, 0x07, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"L",44*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF8, 0x07, 0xC0, 0x38, 0x07, 0x00, 0x38, 0x07, 0x00, 0x38, 0x07, 0x00, 0x38, 0x07, + 0x00, 0x2C, 0x0B, 0x00, 0x2C, 0x0B, 0x00, 0x2C, 0x0B, 0x00, 0x2C, 0x0B, 0x00, 0x2E, 0x13, 0x00, + 0x26, 0x13, 0x00, 0x26, 0x13, 0x00, 0x26, 0x13, 0x00, 0x26, 0x23, 0x00, 0x23, 0x23, 0x00, 0x23, + 0x23, 0x00, 0x23, 0x23, 0x00, 0x23, 0x43, 0x00, 0x23, 0xC3, 0x00, 0x21, 0xC3, 0x00, 0x21, 0xC3, + 0x00, 0x21, 0xC3, 0x00, 0x21, 0x83, 0x00, 0x20, 0x83, 0x00, 0xF8, 0x8F, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"M",45*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0F, 0x80, 0x1C, 0x02, 0x00, 0x1C, 0x02, 0x00, 0x16, 0x02, 0x00, 0x16, 0x02, + 0x00, 0x16, 0x02, 0x00, 0x13, 0x02, 0x00, 0x13, 0x02, 0x00, 0x11, 0x82, 0x00, 0x11, 0x82, 0x00, + 0x10, 0xC2, 0x00, 0x10, 0xC2, 0x00, 0x10, 0xE2, 0x00, 0x10, 0x62, 0x00, 0x10, 0x62, 0x00, 0x10, + 0x32, 0x00, 0x10, 0x32, 0x00, 0x10, 0x1A, 0x00, 0x10, 0x1A, 0x00, 0x10, 0x0E, 0x00, 0x10, 0x0E, + 0x00, 0x10, 0x0E, 0x00, 0x10, 0x06, 0x00, 0x10, 0x06, 0x00, 0x7C, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"N",46*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x06, 0x18, 0x00, 0x0C, 0x04, 0x00, 0x18, 0x06, 0x00, 0x10, 0x02, + 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x20, 0x01, 0x00, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, + 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, + 0x01, 0x80, 0x60, 0x01, 0x80, 0x20, 0x01, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x10, 0x02, + 0x00, 0x18, 0x06, 0x00, 0x0C, 0x04, 0x00, 0x06, 0x18, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"O",47*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x18, 0x0E, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x01, + 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x03, 0x00, + 0x18, 0x07, 0x00, 0x18, 0x0E, 0x00, 0x1F, 0xF8, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"P",48*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x06, 0x18, 0x00, 0x0C, 0x04, 0x00, 0x18, 0x06, 0x00, 0x10, 0x02, + 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, + 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, + 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x27, 0xC1, 0x00, 0x3C, 0x63, 0x00, 0x38, 0x33, + 0x00, 0x18, 0x32, 0x00, 0x08, 0x3C, 0x00, 0x06, 0x18, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x1C, 0x80, + 0x00, 0x0F, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Q",49*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x18, 0x1C, 0x00, 0x18, 0x06, 0x00, 0x18, 0x07, 0x00, 0x18, 0x03, + 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, + 0x18, 0x1C, 0x00, 0x1F, 0xF0, 0x00, 0x18, 0x60, 0x00, 0x18, 0x60, 0x00, 0x18, 0x70, 0x00, 0x18, + 0x30, 0x00, 0x18, 0x30, 0x00, 0x18, 0x38, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x18, 0x1C, + 0x00, 0x18, 0x0C, 0x00, 0x18, 0x0E, 0x00, 0x18, 0x06, 0x00, 0x7E, 0x07, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"R",50*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x0C, 0x1E, 0x00, 0x18, 0x0E, 0x00, 0x10, 0x06, 0x00, 0x30, 0x02, + 0x00, 0x30, 0x02, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x38, 0x00, 0x00, 0x1E, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x00, 0x20, 0x03, 0x00, 0x20, 0x03, 0x00, 0x30, 0x03, + 0x00, 0x10, 0x06, 0x00, 0x18, 0x06, 0x00, 0x1E, 0x0C, 0x00, 0x11, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"S",51*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x30, 0xC3, 0x00, 0x20, 0xC1, 0x00, 0x20, 0xC1, 0x80, 0x40, 0xC0, + 0x80, 0x40, 0xC0, 0x80, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"T",52*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x0F, 0x80, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, + 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, + 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, + 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, + 0x00, 0x18, 0x04, 0x00, 0x0C, 0x04, 0x00, 0x06, 0x18, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"U",53*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x0F, 0x80, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x1C, 0x06, 0x00, 0x0C, 0x04, + 0x00, 0x0C, 0x04, 0x00, 0x0C, 0x04, 0x00, 0x0E, 0x0C, 0x00, 0x0E, 0x08, 0x00, 0x06, 0x08, 0x00, + 0x06, 0x08, 0x00, 0x06, 0x18, 0x00, 0x07, 0x10, 0x00, 0x03, 0x10, 0x00, 0x03, 0x10, 0x00, 0x03, + 0x20, 0x00, 0x03, 0xA0, 0x00, 0x01, 0xA0, 0x00, 0x01, 0xA0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, + 0x00, 0x01, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"V",54*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF9, 0xF3, 0xC0, 0x70, 0xC1, 0x80, 0x30, 0xC1, 0x00, 0x30, 0xC1, 0x00, 0x30, 0xC1, + 0x00, 0x30, 0x61, 0x00, 0x30, 0xE1, 0x00, 0x30, 0xE2, 0x00, 0x30, 0xE2, 0x00, 0x18, 0xE2, 0x00, + 0x18, 0xE2, 0x00, 0x19, 0x62, 0x00, 0x19, 0x32, 0x00, 0x19, 0x34, 0x00, 0x19, 0x34, 0x00, 0x19, + 0x34, 0x00, 0x0E, 0x34, 0x00, 0x0E, 0x34, 0x00, 0x0E, 0x38, 0x00, 0x0E, 0x18, 0x00, 0x0E, 0x18, + 0x00, 0x0E, 0x18, 0x00, 0x04, 0x18, 0x00, 0x04, 0x18, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"W",55*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x1F, 0x00, 0x1C, 0x06, 0x00, 0x0C, 0x04, 0x00, 0x0C, 0x08, 0x00, 0x0E, 0x08, + 0x00, 0x06, 0x18, 0x00, 0x07, 0x10, 0x00, 0x03, 0x30, 0x00, 0x03, 0xA0, 0x00, 0x01, 0xE0, 0x00, + 0x01, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, + 0x70, 0x00, 0x03, 0x30, 0x00, 0x02, 0x30, 0x00, 0x06, 0x18, 0x00, 0x04, 0x18, 0x00, 0x04, 0x1C, + 0x00, 0x08, 0x0C, 0x00, 0x08, 0x0E, 0x00, 0x18, 0x06, 0x00, 0x7C, 0x1F, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"X",56*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x0F, 0x80, 0x38, 0x06, 0x00, 0x18, 0x06, 0x00, 0x1C, 0x04, 0x00, 0x0C, 0x04, + 0x00, 0x0C, 0x08, 0x00, 0x06, 0x08, 0x00, 0x06, 0x08, 0x00, 0x07, 0x10, 0x00, 0x03, 0x10, 0x00, + 0x03, 0x30, 0x00, 0x01, 0xA0, 0x00, 0x01, 0xA0, 0x00, 0x01, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Y",57*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x1C, 0x06, 0x00, 0x18, 0x0E, 0x00, 0x30, 0x0C, 0x00, 0x20, 0x1C, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x1C, 0x01, + 0x00, 0x18, 0x02, 0x00, 0x38, 0x06, 0x00, 0x30, 0x0E, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Z",58*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"[",59*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x01, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x10, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, /*"\",60*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x3F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"]",61*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x03, 0x60, 0x00, 0x06, 0x10, 0x00, 0x08, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"^",62*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xC0, /*"_",63*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"`",64*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x00, + 0x0C, 0x18, 0x00, 0x18, 0x0C, 0x00, 0x18, 0x0C, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x01, + 0xFC, 0x00, 0x07, 0x0C, 0x00, 0x1C, 0x0C, 0x00, 0x18, 0x0C, 0x00, 0x30, 0x0C, 0x00, 0x30, 0x0C, + 0x00, 0x30, 0x0C, 0x80, 0x30, 0x1C, 0x80, 0x18, 0x3C, 0x80, 0x0F, 0xC7, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"a",65*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0xF8, 0x00, + 0x1B, 0x0C, 0x00, 0x1E, 0x06, 0x00, 0x1C, 0x06, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, + 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, + 0x00, 0x18, 0x06, 0x00, 0x1C, 0x06, 0x00, 0x1E, 0x0C, 0x00, 0x11, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"b",66*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x00, + 0x07, 0x0C, 0x00, 0x0C, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x10, 0x01, + 0x00, 0x18, 0x02, 0x00, 0x0C, 0x02, 0x00, 0x06, 0x0C, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"c",67*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x03, 0xE6, 0x00, + 0x06, 0x1E, 0x00, 0x0C, 0x0E, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, + 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, + 0x00, 0x18, 0x0E, 0x00, 0x18, 0x0E, 0x00, 0x0C, 0x17, 0x80, 0x03, 0xE4, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"d",68*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, + 0x06, 0x0C, 0x00, 0x0C, 0x06, 0x00, 0x18, 0x06, 0x00, 0x10, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, + 0x03, 0x00, 0x3F, 0xFF, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x10, 0x01, + 0x00, 0x18, 0x01, 0x00, 0x0C, 0x02, 0x00, 0x06, 0x0C, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"e",69*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC1, 0x00, 0x01, 0x81, 0x80, 0x01, 0x01, 0x80, 0x03, 0x01, + 0x80, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x3F, 0xFC, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"f",70*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE3, 0x80, + 0x06, 0x3D, 0x80, 0x0C, 0x18, 0x00, 0x18, 0x0C, 0x00, 0x18, 0x0C, 0x00, 0x18, 0x0C, 0x00, 0x18, + 0x0C, 0x00, 0x0C, 0x18, 0x00, 0x06, 0x18, 0x00, 0x0F, 0xE0, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0x07, 0xFC, 0x00, 0x18, 0x1E, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, + 0x30, 0x03, 0x00, 0x38, 0x02, 0x00, 0x1C, 0x0E, 0x00, 0x07, 0xF8, 0x00, /*"g",71*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0xF8, 0x00, + 0x1B, 0x0C, 0x00, 0x1C, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, + 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x7E, 0x1F, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"h",72*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x0F, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x0F, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"i",73*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xFC, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, + 0x18, 0x08, 0x00, 0x18, 0x18, 0x00, 0x18, 0x30, 0x00, 0x0F, 0xC0, 0x00, /*"j",74*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x3F, 0x00, + 0x18, 0x1C, 0x00, 0x18, 0x10, 0x00, 0x18, 0x30, 0x00, 0x18, 0x60, 0x00, 0x18, 0xC0, 0x00, 0x19, + 0xC0, 0x00, 0x1B, 0xE0, 0x00, 0x1E, 0x60, 0x00, 0x1C, 0x70, 0x00, 0x18, 0x38, 0x00, 0x18, 0x18, + 0x00, 0x18, 0x1C, 0x00, 0x18, 0x0E, 0x00, 0x18, 0x06, 0x00, 0x7E, 0x1F, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"k",75*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"l",76*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x77, 0x9E, 0x00, + 0x38, 0xE3, 0x00, 0x38, 0xC3, 0x00, 0x30, 0xC3, 0x00, 0x30, 0xC3, 0x00, 0x30, 0xC3, 0x00, 0x30, + 0xC3, 0x00, 0x30, 0xC3, 0x00, 0x30, 0xC3, 0x00, 0x30, 0xC3, 0x00, 0x30, 0xC3, 0x00, 0x30, 0xC3, + 0x00, 0x30, 0xC3, 0x00, 0x30, 0xC3, 0x00, 0x30, 0xC3, 0x00, 0x79, 0xE7, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"m",77*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xF8, 0x00, + 0x7B, 0x0C, 0x00, 0x1E, 0x06, 0x00, 0x1C, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, + 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x7E, 0x1F, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"n",78*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x00, + 0x06, 0x18, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x30, 0x03, 0x00, 0x30, + 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x10, 0x02, + 0x00, 0x18, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x06, 0x18, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"o",79*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xF0, 0x00, + 0x7B, 0x0C, 0x00, 0x1C, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, + 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x1C, 0x0C, 0x00, 0x1A, 0x18, 0x00, 0x19, 0xF0, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x7E, 0x00, 0x00, /*"p",80*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE2, 0x00, + 0x06, 0x1E, 0x00, 0x0C, 0x0E, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, + 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, 0x00, 0x30, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x18, 0x0E, 0x00, 0x0C, 0x16, 0x00, 0x03, 0xE6, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1F, 0x80, /*"q",81*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1E, 0x00, + 0x7E, 0x23, 0x00, 0x06, 0xC3, 0x00, 0x06, 0x83, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"r",82*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF2, 0x00, + 0x06, 0x0E, 0x00, 0x0C, 0x06, 0x00, 0x0C, 0x02, 0x00, 0x0C, 0x02, 0x00, 0x0E, 0x00, 0x00, 0x07, + 0x80, 0x00, 0x03, 0xE0, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x1C, 0x00, 0x10, 0x0E, 0x00, 0x10, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x1E, 0x0C, 0x00, 0x11, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"s",83*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x3F, 0xFC, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x02, 0x00, 0x03, 0x04, 0x00, 0x01, 0x84, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"t",84*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x78, 0x1E, 0x00, + 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, + 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, 0x00, 0x18, 0x06, + 0x00, 0x18, 0x06, 0x00, 0x18, 0x0E, 0x00, 0x0C, 0x17, 0x80, 0x07, 0xE4, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"u",85*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x1F, 0x00, + 0x18, 0x0C, 0x00, 0x18, 0x04, 0x00, 0x0C, 0x08, 0x00, 0x0C, 0x08, 0x00, 0x0C, 0x18, 0x00, 0x06, + 0x10, 0x00, 0x06, 0x10, 0x00, 0x06, 0x20, 0x00, 0x03, 0x20, 0x00, 0x03, 0x20, 0x00, 0x03, 0xC0, + 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"v",86*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xE7, 0xC0, + 0x70, 0xC1, 0x00, 0x30, 0xC1, 0x00, 0x30, 0xC1, 0x00, 0x30, 0xE2, 0x00, 0x10, 0xE2, 0x00, 0x18, + 0xE2, 0x00, 0x19, 0x66, 0x00, 0x19, 0x34, 0x00, 0x0D, 0x34, 0x00, 0x0F, 0x34, 0x00, 0x0E, 0x38, + 0x00, 0x0E, 0x18, 0x00, 0x06, 0x18, 0x00, 0x06, 0x18, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"w",87*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x3E, 0x00, + 0x0E, 0x08, 0x00, 0x06, 0x10, 0x00, 0x06, 0x10, 0x00, 0x03, 0x20, 0x00, 0x03, 0xE0, 0x00, 0x01, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0x60, 0x00, 0x02, 0x30, 0x00, 0x06, 0x30, + 0x00, 0x04, 0x18, 0x00, 0x08, 0x18, 0x00, 0x18, 0x0C, 0x00, 0x7C, 0x3F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"x",88*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x1F, 0x80, + 0x0C, 0x06, 0x00, 0x0C, 0x04, 0x00, 0x06, 0x04, 0x00, 0x06, 0x0C, 0x00, 0x02, 0x08, 0x00, 0x03, + 0x08, 0x00, 0x03, 0x18, 0x00, 0x01, 0x90, 0x00, 0x01, 0x90, 0x00, 0x01, 0xB0, 0x00, 0x00, 0xE0, + 0x00, 0x00, 0xE0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x00, 0x19, 0x80, 0x00, 0x1F, 0x00, 0x00, 0x1E, 0x00, 0x00, /*"y",89*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, + 0x38, 0x1C, 0x00, 0x30, 0x38, 0x00, 0x20, 0x30, 0x00, 0x20, 0x70, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x03, 0x00, 0x00, 0x06, 0x02, 0x00, 0x0E, 0x02, + 0x00, 0x0C, 0x06, 0x00, 0x18, 0x04, 0x00, 0x38, 0x0C, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"z",90*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x00, 0x00, 0x60, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"{",91*/ + + 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, /*"|",92*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"}",93*/ + + 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x23, 0x00, 0x00, 0x21, 0x80, 0x80, 0x40, 0xC0, 0x80, 0x40, + 0x61, 0x00, 0x00, 0x31, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"~",94*/ +}; + +const esp_painter_basic_font_t esp_painter_basic_font_36 = { + .bitmap = bitmap, + .width = 18, + .height = 36, +}; + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_40.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_40.c new file mode 100644 index 0000000..622df3d --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_40.c @@ -0,0 +1,876 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "sdkconfig.h" + +#include "esp_painter_font.h" + +#if CONFIG_ESP_PAINTER_BASIC_FONT_40 + +static const uint8_t bitmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*" ",0*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, + 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"!",1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x01, 0xE7, 0x80, 0x01, 0xE7, 0x80, 0x03, + 0xCF, 0x00, 0x03, 0x8E, 0x00, 0x07, 0x1C, 0x00, 0x06, 0x18, 0x00, 0x0C, 0x30, 0x00, 0x18, 0x60, + 0x00, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*""",2*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x7F, 0xFF, 0xC0, 0x7F, 0xFF, 0xC0, 0x7F, 0xFF, 0xC0, + 0x06, 0x06, 0x00, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00, 0x04, + 0x04, 0x00, 0x04, 0x04, 0x00, 0x04, 0x04, 0x00, 0x7F, 0xFF, 0xC0, 0x7F, 0xFF, 0xC0, 0x7F, 0xFF, + 0xC0, 0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00, + 0x0C, 0x0C, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"#",3*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0xFC, 0x00, 0x03, 0x22, 0x00, 0x06, 0x21, 0x00, 0x04, 0x21, + 0x80, 0x0C, 0x21, 0x80, 0x0C, 0x23, 0x80, 0x0C, 0x23, 0x80, 0x0E, 0x20, 0x00, 0x07, 0x20, 0x00, + 0x07, 0xA0, 0x00, 0x03, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x3E, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x27, 0x00, 0x00, 0x23, 0x80, 0x00, 0x23, 0x80, 0x0C, 0x21, + 0x80, 0x1C, 0x21, 0x80, 0x1C, 0x21, 0x80, 0x18, 0x21, 0x80, 0x18, 0x21, 0x00, 0x08, 0x23, 0x00, + 0x06, 0x24, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"$",4*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x01, 0x00, 0x66, 0x03, 0x00, 0x62, 0x02, 0x00, 0x42, 0x06, 0x00, 0xC3, 0x04, + 0x00, 0xC3, 0x0C, 0x00, 0xC3, 0x08, 0x00, 0xC3, 0x08, 0x00, 0xC3, 0x18, 0x00, 0xC3, 0x10, 0x00, + 0xC3, 0x30, 0x00, 0xC3, 0x20, 0x00, 0x66, 0x60, 0x00, 0x66, 0x47, 0x80, 0x3C, 0x44, 0xC0, 0x00, + 0x8C, 0xC0, 0x00, 0x88, 0x40, 0x01, 0x98, 0x60, 0x01, 0x18, 0x60, 0x03, 0x18, 0x60, 0x02, 0x18, + 0x60, 0x02, 0x18, 0x60, 0x04, 0x18, 0x60, 0x04, 0x18, 0x60, 0x0C, 0x08, 0x60, 0x08, 0x08, 0x40, + 0x18, 0x04, 0xC0, 0x10, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"%",5*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xC0, 0x00, 0x04, 0x60, 0x00, 0x08, 0x30, 0x00, 0x18, 0x30, 0x00, 0x18, 0x30, + 0x00, 0x18, 0x30, 0x00, 0x18, 0x30, 0x00, 0x18, 0x30, 0x00, 0x18, 0x60, 0x00, 0x1C, 0x60, 0x00, + 0x0C, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x0E, 0x1F, 0xC0, 0x1E, 0x07, 0x00, 0x36, 0x06, 0x00, 0x27, + 0x06, 0x00, 0x67, 0x06, 0x00, 0x43, 0x84, 0x00, 0xC3, 0x84, 0x00, 0xC1, 0xC4, 0x00, 0xC1, 0xCC, + 0x00, 0xC0, 0xE8, 0x00, 0xC0, 0x78, 0x00, 0xC0, 0x70, 0x00, 0x60, 0x38, 0x20, 0x60, 0x3C, 0x60, + 0x38, 0x4F, 0xC0, 0x0F, 0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"&",6*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0F, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"'",7*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x00, + 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"(",8*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*")",9*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, + 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x70, 0xE1, 0xC0, 0x78, 0x43, 0xC0, 0x7C, 0x47, 0xC0, + 0x1E, 0x4F, 0x00, 0x07, 0x5C, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x07, 0x5C, 0x00, 0x1E, + 0x4F, 0x00, 0x7C, 0x47, 0xC0, 0x78, 0x43, 0xC0, 0x70, 0xE1, 0xC0, 0x00, 0xE0, 0x00, 0x00, 0xE0, + 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"*",10*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0x20, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"+",11*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, /*",",12*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"-",13*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*".",14*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"/",15*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x03, 0x0C, 0x00, 0x06, 0x06, 0x00, 0x0C, 0x03, + 0x00, 0x0C, 0x01, 0x00, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x30, 0x00, 0xC0, + 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, + 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x18, 0x01, + 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x0C, 0x01, 0x00, 0x0C, 0x03, 0x00, 0x06, 0x06, 0x00, + 0x03, 0x0C, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"0",16*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0xF0, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"1",17*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x06, 0x0E, 0x00, 0x1C, 0x03, 0x00, 0x18, 0x03, + 0x00, 0x38, 0x01, 0x80, 0x38, 0x01, 0x80, 0x3C, 0x01, 0x80, 0x3C, 0x01, 0x80, 0x1C, 0x01, 0x80, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x80, 0x0C, 0x00, 0x80, 0x18, 0x01, 0x80, 0x30, 0x03, 0x00, + 0x3F, 0xFF, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"2",18*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x0C, 0x1C, 0x00, 0x18, 0x06, 0x00, 0x30, 0x06, + 0x00, 0x38, 0x03, 0x00, 0x38, 0x03, 0x00, 0x38, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x80, 0x18, 0x01, 0x80, 0x3C, 0x01, 0x80, 0x3C, 0x01, 0x00, 0x38, 0x03, 0x00, 0x1C, 0x06, 0x00, + 0x0E, 0x0C, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"3",19*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x3C, + 0x00, 0x00, 0x2C, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x8C, 0x00, 0x01, 0x8C, 0x00, + 0x01, 0x0C, 0x00, 0x02, 0x0C, 0x00, 0x06, 0x0C, 0x00, 0x04, 0x0C, 0x00, 0x08, 0x0C, 0x00, 0x18, + 0x0C, 0x00, 0x10, 0x0C, 0x00, 0x20, 0x0C, 0x00, 0x60, 0x0C, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x0C, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"4",20*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x80, 0x0F, 0xFF, 0x80, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x11, 0xF8, 0x00, 0x16, 0x0C, 0x00, 0x1C, 0x06, 0x00, 0x18, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x1C, 0x01, + 0x80, 0x3C, 0x01, 0x80, 0x3C, 0x01, 0x80, 0x38, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x06, 0x00, + 0x0E, 0x0C, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"5",21*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x01, 0x83, 0x00, 0x02, 0x03, 0x80, 0x04, 0x03, + 0x80, 0x08, 0x03, 0x80, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0xFC, 0x00, 0x33, 0x06, 0x00, 0x34, 0x03, 0x00, 0x3C, 0x01, 0x80, 0x38, + 0x01, 0x80, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, + 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x01, 0x80, 0x0C, 0x01, 0x80, 0x06, 0x03, 0x00, + 0x03, 0x06, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"6",22*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x0F, 0xFF, 0xC0, 0x0C, 0x00, 0x80, 0x18, 0x01, + 0x80, 0x10, 0x03, 0x00, 0x10, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, + 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, 0x00, + 0x01, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"7",23*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x07, 0x07, 0x00, 0x0C, 0x01, 0x80, 0x0C, 0x01, + 0x80, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x1C, 0x00, 0xC0, + 0x0E, 0x01, 0x80, 0x0F, 0x83, 0x00, 0x07, 0xE6, 0x00, 0x01, 0xF8, 0x00, 0x03, 0x7C, 0x00, 0x06, + 0x1E, 0x00, 0x0C, 0x07, 0x00, 0x18, 0x03, 0x80, 0x38, 0x01, 0xC0, 0x30, 0x01, 0xC0, 0x30, 0x00, + 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x18, 0x01, 0x80, 0x0C, 0x01, 0x00, + 0x06, 0x06, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"8",24*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x06, 0x04, 0x00, 0x0C, 0x02, 0x00, 0x18, 0x01, + 0x00, 0x18, 0x01, 0x80, 0x30, 0x00, 0x80, 0x30, 0x00, 0x80, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, + 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x18, 0x01, 0xC0, 0x18, 0x03, 0xC0, 0x0C, + 0x06, 0xC0, 0x06, 0x0C, 0xC0, 0x03, 0xF0, 0xC0, 0x00, 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x1C, 0x03, 0x00, 0x1C, 0x06, 0x00, 0x1C, 0x0C, 0x00, + 0x0C, 0x18, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"9",25*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*":",26*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*";",27*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"<",28*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"=",29*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*">",30*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFC, 0x00, 0x03, 0x06, 0x00, 0x06, 0x01, 0x00, 0x0C, 0x01, 0x80, 0x08, 0x00, + 0xC0, 0x18, 0x00, 0xC0, 0x1C, 0x00, 0xC0, 0x1E, 0x00, 0xC0, 0x1E, 0x00, 0xC0, 0x0E, 0x00, 0xC0, + 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x00, + 0x00, 0xF0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"?",31*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7C, 0x00, 0x01, 0x83, 0x00, 0x03, 0x01, 0x80, 0x06, 0x00, 0x80, 0x0C, 0x00, + 0x40, 0x08, 0x1F, 0x40, 0x18, 0x67, 0x20, 0x18, 0x63, 0x20, 0x10, 0xC3, 0x20, 0x30, 0x86, 0x20, + 0x31, 0x86, 0x20, 0x31, 0x86, 0x20, 0x33, 0x06, 0x20, 0x33, 0x06, 0x20, 0x33, 0x06, 0x20, 0x33, + 0x0E, 0x20, 0x33, 0x0C, 0x40, 0x33, 0x0C, 0x40, 0x33, 0x1C, 0x40, 0x11, 0x14, 0x80, 0x18, 0xE7, + 0x00, 0x18, 0x00, 0x20, 0x08, 0x00, 0x40, 0x0C, 0x00, 0x40, 0x04, 0x00, 0x80, 0x02, 0x01, 0x00, + 0x01, 0x86, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"@",32*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xF0, + 0x00, 0x01, 0x30, 0x00, 0x01, 0x30, 0x00, 0x01, 0x30, 0x00, 0x03, 0x38, 0x00, 0x03, 0x38, 0x00, + 0x02, 0x18, 0x00, 0x02, 0x18, 0x00, 0x06, 0x18, 0x00, 0x06, 0x1C, 0x00, 0x04, 0x0C, 0x00, 0x04, + 0x0C, 0x00, 0x0C, 0x0C, 0x00, 0x0F, 0xFE, 0x00, 0x08, 0x06, 0x00, 0x08, 0x06, 0x00, 0x18, 0x06, + 0x00, 0x10, 0x07, 0x00, 0x10, 0x07, 0x00, 0x10, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x80, + 0x30, 0x03, 0x80, 0xFC, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"A",33*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x18, 0x07, 0x00, 0x18, 0x01, 0x80, 0x18, 0x01, + 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, + 0x18, 0x01, 0x80, 0x18, 0x03, 0x00, 0x18, 0x0E, 0x00, 0x1F, 0xF8, 0x00, 0x18, 0x06, 0x00, 0x18, + 0x01, 0x80, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0x60, 0x18, 0x00, 0x60, 0x18, 0x00, + 0x60, 0x18, 0x00, 0x60, 0x18, 0x00, 0x60, 0x18, 0x00, 0xE0, 0x18, 0x00, 0xC0, 0x18, 0x01, 0x80, + 0x18, 0x07, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"B",34*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x40, 0x03, 0x03, 0xC0, 0x06, 0x01, 0xC0, 0x0C, 0x00, + 0xC0, 0x18, 0x00, 0x40, 0x18, 0x00, 0x60, 0x30, 0x00, 0x20, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x20, 0x30, 0x00, 0x40, 0x18, 0x00, 0x40, 0x18, 0x00, 0x80, 0x0C, 0x01, 0x00, + 0x03, 0x06, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"C",35*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0, 0x00, 0x18, 0x1C, 0x00, 0x18, 0x06, 0x00, 0x18, 0x03, + 0x00, 0x18, 0x01, 0x80, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0x60, + 0x18, 0x00, 0x60, 0x18, 0x00, 0x60, 0x18, 0x00, 0x60, 0x18, 0x00, 0x60, 0x18, 0x00, 0x60, 0x18, + 0x00, 0x60, 0x18, 0x00, 0x60, 0x18, 0x00, 0x60, 0x18, 0x00, 0x60, 0x18, 0x00, 0x40, 0x18, 0x00, + 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0x80, 0x18, 0x01, 0x80, 0x18, 0x03, 0x00, 0x18, 0x06, 0x00, + 0x18, 0x1C, 0x00, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"D",36*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x80, 0x18, 0x03, 0xC0, 0x18, 0x01, 0xC0, 0x18, 0x00, + 0xC0, 0x18, 0x00, 0x60, 0x18, 0x00, 0x20, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x04, 0x00, + 0x18, 0x04, 0x00, 0x18, 0x04, 0x00, 0x18, 0x0C, 0x00, 0x1F, 0xFC, 0x00, 0x18, 0x0C, 0x00, 0x18, + 0x04, 0x00, 0x18, 0x04, 0x00, 0x18, 0x04, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x20, 0x18, 0x00, 0x60, 0x18, 0x00, 0x40, 0x18, 0x00, 0xC0, + 0x18, 0x03, 0xC0, 0x7F, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"E",37*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x80, 0x18, 0x03, 0xC0, 0x18, 0x01, 0xC0, 0x18, 0x00, + 0xC0, 0x18, 0x00, 0x60, 0x18, 0x00, 0x20, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x02, 0x00, + 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x06, 0x00, 0x1F, 0xFE, 0x00, 0x18, 0x06, 0x00, 0x18, + 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x02, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"F",38*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x03, 0x0F, 0x00, 0x04, 0x03, 0x00, 0x0C, 0x03, + 0x00, 0x18, 0x01, 0x80, 0x10, 0x00, 0x80, 0x30, 0x00, 0x80, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x0F, 0xF0, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, 0x30, 0x01, 0x80, 0x30, 0x01, + 0x80, 0x30, 0x01, 0x80, 0x10, 0x01, 0x80, 0x18, 0x01, 0x80, 0x0C, 0x01, 0x80, 0x0C, 0x01, 0x80, + 0x07, 0x06, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"G",39*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x07, 0xE0, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, + 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, + 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x1F, 0xFF, 0x80, 0x18, 0x01, 0x80, 0x18, + 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, + 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, + 0x18, 0x01, 0x80, 0x7E, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"H",40*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"I",41*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x70, 0x0C, 0x00, 0x70, 0x18, 0x00, 0x70, 0x10, 0x00, 0x30, + 0x60, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00, /*"J",42*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x1F, 0xC0, 0x18, 0x07, 0x00, 0x18, 0x06, 0x00, 0x18, 0x04, + 0x00, 0x18, 0x08, 0x00, 0x18, 0x18, 0x00, 0x18, 0x30, 0x00, 0x18, 0x20, 0x00, 0x18, 0x40, 0x00, + 0x18, 0xC0, 0x00, 0x18, 0xC0, 0x00, 0x19, 0xC0, 0x00, 0x1A, 0xE0, 0x00, 0x1E, 0xE0, 0x00, 0x1C, + 0x70, 0x00, 0x18, 0x70, 0x00, 0x18, 0x38, 0x00, 0x18, 0x38, 0x00, 0x18, 0x1C, 0x00, 0x18, 0x1C, + 0x00, 0x18, 0x0E, 0x00, 0x18, 0x0E, 0x00, 0x18, 0x06, 0x00, 0x18, 0x07, 0x00, 0x18, 0x03, 0x00, + 0x18, 0x03, 0x80, 0x7E, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"K",43*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x20, 0x0C, 0x00, 0x20, 0x0C, 0x00, 0x40, 0x0C, 0x00, 0xC0, + 0x0C, 0x01, 0xC0, 0x7F, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"L",44*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x03, 0xE0, 0x38, 0x03, 0x80, 0x38, 0x03, 0x80, 0x38, 0x03, + 0x80, 0x3C, 0x07, 0x80, 0x2C, 0x05, 0x80, 0x2C, 0x05, 0x80, 0x2C, 0x05, 0x80, 0x2C, 0x05, 0x80, + 0x26, 0x09, 0x80, 0x26, 0x09, 0x80, 0x26, 0x09, 0x80, 0x26, 0x09, 0x80, 0x27, 0x11, 0x80, 0x23, + 0x11, 0x80, 0x23, 0x11, 0x80, 0x23, 0x11, 0x80, 0x23, 0x21, 0x80, 0x23, 0xA1, 0x80, 0x21, 0xA1, + 0x80, 0x21, 0xA1, 0x80, 0x21, 0xE1, 0x80, 0x21, 0xC1, 0x80, 0x20, 0xC1, 0x80, 0x20, 0xC1, 0x80, + 0x20, 0xC1, 0x80, 0xF8, 0x87, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"M",45*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x07, 0xF0, 0x1C, 0x00, 0x80, 0x1C, 0x00, 0x80, 0x16, 0x00, + 0x80, 0x16, 0x00, 0x80, 0x13, 0x00, 0x80, 0x13, 0x00, 0x80, 0x11, 0x80, 0x80, 0x11, 0x80, 0x80, + 0x10, 0xC0, 0x80, 0x10, 0xC0, 0x80, 0x10, 0x60, 0x80, 0x10, 0x60, 0x80, 0x10, 0x30, 0x80, 0x10, + 0x30, 0x80, 0x10, 0x38, 0x80, 0x10, 0x18, 0x80, 0x10, 0x1C, 0x80, 0x10, 0x0C, 0x80, 0x10, 0x0E, + 0x80, 0x10, 0x06, 0x80, 0x10, 0x07, 0x80, 0x10, 0x03, 0x80, 0x10, 0x03, 0x80, 0x10, 0x01, 0x80, + 0x10, 0x01, 0x80, 0xFE, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"N",46*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x03, 0x04, 0x00, 0x0C, 0x03, 0x00, 0x08, 0x01, + 0x00, 0x18, 0x01, 0x80, 0x30, 0x00, 0x80, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x20, 0x00, 0x40, + 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, + 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x20, 0x00, 0x40, 0x30, 0x00, + 0xC0, 0x30, 0x00, 0xC0, 0x10, 0x00, 0xC0, 0x18, 0x01, 0x80, 0x08, 0x01, 0x00, 0x04, 0x03, 0x00, + 0x03, 0x0C, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"O",47*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x18, 0x06, 0x00, 0x18, 0x03, 0x00, 0x18, 0x01, + 0x80, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, + 0x18, 0x00, 0xC0, 0x18, 0x01, 0x80, 0x18, 0x03, 0x00, 0x18, 0x0E, 0x00, 0x1F, 0xF8, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"P",48*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x07, 0x06, 0x00, 0x0C, 0x03, 0x00, 0x08, 0x01, + 0x00, 0x18, 0x00, 0x80, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x60, 0x00, 0x60, + 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, + 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x60, 0x23, 0xE0, + 0x40, 0x37, 0xF0, 0xC0, 0x3E, 0x38, 0xC0, 0x1C, 0x1C, 0x80, 0x18, 0x1D, 0x80, 0x0C, 0x1F, 0x00, + 0x06, 0x0E, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x07, 0xC0, 0x00, 0x03, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Q",49*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x0C, 0x0E, 0x00, 0x0C, 0x03, 0x00, 0x0C, 0x03, + 0x80, 0x0C, 0x01, 0x80, 0x0C, 0x01, 0x80, 0x0C, 0x01, 0x80, 0x0C, 0x01, 0x80, 0x0C, 0x01, 0x80, + 0x0C, 0x03, 0x00, 0x0C, 0x03, 0x00, 0x0C, 0x0E, 0x00, 0x0F, 0xF8, 0x00, 0x0C, 0x30, 0x00, 0x0C, + 0x30, 0x00, 0x0C, 0x38, 0x00, 0x0C, 0x18, 0x00, 0x0C, 0x1C, 0x00, 0x0C, 0x1C, 0x00, 0x0C, 0x0C, + 0x00, 0x0C, 0x0E, 0x00, 0x0C, 0x06, 0x00, 0x0C, 0x07, 0x00, 0x0C, 0x07, 0x00, 0x0C, 0x03, 0x00, + 0x0C, 0x03, 0x80, 0x3F, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"R",50*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x06, 0x0F, 0x00, 0x08, 0x03, 0x80, 0x18, 0x03, + 0x80, 0x30, 0x01, 0x80, 0x30, 0x00, 0x80, 0x30, 0x00, 0x80, 0x30, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x07, 0x80, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x1E, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x80, 0x00, 0x01, 0xC0, 0x00, 0x00, 0xC0, 0x20, 0x00, + 0xC0, 0x20, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x10, 0x00, 0xC0, 0x18, 0x01, 0x80, 0x1C, 0x01, 0x00, + 0x1F, 0x06, 0x00, 0x10, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"S",51*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xC0, 0x38, 0x60, 0xC0, 0x30, 0x60, 0x40, 0x20, 0x60, + 0x40, 0x60, 0x60, 0x20, 0x40, 0x60, 0x20, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"T",52*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x03, 0xE0, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, + 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, + 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, + 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, + 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x00, 0x80, 0x08, 0x01, 0x00, 0x0C, 0x03, 0x00, + 0x06, 0x06, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"U",53*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x07, 0xE0, 0x1C, 0x01, 0x80, 0x1C, 0x01, 0x80, 0x0C, 0x01, + 0x00, 0x0C, 0x01, 0x00, 0x0E, 0x01, 0x00, 0x0E, 0x03, 0x00, 0x0E, 0x02, 0x00, 0x06, 0x02, 0x00, + 0x07, 0x02, 0x00, 0x07, 0x04, 0x00, 0x07, 0x04, 0x00, 0x03, 0x04, 0x00, 0x03, 0x04, 0x00, 0x03, + 0x88, 0x00, 0x03, 0x88, 0x00, 0x01, 0x88, 0x00, 0x01, 0x98, 0x00, 0x01, 0xD0, 0x00, 0x01, 0xD0, + 0x00, 0x00, 0xD0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"V",54*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xF9, 0xF0, 0x70, 0x60, 0x40, 0x30, 0x60, 0x40, 0x30, 0x60, + 0x40, 0x30, 0x70, 0x40, 0x30, 0x70, 0xC0, 0x38, 0x30, 0x80, 0x38, 0x70, 0x80, 0x18, 0x70, 0x80, + 0x18, 0x70, 0x80, 0x18, 0x70, 0x80, 0x18, 0xF9, 0x00, 0x18, 0xB9, 0x00, 0x1C, 0x99, 0x00, 0x0C, + 0x99, 0x00, 0x0C, 0x99, 0x00, 0x0D, 0x9B, 0x00, 0x0D, 0x1A, 0x00, 0x0D, 0x1E, 0x00, 0x0D, 0x0E, + 0x00, 0x0F, 0x0E, 0x00, 0x07, 0x0E, 0x00, 0x06, 0x0E, 0x00, 0x06, 0x0C, 0x00, 0x06, 0x0C, 0x00, + 0x06, 0x04, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"W",55*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x0F, 0xC0, 0x1C, 0x03, 0x00, 0x0C, 0x03, 0x00, 0x0E, 0x02, + 0x00, 0x06, 0x06, 0x00, 0x07, 0x04, 0x00, 0x03, 0x0C, 0x00, 0x03, 0x88, 0x00, 0x01, 0x98, 0x00, + 0x01, 0xD0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x70, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xB8, 0x00, 0x01, 0x98, 0x00, 0x01, 0x1C, 0x00, 0x03, 0x0C, + 0x00, 0x02, 0x0E, 0x00, 0x06, 0x0E, 0x00, 0x04, 0x07, 0x00, 0x0C, 0x07, 0x00, 0x08, 0x03, 0x00, + 0x18, 0x03, 0x80, 0x7E, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"X",56*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x07, 0xE0, 0x1C, 0x01, 0x80, 0x1C, 0x01, 0x00, 0x0C, 0x01, + 0x00, 0x0E, 0x03, 0x00, 0x0E, 0x02, 0x00, 0x06, 0x06, 0x00, 0x07, 0x04, 0x00, 0x03, 0x04, 0x00, + 0x03, 0x08, 0x00, 0x01, 0x88, 0x00, 0x01, 0x98, 0x00, 0x01, 0xD0, 0x00, 0x00, 0xD0, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Y",57*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x1E, 0x01, 0x80, 0x18, 0x03, 0x80, 0x18, 0x03, + 0x00, 0x30, 0x07, 0x00, 0x20, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x40, 0x1C, 0x00, 0xC0, 0x18, 0x00, 0x80, 0x38, 0x01, 0x80, + 0x30, 0x07, 0x80, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Z",58*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0xFF, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"[",59*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"\",60*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x1F, 0xF0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"]",61*/ + + 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x01, 0xF8, 0x00, 0x01, 0x9C, 0x00, 0x03, 0x0C, 0x00, 0x04, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"^",62*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, /*"_",63*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"`",64*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xF8, 0x00, 0x0C, 0x06, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0xFB, 0x00, 0x07, 0x03, 0x00, 0x0C, 0x03, 0x00, 0x18, 0x03, + 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x20, 0x18, 0x07, 0x20, + 0x1C, 0x1B, 0x20, 0x07, 0xE1, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"a",65*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x7C, 0x00, 0x19, 0x87, 0x00, 0x1A, 0x01, 0x00, 0x1C, 0x01, 0x80, 0x1C, 0x01, 0x80, 0x18, + 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, + 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x1C, 0x03, 0x00, + 0x1E, 0x06, 0x00, 0x11, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"b",66*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xF8, 0x00, 0x03, 0x06, 0x00, 0x0E, 0x02, 0x00, 0x0C, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, + 0x03, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x80, 0x18, 0x00, 0x80, 0x18, 0x01, 0x00, 0x0C, 0x03, 0x00, + 0x06, 0x06, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"c",67*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x0F, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, + 0x01, 0xF9, 0x80, 0x06, 0x0D, 0x80, 0x0C, 0x03, 0x80, 0x08, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, + 0x01, 0x80, 0x30, 0x01, 0x80, 0x30, 0x01, 0x80, 0x30, 0x01, 0x80, 0x30, 0x01, 0x80, 0x30, 0x01, + 0x80, 0x30, 0x01, 0x80, 0x30, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x03, 0x80, 0x0C, 0x07, 0x80, + 0x06, 0x0D, 0xE0, 0x01, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"d",68*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF8, 0x00, 0x03, 0x06, 0x00, 0x06, 0x03, 0x00, 0x04, 0x01, 0x80, 0x0C, 0x00, 0x80, 0x08, + 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x1F, 0xFF, 0xC0, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x40, 0x0C, 0x00, 0x80, 0x06, 0x01, 0x80, + 0x03, 0x83, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"e",69*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x70, 0x40, 0x00, 0xC0, 0x60, 0x00, 0xC0, 0x60, 0x01, 0x80, + 0x60, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, + 0x3F, 0xFF, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, + 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, + 0x01, 0x80, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"f",70*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xF8, 0xE0, 0x06, 0x0F, 0xE0, 0x0C, 0x06, 0x60, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, + 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x0C, 0x06, 0x00, 0x06, 0x0C, 0x00, 0x0D, 0xF8, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x0F, 0xFE, 0x00, 0x0C, 0x7F, 0x80, + 0x18, 0x01, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x18, 0x01, 0x80, 0x0E, + 0x03, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, /*"g",71*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x78, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x7C, 0x00, 0x19, 0x87, 0x00, 0x1E, 0x03, 0x00, 0x1C, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, + 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, + 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, + 0x18, 0x01, 0x80, 0x7E, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"h",72*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x0F, 0xE0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"i",73*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0xFE, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x18, 0x0C, 0x00, 0x18, 0x0C, 0x00, 0x1C, + 0x30, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, /*"j",74*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x0C, 0x1F, 0xC0, 0x0C, 0x06, 0x00, 0x0C, 0x04, 0x00, 0x0C, 0x08, 0x00, 0x0C, 0x18, 0x00, 0x0C, + 0x30, 0x00, 0x0C, 0x60, 0x00, 0x0C, 0xE0, 0x00, 0x0D, 0xB0, 0x00, 0x0F, 0x38, 0x00, 0x0E, 0x18, + 0x00, 0x0C, 0x1C, 0x00, 0x0C, 0x0E, 0x00, 0x0C, 0x06, 0x00, 0x0C, 0x07, 0x00, 0x0C, 0x03, 0x00, + 0x0C, 0x03, 0x80, 0x3F, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"k",75*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"l",76*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x73, 0xC7, 0x80, 0x3C, 0x78, 0xC0, 0x38, 0x70, 0xC0, 0x30, 0x60, 0xC0, 0x30, 0x60, 0xC0, 0x30, + 0x60, 0xC0, 0x30, 0x60, 0xC0, 0x30, 0x60, 0xC0, 0x30, 0x60, 0xC0, 0x30, 0x60, 0xC0, 0x30, 0x60, + 0xC0, 0x30, 0x60, 0xC0, 0x30, 0x60, 0xC0, 0x30, 0x60, 0xC0, 0x30, 0x60, 0xC0, 0x30, 0x60, 0xC0, + 0x30, 0x60, 0xC0, 0xFD, 0xFB, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"m",77*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x7C, 0x00, 0x79, 0x87, 0x00, 0x1E, 0x03, 0x00, 0x1C, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, + 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, + 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, + 0x18, 0x01, 0x80, 0x7E, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"n",78*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xF8, 0x00, 0x03, 0x0E, 0x00, 0x0C, 0x03, 0x00, 0x0C, 0x01, 0x00, 0x18, 0x01, 0x80, 0x18, + 0x00, 0x80, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, 0xC0, 0x30, 0x00, + 0xC0, 0x30, 0x00, 0xC0, 0x10, 0x00, 0x80, 0x18, 0x01, 0x80, 0x08, 0x01, 0x00, 0x0C, 0x03, 0x00, + 0x07, 0x0E, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"o",79*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0xF8, 0x00, 0x7B, 0x86, 0x00, 0x1E, 0x03, 0x00, 0x1C, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, + 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, + 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x00, 0xC0, 0x18, 0x01, 0x80, 0x18, 0x01, 0x80, 0x1C, 0x03, 0x00, + 0x1B, 0x0E, 0x00, 0x19, 0xF8, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, /*"p",80*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xF1, 0x00, 0x06, 0x09, 0x00, 0x0C, 0x07, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x30, + 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, + 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x10, 0x03, 0x00, 0x18, 0x03, 0x00, 0x0C, 0x07, 0x00, + 0x06, 0x1B, 0x00, 0x03, 0xF3, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, /*"q",81*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x07, 0x80, 0x3F, 0x18, 0xC0, 0x03, 0x30, 0xC0, 0x03, 0x60, 0xC0, 0x03, 0xC0, 0x00, 0x03, + 0x80, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"r",82*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xF8, 0x00, 0x03, 0x07, 0x80, 0x04, 0x03, 0x80, 0x0C, 0x01, 0x80, 0x0C, 0x00, 0x80, 0x0C, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x0F, + 0x00, 0x00, 0x03, 0x80, 0x10, 0x01, 0x80, 0x10, 0x01, 0x80, 0x18, 0x01, 0x80, 0x1C, 0x01, 0x00, + 0x1E, 0x07, 0x00, 0x11, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"s",83*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80, 0x00, + 0x3F, 0xFF, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, + 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x80, 0x01, 0x80, 0x80, 0x00, 0x81, 0x00, + 0x00, 0xC2, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"t",84*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, + 0x78, 0x0F, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, + 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, + 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x03, 0x00, 0x18, 0x07, 0x00, 0x0C, 0x07, 0x00, + 0x0E, 0x1B, 0xC0, 0x03, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"u",85*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x0F, 0xC0, 0x1C, 0x03, 0x00, 0x0C, 0x03, 0x00, 0x0C, 0x02, 0x00, 0x0E, 0x06, 0x00, 0x06, + 0x04, 0x00, 0x06, 0x04, 0x00, 0x07, 0x0C, 0x00, 0x03, 0x08, 0x00, 0x03, 0x08, 0x00, 0x03, 0x98, + 0x00, 0x01, 0x90, 0x00, 0x01, 0x90, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"v",86*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFD, 0xF1, 0xF0, 0x70, 0x60, 0x40, 0x30, 0x60, 0x40, 0x30, 0x60, 0x80, 0x38, 0x70, 0x80, 0x18, + 0x70, 0x80, 0x18, 0x71, 0x80, 0x18, 0xB1, 0x00, 0x1C, 0xB9, 0x00, 0x0C, 0x99, 0x00, 0x0C, 0x9B, + 0x00, 0x0D, 0x1A, 0x00, 0x0F, 0x1A, 0x00, 0x07, 0x0E, 0x00, 0x07, 0x0E, 0x00, 0x06, 0x0C, 0x00, + 0x06, 0x0C, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"w",87*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0x9F, 0x80, 0x0E, 0x06, 0x00, 0x06, 0x04, 0x00, 0x07, 0x0C, 0x00, 0x03, 0x88, 0x00, 0x01, + 0x90, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xB0, + 0x00, 0x01, 0x38, 0x00, 0x03, 0x18, 0x00, 0x02, 0x0C, 0x00, 0x04, 0x0E, 0x00, 0x0C, 0x06, 0x00, + 0x1C, 0x07, 0x00, 0x7E, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"x",88*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0x8F, 0xE0, 0x0E, 0x03, 0x80, 0x06, 0x01, 0x00, 0x06, 0x03, 0x00, 0x07, 0x02, 0x00, 0x03, + 0x06, 0x00, 0x03, 0x06, 0x00, 0x01, 0x84, 0x00, 0x01, 0x8C, 0x00, 0x01, 0x88, 0x00, 0x00, 0xC8, + 0x00, 0x00, 0xD8, 0x00, 0x00, 0x50, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x1C, 0x80, 0x00, 0x1F, + 0x80, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, /*"y",89*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0xFF, 0x00, 0x1C, 0x07, 0x00, 0x18, 0x0E, 0x00, 0x18, 0x0C, 0x00, 0x10, 0x1C, 0x00, 0x10, + 0x38, 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, + 0x00, 0x01, 0x80, 0x00, 0x03, 0x80, 0x80, 0x07, 0x00, 0x80, 0x06, 0x01, 0x80, 0x0E, 0x01, 0x00, + 0x1C, 0x03, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"z",90*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x30, 0x00, 0x00, 0x40, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"{",91*/ + + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, /*"|",92*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC0, 0x00, 0x01, + 0x80, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"}",93*/ + + 0x07, 0x80, 0x00, 0x19, 0xC0, 0x00, 0x10, 0xE0, 0x20, 0x20, 0x70, 0x20, 0x20, 0x38, 0x40, 0x00, + 0x1C, 0x80, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"~",94*/ +}; + +const esp_painter_basic_font_t esp_painter_basic_font_40 = { + .bitmap = bitmap, + .width = 20, + .height = 40, +}; + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_44.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_44.c new file mode 100644 index 0000000..052c25a --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_44.c @@ -0,0 +1,971 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "sdkconfig.h" + +#include "esp_painter_font.h" + +#if CONFIG_ESP_PAINTER_BASIC_FONT_44 + +static const uint8_t bitmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*" ",0*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"!",1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x80, 0x01, 0xE3, 0xC0, 0x01, + 0xE3, 0xC0, 0x03, 0xC7, 0x80, 0x03, 0x87, 0x00, 0x07, 0x8F, 0x00, 0x07, 0x0E, 0x00, 0x0E, 0x1C, + 0x00, 0x0C, 0x18, 0x00, 0x18, 0x30, 0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*""",2*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0xC0, 0x01, 0x00, 0x80, 0x01, 0x00, + 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x3F, 0xFF, 0xF0, + 0x3F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF0, 0x03, 0x01, 0x80, 0x03, 0x01, 0x80, 0x03, + 0x01, 0x80, 0x03, 0x01, 0x80, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, + 0x00, 0x3F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF0, 0x06, 0x03, 0x00, + 0x06, 0x03, 0x00, 0x06, 0x03, 0x00, 0x06, 0x03, 0x00, 0x06, 0x03, 0x00, 0x06, 0x03, 0x00, 0x06, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"#",3*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0xFE, 0x00, 0x01, 0xB3, 0x00, 0x03, 0x31, + 0x80, 0x06, 0x31, 0xC0, 0x0E, 0x31, 0xC0, 0x0E, 0x33, 0xC0, 0x0E, 0x33, 0xC0, 0x0E, 0x33, 0x80, + 0x0F, 0x30, 0x00, 0x07, 0x30, 0x00, 0x07, 0xB0, 0x00, 0x03, 0xF0, 0x00, 0x01, 0xF0, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x37, 0x80, 0x00, 0x33, + 0x80, 0x00, 0x33, 0xC0, 0x00, 0x31, 0xC0, 0x0E, 0x31, 0xC0, 0x1E, 0x31, 0xC0, 0x1E, 0x31, 0xC0, + 0x1E, 0x31, 0xC0, 0x1C, 0x31, 0x80, 0x0C, 0x31, 0x80, 0x0C, 0x33, 0x00, 0x07, 0x36, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"$",4*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x80, 0x11, 0x01, 0x80, 0x31, 0x81, + 0x00, 0x20, 0x83, 0x00, 0x60, 0xC2, 0x00, 0x60, 0xC2, 0x00, 0x60, 0xC6, 0x00, 0x60, 0xC4, 0x00, + 0x60, 0xCC, 0x00, 0x60, 0xC8, 0x00, 0x60, 0xC8, 0x00, 0x60, 0xD8, 0x00, 0x20, 0x90, 0x00, 0x31, + 0xB0, 0x00, 0x11, 0xA1, 0xC0, 0x0E, 0x22, 0x20, 0x00, 0x46, 0x30, 0x00, 0x44, 0x10, 0x00, 0xCC, + 0x18, 0x00, 0x8C, 0x18, 0x00, 0x8C, 0x18, 0x01, 0x0C, 0x18, 0x01, 0x0C, 0x18, 0x03, 0x0C, 0x18, + 0x02, 0x0C, 0x18, 0x02, 0x0C, 0x18, 0x04, 0x04, 0x10, 0x04, 0x06, 0x30, 0x0C, 0x02, 0x20, 0x08, + 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"%",5*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x03, 0x38, 0x00, 0x06, 0x18, + 0x00, 0x0E, 0x1C, 0x00, 0x0E, 0x1C, 0x00, 0x0E, 0x1C, 0x00, 0x0E, 0x1C, 0x00, 0x0E, 0x1C, 0x00, + 0x0E, 0x18, 0x00, 0x0E, 0x38, 0x00, 0x0F, 0x30, 0x00, 0x07, 0x60, 0x00, 0x07, 0xC0, 0x00, 0x07, + 0x87, 0xF0, 0x0F, 0x81, 0xC0, 0x1B, 0x81, 0x80, 0x1B, 0xC1, 0x80, 0x31, 0xC1, 0x80, 0x31, 0xC1, + 0x80, 0x70, 0xE1, 0x00, 0x70, 0xE1, 0x00, 0x70, 0x73, 0x00, 0x70, 0x7A, 0x00, 0x70, 0x3A, 0x00, + 0x70, 0x3C, 0x00, 0x38, 0x1E, 0x08, 0x38, 0x1E, 0x18, 0x1E, 0x37, 0x10, 0x0F, 0xE3, 0xF0, 0x07, + 0xC1, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"&",6*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"'",7*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, + 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"(",8*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*")",9*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, + 0x70, 0x70, 0x70, 0x78, 0x20, 0xF0, 0x7E, 0x23, 0xF0, 0x1F, 0x27, 0xC0, 0x03, 0xAE, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x70, 0x00, 0x03, 0xAE, 0x00, 0x1F, 0x27, 0xC0, 0x7E, 0x23, 0xF0, 0x78, 0x20, + 0xF0, 0x70, 0x70, 0x70, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"*",10*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x7F, + 0xFF, 0xF8, 0x7F, 0xFF, 0xF8, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"+",11*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, /*",",12*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0xFF, 0xF0, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"-",13*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*".",14*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"/",15*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x86, 0x00, 0x03, 0x03, + 0x00, 0x06, 0x01, 0x80, 0x0E, 0x01, 0xC0, 0x0C, 0x00, 0xC0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, + 0x1C, 0x00, 0x60, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, + 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, + 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x1C, 0x00, 0x60, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, + 0x0C, 0x00, 0xC0, 0x0E, 0x01, 0xC0, 0x06, 0x01, 0x80, 0x03, 0x03, 0x00, 0x01, 0x86, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"0",16*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, + 0x00, 0x07, 0xF8, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x7C, 0x00, 0x07, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"1",17*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x03, 0x07, 0x00, 0x04, 0x03, + 0x80, 0x08, 0x01, 0xC0, 0x08, 0x00, 0xE0, 0x18, 0x00, 0xE0, 0x18, 0x00, 0xE0, 0x1C, 0x00, 0xE0, + 0x1C, 0x00, 0xE0, 0x0C, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, + 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, + 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, 0x20, 0x06, 0x00, 0x20, + 0x0C, 0x00, 0x60, 0x18, 0x00, 0x60, 0x30, 0x00, 0xC0, 0x3F, 0xFF, 0xC0, 0x3F, 0xFF, 0xC0, 0x3F, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"2",18*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x03, 0x0E, 0x00, 0x06, 0x03, + 0x00, 0x0C, 0x03, 0x80, 0x0C, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x06, 0x01, 0xC0, + 0x00, 0x01, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x03, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x0C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, + 0x1C, 0x00, 0xC0, 0x1C, 0x01, 0xC0, 0x08, 0x01, 0x80, 0x0C, 0x03, 0x80, 0x06, 0x0F, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"3",19*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x0F, 0x00, 0x00, 0x17, 0x00, 0x00, 0x17, 0x00, 0x00, 0x27, 0x00, 0x00, 0x27, 0x00, + 0x00, 0x47, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x87, 0x00, 0x01, 0x07, 0x00, 0x01, 0x07, 0x00, 0x02, + 0x07, 0x00, 0x06, 0x07, 0x00, 0x04, 0x07, 0x00, 0x0C, 0x07, 0x00, 0x08, 0x07, 0x00, 0x10, 0x07, + 0x00, 0x30, 0x07, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0F, 0x80, 0x00, + 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"4",20*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x07, 0xFF, 0xE0, 0x07, 0xFF, + 0xE0, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x7C, 0x00, 0x0D, 0xFF, 0x00, 0x0B, 0x07, 0x80, 0x0A, + 0x03, 0xC0, 0x0C, 0x01, 0xC0, 0x0C, 0x01, 0xC0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x0C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, + 0x1C, 0x01, 0xC0, 0x18, 0x01, 0xC0, 0x08, 0x01, 0x80, 0x04, 0x03, 0x80, 0x02, 0x07, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"5",21*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC1, 0x80, 0x03, 0x01, + 0x80, 0x06, 0x01, 0xC0, 0x06, 0x01, 0xC0, 0x0C, 0x01, 0xC0, 0x0C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x7E, 0x00, 0x39, 0xFF, 0x80, 0x3B, + 0x83, 0xC0, 0x3B, 0x01, 0xE0, 0x3E, 0x00, 0xE0, 0x3C, 0x00, 0xE0, 0x38, 0x00, 0x70, 0x38, 0x00, + 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x18, 0x00, 0x70, 0x1C, 0x00, 0x70, + 0x1C, 0x00, 0x60, 0x0C, 0x00, 0xE0, 0x0E, 0x00, 0xC0, 0x07, 0x01, 0x80, 0x03, 0x83, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"6",22*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xE0, 0x0F, 0xFF, 0xE0, 0x0F, 0xFF, + 0xC0, 0x0C, 0x00, 0xC0, 0x08, 0x00, 0x80, 0x18, 0x01, 0x00, 0x10, 0x01, 0x00, 0x10, 0x02, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"7",23*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x03, 0x87, 0x00, 0x06, 0x01, + 0x80, 0x0E, 0x01, 0xC0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, + 0x1E, 0x00, 0xE0, 0x1E, 0x00, 0xC0, 0x0F, 0x01, 0xC0, 0x0F, 0x81, 0x80, 0x07, 0xE3, 0x00, 0x01, + 0xFE, 0x00, 0x01, 0xFC, 0x00, 0x07, 0x3F, 0x00, 0x0E, 0x0F, 0x80, 0x0C, 0x07, 0x80, 0x1C, 0x03, + 0xC0, 0x38, 0x01, 0xE0, 0x38, 0x01, 0xE0, 0x38, 0x00, 0xE0, 0x38, 0x00, 0xE0, 0x38, 0x00, 0xE0, + 0x38, 0x00, 0xE0, 0x18, 0x00, 0xC0, 0x1C, 0x01, 0xC0, 0x0E, 0x01, 0x80, 0x07, 0x07, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"8",24*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x03, 0x07, 0x00, 0x06, 0x01, + 0x80, 0x0C, 0x00, 0xC0, 0x1C, 0x00, 0xC0, 0x18, 0x00, 0xE0, 0x38, 0x00, 0x60, 0x38, 0x00, 0x60, + 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0xF0, 0x3C, + 0x00, 0xF0, 0x1C, 0x01, 0xF0, 0x1E, 0x03, 0x70, 0x0F, 0x06, 0x70, 0x07, 0xFC, 0x70, 0x01, 0xF8, + 0x70, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, + 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0x80, 0x0E, 0x03, 0x00, 0x0E, 0x07, 0x00, 0x06, 0x0C, 0x00, 0x01, + 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"9",25*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*":",26*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x00, 0x00, /*";",27*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x01, + 0x80, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"<",28*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF0, 0x7F, 0xFF, + 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"=",29*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, + 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*">",30*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x03, 0xFF, 0x80, 0x07, 0x03, + 0xC0, 0x0C, 0x00, 0xE0, 0x18, 0x00, 0xE0, 0x30, 0x00, 0x70, 0x30, 0x00, 0x70, 0x38, 0x00, 0x70, + 0x3C, 0x00, 0x70, 0x3C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, + 0x03, 0x80, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"?",31*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC1, 0x80, 0x03, 0x80, + 0xC0, 0x03, 0x00, 0x60, 0x06, 0x00, 0x30, 0x0C, 0x0D, 0xB0, 0x0C, 0x1B, 0xB0, 0x1C, 0x33, 0x98, + 0x1C, 0x73, 0x18, 0x18, 0x63, 0x18, 0x38, 0xE3, 0x18, 0x38, 0xE3, 0x18, 0x38, 0xC3, 0x18, 0x39, + 0xC3, 0x18, 0x39, 0xC3, 0x18, 0x39, 0xC2, 0x18, 0x39, 0xC6, 0x18, 0x39, 0xC6, 0x30, 0x39, 0xC6, + 0x30, 0x39, 0xC6, 0x30, 0x19, 0xCE, 0x60, 0x1C, 0xF7, 0xC0, 0x1C, 0x63, 0x80, 0x0C, 0x00, 0x08, + 0x0E, 0x00, 0x10, 0x07, 0x00, 0x30, 0x07, 0x00, 0x60, 0x03, 0xC1, 0xC0, 0x01, 0xFF, 0x80, 0x00, + 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"@",32*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, + 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x98, 0x00, 0x00, 0x98, 0x00, 0x01, 0x9C, 0x00, + 0x01, 0x9C, 0x00, 0x01, 0x0C, 0x00, 0x01, 0x0C, 0x00, 0x03, 0x0E, 0x00, 0x03, 0x0E, 0x00, 0x02, + 0x06, 0x00, 0x02, 0x06, 0x00, 0x06, 0x07, 0x00, 0x06, 0x07, 0x00, 0x04, 0x07, 0x00, 0x07, 0xFF, + 0x00, 0x0C, 0x03, 0x80, 0x0C, 0x03, 0x80, 0x08, 0x03, 0x80, 0x08, 0x01, 0x80, 0x18, 0x01, 0xC0, + 0x18, 0x01, 0xC0, 0x10, 0x01, 0xC0, 0x10, 0x01, 0xC0, 0x30, 0x00, 0xE0, 0x38, 0x01, 0xE0, 0xFC, + 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"A",33*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x0E, 0x07, 0x80, 0x0E, 0x01, + 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, + 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x01, 0xC0, 0x0E, 0x03, 0x80, 0x0E, 0x07, 0x00, 0x0F, + 0xFC, 0x00, 0x0E, 0x03, 0x00, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, + 0x60, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, + 0x0E, 0x00, 0x70, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x01, 0xC0, 0x0E, 0x03, 0x80, 0x7F, + 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"B",34*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x20, 0x01, 0x83, 0xE0, 0x03, 0x00, + 0xE0, 0x06, 0x00, 0x60, 0x0C, 0x00, 0x60, 0x1C, 0x00, 0x30, 0x18, 0x00, 0x30, 0x38, 0x00, 0x10, + 0x38, 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x38, 0x00, 0x10, 0x38, 0x00, 0x10, 0x38, 0x00, 0x20, + 0x1C, 0x00, 0x20, 0x1C, 0x00, 0x40, 0x0E, 0x00, 0xC0, 0x07, 0x83, 0x80, 0x03, 0xFF, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"C",35*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x0E, 0x0C, 0x00, 0x0E, 0x03, + 0x00, 0x0E, 0x01, 0x80, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, + 0x0E, 0x00, 0x60, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, + 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, + 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x60, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, + 0x0E, 0x00, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x03, 0x80, 0x0E, 0x07, 0x00, 0x0E, 0x1C, 0x00, 0x7F, + 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"D",36*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xC0, 0x0E, 0x01, 0xE0, 0x0E, 0x00, + 0x60, 0x0E, 0x00, 0x20, 0x0E, 0x00, 0x30, 0x0E, 0x00, 0x10, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x0E, 0x01, 0x00, 0x0E, 0x01, 0x00, 0x0E, 0x03, 0x00, 0x0F, + 0xFF, 0x00, 0x0E, 0x03, 0x00, 0x0E, 0x03, 0x00, 0x0E, 0x01, 0x00, 0x0E, 0x01, 0x00, 0x0E, 0x01, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x08, + 0x0E, 0x00, 0x08, 0x0E, 0x00, 0x10, 0x0E, 0x00, 0x30, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0xF0, 0x7F, + 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"E",37*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x0E, 0x01, 0xE0, 0x0E, 0x00, + 0x70, 0x0E, 0x00, 0x30, 0x0E, 0x00, 0x10, 0x0E, 0x00, 0x18, 0x0E, 0x00, 0x08, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x80, 0x0E, 0x00, 0x80, 0x0E, 0x00, 0x80, 0x0E, + 0x01, 0x80, 0x0F, 0xFF, 0x80, 0x0E, 0x01, 0x80, 0x0E, 0x01, 0x80, 0x0E, 0x00, 0x80, 0x0E, 0x00, + 0x80, 0x0E, 0x00, 0x80, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x7F, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"F",38*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x80, 0x03, 0x87, 0x80, 0x07, 0x03, + 0x80, 0x0E, 0x01, 0x80, 0x0C, 0x00, 0x80, 0x1C, 0x00, 0xC0, 0x18, 0x00, 0x40, 0x38, 0x00, 0x40, + 0x38, 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x0F, 0xF8, 0x70, 0x03, + 0xC0, 0x70, 0x01, 0xC0, 0x30, 0x01, 0xC0, 0x38, 0x01, 0xC0, 0x38, 0x01, 0xC0, 0x38, 0x01, 0xC0, + 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x0C, 0x01, 0xC0, 0x06, 0x03, 0xC0, 0x03, 0x86, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"G",39*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x03, 0xF8, 0x1C, 0x00, 0xE0, 0x1C, 0x00, + 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, + 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, + 0x00, 0xE0, 0x1F, 0xFF, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, + 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, + 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x7F, + 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"H",40*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x0F, + 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"I",41*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xF8, 0x00, 0x0E, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x18, 0x0E, 0x00, 0x3C, 0x0C, 0x00, 0x3C, 0x1C, 0x00, 0x3C, 0x18, 0x00, 0x1C, 0x30, + 0x00, 0x0F, 0xC0, 0x00, /*"J",42*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC7, 0xF0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, + 0x80, 0x0E, 0x03, 0x00, 0x0E, 0x02, 0x00, 0x0E, 0x06, 0x00, 0x0E, 0x04, 0x00, 0x0E, 0x08, 0x00, + 0x0E, 0x18, 0x00, 0x0E, 0x10, 0x00, 0x0E, 0x20, 0x00, 0x0E, 0x20, 0x00, 0x0E, 0x70, 0x00, 0x0E, + 0xF0, 0x00, 0x0E, 0xB8, 0x00, 0x0F, 0x38, 0x00, 0x0F, 0x1C, 0x00, 0x0E, 0x1C, 0x00, 0x0E, 0x0C, + 0x00, 0x0E, 0x0E, 0x00, 0x0E, 0x06, 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x03, 0x80, + 0x0E, 0x03, 0x80, 0x0E, 0x01, 0x80, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0xC0, 0x0E, 0x00, 0xE0, 0x7F, + 0xC3, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"K",43*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x08, + 0x0E, 0x00, 0x08, 0x0E, 0x00, 0x10, 0x0E, 0x00, 0x30, 0x0E, 0x00, 0x30, 0x0E, 0x00, 0xF0, 0x7F, + 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"L",44*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x01, 0xF8, 0x1C, 0x01, 0xE0, 0x1C, 0x01, + 0xE0, 0x1C, 0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x1E, 0x02, 0xE0, 0x1E, 0x02, 0xE0, 0x1E, 0x02, 0xE0, + 0x16, 0x02, 0xE0, 0x17, 0x06, 0xE0, 0x17, 0x04, 0xE0, 0x17, 0x04, 0xE0, 0x17, 0x04, 0xE0, 0x13, + 0x0C, 0xE0, 0x13, 0x88, 0xE0, 0x13, 0x88, 0xE0, 0x13, 0x88, 0xE0, 0x11, 0x88, 0xE0, 0x11, 0x98, + 0xE0, 0x11, 0xD0, 0xE0, 0x11, 0xD0, 0xE0, 0x11, 0xD0, 0xE0, 0x10, 0xF0, 0xE0, 0x10, 0xF0, 0xE0, + 0x10, 0xE0, 0xE0, 0x10, 0xE0, 0xE0, 0x10, 0x60, 0xE0, 0x10, 0x60, 0xE0, 0x10, 0x60, 0xE0, 0x7C, + 0x43, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"M",45*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x01, 0xFC, 0x1C, 0x00, 0x20, 0x1E, 0x00, + 0x20, 0x1E, 0x00, 0x20, 0x1F, 0x00, 0x20, 0x17, 0x00, 0x20, 0x17, 0x80, 0x20, 0x13, 0x80, 0x20, + 0x13, 0xC0, 0x20, 0x11, 0xE0, 0x20, 0x10, 0xE0, 0x20, 0x10, 0xF0, 0x20, 0x10, 0x70, 0x20, 0x10, + 0x78, 0x20, 0x10, 0x38, 0x20, 0x10, 0x3C, 0x20, 0x10, 0x1C, 0x20, 0x10, 0x1E, 0x20, 0x10, 0x0E, + 0x20, 0x10, 0x0F, 0x20, 0x10, 0x07, 0x20, 0x10, 0x07, 0xA0, 0x10, 0x03, 0xE0, 0x10, 0x01, 0xE0, + 0x10, 0x01, 0xE0, 0x10, 0x00, 0xE0, 0x10, 0x00, 0xE0, 0x10, 0x00, 0x60, 0x10, 0x00, 0x60, 0xFE, + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"N",46*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x01, 0x83, 0x00, 0x03, 0x01, + 0x80, 0x06, 0x00, 0xC0, 0x0E, 0x00, 0xE0, 0x0C, 0x00, 0x60, 0x1C, 0x00, 0x60, 0x1C, 0x00, 0x70, + 0x18, 0x00, 0x30, 0x18, 0x00, 0x30, 0x38, 0x00, 0x38, 0x38, 0x00, 0x38, 0x38, 0x00, 0x38, 0x38, + 0x00, 0x38, 0x38, 0x00, 0x38, 0x38, 0x00, 0x38, 0x38, 0x00, 0x38, 0x38, 0x00, 0x38, 0x38, 0x00, + 0x38, 0x38, 0x00, 0x38, 0x18, 0x00, 0x30, 0x1C, 0x00, 0x30, 0x1C, 0x00, 0x70, 0x0C, 0x00, 0x70, + 0x0C, 0x00, 0x60, 0x06, 0x00, 0xE0, 0x06, 0x00, 0xC0, 0x03, 0x01, 0x80, 0x01, 0x83, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"O",47*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x0E, 0x03, 0x80, 0x0E, 0x01, + 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, + 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, + 0x01, 0xC0, 0x0E, 0x07, 0x80, 0x0F, 0xFE, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x7F, + 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"P",48*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x03, 0x83, 0x00, 0x06, 0x01, + 0x80, 0x0E, 0x00, 0xC0, 0x1C, 0x00, 0xE0, 0x18, 0x00, 0x60, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, + 0x38, 0x00, 0x30, 0x30, 0x00, 0x30, 0x70, 0x00, 0x38, 0x70, 0x00, 0x38, 0x70, 0x00, 0x38, 0x70, + 0x00, 0x38, 0x70, 0x00, 0x38, 0x70, 0x00, 0x38, 0x70, 0x00, 0x38, 0x70, 0x00, 0x38, 0x70, 0x00, + 0x38, 0x70, 0x00, 0x38, 0x70, 0x00, 0x38, 0x31, 0xF0, 0x30, 0x3B, 0xF8, 0x70, 0x3B, 0x1C, 0x70, + 0x1E, 0x0C, 0x60, 0x1C, 0x0E, 0x60, 0x0E, 0x0E, 0xC0, 0x06, 0x07, 0x80, 0x03, 0x87, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x07, 0x90, 0x00, 0x03, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"Q",49*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x0E, 0x07, 0x80, 0x0E, 0x03, + 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, + 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x03, 0x80, 0x0E, + 0x07, 0x00, 0x0F, 0xFC, 0x00, 0x0E, 0x18, 0x00, 0x0E, 0x1C, 0x00, 0x0E, 0x0C, 0x00, 0x0E, 0x0E, + 0x00, 0x0E, 0x0E, 0x00, 0x0E, 0x06, 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x03, 0x80, + 0x0E, 0x03, 0x80, 0x0E, 0x01, 0x80, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0xE0, 0x7F, + 0xC0, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"R",50*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x07, 0x07, 0x80, 0x0C, 0x03, + 0xC0, 0x1C, 0x01, 0xC0, 0x18, 0x00, 0xC0, 0x38, 0x00, 0xC0, 0x38, 0x00, 0x40, 0x38, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x0F, 0xE0, 0x00, 0x07, + 0xF8, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x03, + 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x00, 0xE0, 0x20, 0x00, 0xE0, 0x20, 0x00, 0xE0, 0x30, 0x00, 0xE0, + 0x10, 0x00, 0xE0, 0x18, 0x00, 0xC0, 0x18, 0x01, 0xC0, 0x1C, 0x03, 0x80, 0x1F, 0x07, 0x00, 0x10, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"S",51*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xE0, 0x38, 0x70, 0xE0, 0x30, 0x70, + 0x60, 0x20, 0x70, 0x20, 0x20, 0x70, 0x30, 0x60, 0x70, 0x10, 0x40, 0x70, 0x10, 0x00, 0x70, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"T",52*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x03, 0xF8, 0x1C, 0x00, 0x40, 0x1C, 0x00, + 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, + 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, + 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, + 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x40, + 0x1C, 0x00, 0x40, 0x1C, 0x00, 0x80, 0x0E, 0x00, 0x80, 0x0E, 0x01, 0x00, 0x03, 0x86, 0x00, 0x00, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"U",53*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x01, 0xF8, 0x1E, 0x00, 0x60, 0x0C, 0x00, + 0x40, 0x0E, 0x00, 0x40, 0x0E, 0x00, 0xC0, 0x0E, 0x00, 0xC0, 0x0E, 0x00, 0x80, 0x07, 0x00, 0x80, + 0x07, 0x01, 0x80, 0x07, 0x01, 0x00, 0x07, 0x01, 0x00, 0x03, 0x01, 0x00, 0x03, 0x83, 0x00, 0x03, + 0x82, 0x00, 0x03, 0x82, 0x00, 0x01, 0x82, 0x00, 0x01, 0xC6, 0x00, 0x01, 0xC4, 0x00, 0x01, 0xC4, + 0x00, 0x00, 0xC4, 0x00, 0x00, 0xEC, 0x00, 0x00, 0xE8, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"V",54*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0x7C, 0x30, 0x38, 0x30, 0x30, 0x30, + 0x10, 0x30, 0x38, 0x30, 0x38, 0x38, 0x30, 0x38, 0x38, 0x20, 0x38, 0x38, 0x20, 0x18, 0x38, 0x20, + 0x18, 0x38, 0x20, 0x18, 0x38, 0x60, 0x18, 0x3C, 0x40, 0x1C, 0x3C, 0x40, 0x1C, 0x7C, 0x40, 0x1C, + 0x5C, 0x40, 0x0C, 0x4C, 0x40, 0x0C, 0x4C, 0xC0, 0x0C, 0x4C, 0x80, 0x0C, 0xCE, 0x80, 0x0E, 0x8E, + 0x80, 0x0E, 0x8E, 0x80, 0x06, 0x86, 0x80, 0x06, 0x87, 0x80, 0x07, 0x87, 0x00, 0x07, 0x07, 0x00, + 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x03, 0x03, 0x00, 0x02, 0x02, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"W",55*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x87, 0xF0, 0x0E, 0x00, 0xC0, 0x0E, 0x00, + 0x80, 0x06, 0x01, 0x80, 0x07, 0x01, 0x00, 0x07, 0x03, 0x00, 0x03, 0x82, 0x00, 0x03, 0x86, 0x00, + 0x01, 0xC6, 0x00, 0x01, 0xCC, 0x00, 0x00, 0xEC, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x78, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x30, 0x00, 0x00, 0x38, 0x00, 0x00, 0x78, 0x00, 0x00, 0x5C, 0x00, 0x00, 0xDC, + 0x00, 0x00, 0x8E, 0x00, 0x01, 0x8E, 0x00, 0x01, 0x06, 0x00, 0x03, 0x07, 0x00, 0x03, 0x03, 0x00, + 0x02, 0x03, 0x80, 0x06, 0x03, 0x80, 0x04, 0x01, 0xC0, 0x0C, 0x01, 0xC0, 0x1C, 0x01, 0xE0, 0x7F, + 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"X",56*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x83, 0xF8, 0x1C, 0x00, 0xE0, 0x1C, 0x00, + 0x40, 0x0E, 0x00, 0xC0, 0x0E, 0x00, 0x80, 0x0E, 0x00, 0x80, 0x07, 0x01, 0x00, 0x07, 0x01, 0x00, + 0x03, 0x03, 0x00, 0x03, 0x82, 0x00, 0x03, 0x82, 0x00, 0x01, 0xC4, 0x00, 0x01, 0xC4, 0x00, 0x01, + 0xCC, 0x00, 0x00, 0xE8, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x03, + 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"Y",57*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xF0, 0x0F, 0x00, 0xE0, 0x0E, 0x00, + 0xE0, 0x0C, 0x01, 0xC0, 0x18, 0x01, 0xC0, 0x10, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, + 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, 0x10, + 0x07, 0x00, 0x30, 0x0E, 0x00, 0x20, 0x0E, 0x00, 0x60, 0x1C, 0x00, 0xE0, 0x1C, 0x01, 0xE0, 0x3F, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"Z",58*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"[",59*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, + 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, /*"\",60*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x1F, 0xF8, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x08, 0x00, 0x1F, 0xF8, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"]",61*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0xFC, 0x00, 0x01, + 0xC6, 0x00, 0x03, 0x03, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"^",62*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0xFC, /*"_",63*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x01, 0xE0, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"`",64*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFE, 0x00, 0x07, 0x07, 0x00, 0x0C, 0x03, 0x80, 0x1C, + 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x0F, 0xC0, 0x00, 0xF9, + 0xC0, 0x07, 0x81, 0xC0, 0x0E, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x38, 0x01, 0xC0, + 0x38, 0x01, 0xC0, 0x38, 0x01, 0xC4, 0x38, 0x01, 0xC4, 0x1C, 0x03, 0xC4, 0x0E, 0x0D, 0xC8, 0x07, + 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"a",65*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x1F, 0x00, 0x0E, 0x61, 0x80, 0x0E, 0x80, 0xC0, 0x0F, + 0x00, 0xE0, 0x0F, 0x00, 0xE0, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, + 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, + 0x0E, 0x00, 0x60, 0x0E, 0x00, 0xE0, 0x0F, 0x00, 0xC0, 0x0F, 0x01, 0xC0, 0x0C, 0x83, 0x80, 0x08, + 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"b",66*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x01, 0xC3, 0x00, 0x03, 0x81, 0x80, 0x07, + 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x0E, 0x00, 0x20, 0x0E, 0x00, 0x40, 0x06, 0x00, 0x40, 0x07, 0x00, 0x80, 0x01, 0xC1, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"c",67*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x0F, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, + 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, + 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0xF9, 0xC0, 0x03, 0x85, 0xC0, 0x07, 0x03, 0xC0, 0x06, + 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, + 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, + 0x0C, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x03, 0xC0, 0x07, 0x07, 0xC0, 0x03, 0x8D, 0xF8, 0x00, + 0xF1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"d",68*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x01, 0x87, 0x00, 0x03, 0x03, 0x80, 0x06, + 0x01, 0xC0, 0x0E, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, + 0xE0, 0x1F, 0xFF, 0xE0, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x0C, 0x00, 0x40, 0x0E, 0x00, 0x40, 0x06, 0x00, 0x80, 0x07, 0x01, 0x00, 0x03, 0xC3, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"e",69*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x18, 0x30, 0x00, 0x70, + 0x38, 0x00, 0x60, 0x38, 0x00, 0xE0, 0x38, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, + 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x0F, + 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"f",70*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x38, 0x03, 0x07, 0xF8, 0x06, 0x03, 0x98, 0x0E, + 0x03, 0x80, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, + 0xC0, 0x0E, 0x01, 0x80, 0x06, 0x03, 0x80, 0x03, 0x07, 0x00, 0x0E, 0xFC, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x07, 0xFF, 0x80, 0x0E, 0x3F, 0xE0, 0x1C, 0x00, 0xF0, 0x38, + 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x1C, 0x00, 0xE0, 0x0F, 0x03, + 0xC0, 0x01, 0xFE, 0x00, /*"g",71*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x3E, 0x00, 0x0E, 0x43, 0x80, 0x0F, 0x81, 0x80, 0x0F, + 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, + 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, + 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x3F, + 0x87, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"h",72*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x07, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"i",73*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x07, + 0xC0, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x7F, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, + 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, + 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, + 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, + 0x03, 0x80, 0x00, 0x03, 0x80, 0x1C, 0x03, 0x00, 0x1C, 0x07, 0x00, 0x1C, 0x06, 0x00, 0x0E, 0x1C, + 0x00, 0x07, 0xF0, 0x00, /*"j",74*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x07, 0xF0, 0x0E, 0x01, 0x80, 0x0E, 0x03, 0x00, 0x0E, + 0x06, 0x00, 0x0E, 0x04, 0x00, 0x0E, 0x0C, 0x00, 0x0E, 0x18, 0x00, 0x0E, 0x30, 0x00, 0x0E, 0x78, + 0x00, 0x0E, 0xDC, 0x00, 0x0F, 0x9C, 0x00, 0x0F, 0x0E, 0x00, 0x0E, 0x06, 0x00, 0x0E, 0x07, 0x00, + 0x0E, 0x03, 0x80, 0x0E, 0x03, 0x80, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0xC0, 0x0E, 0x00, 0xE0, 0x3F, + 0x83, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"k",75*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x07, 0xF8, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x0F, + 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"l",76*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0xF9, 0xF1, 0xE0, 0x3A, 0x3E, 0x30, 0x3C, 0x3C, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, + 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0xFE, + 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"m",77*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x0E, 0x43, 0x80, 0x0E, 0x83, 0x80, 0x0F, + 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, + 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, + 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x3F, + 0x87, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"n",78*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x03, 0x87, 0x00, 0x07, 0x01, 0x80, 0x0E, + 0x01, 0xC0, 0x0C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x18, 0x00, 0x60, 0x38, 0x00, 0x70, 0x38, 0x00, + 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x18, 0x00, 0x70, + 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x0E, 0x00, 0xC0, 0x06, 0x01, 0x80, 0x03, 0x87, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"o",79*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3E, 0x00, 0x7E, 0x43, 0x80, 0x0E, 0x81, 0xC0, 0x0F, + 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, + 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, + 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0F, 0x00, 0xC0, 0x0F, 0x01, 0xC0, 0x0E, 0xC3, 0x80, 0x0E, + 0x3E, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x7F, 0xC0, 0x00, /*"p",80*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x40, 0x03, 0x04, 0xC0, 0x06, 0x03, 0xC0, 0x0C, + 0x03, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x38, 0x01, 0xC0, 0x38, 0x01, 0xC0, 0x38, 0x01, + 0xC0, 0x38, 0x01, 0xC0, 0x38, 0x01, 0xC0, 0x38, 0x01, 0xC0, 0x38, 0x01, 0xC0, 0x38, 0x01, 0xC0, + 0x18, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x03, 0xC0, 0x0E, 0x03, 0xC0, 0x07, 0x0D, 0xC0, 0x01, + 0xF1, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, + 0xC0, 0x00, 0x0F, 0xF8, /*"q",81*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x7F, 0x83, 0xE0, 0x03, 0x8C, 0x70, 0x03, 0x98, 0x70, 0x03, + 0xB0, 0x70, 0x03, 0xA0, 0x70, 0x03, 0xC0, 0x00, 0x03, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, + 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, + 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x7F, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"r",82*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0xC3, 0xC0, 0x03, 0x81, 0xC0, 0x07, + 0x00, 0xC0, 0x07, 0x00, 0x40, 0x07, 0x00, 0x40, 0x07, 0x80, 0x00, 0x03, 0xC0, 0x00, 0x03, 0xF8, + 0x00, 0x00, 0xFE, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x0F, 0xC0, 0x00, 0x03, 0xE0, 0x08, 0x01, 0xE0, + 0x08, 0x00, 0xE0, 0x0C, 0x00, 0xE0, 0x0C, 0x00, 0xE0, 0x0E, 0x01, 0xC0, 0x0F, 0x83, 0x80, 0x08, + 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"s",83*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0xE0, 0x00, 0x03, 0xE0, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, + 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x20, 0x00, 0xE0, 0x20, 0x00, 0x60, 0x40, 0x00, 0x70, 0x80, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"t",84*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x40, 0x7E, 0x0F, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, + 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, + 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, + 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x03, 0xC0, 0x07, 0x03, 0xC0, 0x07, 0x8D, 0xF8, 0x01, + 0xF1, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"u",85*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x83, 0xF0, 0x0E, 0x00, 0xC0, 0x06, 0x00, 0x80, 0x07, + 0x00, 0x80, 0x07, 0x01, 0x80, 0x03, 0x01, 0x00, 0x03, 0x01, 0x00, 0x03, 0x83, 0x00, 0x01, 0x82, + 0x00, 0x01, 0x82, 0x00, 0x01, 0xC6, 0x00, 0x00, 0xC4, 0x00, 0x00, 0xC4, 0x00, 0x00, 0xEC, 0x00, + 0x00, 0x68, 0x00, 0x00, 0x68, 0x00, 0x00, 0x78, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"v",86*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0xFC, 0x38, 0x70, 0x30, 0x38, 0x30, 0x30, 0x38, + 0x30, 0x20, 0x18, 0x38, 0x20, 0x18, 0x38, 0x60, 0x1C, 0x38, 0x40, 0x1C, 0x78, 0x40, 0x0C, 0x5C, + 0x40, 0x0C, 0x4C, 0x40, 0x0E, 0xCC, 0x80, 0x0E, 0xCC, 0x80, 0x06, 0x8C, 0x80, 0x07, 0x8E, 0x80, + 0x07, 0x87, 0x00, 0x07, 0x87, 0x00, 0x03, 0x07, 0x00, 0x03, 0x07, 0x00, 0x03, 0x02, 0x00, 0x03, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"w",87*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC7, 0xE0, 0x07, 0x81, 0x80, 0x03, 0x81, 0x00, 0x01, + 0x83, 0x00, 0x01, 0xC2, 0x00, 0x00, 0xC4, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x78, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x8E, 0x00, + 0x01, 0x86, 0x00, 0x01, 0x03, 0x00, 0x03, 0x03, 0x80, 0x06, 0x01, 0x80, 0x0E, 0x01, 0xC0, 0x3F, + 0x87, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"x",88*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x87, 0xF0, 0x0E, 0x01, 0xC0, 0x06, 0x01, 0x80, 0x06, + 0x01, 0x00, 0x07, 0x01, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x00, 0x03, 0x82, 0x00, 0x01, 0x86, + 0x00, 0x01, 0x86, 0x00, 0x00, 0xC4, 0x00, 0x00, 0xCC, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x78, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x1C, 0xC0, 0x00, 0x1F, 0x80, + 0x00, 0x0F, 0x00, 0x00, /*"y",89*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xC0, 0x0E, 0x01, 0xC0, 0x18, 0x03, 0x80, 0x18, + 0x07, 0x00, 0x10, 0x07, 0x00, 0x10, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x20, + 0x03, 0x80, 0x20, 0x07, 0x00, 0x60, 0x07, 0x00, 0x40, 0x0E, 0x00, 0xC0, 0x1C, 0x01, 0xC0, 0x1F, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"z",90*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x03, 0x80, 0x00, + 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"{",91*/ + + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x30, 0x00, /*"|",92*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, + 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, + 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x18, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, + 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, + 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"}",93*/ + + 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x0F, 0xE0, 0x00, 0x18, 0xF0, 0x08, 0x10, 0x78, 0x08, 0x20, + 0x3C, 0x10, 0x20, 0x1E, 0x30, 0x00, 0x0F, 0xE0, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /*"~",94*/ +}; + +const esp_painter_basic_font_t esp_painter_basic_font_44 = { + .bitmap = bitmap, + .width = 22, + .height = 44, +}; + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_48.c b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_48.c new file mode 100644 index 0000000..d55b370 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/font/basic_font_48.c @@ -0,0 +1,971 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "sdkconfig.h" + +#include "esp_painter_font.h" + +#if CONFIG_ESP_PAINTER_BASIC_FONT_48 + +static const uint8_t bitmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*" ",0*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"!",1*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0xF0, 0xF0, 0x01, + 0xF1, 0xF0, 0x01, 0xF1, 0xF0, 0x03, 0xE3, 0xE0, 0x03, 0xC3, 0xC0, 0x07, 0x87, 0x80, 0x07, 0x07, + 0x00, 0x0E, 0x0E, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x18, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*""",2*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x60, 0x01, 0x80, 0x60, 0x01, 0x80, + 0x60, 0x01, 0x00, 0x40, 0x01, 0x00, 0x40, 0x01, 0x00, 0x40, 0x01, 0x00, 0x40, 0x01, 0x00, 0x40, + 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x03, 0x00, 0xC0, 0x03, + 0x00, 0xC0, 0x03, 0x00, 0xC0, 0x03, 0x00, 0xC0, 0x03, 0x00, 0xC0, 0x03, 0x00, 0xC0, 0x03, 0x00, + 0xC0, 0x02, 0x00, 0x80, 0x02, 0x00, 0x80, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, + 0x3F, 0xFF, 0xFC, 0x06, 0x01, 0x80, 0x06, 0x01, 0x80, 0x06, 0x01, 0x80, 0x06, 0x01, 0x80, 0x06, + 0x01, 0x80, 0x06, 0x01, 0x80, 0x06, 0x01, 0x80, 0x06, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"#",3*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x7F, 0x00, 0x01, 0xD9, 0xC0, 0x03, 0x18, + 0xE0, 0x07, 0x18, 0x60, 0x06, 0x18, 0x70, 0x0E, 0x18, 0x70, 0x0E, 0x18, 0xF0, 0x0E, 0x18, 0xF0, + 0x0E, 0x18, 0xE0, 0x0F, 0x18, 0x00, 0x07, 0x98, 0x00, 0x07, 0xD8, 0x00, 0x03, 0xF8, 0x00, 0x01, + 0xF8, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, + 0xC0, 0x00, 0x1B, 0xE0, 0x00, 0x19, 0xE0, 0x00, 0x18, 0xF0, 0x00, 0x18, 0xF0, 0x0E, 0x18, 0x70, + 0x1E, 0x18, 0x70, 0x1E, 0x18, 0x70, 0x1E, 0x18, 0x70, 0x1C, 0x18, 0x70, 0x0C, 0x18, 0x60, 0x0C, + 0x18, 0xE0, 0x06, 0x18, 0xC0, 0x03, 0x1B, 0x80, 0x00, 0xFE, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"$",4*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x20, 0x18, 0x80, 0x60, 0x30, 0xC0, + 0x40, 0x20, 0x40, 0xC0, 0x60, 0x60, 0x80, 0x60, 0x60, 0x80, 0x60, 0x61, 0x80, 0x60, 0x61, 0x00, + 0x60, 0x63, 0x00, 0x60, 0x62, 0x00, 0x60, 0x62, 0x00, 0x60, 0x66, 0x00, 0x60, 0x64, 0x00, 0x30, + 0x4C, 0x00, 0x30, 0xC8, 0x00, 0x19, 0x88, 0x00, 0x0F, 0x18, 0xF0, 0x00, 0x11, 0x98, 0x00, 0x33, + 0x0C, 0x00, 0x23, 0x0C, 0x00, 0x22, 0x04, 0x00, 0x66, 0x06, 0x00, 0x46, 0x06, 0x00, 0x46, 0x06, + 0x00, 0x86, 0x06, 0x00, 0x86, 0x06, 0x01, 0x86, 0x06, 0x01, 0x06, 0x06, 0x01, 0x06, 0x06, 0x02, + 0x02, 0x04, 0x02, 0x03, 0x0C, 0x06, 0x01, 0x98, 0x04, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"%",5*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x03, 0x38, 0x00, 0x07, 0x18, + 0x00, 0x06, 0x1C, 0x00, 0x0E, 0x1C, 0x00, 0x0E, 0x1C, 0x00, 0x0E, 0x1C, 0x00, 0x0E, 0x1C, 0x00, + 0x0E, 0x1C, 0x00, 0x0E, 0x18, 0x00, 0x0E, 0x38, 0x00, 0x0F, 0x30, 0x00, 0x07, 0x70, 0x00, 0x07, + 0x60, 0x00, 0x07, 0x80, 0x00, 0x07, 0x87, 0xF8, 0x0B, 0x80, 0xE0, 0x1B, 0xC0, 0xC0, 0x39, 0xC0, + 0xC0, 0x31, 0xE0, 0xC0, 0x31, 0xE0, 0xC0, 0x70, 0xF0, 0x80, 0x70, 0xF0, 0x80, 0x70, 0x79, 0x80, + 0x70, 0x39, 0x80, 0x70, 0x3D, 0x00, 0x70, 0x1F, 0x00, 0x78, 0x0E, 0x00, 0x38, 0x0F, 0x02, 0x3C, + 0x0F, 0x86, 0x1E, 0x1B, 0xC4, 0x0F, 0xF1, 0xFC, 0x07, 0xC0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"&",6*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, + 0x80, 0x00, 0x1F, 0x80, 0x00, 0x0F, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"'",7*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"(",8*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0x80, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*")",9*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x30, 0x38, 0x38, 0x7C, 0x38, 0x7C, 0x7E, 0x10, 0xFC, 0x3F, 0x11, 0xF8, 0x0F, + 0x93, 0xE0, 0x01, 0xD7, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x01, 0xD7, 0x00, 0x0F, 0x93, + 0xE0, 0x3F, 0x11, 0xF8, 0x7E, 0x10, 0xFC, 0x7C, 0x38, 0x7C, 0x70, 0x38, 0x1C, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"*",10*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"+",11*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x1F, + 0x80, 0x00, 0x1F, 0x80, 0x00, 0x0F, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, /*",",12*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"-",13*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x1F, + 0x80, 0x00, 0x1F, 0x80, 0x00, 0x1F, 0x80, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*".",14*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x60, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"/",15*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x01, 0xC3, 0x80, 0x03, 0x80, + 0xC0, 0x03, 0x00, 0xE0, 0x06, 0x00, 0x60, 0x0E, 0x00, 0x70, 0x0C, 0x00, 0x30, 0x1C, 0x00, 0x38, + 0x1C, 0x00, 0x38, 0x1C, 0x00, 0x38, 0x18, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, + 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, + 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x18, 0x00, 0x18, 0x1C, 0x00, 0x38, + 0x1C, 0x00, 0x38, 0x1C, 0x00, 0x38, 0x0C, 0x00, 0x30, 0x0E, 0x00, 0x70, 0x06, 0x00, 0x60, 0x03, + 0x00, 0xE0, 0x03, 0x80, 0xC0, 0x00, 0xC3, 0x80, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"0",16*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, + 0x00, 0x03, 0xFC, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x3E, 0x00, 0x03, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"1",17*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x01, 0x83, 0xC0, 0x06, 0x00, + 0xE0, 0x0C, 0x00, 0x70, 0x0C, 0x00, 0x70, 0x1C, 0x00, 0x38, 0x1C, 0x00, 0x38, 0x1C, 0x00, 0x38, + 0x1E, 0x00, 0x38, 0x1E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, + 0x03, 0x80, 0x00, 0x07, 0x00, 0x08, 0x06, 0x00, 0x08, 0x0C, 0x00, 0x18, 0x1C, 0x00, 0x10, 0x38, + 0x00, 0x70, 0x3F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF0, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"2",18*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x01, 0x83, 0x80, 0x02, 0x01, + 0xC0, 0x04, 0x00, 0xE0, 0x0C, 0x00, 0xE0, 0x0C, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, + 0x06, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xC0, 0x00, + 0x01, 0x80, 0x00, 0x07, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x07, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0x70, 0x00, 0x00, 0x30, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x0C, 0x00, 0x38, 0x1E, 0x00, 0x38, 0x1E, 0x00, 0x38, 0x1E, 0x00, 0x30, 0x1C, 0x00, 0x70, 0x0C, + 0x00, 0x60, 0x06, 0x00, 0xC0, 0x03, 0x03, 0x80, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"3",19*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, + 0x80, 0x00, 0x07, 0x80, 0x00, 0x0F, 0x80, 0x00, 0x0B, 0x80, 0x00, 0x13, 0x80, 0x00, 0x13, 0x80, + 0x00, 0x23, 0x80, 0x00, 0x63, 0x80, 0x00, 0x43, 0x80, 0x00, 0xC3, 0x80, 0x00, 0x83, 0x80, 0x01, + 0x03, 0x80, 0x03, 0x03, 0x80, 0x02, 0x03, 0x80, 0x06, 0x03, 0x80, 0x04, 0x03, 0x80, 0x0C, 0x03, + 0x80, 0x18, 0x03, 0x80, 0x18, 0x03, 0x80, 0x30, 0x03, 0x80, 0x3F, 0xFF, 0xFC, 0x00, 0x03, 0x80, + 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, + 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x07, 0xC0, 0x00, 0x7F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"4",20*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xF0, 0x07, 0xFF, 0xF0, 0x07, 0xFF, + 0xF0, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x7F, 0x00, 0x0C, + 0xFF, 0x80, 0x0D, 0x81, 0xE0, 0x0F, 0x00, 0xE0, 0x0E, 0x00, 0x70, 0x0C, 0x00, 0x70, 0x00, 0x00, + 0x78, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x0C, 0x00, 0x38, + 0x1E, 0x00, 0x38, 0x1E, 0x00, 0x38, 0x1E, 0x00, 0x30, 0x1C, 0x00, 0x70, 0x0C, 0x00, 0x70, 0x0C, + 0x00, 0xE0, 0x06, 0x00, 0xC0, 0x03, 0x83, 0x80, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"5",21*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x80, 0x00, 0xE0, 0x40, 0x01, 0x80, + 0x60, 0x03, 0x00, 0x70, 0x06, 0x00, 0x70, 0x06, 0x00, 0x70, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x3F, 0x00, 0x38, + 0xFF, 0xC0, 0x39, 0xC1, 0xE0, 0x3B, 0x00, 0x70, 0x3E, 0x00, 0x38, 0x3C, 0x00, 0x38, 0x3C, 0x00, + 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, + 0x18, 0x00, 0x1C, 0x1C, 0x00, 0x1C, 0x1C, 0x00, 0x18, 0x0C, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x07, + 0x00, 0x30, 0x03, 0x80, 0x60, 0x01, 0xC1, 0xC0, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"6",22*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF8, 0x0F, 0xFF, 0xF8, 0x0F, 0xFF, + 0xF0, 0x0E, 0x00, 0x30, 0x0C, 0x00, 0x20, 0x18, 0x00, 0x40, 0x18, 0x00, 0xC0, 0x10, 0x00, 0x80, + 0x00, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x70, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, + 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"7",23*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x03, 0x81, 0xC0, 0x07, 0x00, + 0xE0, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x1C, 0x00, 0x38, 0x1C, 0x00, 0x38, 0x1C, 0x00, 0x38, + 0x1C, 0x00, 0x38, 0x1E, 0x00, 0x38, 0x1E, 0x00, 0x30, 0x0F, 0x00, 0x70, 0x07, 0xC0, 0xE0, 0x07, + 0xE1, 0xC0, 0x01, 0xFB, 0x80, 0x00, 0xFE, 0x00, 0x03, 0xBF, 0x00, 0x07, 0x0F, 0xC0, 0x0E, 0x03, + 0xE0, 0x0C, 0x01, 0xE0, 0x1C, 0x00, 0xF0, 0x1C, 0x00, 0x70, 0x38, 0x00, 0x78, 0x38, 0x00, 0x38, + 0x38, 0x00, 0x38, 0x38, 0x00, 0x38, 0x38, 0x00, 0x38, 0x18, 0x00, 0x30, 0x1C, 0x00, 0x70, 0x0C, + 0x00, 0x60, 0x06, 0x00, 0xC0, 0x03, 0x81, 0x80, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"8",24*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x03, 0x83, 0x00, 0x06, 0x01, + 0x80, 0x0E, 0x00, 0xC0, 0x1C, 0x00, 0x60, 0x1C, 0x00, 0x60, 0x18, 0x00, 0x70, 0x38, 0x00, 0x30, + 0x38, 0x00, 0x30, 0x38, 0x00, 0x38, 0x38, 0x00, 0x38, 0x38, 0x00, 0x38, 0x38, 0x00, 0x38, 0x38, + 0x00, 0x38, 0x3C, 0x00, 0x78, 0x1C, 0x00, 0xF8, 0x1C, 0x00, 0xB8, 0x1E, 0x01, 0xB8, 0x0F, 0x87, + 0x38, 0x07, 0xFE, 0x38, 0x01, 0xF8, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x60, 0x00, 0x00, 0xE0, 0x0E, 0x00, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, + 0x03, 0x80, 0x0E, 0x07, 0x00, 0x06, 0x0E, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"9",25*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*":",26*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*";",27*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x70, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x80, 0x00, 0x01, 0xC0, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0x70, 0x00, 0x00, 0x38, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"<",28*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3F, 0xFF, 0xFC, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"=",29*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x03, 0x80, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x60, 0x00, 0x00, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x01, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*">",30*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x03, 0xFF, 0x80, 0x07, 0x03, + 0xC0, 0x0C, 0x01, 0xE0, 0x18, 0x00, 0xF0, 0x10, 0x00, 0x70, 0x30, 0x00, 0x78, 0x30, 0x00, 0x78, + 0x3C, 0x00, 0x78, 0x3E, 0x00, 0x78, 0x3E, 0x00, 0x78, 0x1E, 0x00, 0x78, 0x00, 0x00, 0xF0, 0x00, + 0x01, 0xF0, 0x00, 0x03, 0xE0, 0x00, 0x07, 0x80, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"?",31*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xE0, 0xC0, 0x01, 0x80, + 0x60, 0x03, 0x00, 0x10, 0x07, 0x00, 0x18, 0x06, 0x00, 0x0C, 0x0C, 0x07, 0x6C, 0x0C, 0x0C, 0xEC, + 0x1C, 0x1C, 0xE6, 0x1C, 0x18, 0xE6, 0x18, 0x38, 0xC6, 0x38, 0x30, 0xC6, 0x38, 0x70, 0xC6, 0x38, + 0x70, 0xC6, 0x38, 0x60, 0xC6, 0x38, 0x61, 0xC6, 0x38, 0xE1, 0xC6, 0x38, 0xE1, 0x86, 0x38, 0xE1, + 0x86, 0x38, 0xE1, 0x8C, 0x38, 0xE1, 0x8C, 0x38, 0xE3, 0x8C, 0x1C, 0xE3, 0x98, 0x1C, 0x65, 0x98, + 0x1C, 0x7D, 0xF0, 0x0C, 0x38, 0xE0, 0x0E, 0x00, 0x06, 0x0E, 0x00, 0x04, 0x07, 0x00, 0x0C, 0x03, + 0x80, 0x38, 0x01, 0xE0, 0xF0, 0x00, 0xFF, 0xE0, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"@",32*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x7C, + 0x00, 0x00, 0x7C, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x4C, 0x00, 0x00, 0xCC, 0x00, 0x00, 0xCE, 0x00, + 0x00, 0x8E, 0x00, 0x00, 0x86, 0x00, 0x01, 0x86, 0x00, 0x01, 0x87, 0x00, 0x01, 0x07, 0x00, 0x01, + 0x03, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x80, 0x02, 0x03, 0x80, 0x02, 0x03, 0x80, 0x06, 0x01, + 0x80, 0x07, 0xFF, 0xC0, 0x04, 0x01, 0xC0, 0x04, 0x01, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xE0, + 0x08, 0x00, 0xE0, 0x08, 0x00, 0xE0, 0x18, 0x00, 0xE0, 0x18, 0x00, 0x70, 0x18, 0x00, 0x70, 0x18, + 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x78, 0xFE, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"A",33*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x0E, 0x03, 0xC0, 0x0E, 0x00, + 0xE0, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, + 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, + 0x00, 0xE0, 0x0E, 0x03, 0x80, 0x0F, 0xFE, 0x00, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0x60, 0x0E, 0x00, + 0x70, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, + 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x38, 0x0E, + 0x00, 0x38, 0x0E, 0x00, 0x70, 0x0E, 0x01, 0xC0, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"B",34*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xE0, 0xF8, 0x01, 0x80, + 0x38, 0x03, 0x00, 0x18, 0x07, 0x00, 0x18, 0x06, 0x00, 0x08, 0x0E, 0x00, 0x0C, 0x0C, 0x00, 0x04, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x18, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x1C, 0x00, 0x04, + 0x1C, 0x00, 0x04, 0x1C, 0x00, 0x08, 0x0E, 0x00, 0x08, 0x0E, 0x00, 0x18, 0x07, 0x00, 0x30, 0x07, + 0x80, 0x60, 0x03, 0xC0, 0xE0, 0x00, 0xFF, 0x80, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"C",35*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x0F, 0x07, 0x00, 0x0E, 0x01, + 0x80, 0x0E, 0x00, 0xC0, 0x0E, 0x00, 0x60, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x30, 0x0E, 0x00, 0x38, + 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, + 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, + 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x18, 0x0E, 0x00, 0x38, + 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x30, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0xE0, 0x0E, + 0x01, 0xC0, 0x0E, 0x03, 0x80, 0x0E, 0x0F, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"D",36*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, + 0x70, 0x0E, 0x00, 0x30, 0x0E, 0x00, 0x10, 0x0E, 0x00, 0x18, 0x0E, 0x00, 0x08, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x80, 0x0E, 0x00, 0x80, 0x0E, 0x00, 0x80, 0x0E, + 0x00, 0x80, 0x0E, 0x01, 0x80, 0x0F, 0xFF, 0x80, 0x0E, 0x03, 0x80, 0x0E, 0x01, 0x80, 0x0E, 0x00, + 0x80, 0x0E, 0x00, 0x80, 0x0E, 0x00, 0x80, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x04, 0x0E, 0x00, 0x04, 0x0E, 0x00, 0x08, 0x0E, 0x00, 0x08, 0x0E, + 0x00, 0x18, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0xF0, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"E",37*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF8, 0x0E, 0x00, 0x78, 0x0E, 0x00, + 0x3C, 0x0E, 0x00, 0x0C, 0x0E, 0x00, 0x04, 0x0E, 0x00, 0x06, 0x0E, 0x00, 0x02, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x40, 0x0E, 0x00, 0x40, 0x0E, 0x00, 0x40, 0x0E, + 0x00, 0x40, 0x0E, 0x00, 0xC0, 0x0F, 0xFF, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0xC0, 0x0E, 0x00, + 0x40, 0x0E, 0x00, 0x40, 0x0E, 0x00, 0x40, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"F",38*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x20, 0x01, 0xC3, 0xE0, 0x03, 0x00, + 0xE0, 0x06, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x0C, 0x00, 0x20, 0x1C, 0x00, 0x30, 0x18, 0x00, 0x30, + 0x38, 0x00, 0x10, 0x38, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x03, + 0xFE, 0x70, 0x00, 0xF8, 0x70, 0x00, 0x70, 0x70, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, + 0x38, 0x00, 0x70, 0x18, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x0C, 0x00, 0x70, 0x06, + 0x00, 0x70, 0x07, 0x00, 0xF0, 0x01, 0xC1, 0x80, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"G",39*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x83, 0xFE, 0x1C, 0x00, 0x70, 0x1C, 0x00, + 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, + 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, + 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1F, 0xFF, 0xF0, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, + 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, + 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, + 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0xFF, 0x83, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"H",40*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x1C, 0x00, 0x00, 0x1C, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"I",41*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x03, 0x80, 0x00, 0x03, + 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, + 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, + 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, + 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, + 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, + 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x00, 0x03, 0x80, 0x18, 0x03, 0x00, 0x3C, 0x07, + 0x00, 0x3C, 0x06, 0x00, 0x3C, 0x0C, 0x00, 0x1C, 0x18, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, /*"J",42*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC3, 0xFC, 0x0E, 0x00, 0xF0, 0x0E, 0x00, + 0xE0, 0x0E, 0x00, 0xC0, 0x0E, 0x01, 0x80, 0x0E, 0x01, 0x00, 0x0E, 0x03, 0x00, 0x0E, 0x06, 0x00, + 0x0E, 0x04, 0x00, 0x0E, 0x0C, 0x00, 0x0E, 0x08, 0x00, 0x0E, 0x10, 0x00, 0x0E, 0x30, 0x00, 0x0E, + 0x38, 0x00, 0x0E, 0x78, 0x00, 0x0E, 0xF8, 0x00, 0x0E, 0x9C, 0x00, 0x0F, 0x1C, 0x00, 0x0F, 0x0E, + 0x00, 0x0E, 0x0E, 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x03, 0x80, 0x0E, 0x03, 0x80, + 0x0E, 0x03, 0x80, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, + 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x78, 0x7F, 0xC1, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"K",43*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xE0, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x07, 0x00, 0x04, 0x07, 0x00, 0x04, 0x07, 0x00, 0x0C, 0x07, 0x00, 0x08, 0x07, + 0x00, 0x18, 0x07, 0x00, 0x38, 0x07, 0x00, 0x78, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"L",44*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x7E, 0x1C, 0x00, 0x78, 0x1C, 0x00, + 0x78, 0x1E, 0x00, 0x78, 0x1E, 0x00, 0xB8, 0x1E, 0x00, 0xB8, 0x1E, 0x00, 0xB8, 0x1E, 0x00, 0xB8, + 0x17, 0x01, 0xB8, 0x17, 0x01, 0x38, 0x17, 0x01, 0x38, 0x17, 0x01, 0x38, 0x13, 0x81, 0x38, 0x13, + 0x83, 0x38, 0x13, 0x82, 0x38, 0x13, 0x82, 0x38, 0x13, 0x82, 0x38, 0x11, 0xC6, 0x38, 0x11, 0xC4, + 0x38, 0x11, 0xC4, 0x38, 0x11, 0xC4, 0x38, 0x10, 0xEC, 0x38, 0x10, 0xEC, 0x38, 0x10, 0xE8, 0x38, + 0x10, 0xE8, 0x38, 0x10, 0xF8, 0x38, 0x10, 0x78, 0x38, 0x10, 0x70, 0x38, 0x10, 0x70, 0x38, 0x10, + 0x70, 0x38, 0x10, 0x30, 0x38, 0x10, 0x30, 0x38, 0x7C, 0x21, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"M",45*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0xFE, 0x0E, 0x00, 0x10, 0x0F, 0x00, + 0x10, 0x0F, 0x00, 0x10, 0x0F, 0x80, 0x10, 0x0B, 0x80, 0x10, 0x0B, 0xC0, 0x10, 0x09, 0xC0, 0x10, + 0x09, 0xE0, 0x10, 0x08, 0xE0, 0x10, 0x08, 0xE0, 0x10, 0x08, 0x70, 0x10, 0x08, 0x70, 0x10, 0x08, + 0x38, 0x10, 0x08, 0x38, 0x10, 0x08, 0x1C, 0x10, 0x08, 0x1C, 0x10, 0x08, 0x0E, 0x10, 0x08, 0x0E, + 0x10, 0x08, 0x07, 0x10, 0x08, 0x07, 0x10, 0x08, 0x03, 0x90, 0x08, 0x03, 0x90, 0x08, 0x03, 0xD0, + 0x08, 0x01, 0xD0, 0x08, 0x01, 0xF0, 0x08, 0x00, 0xF0, 0x08, 0x00, 0xF0, 0x08, 0x00, 0x70, 0x08, + 0x00, 0x70, 0x08, 0x00, 0x30, 0x08, 0x00, 0x30, 0x7F, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"N",46*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0xC1, 0x80, 0x01, 0x80, + 0x40, 0x03, 0x00, 0x60, 0x06, 0x00, 0x30, 0x0E, 0x00, 0x38, 0x0C, 0x00, 0x18, 0x1C, 0x00, 0x18, + 0x1C, 0x00, 0x1C, 0x1C, 0x00, 0x1C, 0x18, 0x00, 0x0C, 0x38, 0x00, 0x0E, 0x38, 0x00, 0x0E, 0x38, + 0x00, 0x0E, 0x38, 0x00, 0x0E, 0x38, 0x00, 0x0E, 0x38, 0x00, 0x0E, 0x38, 0x00, 0x0E, 0x38, 0x00, + 0x0E, 0x38, 0x00, 0x0E, 0x38, 0x00, 0x0E, 0x38, 0x00, 0x0E, 0x18, 0x00, 0x0C, 0x1C, 0x00, 0x0C, + 0x1C, 0x00, 0x1C, 0x0C, 0x00, 0x1C, 0x0C, 0x00, 0x18, 0x0E, 0x00, 0x38, 0x06, 0x00, 0x30, 0x03, + 0x00, 0x60, 0x01, 0x80, 0xC0, 0x00, 0xC1, 0x80, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"O",47*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x0E, 0x01, 0xC0, 0x0E, 0x00, + 0x70, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, + 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x38, 0x0E, + 0x00, 0x30, 0x0E, 0x00, 0x70, 0x0E, 0x01, 0xC0, 0x0F, 0xFF, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"P",48*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x01, 0xC3, 0x80, 0x03, 0x00, + 0xC0, 0x07, 0x00, 0x60, 0x06, 0x00, 0x60, 0x0E, 0x00, 0x30, 0x0C, 0x00, 0x30, 0x1C, 0x00, 0x38, + 0x1C, 0x00, 0x38, 0x18, 0x00, 0x18, 0x18, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, + 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, + 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x18, 0xF8, 0x18, + 0x1D, 0xFC, 0x18, 0x1D, 0x8E, 0x38, 0x0F, 0x0E, 0x38, 0x0F, 0x07, 0x30, 0x06, 0x07, 0x60, 0x07, + 0x07, 0x60, 0x03, 0x03, 0xC0, 0x01, 0xC3, 0x80, 0x00, 0x7F, 0x80, 0x00, 0x03, 0xC8, 0x00, 0x01, + 0xF8, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Q",49*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x0E, 0x03, 0xC0, 0x0E, 0x00, + 0xE0, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x78, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, + 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, + 0x00, 0xE0, 0x0E, 0x03, 0xC0, 0x0F, 0xFF, 0x00, 0x0E, 0x0C, 0x00, 0x0E, 0x0E, 0x00, 0x0E, 0x0E, + 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x03, 0x80, 0x0E, 0x03, 0x80, + 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, + 0x00, 0xF0, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x78, 0x7F, 0xC0, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"R",50*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x03, 0x03, 0xE0, 0x0E, 0x00, + 0xE0, 0x0C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x38, 0x00, 0x30, 0x38, 0x00, 0x30, 0x38, 0x00, 0x10, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x0F, + 0xE0, 0x00, 0x03, 0xF8, 0x00, 0x01, 0xFE, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x07, + 0xE0, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x78, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x20, 0x00, 0x38, 0x30, 0x00, 0x38, 0x30, 0x00, 0x38, 0x10, 0x00, 0x30, 0x18, 0x00, 0x70, 0x1C, + 0x00, 0x60, 0x1E, 0x00, 0xE0, 0x1F, 0x81, 0x80, 0x10, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"S",51*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF8, 0x38, 0x38, 0x38, 0x30, 0x38, + 0x18, 0x30, 0x38, 0x08, 0x20, 0x38, 0x08, 0x60, 0x38, 0x0C, 0x40, 0x38, 0x04, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"T",52*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0xFE, 0x1C, 0x00, 0x10, 0x1C, 0x00, + 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, + 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, + 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, + 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, + 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x1C, 0x00, 0x10, 0x0C, 0x00, 0x20, 0x0E, + 0x00, 0x60, 0x07, 0x00, 0xC0, 0x03, 0x81, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"U",53*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0xFE, 0x1E, 0x00, 0x38, 0x0E, 0x00, + 0x30, 0x0E, 0x00, 0x30, 0x0E, 0x00, 0x30, 0x0F, 0x00, 0x20, 0x07, 0x00, 0x20, 0x07, 0x00, 0x60, + 0x07, 0x00, 0x60, 0x07, 0x00, 0x40, 0x03, 0x80, 0x40, 0x03, 0x80, 0xC0, 0x03, 0x80, 0x80, 0x03, + 0x80, 0x80, 0x01, 0xC0, 0x80, 0x01, 0xC1, 0x80, 0x01, 0xC1, 0x00, 0x01, 0xC1, 0x00, 0x00, 0xE1, + 0x00, 0x00, 0xE3, 0x00, 0x00, 0xE2, 0x00, 0x00, 0xE2, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x76, 0x00, + 0x00, 0x74, 0x00, 0x00, 0x74, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"V",54*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x7E, 0x3F, 0x38, 0x1C, 0x0C, 0x38, 0x1C, + 0x0C, 0x38, 0x1C, 0x0C, 0x38, 0x1C, 0x08, 0x38, 0x1C, 0x08, 0x18, 0x1C, 0x08, 0x18, 0x1C, 0x08, + 0x1C, 0x1C, 0x18, 0x1C, 0x1E, 0x18, 0x1C, 0x1E, 0x10, 0x1C, 0x1E, 0x10, 0x1C, 0x3E, 0x10, 0x0C, + 0x3E, 0x10, 0x0C, 0x2E, 0x30, 0x0E, 0x26, 0x20, 0x0E, 0x27, 0x20, 0x0E, 0x67, 0x20, 0x0E, 0x67, + 0x20, 0x06, 0x47, 0x60, 0x06, 0x47, 0x60, 0x06, 0x43, 0x40, 0x07, 0xC3, 0x40, 0x07, 0xC3, 0xC0, + 0x07, 0x83, 0xC0, 0x07, 0x83, 0xC0, 0x03, 0x83, 0xC0, 0x03, 0x83, 0x80, 0x03, 0x81, 0x80, 0x03, + 0x01, 0x80, 0x03, 0x01, 0x80, 0x03, 0x01, 0x80, 0x01, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"W",55*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x81, 0xFC, 0x0E, 0x00, 0x70, 0x0E, 0x00, + 0x60, 0x07, 0x00, 0x60, 0x07, 0x00, 0xC0, 0x03, 0x80, 0xC0, 0x03, 0x81, 0x80, 0x01, 0xC1, 0x80, + 0x01, 0xC3, 0x00, 0x00, 0xE3, 0x00, 0x00, 0xE2, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x74, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3E, + 0x00, 0x00, 0x6E, 0x00, 0x00, 0x6E, 0x00, 0x00, 0xC7, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x83, 0x80, + 0x01, 0x83, 0x80, 0x01, 0x01, 0xC0, 0x03, 0x01, 0xC0, 0x02, 0x01, 0xE0, 0x06, 0x00, 0xE0, 0x06, + 0x00, 0xF0, 0x0C, 0x00, 0x70, 0x1C, 0x00, 0x78, 0x7F, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"X",56*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x81, 0xFC, 0x1E, 0x00, 0x70, 0x0E, 0x00, + 0x60, 0x0E, 0x00, 0x60, 0x0E, 0x00, 0x40, 0x07, 0x00, 0xC0, 0x07, 0x00, 0x80, 0x07, 0x00, 0x80, + 0x03, 0x81, 0x80, 0x03, 0x81, 0x00, 0x03, 0xC1, 0x00, 0x01, 0xC3, 0x00, 0x01, 0xC2, 0x00, 0x00, + 0xE2, 0x00, 0x00, 0xE2, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x74, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, + 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x38, 0x00, 0x00, 0x38, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Y",57*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFC, 0x07, 0x80, 0x38, 0x0F, 0x00, + 0x78, 0x0C, 0x00, 0x70, 0x0C, 0x00, 0xF0, 0x18, 0x00, 0xE0, 0x10, 0x01, 0xE0, 0x00, 0x01, 0xC0, + 0x00, 0x03, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x80, 0x00, 0x07, 0x00, 0x00, 0x0F, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x78, + 0x00, 0x00, 0x70, 0x00, 0x00, 0xF0, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xC0, 0x00, + 0x03, 0xC0, 0x00, 0x03, 0x80, 0x00, 0x07, 0x80, 0x04, 0x07, 0x00, 0x0C, 0x0F, 0x00, 0x08, 0x0E, + 0x00, 0x18, 0x1E, 0x00, 0x38, 0x1C, 0x00, 0xF0, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"Z",58*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x7F, 0xF8, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, + 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"[",59*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x80, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"\",60*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x1F, 0xFC, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x1F, 0xFC, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"]",61*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x77, 0x00, 0x00, + 0xE1, 0x80, 0x01, 0x80, 0xC0, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"^",62*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, /*"_",63*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x03, 0xE0, 0x00, 0x00, 0xF0, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"`",64*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x03, 0x03, 0x80, 0x0C, + 0x01, 0xC0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x1C, 0x00, 0xE0, 0x00, 0x00, + 0xE0, 0x00, 0x0F, 0xE0, 0x00, 0xF0, 0xE0, 0x03, 0x80, 0xE0, 0x0E, 0x00, 0xE0, 0x1C, 0x00, 0xE0, + 0x1C, 0x00, 0xE0, 0x38, 0x00, 0xE0, 0x38, 0x00, 0xE0, 0x38, 0x00, 0xE0, 0x38, 0x00, 0xE2, 0x38, + 0x00, 0xE2, 0x1C, 0x03, 0xE2, 0x0E, 0x0E, 0xE4, 0x03, 0xF8, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"a",65*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x1F, 0x00, 0x0E, 0x61, 0xC0, 0x0E, + 0xC0, 0xE0, 0x0F, 0x80, 0x70, 0x0F, 0x00, 0x70, 0x0F, 0x00, 0x30, 0x0E, 0x00, 0x38, 0x0E, 0x00, + 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, + 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x30, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0F, + 0x00, 0x60, 0x0F, 0x00, 0xC0, 0x0C, 0xC1, 0x80, 0x08, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"b",66*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0xE0, 0xC0, 0x03, + 0x80, 0x60, 0x03, 0x00, 0x70, 0x07, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x1C, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x0E, 0x00, 0x08, 0x0E, 0x00, 0x10, 0x07, + 0x00, 0x30, 0x03, 0x80, 0x60, 0x01, 0xC0, 0xC0, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"c",67*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x03, 0xF0, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x7C, 0x70, 0x01, 0xC3, 0x70, 0x03, + 0x00, 0xF0, 0x07, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x1C, 0x00, + 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, + 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x0C, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0xF0, 0x06, + 0x00, 0xF0, 0x03, 0x01, 0x70, 0x01, 0x82, 0x7E, 0x00, 0xFC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"d",68*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x01, 0xC1, 0x80, 0x03, + 0x80, 0xC0, 0x07, 0x00, 0x60, 0x06, 0x00, 0x70, 0x0E, 0x00, 0x30, 0x0C, 0x00, 0x38, 0x1C, 0x00, + 0x38, 0x1C, 0x00, 0x38, 0x1C, 0x00, 0x38, 0x1F, 0xFF, 0xF8, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x0E, 0x00, 0x10, 0x0E, 0x00, 0x20, 0x07, + 0x00, 0x20, 0x03, 0x80, 0x40, 0x01, 0xC1, 0x80, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"e",69*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x1C, + 0x0C, 0x00, 0x30, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x3F, 0xFF, 0xE0, 0x00, 0xE0, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, + 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"f",70*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x1C, 0x01, 0x83, 0xFC, 0x03, + 0x01, 0xCC, 0x07, 0x01, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, + 0xE0, 0x0E, 0x00, 0xE0, 0x07, 0x00, 0xC0, 0x03, 0x01, 0xC0, 0x01, 0x83, 0x80, 0x07, 0x7E, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x07, 0xFF, 0xC0, 0x03, + 0xFF, 0xF0, 0x0E, 0x01, 0xF0, 0x0C, 0x00, 0x78, 0x1C, 0x00, 0x38, 0x1C, 0x00, 0x38, 0x1C, 0x00, + 0x38, 0x1C, 0x00, 0x30, 0x0E, 0x00, 0x70, 0x07, 0x81, 0xC0, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, /*"g",71*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x1F, 0x80, 0x0E, 0x61, 0xC0, 0x0E, + 0xC0, 0xE0, 0x0F, 0x80, 0x70, 0x0F, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, + 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, + 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, + 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x7F, 0xC3, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"h",72*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x3E, + 0x00, 0x00, 0x3E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"i",73*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x01, 0xF0, 0x00, 0x01, + 0xF0, 0x00, 0x01, 0xF0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x1F, 0xE0, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, + 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, + 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0x0E, 0x00, + 0xC0, 0x0E, 0x01, 0xC0, 0x0E, 0x03, 0x80, 0x07, 0x06, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, /*"j",74*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, + 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x07, 0xF0, 0x0E, 0x01, 0x80, 0x0E, + 0x03, 0x00, 0x0E, 0x03, 0x00, 0x0E, 0x06, 0x00, 0x0E, 0x0C, 0x00, 0x0E, 0x18, 0x00, 0x0E, 0x30, + 0x00, 0x0E, 0x38, 0x00, 0x0E, 0x78, 0x00, 0x0E, 0xDC, 0x00, 0x0F, 0x9C, 0x00, 0x0F, 0x0E, 0x00, + 0x0E, 0x07, 0x00, 0x0E, 0x07, 0x00, 0x0E, 0x03, 0x80, 0x0E, 0x03, 0x80, 0x0E, 0x01, 0xC0, 0x0E, + 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xF0, 0x7F, 0xC3, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"k",75*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x1C, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x1C, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, + 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x07, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"l",76*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x7C, 0xF8, 0xF0, 0x1D, 0x3D, 0x38, 0x1E, + 0x1E, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x7F, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"m",77*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x7E, 0x1F, 0x80, 0x0E, 0x61, 0xC0, 0x0E, + 0xC0, 0xE0, 0x0F, 0x80, 0x60, 0x0F, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, + 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, + 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, + 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x7F, 0xC3, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"n",78*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x01, 0xC3, 0x80, 0x03, + 0x00, 0xC0, 0x06, 0x00, 0x60, 0x0E, 0x00, 0x30, 0x1C, 0x00, 0x38, 0x1C, 0x00, 0x38, 0x18, 0x00, + 0x18, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, + 0x38, 0x00, 0x1C, 0x38, 0x00, 0x1C, 0x1C, 0x00, 0x38, 0x1C, 0x00, 0x38, 0x0C, 0x00, 0x30, 0x06, + 0x00, 0x60, 0x07, 0x00, 0xC0, 0x01, 0xC1, 0x80, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"o",79*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1F, 0x00, 0x7E, 0x60, 0xC0, 0x0E, + 0x80, 0x60, 0x0F, 0x00, 0x30, 0x0F, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x1C, 0x0E, 0x00, + 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, + 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x1C, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0E, 0x00, 0x38, 0x0F, + 0x00, 0x70, 0x0F, 0x80, 0xE0, 0x0E, 0xC1, 0xC0, 0x0E, 0x3F, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, /*"p",80*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x10, 0x01, 0x83, 0x30, 0x07, + 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x0C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x38, 0x00, + 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, + 0x38, 0x00, 0x70, 0x38, 0x00, 0x70, 0x18, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x1C, 0x00, 0x70, 0x0C, + 0x00, 0xF0, 0x06, 0x01, 0xF0, 0x03, 0x83, 0x70, 0x00, 0xFC, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x03, 0xFE, 0x00, 0x00, 0x00, /*"q",81*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x3F, 0xC0, 0xF0, 0x01, 0xC3, 0x1C, 0x01, + 0xC6, 0x1C, 0x01, 0xC8, 0x1C, 0x01, 0xD8, 0x1C, 0x01, 0xF0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xE0, + 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, + 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, + 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x01, 0xC0, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"r",82*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x01, 0xC1, 0xC0, 0x03, + 0x80, 0xE0, 0x07, 0x00, 0x60, 0x07, 0x00, 0x60, 0x07, 0x00, 0x20, 0x07, 0x00, 0x00, 0x07, 0x80, + 0x00, 0x03, 0xE0, 0x00, 0x01, 0xF8, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x07, 0xE0, + 0x00, 0x01, 0xE0, 0x00, 0x00, 0xF0, 0x08, 0x00, 0x70, 0x08, 0x00, 0x70, 0x0C, 0x00, 0x70, 0x0C, + 0x00, 0x60, 0x0E, 0x00, 0xE0, 0x0F, 0x81, 0xC0, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"s",83*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x70, 0x00, 0x01, 0xF0, 0x00, 0x1F, 0xFF, 0xE0, 0x00, 0x70, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x00, 0x00, 0x70, 0x10, 0x00, 0x70, 0x10, 0x00, + 0x70, 0x20, 0x00, 0x38, 0x20, 0x00, 0x18, 0x40, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"t",84*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x7E, 0x03, 0xF0, 0x0E, 0x00, 0x70, 0x0E, + 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, + 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, + 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0x70, 0x0E, 0x00, 0xF0, 0x06, + 0x00, 0xF0, 0x07, 0x01, 0x70, 0x03, 0x82, 0x7E, 0x01, 0xFC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"u",85*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC1, 0xFC, 0x0F, 0x00, 0x70, 0x07, + 0x00, 0x60, 0x07, 0x00, 0x60, 0x03, 0x00, 0x40, 0x03, 0x80, 0x40, 0x03, 0x80, 0xC0, 0x01, 0x80, + 0x80, 0x01, 0xC0, 0x80, 0x01, 0xC1, 0x80, 0x00, 0xC1, 0x00, 0x00, 0xE1, 0x00, 0x00, 0xE3, 0x00, + 0x00, 0xE2, 0x00, 0x00, 0x62, 0x00, 0x00, 0x76, 0x00, 0x00, 0x74, 0x00, 0x00, 0x3C, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"v",86*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x7E, 0x3F, 0x38, 0x3C, 0x0C, 0x38, + 0x18, 0x08, 0x18, 0x18, 0x08, 0x18, 0x1C, 0x18, 0x1C, 0x1C, 0x18, 0x1C, 0x1C, 0x10, 0x1C, 0x3C, + 0x10, 0x0C, 0x2E, 0x30, 0x0E, 0x2E, 0x30, 0x0E, 0x26, 0x20, 0x0E, 0x66, 0x20, 0x06, 0x46, 0x20, + 0x07, 0x47, 0x60, 0x07, 0xC7, 0x40, 0x07, 0xC3, 0x40, 0x03, 0x83, 0x40, 0x03, 0x83, 0xC0, 0x03, + 0x83, 0x80, 0x03, 0x81, 0x80, 0x01, 0x81, 0x80, 0x01, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"w",87*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xE3, 0xF8, 0x07, 0x80, 0xE0, 0x03, + 0x80, 0xC0, 0x01, 0xC0, 0x80, 0x01, 0xC1, 0x80, 0x00, 0xE3, 0x00, 0x00, 0x72, 0x00, 0x00, 0x76, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x3E, 0x00, + 0x00, 0x67, 0x00, 0x00, 0x67, 0x00, 0x00, 0xC3, 0x80, 0x00, 0x83, 0x80, 0x01, 0x81, 0xC0, 0x03, + 0x00, 0xC0, 0x03, 0x00, 0xE0, 0x07, 0x00, 0xF0, 0x3F, 0x83, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"x",88*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC3, 0xFC, 0x0F, 0x00, 0xE0, 0x07, + 0x00, 0x60, 0x07, 0x00, 0x40, 0x03, 0x00, 0xC0, 0x03, 0x80, 0x80, 0x03, 0x80, 0x80, 0x01, 0x81, + 0x80, 0x01, 0xC1, 0x00, 0x01, 0xC1, 0x00, 0x00, 0xC3, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x62, 0x00, + 0x00, 0x66, 0x00, 0x00, 0x74, 0x00, 0x00, 0x34, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x30, 0x00, 0x00, 0x20, + 0x00, 0x0C, 0x60, 0x00, 0x1E, 0x40, 0x00, 0x1F, 0xC0, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, /*"y",89*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF0, 0x0E, 0x00, 0xF0, 0x1C, + 0x00, 0xE0, 0x18, 0x01, 0xC0, 0x18, 0x03, 0xC0, 0x10, 0x03, 0x80, 0x00, 0x07, 0x00, 0x00, 0x0F, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x38, 0x00, 0x00, 0x78, 0x00, 0x00, 0x70, 0x00, + 0x00, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xC0, 0x08, 0x03, 0x80, 0x18, 0x07, 0x80, 0x18, 0x07, + 0x00, 0x30, 0x0E, 0x00, 0x30, 0x1E, 0x00, 0xF0, 0x1F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"z",90*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0xC0, 0x00, + 0x01, 0x80, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x01, + 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"{",91*/ + + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, /*"|",92*/ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x30, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30, 0x00, 0x00, 0x60, 0x00, 0x00, 0xC0, + 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, + 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, + 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x80, 0x00, 0x01, 0x80, + 0x00, 0x03, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"}",93*/ + + 0x03, 0xC0, 0x00, 0x0F, 0xE0, 0x00, 0x08, 0x70, 0x02, 0x10, 0x38, 0x02, 0x10, 0x1C, 0x04, 0x20, + 0x0E, 0x04, 0x20, 0x07, 0x88, 0x00, 0x03, 0xF8, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*"~",94*/ +}; + +const esp_painter_basic_font_t esp_painter_basic_font_48 = { + .bitmap = bitmap, + .width = 24, + .height = 48, +}; + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_text_overlay/idf_component.yml b/components/third_party/esp_capture/src/impl/capture_text_overlay/idf_component.yml new file mode 100644 index 0000000..1efd9c8 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_text_overlay/idf_component.yml @@ -0,0 +1,5 @@ +version: "0.0.1" +description: Capture Text Overlay +url: https://github.com/espressif/esp-adf-libs/tree/master +dependencies: + idf : ">=4.4" diff --git a/components/third_party/esp_capture/src/impl/capture_video_enc/CMakeLists.txt b/components/third_party/esp_capture/src/impl/capture_video_enc/CMakeLists.txt new file mode 100755 index 0000000..b7271d0 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_video_enc/CMakeLists.txt @@ -0,0 +1,8 @@ + +set(component_srcdirs "./") + +idf_component_register( + SRC_DIRS ${component_srcdirs} + INCLUDE_DIRS ./ + REQUIRES esp_driver_jpeg +) diff --git a/components/third_party/esp_capture/src/impl/capture_video_enc/capture_video_enc.c b/components/third_party/esp_capture/src/impl/capture_video_enc/capture_video_enc.c new file mode 100644 index 0000000..c822673 --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_video_enc/capture_video_enc.c @@ -0,0 +1,249 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "esp_capture_types.h" +#include "esp_capture_venc_if.h" +#include +#include +#include "esp_log.h" +#include "esp_video_enc.h" +#include "esp_video_codec_utils.h" + +#define TAG "VENC" + +#define ALIGN_UP(size, align) (((size) + (align)-1) & ~((align)-1)) + +typedef struct { + esp_capture_venc_if_t base; + esp_capture_video_info_t info; + esp_video_codec_pixel_fmt_t src_fmt; + int out_frame_size; + int bitrate; + bool started; + esp_video_enc_handle_t enc_handle; +} venc_inst_t; + +static int venc_get_support_codecs(esp_capture_venc_if_t *h, const esp_capture_codec_type_t **codecs, uint8_t *num) +{ + static esp_capture_codec_type_t vcodecs[] = { + ESP_CAPTURE_CODEC_TYPE_MJPEG, + ESP_CAPTURE_CODEC_TYPE_H264, + }; + *codecs = vcodecs; + *num = sizeof(vcodecs) / sizeof(vcodecs[0]); + return ESP_CAPTURE_ERR_OK; +} + +static int venc_get_input_codecs(esp_capture_venc_if_t *h, esp_capture_codec_type_t out_codec, const esp_capture_codec_type_t **codecs, uint8_t *num) +{ + static esp_capture_codec_type_t jpeg_inputs[] = { + ESP_CAPTURE_CODEC_TYPE_RGB565, + }; + static esp_capture_codec_type_t h264_inputs[] = { + ESP_CAPTURE_CODEC_TYPE_YUV420, + }; + if (out_codec == ESP_CAPTURE_CODEC_TYPE_MJPEG) { + *codecs = jpeg_inputs; + *num = sizeof(jpeg_inputs) / sizeof(jpeg_inputs[0]); + return ESP_CAPTURE_ERR_OK; + } + if (out_codec == ESP_CAPTURE_CODEC_TYPE_H264) { + h264_inputs[0] = ESP_CAPTURE_CODEC_TYPE_YUV420; + *codecs = h264_inputs; + *num = sizeof(h264_inputs) / sizeof(h264_inputs[0]); + return ESP_CAPTURE_ERR_OK; + } + *codecs = NULL; + *num = 0; + return ESP_CAPTURE_ERR_NOT_SUPPORTED; +} + +static bool is_venc_codec_support(esp_capture_venc_if_t *h, esp_capture_codec_type_t out_codec, esp_capture_codec_type_t src_codec) +{ + const esp_capture_codec_type_t *in_codecs = NULL; + uint8_t num = 0; + venc_get_input_codecs(h, out_codec, &in_codecs, &num); + for (int i = 0; i < num; i++) { + if (in_codecs[i] == src_codec) { + return true; + } + } + return false; +} + +static int venc_get_frame_size(esp_capture_venc_if_t *h, int *in_frame_size, int *out_frame_size) +{ + venc_inst_t *venc = (venc_inst_t *)h; + if (venc->started == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + esp_video_codec_resolution_t res = { + .width = venc->info.width, + .height = venc->info.height, + }; + *in_frame_size = (int)esp_video_codec_get_image_size(venc->src_fmt, &res); + if (venc->info.codec == ESP_CAPTURE_CODEC_TYPE_MJPEG) { + *out_frame_size = *in_frame_size / 20; + } else if (venc->info.codec == ESP_CAPTURE_CODEC_TYPE_H264) { + *out_frame_size = *in_frame_size; + *out_frame_size = ALIGN_UP(*out_frame_size, 128); + } else { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + return ESP_CAPTURE_ERR_OK; +} + +static esp_video_codec_type_t map_codec_type(esp_capture_codec_type_t codec) +{ + switch (codec) { + case ESP_CAPTURE_CODEC_TYPE_MJPEG: + return ESP_VIDEO_CODEC_TYPE_MJPEG; + case ESP_CAPTURE_CODEC_TYPE_H264: + return ESP_VIDEO_CODEC_TYPE_H264; + default: + return ESP_VIDEO_CODEC_TYPE_NONE; + } +} + +static esp_video_codec_pixel_fmt_t map_pixel_fmt(esp_capture_codec_type_t codec) +{ + switch (codec) { + case ESP_CAPTURE_CODEC_TYPE_RGB565: + return ESP_VIDEO_CODEC_PIXEL_FMT_RGB565_LE; + case ESP_CAPTURE_CODEC_TYPE_YUV420: + #if CONFIG_IDF_TARGET_ESP32S3 + return ESP_VIDEO_CODEC_PIXEL_FMT_YUV420P; + #endif + return ESP_VIDEO_CODEC_PIXEL_FMT_O_UYY_E_VYY; + default: + return ESP_VIDEO_CODEC_PIXEL_FMT_NONE; + } +} + +static int venc_start(esp_capture_venc_if_t *h, esp_capture_codec_type_t src_codec, esp_capture_video_info_t *info) +{ + venc_inst_t *venc = (venc_inst_t *)h; + if (is_venc_codec_support(h, info->codec, src_codec) == false) { + ESP_LOGE(TAG, "Codec not supported src:%d dst:%d", src_codec, info->codec); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + venc->info = *info; + venc->src_fmt = map_pixel_fmt(src_codec); + esp_video_enc_cfg_t enc_cfg = { + .codec_type = map_codec_type(info->codec), + .resolution = { + .width = info->width, + .height = info->height, + }, + .in_fmt = map_pixel_fmt(src_codec), + .fps = info->fps, + }; + int ret = esp_video_enc_open(&enc_cfg, &venc->enc_handle); + if (ret != ESP_VC_ERR_OK) { + ESP_LOGE(TAG, "Fail to open encoder"); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + venc->started = true; + int in_size = 0; + venc_get_frame_size(h, &in_size, &venc->out_frame_size); + return ESP_CAPTURE_ERR_OK; +} + +static int venc_set_bitrate(esp_capture_venc_if_t *h, int bitrate) +{ + venc_inst_t *venc = (venc_inst_t *)h; + esp_video_enc_set_bitrate(venc->enc_handle, bitrate); + return ESP_CAPTURE_ERR_OK; +} + +static int venc_encode_frame(esp_capture_venc_if_t *h, esp_capture_stream_frame_t *raw, esp_capture_stream_frame_t *encoded) +{ + venc_inst_t *venc = (venc_inst_t *)h; + if (venc->started == false || venc->enc_handle == NULL) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + if (encoded->size < venc->out_frame_size) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + esp_video_enc_in_frame_t in_frame = { + .pts = raw->pts, + .data = raw->data, + .size = raw->size, + }; + esp_video_enc_out_frame_t out_frame = { + .data = encoded->data, + .size = encoded->size, + }; + int ret = esp_video_enc_process(venc->enc_handle, &in_frame, &out_frame); + if (ret == ESP_VC_ERR_BUF_NOT_ENOUGH) { + return ESP_CAPTURE_ERR_NOT_ENOUGH; + } + if (ret != ESP_VC_ERR_OK) { + ESP_LOGE(TAG, "Fail to encode frame"); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + encoded->pts = out_frame.pts; + encoded->size = out_frame.encoded_size; + ; + return ESP_CAPTURE_ERR_OK; +} + +static int venc_stop(esp_capture_venc_if_t *h) +{ + venc_inst_t *venc = (venc_inst_t *)h; + if (venc->enc_handle) { + esp_video_enc_close(venc->enc_handle); + venc->enc_handle = NULL; + } + venc->started = false; + return ESP_CAPTURE_ERR_OK; +} + +static esp_capture_venc_if_t *venc_clone(esp_capture_venc_if_t *h) +{ + venc_inst_t *venc = (venc_inst_t *)calloc(1, sizeof(venc_inst_t)); + if (venc == NULL) { + return NULL; + } + venc_inst_t *his = (venc_inst_t *)h; + venc->base = his->base; + return &(venc->base); +} + +esp_capture_venc_if_t *esp_capture_new_video_encoder(void) +{ + venc_inst_t *venc = (venc_inst_t *)calloc(1, sizeof(venc_inst_t)); + if (venc == NULL) { + return NULL; + } + venc->base.clone = venc_clone; + venc->base.get_support_codecs = venc_get_support_codecs; + venc->base.get_input_codecs = venc_get_input_codecs; + venc->base.set_bitrate = venc_set_bitrate; + venc->base.start = venc_start; + venc->base.get_frame_size = venc_get_frame_size; + venc->base.encode_frame = venc_encode_frame; + venc->base.stop = venc_stop; + return &(venc->base); +} diff --git a/components/third_party/esp_capture/src/impl/capture_video_enc/idf_component.yml b/components/third_party/esp_capture/src/impl/capture_video_enc/idf_component.yml new file mode 100644 index 0000000..291c3ee --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_video_enc/idf_component.yml @@ -0,0 +1,4 @@ +dependencies: + espressif/esp_video_codec: "~0.5.2" + esp_capture: + path: ../../../../esp_capture diff --git a/components/third_party/esp_capture/src/impl/capture_video_src/CMakeLists.txt b/components/third_party/esp_capture/src/impl/capture_video_src/CMakeLists.txt new file mode 100755 index 0000000..dcb696c --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_video_src/CMakeLists.txt @@ -0,0 +1,12 @@ +if (${IDF_TARGET} STREQUAL "esp32s3") +list(APPEND COMPONENT_SRCS capture_video_dvp_src.c) +endif() + +if (${IDF_TARGET} STREQUAL "esp32p4") +list(APPEND COMPONENT_SRCS capture_video_v4l2_src.c) +list(APPEND COMPONENT_REQUIRES esp_mm) +endif() + +idf_component_register(SRCS "${COMPONENT_SRCS}" + INCLUDE_DIRS "${COMPONENT_INCLUDES}" + REQUIRES ${COMPONENT_REQUIRES}) diff --git a/components/third_party/esp_capture/src/impl/capture_video_src/capture_video_dvp_src.c b/components/third_party/esp_capture/src/impl/capture_video_src/capture_video_dvp_src.c new file mode 100644 index 0000000..87856bb --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_video_src/capture_video_dvp_src.c @@ -0,0 +1,316 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#include "sdkconfig.h" +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#include "esp_camera.h" +#include "esp_log.h" +#include "esp_capture_defaults.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +#define TAG "DVP_SRC" + +typedef struct { + esp_capture_video_src_if_t base; + esp_capture_video_dvp_src_cfg_t cfg; + esp_capture_video_info_t vid_info; + bool dvp_inited; + bool need_convert_420; + camera_fb_t *pic[2]; + uint8_t *yuv420_cache; + SemaphoreHandle_t yuv420_lock; + uint8_t cur_pic; +} dvp_src_t; + +static int dvp_src_open(esp_capture_video_src_if_t *src) +{ + return 0; +} + +framesize_t get_video_quality(int width, int height) +{ + if (width == 320 && height == 240) { + return FRAMESIZE_QVGA; + } + if (width == 480 && height == 320) { + return FRAMESIZE_HVGA; + } + if (width == 640 && height == 480) { + return FRAMESIZE_VGA; + } + if (width == 1024 && height == 768) { + return FRAMESIZE_XGA; + } + if (width == 1280 && height == 720) { + return FRAMESIZE_HD; + } + if (width == 1920 && height == 1080) { + return FRAMESIZE_FHD; + } + return FRAMESIZE_QVGA; +} + +static int dvp_src_get_support_codecs(esp_capture_video_src_if_t *src, const esp_capture_codec_type_t **codecs, uint8_t *num) +{ + static esp_capture_codec_type_t dvp_codecs[] = { + ESP_CAPTURE_CODEC_TYPE_MJPEG, + ESP_CAPTURE_CODEC_TYPE_YUV422P, + ESP_CAPTURE_CODEC_TYPE_YUV420, + }; + *codecs = dvp_codecs; + *num = sizeof(dvp_codecs) / sizeof(dvp_codecs[0]); + return 0; +} + +static bool dvp_src_codec_supported(esp_capture_video_src_if_t *src, esp_capture_codec_type_t codec) +{ + uint8_t n = 0; + const esp_capture_codec_type_t *codecs; + dvp_src_get_support_codecs(src, &codecs, &n); + for (uint8_t i = 0; i < n; i++) { + if (codecs[i] == codec) { + return true; + } + } + return false; +} + +static int dvp_src_negotiate_caps(esp_capture_video_src_if_t *src, esp_capture_video_info_t *in_cap, esp_capture_video_info_t *out_caps) +{ + dvp_src_t *dvp_src = (dvp_src_t *)src; + if (dvp_src_codec_supported(src, in_cap->codec) == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + if (dvp_src->dvp_inited) { + if (in_cap->codec == dvp_src->vid_info.codec) { + *out_caps = *in_cap; + return 0; + } + esp_camera_deinit(); + dvp_src->dvp_inited = 0; + } + + camera_config_t camera_config = { + .pin_pwdn = dvp_src->cfg.pwr_pin, + .pin_reset = dvp_src->cfg.reset_pin, + .pin_xclk = dvp_src->cfg.xclk_pin, + .pin_sccb_sda = -1, + .pin_sccb_scl = -1, + .pin_d7 = dvp_src->cfg.data[7], + .pin_d6 = dvp_src->cfg.data[6], + .pin_d5 = dvp_src->cfg.data[5], + .pin_d4 = dvp_src->cfg.data[4], + .pin_d3 = dvp_src->cfg.data[3], + .pin_d2 = dvp_src->cfg.data[2], + .pin_d1 = dvp_src->cfg.data[1], + .pin_d0 = dvp_src->cfg.data[0], + .pin_vsync = dvp_src->cfg.vsync_pin, + .pin_href = dvp_src->cfg.href_pin, + .pin_pclk = dvp_src->cfg.pclk_pin, + .xclk_freq_hz = dvp_src->cfg.xclk_freq, + .ledc_timer = LEDC_TIMER_0, + .ledc_channel = LEDC_CHANNEL_0, + .jpeg_quality = 12, // 0-63 lower number means higher quality + .fb_count = dvp_src->cfg.buf_count, + .grab_mode = CAMERA_GRAB_WHEN_EMPTY, + .sccb_i2c_port = dvp_src->cfg.i2c_port, + }; + dvp_src->need_convert_420 = false; + if (camera_config.xclk_freq_hz == 0) { + camera_config.xclk_freq_hz = 20000000; + } + if (in_cap->codec == ESP_CAPTURE_CODEC_TYPE_MJPEG) { + camera_config.pixel_format = PIXFORMAT_JPEG; + } else if (in_cap->codec == ESP_CAPTURE_CODEC_TYPE_YUV422P || in_cap->codec == ESP_CAPTURE_CODEC_TYPE_YUV420) { + camera_config.pixel_format = PIXFORMAT_YUV422; + if (in_cap->codec == ESP_CAPTURE_CODEC_TYPE_YUV420) { + dvp_src->need_convert_420 = true; + } + } else { + ESP_LOGE(TAG, "Format not supported %d", in_cap->codec); + return -1; + } + camera_config.frame_size = get_video_quality(in_cap->width, in_cap->height); + esp_err_t err = esp_camera_init(&camera_config); + if (err == ESP_OK) { + *out_caps = *in_cap; + dvp_src->vid_info = *in_cap; + dvp_src->dvp_inited = true; + return 0; + } + ESP_LOGE(TAG, "Camera init failed with error 0x%x", err); + return -1; +} + +static int dvp_src_start(esp_capture_video_src_if_t *src) +{ + dvp_src_t *dvp_src = (dvp_src_t *)src; + if (dvp_src->dvp_inited == false) { + return -1; + } + if (dvp_src->need_convert_420) { + dvp_src->yuv420_cache = malloc(dvp_src->vid_info.width * dvp_src->vid_info.height * 3 / 2); + if (dvp_src->yuv420_cache == NULL) { + return ESP_CAPTURE_ERR_NO_MEM; + } + dvp_src->yuv420_lock = xSemaphoreCreateCounting(1, 1); + if (dvp_src->yuv420_lock == NULL) { + return ESP_CAPTURE_ERR_NO_MEM; + } + } + return 0; +} + +static void convert_yuv420(uint32_t w, uint32_t h, uint8_t *src, uint8_t *dst) +{ + uint32_t bytes = w * h; + uint8_t *y = dst; + uint8_t *u = dst + bytes; + uint8_t *v = u + (bytes >> 2); + w >>= 1; + h >>= 1; + for (int i = 0; i < h; i++) { + for (int i = 0; i < w; i++) { + *(y++) = *(src++); + *(u++) = *(src++); + *(y++) = *(src++); + *(v++) = *(src++); + } + for (int i = 0; i < w; i++) { + *(y++) = *(src); + src += 2; + *(y++) = *(src); + src += 2; + } + } +} + +static int dvp_src_acquire_frame(esp_capture_video_src_if_t *src, esp_capture_stream_frame_t *frame) +{ + dvp_src_t *dvp_src = (dvp_src_t *)src; + if (dvp_src->dvp_inited == false) { + return -1; + } + camera_fb_t *fb = esp_camera_fb_get(); + if (fb == NULL) { + ESP_LOGE(TAG, "Camera capture failed"); + return -1; + } + for (int i = 0; i < dvp_src->cfg.buf_count; i++) { + if (dvp_src->pic[i] == NULL) { + dvp_src->pic[i] = fb; + if (dvp_src->need_convert_420) { + xSemaphoreTake(dvp_src->yuv420_lock, portMAX_DELAY); + dvp_src->cur_pic = i; + convert_yuv420(dvp_src->vid_info.width, dvp_src->vid_info.height, + dvp_src->pic[i]->buf, dvp_src->yuv420_cache); + frame->data = dvp_src->yuv420_cache; + frame->size = dvp_src->pic[i]->len * 3 / 4; + } else { + frame->data = dvp_src->pic[i]->buf; + frame->size = dvp_src->pic[i]->len; + } + return 0; + } + } + ESP_LOGE(TAG, "Impossible"); + // User not consumed, should not happen + esp_camera_fb_return(fb); + return -1; +} + +static int dvp_src_release_frame(esp_capture_video_src_if_t *src, esp_capture_stream_frame_t *frame) +{ + dvp_src_t *dvp_src = (dvp_src_t *)src; + if (dvp_src->dvp_inited == false) { + return -1; + } + int sel = -1; + if (dvp_src->need_convert_420) { + sel = dvp_src->cur_pic; + } else { + for (int i = 0; i < dvp_src->cfg.buf_count; i++) { + if (dvp_src->pic[i] && frame->data == dvp_src->pic[i]->buf) { + sel = i; + break; + } + } + } + if (sel >= 0 && dvp_src->pic[sel]) { + if (dvp_src->need_convert_420) { + xSemaphoreGive(dvp_src->yuv420_lock); + } + esp_camera_fb_return(dvp_src->pic[sel]); + dvp_src->pic[sel] = NULL; + return 0; + } + return -1; +} + +static int dvp_src_stop(esp_capture_video_src_if_t *src) +{ + dvp_src_t *dvp_src = (dvp_src_t *)src; + if (dvp_src->dvp_inited) { + dvp_src->dvp_inited = false; + dvp_src->pic[0] = dvp_src->pic[1] = NULL; + if (dvp_src->yuv420_lock) { + vSemaphoreDelete(dvp_src->yuv420_lock); + dvp_src->yuv420_lock = NULL; + } + esp_camera_deinit(); + } + if (dvp_src->yuv420_cache) { + free(dvp_src->yuv420_cache); + dvp_src->yuv420_cache = NULL; + } + return 0; +} + +static int dvp_src_close(esp_capture_video_src_if_t *src) +{ + return dvp_src_stop(src); +} + +esp_capture_video_src_if_t *esp_capture_new_video_dvp_src(esp_capture_video_dvp_src_cfg_t *cfg) +{ + dvp_src_t *dvp = calloc(1, sizeof(dvp_src_t)); + if (dvp == NULL) { + return NULL; + } + dvp->base.open = dvp_src_open; + dvp->base.get_support_codecs = dvp_src_get_support_codecs; + dvp->base.negotiate_caps = dvp_src_negotiate_caps; + dvp->base.start = dvp_src_start; + dvp->base.acquire_frame = dvp_src_acquire_frame; + dvp->base.release_frame = dvp_src_release_frame; + dvp->base.stop = dvp_src_stop; + dvp->base.close = dvp_src_close; + dvp->cfg = *cfg; + if (cfg->buf_count == 0) { + dvp->cfg.buf_count = 1; + } + return &dvp->base; +} + +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_video_src/capture_video_v4l2_src.c b/components/third_party/esp_capture/src/impl/capture_video_src/capture_video_v4l2_src.c new file mode 100644 index 0000000..dc5b19e --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_video_src/capture_video_v4l2_src.c @@ -0,0 +1,325 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#include +#if CONFIG_IDF_TARGET_ESP32P4 +#include "esp_capture_types.h" +#include "linux/videodev2.h" +#include "esp_video_init.h" +#include +#include +#include +#include +#include +#include +#include "esp_capture_video_src_if.h" +#include "esp_capture_defaults.h" +#include "esp_cache.h" +#include "esp_log.h" + +#define TAG "V4L2_SRC" + +#define MAX_BUFS (4) +#define MAX_SUPPORT_FORMATS_NUM (4) +#define FMT_STR(fmt) ((uint8_t *)&fmt)[0], ((uint8_t *)&fmt)[1], ((uint8_t *)&fmt)[2], ((uint8_t *)&fmt)[3] + +typedef struct { + esp_capture_video_src_if_t base; + char dev_name[16]; + uint8_t buf_count; + esp_capture_codec_type_t support_formats[MAX_SUPPORT_FORMATS_NUM]; + uint8_t format_count; + int fd; + uint8_t *fb_buffer[MAX_BUFS]; + struct v4l2_buffer v4l2_buf[MAX_BUFS]; + bool fb_used[MAX_BUFS]; + bool nego_ok; + bool started; +} v4l2_src_t; + +static esp_capture_codec_type_t get_codec_type(uint32_t fmt) +{ + switch (fmt) { + case V4L2_PIX_FMT_RGB565: + return ESP_CAPTURE_CODEC_TYPE_RGB565; + case V4L2_PIX_FMT_YUV420: + return ESP_CAPTURE_CODEC_TYPE_YUV420; + case V4L2_PIX_FMT_YUV422P: + return ESP_CAPTURE_CODEC_TYPE_YUV422P; + case V4L2_PIX_FMT_MJPEG: + case V4L2_PIX_FMT_JPEG: + return ESP_CAPTURE_CODEC_TYPE_MJPEG; + default: + return ESP_CAPTURE_CODEC_TYPE_NONE; + } +} + +static uint32_t get_v4l2_type(esp_capture_codec_type_t codec) +{ + switch (codec) { + case ESP_CAPTURE_CODEC_TYPE_YUV420: + return V4L2_PIX_FMT_YUV420; + case ESP_CAPTURE_CODEC_TYPE_YUV422P: + return V4L2_PIX_FMT_YUV422P; + case ESP_CAPTURE_CODEC_TYPE_MJPEG: + return V4L2_PIX_FMT_MJPEG; + case ESP_CAPTURE_CODEC_TYPE_RGB565: + return V4L2_PIX_FMT_RGB565; + default: + return 0; + } +} + +static int v4l2_open(esp_capture_video_src_if_t *src) +{ + v4l2_src_t *v4l2 = (v4l2_src_t *)src; + do { + struct v4l2_capability capability; + v4l2->fd = open(v4l2->dev_name, O_RDONLY); + if (v4l2->fd <= 0) { + ESP_LOGE(TAG, "failed to open device"); + return ESP_FAIL; + } + if (ioctl(v4l2->fd, VIDIOC_QUERYCAP, &capability)) { + ESP_LOGE(TAG, "failed to get capability"); + break; + } + if ((capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) != V4L2_CAP_VIDEO_CAPTURE) { + ESP_LOGE(TAG, "Not support capture"); + break; + } + v4l2->format_count = 0; + for (int i = 0; i < MAX_SUPPORT_FORMATS_NUM; i++) { + struct v4l2_fmtdesc fmtdesc = { + .index = i, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + }; + if (ioctl(v4l2->fd, VIDIOC_ENUM_FMT, &fmtdesc) != 0) { + break; + } + v4l2->support_formats[i] = get_codec_type(fmtdesc.pixelformat); + v4l2->format_count++; + ESP_LOGI(TAG, "Support Format: %c%c%c%c", FMT_STR(fmtdesc.pixelformat)); + } + if (v4l2->format_count == 0) { + ESP_LOGE(TAG, "No support format"); + break; + } + ESP_LOGI(TAG, "Success to open camera"); + return 0; + } while (0); + if (v4l2->fd > 0) { + close(v4l2->fd); + v4l2->fd = 0; + } + return -1; +} + +static int v4l2_get_support_codecs(esp_capture_video_src_if_t *src, const esp_capture_codec_type_t **codecs, + uint8_t *num) +{ + v4l2_src_t *v4l2 = (v4l2_src_t *)src; + *codecs = v4l2->support_formats; + *num = v4l2->format_count; + return 0; +} + +static bool v4l2_is_input_supported(v4l2_src_t *v4l2, esp_capture_codec_type_t in_codec) +{ + for (uint8_t i = 0; i < v4l2->format_count; i++) { + if (v4l2->support_formats[i] == in_codec) { + return true; + } + } + return false; +} + +static int v4l2_negotiate_caps(esp_capture_video_src_if_t *src, esp_capture_video_info_t *in_cap, + esp_capture_video_info_t *out_caps) +{ + v4l2_src_t *v4l2 = (v4l2_src_t *)src; + if (v4l2_is_input_supported(v4l2, in_cap->codec) == false) { + return ESP_CAPTURE_ERR_NOT_SUPPORTED; + } + if (v4l2->nego_ok) { + *out_caps = *in_cap; + return ESP_CAPTURE_ERR_OK; + } + do { + struct v4l2_requestbuffers req = { 0 }; + struct v4l2_format format = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix.width = in_cap->width, + .fmt.pix.height = in_cap->height, + }; + int ret = 0; + format.fmt.pix.pixelformat = get_v4l2_type(in_cap->codec); + if ((ret = ioctl(v4l2->fd, VIDIOC_S_FMT, &format)) != 0) { + ESP_LOGE(TAG, "failed to set format codec %d %x ret %d", (int)in_cap->codec, (int)format.fmt.pix.pixelformat, ret); + break; + } + req.count = v4l2->buf_count; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + if (ioctl(v4l2->fd, VIDIOC_REQBUFS, &req) != 0) { + ESP_LOGE(TAG, "failed to require buffer"); + break; + } + for (int i = 0; i < v4l2->buf_count; i++) { + struct v4l2_buffer buf; + memset(&buf, 0, sizeof(buf)); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = i; + if (ioctl(v4l2->fd, VIDIOC_QUERYBUF, &buf) != 0) { + break; + } + v4l2->fb_buffer[i] = (uint8_t *)mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, v4l2->fd, buf.m.offset); + if (!v4l2->fb_buffer[i]) { + ESP_LOGE(TAG, "failed to map buffer"); + break; + } + if (ioctl(v4l2->fd, VIDIOC_QBUF, &buf) != 0) { + ESP_LOGE(TAG, "failed to queue video frame"); + break; + } + } + *out_caps = *in_cap; + v4l2->nego_ok = true; + return ESP_CAPTURE_ERR_OK; + } while (0); + return ESP_CAPTURE_ERR_NOT_SUPPORTED; +} + +static int v4l2_start(esp_capture_video_src_if_t *src) +{ + v4l2_src_t *v4l2 = (v4l2_src_t *)src; + if (v4l2 == NULL) { + return -1; + } + if (v4l2->nego_ok == false) { + ESP_LOGE(TAG, "negotiation not all right"); + return -1; + } + uint32_t type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ioctl(v4l2->fd, VIDIOC_STREAMON, &type); + v4l2->started = true; + return 0; +} + +static int v4l2_acquire_frame(esp_capture_video_src_if_t *src, esp_capture_stream_frame_t *frame) +{ + v4l2_src_t *v4l2 = (v4l2_src_t *)src; + if (v4l2->started == false) { + return -1; + } + struct v4l2_buffer buf = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .memory = V4L2_MEMORY_MMAP, + }; + int ret = ioctl(v4l2->fd, VIDIOC_DQBUF, &buf); + if (ret != 0) { + ESP_LOGE(TAG, "failed to receive video frame ret %d", ret); + return -1; + } + v4l2->fb_used[buf.index] = true; + frame->data = v4l2->fb_buffer[buf.index]; + frame->size = buf.bytesused; + v4l2->v4l2_buf[buf.index] = buf; + esp_cache_msync(frame->data, frame->size, ESP_CACHE_MSYNC_FLAG_DIR_M2C); + return 0; +} + +static int v4l2_release_frame(esp_capture_video_src_if_t *src, esp_capture_stream_frame_t *frame) +{ + v4l2_src_t *v4l2 = (v4l2_src_t *)src; + if (v4l2 == NULL) { + return -1; + } + if (v4l2->started == false) { + return -1; + } + for (int i = 0; i < v4l2->buf_count; i++) { + struct v4l2_buffer *buf = &v4l2->v4l2_buf[i]; + if (v4l2->fb_used[i] && v4l2->fb_buffer[i] == frame->data) { + v4l2->fb_used[i] = 0; + ioctl(v4l2->fd, VIDIOC_QBUF, buf); + return 0; + } + } + ESP_LOGW(TAG, "not found frame %p", frame->data); + return -1; +} + +static int v4l2_stop(esp_capture_video_src_if_t *src) +{ + v4l2_src_t *v4l2 = (v4l2_src_t *)src; + if (v4l2 == NULL) { + return -1; + } + uint32_t type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ioctl(v4l2->fd, VIDIOC_STREAMOFF, &type); + v4l2->nego_ok = false; + return 0; +} + +static int v4l2_close(esp_capture_video_src_if_t *src) +{ + v4l2_src_t *v4l2 = (v4l2_src_t *)src; + if (v4l2 == NULL) { + return -1; + } + for (int i = 0; i < v4l2->buf_count; i++) { + if (v4l2->fb_used[i]) { + v4l2->fb_used[i] = 0; + } + } + if (v4l2->fd > 0) { + close(v4l2->fd); + } + v4l2->fd = 0; + return 0; +} + +esp_capture_video_src_if_t *esp_capture_new_video_v4l2_src(esp_capture_video_v4l2_src_cfg_t *cfg) +{ + if (cfg == NULL || cfg->buf_count == 0) { + return NULL; + } + v4l2_src_t *v4l2 = calloc(1, sizeof(v4l2_src_t)); + if (v4l2 == NULL) { + return NULL; + } + v4l2->base.open = v4l2_open; + v4l2->base.get_support_codecs = v4l2_get_support_codecs; + v4l2->base.negotiate_caps = v4l2_negotiate_caps; + v4l2->base.start = v4l2_start; + v4l2->base.acquire_frame = v4l2_acquire_frame; + v4l2->base.release_frame = v4l2_release_frame; + v4l2->base.stop = v4l2_stop; + v4l2->base.close = v4l2_close; + strncpy(v4l2->dev_name, cfg->dev_name, sizeof(v4l2->dev_name)); + v4l2->buf_count = (cfg->buf_count > MAX_BUFS ? MAX_BUFS : cfg->buf_count); + return &v4l2->base; +} +#endif diff --git a/components/third_party/esp_capture/src/impl/capture_video_src/idf_component.yml b/components/third_party/esp_capture/src/impl/capture_video_src/idf_component.yml new file mode 100644 index 0000000..4ce7c8c --- /dev/null +++ b/components/third_party/esp_capture/src/impl/capture_video_src/idf_component.yml @@ -0,0 +1,12 @@ +dependencies: + espressif/esp_video: + version: "~0.8.0" + rules: + - if: "target in [esp32p4]" + + espressif/esp32-camera: + version: "~2.0.15" + rules: + - if: "target in [esp32s3,esp32s2,esp32]" + esp_capture: + path: ../../../../esp_capture diff --git a/components/third_party/esp_capture/src/share_q.c b/components/third_party/esp_capture/src/share_q.c new file mode 100644 index 0000000..eeb7c31 --- /dev/null +++ b/components/third_party/esp_capture/src/share_q.c @@ -0,0 +1,270 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "msg_q.h" +#include "share_q.h" +#include +#include +#include +#include +#include +#include + +typedef struct { + int ref_count; + void *frame_data; +} share_item_t; + +typedef struct { + msg_q_handle_t q; + bool enable; +} share_user_info_t; + +// Shared queue structure +typedef struct share_q_t { + bool external; + share_q_cfg_t cfg; + share_user_info_t *user_q; + share_item_t *items; + uint8_t valid_count; + uint8_t rp; + uint8_t wp; + pthread_mutex_t lock; + pthread_cond_t cond; +} share_q_t; + +share_q_t *share_q_create(share_q_cfg_t *cfg) +{ + if (cfg == NULL) { + return NULL; + } + share_q_t *q = (share_q_t *)calloc(1, sizeof(share_q_t)); + if (q == NULL) { + return NULL; + } + q->cfg = *cfg; + q->items = (share_item_t *)calloc(cfg->q_count, sizeof(share_item_t)); + q->user_q = (share_user_info_t *)calloc(cfg->user_count, sizeof(share_user_info_t)); + if (q->items == NULL || q->user_q == NULL) { + goto _exit; + } + q->external = cfg->use_external_q; + if (cfg->use_external_q == false) { + for (int i = 0; i < cfg->user_count; i++) { + // TODO default all outports not enabled + // q->user_q[i].enable = true; + q->user_q[i].q = msg_q_create(cfg->q_count, cfg->item_size); + if (q->user_q[i].q == NULL) { + goto _exit; + } + } + q->valid_count = cfg->user_count; + } + pthread_cond_init(&q->cond, NULL); + q->rp = 0; + q->wp = 0; + pthread_mutex_init(&q->lock, NULL); + return q; +_exit: + share_q_destroy(q); + return NULL; +} + +int share_q_set_external(share_q_t *q, uint8_t index, msg_q_handle_t handle) +{ + if (q == NULL || index >= q->cfg.user_count || q->cfg.use_external_q == false) { + return -1; + } + pthread_mutex_lock(&q->lock); + q->user_q[index].q = handle; + pthread_mutex_unlock(&q->lock); + return 0; +} +int share_q_enable(share_q_t *q, uint8_t index, bool enable) +{ + if (q == NULL || index >= q->cfg.user_count) { + return -1; + } + pthread_mutex_lock(&q->lock); + q->user_q[index].enable = enable; + uint8_t valid_count = 0; + for (int i = 0; i < q->cfg.user_count; i++) { + if (q->user_q[i].enable) { + valid_count++; + } + } + q->valid_count = valid_count; + + // When disable, receive all from queues + if (enable == false) { + pthread_mutex_unlock(&q->lock); + void *frame = calloc(1, q->cfg.item_size); + if (frame) { + while (msg_q_recv(q->user_q[index].q, frame, q->cfg.item_size, true) == 0) { + share_q_release(q, frame); + } + free(frame); + } + pthread_mutex_lock(&q->lock); + } + pthread_mutex_unlock(&q->lock); + return 0; +} + +bool share_q_is_enabled(share_q_handle_t q, uint8_t index) +{ + if (q == NULL || index >= q->cfg.user_count) { + return false; + } + pthread_mutex_lock(&q->lock); + bool enabled = q->user_q[index].enable; + pthread_mutex_unlock(&q->lock); + return enabled; +} + +// Get a user queue handle +msg_q_handle_t share_q_get_q(share_q_t *q, uint8_t index) +{ + if (q == NULL || index >= q->cfg.user_count) { + return NULL; + } + return q->user_q[index].q; +} + +// Receive a frame from a user queue +int share_q_recv(share_q_t *q, uint8_t index, void *frame) +{ + if (q == NULL || index >= q->cfg.user_count) { + return -1; + } + int ret = msg_q_recv(q->user_q[index].q, frame, q->cfg.item_size, false); + return ret; +} + +int share_q_recv_all(share_q_handle_t q, void *frame) +{ + if (q == NULL || frame == NULL) { + return -1; + } + // pthread_mutex_lock(&q->lock); + for (int i = 0; i < q->cfg.user_count; i++) { + if (q->user_q[i].enable) { + while (msg_q_recv(q->user_q[i].q, frame, q->cfg.item_size, true) == 0) { + share_q_release(q, frame); + } + } + } + // pthread_mutex_unlock(&q->lock); + return 0; +} + +// Add an item to the shared queue +int share_q_add(share_q_t *q, void *item) +{ + if (q == NULL || item == NULL) { + return -1; + } + pthread_mutex_lock(&q->lock); + if (q->valid_count == 0) { + q->cfg.release_frame(item, q->cfg.ctx); + pthread_mutex_unlock(&q->lock); + return 0; + } + // Check if the next write position will overwrite an unreleased item + int next_wp = (q->wp + 1) % q->cfg.q_count; + while (next_wp == q->rp) { + // Queue is full, cannot add new item + pthread_cond_wait(&q->cond, &q->lock); + } + // Add into items first + share_item_t *q_item = q->items + q->wp; + q_item->frame_data = q->cfg.get_frame_data(item); + q_item->ref_count = q->valid_count; + q->wp = next_wp; + // Add items into user queues + for (int i = 0; i < q->cfg.user_count; i++) { + if (q->user_q[i].enable == false || q->user_q[i].q == NULL) { + continue; + } + if (msg_q_send(q->user_q[i].q, item, q->cfg.item_size) != 0) { + pthread_mutex_unlock(&q->lock); + return -1; + } + } + pthread_mutex_unlock(&q->lock); + return 0; +} + +// Release an item from the shared queue +int share_q_release(share_q_t *q, void *item) +{ + if (q == NULL || item == NULL) { + return -1; + } + pthread_mutex_lock(&q->lock); + // Find and decrement reference count + int rp = q->rp; + int wp = q->wp; + void *frame_data = q->cfg.get_frame_data(item); + while (rp != wp) { + share_item_t *q_item = &q->items[rp]; + if (q_item->frame_data == frame_data) { + q_item->ref_count--; + if (q_item->ref_count == 0) { + q->cfg.release_frame(item, q->cfg.ctx); + q->rp = (q->rp + 1) % q->cfg.q_count; + pthread_cond_signal(&q->cond); + } + pthread_mutex_unlock(&q->lock); + return 0; + } + rp = (rp + 1) % (q->cfg.q_count); + } + pthread_mutex_unlock(&q->lock); + printf("Not found frame data in q %p\n", frame_data); + return -1; +} + +void share_q_destroy(share_q_t *q) +{ + if (q == NULL) { + return; + } + if (q->items) { + free(q->items); + } + if (q->user_q) { + if (q->external == false) { + for (int i = 0; i < q->cfg.user_count; i++) { + if (q->user_q[i].q) { + msg_q_destroy(q->user_q[i].q); + } + } + } + free(q->user_q); + } + pthread_mutex_destroy(&(q->lock)); + pthread_cond_destroy(&(q->cond)); + free(q); +} \ No newline at end of file diff --git a/components/third_party/esp_capture/src/share_q.h b/components/third_party/esp_capture/src/share_q.h new file mode 100644 index 0000000..d905cd3 --- /dev/null +++ b/components/third_party/esp_capture/src/share_q.h @@ -0,0 +1,179 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "msg_q.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Shared queue configuration + */ +typedef struct { + uint8_t user_count; /*!< Output user counts */ + uint8_t q_count; /*!< Maximum queue count for output user */ + int item_size; /*!< Item size to fill into queue */ + void *(*get_frame_data)(void *item); /*!< Function to get frame data (used to distinguish frame) */ + int (*release_frame)(void *item, void *ctx); /*!< Function to release frame */ + void *ctx; /*!< Input context for release frame */ + bool use_external_q; /*!< Input context for release frame */ +} share_q_cfg_t; + +/** + * @brief Shared queue handle + * + * @note This shared queue is designed for distributing frame data. It has one input + * and multiple output consumers. The data is shared by reference and is only + * released when all consumers have finished using the frame. When input data + * arrives, the frame is pushed to all active output queues. Each consumer retrieves + * frame data from the queue and releases it when done. The shared queue tracks + * the release actions of consumers and uses a reference count to determine when + * to release the actual frame data. + */ +typedef struct share_q_t *share_q_handle_t; + +/** + * @brief Create share queue + * + * @param[in] cfg Share queue configuration + * @return + * - NULL No resources for share queue + * - Others Share queue handle + * + */ +share_q_handle_t share_q_create(share_q_cfg_t *cfg); + +/** + * @brief Set external queue for share queue by index + * + * @param[in] q Share queue handle + * @param[in] index Index of the queue + * @param[in] handle Message queue handle + * + * @return + * - 0 On success + * - -1 Invalid input arguments + * + */ +int share_q_set_external(share_q_handle_t q, uint8_t index, msg_q_handle_t handle); + +/** + * @brief Receive frame from share queue by index + * + * @param[in] q Share queue handle + * @param[in] index Index of the queue + * @param[out] frame Frame information to be filled + * + * @return + * - 0 On success + * - -1 Invalid input arguments + * + */ +int share_q_recv(share_q_handle_t q, uint8_t index, void *frame); + +/** + * @brief Receive all frames from share queue and release input frame accordingly + * + * @param[in] q Share queue handle + * @param[out] frame Frame information to be filled + * + * @return + * - 0 On success + * - -1 Invalid input arguments + * + */ +int share_q_recv_all(share_q_handle_t q, void *frame); + +/** + * @brief Enable or disable shared queue output by index + * + * @note Enabling or disabling can happen at any time. When disabled, input frames + * will not be inserted into the queue of the specified output index. + * + * @param[in] q Shared queue handle + * @param[in] index Output index + * @param[in] enable Enable or disable + * + * @return + * - 0 On success + * - -1 Invalid input arguments + * + */ +int share_q_enable(share_q_handle_t q, uint8_t index, bool enable); + +/** + * @brief Check whether output queue of the specified index is enabled + * + * @param[in] q Shared queue handle + * @param[in] index Output index + * + * @return + * - true Queue of the specified index is enabled + * - false Queue of the specified index is disabled + * + */ +bool share_q_is_enabled(share_q_handle_t q, uint8_t index); + +/** + * @brief Add frame into share queue + * + * @param[in] q Shared queue handle + * @param[in] item Frame to be inserted into queues + * + * @return + * - 0 On success + * - -1 Fail to add frame + * + */ +int share_q_add(share_q_handle_t q, void *item); + +/** + * @brief Release frame + * + * @param[in] q Shared queue handle + * @param[in] item Frame to be released + * + * @return + * - 0 On success + * - -1 Fail to release frame + * + */ +int share_q_release(share_q_handle_t q, void *item); + +/** + * @brief Destroy share queue + * + * @param[in] q Shared queue handle + * + */ +void share_q_destroy(share_q_handle_t q); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_webrtc/CMakeLists.txt b/components/third_party/esp_webrtc/CMakeLists.txt new file mode 100755 index 0000000..d298959 --- /dev/null +++ b/components/third_party/esp_webrtc/CMakeLists.txt @@ -0,0 +1,15 @@ + +set(component_srcdirs "src") + +# AppRTC +set(signalling_srcdirs "impl/apprtc_signal" "impl/whip_signal") +set(signalling_incdirs "impl/apprtc_signal" "impl/whip_signal/include") + +list(APPEND component_srcdirs ${signalling_srcdirs}) + +idf_component_register( + SRC_DIRS ${component_srcdirs} + INCLUDE_DIRS ./include ${signalling_incdirs} + REQUIRES mbedtls json esp_http_client esp_websocket_client esp_netif media_lib_sal + esp_capture webrtc_utils esp_codec_dev av_render +) diff --git a/components/third_party/esp_webrtc/README.md b/components/third_party/esp_webrtc/README.md new file mode 100644 index 0000000..f8df49c --- /dev/null +++ b/components/third_party/esp_webrtc/README.md @@ -0,0 +1,123 @@ +# ESP_WebRTC + +`esp_webrtc` is an integrated solution for building WebRTC connections. Its base code is derived from [libpeer](https://github.com/sepfy/libpeer.git) with enhancements and improvements. + +## Enhanced Features + +Comparing with original `libpeer` code realization, `esp_peer` do following improvements: +1. Full support for TURN (RFC5766 and RFC8656) +2. Connection speed optimized for multiple candidates +3. Supports both Controlling and Controlled roles +4. Support RTP NACK report and handling +5. Support SACK for SCTP, support huge data split and combination +6. Easy to add customized signaling and peer connection implementation +7. Support send and receive in separate task to avoid affect each other + +## Architecture + +`esp_webrtc` consists of three main components: + +### 1. Signaling +Signaling is used to detect peer and exchange SDP information or control commands. +In `esp_webrtc`, signaling is abstracted as `esp_peer_signaling`. +You can refer to the `esp_signaling_get_apprtc_impl` sample code for implementation details. + +### 2. PeerConnection +PeerConnection uses ICE to find connectable peer's IP and port, then establishes connection with the peer. +After the connection is established, users can: +- Send and receive media data (RTP payload) through PeerConnection +- Send and receive user data (DataChannel over SCTP) through PeerConnection + +In `esp_webrtc` PeerConnection is abstracted as `esp_peer`. +A default implementation is provided as `esp_peer_get_default_impl`, which users can utilize if they only require the PeerConnection functionality. + +### 3. WebRTC Solution +The WebRTC solution combines signaling, PeerConnection, and a media system. +It provides a high-level API to simplify WebRTC application development. +Users only need to configure basic settings like audio and video codecs. +`esp_webrtc` will do following things automatically: +- Capture and transmit audio and video streams +- Automatically render received streams using `av_render` + +For customization, users only need to modify the signaling implementation. + +## Usage of `esp_peer` +If user only need the Peer Connection protocol and don't require the entire WebRTC solution, can utilize the `esp_peer` API + +1. **Create a PeerConnection** + Call `esp_peer_open` to create a PeerConnection. + This API should be invoked after gathering the ICE server URL. + If the peer is a public server, can set `server_num` to 0. + +2. **Run the PeerConnection Main Loop** + After opening the PeerConnection, create a task to periodically call the main loop: `esp_peer_main_loop`. + +3. **Initiate a New Peer Connection** + Call `esp_peer_new_connection` to initiate a new connection. + This gathers local candidates and reports the local SDP via the `on_msg` callback. + The user must send the SDP to the signaling server. + +4. **Set Remote SDP or Candidates** + Upon receiving the remote SDP from the signaling server, call `esp_peer_send_msg` to set the remote SDP or candidates. + +5. **Connection Establishment** + `esp_peer` attempts to build the connection and reports the connection status via the `on_state` callback. + +6. **Send Data** + - Send audio: `esp_peer_send_audio` + - Send video: `esp_peer_send_video` + - Send data through the data channel: `esp_peer_send_data` + +7. **Receive Data** + Peer-sent data is received via callbacks: + - `on_audio_info` and `on_audio_data` for audio information and data + - `on_video_info` and `on_video_data` for video information and data + - `on_data` for data channel messages + +8. **Disconnect the Peer** + Use `esp_peer_disconnect` to disconnect from the peer. + +9. **Close the PeerConnection** + Use `esp_peer_close` to close the PeerConnection. + +## Typical Call Sequence of `esp_webrtc` + +### Connection Build Flow: +```mermaid +sequenceDiagram +APP ->> esp_webrtc: esp_webrtc_start +esp_webrtc ->> esp_signaling: esp_signaling_start +esp_signaling -->> esp_webrtc: on_ice_info() +esp_webrtc ->> esp_peer: esp_peer_open +esp_signaling -->> esp_webrtc: on_connected() +esp_webrtc ->> esp_peer: esp_peer_new_connection +esp_peer -->> esp_webrtc: on_msg(ESP_PEER_MSG_TYPE_SDP) +esp_webrtc ->> esp_signaling: esp_peer_signaling_send_msg +esp_signaling -->> esp_webrtc: on_msg(ESP_PEER_SIGNALING_MSG_SDP) +esp_webrtc ->> esp_peer: esp_peer_send_msg +esp_peer -->> esp_webrtc: on_state(ESP_PEER_STATE_CONNECTED) +``` + +### Stop Flow by User: +```mermaid +sequenceDiagram +APP ->> esp_webrtc: esp_webrtc_stop +esp_webrtc ->> esp_peer: esp_peer_disconnect +esp_webrtc ->> esp_peer: esp_peer_close +esp_webrtc ->> esp_signaling: esp_peer_signaling_stop +``` + +### Disconnect Flow When Peer Normally Leaves (Signaling Still Active, Waiting for Peer to Rejoin): +```mermaid +sequenceDiagram +esp_signaling -->> esp_webrtc: on_msg(ESP_PEER_SIGNALING_MSG_BYE) +esp_webrtc ->> esp_peer: esp_peer_disconnect +esp_webrtc ->> esp_peer: esp_peer_new_connection +``` + +## Simple Usage of `esp_webrtc` + +1. Build the capture and render system. +2. Configure your WebRTC settings. +3. Start WebRTC call `esp_webrtc_start`. +4. Stop WebRTC call `esp_webrtc_stop`. diff --git a/components/third_party/esp_webrtc/idf_component.yml b/components/third_party/esp_webrtc/idf_component.yml new file mode 100755 index 0000000..d8cd73e --- /dev/null +++ b/components/third_party/esp_webrtc/idf_component.yml @@ -0,0 +1,9 @@ +## IDF Component Manager Manifest File +description: Espressif WebRTC Library +version: 0.9.0 + +dependencies: + espressif/nghttp: "~1.65.0" + espressif/esp_websocket_client: "~1.4.0" + espressif/esp_codec_dev: "~1.3.4" + espressif/esp_libsrtp: "~1.0.0" diff --git a/components/third_party/esp_webrtc/impl/apprtc_signal/CMakeLists.txt b/components/third_party/esp_webrtc/impl/apprtc_signal/CMakeLists.txt new file mode 100755 index 0000000..e3d6b4c --- /dev/null +++ b/components/third_party/esp_webrtc/impl/apprtc_signal/CMakeLists.txt @@ -0,0 +1,9 @@ + +file(GLOB CODES "*.c") + +idf_component_register( + SRCS ${CODES} + INCLUDE_DIRS ./ + REQUIRES json esp_http_client esp_websocket_client +) + diff --git a/components/third_party/esp_webrtc/impl/apprtc_signal/https_client.c b/components/third_party/esp_webrtc/impl/apprtc_signal/https_client.c new file mode 100644 index 0000000..b7ee6fc --- /dev/null +++ b/components/third_party/esp_webrtc/impl/apprtc_signal/https_client.c @@ -0,0 +1,187 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include +#include "esp_log.h" +#include "https_client.h" +#include "esp_tls.h" +#include +#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE +#include "esp_crt_bundle.h" +#endif +#include "esp_http_client.h" + +static const char *TAG = "HTTPS_CLIENT"; + +typedef struct { + http_header_t header; + http_body_t body; + uint8_t *data; + int fill_size; + int size; + void *ctx; +} http_info_t; + +esp_err_t _http_event_handler(esp_http_client_event_t *evt) +{ + http_info_t *info = evt->user_data; + switch (evt->event_id) { + case HTTP_EVENT_ERROR: + ESP_LOGD(TAG, "HTTP_EVENT_ERROR"); + break; + case HTTP_EVENT_ON_CONNECTED: + ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED"); + break; + case HTTP_EVENT_HEADER_SENT: + ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT"); + break; + case HTTP_EVENT_ON_HEADER: + if (info->header) { + info->header(evt->header_key, evt->header_value, info->ctx); + } + ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, + evt->header_value); + break; + case HTTP_EVENT_ON_DATA: + ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len); + if (!esp_http_client_is_chunked_response(evt->client)) { + int content_len = esp_http_client_get_content_length(evt->client); + if (info->data == NULL && content_len) { + info->data = malloc(content_len); + if (info->data) { + info->size = content_len; + } + } + if (evt->data_len && info->fill_size + evt->data_len <= info->size) { + memcpy(info->data + info->fill_size, evt->data, evt->data_len); + info->fill_size += evt->data_len; + } + } + break; + case HTTP_EVENT_ON_FINISH: + ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH"); + if (info->fill_size && info->body) { + http_resp_t resp = { + .data = (char *)info->data, + .size = info->fill_size, + }; + info->body(&resp, info->ctx); + } + break; + case HTTP_EVENT_DISCONNECTED: + ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED"); + int mbedtls_err = 0; + esp_err_t err = esp_tls_get_and_clear_last_error((esp_tls_error_handle_t)evt->data, + &mbedtls_err, NULL); + if (err != 0) { + ESP_LOGD(TAG, "Last esp error code: 0x%x", err); + ESP_LOGD(TAG, "Last mbedtls failure: 0x%x", mbedtls_err); + } + break; + case HTTP_EVENT_REDIRECT: + esp_http_client_set_redirection(evt->client); + break; + } + return ESP_OK; +} + +int https_send_request(const char *method, char **headers, const char *url, char *data, http_header_t header_cb, http_body_t body, void *ctx) +{ + http_info_t info = { + .body = body, + .header = header_cb, + .ctx = ctx, + }; + esp_http_client_config_t config = { + .url = url, + .event_handler = _http_event_handler, +#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE + .crt_bundle_attach = esp_crt_bundle_attach, +#endif + .user_data = &info, + .timeout_ms = 10000, // Change default timeout to be 10s + }; + esp_http_client_handle_t client = esp_http_client_init(&config); + if (client == NULL) { + ESP_LOGE(TAG, "Fail to init client"); + return -1; + } + // POST + int err = 0; + esp_http_client_set_url(client, url); + if (strcmp(method, "POST") == 0) { + esp_http_client_set_method(client, HTTP_METHOD_POST); + } else if (strcmp(method, "DELETE") == 0) { + esp_http_client_set_method(client, HTTP_METHOD_DELETE); + } else if (strcmp(method, "PATCH") == 0) { + esp_http_client_set_method(client, HTTP_METHOD_PATCH); + } else { + err = -1; + goto _exit; + } + bool has_content_type = false; + if (headers) { + int i = 0; + // TODO suppose header writable + while (headers[i]) { + char *dot = strchr(headers[i], ':'); + if (dot) { + *dot = 0; + if (strcmp(headers[i], "Content-Type") == 0) { + has_content_type = true; + } + char *cont = dot + 2; + esp_http_client_set_header(client, headers[i], cont); + *dot = ':'; + } + i++; + } + } + if (data != NULL) { + if (has_content_type == false) { + esp_http_client_set_header(client, "Content-Type", "text/plain;charset=UTF-8"); + } + esp_http_client_set_post_field(client, data, strlen(data)); + } + err = esp_http_client_perform(client); + if (err == ESP_OK) { + ESP_LOGI(TAG, "HTTP POST Status = %d, content_length = %lld", + esp_http_client_get_status_code(client), + esp_http_client_get_content_length(client)); + } else { + ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err)); + } +_exit: + esp_http_client_cleanup(client); + if (info.data) { + free(info.data); + } + return err; +} + +int https_post(const char *url, char **headers, char *data, http_header_t header_cb, http_body_t body, void *ctx) +{ + return https_send_request("POST", headers, url, data, header_cb, body, ctx); +} diff --git a/components/third_party/esp_webrtc/impl/apprtc_signal/https_client.h b/components/third_party/esp_webrtc/impl/apprtc_signal/https_client.h new file mode 100644 index 0000000..e118ac7 --- /dev/null +++ b/components/third_party/esp_webrtc/impl/apprtc_signal/https_client.h @@ -0,0 +1,95 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Https response data + */ +typedef struct { + char *data; /*!< Response data */ + int size; /*!< Response data size */ +} http_resp_t; + +/** + * @brief Https response callback + * + * @param[in] resp Body content + * @param[in] ctx User context + */ +typedef void (*http_body_t)(http_resp_t *resp, void *ctx); + +/** + * @brief Https header callback + * + * @param[in] key Header key + * @param[in] key Header value + * @param[in] ctx User context + */ +typedef void (*http_header_t)(const char *key, const char *value, void *ctx); + +/** + * @brief Send https requests + * + * @note This API is run in synchronized until response or error returns + * + * @param[in] method HTTP method to do + * @param[in] headers HTTP headers, headers are array of "Type: Info", last one need set to NULL + * @param[in] url HTTPS URL + * @param[in] data Content data to be sent + * @param[in] header_cb Header callback + * @param[in] body Body callback + * @param[in] ctx User context + * + * @return + * - 0 On success + * - Others Fail to do https request + */ +int https_send_request(const char *method, char **headers, const char *url, char *data, http_header_t header_cb, http_body_t body_cb, void *ctx); + +/** + * @brief Do post https request + * + * @note This API will internally call `https_send_request` + * + * @param[in] url HTTPS URL to post + * @param[in] headers HTTP headers, headers are array of "Type: Info", last one need set to NULL + * @param[in] data Content data to be sent + * @param[in] header_cb Header callback + * @param[in] body Body callback + * @param[in] ctx User context + * + * @return + * - 0 On success + * - Others Fail to do https request + */ +int https_post(const char *url, char **headers, char *data, http_header_t header_cb, http_body_t body, void *ctx); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_webrtc/impl/apprtc_signal/signal_default.c b/components/third_party/esp_webrtc/impl/apprtc_signal/signal_default.c new file mode 100644 index 0000000..fac43f0 --- /dev/null +++ b/components/third_party/esp_webrtc/impl/apprtc_signal/signal_default.c @@ -0,0 +1,703 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include +#include +#include +#include +#include "cJSON.h" +#include "esp_log.h" +#include "media_lib_os.h" +#include "esp_peer_signaling.h" +#include +#include +#include +#include +#include "esp_netif.h" +#include +#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE +#include "esp_crt_bundle.h" +#endif +#include "esp_websocket_client.h" +#include "esp_tls.h" + +#include + +#include "https_client.h" + +#define TAG "APPRTC_SIG" + +typedef struct { + char *client_id; + bool is_initiator; + char *wss_url; + char *room_link; + char *wss_post_url; + char *room_id; + char *ice_server; + cJSON *msg_json; + cJSON *root; + char *base_url; +} client_info_t; + +typedef struct { + esp_websocket_client_handle_t ws; + int connected; +} wss_client_t; + +typedef struct { + client_info_t client_info; + esp_peer_signaling_ice_info_t ice_info; + wss_client_t *wss_client; + esp_peer_signaling_cfg_t cfg; + uint32_t ice_expire_time; + bool ice_reloading; + bool ice_reload_stop; +} wss_sig_t; + +static int wss_signal_stop(esp_peer_signaling_handle_t sig); + +static void free_client_info(client_info_t *info) +{ + if (info->root) { + cJSON_Delete(info->root); + } + if (info->base_url) { + free(info->base_url); + info->base_url = NULL; + } + memset(info, 0, sizeof(client_info_t)); +} + +static void free_ice_info(esp_peer_signaling_ice_info_t *info) +{ + if (info->server_info.stun_url) { + free(info->server_info.stun_url); + } + if (info->server_info.user) { + free(info->server_info.user); + } + if (info->server_info.psw) { + free(info->server_info.psw); + } + memset(info, 0, sizeof(esp_peer_signaling_ice_info_t)); +} + +static void get_client_info(http_resp_t *resp, void *ctx) +{ + const char *data = (const char *)resp->data; + cJSON *root = cJSON_Parse((const char *)data); + if (!root) { + return; + } + client_info_t *info = (client_info_t *)ctx; + cJSON *result_json = cJSON_GetObjectItem(root, "result"); + cJSON *params_json = cJSON_GetObjectItem(root, "params"); + if (result_json) { + ESP_LOGI(TAG, "result %s", result_json->valuestring); + } else { + cJSON_Delete(root); + return; + } + + if (params_json) { + cJSON *item = cJSON_GetObjectItem(params_json, "client_id"); + if (item) { + info->client_id = item->valuestring; + } + item = cJSON_GetObjectItem(params_json, "is_initiator"); + if (item) { + info->is_initiator = (strcmp(item->valuestring, "true") == 0); + } + item = cJSON_GetObjectItem(params_json, "wss_post_url"); + if (item) { + info->wss_post_url = item->valuestring; + } + item = cJSON_GetObjectItem(params_json, "room_id"); + if (item) { + info->room_id = item->valuestring; + } + item = cJSON_GetObjectItem(params_json, "wss_url"); + if (item) { + info->wss_url = item->valuestring; + } + item = cJSON_GetObjectItem(params_json, "ice_server_url"); + if (item) { + info->ice_server = item->valuestring; + } + item = cJSON_GetObjectItem(params_json, "messages"); + if (item) { + info->msg_json = item; + } + info->root = root; + printf("Initials set to %d\n", info->is_initiator); + return; + } + cJSON_Delete(root); +} + +static void credential_body(http_resp_t *resp, void *ctx) +{ + const char *data = (const char *)resp->data; + cJSON *root = cJSON_Parse((const char *)data); + if (!root) { + return; + } + esp_peer_signaling_ice_info_t *ice = (esp_peer_signaling_ice_info_t *)ctx; + do { + cJSON *ice_servers = cJSON_GetObjectItem(root, "iceServers"); + if (ice_servers == NULL) { + break; + } + cJSON *server_arr = cJSON_GetArrayItem(ice_servers, 0); + if (server_arr == NULL) { + break; + } + cJSON *url_arr = cJSON_GetObjectItem(server_arr, "urls"); + if (url_arr == NULL) { + break; + } + cJSON *urls = cJSON_GetArrayItem(url_arr, 0); + if (urls == NULL) { + break; + } + // TODO only set first url + ice->server_info.stun_url = strdup(urls->valuestring); + if (ice->server_info.stun_url == NULL) { + break; + } + cJSON *user = cJSON_GetObjectItem(server_arr, "username"); + if (user == NULL) { + break; + } + ice->server_info.user = strdup(user->valuestring); + cJSON *psw = cJSON_GetObjectItem(server_arr, "credential"); + if (psw == NULL) { + break; + } + ice->server_info.psw = strdup(psw->valuestring); + printf("Got url:%s user_name: %s psw:%s\n", ice->server_info.stun_url, ice->server_info.user, ice->server_info.psw); + } while (0); + cJSON_Delete(root); +} + +static void destroy_wss(wss_client_t *wss) +{ + if (wss->ws) { + esp_websocket_client_stop(wss->ws); + esp_websocket_client_destroy(wss->ws); + } + free(wss); +} + +static int send_register(wss_client_t *wss, client_info_t *info) +{ + cJSON *json = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "cmd", "register"); + cJSON_AddStringToObject(json, "roomid", info->room_id); + cJSON_AddStringToObject(json, "clientid", info->client_id); + int ret = 0; + char *payload = cJSON_PrintUnformatted(json); + cJSON_Delete(json); + if (payload) { + ESP_LOGI(TAG, "send to remote : %s", payload); + ret = esp_websocket_client_send_text(wss->ws, payload, strlen(payload), portMAX_DELAY); + free(payload); + } + return ret; +} + +static int send_bye(wss_client_t *wss, client_info_t *info) +{ + cJSON *json = cJSON_CreateObject(); + cJSON *msg = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "cmd", "send"); + cJSON_AddStringToObject(msg, "type", "bye"); + char *msg_body = cJSON_PrintUnformatted(msg); + if (msg_body) { + cJSON_AddStringToObject(json, "msg", msg_body); + } + char *payload = cJSON_PrintUnformatted(json); + cJSON_Delete(json); + cJSON_Delete(msg); + int ret = 0; + if (payload) { + ESP_LOGI(TAG, "send to remote : %s", payload); + ret = esp_websocket_client_send_text(wss->ws, payload, strlen(payload), portMAX_DELAY); + free(payload); + } + free(msg_body); + return ret > 0 ? 0 : -1; +} + +/* The custom on_connect handler for this instance of the websocket code. */ +static int on_connect(void *user) +{ + wss_sig_t *sg = user; + wss_client_t *wss = sg->wss_client; + wss->connected = true; + send_register(wss, &sg->client_info); + if (sg->cfg.on_connected) { + sg->cfg.on_connected(sg->cfg.ctx); + } + return 0; +} + +/* The custom on_text handler for this instance of the websocket code. */ +static int on_text(void *user, const char *text, size_t len) +{ + if (len == 0) { + return 0; + } + // printf("on_text(user, ws, '%.*s', %zd)\n", (int) len, text, len); + wss_sig_t *sg = user; + if (sg->cfg.on_msg) { + cJSON *_json = cJSON_Parse(text); + cJSON *msg = NULL; + do { + if (_json == NULL) { + break; + } + msg = cJSON_GetObjectItem(_json, "msg"); + // Json string in json + cJSON *method; + if (msg == NULL) { + method = cJSON_GetObjectItem(_json, "type"); + if (method == NULL) { + break; + } + msg = _json; + } else { + msg = cJSON_Parse(msg->valuestring); + method = cJSON_GetObjectItem(msg, "type"); + if (method == NULL) { + break; + } + } + if (strcmp(method->valuestring, "offer") == 0) { + cJSON *sdp = cJSON_GetObjectItem(msg, "sdp"); + if (sdp) { + esp_peer_signaling_msg_t msg = { + .type = ESP_PEER_SIGNALING_MSG_SDP, + .data = (uint8_t *)sdp->valuestring, + .size = strlen(sdp->valuestring), + }; + sg->cfg.on_msg(&msg, sg->cfg.ctx); + } + } else if (strcmp(method->valuestring, "answer") == 0) { + cJSON *sdp = cJSON_GetObjectItem(msg, "sdp"); + if (sdp) { + esp_peer_signaling_msg_t msg = { + .type = ESP_PEER_SIGNALING_MSG_SDP, + .data = (uint8_t *)sdp->valuestring, + .size = strlen(sdp->valuestring), + }; + sg->cfg.on_msg(&msg, sg->cfg.ctx); + } + } else if (strcmp(method->valuestring, "bye") == 0) { + // Peer closed + esp_peer_signaling_msg_t msg = { + .type = ESP_PEER_SIGNALING_MSG_BYE, + }; + sg->cfg.on_msg(&msg, sg->cfg.ctx); + // When peer leave change rule to caller directly + ESP_LOGI(TAG, "Peer leaved become controlling now"); + sg->ice_info.is_initiator = true; + } else if (strcmp(method->valuestring, "candidate") == 0) { + cJSON *candidate = cJSON_GetObjectItem(msg, "candidate"); + if (candidate) { + esp_peer_signaling_msg_t msg = { + .type = ESP_PEER_SIGNALING_MSG_CANDIDATE, + .data = (uint8_t *)candidate->valuestring, + .size = strlen(candidate->valuestring), + }; + sg->cfg.on_msg(&msg, sg->cfg.ctx); + } + } else if (strcmp(method->valuestring, "customized") == 0) { + cJSON *custom_data = cJSON_GetObjectItem(msg, "data"); + if (custom_data) { + esp_peer_signaling_msg_t msg = { + .type = ESP_PEER_SIGNALING_MSG_CUSTOMIZED, + .data = (uint8_t *)custom_data->valuestring, + .size = strlen(custom_data->valuestring), + }; + sg->cfg.on_msg(&msg, sg->cfg.ctx); + } + } + } while (0); + if (msg == NULL) { + ESP_LOGE(TAG, "Bad json input"); + } else if (msg != _json) { + cJSON_Delete(msg); + } + cJSON_Delete(_json); + } + return 0; +} + +/* The custom on_close handler for this instance of the websocket code. */ +static int on_close(void *user, int code) +{ + printf("on_close code %d\n", code); + wss_sig_t *sg = (wss_sig_t *)user; + if (sg->cfg.on_close) { + sg->cfg.on_close(sg->cfg.ctx); + } + return 0; +} + +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 void websocket_event_handler(void *ctx, esp_event_base_t base, int32_t event_id, void *event_data) +{ + esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data; + switch (event_id) { + case WEBSOCKET_EVENT_CONNECTED: + ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED"); + on_connect(ctx); + break; + case WEBSOCKET_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED"); + on_close(ctx, (int)data->error_handle.esp_ws_handshake_status_code); + 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); + } + break; + case WEBSOCKET_EVENT_DATA: + on_text(ctx, data->data_ptr, data->data_len); + break; + case WEBSOCKET_EVENT_ERROR: + ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR"); + 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); + } + break; + } +} + +int create_wss(wss_sig_t *sg) +{ + wss_client_t *wss = calloc(1, sizeof(wss_client_t)); + if (wss == NULL) { + return -1; + } + char origin[128]; + snprintf(origin, 128, "Origin: %s\r\n", sg->client_info.base_url); + + esp_websocket_client_config_t ws_cfg = { + .uri = sg->client_info.wss_url, + .headers = origin, +#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE + .crt_bundle_attach = esp_crt_bundle_attach, +#endif + // .keep_alive_enable = true, + .reconnect_timeout_ms = 60 * 1000, + // .ping_interval_sec = 30, + .network_timeout_ms = 10000, + // .keep_alive_interval = 60, + .buffer_size = 20 * 1024, + }; + ESP_LOGI(TAG, "Connecting to %s...", ws_cfg.uri); + wss->ws = esp_websocket_client_init(&ws_cfg); + do { + if (wss->ws == NULL) { + break; + } + esp_websocket_register_events(wss->ws, WEBSOCKET_EVENT_ANY, websocket_event_handler, (void *)sg); + int ret = esp_websocket_client_start(wss->ws); + if (ret != 0) { + break; + } + sg->wss_client = wss; + return ret; + } while (0); + return -1; +} + +static void wss_send_offer_msg(wss_sig_t *sg) +{ + cJSON *msg = sg->client_info.msg_json; + if (msg == NULL) { + return; + } + int n = cJSON_GetArraySize(msg); + for (int i = 0; i < n; i++) { + cJSON *item = cJSON_GetArrayItem(msg, i); + if (item == NULL || item->valuestring == NULL) { + continue; + } + char *msg_body = item->valuestring; + ESP_LOGD(TAG, "Send initial sdp message"); + on_text(sg, msg_body, strlen(msg_body)); + } +} + +static char *get_base_url(char *room_url) +{ + char *url = strdup(room_url); + if (url) { + char *sep = strstr(url, "/join"); + if (sep) { + *sep = 0; + } + } + return url; +} + +static void update_ice_expire_time(wss_sig_t *sg) +{ + uint32_t expire_time = atoi(sg->ice_info.server_info.user); + uint32_t cur = time(NULL); + if (expire_time > cur && expire_time < cur + 3600) { + sg->ice_expire_time = expire_time - cur; + if (sg->ice_expire_time > 60) { + sg->ice_expire_time -= 60; + } + } else { + // Reload every 5 minutes + sg->ice_expire_time = 300; + } +} + +static void reload_ice_task(void *arg) +{ + wss_sig_t *sg = (wss_sig_t *)arg; + uint32_t start = time(NULL); + update_ice_expire_time(sg); + while (!sg->ice_reload_stop) { + media_lib_thread_sleep(50); + uint32_t cur = time(NULL); + if (cur > start + sg->ice_expire_time) { + int ret = https_post(sg->client_info.ice_server, NULL, NULL, NULL, credential_body, &sg->ice_info); + if (ret != 0) { + break; + } + // Also send empty message to keep alive + char *heart_beat = "{\"cmd\":\"send\",\"msg\":\"{\\\"error\\\":\\\"\\\"}\"}"; + esp_websocket_client_send_text(sg->wss_client->ws, heart_beat, strlen(heart_beat), portMAX_DELAY); + update_ice_expire_time(sg); + if (sg->cfg.on_ice_info) { + sg->cfg.on_ice_info(&sg->ice_info, sg->cfg.ctx); + } + start = cur; + } + } + sg->ice_reload_stop = false; + sg->ice_reloading = false; + media_lib_thread_destroy(NULL); +} + +static int stop_reload_ice_timer(wss_sig_t *sg) +{ + sg->ice_reload_stop = true; + while (sg->ice_reloading) { + media_lib_thread_sleep(50); + } + return 0; +} + +static int start_reload_ice_timer(wss_sig_t *sg) +{ + media_lib_thread_handle_t handle; + media_lib_thread_create_from_scheduler(&handle, "ice_reload", reload_ice_task, sg); + if (handle) { + sg->ice_reloading = true; + return 0; + } + return -1; +} + +static int wss_signal_start(esp_peer_signaling_cfg_t *cfg, esp_peer_signaling_handle_t *h) +{ + if (cfg->signal_url == NULL || cfg == NULL || h == NULL) { + return -1; + } + wss_sig_t *sg = calloc(1, sizeof(wss_sig_t)); + if (sg == NULL) { + return -1; + } + int ret = 0; + // Http post to get client info firstly + https_post(cfg->signal_url, NULL, NULL, NULL, get_client_info, &sg->client_info); + if (sg->client_info.client_id == NULL) { + ESP_LOGE(TAG, "Fail to get client id for room %s", cfg->signal_url); + ret = -1; + goto __exit; + } + sg->client_info.base_url = get_base_url(cfg->signal_url); + if (sg->client_info.base_url == NULL) { + ret = -1; + goto __exit; + } + if (sg->client_info.ice_server == NULL) { + ESP_LOGE(TAG, "Fail to get server url %s", cfg->signal_url); + ret = -1; + goto __exit; + } + https_post(sg->client_info.ice_server, NULL, NULL, NULL, credential_body, &sg->ice_info); + sg->ice_info.is_initiator = sg->client_info.is_initiator; + if (sg->ice_info.server_info.psw == NULL) { + printf("Fail to get password\n"); + ret = -1; + goto __exit; + } + // Copy configuration + sg->cfg = *cfg; + if (sg->cfg.on_ice_info) { + sg->cfg.on_ice_info(&sg->ice_info, sg->cfg.ctx); + } + ESP_LOGI(TAG, "Registering signaling channel."); + *h = sg; + ret = create_wss(sg); + if (ret == 0) { + start_reload_ice_timer(sg); + wss_send_offer_msg(sg); + return ret; + } +__exit: + *h = NULL; + wss_signal_stop(sg); + return ret; +} + +static int send_customized_data(wss_client_t *wss, esp_peer_signaling_msg_t *msg) +{ + if (msg->data[msg->size] != 0) { + ESP_LOGW(TAG, "Not a valid string"); + return -1; + } + cJSON *json = cJSON_CreateObject(); + cJSON *msg_obj = cJSON_CreateObject(); + cJSON_AddStringToObject(json, "cmd", "send"); + cJSON_AddStringToObject(msg_obj, "type", "customized"); + cJSON_AddStringToObject(msg_obj, "data", (char *)msg->data); + char *msg_body = cJSON_PrintUnformatted(msg_obj); + if (msg_body) { + cJSON_AddStringToObject(json, "msg", msg_body); + } + char *payload = cJSON_PrintUnformatted(json); + cJSON_Delete(json); + cJSON_Delete(msg_obj); + int ret = 0; + if (payload) { + ESP_LOGI(TAG, "send to remote : %s", payload); + ret = esp_websocket_client_send_text(wss->ws, payload, strlen(payload), portMAX_DELAY); + free(payload); + } + free(msg_body); + return ret > 0 ? 0 : -1; +} + +static int wss_signal_send_msg(esp_peer_signaling_handle_t h, esp_peer_signaling_msg_t *msg) +{ + wss_sig_t *sg = (wss_sig_t *)h; + if (msg->type == ESP_PEER_SIGNALING_MSG_CUSTOMIZED) { + return send_customized_data(sg->wss_client, msg); + } + char request_room[128]; + snprintf(request_room, 128, "%s/message/%s/%s", + sg->client_info.base_url, + sg->client_info.room_id, sg->client_info.client_id); + ESP_LOGI(TAG, "Begin to send offer to %s", request_room); + if (msg->type == ESP_PEER_SIGNALING_MSG_BYE) { + return send_bye(sg->wss_client, &sg->client_info); + } + cJSON *json = cJSON_CreateObject(); + if (msg->type == ESP_PEER_SIGNALING_MSG_SDP) { + if (sg->ice_info.is_initiator) { + cJSON_AddStringToObject(json, "type", "offer"); + } else { + cJSON_AddStringToObject(json, "type", "answer"); + } + cJSON_AddStringToObject(json, "sdp", (char *)msg->data); + } else if (msg->type == ESP_PEER_SIGNALING_MSG_CANDIDATE) { + cJSON_AddStringToObject(json, "type", "candidate"); + cJSON_AddStringToObject(json, "candidate", (char *)msg->data); + } else { + return 0; + } + char *payload = cJSON_PrintUnformatted(json); + cJSON_Delete(json); + int ret = https_post(request_room, NULL, payload, NULL, NULL, NULL); + free(payload); + return ret; +} + +static void wss_send_leave(wss_sig_t *sg) +{ + char request_room[128]; + snprintf(request_room, 128, "%s/leave/%s/%s", + sg->client_info.base_url, + sg->client_info.room_id, sg->client_info.client_id); + char header[128]; + char *headers[2] = { header, NULL }; + snprintf(header, 128, "Referer: %s/r/%s", sg->client_info.base_url, sg->client_info.room_id); + https_post(request_room, headers, NULL, NULL, NULL, NULL); + esp_peer_signaling_msg_t msg = { + .type = ESP_PEER_SIGNALING_MSG_BYE, + }; + wss_signal_send_msg(sg, &msg); + media_lib_thread_sleep(100); + snprintf(request_room, 128, "%s/%s/%s", + sg->client_info.wss_post_url, + sg->client_info.room_id, sg->client_info.client_id); + https_send_request("DELETE", NULL, request_room, NULL, NULL, NULL, NULL); +} + +int wss_signal_stop(esp_peer_signaling_handle_t h) +{ + wss_sig_t *sg = (wss_sig_t *)h; + if (sg->wss_client) { + printf("Before to send leave....\n"); + wss_send_leave(sg); + destroy_wss(sg->wss_client); + } + stop_reload_ice_timer(sg); + free_client_info(&sg->client_info); + free_ice_info(&sg->ice_info); + free(sg); + return 0; +} + +const esp_peer_signaling_impl_t *esp_signaling_get_apprtc_impl(void) +{ + static const esp_peer_signaling_impl_t impl = { + .start = wss_signal_start, + .send_msg = wss_signal_send_msg, + .stop = wss_signal_stop, + }; + return &impl; +} diff --git a/components/third_party/esp_webrtc/impl/peer_default/CHANGELOG.md b/components/third_party/esp_webrtc/impl/peer_default/CHANGELOG.md new file mode 100644 index 0000000..6e7fc2c --- /dev/null +++ b/components/third_party/esp_webrtc/impl/peer_default/CHANGELOG.md @@ -0,0 +1,18 @@ +# Changelog + +## v1.1.0 + +### Features + +- Export buffer configuration for RTP and data channel +- Added support for multiple data channel +- Added notify for data channel open, close event + +### Bug Fixes + +- Fixed keep alive check not take effect if peer closed unexpectly + + +## v1.0.0 + +- Initial version of `peer_default` diff --git a/components/third_party/esp_webrtc/impl/peer_default/CMakeLists.txt b/components/third_party/esp_webrtc/impl/peer_default/CMakeLists.txt new file mode 100644 index 0000000..2af35cf --- /dev/null +++ b/components/third_party/esp_webrtc/impl/peer_default/CMakeLists.txt @@ -0,0 +1,7 @@ +idf_component_register(INCLUDE_DIRS ./include) + +get_filename_component(BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR} NAME) +add_prebuilt_library(${BASE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/libs/${IDF_TARGET}/libpeer_default.a" + PRIV_REQUIRES ${BASE_DIR} esp_timer) +target_link_libraries(${COMPONENT_LIB} INTERFACE "-L ${CMAKE_CURRENT_SOURCE_DIR}/libs/${IDF_TARGET}") +target_link_libraries(${COMPONENT_LIB} INTERFACE peer_default) diff --git a/components/third_party/esp_webrtc/impl/peer_default/idf_component.yml b/components/third_party/esp_webrtc/impl/peer_default/idf_component.yml new file mode 100644 index 0000000..92cf5fa --- /dev/null +++ b/components/third_party/esp_webrtc/impl/peer_default/idf_component.yml @@ -0,0 +1 @@ +version: 1.1.0 diff --git a/components/third_party/esp_webrtc/impl/peer_default/include/esp_peer_default.h b/components/third_party/esp_webrtc/impl/peer_default/include/esp_peer_default.h new file mode 100644 index 0000000..f8fe1d2 --- /dev/null +++ b/components/third_party/esp_webrtc/impl/peer_default/include/esp_peer_default.h @@ -0,0 +1,85 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Peer default data channel configuration + */ +typedef struct { + uint16_t cache_timeout; /*!< Data channel frame keep timeout (unit ms) default: 5000ms if set to 0 */ + uint32_t send_cache_size; /*!< Cache size for outgoing data channel packets (unit Bytes) + default: 100kB if set to 0 */ + uint32_t recv_cache_size; /*!< Cache size for incoming data channel packets (unit Bytes) + default: 100kB if set to 0 */ +} esp_peer_default_data_ch_cfg_t; + +/** + * @brief Peer default jitter buffer configuration + */ +typedef struct { + uint16_t cache_timeout; /*!< Maximum timeout to keep the received RTP packet (unit ms) default: 100ms if set to 0 */ + uint16_t resend_delay; /*!< Not resend until resend delay reached (unit ms) default: 20ms if set to 0*/ + uint32_t cache_size; /*!< Cache size for incoming data channel frame (unit Bytes) + For audio jitter buffer default: 100kB if set to 0 + For video jitter buffer default: 400kB if set to 0 */ +} esp_peer_default_jitter_cfg_t; + +/** + * @brief Peer default RTP send and receive configuration + * + * @note Insufficient RTP resources can disable features like packet retransmission and loss reporting + * Without these features, packet loss may result in audio glitches and mosaic-like video artifacts + */ +typedef struct { + esp_peer_default_jitter_cfg_t audio_recv_jitter; /*!< Audio jitter buffer configuration */ + esp_peer_default_jitter_cfg_t video_recv_jitter; /*!< Video jitter buffer configuration */ + uint32_t send_pool_size; /*!< Send pool size for outgoing RTP packets (unit Bytes) + default: 400kB if set to 0 */ + uint32_t send_queue_num; /*!< Maximum queue number to hold outgoing RTP packet metadata info + default: 256 */ + uint16_t max_resend_count; /*!< Maximum resend count for one RTP packet + default: 3 times */ +} esp_peer_default_rtp_cfg_t; + +/** + * @brief Peer default configuration (optional) + */ +typedef struct { + uint16_t agent_recv_timeout; /*!< ICE agent receive timeout setting (unit ms) + default: 100ms if set to 0 + Some STUN/TURN server reply message slow increase this value */ + esp_peer_default_data_ch_cfg_t data_ch_cfg; /*!< Configuration of data channel */ + esp_peer_default_rtp_cfg_t rtp_cfg; /*!< Configuration of RTP buffer */ +} esp_peer_default_cfg_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_webrtc/impl/peer_default/libs/esp32/libpeer_default.a b/components/third_party/esp_webrtc/impl/peer_default/libs/esp32/libpeer_default.a new file mode 100644 index 0000000..7be96a5 Binary files /dev/null and b/components/third_party/esp_webrtc/impl/peer_default/libs/esp32/libpeer_default.a differ diff --git a/components/third_party/esp_webrtc/impl/peer_default/libs/esp32p4/libpeer_default.a b/components/third_party/esp_webrtc/impl/peer_default/libs/esp32p4/libpeer_default.a new file mode 100644 index 0000000..8997e8f Binary files /dev/null and b/components/third_party/esp_webrtc/impl/peer_default/libs/esp32p4/libpeer_default.a differ diff --git a/components/third_party/esp_webrtc/impl/peer_default/libs/esp32s2/libpeer_default.a b/components/third_party/esp_webrtc/impl/peer_default/libs/esp32s2/libpeer_default.a new file mode 100644 index 0000000..e4740c7 Binary files /dev/null and b/components/third_party/esp_webrtc/impl/peer_default/libs/esp32s2/libpeer_default.a differ diff --git a/components/third_party/esp_webrtc/impl/peer_default/libs/esp32s3/libpeer_default.a b/components/third_party/esp_webrtc/impl/peer_default/libs/esp32s3/libpeer_default.a new file mode 100644 index 0000000..d0f409e Binary files /dev/null and b/components/third_party/esp_webrtc/impl/peer_default/libs/esp32s3/libpeer_default.a differ diff --git a/components/third_party/esp_webrtc/impl/whip_signal/include/esp_peer_whip_signaling.h b/components/third_party/esp_webrtc/impl/whip_signal/include/esp_peer_whip_signaling.h new file mode 100644 index 0000000..27a2f5f --- /dev/null +++ b/components/third_party/esp_webrtc/impl/whip_signal/include/esp_peer_whip_signaling.h @@ -0,0 +1,52 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include +#include "esp_peer_signaling.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief WHIP authorization type + */ +typedef enum { + ESP_PEER_SIGNALING_WHIP_AUTH_TYPE_BEARER = 0, /*!< Use Bearer token */ + ESP_PEER_SIGNALING_WHIP_AUTH_TYPE_BASIC = 1, /*!< Use basic authorization */ +} esp_peer_signaling_whip_auth_type_t; + +/** + * @brief WHIP signaling configuration + */ +typedef struct { + esp_peer_signaling_whip_auth_type_t auth_type; /*!< Authorization type */ + char *token; /*!< Bearer token or username:password for basic authorization */ +} esp_peer_signaling_whip_cfg_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_webrtc/impl/whip_signal/whip_signaling.c b/components/third_party/esp_webrtc/impl/whip_signal/whip_signaling.c new file mode 100644 index 0000000..b6c5c10 --- /dev/null +++ b/components/third_party/esp_webrtc/impl/whip_signal/whip_signaling.c @@ -0,0 +1,300 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include +#include +#include "https_client.h" +#include "esp_peer_signaling.h" +#include "esp_peer_whip_signaling.h" +#include "esp_tls_crypto.h" +#include "esp_log.h" + +#define TAG "WHIP_SIGNALING" + +#define SAFE_FREE(p) if (p) { \ + free(p); \ + p = NULL; \ +} + +#define MAX_SERVER_SUPPORT (4) +#define GET_PARAM_VALUE(str, key) get_param_value(str, key, sizeof(key) - 1) + +typedef struct { + esp_peer_signaling_cfg_t cfg; + esp_peer_signaling_whip_cfg_t *whip_cfg; + uint8_t *remote_sdp; + int remote_sdp_size; + bool local_sdp_sent; + char *location; + esp_peer_ice_server_cfg_t *ice_servers[MAX_SERVER_SUPPORT]; + uint8_t server_num; +} whip_signaling_t; + +static int whip_signaling_start(esp_peer_signaling_cfg_t *cfg, esp_peer_signaling_handle_t *h) +{ + whip_signaling_t *sig = (whip_signaling_t *)calloc(1, sizeof(whip_signaling_t)); + if (sig == NULL) { + return ESP_PEER_ERR_NO_MEM; + } + do { + sig->cfg = *cfg; + esp_peer_signaling_whip_cfg_t *whip_cfg = (esp_peer_signaling_whip_cfg_t *)cfg->extra_cfg; + if (whip_cfg && whip_cfg->token) { + sig->whip_cfg = calloc(1, sizeof(esp_peer_signaling_whip_cfg_t)); + if (sig->whip_cfg == NULL) { + break; + } + sig->whip_cfg->auth_type = whip_cfg->auth_type; + sig->whip_cfg->token = strdup(whip_cfg->token); + if (sig->whip_cfg->token == NULL) { + break; + } + } + *h = sig; + // TODO force to use controlling role OK + esp_peer_signaling_ice_info_t ice_info = { + .is_initiator = true, + }; + sig->cfg.on_ice_info(&ice_info, sig->cfg.ctx); + // Trigger connected + sig->cfg.on_connected(sig->cfg.ctx); + return ESP_PEER_ERR_NONE; + } while (0); + SAFE_FREE(sig); + return ESP_PEER_ERR_NO_MEM; +} + +static void whip_sdp_answer(http_resp_t *resp, void *ctx) +{ + whip_signaling_t *sig = (whip_signaling_t *)ctx; + printf("Get remote SDP %s\n", (char *)resp->data); + SAFE_FREE(sig->remote_sdp); + sig->remote_sdp = (uint8_t *)malloc(resp->size); + if (sig->remote_sdp == NULL) { + ESP_LOGE(TAG, "No enough memory for remote sdp"); + return; + } + memcpy(sig->remote_sdp, resp->data, resp->size); + sig->remote_sdp_size = resp->size; +} + +static char *get_full_path(char *base, const char *url) +{ + if (strncmp(url, "http", 3) == 0) { + return strdup(url); // Already a full URL + } + char *root_path = strstr(base, "//"); + if (!root_path) { + return NULL; + } + root_path += 2; + char *root_path_end = url[0] == '/' ? strchr(root_path, '/') : strrchr(root_path, '/') + 1; + int base_len = root_path_end ? root_path_end - base : strlen(base); + int url_len = strlen(url); + char *full_path = malloc(base_len + url_len + 1); + if (!full_path) { + return NULL; + } + strncpy(full_path, base, base_len); + strcpy(full_path + base_len, url); + return full_path; +} + +static char *get_param_value(char *s, char *param, int param_size) +{ + char *v = strstr(s, param); + if (v == NULL) { + return NULL; + } + v += param_size; + char *v_end = strchr(v, '"'); + if (v_end == NULL) { + return NULL; + } + *v_end = 0; + v = strdup(v); + *v_end = '"'; + return v; +} + +static int extract_ice_info(whip_signaling_t *sig, char *link) +{ + if (sig->server_num > MAX_SERVER_SUPPORT) { + return ESP_PEER_ERR_OVER_LIMITED; + } + esp_peer_ice_server_cfg_t server_cfg = {}; + char *start, *end; + start = strstr(link, "<"); + if (start == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + end = strstr(start, ">"); + if (end == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + *end = '\0'; // Terminate the URL + server_cfg.stun_url = strdup(start + 1); + *end = '>'; + start = end + 1; + server_cfg.user = GET_PARAM_VALUE(link, "username=\""); + server_cfg.psw = GET_PARAM_VALUE(link, "credential=\""); + sig->ice_servers[sig->server_num] = (esp_peer_ice_server_cfg_t *)calloc(1, sizeof(esp_peer_ice_server_cfg_t)); + if (sig->ice_servers[sig->server_num]) { + memcpy(sig->ice_servers[sig->server_num], &server_cfg, sizeof(esp_peer_ice_server_cfg_t)); + sig->server_num++; + } + return ESP_PEER_ERR_NONE; +} + +static void whip_sdp_header(const char *key, const char *value, void *ctx) +{ + whip_signaling_t *sig = (whip_signaling_t *)ctx; + if (value == NULL) { + return; + } + if (strcasecmp(key, "Location") == 0) { + SAFE_FREE(sig->location); + sig->location = get_full_path(sig->cfg.signal_url, value); + } + if (strcasecmp(key, "Link") == 0) { + extract_ice_info(sig, (char *)value); + } +} + +static char *get_auth_header(esp_peer_signaling_whip_cfg_t *whip_cfg) +{ + if (whip_cfg == NULL || whip_cfg->token == NULL) { + return NULL; + } + int cfg_len = strlen(whip_cfg->token); + size_t token_len = cfg_len; + int auth_len = (whip_cfg->auth_type == ESP_PEER_SIGNALING_WHIP_AUTH_TYPE_BASIC) ? strlen("Authorization: Basic ") : strlen("Authorization: Bearer "); + if (whip_cfg->auth_type == ESP_PEER_SIGNALING_WHIP_AUTH_TYPE_BASIC) { + token_len = ((token_len + 2) / 3) * 4 + 1; + } + int len = auth_len + token_len + 1; + char *auth = malloc(len); + if (auth == NULL) { + return NULL; + } + if (whip_cfg->auth_type == ESP_PEER_SIGNALING_WHIP_AUTH_TYPE_BASIC) { + snprintf(auth, len, "Authorization: Basic "); + size_t encoded_len = token_len; + esp_crypto_base64_encode((unsigned char *)auth + auth_len, encoded_len, &encoded_len, (unsigned char *)whip_cfg->token, cfg_len); + auth[auth_len + encoded_len] = '\0'; + } else { + snprintf(auth, len, "Authorization: Bearer %s", whip_cfg->token); + } + return auth; +} + +static int whip_signaling_send_msg(esp_peer_signaling_handle_t h, esp_peer_signaling_msg_t *msg) +{ + whip_signaling_t *sig = (whip_signaling_t *)h; + if (msg->type == ESP_PEER_SIGNALING_MSG_BYE) { + + } else if (msg->type == ESP_PEER_SIGNALING_MSG_SDP) { + if (sig->local_sdp_sent == false) { + char content_type[] = "Content-Type: application/sdp"; + char *auth = get_auth_header(sig->whip_cfg); + char *header[] = { content_type, auth, NULL }; + int ret = https_post(sig->cfg.signal_url, header, (char *)msg->data, whip_sdp_header, whip_sdp_answer, h); + SAFE_FREE(auth); + if (ret != 0 || sig->remote_sdp == NULL) { + ESP_LOGE(TAG, "Fail to post data to %s", sig->cfg.signal_url); + return ESP_PEER_ERR_FAIL; + } + sig->local_sdp_sent = true; + if (sig->server_num) { + // update ice_info + esp_peer_signaling_ice_info_t iec_info = { + .is_initiator = true, + .server_info = *sig->ice_servers[0], + }; + // TODO support more servers? + sig->cfg.on_ice_info(&iec_info, sig->cfg.ctx); + } + // Try to extractor stun lists + esp_peer_signaling_msg_t sdp_msg = { + .type = ESP_PEER_SIGNALING_MSG_SDP, + .data = sig->remote_sdp, + .size = sig->remote_sdp_size, + }; + sig->cfg.on_msg(&sdp_msg, sig->cfg.ctx); + SAFE_FREE(sig->remote_sdp); + } else if (sig->location) { + char *sdp = strstr((char *)msg->data, "a=group:BUNDLE"); + if (sdp) { + // Patch for SDP + char content_type[] = "Content-Type: application/trickle-ice-sdpfrag"; + char *auth = get_auth_header(sig->whip_cfg); + char *header[] = { content_type, auth, NULL }; + int ret = https_send_request("PATCH", header, sig->location, sdp, NULL, NULL, h); + SAFE_FREE(auth); + if (ret != 0) { + ESP_LOGE(TAG, "Fail to post data to %s", sig->location); + return ESP_PEER_ERR_NONE; + } + } + } + } + return ESP_PEER_ERR_NONE; +} + +static int whip_signaling_stop(esp_peer_signaling_handle_t h) +{ + whip_signaling_t *sig = (whip_signaling_t *)h; + if (sig->location) { + char *auth = get_auth_header(sig->whip_cfg); + char *header[] = { auth, NULL }; + https_send_request("DELETE", auth ? header : NULL, + sig->location, NULL, NULL, NULL, NULL); + SAFE_FREE(auth); + } + sig->cfg.on_close(sig->cfg.ctx); + SAFE_FREE(sig->location); + for (int i = 0; i < sig->server_num; i++) { + SAFE_FREE(sig->ice_servers[i]->stun_url); + SAFE_FREE(sig->ice_servers[i]->user); + SAFE_FREE(sig->ice_servers[i]->psw); + SAFE_FREE(sig->ice_servers[i]); + } + if (sig->whip_cfg) { + SAFE_FREE(sig->whip_cfg->token); + SAFE_FREE(sig->whip_cfg); + } + SAFE_FREE(sig); + return 0; +} + +const esp_peer_signaling_impl_t *esp_signaling_get_whip_impl(void) +{ + static const esp_peer_signaling_impl_t impl = { + .start = whip_signaling_start, + .send_msg = whip_signaling_send_msg, + .stop = whip_signaling_stop, + }; + return &impl; +} diff --git a/components/third_party/esp_webrtc/include/esp_peer.h b/components/third_party/esp_webrtc/include/esp_peer.h new file mode 100644 index 0000000..f87c52f --- /dev/null +++ b/components/third_party/esp_webrtc/include/esp_peer.h @@ -0,0 +1,573 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_peer_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Peer state + */ +typedef enum { + ESP_PEER_STATE_CLOSED = 0, /*!< Closed */ + ESP_PEER_STATE_DISCONNECTED = 1, /*!< Disconnected */ + ESP_PEER_STATE_NEW_CONNECTION = 2, /*!< New connection comming */ + ESP_PEER_STATE_PAIRING = 3, /*!< Under candidate pairing */ + ESP_PEER_STATE_PAIRED = 4, /*!< Candidate pairing success */ + ESP_PEER_STATE_CONNECTING = 5, /*!< Building connection with peer */ + ESP_PEER_STATE_CONNECTED = 6, /*!< Connected with peer */ + ESP_PEER_STATE_CONNECT_FAILED = 7, /*!< Connect failed */ + ESP_PEER_STATE_DATA_CHANNEL_CONNECTED = 8, /*!< Data channel is connected */ + ESP_PEER_STATE_DATA_CHANNEL_OPENED = 9, /*!< Data channel is opened */ + ESP_PEER_STATE_DATA_CHANNEL_CLOSED = 10, /*!< Data channel is closed */ + ESP_PEER_STATE_DATA_CHANNEL_DISCONNECTED = 11, /*!< Data channel is disconencted */ +} esp_peer_state_t; + +/** + * @brief Peer video codec + */ +typedef enum { + ESP_PEER_VIDEO_CODEC_NONE = 0, /*!< Invalid video codec type */ + ESP_PEER_VIDEO_CODEC_H264 = 1, /*!< H264 video codec */ + ESP_PEER_VIDEO_CODEC_MJPEG = 2, /*!< MJPEG video codec */ +} esp_peer_video_codec_t; + +/** + * @brief Peer audio codec + */ +typedef enum { + ESP_PEER_AUDIO_CODEC_NONE = 0, /*!< Invalid audio codec type */ + ESP_PEER_AUDIO_CODEC_G711A = 1, /*!< G711-alaw(PCMA) audio codec */ + ESP_PEER_AUDIO_CODEC_G711U = 2, /*!< G711-ulaw(PCMU) audio codec */ + ESP_PEER_AUDIO_CODEC_OPUS = 3, /*!< OPUS audio codec */ +} esp_peer_audio_codec_t; + +/** + * @brief Data channel type + */ +typedef enum { + ESP_PEER_DATA_CHANNEL_NONE = 0, /*!< Invalid type */ + ESP_PEER_DATA_CHANNEL_DATA = 1, /*!< Data type */ + ESP_PEER_DATA_CHANNEL_STRING = 2, /*!< String type */ +} esp_peer_data_channel_type_t; + +/** + * @brief Media transmission direction + */ +typedef enum { + ESP_PEER_MEDIA_DIR_NONE = 0, + ESP_PEER_MEDIA_DIR_SEND_ONLY = (1 << 0), /*!< Send only */ + ESP_PEER_MEDIA_DIR_RECV_ONLY = (1 << 1), /*!< Receive only */ + ESP_PEER_MEDIA_DIR_SEND_RECV = ESP_PEER_MEDIA_DIR_SEND_ONLY | ESP_PEER_MEDIA_DIR_RECV_ONLY, + /*!< Send and receive both */ +} esp_peer_media_dir_t; + +/** + * @brief ICE transport policy + */ +typedef enum { + ESP_PEER_ICE_TRANS_POLICY_ALL = 0, /*!< All ICE candidates will be used for pairing */ + ESP_PEER_ICE_TRANS_POLICY_RELAY = 1, /*!< Only relay ICE candidates will be used for pairing */ +} esp_peer_ice_trans_policy_t; + +/** + * @brief Video stream information + */ +typedef struct { + esp_peer_video_codec_t codec; /*!< Video codec */ + int width; /*!< Video width */ + int height; /*!< Video height */ + int fps; /*!< Video fps */ +} esp_peer_video_stream_info_t; + +/** + * @brief Audio stream information + */ +typedef struct { + esp_peer_audio_codec_t codec; /*!< Audio codec */ + uint32_t sample_rate; /*!< Audio sample rate */ + uint8_t channel; /*!< Audio channel */ +} esp_peer_audio_stream_info_t; + +/** + * @brief Video frame information + */ +typedef struct { + uint32_t pts; /*!< Video frame presentation timestamp */ + uint8_t *data; /*!< Video frame data pointer */ + int size; /*!< Video frame data size */ +} esp_peer_video_frame_t; + +/** + * @brief Audio frame information + */ +typedef struct { + uint32_t pts; /*!< Audio frame presentation timestamp */ + uint8_t *data; /*!< Audio frame data pointer */ + int size; /*!< Audio frame data size */ +} esp_peer_audio_frame_t; + +/** + * @brief Data frame information + */ +typedef struct { + esp_peer_data_channel_type_t type; /*!< Data channel type */ + uint16_t stream_id; /*!< Data channel stream ID */ + uint8_t *data; /*!< Pointer to data to be sent through data channel */ + int size; /*!< Data size */ +} esp_peer_data_frame_t; + +/** + * @brief Peer message type + */ +typedef enum { + ESP_PEER_MSG_TYPE_NONE, /*!< None message type */ + ESP_PEER_MSG_TYPE_SDP, /*!< SDP message type */ + ESP_PEER_MSG_TYPE_CANDIDATE, /*!< ICE candidate message type */ +} esp_peer_msg_type_t; + +/** + * @brief Peer message + */ +typedef struct { + esp_peer_msg_type_t type; /*!< Message type */ + uint8_t *data; /*!< Message data */ + int size; /*!< Message data size */ +} esp_peer_msg_t; + +/** + * @brief Peer data channel configuration for create + */ +typedef struct { + char *label; /*!< Data channel label */ +} esp_peer_data_channel_cfg_t; + +/** + * @brief Peer data channel information + */ +typedef struct { + const char *label; /*!< Data channel label */ + uint16_t stream_id; /*!< Chunk stream id for this channel */ +} esp_peer_data_channel_info_t; + +/** + * @brief Peer handle + */ +typedef void *esp_peer_handle_t; + +/** + * @brief Peer configuration + */ +typedef struct { + esp_peer_ice_server_cfg_t *server_lists; /*< ICE server list */ + uint8_t server_num; /*!< Number of ICE server */ + esp_peer_role_t role; /*!< Peer role */ + esp_peer_ice_trans_policy_t ice_trans_policy; /*!< ICE transport policy */ + esp_peer_audio_stream_info_t audio_info; /*!< Audio stream information */ + esp_peer_video_stream_info_t video_info; /*!< Video stream information */ + esp_peer_media_dir_t audio_dir; /*!< Audio transmission direction */ + esp_peer_media_dir_t video_dir; /*!< Video transmission direction */ + bool no_auto_reconnect; /*!< Disable auto reconnect if connected fail */ + bool enable_data_channel; /*!< Enable data channel */ + bool manual_ch_create; /*!< Manual create data channel + When SCTP role is client, it will try to send DCEP automatically + To disable this behavior can create data channel manually and set this flag + */ + void *extra_cfg; /*!< Extra configuration */ + int extra_size; /*!< Extra configuration size */ + void *ctx; /*!< User context */ + + /** + * @brief Event callback for state + * @param[in] state Current peer state + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_state)(esp_peer_state_t state, void* ctx); + + /** + * @brief Message callback + * @param[in] info Pointer to peer message + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_msg)(esp_peer_msg_t* info, void* ctx); + + /** + * @brief Peer video stream information callback + * @param[in] info Video stream information + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_video_info)(esp_peer_video_stream_info_t* info, void* ctx); + + /** + * @brief Peer audio stream information callback + * @param[in] info Audio stream information + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_audio_info)(esp_peer_audio_stream_info_t* info, void* ctx); + + /** + * @brief Peer audio frame callback + * @param[in] frame Audio frame information + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_audio_data)(esp_peer_audio_frame_t* frame, void* ctx); + + /** + * @brief Peer video frame callback + * @param[in] frame Video frame information + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_video_data)(esp_peer_video_frame_t* frame, void* ctx); + + /** + * @brief Peer data channel opened event callback + * @param[in] ch Data channel information + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_channel_open)(esp_peer_data_channel_info_t *ch, void *ctx); + + /** + * @brief Peer data frame callback + * @param[in] frame Data frame information + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_data)(esp_peer_data_frame_t *frame, void *ctx); + + /** + * @brief Peer data channel closed event callback + * @param[in] ch Data channel information + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_channel_close)(esp_peer_data_channel_info_t *ch, void *ctx); +} esp_peer_cfg_t; + +/** + * @brief Peer connection interface + */ +typedef struct { + /** + * @brief Open peer connection + * @param[in] cfg Peer configuration + * @param[out] peer Peer handle + * @return Status code indicating success or failure. + */ + int (*open)(esp_peer_cfg_t* cfg, esp_peer_handle_t* peer); + + /** + * @brief Create a new conenction + * @param[in] peer Peer handle + * @return Status code indicating success or failure. + */ + int (*new_connection)(esp_peer_handle_t peer); + + /** + * @brief Update ICE information + * @param[in] peer Peer handle + * @param[in] servers ICE Server settings + * @param[in] server_num Number of ICE servers + * @return Status code indicating success or failure. + */ + int (*update_ice_info)(esp_peer_handle_t peer, esp_peer_role_t role, esp_peer_ice_server_cfg_t* server, int server_num); + + /** + * @brief Send message to peer + * @param[in] peer Peer handle + * @param[in] msg Message to be sent + * @return Status code indicating success or failure. + */ + int (*send_msg)(esp_peer_handle_t peer, esp_peer_msg_t* msg); + + /** + * @brief Send video frame data to peer + * @param[in] peer Peer handle + * @param[in] frame Video frame to be sent + * @return Status code indicating success or failure. + */ + int (*send_video)(esp_peer_handle_t peer, esp_peer_video_frame_t* frame); + + /** + * @brief Send audio frame data to peer + * @param[in] peer Peer handle + * @param[in] frame Audio frame to be sent + * @return Status code indicating success or failure. + */ + int (*send_audio)(esp_peer_handle_t peer, esp_peer_audio_frame_t* frame); + + /** + * @brief Send data frame data to peer + * @param[in] peer Peer handle + * @param[in] frame Data frame to be sent + * @return Status code indicating success or failure. + */ + int (*send_data)(esp_peer_handle_t peer, esp_peer_data_frame_t* frame); + + /** + * @brief Manually create data chanel for peer + * @param[in] peer Peer handle + * @param[in] ch_cfg Data channel configuration + * @return Status code indicating success or failure. + */ + int (*create_data_channel)(esp_peer_handle_t peer, esp_peer_data_channel_cfg_t *ch_cfg); + + /** + * @brief Close data chanel for peer + * @param[in] peer Peer handle + * @param[in] label Data channel label + * @return Status code indicating success or failure. + */ + int (*close_data_channel)(esp_peer_handle_t peer, const char *label); + + /** + * @brief Peer main loop + * @note Peer connection need handle peer status change, receive stream data in this loop + * Or create a thread to handle these things and synchronize with this loop + * @param[in] peer Peer handle + * @return Status code indicating success or failure. + */ + int (*main_loop)(esp_peer_handle_t peer); + + /** + * @brief Disconnected with peer + * @param[in] peer Peer handle + * @return Status code indicating success or failure. + */ + int (*disconnect)(esp_peer_handle_t peer); + + /** + * @brief Query peer status + * @param[in] peer Peer handle + */ + void (*query)(esp_peer_handle_t peer); + + /** + * @brief Close peer connection and release related resources + * @param[in] peer Peer handle + * @return Status code indicating success or failure. + */ + int (*close)(esp_peer_handle_t peer); +} esp_peer_ops_t; + +/** + * @brief Open peer connection + * + * @param[in] cfg Peer configuration + * @param[in] ops Peer connection implementation + * @param[out] peer Peer handle + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_open(esp_peer_cfg_t *cfg, const esp_peer_ops_t *ops, esp_peer_handle_t *peer); + +/** + * @brief Create new conenction + * + * @note After new connection is created, It will try gather ICE candidate from ICE servers. + * And report local SDP to let user send to signaling server. + * + * @param[in] peer Peer handle + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_new_connection(esp_peer_handle_t peer); + +/** + * @brief Manually create data channel + * + * @note It will send DCEP event to peer until data channel created + * + * @param[in] peer Peer handle + * @param[in] ch_cfg Configuration for data channel creation + * + * @return + * - ESP_PEER_ERR_NONE Open data channel success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_create_data_channel(esp_peer_handle_t peer, esp_peer_data_channel_cfg_t *ch_cfg); + +/** + * @brief Manually close data channel by label + * + * @param[in] peer Peer handle + * @param[in] label Channel label + * + * @return + * - ESP_PEER_ERR_NONE Close data channel success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_close_data_channel(esp_peer_handle_t peer, const char *label); + +/** + * @brief Update ICE server information + * + * @note After new connection is created, It will try gather ICE candidate from ICE servers. + * And report local SDP to let user send to signaling server. + * + * @param[in] peer Peer handle + * @param[in] role Peer roles of controlling or controlled + * @param[in] server Pointer to array of ICE server configuration + * @param[in] server_num ICE server number + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_update_ice_info(esp_peer_handle_t peer, esp_peer_role_t role, esp_peer_ice_server_cfg_t* server, int server_num); + +/** + * @brief Send message to peer + * + * @param[in] peer Peer handle + * @param[in] msg Message to send to peer + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_send_msg(esp_peer_handle_t peer, esp_peer_msg_t *msg); + +/** + * @brief Send video data to peer + * + * @param[in] peer Peer handle + * @param[in] frame Video frame data + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_send_video(esp_peer_handle_t peer, esp_peer_video_frame_t *frame); + +/** + * @brief Send audio data to peer + * + * @param[in] peer Peer handle + * @param[in] frame Audio frame data + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_send_audio(esp_peer_handle_t peer, esp_peer_audio_frame_t *info); + +/** + * @brief Send data through data channel to peer + * + * @param[in] peer Peer handle + * @param[in] frame Video frame data + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_send_data(esp_peer_handle_t peer, esp_peer_data_frame_t *frame); + +/** + * @brief Run peer connection main loop + * + * @note This loop need to be call repeatedly + * It handle peer connection status change also receive stream data + * Currently default peer realization have no extra thread internally, all is triggered in this loop + * + * @param[in] peer Peer handle + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_main_loop(esp_peer_handle_t peer); + +/** + * @brief Disconnect peer connection + * + * @note Disconnect will try to close socket which communicate with peer and signaling server + * If user want to continue to listen for peer connect in + * User can call `esp_peer_new_connection` so that it will retry to gather ICE candidate and report local SDP + * So that new peer can connection in through offered SDP from signaling server. + * + * @param[in] peer Peer handle + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_disconnect(esp_peer_handle_t peer); + +/** + * @brief Query of peer connection + * + * @note This API is for debug usage only + * + * @param[in] peer Peer handle + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + */ +int esp_peer_query(esp_peer_handle_t peer); + +/** + * @brief Close peer connection + * + * @note Close peer connection will do `esp_peer_disconnect` firstly then release all resource occupied by peer realization. + * + * @param[in] peer Peer handle + * + * @return + * - ESP_PEER_ERR_NONE Open peer connection success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NOT_SUPPORT Not support + */ +int esp_peer_close(esp_peer_handle_t peer); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_webrtc/include/esp_peer_signaling.h b/components/third_party/esp_webrtc/include/esp_peer_signaling.h new file mode 100644 index 0000000..6a7d604 --- /dev/null +++ b/components/third_party/esp_webrtc/include/esp_peer_signaling.h @@ -0,0 +1,171 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_peer_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Signaling ICE information + */ +typedef struct { + esp_peer_ice_server_cfg_t server_info; /*!< STUN/Relay server information (optional if set by user directly) */ + bool is_initiator; /*!< ICE roles, when as initiator also means being a controlling role */ +} esp_peer_signaling_ice_info_t; + +/** + * @brief Signaling handle + */ +typedef void *esp_peer_signaling_handle_t; + +/** + * @brief Signaling message type + */ +typedef enum { + ESP_PEER_SIGNALING_MSG_NONE, /*!< Signaling message type none */ + ESP_PEER_SIGNALING_MSG_SDP, /*!< SDP message type (SDP may contain candidate in it) */ + ESP_PEER_SIGNALING_MSG_CANDIDATE, /*!< Candidate message type */ + ESP_PEER_SIGNALING_MSG_BYE, /*!< Bye message type */ + ESP_PEER_SIGNALING_MSG_CUSTOMIZED, /*!< Customized message type */ +} esp_peer_signaling_msg_type_t; + +typedef struct { + esp_peer_signaling_msg_type_t type; /*!< Signaling message type */ + uint8_t *data; /*!< Signaling message data */ + int size; /*!< Signaling message size */ +} esp_peer_signaling_msg_t; + +/** + * @brief Signaling configuration + */ +typedef struct { + /** + * @brief Event callback for ICE information + * @param[in] info Pointer to the ICE information + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_ice_info)(esp_peer_signaling_ice_info_t* info, void* ctx); + + /** + * @brief Event callback for connected event + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_connected)(void* ctx); + + /** + * @brief Message callback + * @param[in] msg Pointer to signaling message + * @return Status code indicating success or failure. + */ + int (*on_msg)(esp_peer_signaling_msg_t* msg, void* ctx); + + /** + * @brief Event callback for closed event + * @param[in] ctx User context + * @return Status code indicating success or failure. + */ + int (*on_close)(void* ctx); + + char* signal_url; /*!< Signaling server URL */ + void* extra_cfg; /*!< Extra configuration for special signaling server */ + int extra_size; /*!< Size of extra configuration */ + void* ctx; /*!< User context */ +} esp_peer_signaling_cfg_t; + + /** + * @brief Signaling interface +*/ +typedef struct { + /** + * @brief Start signaling + * @param[in] cfg Signaling configuration + * @param[out] sig Signaling handle + * @return Status code indicating success or failure. + */ + int (*start)(esp_peer_signaling_cfg_t* cfg, esp_peer_signaling_handle_t* sig); + + /** + * @brief Send message to signaling server + * @param[in] sig Signaling handle + * @param[in] msg Message to be sent + * @return Status code indicating success or failure. + */ + int (*send_msg)(esp_peer_signaling_handle_t sig, esp_peer_signaling_msg_t* msg); + + /** + * @brief Stop signaling + * @param[in] sig Signaling handle + * @return Status code indicating success or failure. + */ + int (*stop)(esp_peer_signaling_handle_t sig); +} esp_peer_signaling_impl_t; + +/** + * @brief Start signaling + * + * @param[in] cfg Signaling configuration + * @param[in] impl Implement of signaling interface + * @param[out] sig Signaling handle + * + * @return + * - ESP_PEER_ERR_NONE Start signaling success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NO_MEM Not enough memory + */ +int esp_peer_signaling_start(esp_peer_signaling_cfg_t *cfg, const esp_peer_signaling_impl_t *impl, esp_peer_signaling_handle_t *sig); + +/** + * @brief Send message to signaling server + * + * @param[in] sig Signaling handle + * @param[in] msg Message to be sent + * + * @return + * - ESP_PEER_ERR_NONE Send message success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - Others Fail to send message + */ +int esp_peer_signaling_send_msg(esp_peer_signaling_handle_t sig, esp_peer_signaling_msg_t *msg); + +/** + * @brief Stop signaling + * + * @param[in] sig Signaling handle + * + * @return + * - ESP_PEER_ERR_NONE Stop signaling success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - Others Fail to stop + */ +int esp_peer_signaling_stop(esp_peer_signaling_handle_t sig); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_webrtc/include/esp_peer_types.h b/components/third_party/esp_webrtc/include/esp_peer_types.h new file mode 100644 index 0000000..d159b5d --- /dev/null +++ b/components/third_party/esp_webrtc/include/esp_peer_types.h @@ -0,0 +1,68 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Peer error code + */ +typedef enum { + ESP_PEER_ERR_NONE = 0, /*!< None error */ + ESP_PEER_ERR_INVALID_ARG = -1, /*!< Invalid argument */ + ESP_PEER_ERR_NO_MEM = -2, /*!< Not enough memory */ + ESP_PEER_ERR_WRONG_STATE = -3, /*!< Operate on wrong state */ + ESP_PEER_ERR_NOT_SUPPORT = -4, /*!< Not supported operation */ + ESP_PEER_ERR_NOT_EXISTS = -5, /*!< Not existed */ + ESP_PEER_ERR_FAIL = -6, /*!< General error code */ + ESP_PEER_ERR_OVER_LIMITED = -7, /*!< Overlimited */ + ESP_PEER_ERR_BAD_DATA = -8, /*!< Bad input data */ +} esp_peer_err_t; + +/** + * @brief ICE server configuration + */ +typedef struct { + char *stun_url; /*!< STUN/Relay server URL */ + char *user; /*!< User name */ + char *psw; /*!< User password */ +} esp_peer_ice_server_cfg_t; + +/** + * @brief Peer role + */ +typedef enum { + ESP_PEER_ROLE_CONTROLLING, /*!< Controlling role who initialize the connection */ + ESP_PEER_ROLE_CONTROLLED, /*!< Controlled role */ +} esp_peer_role_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_webrtc/include/esp_webrtc.h b/components/third_party/esp_webrtc/include/esp_webrtc.h new file mode 100644 index 0000000..b5d1414 --- /dev/null +++ b/components/third_party/esp_webrtc/include/esp_webrtc.h @@ -0,0 +1,270 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_peer.h" +#include "esp_peer_signaling.h" +#include "esp_capture.h" +#include "av_render.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ESP WebRTC handle + */ +typedef void *esp_webrtc_handle_t; + +typedef enum { + ESP_WEBRTC_CUSTOM_DATA_VIA_NONE, + ESP_WEBRTC_CUSTOM_DATA_VIA_SIGNALING, + ESP_WEBRTC_CUSTOM_DATA_VIA_DATA_CHANNEL, +} esp_webrtc_custom_data_via_t; + +/** + * @brief ESP WebRTC peer connection configuration + */ +typedef struct { + esp_peer_ice_server_cfg_t *server_lists; /*!< STUN/Relay server URL lists, can be NULL when get from signaling */ + uint8_t server_num; /*!< Number of STUN/Relay server URL */ + esp_peer_ice_trans_policy_t ice_trans_policy; /*!< ICE transport policy */ + esp_peer_audio_stream_info_t audio_info; /*!< Audio stream information for send */ + esp_peer_video_stream_info_t video_info; /*!< Video stream information for send */ + esp_peer_media_dir_t audio_dir; /*!< Audio transmission direction */ + esp_peer_media_dir_t video_dir; /*!< Video transmission direction */ + bool enable_data_channel; /*!< Whether enable data channel */ + bool manual_ch_create; /*!< When set, disable auto create data channel in SCTP client mode if `enable_data_channel` set + User need manually call `esp_peer_create_data_channel` instead */ + bool video_over_data_channel; /*!< Whether send and receive video data through data channel */ + bool no_auto_reconnect; /*!< Disable auto reconnect + In room related WebRTC application, connection build up with peer + If peer leaves, it will auto re-enter same room (send new SDP) after clear up + Disable reconnect will do nothing after clear up until call `esp_webrtc_enable_peer_connection` */ + void *extra_cfg; /*!< Extra configuration for peer connection */ + int extra_size; /*!< Size of extra configuration */ + void *ctx; /*!< User context */ + + /** + * @brief This API is used for users who do not care the data channel or signaling details + * And want to receive data from them only + */ + int (*on_custom_data)(esp_webrtc_custom_data_via_t via, uint8_t *data, int size, void *ctx); + + /** + * @brief Following API are function groups for users who want more control over data channels + */ + int (*on_channel_open)(esp_peer_data_channel_info_t *ch, void *ctx); /*!< Callback invoked when a data channel is opened */ + int (*on_data)(esp_peer_data_frame_t *frame, void *ctx); /*!< Callback invoked when data is received on the channel */ + int (*on_channel_close)(esp_peer_data_channel_info_t *ch, void *ctx); /*!< Callback invoked when a data channel is closed */ +} esp_webrtc_peer_cfg_t; + +/** + * @brief ESP WebRTC signaling configuration + */ +typedef struct { + char *signal_url; /*!< Signaling server URL */ + void *extra_cfg; /*!< Extra configuration for special signaling server */ + int extra_size; /*!< Size of extra configuration */ + void *ctx; /*!< User context */ +} esp_webrtc_signaling_cfg_t; + +/** + * @brief ESP WebRTC configuration + */ +typedef struct { + const esp_peer_signaling_impl_t *signaling_impl; /*!< Signaling implementation */ + esp_webrtc_signaling_cfg_t signaling_cfg; /*!< Signaling configuration */ + const esp_peer_ops_t *peer_impl; /*!< Peer connection implementation */ + esp_webrtc_peer_cfg_t peer_cfg; /*!< Peer connection configuration */ +} esp_webrtc_cfg_t; + +/** + * @brief WebRTC event type + */ +typedef enum { + ESP_WEBRTC_EVENT_NONE = 0, /*!< None event */ + ESP_WEBRTC_EVENT_CONNECTED = 1, /*!< Connected event */ + ESP_WEBRTC_EVENT_CONNECT_FAILED = 2, /*!< Connected failed event */ + ESP_WEBRTC_EVENT_DISCONNECTED = 3, /*!< Disconnected event */ + ESP_WEBRTC_EVENT_DATA_CHANNEL_CONNECTED = 4, /*!< Data channel connected event */ + ESP_WEBRTC_EVENT_DATA_CHANNEL_DISCONNECTED = 5, /*!< Data channel disconnected event */ + ESP_WEBRTC_EVENT_DATA_CHANNEL_OPENED = 6, /*!< Data channel opened event, suitable for one data channel only */ + ESP_WEBRTC_EVENT_DATA_CHANNEL_CLOSED = 7, /*!< Data channel closed event, suitable for one data channel only */ +} esp_webrtc_event_type_t; + +/** + * @brief WebRTC event + */ +typedef struct { + esp_webrtc_event_type_t type; /*!< Event type */ + char *body; /*!< Event body (maybe NULL) */ +} esp_webrtc_event_t; + +/** + * @brief WebRTC media provider + * + * @note Media player and capture system are created from outside. + * WebRTC will internally use the capture and player handle to capture media data and do media playback + */ +typedef struct { + esp_capture_handle_t capture; /*!< Capture system handle */ + av_render_handle_t player; /*!< Player handle */ +} esp_webrtc_media_provider_t; + +/** + * @brief WebRTC event handler + * + * @param[in] event WebRTC event + * @param[in] ctx User context + * + * @return Status to indicate success or failure + */ +typedef int (*esp_webrtc_event_handler_t)(esp_webrtc_event_t *event, void *ctx); + +/** + * @brief WebRTC event handler + * + * @param[in] event WebRTC event + * @param[out] rtc_handle WebRTC handle + * + * @return + * - ESP_PEER_ERR_NONE On success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NO_MEM Not enough memory + */ +int esp_webrtc_open(esp_webrtc_cfg_t *cfg, esp_webrtc_handle_t *rtc_handle); + +/** + * @brief WebRTC set media provider + * + * @param[in] rtc_handle WebRTC handle + * @param[in] provider Media player and capture provider setting + * + * @return + * - ESP_PEER_ERR_NONE On success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + */ +int esp_webrtc_set_media_provider(esp_webrtc_handle_t rtc_handle, esp_webrtc_media_provider_t *provider); + +/** + * @brief WebRTC set event handler + * + * @param[in] rtc_handle WebRTC handle + * @param[in] handler Event handler + * @param[in] ctx Event user context + * + * @return + * - ESP_PEER_ERR_NONE On success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + */ +int esp_webrtc_set_event_handler(esp_webrtc_handle_t rtc_handle, esp_webrtc_event_handler_t handler, void *ctx); + +/** + * @brief Start WebRTC + * + * @param[in] rtc_handle WebRTC handle + * + * @return + * - ESP_PEER_ERR_NONE On success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_NO_MEM Not enough memory + */ +int esp_webrtc_enable_peer_connection(esp_webrtc_handle_t rtc_handle, bool enable); + +/** + * @brief Start WebRTC + * + * @param[in] rtc_handle WebRTC handle + * + * @return + * - ESP_PEER_ERR_NONE On success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - Others Fail to start + */ +int esp_webrtc_start(esp_webrtc_handle_t rtc_handle); + +/** + * @brief Send customized data + * + * @param[in] rtc_handle WebRTC handle + * + * @return + * - ESP_PEER_ERR_NONE On success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - Others Fail to send customized data + */ +int esp_webrtc_send_custom_data(esp_webrtc_handle_t rtc_handle, esp_webrtc_custom_data_via_t via, uint8_t *data, int size); + +/** + * @brief Get Peer Connection handle + * + * @param[in] rtc_handle WebRTC handle + * @param[in] peer_handle Peer connection handle + * + * @return + * - ESP_PEER_ERR_NONE On success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - ESP_PEER_ERR_WRONG_STATE Wrong state for peer connection not build yet + */ +int esp_webrtc_get_peer_connection(esp_webrtc_handle_t rtc_handle, esp_peer_handle_t *peer_handle); + +/** + * @brief Query status of WebRTC + * + * @param[in] rtc_handle WebRTC handle + * + * @return + * - ESP_PEER_ERR_NONE On success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + */ +int esp_webrtc_query(esp_webrtc_handle_t rtc_handle); + +/** + * @brief Stop WebRTC + * + * @param[in] rtc_handle WebRTC handle + * + * @return + * - ESP_PEER_ERR_NONE On success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + * - Others Fail to send customized data + */ +int esp_webrtc_stop(esp_webrtc_handle_t rtc_handle); + +/** + * @brief Close WebRTC + * + * @param[in] rtc_handle WebRTC handle + * + * @return + * - ESP_PEER_ERR_NONE On success + * - ESP_PEER_ERR_INVALID_ARG Invalid argument + */ +int esp_webrtc_close(esp_webrtc_handle_t rtc_handle); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_webrtc/include/esp_webrtc_defaults.h b/components/third_party/esp_webrtc/include/esp_webrtc_defaults.h new file mode 100644 index 0000000..4912e10 --- /dev/null +++ b/components/third_party/esp_webrtc/include/esp_webrtc_defaults.h @@ -0,0 +1,68 @@ + +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include "esp_peer.h" +#include "esp_peer_signaling.h" +#include "esp_peer_whip_signaling.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get APPRTC signaling implement + * + * @param[in] cfg Signaling configuration + * @param[in] impl Implement of signaling interface + * @param[out] sig Signaling handle + * + * @return + * - NULL Not enough memory + * - Others APPRTC signaling implementation + */ +const esp_peer_signaling_impl_t *esp_signaling_get_apprtc_impl(void); + +/** + * @brief Get WHIP signaling implementation + * + * @return + * - NULL Not enough memory + * - Others WHIP signaling implementation + */ +const esp_peer_signaling_impl_t *esp_signaling_get_whip_impl(void); + +/** + * @brief Get default peer connection implementation + * @return + * - NULL No default implementation, or not enough memory + * - Others Default implementation + */ +const esp_peer_ops_t *esp_peer_get_default_impl(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_webrtc/include/esp_webrtc_version.h b/components/third_party/esp_webrtc/include/esp_webrtc_version.h new file mode 100644 index 0000000..b7cf033 --- /dev/null +++ b/components/third_party/esp_webrtc/include/esp_webrtc_version.h @@ -0,0 +1,35 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +const char* esp_webrtc_get_version(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/esp_webrtc/src/esp_peer.c b/components/third_party/esp_webrtc/src/esp_peer.c new file mode 100644 index 0000000..93e0219 --- /dev/null +++ b/components/third_party/esp_webrtc/src/esp_peer.c @@ -0,0 +1,198 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "esp_peer.h" +#include +#include + +typedef struct { + esp_peer_ops_t ops; + esp_peer_handle_t handle; +} peer_wrapper_t; + +int esp_peer_open(esp_peer_cfg_t *cfg, const esp_peer_ops_t *ops, esp_peer_handle_t *handle) +{ + if (cfg == NULL || ops == NULL || handle == NULL || ops->open == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = calloc(1, sizeof(peer_wrapper_t)); + if (peer == NULL) { + return ESP_PEER_ERR_NO_MEM; + } + memcpy(&peer->ops, ops, sizeof(esp_peer_ops_t)); + int ret = ops->open(cfg, &peer->handle); + if (ret != ESP_PEER_ERR_NONE) { + free(peer); + return ret; + } + *handle = peer; + return ret; +} + +int esp_peer_new_connection(esp_peer_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.new_connection) { + return peer->ops.new_connection(peer->handle); + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_create_data_channel(esp_peer_handle_t handle, esp_peer_data_channel_cfg_t *ch_cfg) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.create_data_channel) { + return peer->ops.create_data_channel(peer->handle, ch_cfg); + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_close_data_channel(esp_peer_handle_t handle, const char *label) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.close_data_channel) { + return peer->ops.close_data_channel(peer->handle, label); + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_update_ice_info(esp_peer_handle_t handle, esp_peer_role_t role, esp_peer_ice_server_cfg_t* server, int server_num) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.update_ice_info) { + return peer->ops.update_ice_info(peer->handle, role, server, server_num); + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_send_msg(esp_peer_handle_t handle, esp_peer_msg_t *msg) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.send_msg) { + return peer->ops.send_msg(peer->handle, msg); + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_send_video(esp_peer_handle_t handle, esp_peer_video_frame_t *info) +{ + if (handle == NULL || info == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.send_video) { + return peer->ops.send_video(peer->handle, info); + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_send_audio(esp_peer_handle_t handle, esp_peer_audio_frame_t *info) +{ + if (handle == NULL || info == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.send_audio) { + return peer->ops.send_audio(peer->handle, info); + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_send_data(esp_peer_handle_t handle, esp_peer_data_frame_t *info) +{ + if (handle == NULL || info == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.send_data) { + return peer->ops.send_data(peer->handle, info); + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_main_loop(esp_peer_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.main_loop) { + return peer->ops.main_loop(peer->handle); + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_disconnect(esp_peer_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.disconnect) { + return peer->ops.disconnect(peer->handle); + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_query(esp_peer_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + if (peer->ops.query) { + peer->ops.query(peer->handle); + return ESP_PEER_ERR_NONE; + } + return ESP_PEER_ERR_NOT_SUPPORT; +} + +int esp_peer_close(esp_peer_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + peer_wrapper_t *peer = (peer_wrapper_t *)handle; + int ret = ESP_PEER_ERR_NOT_SUPPORT; + if (peer->ops.close) { + ret = peer->ops.close(peer->handle); + } + free(peer); + return ret; +} diff --git a/components/third_party/esp_webrtc/src/esp_peer_signaling.c b/components/third_party/esp_webrtc/src/esp_peer_signaling.c new file mode 100644 index 0000000..4e76d4b --- /dev/null +++ b/components/third_party/esp_webrtc/src/esp_peer_signaling.c @@ -0,0 +1,72 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "esp_peer_signaling.h" +#include +#include +#include + +typedef struct { + esp_peer_signaling_handle_t sig_handle; + esp_peer_signaling_impl_t impl; +} signaling_wrapper_t; + +int esp_peer_signaling_start(esp_peer_signaling_cfg_t *cfg, const esp_peer_signaling_impl_t *impl, esp_peer_signaling_handle_t *handle) +{ + if (cfg == NULL || impl == NULL || handle == NULL || impl->start == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + signaling_wrapper_t *sig = calloc(1, sizeof(signaling_wrapper_t)); + if (sig == NULL) { + return ESP_PEER_ERR_NO_MEM; + } + int ret = impl->start(cfg, &sig->sig_handle); + if (ret != ESP_PEER_ERR_NONE) { + free(sig); + return ret; + } + sig->impl = *impl; + *handle = sig; + return ret; +} + +int esp_peer_signaling_send_msg(esp_peer_signaling_handle_t handle, esp_peer_signaling_msg_t *msg) +{ + if (handle == NULL || msg == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + signaling_wrapper_t *sig = (signaling_wrapper_t *)handle; + return sig->impl.send_msg(sig->sig_handle, msg); +} + +int esp_peer_signaling_stop(esp_peer_signaling_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + signaling_wrapper_t *sig = (signaling_wrapper_t *)handle; + int ret = sig->impl.stop(sig->sig_handle); + free(sig); + return ret; +} \ No newline at end of file diff --git a/components/third_party/esp_webrtc/src/esp_webrtc.c b/components/third_party/esp_webrtc/src/esp_webrtc.c new file mode 100644 index 0000000..f03214c --- /dev/null +++ b/components/third_party/esp_webrtc/src/esp_webrtc.c @@ -0,0 +1,924 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include "esp_log.h" +#include "media_lib_os.h" +#include "esp_timer.h" +#include "esp_webrtc.h" +#include "esp_codec_dev.h" +#include "esp_webrtc_defaults.h" + +#define AUDIO_FRAME_INTERVAL (20) +#define STR_SAME(a, b) (strncmp(a, b, sizeof(b) - 1) == 0) +#define GOTO_LABEL_ON_NULL(label, ptr, code) if (ptr == NULL) { \ + ret = code; \ + goto label; \ +} + +#define SAFE_FREE(ptr) if (ptr) { \ + free(ptr); \ + ptr = NULL; \ +} +#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) + +#define SET_WAIT_BITS(bit) media_lib_event_group_set_bits(rtc->wait_event, bit) +#define WAIT_FOR_BITS(bit) \ + media_lib_event_group_wait_bits(rtc->wait_event, bit, MEDIA_LIB_MAX_LOCK_TIME); \ + media_lib_event_group_clr_bits(rtc->wait_event, bit) + +typedef struct { + esp_webrtc_cfg_t rtc_cfg; + esp_peer_handle_t pc; + esp_peer_signaling_handle_t signaling; + esp_peer_state_t peer_state; + bool running; + bool pause; + media_lib_event_grp_handle_t wait_event; + esp_webrtc_event_handler_t event_handler; + esp_peer_role_t ice_role; + void *ctx; + // These field will change to esp_media_stream in the near feature + + esp_timer_handle_t send_timer; + bool send_going; + esp_webrtc_media_provider_t media_provider; + esp_capture_path_handle_t capture_path; + esp_codec_dev_handle_t play_handle; + esp_peer_audio_stream_info_t recv_aud_info; + esp_peer_video_stream_info_t recv_vid_info; + bool pending_connect; + esp_peer_signaling_ice_info_t ice_info; + bool ice_info_loaded; + bool signaling_connected; + + uint8_t *aud_fifo; + uint32_t aud_fifo_size; + // For debug only + uint32_t vid_send_pts; + uint32_t aud_send_pts; + uint32_t aud_recv_pts; + uint32_t vid_send_size; + uint32_t aud_send_size; + uint32_t aud_recv_size; + uint32_t vid_recv_size; + uint8_t aud_send_num; + uint8_t vid_send_num; + uint8_t aud_recv_num; + uint8_t vid_recv_num; +} webrtc_t; + +static const char *TAG = "webrtc"; + +bool webrtc_tracing = false; + +static void _media_send(void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + if (rtc->rtc_cfg.peer_cfg.audio_info.codec) { + esp_capture_stream_frame_t audio_frame = { + .stream_type = ESP_CAPTURE_STREAM_TYPE_AUDIO, + }; + // Get and send all audio frame without wait + while (esp_capture_acquire_path_frame(rtc->capture_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, + }; + esp_peer_send_audio(rtc->pc, &audio_send_frame); + esp_capture_release_path_frame(rtc->capture_path, &audio_frame); + rtc->aud_send_pts = audio_frame.pts; + rtc->aud_send_num++; + rtc->aud_send_size += audio_frame.size; + if (webrtc_tracing) { + printf("A\n"); + } + } + } + if (rtc->rtc_cfg.peer_cfg.video_info.codec) { + esp_capture_stream_frame_t video_frame = { + .stream_type = ESP_CAPTURE_STREAM_TYPE_VIDEO, + }; + int ret = esp_capture_acquire_path_frame(rtc->capture_path, &video_frame, true); + if (ret == ESP_CAPTURE_ERR_OK) { + if (rtc->rtc_cfg.peer_cfg.enable_data_channel && rtc->rtc_cfg.peer_cfg.video_over_data_channel) { + esp_peer_data_frame_t data_frame = { + .type = ESP_PEER_DATA_CHANNEL_DATA, + .data = video_frame.data, + .size = video_frame.size, + }; + esp_peer_send_data(rtc->pc, &data_frame); + } else { + esp_peer_video_frame_t video_send_frame = { + .pts = video_frame.pts, + .data = video_frame.data, + .size = video_frame.size, + }; + esp_peer_send_video(rtc->pc, &video_send_frame); + } + esp_capture_release_path_frame(rtc->capture_path, &video_frame); + rtc->vid_send_pts = video_frame.pts; + rtc->vid_send_num++; + rtc->vid_send_size += video_frame.size; + if (webrtc_tracing) { + printf("V\n"); + } + } + } +} + +void media_send_task(void *arg) +{ + webrtc_t *rtc = (webrtc_t *)arg; + while (rtc->send_going) { + _media_send(arg); + media_lib_thread_sleep(AUDIO_FRAME_INTERVAL); + } + SET_WAIT_BITS(PC_SEND_QUIT_BIT); + media_lib_thread_destroy(NULL); +} + +static int start_stream(webrtc_t *rtc) +{ + int ret = esp_capture_start(rtc->media_provider.capture); + if (ret == ESP_CAPTURE_ERR_OK) { + media_lib_thread_handle_t handle = NULL; + rtc->send_going = true; + ret = media_lib_thread_create_from_scheduler(&handle, "pc_send", media_send_task, rtc); + if (ret != 0) { + rtc->send_going = false; + } + } else { + ESP_LOGE(TAG, "Fail to start capture ret:%d", ret); + } + return ret; +} + +static int stop_stream(webrtc_t *rtc) +{ + if (rtc->send_going) { + rtc->send_going = false; + WAIT_FOR_BITS(PC_SEND_QUIT_BIT); + } + esp_capture_stop(rtc->media_provider.capture); + av_render_reset(rtc->play_handle); + return 0; +} + +static void pc_notify_app(webrtc_t *rtc, esp_webrtc_event_type_t event_type) +{ + esp_webrtc_event_t event = { + .type = event_type, + }; + if (rtc->event_handler) { + rtc->event_handler(&event, rtc->ctx); + } +} + +static int pc_on_state(esp_peer_state_t state, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + ESP_LOGI(TAG, "PeerConnectionState: %d", state); + if (state != ESP_PEER_STATE_DATA_CHANNEL_OPENED && + state != ESP_PEER_STATE_DATA_CHANNEL_CLOSED && + state != ESP_PEER_STATE_DATA_CHANNEL_CONNECTED && + state != ESP_PEER_STATE_DATA_CHANNEL_DISCONNECTED) { + rtc->peer_state = state; + } + + if (state == ESP_PEER_STATE_CONNECTED) { + start_stream(rtc); + pc_notify_app(rtc, ESP_WEBRTC_EVENT_CONNECTED); + } else if (state == ESP_PEER_STATE_DISCONNECTED) { + stop_stream(rtc); + pc_notify_app(rtc, ESP_WEBRTC_EVENT_DISCONNECTED); + } else if (state == ESP_PEER_STATE_CONNECT_FAILED) { + // Run in mainloop task + pc_notify_app(rtc, ESP_WEBRTC_EVENT_CONNECT_FAILED); + } else if (state == ESP_PEER_STATE_DATA_CHANNEL_CONNECTED) { + pc_notify_app(rtc, ESP_WEBRTC_EVENT_DATA_CHANNEL_CONNECTED); + } else if (state == ESP_PEER_STATE_DATA_CHANNEL_DISCONNECTED) { + pc_notify_app(rtc, ESP_WEBRTC_EVENT_DATA_CHANNEL_DISCONNECTED); + } else if (state == ESP_PEER_STATE_DATA_CHANNEL_OPENED) { + pc_notify_app(rtc, ESP_WEBRTC_EVENT_DATA_CHANNEL_OPENED); + } else if (state == ESP_PEER_STATE_DATA_CHANNEL_CLOSED) { + pc_notify_app(rtc, ESP_WEBRTC_EVENT_DATA_CHANNEL_CLOSED); + } + return 0; +} + +static int pc_on_msg(esp_peer_msg_t *info, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + ESP_LOGI(TAG, "Send client sdp: %s\n", info->data); + return esp_peer_signaling_send_msg(rtc->signaling, (esp_peer_signaling_msg_t *)info); +} + +static void pc_task(void *arg) +{ + webrtc_t *rtc = (webrtc_t *)arg; + ESP_LOGI(TAG, "peer_connection_task started"); + while (rtc->running) { + if (rtc->pause) { + SET_WAIT_BITS(PC_PAUSED_BIT); + WAIT_FOR_BITS(PC_RESUME_BIT); + continue; + } + esp_peer_main_loop(rtc->pc); + media_lib_thread_sleep(10); + } + SET_WAIT_BITS(PC_EXIT_BIT); + media_lib_thread_destroy(NULL); +} + +static av_render_video_codec_t get_video_dec_codec(esp_peer_video_codec_t codec) +{ + switch (codec) { + case ESP_PEER_VIDEO_CODEC_H264: + return AV_RENDER_VIDEO_CODEC_H264; + case ESP_PEER_VIDEO_CODEC_MJPEG: + return AV_RENDER_VIDEO_CODEC_MJPEG; + default: + return AV_RENDER_VIDEO_CODEC_NONE; + } +} +static void convert_dec_vid_info(esp_peer_video_stream_info_t *info, av_render_video_info_t *dec_info) +{ + dec_info->codec = get_video_dec_codec(info->codec); + dec_info->width = info->width; + dec_info->height = info->height; + dec_info->fps = info->fps; +} + +static int pc_on_video_info(esp_peer_video_stream_info_t *info, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + av_render_video_info_t video_info = {}; + convert_dec_vid_info(info, &video_info); + av_render_add_video_stream(rtc->play_handle, &video_info); + rtc->recv_vid_info.codec = info->codec; + return 0; +} + +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 int pc_on_audio_info(esp_peer_audio_stream_info_t *info, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + rtc->recv_aud_info = *info; + av_render_audio_info_t audio_info = {}; + convert_dec_aud_info(info, &audio_info); + printf("Add audio codec %d sample_rate %d\n", audio_info.codec, (int)audio_info.sample_rate); + av_render_add_audio_stream(rtc->play_handle, &audio_info); + rtc->recv_aud_info.codec = info->codec; + return 0; +} + +static int pc_on_audio_data(esp_peer_audio_frame_t *info, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + if (rtc->running == false || rtc->recv_aud_info.codec == ESP_PEER_AUDIO_CODEC_NONE) { + return 0; + } + rtc->aud_recv_pts = info->pts; + rtc->aud_recv_num++; + rtc->aud_recv_size += info->size; + av_render_audio_data_t audio_data = { + .pts = info->pts, + .data = info->data, + .size = info->size, + }; + av_render_add_audio_data(rtc->play_handle, &audio_data); + return 0; +} + +static int pc_on_video_data(esp_peer_video_frame_t *info, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + if (rtc->running == false) { + return 0; + } + rtc->vid_recv_num++; + rtc->vid_recv_size += info->size; + av_render_video_data_t video_data = { + .pts = info->pts, + .data = info->data, + .size = info->size, + }; + av_render_add_video_data(rtc->play_handle, &video_data); + return 0; +} + +static int pc_on_data(esp_peer_data_frame_t *frame, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + // Notify custom data over data channel + if (rtc->rtc_cfg.peer_cfg.video_over_data_channel == false) { + if (rtc->rtc_cfg.peer_cfg.on_data) { + rtc->rtc_cfg.peer_cfg.on_data(frame, rtc->rtc_cfg.peer_cfg.ctx); + } + if (rtc->rtc_cfg.peer_cfg.on_custom_data) { + rtc->rtc_cfg.peer_cfg.on_custom_data(ESP_WEBRTC_CUSTOM_DATA_VIA_DATA_CHANNEL, + frame->data, frame->size, rtc->rtc_cfg.peer_cfg.ctx); + } + return 0; + } + rtc->vid_recv_num++; + rtc->vid_recv_size += frame->size; + // Treat received data as video data + if (rtc->recv_vid_info.codec == ESP_PEER_VIDEO_CODEC_NONE) { + rtc->recv_vid_info.codec = rtc->rtc_cfg.peer_cfg.video_info.codec; + av_render_video_info_t video_info = {}; + convert_dec_vid_info(&rtc->recv_vid_info, &video_info); + av_render_add_video_stream(rtc->play_handle, &video_info); + } + av_render_video_data_t video_data = { + .data = frame->data, + .size = frame->size, + }; + av_render_add_video_data(rtc->play_handle, &video_data); + return 0; +} + +static void free_server_cfg(webrtc_t *rtc) +{ + if (rtc->rtc_cfg.peer_cfg.server_lists == NULL) { + return; + } + esp_peer_ice_server_cfg_t *server_cfg = rtc->rtc_cfg.peer_cfg.server_lists; + for (int i = 0; i < rtc->rtc_cfg.peer_cfg.server_num; i++) { + SAFE_FREE(server_cfg[i].stun_url); + SAFE_FREE(server_cfg[i].user); + SAFE_FREE(server_cfg[i].psw); + } + SAFE_FREE(rtc->rtc_cfg.peer_cfg.server_lists); + rtc->rtc_cfg.peer_cfg.server_num = 0; +} + +static int malloc_server_cfg(webrtc_t *rtc, esp_peer_ice_server_cfg_t *cfg, int server_num) +{ + if (server_num == 0) { + return ESP_PEER_ERR_NONE; + } + if (rtc->rtc_cfg.peer_cfg.server_num) { + ESP_LOGE(TAG, "Already have server config"); + return ESP_PEER_ERR_WRONG_STATE; + } + rtc->rtc_cfg.peer_cfg.server_lists = calloc(1, server_num * sizeof(esp_peer_ice_server_cfg_t)); + if (rtc->rtc_cfg.peer_cfg.server_lists == NULL) { + return ESP_PEER_ERR_NO_MEM; + } + rtc->rtc_cfg.peer_cfg.server_num = server_num; + esp_peer_ice_server_cfg_t *dst = rtc->rtc_cfg.peer_cfg.server_lists; + int ret = ESP_PEER_ERR_NONE; + for (int i = 0; i < server_num; i++) { + GOTO_LABEL_ON_NULL(_exit, cfg[i].stun_url, ESP_PEER_ERR_INVALID_ARG); + dst[i].stun_url = strdup(cfg[i].stun_url); + GOTO_LABEL_ON_NULL(_exit, dst[i].stun_url, ESP_PEER_ERR_NO_MEM); + if (cfg[i].user) { + dst[i].user = strdup(cfg[i].user); + GOTO_LABEL_ON_NULL(_exit, dst[i].user, ESP_PEER_ERR_NO_MEM); + } + if (cfg[i].psw) { + dst[i].psw = strdup(cfg[i].psw); + GOTO_LABEL_ON_NULL(_exit, dst[i].psw, ESP_PEER_ERR_NO_MEM); + } + } + return ret; +_exit: + free_server_cfg(rtc); + return ESP_PEER_ERR_INVALID_ARG; +} + +static esp_capture_codec_type_t get_capture_audio_codec(esp_peer_audio_codec_t aud_codec) +{ + switch (aud_codec) { + default: + return ESP_CAPTURE_CODEC_TYPE_NONE; + 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; + } +} + +static esp_capture_codec_type_t get_capture_video_codec(esp_peer_audio_codec_t vid_codec) +{ + switch (vid_codec) { + default: + return ESP_CAPTURE_CODEC_TYPE_NONE; + case ESP_PEER_VIDEO_CODEC_H264: + return ESP_CAPTURE_CODEC_TYPE_H264; + case ESP_PEER_VIDEO_CODEC_MJPEG: + return ESP_CAPTURE_CODEC_TYPE_MJPEG; + } +} + +static int pc_close(webrtc_t *rtc) +{ + if (rtc->pc) { + esp_peer_disconnect(rtc->pc); + bool still_running = rtc->running; + // Wait for PC task quit + if (rtc->pause) { + rtc->pause = false; + SET_WAIT_BITS(PC_RESUME_BIT); + } + rtc->running = false; + if (still_running) { + WAIT_FOR_BITS(PC_EXIT_BIT); + } + esp_peer_close(rtc->pc); + rtc->pc = NULL; + } + if (rtc->wait_event) { + media_lib_event_group_destroy(rtc->wait_event); + rtc->wait_event = NULL; + } + return ESP_PEER_ERR_NONE; +} + +static int pc_on_channel_open(esp_peer_data_channel_info_t *ch, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + if (rtc->rtc_cfg.peer_cfg.on_channel_open) { + return rtc->rtc_cfg.peer_cfg.on_channel_open(ch, rtc->ctx); + } + return 0; +} + +static int pc_on_channel_close(esp_peer_data_channel_info_t *ch, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + if (rtc->rtc_cfg.peer_cfg.on_channel_close) { + return rtc->rtc_cfg.peer_cfg.on_channel_close(ch, rtc->ctx); + } + return 0; +} + +static int pc_start(webrtc_t *rtc, esp_peer_ice_server_cfg_t *server_info, int server_num) +{ + if (rtc->pc) { + return esp_peer_update_ice_info(rtc->pc, rtc->ice_role, server_info, server_num); + } + esp_peer_cfg_t peer_cfg = { + .server_lists = server_info, + .server_num = server_num, + .ice_trans_policy = rtc->rtc_cfg.peer_cfg.ice_trans_policy, + .audio_dir = rtc->rtc_cfg.peer_cfg.audio_dir, + .video_dir = rtc->rtc_cfg.peer_cfg.video_dir, + .enable_data_channel = rtc->rtc_cfg.peer_cfg.enable_data_channel, + .manual_ch_create = rtc->rtc_cfg.peer_cfg.manual_ch_create, + .no_auto_reconnect = rtc->rtc_cfg.peer_cfg.no_auto_reconnect, + .extra_cfg = rtc->rtc_cfg.peer_cfg.extra_cfg, + .extra_size = rtc->rtc_cfg.peer_cfg.extra_size, + .on_state = pc_on_state, + .on_msg = pc_on_msg, + .on_video_info = pc_on_video_info, + .on_audio_info = pc_on_audio_info, + .on_video_data = pc_on_video_data, + .on_audio_data = pc_on_audio_data, + .on_channel_open = pc_on_channel_open, + .on_channel_close = pc_on_channel_close, + .on_data = pc_on_data, + .role = rtc->ice_role, + .ctx = rtc, + }; + memcpy(&peer_cfg.audio_info, &rtc->rtc_cfg.peer_cfg.audio_info, sizeof(esp_peer_audio_stream_info_t)); + if (rtc->rtc_cfg.peer_cfg.enable_data_channel == false || rtc->rtc_cfg.peer_cfg.video_over_data_channel == false) { + memcpy(&peer_cfg.video_info, &rtc->rtc_cfg.peer_cfg.video_info, sizeof(esp_peer_video_stream_info_t)); + } + int ret = esp_peer_open(&peer_cfg, esp_peer_get_default_impl(), &rtc->pc); + if (ret != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG, "Fail to open peer ret %d", ret); + return ret; + } + media_lib_event_group_create(&rtc->wait_event); + if (rtc->wait_event == NULL) { + return ESP_PEER_ERR_NO_MEM; + } + // Set running flag + rtc->running = true; + media_lib_thread_handle_t thread; + media_lib_thread_create_from_scheduler(&thread, "pc_task", pc_task, rtc); + esp_capture_sink_cfg_t sink_cfg = { + .audio_info = { + .codec = get_capture_audio_codec(peer_cfg.audio_info.codec), + .sample_rate = peer_cfg.audio_info.sample_rate ? peer_cfg.audio_info.sample_rate : 8000, + .channel = peer_cfg.audio_info.channel ? peer_cfg.audio_info.channel : 1, + .bits_per_sample = 16, + }, + .video_info = { + .codec = get_capture_video_codec(rtc->rtc_cfg.peer_cfg.video_info.codec), + .width = rtc->rtc_cfg.peer_cfg.video_info.width, + .height = rtc->rtc_cfg.peer_cfg.video_info.height, + .fps = rtc->rtc_cfg.peer_cfg.video_info.fps, + }, + }; + rtc->play_handle = rtc->media_provider.player; + if (peer_cfg.audio_dir == ESP_PEER_MEDIA_DIR_RECV_ONLY) { + sink_cfg.audio_info.codec = ESP_CAPTURE_CODEC_TYPE_NONE; + } else if (peer_cfg.audio_dir == ESP_PEER_MEDIA_DIR_SEND_ONLY) { + rtc->play_handle = NULL; + } + if (peer_cfg.video_dir == ESP_PEER_MEDIA_DIR_RECV_ONLY) { + sink_cfg.video_info.codec = ESP_CAPTURE_CODEC_TYPE_NONE; + } + esp_capture_setup_path(rtc->media_provider.capture, ESP_CAPTURE_PATH_PRIMARY, &sink_cfg, &rtc->capture_path); + esp_capture_enable_path(rtc->capture_path, ESP_CAPTURE_RUN_TYPE_ALWAYS); + return ret; +} + +static int start_peer_connection(webrtc_t *rtc, esp_peer_signaling_ice_info_t *info) +{ + rtc->ice_role = info->is_initiator ? ESP_PEER_ROLE_CONTROLLING : ESP_PEER_ROLE_CONTROLLED; + int ret; + if (info->server_info.stun_url) { + ret = pc_start(rtc, &info->server_info, 1); + } else { + ret = pc_start(rtc, rtc->rtc_cfg.peer_cfg.server_lists, rtc->rtc_cfg.peer_cfg.server_num); + } + return ret; +} + +static int signal_ice_received(esp_peer_signaling_ice_info_t *info, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + rtc->ice_info_loaded = true; + rtc->ice_info = *info; + if (rtc->pending_connect) { + ESP_LOGI(TAG, "Pending connection until user enable"); + return ESP_PEER_ERR_NONE; + } + return start_peer_connection(rtc, info); +} + +static int signal_connected(void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + rtc->signaling_connected = true; + if (rtc->rtc_cfg.peer_cfg.no_auto_reconnect && rtc->pending_connect) { + printf("Signaling connected, pending for use not enable\n"); + return 0; + } + if (rtc->pc) { + // Create offer so that fetch ice candidate + esp_peer_new_connection(rtc->pc); + } + return 0; +} + +static int signal_new_msg(esp_peer_signaling_msg_t *msg, void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + if (msg->type == ESP_PEER_SIGNALING_MSG_BYE) { + ESP_LOGI(TAG, "Received BYE"); + int ret = ESP_PEER_ERR_NONE; + if (rtc->running) { + // Wait for main loop paused + if (rtc->pause == false) { + rtc->pause = true; + WAIT_FOR_BITS(PC_PAUSED_BIT); + } + esp_peer_disconnect(rtc->pc); + rtc->recv_vid_info.codec = ESP_PEER_VIDEO_CODEC_NONE; + stop_stream(rtc); + if (rtc->rtc_cfg.peer_cfg.no_auto_reconnect == false) { + // Reconnect + ret = esp_peer_new_connection(rtc->pc); + if (rtc->pause) { + // resume main loop + rtc->pause = false; + SET_WAIT_BITS(PC_RESUME_BIT); + } + } + } + return ret; + } else { + if (msg->type == ESP_PEER_SIGNALING_MSG_CUSTOMIZED) { + ESP_LOGI(TAG, "Received customized data"); + if (rtc->rtc_cfg.peer_cfg.on_custom_data) { + return rtc->rtc_cfg.peer_cfg.on_custom_data( + ESP_WEBRTC_CUSTOM_DATA_VIA_SIGNALING, + msg->data, + msg->size, + rtc->rtc_cfg.peer_cfg.ctx); + } + return 0; + } + char *sdp = (char *)msg->data; + esp_peer_msg_t peer_msg = { + .type = msg->type, + .data = msg->data, + .size = msg->size, + }; + if (STR_SAME(sdp, "candidate:")) { + peer_msg.type = ESP_PEER_MSG_TYPE_CANDIDATE; + } + return esp_peer_send_msg(rtc->pc, &peer_msg); + } +} + +static int signal_closed(void *ctx) +{ + webrtc_t *rtc = (webrtc_t *)ctx; + rtc->signaling_connected = false; + // TODO disconnect if peer closed + esp_peer_disconnect(rtc->pc); + return 0; +} + +int esp_webrtc_open(esp_webrtc_cfg_t *cfg, esp_webrtc_handle_t *handle) +{ + if (cfg == NULL || cfg->signaling_impl == NULL || cfg->peer_impl == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + webrtc_t *rtc = (webrtc_t *)calloc(1, sizeof(webrtc_t)); + if (rtc == NULL) { + return ESP_PEER_ERR_NO_MEM; + } + // TODO deep copy of other settings + rtc->rtc_cfg = *cfg; + rtc->rtc_cfg.peer_cfg.server_num = 0; + rtc->rtc_cfg.peer_cfg.server_lists = NULL; + malloc_server_cfg(rtc, cfg->peer_cfg.server_lists, cfg->peer_cfg.server_num); + if (cfg->peer_cfg.extra_cfg) { + void *peer_exta_cfg = calloc(1, cfg->peer_cfg.extra_size); + if (peer_exta_cfg) { + memcpy(peer_exta_cfg, cfg->peer_cfg.extra_cfg, cfg->peer_cfg.extra_size); + } + rtc->rtc_cfg.peer_cfg.extra_cfg = peer_exta_cfg; + } + if (cfg->signaling_cfg.extra_cfg) { + void *signaling_cfg = calloc(1, cfg->signaling_cfg.extra_size); + if (signaling_cfg) { + memcpy(signaling_cfg, cfg->signaling_cfg.extra_cfg, cfg->signaling_cfg.extra_size); + } + rtc->rtc_cfg.signaling_cfg.extra_cfg = signaling_cfg; + } + *handle = rtc; + return ESP_PEER_ERR_NONE; +} + +int esp_webrtc_enable_peer_connection(esp_webrtc_handle_t handle, bool enable) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + webrtc_t *rtc = (webrtc_t *)handle; + rtc->pending_connect = !enable; + int ret = ESP_PEER_ERR_NONE; + if (rtc->pending_connect == false) { + if (rtc->pc == NULL) { + // Create peer connection firstly + if (rtc->ice_info_loaded == false) { + // Wait for ice info loaded + ESP_LOGE(TAG, "ICE info not fetched yet"); + return 0; + } else { + ret = start_peer_connection(rtc, &rtc->ice_info); + if (ret != ESP_PEER_ERR_NONE) { + return ret; + } + } + } + // Signaling already connected + if (rtc->signaling_connected) { + ret = esp_peer_new_connection(rtc->pc); + // Let mainloop resume + if (rtc->pause) { + rtc->pause = false; + SET_WAIT_BITS(PC_RESUME_BIT); + } + } + } else { + // Close connection better than reconnect? + rtc->recv_vid_info.codec = ESP_PEER_VIDEO_CODEC_NONE; + stop_stream(rtc); + pc_close(rtc); + } + return ret; +} + +int esp_webrtc_set_event_handler(esp_webrtc_handle_t handle, esp_webrtc_event_handler_t handler, void *ctx) +{ + if (handle == NULL || handler == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + webrtc_t *rtc = (webrtc_t *)handle; + rtc->event_handler = handler; + rtc->ctx = ctx; + return ESP_PEER_ERR_NONE; +} + +int esp_webrtc_set_media_provider(esp_webrtc_handle_t handle, esp_webrtc_media_provider_t *provider) +{ + if (handle == NULL || provider->capture == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + webrtc_t *rtc = (webrtc_t *)handle; + rtc->media_provider = *provider; + // Temp use esp_codec_dev as simple player + rtc->play_handle = provider->player; + return ESP_PEER_ERR_NONE; +} + +int esp_webrtc_start(esp_webrtc_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + webrtc_t *rtc = (webrtc_t *)handle; + if (rtc->signaling) { + ESP_LOGW(TAG, "Already started"); + return ESP_PEER_ERR_WRONG_STATE; + } + + // Start signaling firstly + esp_peer_signaling_cfg_t sig_cfg = { + .signal_url = rtc->rtc_cfg.signaling_cfg.signal_url, + .extra_cfg = rtc->rtc_cfg.signaling_cfg.extra_cfg, + .extra_size = rtc->rtc_cfg.signaling_cfg.extra_size, + .on_ice_info = signal_ice_received, + .on_connected = signal_connected, + .on_msg = signal_new_msg, + .on_close = signal_closed, + .ctx = rtc + }; + int ret = esp_peer_signaling_start(&sig_cfg, rtc->rtc_cfg.signaling_impl, &rtc->signaling); + if (ret != ESP_PEER_ERR_NONE) { + ESP_LOGE(TAG, "Fail to start signaling"); + return ret; + } + return ret; +} + +int esp_webrtc_send_custom_data(esp_webrtc_handle_t handle, esp_webrtc_custom_data_via_t via, uint8_t *data, int size) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + webrtc_t *rtc = (webrtc_t *)handle; + if (via == ESP_WEBRTC_CUSTOM_DATA_VIA_SIGNALING) { + esp_peer_signaling_msg_t msg = { + .type = ESP_PEER_SIGNALING_MSG_CUSTOMIZED, + .data = data, + .size = size, + }; + return esp_peer_signaling_send_msg(rtc->signaling, &msg); + } + if (via == ESP_WEBRTC_CUSTOM_DATA_VIA_DATA_CHANNEL) { + esp_peer_data_frame_t data_frame = { + .type = ESP_PEER_DATA_CHANNEL_STRING, + .data = data, + .size = size, + }; + return esp_peer_send_data(rtc->pc, &data_frame); + } + return ESP_PEER_ERR_INVALID_ARG; +} + +int esp_webrtc_get_peer_connection(esp_webrtc_handle_t handle, esp_peer_handle_t *peer_handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + webrtc_t *rtc = (webrtc_t *)handle; + if (rtc->pc == NULL) { + return ESP_PEER_ERR_WRONG_STATE; + } + *peer_handle = rtc->pc; + return ESP_PEER_ERR_NONE; +} + +int esp_webrtc_restart(esp_webrtc_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + // TODO add restart code, send bye to signaling server + return ESP_PEER_ERR_NONE; +} + +int esp_webrtc_query(esp_webrtc_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + webrtc_t *rtc = (webrtc_t *)handle; + if (rtc->peer_state != ESP_PEER_STATE_CONNECTED) { + return ESP_PEER_ERR_WRONG_STATE; + } + if (rtc->vid_send_num == 0) { + // Audio only case + ESP_LOGI(TAG, "Send A:%d [%d:%d] Recv A:%d [%d:%d]", + (int)rtc->aud_send_pts, (int)rtc->aud_send_num, (int)rtc->aud_send_size, + (int)rtc->aud_recv_pts, (int)rtc->aud_recv_num, (int)rtc->aud_recv_size); + } else { + ESP_LOGI(TAG, "Send A:%d [%d:%d] V:%d [%d:%d] Recv A:%d [%d:%d] Recv V:[%d:%d]", + (int)rtc->aud_send_pts, (int)rtc->aud_send_num, (int)rtc->aud_send_size, + (int)rtc->vid_send_pts, (int)rtc->vid_send_num, (int)rtc->vid_send_size, + (int)rtc->aud_recv_pts, (int)rtc->aud_recv_num, (int)rtc->aud_recv_size, + (int)rtc->vid_recv_num, (int)rtc->vid_recv_size); + } + esp_peer_query(rtc->pc); + printf("\n"); + // Clear send and receive info + rtc->vid_send_num = 0; + rtc->aud_send_num = 0; + rtc->aud_send_size = 0; + rtc->vid_send_size = 0; + rtc->aud_recv_num = 0; + rtc->aud_recv_size = 0; + rtc->vid_recv_num = 0; + rtc->vid_recv_size = 0; + return ESP_PEER_ERR_NONE; +} + +int esp_webrtc_stop(esp_webrtc_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + webrtc_t *rtc = (webrtc_t *)handle; + int ret = 0; + stop_stream(rtc); + // TODO stop agent + pc_close(rtc); + if (rtc->signaling) { + esp_peer_signaling_stop(rtc->signaling); + rtc->signaling = NULL; + } + return ret; +} + +int esp_webrtc_close(esp_webrtc_handle_t handle) +{ + if (handle == NULL) { + return ESP_PEER_ERR_INVALID_ARG; + } + webrtc_t *rtc = (webrtc_t *)handle; + esp_webrtc_stop(handle); + free_server_cfg(rtc); + SAFE_FREE(rtc->rtc_cfg.peer_cfg.extra_cfg); + SAFE_FREE(rtc->rtc_cfg.signaling_cfg.extra_cfg); + SAFE_FREE(rtc->aud_fifo); + free(rtc); + return ESP_PEER_ERR_NONE; +} diff --git a/components/third_party/esp_webrtc/src/esp_webrtc_version.c b/components/third_party/esp_webrtc/src/esp_webrtc_version.c new file mode 100644 index 0000000..57253aa --- /dev/null +++ b/components/third_party/esp_webrtc/src/esp_webrtc_version.c @@ -0,0 +1,30 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#define ESP_WBRTC_VERSION "0.9.0" + +const char* esp_webrtc_get_version(void) +{ + return ESP_WBRTC_VERSION; +} diff --git a/components/third_party/khash/CMakeLists.txt b/components/third_party/khash/CMakeLists.txt new file mode 100644 index 0000000..ef2a614 --- /dev/null +++ b/components/third_party/khash/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(SRC_DIRS ./ 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/media_lib_sal/CMakeLists.txt b/components/third_party/media_lib_sal/CMakeLists.txt new file mode 100644 index 0000000..43cdc2e --- /dev/null +++ b/components/third_party/media_lib_sal/CMakeLists.txt @@ -0,0 +1,11 @@ +set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}") + +set(COMPONENT_ADD_INCLUDEDIRS include include/port) + +# Edit following two lines to set component requirements (see docs) + +list (APPEND COMPONENT_SRCDIRS ./ ./port ./mem_trace) + +list(APPEND COMPONENT_REQUIRES esp-tls mbedtls esp_netif) + +register_component() diff --git a/components/third_party/media_lib_sal/Kconfig.projbuild b/components/third_party/media_lib_sal/Kconfig.projbuild new file mode 100644 index 0000000..4a7ec1d --- /dev/null +++ b/components/third_party/media_lib_sal/Kconfig.projbuild @@ -0,0 +1,57 @@ +menu "ADF Library Configuration" + +config MEDIA_PROTOCOL_LIB_ENABLE + bool "Enable Media Protocol Library" + default "y" + +config MEDIA_LIB_MEM_AUTO_TRACE + bool "Support trace memory automatically after media_lib_sal init" + default "n" + +config MEDIA_LIB_MEM_TRACE_DEPTH + int + prompt "Memory trace stack depth" if MEDIA_LIB_MEM_AUTO_TRACE + depends on MEDIA_LIB_MEM_AUTO_TRACE + default 3 + help + Set memory trace depth + +config MEDIA_LIB_MEM_TRACE_NUM + int + prompt "Memory trace number" if MEDIA_LIB_MEM_AUTO_TRACE + depends on MEDIA_LIB_MEM_AUTO_TRACE + default 1024 + help + Set memory trace number + +config MEDIA_LIB_MEM_TRACE_MODULE + bool "Trace for module memory usage" + depends on MEDIA_LIB_MEM_AUTO_TRACE + default y + +config MEDIA_LIB_MEM_TRACE_LEAKAGE + depends on MEDIA_LIB_MEM_AUTO_TRACE + bool "Trace for memory leakage" + default y + +config MEDIA_LIB_MEM_TRACE_SAVE_HISTORY + bool "Trace to save memory history" + depends on MEDIA_LIB_MEM_AUTO_TRACE + default n + +config MEDIA_LIB_MEM_SAVE_CACHE_SIZE + int + prompt "Cache buffer size to store save history" if MEDIA_LIB_MEM_TRACE_SAVE_HISTORY + depends on MEDIA_LIB_MEM_TRACE_SAVE_HISTORY + default 32768 + help + Set cache size for memory history + +config MEDIA_LIB_MEM_TRACE_SAVE_PATH + string "Memory trace save path" if MEDIA_LIB_MEM_TRACE_SAVE_HISTORY + depends on MEDIA_LIB_MEM_TRACE_SAVE_HISTORY + default "/sdcard/trace.log" + help + Set memory trace save path + +endmenu diff --git a/components/third_party/media_lib_sal/component.mk b/components/third_party/media_lib_sal/component.mk new file mode 100755 index 0000000..cd6822c --- /dev/null +++ b/components/third_party/media_lib_sal/component.mk @@ -0,0 +1,8 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +COMPONENT_ADD_INCLUDEDIRS := include include/port +COMPONENT_SRCDIRS := . port +COMPONENT_PRIV_INCLUDEDIRS := diff --git a/components/third_party/media_lib_sal/idf_component.yml b/components/third_party/media_lib_sal/idf_component.yml new file mode 100755 index 0000000..61988bd --- /dev/null +++ b/components/third_party/media_lib_sal/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + idf: + version: ">=4.1.0" diff --git a/components/third_party/media_lib_sal/include/data_queue.h b/components/third_party/media_lib_sal/include/data_queue.h new file mode 100644 index 0000000..b1f4021 --- /dev/null +++ b/components/third_party/media_lib_sal/include/data_queue.h @@ -0,0 +1,176 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Struct for data queue + * Data queue works like a queue, you can receive the exact size of data as you send previously. + * It allows you to get continuous buffer so that no need to care ring back issue. + * It adds a fill_end member to record fifo write end position before ring back. + */ +typedef struct { + void *buffer; /*!< Buffer for queue */ + int size; /*!< Buffer size */ + int fill_end; /*!< Buffer write position before ring back */ + int wp; /*!< Write pointer */ + int rp; /*!< Read pointer */ + int filled; /*!< Buffer filled size */ + int user; /*!< Buffer reference by reader or writer */ + int quit; /*!< Buffer quit flag */ + void *lock; /*!< Protect lock */ + void *write_lock; /*!< Write lock to let only one writer at same time */ + void *event; /*!< Event group to wake up reader or writer */ +} data_queue_t; + +/** + * @brief Initialize data queue + * + * @param size: Buffer size + * @return - NULL: Fail to initialize queue + * - Others: Data queue instance + */ +data_queue_t *data_queue_init(int size); + +/** + * @brief Wakeup thread which wait on queue data + * + * @param q: Data queue instance + */ +void data_queue_wakeup(data_queue_t *q); + +/** + * @brief Deinitialize data queue + * + * @param q: Data queue instance + */ +void data_queue_deinit(data_queue_t *q); + +/** + * @brief Get continuous buffer from data queue + * + * @param q: Data queue instance + * @param size: Buffer size want to get + * @return - NULL: Fail to get buffer + * - Others: Buffer data + */ +void *data_queue_get_buffer(data_queue_t *q, int size); + +/** + * @brief Get data pointer being written but not send yet + * + * @param q: Data queue instance + * @return - NULL: Fail to get write buffer + * - Others: Write data pointer + */ +void *data_queue_get_write_data(data_queue_t *q); + +/** + * @brief Send data into data queue + * + * @param q: Data queue instance + * @param size: Buffer size want to get + * @return - 0: On success + * - Others: Fail to send buffer + */ +int data_queue_send_buffer(data_queue_t *q, int size); + +/** + * @brief Read data from data queue, and add reference count + * + * @param q: Data queue instance + * @param[out] buffer: Buffer in front of queue, this buffer is always valid before call `data_queue_read_unlock` + * @param[out] size: Buffer size in front of queue + * @return - 0: On success + * - Others: Fail to read buffer + */ +int data_queue_read_lock(data_queue_t *q, void **buffer, int *size); + +/** + * @brief Release data be read and decrease reference count + * + * @param q: Data queue instance + * @param[out] buffer: Buffer in front of queue + * @param[out] size: Buffer size in front of queue + * @return - 0: On success + * - Others: Fail to read buffer + */ +int data_queue_read_unlock(data_queue_t *q); + +/** + * @brief Peak data unlock, call `data_queue_read_lock` to read data with block + * After peek data, not consume the data and release the lock + * + * @param q: Data queue instance + * @return - 0: On success + * - Others: Fail to read buffer + */ +int data_queue_peek_unlock(data_queue_t *q); + +/** + * @brief Consume all data in queue + * + * @param q: Data queue instance + * @return - 0: On success + * - Others: Fail to read buffer + */ +int data_queue_consume_all(data_queue_t *q); + +/** + * @brief Check whether there are filled data in queue + * + * @param q: Data queue instance + * @return - true: Have data in queue + * - false: Empty, no buffer filled + */ +bool data_queue_have_data(data_queue_t *q); + +/** + * @brief Query data queue information + * + * @param q: Data queue instance + * @param[out] q_num: Data block number in queue + * @param[out] q_size: Total data size kept in queue + * @return - 0: On success + * - Others: Fail to query + */ +int data_queue_query(data_queue_t *q, int *q_num, int *q_size); + +/** + * @brief Query available data size + * + * @param q: Data queue instance + * @return Available size for write queue + */ +int data_queue_get_available(data_queue_t *q); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/media_lib_sal/include/media_lib_crypt.h b/components/third_party/media_lib_sal/include/media_lib_crypt.h new file mode 100644 index 0000000..b4444e9 --- /dev/null +++ b/components/third_party/media_lib_sal/include/media_lib_crypt.h @@ -0,0 +1,192 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef MEDIA_LIB_CRYPT_H +#define MEDIA_LIB_CRYPT_H + +#include "media_lib_crypt_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief MD5 initialize wrapper + * + * @param[out] ctx: MD5 struct pointer + */ +void media_lib_md5_init(media_lib_md5_handle_t *ctx); + +/** + * @brief MD5 resource free wrapper + * + * @param ctx: MD5 instance + */ +void media_lib_md5_free(media_lib_md5_handle_t ctx); + +/** + * @brief MD5 start wrapper + * + * @param ctx: MD5 instance + * + * @return + * - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: MD5 init fail + */ +int media_lib_md5_start(media_lib_md5_handle_t ctx); + +/** + * @brief MD5 add input data + * + * @param ctx: MD5 instance + * @param input: Input data + * @param len: Input data length + * + * @return + * - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: MD5 update fail + */ +int media_lib_md5_update(media_lib_md5_handle_t ctx, const unsigned char *input, size_t len); + +/** + * @brief Get MD5 output + * + * @param ctx: MD5 instance + * @param output: MD5 output + * + * @return + * - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: md5 finish fail + */ +int media_lib_md5_finish(media_lib_md5_handle_t ctx, unsigned char output[16]); + +/** + * @brief SHA256 initialize wrapper + * + * @param[out] ctx: SHA256 instance pointer + */ +void media_lib_sha256_init(media_lib_sha256_handle_t *ctx); + +/** + * @brief SHA256 resource free wrapper + * + * @param ctx: SHA256 instance + */ +void media_lib_sha256_free(media_lib_sha256_handle_t ctx); + +/** + * @brief SHA256 start wrapper + * + * @param ctx: SHA256 instance + * + * @return + * - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: SHA256 start fail + */ +int media_lib_sha256_start(media_lib_sha256_handle_t ctx); + +/** + * @brief SHA256 add input data + * + * @param ctx: SHA256 instance + * @param input: Input data + * @param len: Input data length + * + * @return + * - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: SHA256 update fail + */ +int media_lib_sha256_update(media_lib_sha256_handle_t ctx, const unsigned char *input, size_t len); + +/** + * @brief Get SHA256 output + * + * @param ctx SHA256 instance + * @param output SHA256 value of input + * + * @return + * - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: SHA256 finish fail + */ +int media_lib_sha256_finish(media_lib_sha256_handle_t ctx, unsigned char output[32]); + +/** + * @brief AES initialize wrapper + * + * @param[out] ctx: AES instance pointer + */ +void media_lib_aes_init(media_lib_aes_handle_t *ctx); + +/** + * @brief AES resource free wrapper + * + * @param ctx: AES instance + */ +void media_lib_aes_free(media_lib_aes_handle_t ctx); + +/** + * @brief AES set key + * + * @param ctx: AES instance + * @param key: AES key + * @param key_bits: Bitlength of key + * + * @return + * - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: AES set key fail + * + */ +int media_lib_aes_set_key(media_lib_aes_handle_t ctx, uint8_t *key, uint8_t key_bits); + +/** + * @brief AES-CBC encryption/decryption + * + * @note + * @param ctx: AES instance + * @param decrypt_mode: Set `true` for decryption, `false` for encryption + * @param iv: AES iv information (iv will be updated after each call it must be writable) + * @param input: Input data to decrypt/encrypt + * @param size: Data size + * @param output: Output data + * + * @return + * - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Encrypt/decrypt fail + * + */ +int media_lib_aes_crypt_cbc(media_lib_aes_handle_t ctx, bool decrypt_mode, uint8_t iv[16], uint8_t *input, size_t size, uint8_t *output); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/third_party/media_lib_sal/include/media_lib_err.h b/components/third_party/media_lib_sal/include/media_lib_err.h new file mode 100644 index 0000000..5d1b924 --- /dev/null +++ b/components/third_party/media_lib_sal/include/media_lib_err.h @@ -0,0 +1,61 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2022 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef MEDIA_LIB_ERRCODE_H +#define MEDIA_LIB_ERRCODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" + +typedef enum { + ESP_MEDIA_ERR_OK = ESP_OK, + ESP_MEDIA_ERR_FAIL = ESP_FAIL, + ESP_MEDIA_ERR_NO_MEM = ESP_ERR_NO_MEM, + ESP_MEDIA_ERR_INVALID_ARG = ESP_ERR_INVALID_ARG, + ESP_MEDIA_ERR_WRONG_STATE = ESP_ERR_INVALID_STATE, + ESP_MEDIA_ERR_INVALID_SIZE = ESP_ERR_INVALID_SIZE, + ESP_MEDIA_ERR_NOT_FOUND = ESP_ERR_NOT_FOUND, + ESP_MEDIA_ERR_NOT_SUPPORT = ESP_ERR_NOT_SUPPORTED, + ESP_MEDIA_ERR_TIMEOUT = ESP_ERR_TIMEOUT, + ESP_MEDIA_ERR_INVALID_RESPONSE = ESP_ERR_INVALID_RESPONSE, + ESP_MEDIA_ERR_INVALID_CRC = ESP_ERR_INVALID_CRC, + ESP_MEDIA_ERR_INVALID_VERSION = ESP_ERR_INVALID_VERSION, + + ESP_MEDIA_ERR_BASE = 0x90000, + ESP_MEDIA_ERR_READ_DATA = (ESP_MEDIA_ERR_BASE + 1), + ESP_MEDIA_ERR_WRITE_DATA = (ESP_MEDIA_ERR_BASE + 2), + ESP_MEDIA_ERR_BAD_DATA = (ESP_MEDIA_ERR_BASE + 3), + ESP_MEDIA_ERR_EXCEED_LIMIT = (ESP_MEDIA_ERR_BASE + 4), + ESP_MEDIA_ERR_CONNECT_FAIL = (ESP_MEDIA_ERR_BASE + 5), + ESP_MEDIA_ERR_RESET = (ESP_MEDIA_ERR_BASE + 6) +} esp_media_err_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/include/media_lib_mem_trace.h b/components/third_party/media_lib_sal/include/media_lib_mem_trace.h new file mode 100644 index 0000000..eeebbcb --- /dev/null +++ b/components/third_party/media_lib_sal/include/media_lib_mem_trace.h @@ -0,0 +1,149 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2023 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#ifndef MEDIA_LIB_MEM_TRACE_H +#define MEDIA_LIB_MEM_TRACE_H + +#include "media_lib_os.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MEDIA_LIB_DEFAULT_TRACE_NUM (1024) +#define MEDIA_LIB_DEFAULT_SAVE_CACHE_SIZE (64 * 1024) +#define MEDIA_LIB_DEFAULT_SAVE_PATH "/sdcard/T.log" + +/** + * @brief Memory trace type + */ +typedef enum { + MEDIA_LIB_MEM_TRACE_NONE, /*!< No memory trace */ + MEDIA_LIB_MEM_TRACE_MODULE_USAGE = (1 << 0), /*!< Trace for module memory usage */ + MEDIA_LIB_MEM_TRACE_LEAK = (1 << 1), /*!< Trace for memory leakage */ + MEDIA_LIB_MEM_TRACE_SAVE_HISTORY = (1 << 2), /*!< Save memory history to file for offline analysis */ + MEDIA_LIB_MEM_TRACE_ALL = (MEDIA_LIB_MEM_TRACE_MODULE_USAGE | + MEDIA_LIB_MEM_TRACE_LEAK | + MEDIA_LIB_MEM_TRACE_SAVE_HISTORY), +} media_lib_mem_trace_type_t; + +/** + * @brief Memory trace configuration + */ +typedef struct { + media_lib_mem_trace_type_t trace_type; /*!< Memory tracing type */ + uint8_t stack_depth; /*!< Max stack depth to trace for malloc */ + int record_num; /*!< Default is MEDIA_LIB_DEFAULT_TRACE_NUM if not provided */ + int save_cache_size; /*!< Default is MEDIA_LIB_DEFAULT_SAVE_CACHE_SIZE if not provided, + if malloc frequently to avoid overflow need enlarge this value */ + const char *save_path; +} media_lib_mem_trace_cfg_t; + +/** + * @brief Memory library function pointers + */ +typedef struct { + __media_lib_os_malloc malloc; /*!< Malloc wrapper */ + __media_lib_os_free free; /*!< Free wrapper */ + __media_lib_os_malloc_align malloc_align; /*!< Malloc align wrapper */ + __media_lib_os_free_align free_align; /*!< Malloc align wrapper */ + __media_lib_os_calloc calloc; /*!< Calloc wrapper */ + __media_lib_os_realloc realloc; /*!< Realloc wrapper */ + __media_lib_os_strdup strdup; /*!< Strdup wrapper */ + __media_lib_os_stack_frame get_stack_frame; /*!< Get stack frame wrapper */ +} media_lib_mem_t; + +/** + * @brief Get memory library function group + * @param[out] mem_lib: Memory library to store + * @return - ESP_MEDIA_ERR_INVALID_ARG: Invalid input argument + * - ESP_MEDIA_ERR_OK: On success + */ +int media_lib_get_mem_lib(media_lib_mem_t *mem_lib); + +/** + * @brief Set memory library function group + * When memory trace started, this API is called internally to replace default memory libary + * After stop default library is restored + * @param[in] mem_lib: New memory libary to set + * @return - ESP_MEDIA_ERR_INVALID_ARG: Invalid input argument + * - ESP_MEDIA_ERR_OK: On success + */ +int media_lib_set_mem_lib(media_lib_mem_t *mem_lib); + +/** + * @brief Start memory trace + * @param cfg: Memory trace configuration + * @return - ESP_MEDIA_ERR_INVALID_ARG: Invalid input argument + * - ESP_MEDIA_ERR_OK: On success + */ +int media_lib_start_mem_trace(media_lib_mem_trace_cfg_t *cfg); + +/** + * @brief Add memory to trace + * @param module: Module to be traced (can set to NULL) + * @param addr: Traced memory address + * @param size: Traced memory size + * @param flag: Customized trace flag + * @return - ESP_MEDIA_ERR_WRONG_STATE: Memory trace not started yet + * - ESP_MEDIA_ERR_OK: On success + */ +int media_lib_add_trace_mem(const char *module, void *addr, int size, uint8_t flag); + +/** + * @brief Remove trace memory + * @param addr: Traced memory address + */ +void media_lib_remove_trace_mem(void *addr); + +/** + * @brief Get memory usage + * @param module: Module to be traced (set NULL to get memory usage of all modules) + * @param[out] used_size: Total memory currently used by module + * @param[out] peak_size: Peak memory size used by module + * @return - ESP_MEDIA_ERR_WRONG_STATE: Memory trace not started yet + * - ESP_MEDIA_ERR_NOT_FOUND: Module not found + * - ESP_MEDIA_ERR_OK: On success + */ +int media_lib_get_mem_usage(const char *module, uint32_t *used_size, uint32_t *peak_size); + +/** + * @brief Print memory leakage + * Notes: When use `idf.py monitor` the leakage address will automatically convert to function line + * If use other tools, please use `addr2line` to convert the address to function line + * @param module: Module to be traced (set NULL to print memory leakage of all modules) + * @return - ESP_MEDIA_ERR_WRONG_STATE: Memory trace not started yet + * - Others: Leakage memory size + */ +int media_lib_print_leakage(const char *module); + +/** + * @brief Stop memory trace + */ +void media_lib_stop_mem_trace(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/include/media_lib_netif.h b/components/third_party/media_lib_sal/include/media_lib_netif.h new file mode 100644 index 0000000..6342ada --- /dev/null +++ b/components/third_party/media_lib_sal/include/media_lib_netif.h @@ -0,0 +1,52 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2023 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef MEDIA_LIB_NETIF_H +#define MEDIA_LIB_NETIF_H + +#include "media_lib_netif_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Wrapper for get ipv4 information + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by wrapper function directly + */ +int media_lib_netif_get_ipv4_info(media_lib_net_type_t type, media_lib_ipv4_info_t *ip_info); + +/** + * @brief Wrapper for convert ipv4 into string + * @return - NULL: wrapper function not registered + * - Others: returned by wrapper function directly + */ +char* media_lib_ipv4_ntoa(const media_lib_ipv4_addr_t *addr); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/third_party/media_lib_sal/include/media_lib_os.h b/components/third_party/media_lib_sal/include/media_lib_os.h new file mode 100644 index 0000000..1a0d521 --- /dev/null +++ b/components/third_party/media_lib_sal/include/media_lib_os.h @@ -0,0 +1,323 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef MEDIA_LIB_OS_H +#define MEDIA_LIB_OS_H + +#include +#include "media_lib_os_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MEDIA_LIB_MAX_LOCK_TIME (0xFFFFFFFF) + +/** + * @brief Configuration for thread schedule + */ +typedef struct { + uint8_t priority; /*!< Thread priority */ + uint8_t core_id; /*!< CPU core id for thread to run */ + uint32_t stack_size; /*!< Thread reserve stack size */ +} media_lib_thread_cfg_t; + +/** + * @brief Callback to get thread schedule parameter + */ +typedef void (*media_lib_thread_sched_param_cb)(const char *thread_name, media_lib_thread_cfg_t *thread_cfg); + +/** + * @brief Helper to set scheduler in callback (using default callback signature) + */ +#define MEDIA_LIB_THREAD_SCHEDULE_SET(_thread, _stack_size, _priority, _core_id) \ +if (strcmp(thread_name, _thread) == 0) { \ + thread_cfg->stack_size = _stack_size; \ + thread_cfg->priority = _priority; \ + thread_cfg->core_id = _core_id; \ +} + +/** + * @brief Wrapper for malloc + */ +void *media_lib_malloc(size_t size); + +/** + * @brief Wrapper for free + */ +void media_lib_free(void *buf); + +/** + * @brief Wrapper for malloc alignment + */ +void *media_lib_malloc_align(size_t size, uint8_t align); + +/** + * @brief Wrapper for free alignment + */ +void media_lib_free_align(void *buf); + +/** + * @brief Wrapper for calloc + */ +void *media_lib_calloc(size_t num, size_t size); + +/** + * @brief Wrapper for realloc + */ +void *media_lib_realloc(void *buf, size_t size); + +/** + * @brief Wrapper for strdup + */ +char *media_lib_strdup(const char *str); + +/** + * @brief asprintf wrapper + */ +int media_lib_asprintf(char **str, const char *fmt, ...); + +/** + * @brief Module malloc + */ +void *media_lib_module_malloc(const char* module, size_t size); + +/** + * @brief Module calloc + */ +void *media_lib_module_calloc(const char* module, size_t num, size_t size); + +/** + * @brief Module realloc + */ +void *media_lib_module_realloc(const char* module, void *buf, size_t size); + +/** + * @brief Module strdup + */ +char *media_lib_module_strdup(const char* module, const char *str); + +/** + * @brief Get stack frame + * @param addr: Address array to store stack frame PC + * @param n: Stack depth to trace + * @return Stack depth be traced + */ +int media_lib_get_stack_frame(void** addr, int n); + +/** + * @brief Wrapper for thread create + * @param[out] handle: Thread handle + * @param name: Thread name + * @param body: Thread body + * @param arg: Thread argument + * @param stack_size: Thread stacksize set + * @param prio: Thread priority + * @param core: Run on which cpu core + * @return - ESP_OK: On success + * - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: thread create fail + */ +int media_lib_thread_create(media_lib_thread_handle_t *handle, const char *name, void(*body)(void *arg), void *arg, + uint32_t stack_size, int prio, int core); + +/** + * @brief Set thread schedule callback + * @param cb Callback to get thread schedule setting + */ +void media_lib_thread_set_schedule_cb(media_lib_thread_sched_param_cb cb); + +/** + * @brief Create thread using schedule callback + * NOTES: When callback is not set or not overwrote, it will use default setting + Default stack size is 4K, priority is 10, run on core 0 + * @param[out] handle: Thread handle + * @param name: Thread name + * @param body: Thread body + * @param arg: Thread argument + * @return - ESP_OK: On success + * - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: thread create fail + */ +int media_lib_thread_create_from_scheduler(media_lib_thread_handle_t *handle, const char *name, void(*body)(void *arg), void *arg); + +/** + * @brief Wrapper for thread destroy + * @param handle: Thread handle + */ +void media_lib_thread_destroy(media_lib_thread_handle_t handle); + +/** + * @brief Wrapper for thread set priority + * @param handle: Thread handle + * @param prio: Thread priority + * @return - true: On success + * - false: Set priority fail + */ +bool media_lib_thread_set_priority(media_lib_thread_handle_t handle, int prio); + +/** + * @brief Wrapper for thread sleep + * @param ms: Sleep time millisecond + */ +void media_lib_thread_sleep(uint32_t ms); + +/** + * @brief Wrapper for sema create + * @param[out] sema: Semaphore handle + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: Sema create fail + */ +int media_lib_sema_create(media_lib_sema_handle_t *sema); + +/** + * @brief Wrapper for sema lock with timeout + * @param sema: Semaphore handle + * @param timeout: Lock timeout in milliseconds + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Sema lock fail + */ +int media_lib_sema_lock(media_lib_sema_handle_t sema, uint32_t timeout); + +/** + * @brief Wrapper for sema unlock + * @param sema: Semaphore handle + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Sema unlock fail + */ +int media_lib_sema_unlock(media_lib_sema_handle_t sema); + +/** + * @brief Wrapper for sema destroy + * @param sema: Semaphore handle + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Mutex destroy fail + */ +int media_lib_sema_destroy(media_lib_sema_handle_t sema); + +/** + * @brief Wrapper for mutex create + * @param[out] mutex: Mutex handle + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Mutex create fail + */ +int media_lib_mutex_create(media_lib_mutex_handle_t *mutex); + +/** + * @brief Wrapper for mutex lock with timeout + * @param mutex: Mutex handle + * @param timeout: Lock timeout in milliseconds + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Mutex lock fail + */ +int media_lib_mutex_lock(media_lib_mutex_handle_t mutex, uint32_t timeout); + +/** + * @brief Wrapper for mutex unlock + * @param mutex: Mutex handle + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Mutex unlock fail + */ +int media_lib_mutex_unlock(media_lib_mutex_handle_t mutex); + +/** + * @brief Wrapper for mutex destroy + * @param mutex: Mutex handle + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Mutex destroy fail + */ +int media_lib_mutex_destroy(media_lib_mutex_handle_t mutex); + +/** + * @brief Wrapper for enter critical section + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Enter critical section fail + */ +int media_lib_enter_critical_section(void); + +/** + * @brief Wrapper for leave critical section + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Leave critical section fail + */ +int media_lib_leave_critical_section(void); + +/** + * @brief Wrapper for event group create + * @param[out] event_group: Event group handle pointer + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Event group create fail + */ +int media_lib_event_group_create(media_lib_event_grp_handle_t *event_group); + +/** + * @brief Wrapper for event group set bits + * @param event_group: Event group handle + * @param bits: Set bits mask + * @return Bits being set currently + */ +uint32_t media_lib_event_group_set_bits(media_lib_event_grp_handle_t event_group, uint32_t bits); + +/** + * @brief Wrapper for event group clear bits + * @param event_group: Event group handle + * @param bits: Clear bits mask + * @return Bits being set currently + */ +uint32_t media_lib_event_group_clr_bits(media_lib_event_grp_handle_t event_group, uint32_t bits); + +/** + * @brief Wrapper for event group wait bits + * @param event_group: Event group handle + * @param bits: Waiting for bits which will be set + * @param timeout: Wait timeout in milliseconds + * @return Bits being set currently + */ +uint32_t media_lib_event_group_wait_bits(media_lib_event_grp_handle_t event_group, uint32_t bits, uint32_t timeout); + +/** + * @brief Wrapper for event group destroy + * @param event_group: Event group handle + * @return - 0: On success + * - ESP_ERR_NOT_SUPPORTED: Wrapper function not registered + * - Others: Event group destroy fail + */ +int media_lib_event_group_destroy(media_lib_event_grp_handle_t event_group); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/include/media_lib_socket.h b/components/third_party/media_lib_sal/include/media_lib_socket.h new file mode 100644 index 0000000..9c8c752 --- /dev/null +++ b/components/third_party/media_lib_sal/include/media_lib_socket.h @@ -0,0 +1,219 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef MEDIA_LIB_SOCKET_H +#define MEDIA_LIB_SOCKET_H + +#include "media_lib_socket_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Wrapper for socket accept + * @return - 0: on success + * - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: socket accept fail + */ +int media_lib_socket_accept(int s, struct sockaddr *addr, socklen_t *addrlen); + +/** + * @brief Wrapper for socket bind + * @return - 0: on success + * - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: socket bind fail + */ +int media_lib_socket_bind(int s, const struct sockaddr *name, socklen_t namelen); + +/** + * @brief Wrapper for socket shutdown + * @return - 0: on success + * - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: socket shutdown fail + */ +int media_lib_socket_shutdown(int s, int how); + +/** + * @brief Wrapper for socket close + * @return - 0: on success + * - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: socket close fail + */ +int media_lib_socket_close(int s); + +/** + * @brief Wrapper for socket connect + * @return - 0: on success + * - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: socket connect fail + */ +int media_lib_socket_connect(int s, const struct sockaddr *name, socklen_t namelen); + +/** + * @brief Wrapper for socket listen + * @return - 0: on success + * - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: listen fail + */ +int media_lib_socket_listen(int s, int backlog); + +/** + * @brief Wrapper for socket recv + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by recvb wrapper function directly + */ +ssize_t media_lib_socket_recv(int s, void *mem, size_t len, int flags); + +/** + * @brief Wrapper for socket read + * @return - ESP_ERR_NOT_SUPPORTED wrapper function not registered + * - Others: returned by read wrapper function directly + */ +ssize_t media_lib_socket_read(int s, void *mem, size_t len); + +/** + * @brief Wrapper for socket readv + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by readv wrapper function directly + */ +ssize_t media_lib_socket_readv(int s, const struct iovec *iov, int iovcnt); + +/** + * @brief Wrapper for socket recvfrom + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by recvfrom wrapper function directly + */ +ssize_t media_lib_socket_recvfrom(int s, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); + +/** + * @brief Wrapper for socket recvmsg + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by recvmsg wrapper function directly + */ +ssize_t media_lib_socket_recvmsg(int s, struct msghdr *message, int flags); + +/** + * @brief Wrapper for socket send + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by send wrapper function directly + */ +ssize_t media_lib_socket_send(int s, const void *dataptr, size_t size, int flags); + +/** + * @brief Wrapper for socket sendmsg + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by sendmsg wrapper function directly + */ +ssize_t media_lib_socket_sendmsg(int s, const struct msghdr *message, int flags); + +/** + * @brief Wrapper for socket sendto + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by sendto wrapper function directly + */ +ssize_t media_lib_socket_sendto(int s, const void *dataptr, size_t size, int flags, const struct sockaddr *to, socklen_t tolen); + +/** + * @brief Wrapper for socket open function + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by socket open wrapper function directly + */ +int media_lib_socket_open(int domain, int type, int protocol); + +/** + * @brief Wrapper for socket write + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by socket write wrapper function directly + */ +ssize_t media_lib_socket_write(int s, const void *dataptr, size_t size); + +/** + * @brief Wrapper for socket writev + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by writev wrapper function directly + */ +ssize_t media_lib_socket_writev(int s, const struct iovec *iov, int iovcnt); + +/** + * @brief Wrapper for socket select + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by select wrapper function directly + */ +int media_lib_socket_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, media_lib_timeval *timeout); + +/** + * @brief Wrapper for socket ioctl + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by ioctl wrapper function directly + */ +int media_lib_socket_ioctl(int s, long cmd, void *argp); + +/** + * @brief Wrapper for socket fcntl + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by fcntl wrapper function directly + */ +int media_lib_socket_fcntl(int s, int cmd, int val); + +/** + * @brief Wrapper for inet_ntop + * @return - not NULL: on success + * - NULL: convert to string fail + */ +const char *media_lib_socket_inet_ntop(int af, const void *src, char *dst, socklen_t size); + +/** + * @brief Wrapper for inet_pton + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by inet_pton wrapper function directly + */ +int media_lib_socket_inet_pton(int af, const char *src, void *dst); + +/** + * @brief Wrapper for setsockopt + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by setsockopt wrapper function directly + */ +int media_lib_socket_setsockopt(int s, int level, int optname, const void *opval, socklen_t optlen); + +/** + * @brief Wrapper for getsockopt + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by getsockopt wrapper function directly + */ +int media_lib_socket_getsockopt(int s, int level, int optname, void *opval, socklen_t *optlen); + +/** + * @brief Wrapper for getsockname + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by getsockname wrapper function directly + */ +int media_lib_socket_getsockname(int s, struct sockaddr *name, socklen_t *namelen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/include/media_lib_tls.h b/components/third_party/media_lib_sal/include/media_lib_tls.h new file mode 100644 index 0000000..1cde0a8 --- /dev/null +++ b/components/third_party/media_lib_sal/include/media_lib_tls.h @@ -0,0 +1,86 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2022 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef MEDIA_LIB_TLS_H +#define MEDIA_LIB_TLS_H + +#include "media_lib_tls_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Wrapper for create tls client instance + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by wrapper function directly + */ +media_lib_tls_handle_t media_lib_tls_new(const char *hostname, int hostlen, int port, const media_lib_tls_cfg_t *cfg); + +/** + * @brief Wrapper for create tls server instance + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by wrapper function directly + */ +media_lib_tls_handle_t media_lib_tls_new_server(int fd, const media_lib_tls_server_cfg_t *cfg); + +/** + * @brief Wrapper for esp_tls write + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by wrapper function directly + */ +int media_lib_tls_write(media_lib_tls_handle_t tls, const void *data, size_t datalen); + +/** + * @brief Wrapper for esp_tls read + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by wrapper function directly + */ +int media_lib_tls_read(media_lib_tls_handle_t tls, void *data, size_t datalen); + +/** + * @brief Wrapper for esp_tls getsockfd + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by wrapper function directly + */ +int media_lib_tls_getsockfd(media_lib_tls_handle_t tls); + +/** + * @brief Wrapper for esp_tls delete + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + */ +int media_lib_tls_delete(media_lib_tls_handle_t tls); + +/** + * @brief Wrapper for esp_tls get bytes avail + * @return - ESP_ERR_NOT_SUPPORTED: wrapper function not registered + * - Others: returned by wrapper function directly + */ +int media_lib_tls_get_bytes_avail(media_lib_tls_handle_t tls); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/include/msg_q.h b/components/third_party/media_lib_sal/include/msg_q.h new file mode 100644 index 0000000..4044892 --- /dev/null +++ b/components/third_party/media_lib_sal/include/msg_q.h @@ -0,0 +1,103 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Message queue handle + */ +typedef struct msg_q_t* msg_q_handle_t; + +/** + * @brief Create message queue + * + * @param[in] msg_number The maximum number of messages in the queue + * @param[out] msg_size Message size + * + * @return + * - NULL No resource to create message queue + * - Others Message queue handle + * + */ +msg_q_handle_t msg_q_create(int msg_number, int msg_size); + +/** + * @brief Send message to queue + * + * @param[in] q Message queue handle + * @param[in] msg Message to be inserted into queue + * @param[in] size Message size, need not larger than msg_size when created + * + * @return + * - 0 On success + * - -1 On failure + * + */ +int msg_q_send(msg_q_handle_t q, void *msg, int size); + +/** + * @brief Receive message from queue + * + * @param[in] q Message queue handle + * @param[out] msg Message to be inserted into queue + * @param[in] size Message size, need not larger than msg_size when created + * @param[in] no_wait If true, return immediately if no message in queue + * + * @return + * - 0 On success + * - -1 On failure + * - 1 If no message in queue and no_wait is true + * + */ +int msg_q_recv(msg_q_handle_t q, void *msg, int size, bool no_wait); + +/** + * @brief Get items number in message queue + * + * @param[in] q Message queue handle + * + * @return + * - 0 No message in queue + * - Others Current queued items number + * + */ +int msg_q_number(msg_q_handle_t q); + +/** + * @brief Destroy message queue + * + * @param[in] q Message queue handle + * + */ +void msg_q_destroy(msg_q_handle_t q); + +#ifdef __cplusplus +} +#endif diff --git a/components/third_party/media_lib_sal/include/port/media_lib_adapter.h b/components/third_party/media_lib_sal/include/port/media_lib_adapter.h new file mode 100644 index 0000000..65c3e1a --- /dev/null +++ b/components/third_party/media_lib_sal/include/port/media_lib_adapter.h @@ -0,0 +1,91 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef MEDIA_LIB_ADAPTER_H +#define MEDIA_LIB_ADAPTER_H + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif +/** + * @brief Add all external library that media used using default function wrapper + * + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_ARG: wrapper functions not OK + */ +esp_err_t media_lib_add_default_adapter(void); + +/** + * @brief Add default OS wrapper fuctions + * + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_ARG: OS wrapper functions not OK + */ +esp_err_t media_lib_add_default_os_adapter(void); + +/** + * @brief Add default crypt related wrapper functions + * + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_ARG: crypt wrapper functions not OK + */ +esp_err_t media_lib_add_default_crypt_adapter(void); + +/** + * @brief Add default socket related wrapper functions + * + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_ARG: socket wrapper functions not OK + */ +esp_err_t media_lib_add_default_socket_adapter(void); + +/** + * @brief Add default tls related wrapper functions + * + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_ARG: tls wrapper functions not OK + */ +esp_err_t media_lib_add_default_tls_adapter(void); + +/** + * @brief Add default network interface related wrapper functions + * + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_ARG: Network interface wrapper functions not OK + */ +esp_err_t media_lib_add_default_netif_adapter(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/include/port/media_lib_crypt_reg.h b/components/third_party/media_lib_sal/include/port/media_lib_crypt_reg.h new file mode 100644 index 0000000..f4bb920 --- /dev/null +++ b/components/third_party/media_lib_sal/include/port/media_lib_crypt_reg.h @@ -0,0 +1,87 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#ifndef MEDIA_LIB_CRYPT_REG_H +#define MEDIA_LIB_CRYPT_REG_H + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* media_lib_md5_handle_t; +typedef void (*__media_lib_md5_init)(media_lib_md5_handle_t *ctx); +typedef void (*__media_lib_md5_free)(media_lib_md5_handle_t ctx); +typedef int (*__media_lib_md5_start)(media_lib_md5_handle_t ctx); +typedef int (*__media_lib_md5_update_ret)(media_lib_md5_handle_t ctx, const unsigned char *input, size_t len); +typedef int (*__media_lib_md5_finish_ret)(media_lib_md5_handle_t ctx, unsigned char output[16]); + +typedef void* media_lib_sha256_handle_t; +typedef void (*__media_lib_sha256_init)(media_lib_sha256_handle_t *ctx); +typedef void (*__media_lib_sha256_free)(media_lib_sha256_handle_t ctx); +typedef int (*__media_lib_sha256_start)(media_lib_sha256_handle_t ctx); +typedef int (*__media_lib_sha256_update_ret)(media_lib_sha256_handle_t ctx, const unsigned char *input, size_t len); +typedef int (*__media_lib_sha256_finish_ret)(media_lib_sha256_handle_t ctx, unsigned char output[32]); + +typedef void* media_lib_aes_handle_t; +typedef void (*__media_lib_aes_init)(media_lib_aes_handle_t *ctx); +typedef void (*__media_lib_aes_free)(media_lib_aes_handle_t ctx); +typedef int (*__media_lib_aes_set_key)(media_lib_aes_handle_t ctx, uint8_t *key, uint8_t key_bits); +typedef int (*__media_lib_aes_crypt_cbc)(media_lib_aes_handle_t ctx, bool decrypt_mode, uint8_t iv[16], + uint8_t *input, size_t size, uint8_t *output); +typedef struct { + __media_lib_md5_init md5_init; /*!< MD5 lib init */ + __media_lib_md5_free md5_free; /*!< MD5 lib deinit */ + __media_lib_md5_start md5_start; /*!< MD5 start add data */ + __media_lib_md5_update_ret md5_update; /*!< MD5 start add extra data */ + __media_lib_md5_finish_ret md5_finish; /*!< get MD5 value */ + __media_lib_sha256_init sha256_init; /*!< SHA256 lib init */ + __media_lib_sha256_free sha256_free; /*!< SHA256 lib deinit */ + __media_lib_sha256_start sha256_start; /*!< SHA256 start add data */ + __media_lib_sha256_update_ret sha256_update; /*!< SHA256 start add extra data */ + __media_lib_sha256_finish_ret sha256_finish; /*!< Get SHA256 value */ + __media_lib_aes_init aes_init; /*!< AES lib init */ + __media_lib_aes_free aes_free; /*!< AES lib free */ + __media_lib_aes_set_key aes_set_key; /*!< AES set key */ + __media_lib_aes_crypt_cbc aes_crypt_cbc; /*!< AES-CBC encrypt/decrypt */ +} media_lib_crypt_t; + +/** + * @brief Register Crypt related wrapper functions for media library + * + * @param crypt_lib Crypt wrapper function lists + * +* @return +* - ESP_OK: on success +* - ESP_ERR_INVALID_ARG: some members of crypt lib not set +*/ +esp_err_t media_lib_crypt_register(media_lib_crypt_t *crypt_lib); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/include/port/media_lib_netif_reg.h b/components/third_party/media_lib_sal/include/port/media_lib_netif_reg.h new file mode 100644 index 0000000..52cdefe --- /dev/null +++ b/components/third_party/media_lib_sal/include/port/media_lib_netif_reg.h @@ -0,0 +1,71 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2023 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#ifndef MEDIA_LIB_NETIF_REG_H +#define MEDIA_LIB_NETIF_REG_H + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t addr; /*!< IPv4 address */ +} media_lib_ipv4_addr_t; + +typedef struct { + media_lib_ipv4_addr_t ip; /**< Interface IPV4 address */ + media_lib_ipv4_addr_t netmask; /**< Interface IPV4 netmask */ + media_lib_ipv4_addr_t gw; /**< Interface IPV4 gateway address */ +} media_lib_ipv4_info_t; + +typedef enum { + MEDIA_LIB_NET_TYPE_STA = 0, /**< Wi-Fi STA (station) interface */ + MEDIA_LIB_NET_TYPE_AP, /**< Wi-Fi soft-AP interface */ + MEDIA_LIB_NET_TYPE_ETH, /**< Ethernet interface */ +} media_lib_net_type_t; + +typedef int (*__media_lib_netif_get_ipv4_info)(media_lib_net_type_t type, media_lib_ipv4_info_t *ip_info); +typedef char* (*__media_lib_ipv4_ntoa)(const media_lib_ipv4_addr_t *addr); +typedef struct { + __media_lib_netif_get_ipv4_info get_ipv4_info; /*!< Get ipv4 information */ + __media_lib_ipv4_ntoa ipv4_ntoa; +} media_lib_netif_t; + +/** + * @brief Register network interface related wrapper functions for media library + * + * @param netif_lib: Network interface wrapper function lists + * +* @return +* - ESP_OK: On success +* - ESP_ERR_INVALID_ARG: Some members of network interface lib not set +*/ +esp_err_t media_lib_netif_register(media_lib_netif_t *netif_lib); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/include/port/media_lib_os_reg.h b/components/third_party/media_lib_sal/include/port/media_lib_os_reg.h new file mode 100644 index 0000000..07c2197 --- /dev/null +++ b/components/third_party/media_lib_sal/include/port/media_lib_os_reg.h @@ -0,0 +1,130 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef MEDIA_LIB_OS_REG_H +#define MEDIA_LIB_OS_REG_H + +#include +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* (*__media_lib_os_malloc)(size_t size); +typedef void (*__media_lib_os_free)(void* buf); +typedef void* (*__media_lib_os_calloc)(size_t num, size_t size); +typedef void* (*__media_lib_os_realloc)(void* buf, size_t size); +typedef char* (*__media_lib_os_strdup)(const char* str); +typedef void* (*__media_lib_os_malloc_align)(size_t size, uint8_t align); +typedef void (*__media_lib_os_free_align)(void* buf); +typedef int (*__media_lib_os_stack_frame)(void** addr, int n); + +typedef void *media_lib_thread_handle_t; +typedef int (*__media_lib_os_thread_create)(media_lib_thread_handle_t *handle, const char *name, void(*body)(void *arg), void *arg, + uint32_t stack_size, int prio, int core); +typedef void (*__media_lib_os_thread_destroy)(media_lib_thread_handle_t handle); +typedef bool (*__media_lib_os_thread_set_priority)(media_lib_thread_handle_t handle, int prio); +typedef void (*__media_lib_os_thread_sleep)(uint32_t ms); + +typedef void *media_lib_sema_handle_t; +typedef int (*__media_lib_os_sema_create)(media_lib_sema_handle_t *sema); +typedef int (*__media_lib_os_sema_lock)(media_lib_sema_handle_t sema, uint32_t timeout); +typedef int (*__media_lib_os_sema_unlock)(media_lib_sema_handle_t sema); +typedef int (*__media_lib_os_sema_destroy)(media_lib_sema_handle_t sema); + +typedef void *media_lib_mutex_handle_t; +typedef int (*__media_lib_os_mutex_create)(media_lib_mutex_handle_t *mutex); +typedef int (*__media_lib_os_mutex_lock)(media_lib_mutex_handle_t mutex, uint32_t timeout); +typedef int (*__media_lib_os_mutex_unlock)(media_lib_mutex_handle_t mutex); +typedef int (*__media_lib_os_mutex_destroy)(media_lib_mutex_handle_t mutex); + +typedef int (*__media_lib_os_enter_critical_section)(); +typedef int (*__media_lib_os_leave_critical_section)(); + +typedef void *media_lib_event_grp_handle_t; +typedef int (*__media_lib_os_event_group_create)(media_lib_event_grp_handle_t *event_group); +typedef uint32_t (*__media_lib_os_event_group_set_bits)(media_lib_event_grp_handle_t event_group, uint32_t bits); +typedef uint32_t (*__media_lib_os_event_group_clr_bits)(media_lib_event_grp_handle_t event_group, uint32_t bits); +typedef uint32_t (*__media_lib_os_event_group_wait_bits)(media_lib_event_grp_handle_t event_group, uint32_t bits, uint32_t timeout); +typedef int (*__media_lib_os_event_group_destroy)(media_lib_event_grp_handle_t event_group); + +/** + * @brief struct for OS related wrapper functions + */ +typedef struct { + __media_lib_os_malloc malloc; /*!< malloc wrapper */ + __media_lib_os_free free; /*!< free wrapper */ + __media_lib_os_calloc calloc; /*!< calloc wrapper */ + __media_lib_os_realloc realloc; /*!< realloc wrapper */ + __media_lib_os_strdup strdup; /*!< strdup wrapper */ + __media_lib_os_malloc_align malloc_align; /*!< malloc align */ + __media_lib_os_free_align free_align; /*!< free align */ + __media_lib_os_stack_frame get_stack_frame; /*!< stack frame */ + + __media_lib_os_thread_create thread_create; /*!< thread create wrapper */ + __media_lib_os_thread_destroy thread_destroy; /*!< thread destroy wrapper */ + __media_lib_os_thread_set_priority thread_set_prio; /*!< set thread priority wrapper */ + __media_lib_os_thread_sleep thread_sleep; /*!< thread sleep wrapper */ + + __media_lib_os_sema_create sema_create; /*!< sema create wrapper */ + __media_lib_os_sema_lock sema_lock; /*!< sema lock wrapper */ + __media_lib_os_sema_unlock sema_unlock; /*!< sema unlock wrapper */ + __media_lib_os_sema_destroy sema_destroy; /*!< sema destroy wrapper */ + + __media_lib_os_mutex_create mutex_create; /*!< mutex create wrapper */ + __media_lib_os_mutex_lock mutex_lock; /*!< mutex lock wrapper */ + __media_lib_os_mutex_unlock mutex_unlock; /*!< mutex unlock wrapper */ + __media_lib_os_mutex_destroy mutex_destroy; /*!< mutex destroy wrapper */ + + __media_lib_os_enter_critical_section enter_critical; /*!< enter critical wrapper */ + __media_lib_os_leave_critical_section leave_critical; /*!< leave critical wrapper */ + + __media_lib_os_event_group_create group_create; /*!< event group create wrapper */ + __media_lib_os_event_group_set_bits group_set_bits; /*!< event group set bits wrapper */ + __media_lib_os_event_group_clr_bits group_clr_bits; /*!< event group clear bits wrapper */ + __media_lib_os_event_group_wait_bits group_wait_bits; /*!< event group wait for bits wrapper */ + __media_lib_os_event_group_destroy group_destroy; /*!< event group destroy wrapper */ +} media_lib_os_t; + +/** + * @brief Register OS related wrapper functions for media library + * + * @param os_lib OS wrapper function lists + * +* @return +* - ESP_OK: on success +* - ESP_ERR_INVALID_ARG: some members of OS lib not set +*/ +esp_err_t media_lib_os_register(media_lib_os_t *os_lib); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/components/third_party/media_lib_sal/include/port/media_lib_socket_reg.h b/components/third_party/media_lib_sal/include/port/media_lib_socket_reg.h new file mode 100644 index 0000000..60beab7 --- /dev/null +++ b/components/third_party/media_lib_sal/include/port/media_lib_socket_reg.h @@ -0,0 +1,115 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#ifndef MEDIA_LIB_SOCKET_REG_H +#define MEDIA_LIB_SOCKET_REG_H + +#include "lwip/sockets.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Use short timeval to compatible with IDF higher version which support 64bits timer + */ +typedef struct { + int tv_sec; + int tv_usec; +} media_lib_timeval; + +typedef int (*__media_lib_socket_accept)(int s, struct sockaddr *addr, socklen_t *addrlen); +typedef int (*__media_lib_socket_bind)(int s, const struct sockaddr *name, socklen_t namelen); +typedef int (*__media_lib_socket_shutdown)(int s, int how); +typedef int (*__media_lib_socket_close)(int s); +typedef int (*__media_lib_socket_connect)(int s, const struct sockaddr *name, socklen_t namelen); +typedef int (*__media_lib_socket_listen)(int s, int backlog); +typedef ssize_t (*__media_lib_socket_recv)(int s, void *mem, size_t len, int flags); +typedef ssize_t (*__media_lib_socket_read)(int s, void *mem, size_t len); +typedef ssize_t (*__media_lib_socket_readv)(int s, const struct iovec *iov, int iovcnt); +typedef ssize_t (*__media_lib_socket_recvfrom)(int s, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); +typedef ssize_t (*__media_lib_socket_recvmsg)(int s, struct msghdr *message, int flags); +typedef ssize_t (*__media_lib_socket_send)(int s, const void *dataptr, size_t size, int flags); +typedef ssize_t (*__media_lib_socket_sendmsg)(int s, const struct msghdr *message, int flags); +typedef ssize_t (*__media_lib_socket_sendto)(int s, const void *dataptr, size_t size, int flags, const struct sockaddr *to, socklen_t tolen); +typedef int (*__media_lib_socket_open)(int domain, int type, int protocol); +typedef ssize_t (*__media_lib_socket_write)(int s, const void *dataptr, size_t size); +typedef ssize_t (*__media_lib_socket_writev)(int s, const struct iovec *iov, int iovcnt); +typedef int (*__media_lib_socket_select)(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, media_lib_timeval *timeout); +typedef int (*__media_lib_socket_ioctl)(int s, long cmd, void *argp); +typedef int (*__media_lib_socket_fcntl)(int s, int cmd, int val); +typedef const char *(*__media_lib_socket_inet_ntop)(int af, const void *src, char *dst, socklen_t size); +typedef int (*__media_lib_socket_inet_pton)(int af, const char *src, void *dst); +typedef int (*__media_lib_socket_setsockopt)(int s, int level, int optname, const void *opval, socklen_t optlen); +typedef int (*__media_lib_socket_getsockopt)(int s, int level, int optname, void *opval, socklen_t *optlen); +typedef int (*__media_lib_socket_getsockname)(int s, struct sockaddr *name, socklen_t *namelen); + +/** + * @brief Socket Wrapper Functions Group + */ +typedef struct { + __media_lib_socket_accept sock_accept; /*!< Socket accept Func Pointer */ + __media_lib_socket_bind sock_bind; /*!< Socket bind Func Pointer */ + __media_lib_socket_shutdown sock_shutdown; /*!< Socket shutdown Func Pointer */ + __media_lib_socket_close sock_close; /*!< Socket close Func Pointer */ + __media_lib_socket_connect sock_connect; /*!< Socket connect Func Pointer */ + __media_lib_socket_listen sock_listen; /*!< Socket listen Func Pointer */ + __media_lib_socket_recv sock_recv; /*!< Socket recv Func Pointer */ + __media_lib_socket_read sock_read; /*!< Socket read Func Pointer */ + __media_lib_socket_readv sock_readv; /*!< Socket readb Func Pointer */ + __media_lib_socket_recvfrom sock_recvfrom; /*!< Socket recvfrom Func Pointer */ + __media_lib_socket_recvmsg sock_recvmsg; /*!< Socket recvmsg Func Pointer */ + __media_lib_socket_send sock_send; /*!< Socket send Func Pointer */ + __media_lib_socket_sendmsg sock_sendmsg; /*!< Socket sendmsg Func Pointer */ + __media_lib_socket_sendto sock_sendto; /*!< Socket sendto Func Pointer */ + __media_lib_socket_open sock_open; /*!< Socket open Func Pointer */ + __media_lib_socket_write sock_write; /*!< Socket write Func Pointer */ + __media_lib_socket_writev sock_writev; /*!< Socket writev Func Pointer */ + __media_lib_socket_select sock_select; /*!< Socket select Func Pointer */ + __media_lib_socket_ioctl sock_ioctl; /*!< Socket ioctl Func Pointer */ + __media_lib_socket_fcntl sock_fcntl; /*!< Socket fcntl Func Pointer */ + __media_lib_socket_inet_ntop sock_inet_ntop; /*!< Socket inet_ntop Func Pointer */ + __media_lib_socket_inet_pton sock_inet_pton; /*!< Socket inet_pton Func Pointer */ + __media_lib_socket_setsockopt sock_setsockopt; /*!< Socket setspckopt Func Pointer */ + __media_lib_socket_getsockopt sock_getsockopt; /*!< Socket getsockopt Func Pointer */ + __media_lib_socket_getsockname sock_getsockname; /*!< Socket getsockname Func Pointer */ +} media_lib_socket_t; + +/** + * @brief Register Socket lib functions for media library + * + * @param socket_lib Socket wrapper function lists + * + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_ARG: some members of socket lib not set + */ +esp_err_t media_lib_socket_register(media_lib_socket_t *socket_lib); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/include/port/media_lib_tls_reg.h b/components/third_party/media_lib_sal/include/port/media_lib_tls_reg.h new file mode 100644 index 0000000..3435719 --- /dev/null +++ b/components/third_party/media_lib_sal/include/port/media_lib_tls_reg.h @@ -0,0 +1,97 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2022 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#ifndef MEDIA_LIB_TLS_REG_H +#define MEDIA_LIB_TLS_REG_H + +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const char *cacert_buf; /*!< Certificate Authority's certificate in a buffer */ + int cacert_bytes; /*!< Size of Certificate Authority certificate */ + const char *clientcert_buf; /*!< Client certificate legacy name */ + int clientcert_bytes; /*!< Size of client certificate legacy name */ + const char *clientkey_buf; /*!< Client key legacy name */ + int clientkey_bytes; /*!< Size of client key legacy name */ + const char *clientkey_password; /*!< Client key decryption password string */ + int clientkey_password_len; /*!< String length of the password */ + bool non_block; /*!< Configure non-blocking mode */ + bool use_secure_element; /*!< Enable this option to use secure element */ + int timeout_ms; /*!< Network timeout in milliseconds */ + bool use_global_ca_store; /*!< Use a global ca_store for all the connections */ + bool skip_common_name; /*!< Skip any validation of server certificate CN field */ + int (*crt_bundle_attach)(void *conf); /*!< Function pointer to esp_crt_bundle_attach */ +} media_lib_tls_cfg_t; + +typedef struct { + const char *cacert_buf; /*!< Client CA certificate in a buffer */ + int cacert_bytes; /*!< Size of client CA certificate */ + const char *servercert_buf; /*!< Server certificate in a buffer */ + int servercert_bytes; /*!< Size of server certificate */ + const char *serverkey_buf; /*!< Server key in a buffer */ + int serverkey_bytes; /*!< Size of server key */ + const char *serverkey_password; /*!< Server key decryption password string */ + int serverkey_password_len; /*!< String length of the password */ +} media_lib_tls_server_cfg_t; + +typedef void *media_lib_tls_handle_t; +typedef media_lib_tls_handle_t (*__media_lib_tls_new)(const char *hostname, int hostlen, int port, + const media_lib_tls_cfg_t *cfg); +typedef media_lib_tls_handle_t (*__media_lib_tls_new_server)(int sock_fd, const media_lib_tls_server_cfg_t *cfg); +typedef int (*__media_lib_tls_write)(media_lib_tls_handle_t tls, const void *data, size_t datalen); +typedef int (*__media_lib_tls_read)(media_lib_tls_handle_t tls, void *data, size_t datalen); +typedef int (*__media_lib_tls_getsockfd)(media_lib_tls_handle_t tls); +typedef int (*__media_lib_tls_delete)(media_lib_tls_handle_t tls); +typedef int (*__media_lib_tls_get_bytes_avail)(media_lib_tls_handle_t tls); + +typedef struct { + __media_lib_tls_new tls_new; /*!< tls lib new */ + __media_lib_tls_new_server tls_new_server; /*!< tls lib new server */ + __media_lib_tls_write tls_write; /*!< tls lib write */ + __media_lib_tls_read tls_read; /*!< tls lib read */ + __media_lib_tls_getsockfd tls_getsockfd; /*!< tls lib getsockfd */ + __media_lib_tls_delete tls_delete; /*!< tls lib delete */ + __media_lib_tls_get_bytes_avail tls_get_bytes_avail; /*!< tls lib get bytes avail */ +} media_lib_tls_t; + +/** + * @brief Register tls related wrapper functions for media library + * + * @param tls_lib tls wrapper function lists + * + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_ARG: some members of tls lib not set + */ +esp_err_t media_lib_tls_register(media_lib_tls_t *tls_lib); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/media_lib_adapter.c b/components/third_party/media_lib_sal/media_lib_adapter.c new file mode 100644 index 0000000..ce096b8 --- /dev/null +++ b/components/third_party/media_lib_sal/media_lib_adapter.c @@ -0,0 +1,83 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#include "esp_log.h" +#include "media_lib_adapter.h" +#include "media_lib_mem_trace.h" + +#define TAG "MEDIA_ADAPTER" + +#ifdef CONFIG_MEDIA_LIB_MEM_AUTO_TRACE +static void add_memory_trace(void) +{ + media_lib_mem_trace_cfg_t trace_cfg = {0}; +#ifdef CONFIG_MEDIA_LIB_MEM_TRACE_MODULE + trace_cfg.trace_type |= MEDIA_LIB_MEM_TRACE_MODULE_USAGE; +#endif +#ifdef CONFIG_MEDIA_LIB_MEM_TRACE_LEAKAGE + trace_cfg.trace_type |= MEDIA_LIB_MEM_TRACE_LEAK; +#endif +#ifdef CONFIG_MEDIA_LIB_MEM_TRACE_SAVE_HISTORY + trace_cfg.trace_type |= MEDIA_LIB_MEM_TRACE_SAVE_HISTORY; + trace_cfg.save_cache_size = CONFIG_MEDIA_LIB_MEM_SAVE_CACHE_SIZE; + trace_cfg.save_path = CONFIG_MEDIA_LIB_MEM_TRACE_SAVE_PATH; +#endif + trace_cfg.stack_depth = CONFIG_MEDIA_LIB_MEM_TRACE_DEPTH; + trace_cfg.record_num = CONFIG_MEDIA_LIB_MEM_TRACE_NUM; + media_lib_start_mem_trace(&trace_cfg); +} +#endif + +esp_err_t media_lib_add_default_adapter(void) +{ + esp_err_t ret; + ret = media_lib_add_default_os_adapter(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Fail to add os lib"); + } +#ifdef CONFIG_MEDIA_PROTOCOL_LIB_ENABLE + ret = media_lib_add_default_crypt_adapter(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Fail to add crypt lib"); + } + ret = media_lib_add_default_socket_adapter(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Fail to add socket lib"); + } + ret = media_lib_add_default_tls_adapter(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Fail to add tls lib"); + } + ret = media_lib_add_default_netif_adapter(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Fail to add netif lib"); + } +#endif +#ifdef CONFIG_MEDIA_LIB_MEM_AUTO_TRACE + add_memory_trace(); +#endif + return ret; +} diff --git a/components/third_party/media_lib_sal/media_lib_common.c b/components/third_party/media_lib_sal/media_lib_common.c new file mode 100644 index 0000000..5cba65b --- /dev/null +++ b/components/third_party/media_lib_sal/media_lib_common.c @@ -0,0 +1,42 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#include "media_lib_common.h" + +bool media_lib_verify(void *lib, int size) +{ + int i; + void **check = (void **)lib; + if (lib == NULL) { + return false; + } + for (i = 0; i < size / sizeof(void *); i++) { + if (check[i] == NULL) { + return false; + } + } + return true; +} \ No newline at end of file diff --git a/components/third_party/media_lib_sal/media_lib_common.h b/components/third_party/media_lib_sal/media_lib_common.h new file mode 100644 index 0000000..a3e8afe --- /dev/null +++ b/components/third_party/media_lib_sal/media_lib_common.h @@ -0,0 +1,58 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#ifndef MEDIA_LIB_COMMON_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "esp_err.h" + +/** + * @brief verify library functions pointer all set or not + * + * @param lib wrapper function struct pointer + * @param size wrapper struct size + * @return + * -true library verify OK + * -false library verify Fail + */ +bool media_lib_verify(void *lib, int size); + +#define MEDIA_LIB_DEFAULT_INSTALLER(src, dst, type) \ + if (media_lib_verify(src, sizeof(type)) == false) { \ + return ESP_ERR_INVALID_ARG; \ + } \ + memcpy(dst, src, sizeof(type)); \ + return ESP_OK; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/third_party/media_lib_sal/media_lib_crypt.c b/components/third_party/media_lib_sal/media_lib_crypt.c new file mode 100644 index 0000000..5e930a7 --- /dev/null +++ b/components/third_party/media_lib_sal/media_lib_crypt.c @@ -0,0 +1,146 @@ + +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#include "media_lib_crypt.h" +#include "media_lib_crypt_reg.h" +#include "media_lib_common.h" + +#ifdef CONFIG_MEDIA_PROTOCOL_LIB_ENABLE +static media_lib_crypt_t media_crypt_lib; + +esp_err_t media_lib_crypt_register(media_lib_crypt_t *crypt_lib) +{ + MEDIA_LIB_DEFAULT_INSTALLER(crypt_lib, &media_crypt_lib, media_lib_crypt_t); +} + +void media_lib_md5_init(media_lib_md5_handle_t *ctx) +{ + if (media_crypt_lib.md5_init) { + media_crypt_lib.md5_init(ctx); + } +} + +void media_lib_md5_free(media_lib_md5_handle_t ctx) +{ + if (media_crypt_lib.md5_free) { + media_crypt_lib.md5_free(ctx); + } +} + +int media_lib_md5_start(media_lib_md5_handle_t ctx) +{ + if (media_crypt_lib.md5_start) { + return media_crypt_lib.md5_start(ctx); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_md5_update(media_lib_md5_handle_t ctx, const unsigned char *input, size_t len) +{ + if (media_crypt_lib.md5_update) { + return media_crypt_lib.md5_update(ctx, input, len); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_md5_finish(media_lib_md5_handle_t ctx, unsigned char output[16]) +{ + if (media_crypt_lib.md5_finish) { + return media_crypt_lib.md5_finish(ctx, output); + } + return ESP_ERR_NOT_SUPPORTED; +} + +void media_lib_sha256_init(media_lib_sha256_handle_t *ctx) +{ + if (media_crypt_lib.sha256_init) { + media_crypt_lib.sha256_init(ctx); + } +} + +void media_lib_sha256_free(media_lib_sha256_handle_t ctx) +{ + if (media_crypt_lib.sha256_free) { + media_crypt_lib.sha256_free(ctx); + } +} + +int media_lib_sha256_start(media_lib_sha256_handle_t ctx) +{ + if (media_crypt_lib.sha256_start) { + return media_crypt_lib.sha256_start(ctx); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_sha256_update(media_lib_sha256_handle_t ctx, const unsigned char *input, size_t len) +{ + if (media_crypt_lib.sha256_update) { + return media_crypt_lib.sha256_update(ctx, input, len); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_sha256_finish(media_lib_sha256_handle_t ctx, unsigned char output[32]) +{ + if (media_crypt_lib.sha256_finish) { + return media_crypt_lib.sha256_finish(ctx, output); + } + return ESP_ERR_NOT_SUPPORTED; +} + +void media_lib_aes_init(media_lib_aes_handle_t *ctx) +{ + if (media_crypt_lib.aes_init) { + media_crypt_lib.aes_init(ctx); + } +} + +void media_lib_aes_free(media_lib_aes_handle_t ctx) +{ + if (media_crypt_lib.aes_free) { + media_crypt_lib.aes_free(ctx); + } +} + +int media_lib_aes_set_key(media_lib_aes_handle_t ctx, uint8_t *key, uint8_t key_bits) +{ + if (media_crypt_lib.aes_set_key) { + media_crypt_lib.aes_set_key(ctx, key, key_bits); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_aes_crypt_cbc(media_lib_aes_handle_t ctx, bool decrypt_mode, uint8_t iv[16], uint8_t *input, size_t size, uint8_t *output) +{ + if (media_crypt_lib.aes_crypt_cbc) { + media_crypt_lib.aes_crypt_cbc(ctx, decrypt_mode, iv, input, size, output); + } + return ESP_ERR_NOT_SUPPORTED; +} + +#endif diff --git a/components/third_party/media_lib_sal/media_lib_netif.c b/components/third_party/media_lib_sal/media_lib_netif.c new file mode 100644 index 0000000..d4df63d --- /dev/null +++ b/components/third_party/media_lib_sal/media_lib_netif.c @@ -0,0 +1,55 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2023 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#include "media_lib_netif.h" +#include "media_lib_netif_reg.h" +#include "media_lib_common.h" + +#ifdef CONFIG_MEDIA_PROTOCOL_LIB_ENABLE + +static media_lib_netif_t media_netif_lib; + +esp_err_t media_lib_netif_register(media_lib_netif_t *netif_lib) +{ + MEDIA_LIB_DEFAULT_INSTALLER(netif_lib, &media_netif_lib, media_lib_netif_t); +} + +int media_lib_netif_get_ipv4_info(media_lib_net_type_t type, media_lib_ipv4_info_t *ip_info) +{ + if (media_netif_lib.get_ipv4_info) { + return media_netif_lib.get_ipv4_info(type, ip_info); + } + return ESP_ERR_NOT_SUPPORTED; +} + +char* media_lib_ipv4_ntoa(const media_lib_ipv4_addr_t *addr) +{ + if (media_netif_lib.ipv4_ntoa) { + return media_netif_lib.ipv4_ntoa(addr); + } + return NULL; +} +#endif diff --git a/components/third_party/media_lib_sal/media_lib_os.c b/components/third_party/media_lib_sal/media_lib_os.c new file mode 100644 index 0000000..66e7936 --- /dev/null +++ b/components/third_party/media_lib_sal/media_lib_os.c @@ -0,0 +1,329 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#include +#include "media_lib_os_reg.h" +#include "media_lib_common.h" +#include "media_lib_os.h" +#include "media_lib_err.h" +#include "media_lib_mem_trace.h" + +#define MEDIA_LIB_DEFAULT_THREAD_CORE 0 +#define MEDIA_LIB_DEFAULT_THREAD_PRIORITY 10 +#define MEDIA_LIB_DEFAULT_THREAD_STACK_SIZE (4*1024) + +static media_lib_os_t media_os_lib; +static media_lib_thread_sched_param_cb thread_sched_cb; + +esp_err_t media_lib_os_register(media_lib_os_t *os_lib) +{ + MEDIA_LIB_DEFAULT_INSTALLER(os_lib, &media_os_lib, media_lib_os_t); +} + +int media_lib_get_mem_lib(media_lib_mem_t* mem_lib) +{ + if (mem_lib == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + mem_lib->malloc = media_os_lib.malloc; + mem_lib->free = media_os_lib.free; + mem_lib->malloc_align = media_os_lib.malloc_align; + mem_lib->free_align = media_os_lib.free_align; + mem_lib->calloc = media_os_lib.calloc; + mem_lib->realloc = media_os_lib.realloc; + mem_lib->strdup = media_os_lib.strdup; + mem_lib->get_stack_frame = media_os_lib.get_stack_frame; + return ESP_MEDIA_ERR_OK; +} + +int media_lib_set_mem_lib(media_lib_mem_t* mem_lib) +{ + if (mem_lib == NULL) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + media_os_lib.malloc = mem_lib->malloc; + media_os_lib.free = mem_lib->free; + media_os_lib.malloc_align = mem_lib->malloc_align; + media_os_lib.free_align = mem_lib->free_align; + media_os_lib.calloc = mem_lib->calloc; + media_os_lib.realloc = mem_lib->realloc; + media_os_lib.strdup = mem_lib->strdup; + return ESP_MEDIA_ERR_OK; +} + +void *media_lib_malloc(size_t size) +{ + if (media_os_lib.malloc) { + return media_os_lib.malloc(size); + } + return NULL; +} + +void media_lib_free(void *buf) +{ + if (media_os_lib.free) { + media_os_lib.free(buf); + } +} + +void *media_lib_malloc_align(size_t size, uint8_t align) +{ + if (media_os_lib.malloc_align) { + return media_os_lib.malloc_align(size, align); + } + return NULL; +} + +void media_lib_free_align(void *buf) +{ + if (media_os_lib.free_align) { + media_os_lib.free_align(buf); + } else if (media_os_lib.free) { + media_os_lib.free(buf); + } +} + +void *media_lib_calloc(size_t num, size_t size) +{ + if (media_os_lib.calloc) { + return media_os_lib.calloc(num, size); + } + return NULL; +} + +void *media_lib_realloc(void *buf, size_t size) +{ + if (media_os_lib.realloc) { + return media_os_lib.realloc(buf, size); + } + return NULL; +} + +char *media_lib_strdup(const char *str) +{ + if (media_os_lib.strdup) { + return media_os_lib.strdup(str); + } + return NULL; +} + +int media_lib_asprintf(char **str, const char *fmt, ...) +{ + int size; + va_list args, back_args; + va_start(args, fmt); + va_copy(back_args, args); + size = vsnprintf(NULL, 0, fmt, back_args); + va_end(back_args); + if (size <= 0) { + *str = NULL; + return -1; + } + *str = (char *)media_lib_malloc(size + 1); + size = vsprintf(*str, fmt, args); + va_end(args); + return size; +} + +int media_lib_get_stack_frame(void** addr, int n) +{ + if (media_os_lib.get_stack_frame) { + return media_os_lib.get_stack_frame(addr, n); + } + return 0; +} + +void media_lib_thread_set_schedule_cb(media_lib_thread_sched_param_cb cb) +{ + thread_sched_cb = cb; +} + +int media_lib_thread_create(media_lib_thread_handle_t *handle, const char *name, + void(*body)(void *arg), void *arg, + uint32_t stack_size, int prio, int core) +{ + if (media_os_lib.thread_create) { + return media_os_lib.thread_create(handle, name, body, arg, stack_size, + prio, core); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_thread_create_from_scheduler(media_lib_thread_handle_t *handle, const char *name, void(*body)(void *arg), void *arg) +{ + media_lib_thread_cfg_t thread_cfg = { + .core_id = MEDIA_LIB_DEFAULT_THREAD_CORE, + .priority = MEDIA_LIB_DEFAULT_THREAD_PRIORITY, + .stack_size = MEDIA_LIB_DEFAULT_THREAD_STACK_SIZE, + }; + if (thread_sched_cb) { + thread_sched_cb(name, &thread_cfg); + } + return media_lib_thread_create(handle, name, body, arg, + thread_cfg.stack_size, thread_cfg.priority, thread_cfg.core_id); +} + +void media_lib_thread_destroy(media_lib_thread_handle_t handle) +{ + if (media_os_lib.thread_destroy) { + media_os_lib.thread_destroy(handle); + } +} + +bool media_lib_thread_set_priority(media_lib_thread_handle_t handle, int prio) +{ + if (media_os_lib.thread_set_prio) { + return media_os_lib.thread_set_prio(handle, prio); + } + return false; +} + +void media_lib_thread_sleep(uint32_t ms) +{ + if (media_os_lib.thread_sleep) { + media_os_lib.thread_sleep(ms); + } +} + +int media_lib_sema_create(media_lib_sema_handle_t *sema) +{ + if (media_os_lib.sema_create) { + return media_os_lib.sema_create(sema); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_sema_lock(media_lib_sema_handle_t sema, uint32_t timeout) +{ + if (media_os_lib.sema_lock) { + return media_os_lib.sema_lock(sema, timeout); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_sema_unlock(media_lib_sema_handle_t sema) +{ + if (media_os_lib.sema_unlock) { + return media_os_lib.sema_unlock(sema); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_sema_destroy(media_lib_sema_handle_t sema) +{ + if (media_os_lib.sema_destroy) { + return media_os_lib.sema_destroy(sema); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_mutex_create(media_lib_mutex_handle_t *mutex) +{ + if (media_os_lib.mutex_create) { + return media_os_lib.mutex_create(mutex); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_mutex_lock(media_lib_mutex_handle_t mutex, uint32_t timeout) +{ + if (media_os_lib.mutex_lock) { + return media_os_lib.mutex_lock(mutex, timeout); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_mutex_unlock(media_lib_mutex_handle_t mutex) +{ + if (media_os_lib.mutex_unlock) { + return media_os_lib.mutex_unlock(mutex); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_mutex_destroy(media_lib_mutex_handle_t mutex) +{ + if (media_os_lib.mutex_destroy) { + return media_os_lib.mutex_destroy(mutex); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_enter_critical_section(void) +{ + if (media_os_lib.leave_critical) { + return media_os_lib.leave_critical(); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_leave_critical_section(void) +{ + if (media_os_lib.leave_critical) { + return media_os_lib.leave_critical(); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_event_group_create(media_lib_event_grp_handle_t *event_group) +{ + if (media_os_lib.group_create) { + return media_os_lib.group_create(event_group); + } + return ESP_ERR_NOT_SUPPORTED; +} + +uint32_t media_lib_event_group_set_bits(media_lib_event_grp_handle_t event_group, uint32_t bits) +{ + if (media_os_lib.group_set_bits) { + return media_os_lib.group_set_bits(event_group, bits); + } + return 0; +} + +uint32_t media_lib_event_group_clr_bits(media_lib_event_grp_handle_t event_group, uint32_t bits) { + if (media_os_lib.group_clr_bits) { + return media_os_lib.group_clr_bits(event_group, bits); + } + return 0; +} + +uint32_t media_lib_event_group_wait_bits(media_lib_event_grp_handle_t event_group, + uint32_t bits, uint32_t timeout) +{ + if (media_os_lib.group_wait_bits) { + return media_os_lib.group_wait_bits(event_group, bits, timeout); + } + return 0; +} + +int media_lib_event_group_destroy(media_lib_event_grp_handle_t event_group) +{ + if (media_os_lib.group_destroy) { + return media_os_lib.group_destroy(event_group); + } + return ESP_ERR_NOT_SUPPORTED; +} diff --git a/components/third_party/media_lib_sal/media_lib_socket.c b/components/third_party/media_lib_sal/media_lib_socket.c new file mode 100644 index 0000000..089f407 --- /dev/null +++ b/components/third_party/media_lib_sal/media_lib_socket.c @@ -0,0 +1,246 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#include "media_lib_socket.h" +#include "media_lib_socket_reg.h" +#include "media_lib_common.h" + +#ifdef CONFIG_MEDIA_PROTOCOL_LIB_ENABLE +static media_lib_socket_t media_socket_lib; + +esp_err_t media_lib_socket_register(media_lib_socket_t *socket_lib) +{ + MEDIA_LIB_DEFAULT_INSTALLER(socket_lib, &media_socket_lib, + media_lib_socket_t); +} + +int media_lib_socket_accept(int s, struct sockaddr *addr, socklen_t *addrlen) +{ + if (media_socket_lib.sock_accept) { + return media_socket_lib.sock_accept(s, addr, addrlen); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_bind(int s, const struct sockaddr *name, socklen_t namelen) +{ + if (media_socket_lib.sock_bind) { + return media_socket_lib.sock_bind(s, name, namelen); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_shutdown(int s, int how) +{ + if (media_socket_lib.sock_shutdown) { + return media_socket_lib.sock_shutdown(s, how); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_close(int s) +{ + if (media_socket_lib.sock_close) { + return media_socket_lib.sock_close(s); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_connect(int s, const struct sockaddr *name, socklen_t namelen) +{ + if (media_socket_lib.sock_connect) { + return media_socket_lib.sock_connect(s, name, namelen); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_listen(int s, int backlog) +{ + if (media_socket_lib.sock_listen) { + return media_socket_lib.sock_listen(s, backlog); + } + return ESP_ERR_NOT_SUPPORTED; +} + +ssize_t media_lib_socket_recv(int s, void *mem, size_t len, int flags) +{ + if (media_socket_lib.sock_recv) { + return media_socket_lib.sock_recv(s, mem, len, flags); + } + return ESP_ERR_NOT_SUPPORTED; +} + +ssize_t media_lib_socket_read(int s, void *mem, size_t len) +{ + if (media_socket_lib.sock_read) { + return media_socket_lib.sock_read(s, mem, len); + } + return ESP_ERR_NOT_SUPPORTED; +} + +ssize_t media_lib_socket_readv(int s, const struct iovec *iov, int iovcnt) +{ + if (media_socket_lib.sock_readv) { + return media_socket_lib.sock_readv(s, iov, iovcnt); + } + return ESP_ERR_NOT_SUPPORTED; +} + +ssize_t media_lib_socket_recvfrom(int s, void *mem, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen) +{ + if (media_socket_lib.sock_recvfrom) { + return media_socket_lib.sock_recvfrom(s, mem, len, flags, from, + fromlen); + } + return ESP_ERR_NOT_SUPPORTED; +} + +ssize_t media_lib_socket_recvmsg(int s, struct msghdr *message, int flags) +{ + if (media_socket_lib.sock_recvmsg) { + return media_socket_lib.sock_recvmsg(s, message, flags); + } + return ESP_ERR_NOT_SUPPORTED; +} + +ssize_t media_lib_socket_send(int s, const void *dataptr, size_t size, int flags) +{ + if (media_socket_lib.sock_send) { + return media_socket_lib.sock_send(s, dataptr, size, flags); + } + return ESP_ERR_NOT_SUPPORTED; +} + +ssize_t media_lib_socket_sendmsg(int s, const struct msghdr *message, int flags) +{ + if (media_socket_lib.sock_sendmsg) { + return media_socket_lib.sock_sendmsg(s, message, flags); + } + return ESP_ERR_NOT_SUPPORTED; +} + +ssize_t media_lib_socket_sendto(int s, const void *dataptr, size_t size, + int flags, const struct sockaddr *to, + socklen_t tolen) +{ + if (media_socket_lib.sock_sendto) { + return media_socket_lib.sock_sendto(s, dataptr, size, flags, to, tolen); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_open(int domain, int type, int protocol) +{ + if (media_socket_lib.sock_open) { + return media_socket_lib.sock_open(domain, type, protocol); + } + return ESP_ERR_NOT_SUPPORTED; +} + +ssize_t media_lib_socket_write(int s, const void *dataptr, size_t size) +{ + if (media_socket_lib.sock_write) { + return media_socket_lib.sock_write(s, dataptr, size); + } + return ESP_ERR_NOT_SUPPORTED; +} + +ssize_t media_lib_socket_writev(int s, const struct iovec *iov, int iovcnt) +{ + if (media_socket_lib.sock_writev) { + return media_socket_lib.sock_writev(s, iov, iovcnt); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_select(int maxfdp1, fd_set *readset, fd_set *writeset, + fd_set *exceptset, media_lib_timeval *timeout) +{ + if (media_socket_lib.sock_select) { + return media_socket_lib.sock_select(maxfdp1, readset, writeset, + exceptset, timeout); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_ioctl(int s, long cmd, void *argp) { + if (media_socket_lib.sock_ioctl) { + return media_socket_lib.sock_ioctl(s, cmd, argp); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_fcntl(int s, int cmd, int val) +{ + if (media_socket_lib.sock_fcntl) { + return media_socket_lib.sock_fcntl(s, cmd, val); + } + return ESP_ERR_NOT_SUPPORTED; +} + +const char *media_lib_socket_inet_ntop(int af, const void *src, char *dst, socklen_t size) +{ + if (media_socket_lib.sock_inet_ntop) { + return media_socket_lib.sock_inet_ntop(af, src, dst, size); + } + return NULL; +} + +int media_lib_socket_inet_pton(int af, const char *src, void *dst) +{ + if (media_socket_lib.sock_inet_pton) { + return media_socket_lib.sock_inet_pton(af, src, dst); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_setsockopt(int s, int level, int optname, + const void *opval, socklen_t optlen) +{ + if (media_socket_lib.sock_setsockopt) { + return media_socket_lib.sock_setsockopt(s, level, optname, opval, optlen); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_getsockopt(int s, int level, int optname, + void *opval, socklen_t *optlen) +{ + if (media_socket_lib.sock_getsockopt) { + return media_socket_lib.sock_getsockopt(s, level, optname, opval, optlen); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_socket_getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + if (media_socket_lib.sock_getsockname) { + return media_socket_lib.sock_getsockname(s, name, namelen); + } + return ESP_ERR_NOT_SUPPORTED; +} +#endif diff --git a/components/third_party/media_lib_sal/media_lib_tls.c b/components/third_party/media_lib_sal/media_lib_tls.c new file mode 100644 index 0000000..1ad4e5b --- /dev/null +++ b/components/third_party/media_lib_sal/media_lib_tls.c @@ -0,0 +1,98 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2022 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#include "media_lib_tls.h" +#include "media_lib_tls_reg.h" +#include "media_lib_common.h" + +#ifdef CONFIG_MEDIA_PROTOCOL_LIB_ENABLE +static media_lib_tls_t media_tls_lib; + +esp_err_t media_lib_tls_register(media_lib_tls_t *tls_lib) +{ + MEDIA_LIB_DEFAULT_INSTALLER(tls_lib, &media_tls_lib, media_lib_tls_t); +} + +media_lib_tls_handle_t media_lib_tls_new(const char *hostname, int hostlen, int port, const media_lib_tls_cfg_t *cfg) +{ + if (media_tls_lib.tls_new) { + return media_tls_lib.tls_new(hostname, hostlen, port, cfg); + } + return NULL; +} + +media_lib_tls_handle_t media_lib_tls_new_server(int fd, const media_lib_tls_server_cfg_t *cfg) +{ + if (media_tls_lib.tls_new_server) { + return media_tls_lib.tls_new_server(fd, cfg); + } + return NULL; +} + +int media_lib_tls_get_bytes_avail(media_lib_tls_handle_t tls) +{ + if (media_tls_lib.tls_get_bytes_avail) { + return media_tls_lib.tls_get_bytes_avail(tls); + } + + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_tls_write(media_lib_tls_handle_t tls, const void *data, size_t datalen) +{ + if (media_tls_lib.tls_write) { + return media_tls_lib.tls_write(tls, data, datalen); + } + + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_tls_read(media_lib_tls_handle_t tls, void *data, size_t datalen) +{ + if (media_tls_lib.tls_read) { + return media_tls_lib.tls_read(tls, data, datalen); + } + + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_tls_getsockfd(media_lib_tls_handle_t tls) +{ + if (media_tls_lib.tls_getsockfd) { + return media_tls_lib.tls_getsockfd(tls); + } + return ESP_ERR_NOT_SUPPORTED; +} + +int media_lib_tls_delete(media_lib_tls_handle_t tls) +{ + if (media_tls_lib.tls_delete) { + return media_tls_lib.tls_delete(tls); + } + return ESP_ERR_NOT_SUPPORTED; +} + +#endif diff --git a/components/third_party/media_lib_sal/mem_trace/README.md b/components/third_party/media_lib_sal/mem_trace/README.md new file mode 100644 index 0000000..fe550cc --- /dev/null +++ b/components/third_party/media_lib_sal/mem_trace/README.md @@ -0,0 +1,236 @@ +# Memory trace for media_lib_sal + +## Functions + +This module is designed to keep tracing of memory status for modules using media_lib_sal. +It supports following tracing functions: +- Support tracing for memory usage of different modules +- Support tracing for memory leakage +- Support save memory allocation and free history to file for offline analysis + Script [mem_trace.pl](mem_trace.pl) can draw allocation tree with detail function line information +- Support tracing for Xtensa, Risc-V, Linux architecture +- Support tracing on runtime, no overhead when tracing not enabled + +## How to use + +1. Call `media_lib_start_mem_trace` manually or enable trace automatically after `media_lib_sal` adapter installed. + ``` + ADF Library Configuration --> Support trace memory automatically + ``` + +2. After `MEDIA_LIB_MEM_TRACE_MODULE` enabled, memory allocated by `media_lib_module_malloc` and other similar API will be traced. Users can use `media_lib_get_mem_usage` to see module memory usage information. + +3. After `MEDIA_LIB_MEM_TRACE_LEAKAGE` enabled, users can call `media_lib_print_leakage` to see module memory leakages. + +4. Stop tracing after call `media_lib_stop_mem_trace`, total memory usage and leakages will be shown, memory history will save to file system if enabled. + + +## Use offline tool to analysis memory allocation details +1. Enable save memory usage history to file option + ``` + ADF Library Configuration --> Support trace memory automatically --> Trace to save memory history + ``` + After test, call `media_lib_stop_mem_trace` to sync history to files. + Users can use either SDCard or internal flash(SPIFFS partition) to store file. + +2. Show memory allocation details tree + ``` + ./mem_trace.pl elf_file_path trace_log_path + + Example: + ./mem_trace.pl build/play_mp3_control.elf /media/tempo/3532-3132/TRACE.LOG + ├── root: max-use:42949 Leak: 471 + │ ├── esp_codec: max-use:30480 + │ │ ├── esp-mp3: max-use:30480 + │ │ │ ├── src: max-use:30480 + │ │ │ │ ├── pvmp3_framedecoder.cpp: max-use:30480 + │ │ │ │ │ ├── 689: max-use:8448 + │ │ │ │ │ ├── 700: max-use:8192 + │ │ │ │ │ ├── 693: max-use:4624 + │ │ │ │ │ ├── 691: max-use:4608 + │ │ │ │ │ ├── 697: max-use:4608 + │ ├── components: max-use:8267 Leak: 471 + │ │ ├── audio_pipeline: max-use:6572 Leak: 48 + │ │ │ ├── audio_element.c: max-use:4168 + │ │ │ │ ├── 468: max-use:3600 + │ │ │ │ ├── 943: max-use:432 + │ │ │ │ ├── 679: max-use:128 + │ │ │ │ ├── 553: max-use:8 + │ │ │ ├── ringbuf.c: max-use:2084 + │ │ │ │ ├── 68: max-use:2048 + │ │ │ │ ├── 67: max-use:36 + │ │ │ ├── audio_event_iface.c: max-use:240 Leak: 48 + │ │ │ │ ├── 67: max-use:192 Leak: 48 + │ │ │ │ ├── 185: max-use:48 + │ │ │ ├── audio_pipeline.c: max-use:80 + │ │ │ │ ├── 251: max-use:32 + │ │ │ │ ├── 284: max-use:32 + │ │ │ │ ├── 472: max-use:16 + │ │ ├── audio_stream: max-use:1336 + │ │ │ ├── i2s_stream.c: max-use:1336 + │ │ │ │ ├── 142: max-use:1200 + │ │ │ │ ├── 383: max-use:136 + │ │ ├── esp_peripherals: max-use:315 Leak: 315 + │ │ │ ├── lib: max-use:140 Leak: 140 + │ │ │ │ ├── adc_button: max-use:140 Leak: 140 + │ │ │ │ │ ├── adc_button.c: max-use:140 Leak: 140 + │ │ │ │ │ │ ├── 105: max-use:72 Leak: 72 + │ │ │ │ │ │ ├── 88: max-use:28 Leak: 28 + │ │ │ │ │ │ ├── 80: max-use:24 Leak: 24 + │ │ │ │ │ │ ├── 415: max-use:16 Leak: 16 + │ │ │ ├── esp_peripherals.c: max-use:111 Leak: 111 + │ │ │ │ ├── 316: max-use:48 Leak: 48 + │ │ │ │ ├── 138: max-use:48 Leak: 48 + │ │ │ │ ├── 320: max-use:15 Leak: 15 + │ │ │ ├── driver: max-use:40 Leak: 40 + │ │ │ │ ├── i2c_bus: max-use:40 Leak: 40 + │ │ │ │ │ ├── i2c_bus.c: max-use:40 Leak: 40 + │ │ │ │ │ │ ├── 70: max-use:40 Leak: 40 + │ │ │ ├── periph_adc_button.c: max-use:24 Leak: 24 + │ │ │ │ ├── 77: max-use:24 Leak: 24 + │ │ ├── audio_hal: max-use:100 Leak: 100 + │ │ │ ├── audio_hal.c: max-use:72 Leak: 72 + │ │ │ │ ├── 44: max-use:72 Leak: 72 + │ │ │ ├── audio_volume.c: max-use:28 Leak: 28 + │ │ │ │ ├── 119: max-use:28 Leak: 28 + │ │ ├── audio_board: max-use:8 Leak: 8 + │ │ │ ├── esp32_s3_korvo2_v3: max-use:8 Leak: 8 + │ │ │ │ ├── board.c: max-use:8 Leak: 8 + │ │ │ │ │ ├── 42: max-use:8 Leak: 8 + │ ├── esp_processing: max-use:4266 + │ │ ├── esp-wrapper: max-use:4246 + │ │ │ ├── mp3_decoder.c: max-use:4246 + │ │ │ │ ├── 85: max-use:2106 + │ │ │ │ ├── 291: max-use:1968 + │ │ │ │ ├── 552: max-use:88 + │ │ │ │ ├── 189: max-use:56 + │ │ │ │ ├── 183: max-use:28 + │ │ ├── esp-share: max-use:20 + │ │ │ ├── mpeg_parser.c: max-use:20 + │ │ │ │ ├── 71: max-use:20 + + ``` + +3. Find heap issue possible cause + We often meet `tlsf_assert` when `tlsf` check heap corruption happen but the assertion point may far away from the causes. Following section give a way to find the possible causes using offline search skills. + Here is the test example: + ```c + int test_used_after_free() { + uint8_t* data = (uint8_t*) media_lib_malloc(1024); // line 563 + uint8_t* data1 = (uint8_t*) media_lib_malloc(1024); // line 564 + media_lib_free(data); + // data is used after free and overwrote + memset(data, 0xFF, 1028); + media_lib_free(data1); + return 0; + } + ``` + Uses can enable gdb-stub to help to locate the issue point. + + + * 3-1 Stop tracing before `tlsf_assert` happen + ```c + /* Add following code in heap/multi_heap.c + * Need leave critical section so that write history can be finished + */ + multi_heap_handle_t assert_heap; + void heap_assert_unlock() { + if (assert_heap) { + MULTI_HEAP_UNLOCK(assert_heap->lock); + } + } + + void multi_heap_internal_lock(multi_heap_handle_t heap) + { + assert_heap = heap; // Add this line + MULTI_HEAP_LOCK(heap->lock); + } + + /* Add following code in heap/tlsf/tlsf_common.h + * Let memory trace stop before assert + */ + void heap_assert_unlock(); + void media_lib_stop_mem_trace(); + #if !defined (tlsf_assert) + #define tlsf_assert(a) { if (a) {} else {heap_assert_unlock(); media_lib_stop_mem_trace(); *(int*)0 = 0;}} + #endif + ``` + + * 3-2 When `tlsf_assert` happen, use gdb to check corruption address + It can see that block header before memory is wrong and most possibly caused by memory overwritten. + ``` + 0x40381899 in tlsf_free (tlsf=0x3c0a0014, ptr=0x3c0b6770) at /home/tempo/c6/esp-adf-internal/esp-idf/components/heap/tlsf/tlsf.c:1119 + 1119 tlsf_assert(!block_is_free(block) && "block already marked as free"); + (gdb) p block + $1 = (block_header_t *) 0x3c0b6768 + (gdb) p *block + $2 = {prev_phys_block = 0xffffffff, size = 4294967295, next_free = 0x3c0a0014, prev_free = 0x3c0a0014} + ``` + +* 3-3 Use [mem_trace.pl](mem_trace.pl) to find possible causes + Use the address before header to check where the address come from then review code to find deeper causes. + ``` + $ mem_trace.pl play_mp3_control.elf trace.log --last_malloc 0x3c0b6768 + Get last malloc buffer: 3c0b636c position:1020/1024 freed:1 + /home/tempo/c6/esp-adf-internal/components/esp-adf-libs/media_lib_sal/media_lib_os.c:76 + /home/tempo/c6/esp-adf-internal/examples/get-started/play_mp3_control/main/malloc_test.c:563 + /home/tempo/c6/esp-adf-internal/examples/get-started/play_mp3_control/main/malloc_test.c:574 + ``` + +## How to save data into flash and read from it + +* Add spiffs partition to partition table + ``` + factory, 0, 0, 0x10000, 1M + storage, data, spiffs, , 256k + ``` + +* Mount spiffs partition in code + ```c + #include "esp_spiffs.h" + static int mount_spiffs(void) + { + esp_vfs_spiffs_conf_t conf = { + .base_path = "/storage", + .partition_label = "storage", + .max_files = 5, + .format_if_mount_failed = true + }; + esp_vfs_spiffs_register(&conf); + return 0; + } + ``` + +* Change memory history save path + ``` + ADF Library Configuration --> Support trace memory automatically --> Memory trace save path + ``` + +* After history saved, dump it from flash + ``` + 1: Get partition data information, memorize spiffs partition offset and size + $ idf.py partition-table + storage,data,spiffs,0x110000,256K, + + 2: Load partition data to PC use esptool + $ esptool.py --chip esp32s3 -p /dev/ttyUSB0 -b 460800 --before=default_reset --after=hard_reset read_flash 0x110000 0x40000 spiffs.bin + + 3: Unpack partition data use mkspiffs: + $ git clone https://github.com/igrr/mkspiffs.git + $ cd mkspiff + $ ./build_all_configs.sh + $ mkspiffs -u my_spiffs spiffs.bin + + Afterwards users can get the history file under folder my_spiffs. + If mkspiffs installed into system path, you can use script to finish step 1-3 automatically. + $ ./mem_trace.pl --load_spiffs + ``` + +* Analysis history file using [mem_trace.pl](mem_trace.pl) + ``` + mem_trace.pl build/play_mp3_control.elf my_spiffs/trace.log + ``` + +Notes: If SPI-RAM is enabled, write thread stack is put to SPI-RAM, you need enable `CONFIG_SPIRAM_FETCH_INSTRUCTIONS` and `CONFIG_SPIRAM_RODATA` to avoid cache assertion. + + \ No newline at end of file diff --git a/components/third_party/media_lib_sal/mem_trace/media_lib_mem_his.c b/components/third_party/media_lib_sal/mem_trace/media_lib_mem_his.c new file mode 100644 index 0000000..8e493fa --- /dev/null +++ b/components/third_party/media_lib_sal/mem_trace/media_lib_mem_his.c @@ -0,0 +1,288 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2023 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#include "media_lib_mem_his.h" +#include "media_lib_mem_trace.h" +#include "media_lib_err.h" +#include "esp_log.h" + +#define TAG "Mem_His" +#define SAVE_SLICE_NUM (4) +#define MAX_WRITE_RETRY (5) + +#undef WRITE_USE_LIBC + +#ifndef WRITE_USE_LIBC +#include +#include +#endif + +#pragma pack(push) +#pragma pack(1) + +typedef struct { + char act; + uint8_t trace_num; + uint8_t flag; + uint8_t reserv[1]; + void *addr; + uint32_t size; +} malloc_detail_t; + +typedef struct { + char act; + uint8_t reserv[1]; + void *addr; +} free_detail_t; + +#pragma pack(pop) + +typedef struct { + media_lib_mutex_handle_t write_mutex; +#ifdef WRITE_USE_LIBC + FILE *fp; +#else + int fd; +#endif + bool running; + bool stopping; + uint8_t *save_cache[SAVE_SLICE_NUM]; + int cache_size; + int cache_filled; + int wp; + int n; +} save_his_t; + +static save_his_t *save_his = NULL; + +static inline bool his_wait_for_enough(int size) +{ + save_his_t *his = save_his; + int retry = MAX_WRITE_RETRY; + while (retry) { + if (his->n < SAVE_SLICE_NUM || his->cache_filled + size <= his->cache_size) { + return true; + } + retry--; + media_lib_thread_sleep(10); + } + ESP_LOGE(TAG, "Malloc too frequently over tracing write speed"); + return false; +} + +static void his_write(void *buffer, int size) +{ + save_his_t *his = save_his; + while (size) { + if (his->n >= SAVE_SLICE_NUM) { + media_lib_thread_sleep(10); + continue; + } + media_lib_mutex_lock(his->write_mutex, MEDIA_LIB_MAX_LOCK_TIME); + if (his->cache_filled < his->cache_size) { + int left = his->cache_size - his->cache_filled; + int fill = left > size ? size : left; + memcpy(his->save_cache[his->wp] + his->cache_filled, buffer, fill); + his->cache_filled += fill; + if (left > fill) { + media_lib_mutex_unlock(his->write_mutex); + break; + } + his->cache_filled = 0; + his->wp = (his->wp + 1) % SAVE_SLICE_NUM; + his->n++; + size -= fill; + buffer += fill; + } + media_lib_mutex_unlock(his->write_mutex); + } +} + +static void save_thread(void *arg) +{ + save_his_t *his = save_his; + his->running = true; + while (1) { + if (his->n) { + media_lib_mutex_lock(his->write_mutex, MEDIA_LIB_MAX_LOCK_TIME); + int head = (his->wp + SAVE_SLICE_NUM - his->n) % SAVE_SLICE_NUM; + media_lib_mutex_unlock(his->write_mutex); +#ifdef WRITE_USE_LIBC + fwrite(his->save_cache[head], his->cache_size, 1, his->fp); +#else + write(his->fd, his->save_cache[head], his->cache_size); +#endif + media_lib_mutex_lock(his->write_mutex, MEDIA_LIB_MAX_LOCK_TIME); + his->n--; + media_lib_mutex_unlock(his->write_mutex); + continue; + } + if (his->stopping) { + break; + } + media_lib_thread_sleep(10); + } + if (his->cache_filled) { +#ifdef WRITE_USE_LIBC + fwrite(his->save_cache[his->wp], his->cache_filled, 1, his->fp); +#else + write(his->fd, his->save_cache[his->wp], his->cache_filled); +#endif + } +#ifdef WRITE_USE_LIBC + if (save_his->fp) { + fclose(save_his->fp); + save_his->fp = NULL; + } +#else + if (save_his->fd > 0) { + close(save_his->fd); + save_his->fd = 0; + } +#endif + ESP_LOGI(TAG, "Sync write left %d done", his->cache_filled); + his->stopping = false; + his->running = false; + media_lib_thread_destroy(NULL); +} + +static void sync_mem_his(void) +{ + save_his_t *his = save_his; + his->stopping = true; + ESP_LOGI(TAG, "waiting for write quit"); + while (his->running) { + media_lib_thread_sleep(10); + } +} + +int media_lib_start_mem_his(media_lib_mem_trace_cfg_t *cfg) +{ + int ret = ESP_MEDIA_ERR_FAIL; + do { + if (save_his) { + return ESP_MEDIA_ERR_OK; + } + save_his = (save_his_t *) calloc(1, sizeof(save_his_t)); + if (save_his == NULL) { + ret = ESP_MEDIA_ERR_NO_MEM; + break; + } + if (media_lib_mutex_create(&save_his->write_mutex) != ESP_MEDIA_ERR_OK) { + break; + } + const char *file = cfg->save_path ? cfg->save_path : MEDIA_LIB_DEFAULT_SAVE_PATH; +#ifdef WRITE_USE_LIBC + save_his->fp = fopen(file, "wb"); + if (save_his->fp == NULL) { + ESP_LOGE(TAG, "Fail to open file %s", file); + break; + } +#else + save_his->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC); + if (save_his->fd <= 0) { + ESP_LOGE(TAG, "Fail to open file %s", file); + break; + } +#endif + int size = cfg->save_cache_size ? cfg->save_cache_size : MEDIA_LIB_DEFAULT_SAVE_CACHE_SIZE; + int each_size = size / SAVE_SLICE_NUM; + size = each_size * SAVE_SLICE_NUM; + save_his->save_cache[0] = (uint8_t *) media_lib_malloc(size); + if (save_his->save_cache[0] == NULL) { + ESP_LOGE(TAG, "Fail to allocate for save history"); + ret = ESP_MEDIA_ERR_NO_MEM; + break; + } + for (int i = 1; i < SAVE_SLICE_NUM; i++) { + save_his->save_cache[i] = save_his->save_cache[i - 1] + each_size; + } + save_his->cache_size = each_size; + media_lib_thread_handle_t h; + if (media_lib_thread_create_from_scheduler(&h, "MemSave", save_thread, NULL) != ESP_MEDIA_ERR_OK) { + ESP_LOGE(TAG, "No thread resource"); + break; + } + return ESP_MEDIA_ERR_OK; + } while (0); + if (save_his) { + media_lib_stop_mem_his(); + } + return ret; +} + +void media_lib_add_mem_malloc_his(void *addr, int size, int stack_num, void *stack, uint8_t flag) +{ + malloc_detail_t detail; + detail.flag = flag; + detail.act = '+'; + detail.addr = addr; + detail.size = size; + detail.trace_num = stack_num; + int frame_size = stack_num * sizeof(void *); + if (his_wait_for_enough(sizeof(detail) + frame_size)) { + his_write(&detail, sizeof(detail)); + his_write(stack, frame_size); + } +} + +void media_lib_add_mem_free_his(void *addr) +{ + free_detail_t detail; + detail.act = '-'; + detail.addr = addr; + if (his_wait_for_enough(sizeof(detail))) { + his_write(&detail, sizeof(detail)); + } +} + +void media_lib_stop_mem_his(void) +{ + if (save_his == NULL) { + return; + } + sync_mem_his(); + if (save_his->write_mutex) { + media_lib_mutex_destroy(save_his->write_mutex); + save_his->write_mutex = NULL; + } +#ifdef WRITE_USE_LIBC + if (save_his->fp) { + fclose(save_his->fp); + save_his->fp = NULL; + } +#else + if (save_his->fd > 0) { + close(save_his->fd); + save_his->fd = 0; + } +#endif + if (save_his->save_cache[0]) { + media_lib_free(save_his->save_cache[0]); + save_his->save_cache[0] = NULL; + } + save_his = NULL; +} diff --git a/components/third_party/media_lib_sal/mem_trace/media_lib_mem_his.h b/components/third_party/media_lib_sal/mem_trace/media_lib_mem_his.h new file mode 100644 index 0000000..1ee19de --- /dev/null +++ b/components/third_party/media_lib_sal/mem_trace/media_lib_mem_his.h @@ -0,0 +1,67 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2023 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ +#ifndef MEDIA_LIB_MEM_HIS_H +#define MEDIA_LIB_MEM_HIS_H + +#include "media_lib_mem_trace.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Start memory history trace + * @param cfg: Memory trace configuration + * @return - ESP_MEDIA_ERR_NO_MEM: Not enough memory + * - ESP_MEDIA_ERR_FAIL: Not resource + * - ESP_MEDIA_ERR_OK: On success + */ +int media_lib_start_mem_his(media_lib_mem_trace_cfg_t *cfg); + +/** + * @brief Add malloc action to history + * @param addr: Memory address + * @param size: Memory size + * @param stack_num: Call stack number to record + * @param stack: Stack frames to record + * @param flag: Flag to record + */ +void media_lib_add_mem_malloc_his(void *addr, int size, int stack_num, void *stack, uint8_t flag); + +/** + * @brief Add free action to history + * @param addr: Memory address + */ +void media_lib_add_mem_free_his(void *addr); + +/** + * @brief Stop memory history trace + */ +void media_lib_stop_mem_his(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/third_party/media_lib_sal/mem_trace/media_lib_mem_trace.c b/components/third_party/media_lib_sal/mem_trace/media_lib_mem_trace.c new file mode 100644 index 0000000..bcb73c9 --- /dev/null +++ b/components/third_party/media_lib_sal/mem_trace/media_lib_mem_trace.c @@ -0,0 +1,587 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2023 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ +#include +#include "media_lib_mem_trace.h" +#include "media_lib_mem_his.h" +#include "media_lib_err.h" +#include "esp_log.h" + +#define TAG "Mem_Trace" +#define MAX_STACK_DEPTH (10) + +typedef struct { + void *addr; + int size; + uint8_t depth; + uint8_t module_id; + void *stack[0]; +} mem_trace_item_t; + +typedef struct _module_mem_info { + char *module; + uint8_t module_id; + uint32_t mem_usage; + uint32_t peak_mem_usage; + struct _module_mem_info *next; +} module_mem_info_t; + +typedef struct { + mem_trace_item_t *trace_item; + module_mem_info_t *module_lists; + uint16_t module_num; + uint16_t trace_item_num; + int item_size; + uint32_t mem_usage; + uint32_t peak_mem_usage; + media_lib_mem_t kept; + bool overflow; + media_lib_mutex_handle_t mutex; +} mem_trace_t; + +static media_lib_mem_trace_cfg_t trace_cfg; +static mem_trace_t *mem_trace; + +static module_mem_info_t *get_module(const char *name) +{ + if (name == NULL) { + return NULL; + } + module_mem_info_t *iter = mem_trace->module_lists; + while (iter) { + if (iter->module == name || strcmp(name, iter->module) == 0) { + return iter; + } + iter = iter->next; + } + return NULL; +} + +static module_mem_info_t *get_module_by_id(uint8_t module_id) +{ + module_mem_info_t *iter = mem_trace->module_lists; + while (iter) { + if (iter->module_id == module_id) { + return iter; + } + iter = iter->next; + } + return NULL; +} + +static module_mem_info_t *alloc_module(const char *name) +{ + if (mem_trace->module_num > 0xFF) { + ESP_LOGE(TAG, "Too many modules, max support 255"); + return NULL; + } + module_mem_info_t *m = (module_mem_info_t *) mem_trace->kept.calloc(1, sizeof(module_mem_info_t)); + if (m == NULL) { + return NULL; + } + m->module = mem_trace->kept.strdup(name); + if (m->module == NULL) { + mem_trace->kept.free(m); + return NULL; + } + m->module_id = (uint8_t) mem_trace->module_num; + mem_trace->module_num++; + // Insert into module lists + if (mem_trace->module_lists == NULL) { + mem_trace->module_lists = m; + } else { + module_mem_info_t *tail = mem_trace->module_lists; + while (tail) { + if (tail->next == NULL) { + tail->next = m; + break; + } + tail = tail->next; + } + } + return m; +} + +static void free_module(void) +{ + module_mem_info_t *iter = mem_trace->module_lists; + while (iter) { + module_mem_info_t *nxt = iter->next; + if (iter->module) { + mem_trace->kept.free(iter->module); + } + mem_trace->kept.free(iter); + iter = nxt; + } +} + +static void print_mem_usage(const char *module) +{ + module_mem_info_t *m = get_module(module); + if (m == NULL) { + ESP_LOGI(TAG, "Total unfree: %d peak usage: %d", (int) mem_trace->mem_usage, (int) mem_trace->peak_mem_usage); + } else { + ESP_LOGI(TAG, "Module %s unfree: %d peak usage: %d", module, (int) mem_trace->mem_usage, + (int) mem_trace->peak_mem_usage); + } +} + +static int print_leak(const char *module) +{ + module_mem_info_t *m = get_module(module); + if (m) { + ESP_LOGI(TAG, "Leakage module:%s", module); + } + int leak_size = 0; + void *trace_arr = mem_trace->trace_item; + int item_size = mem_trace->item_size; + for (int i = 0; i < mem_trace->trace_item_num; i++) { + // TODO correct print + mem_trace_item_t *item = (mem_trace_item_t *) trace_arr; + if (m == NULL || m->module_id == item->module_id) { + printf("%p size: %d\n", item->addr, item->size); + for (int d = 0; d < item->depth; d++) { + printf("%p:", item->stack[d]); + } + printf("\n"); + leak_size += item->size; + } + trace_arr += item_size; + } + printf("total leakage: %d\n", leak_size); + return leak_size; +} + +static uint8_t add_mem_usage(const char *module, int size) +{ + mem_trace->mem_usage += size; + if (mem_trace->peak_mem_usage < mem_trace->mem_usage) { + mem_trace->peak_mem_usage = mem_trace->mem_usage; + } + if ((trace_cfg.trace_type & MEDIA_LIB_MEM_TRACE_MODULE_USAGE) == 0) { + return 0; + } + module_mem_info_t *m = get_module(module); + if (module && m == NULL) { + m = alloc_module(module); + } + if (m) { + m->mem_usage += size; + if (m->peak_mem_usage < m->mem_usage) { + m->peak_mem_usage = m->mem_usage; + } + return m->module_id; + } + return 0; +} + +static void remove_mem_usage(uint8_t module_id, int size) +{ + mem_trace->mem_usage -= size; + if ((trace_cfg.trace_type & MEDIA_LIB_MEM_TRACE_MODULE_USAGE) == 0) { + return; + } + module_mem_info_t *m = get_module_by_id(module_id); + if (m) { + m->mem_usage -= size; + } +} + +static mem_trace_item_t *get_trace_item(void *addr) +{ + if (mem_trace->trace_item_num == 0) { + return NULL; + } + void *trace_arr = mem_trace->trace_item; + for (int i = 0; i < mem_trace->trace_item_num; i++) { + mem_trace_item_t *item = (mem_trace_item_t *) trace_arr; + if (addr == item->addr) { + return item; + } + trace_arr += mem_trace->item_size; + } + return NULL; +} + +static void add_trace_item(uint8_t module_id, void *ptr, int size, void **stack, int depth) +{ + if (mem_trace->trace_item_num >= trace_cfg.record_num) { + if (mem_trace->overflow == false) { + mem_trace->overflow = true; + ESP_LOGE(TAG, "Trace overflow %d > %d", mem_trace->trace_item_num, trace_cfg.record_num); + } + return; + } + if (size == 860) { + *(int*)0 = 0; + } + mem_trace->overflow = false; + void *trace_arr = mem_trace->trace_item; + int item_size = mem_trace->item_size * mem_trace->trace_item_num; + mem_trace_item_t *item = (mem_trace_item_t *) (trace_arr + item_size); + item->module_id = module_id; + item->addr = ptr; + item->size = size; + item->depth = depth; + if (depth) { + memcpy(item->stack, (void *) stack, depth * sizeof(void *)); + } + if (size == 860) { + printf("%p size: %d\n", item->addr, item->size); + for (int d = 0; d < item->depth; d++) { + printf("%p:", item->stack[d]); + } + } + mem_trace->trace_item_num++; +} + +static void remove_trace_item(mem_trace_item_t *item) +{ + void *trace_arr = mem_trace->trace_item; + int item_size = mem_trace->item_size; + if (mem_trace->trace_item_num) { + mem_trace->trace_item_num--; + mem_trace_item_t *tail = (mem_trace_item_t *) (trace_arr + mem_trace->trace_item_num * item_size); + *item = *tail; + memset(tail, 0, sizeof(mem_trace_item_t)); + } +} + +static __attribute__((always_inline)) inline void add_trace(const char *module, void *ptr, int size, uint8_t flag) +{ + media_lib_mutex_lock(mem_trace->mutex, MEDIA_LIB_MAX_LOCK_TIME); + uint8_t module_id = add_mem_usage(module, size); + int n = trace_cfg.stack_depth; + void *stack[MAX_STACK_DEPTH]; + + if (n) { + if (trace_cfg.trace_type & (MEDIA_LIB_MEM_TRACE_SAVE_HISTORY | MEDIA_LIB_MEM_TRACE_LEAK)) { + n = mem_trace->kept.get_stack_frame(stack, n); + } else { + n = 0; + } + } + if (trace_cfg.trace_type & MEDIA_LIB_MEM_TRACE_SAVE_HISTORY) { + media_lib_add_mem_malloc_his(ptr, size, n, stack, flag); + } + if (trace_cfg.record_num) { + add_trace_item(module_id, ptr, size, stack, n); + } + media_lib_mutex_unlock(mem_trace->mutex); +} + +static __attribute__((always_inline)) inline void remove_trace(void *ptr) +{ + media_lib_mutex_lock(mem_trace->mutex, MEDIA_LIB_MAX_LOCK_TIME); + if (trace_cfg.trace_type & MEDIA_LIB_MEM_TRACE_SAVE_HISTORY) { + media_lib_add_mem_free_his(ptr); + } + mem_trace_item_t *item = get_trace_item(ptr); + if (item) { + remove_mem_usage(item->module_id, item->size); + remove_trace_item(item); + } + media_lib_mutex_unlock(mem_trace->mutex); +} + +static void *_malloc(size_t size) +{ + void *ptr = mem_trace->kept.malloc(size); + if (ptr) { + add_trace(NULL, ptr, size, 0); + } + return ptr; +} + +static void *_malloc_align(size_t size, uint8_t align) +{ + void *ptr = mem_trace->kept.malloc_align(size, align); + if (ptr) { + add_trace(NULL, ptr, size, 0); + } + return ptr; +} + +static void _free_align(void *buf) +{ + remove_trace(buf); + mem_trace->kept.free_align(buf); +} + +static void _free(void *buf) +{ + remove_trace(buf); + mem_trace->kept.free(buf); +} + +static void *_calloc(size_t num, size_t size) +{ + void *ptr = mem_trace->kept.calloc(num, size); + if (ptr) { + add_trace(NULL, ptr, num * size, 0); + } + return ptr; +} + +static void *_realloc(void *buf, size_t size) +{ + void *ptr = mem_trace->kept.realloc(buf, size); + media_lib_mutex_lock(mem_trace->mutex, MEDIA_LIB_MAX_LOCK_TIME); + if (buf) { + remove_trace(buf); + } + if (ptr) { + add_trace(NULL, ptr, size, 0); + } + media_lib_mutex_unlock(mem_trace->mutex); + return ptr; +} + +static char *_strdup(const char *str) +{ + char *ptr = mem_trace->kept.strdup(str); + if (ptr) { + int len = strlen(ptr) + 1; + add_trace(NULL, ptr, len, 0); + } + return ptr; +} + +void *media_lib_module_malloc(const char *module, size_t size) +{ + if (trace_cfg.trace_type == MEDIA_LIB_MEM_TRACE_NONE) { + return media_lib_malloc(size); + } + void *ptr = mem_trace->kept.malloc(size); + if (ptr) { + add_trace(module, ptr, size, 0); + } + return ptr; +} + +void *media_lib_module_calloc(const char *module, size_t num, size_t size) +{ + if (trace_cfg.trace_type == MEDIA_LIB_MEM_TRACE_NONE) { + return media_lib_calloc(num, size); + } + void *ptr = mem_trace->kept.calloc(num, size); + if (ptr) { + add_trace(module, ptr, num * size, 0); + } + return ptr; +} + +void *media_lib_module_realloc(const char *module, void *buf, size_t size) +{ + if (trace_cfg.trace_type == MEDIA_LIB_MEM_TRACE_NONE) { + return media_lib_realloc(buf, size); + } + media_lib_mutex_lock(mem_trace->mutex, MEDIA_LIB_MAX_LOCK_TIME); + void *ptr = mem_trace->kept.realloc(buf, size); + if (buf) { + remove_trace(buf); + } + if (ptr) { + add_trace(module, ptr, size, 0); + } + media_lib_mutex_unlock(mem_trace->mutex); + return ptr; +} + +char *media_lib_module_strdup(const char *module, const char *str) +{ + if (trace_cfg.trace_type == MEDIA_LIB_MEM_TRACE_NONE) { + return media_lib_strdup(str); + } + char *ptr = mem_trace->kept.strdup(str); + if (ptr) { + int len = strlen(ptr) + 1; + add_trace(module, ptr, len, 0); + } + return ptr; +} + +int media_lib_start_mem_trace(media_lib_mem_trace_cfg_t *cfg) +{ + if (cfg == NULL || cfg->trace_type == MEDIA_LIB_MEM_TRACE_NONE) { + return ESP_MEDIA_ERR_INVALID_ARG; + } + if (trace_cfg.trace_type != MEDIA_LIB_MEM_TRACE_NONE || mem_trace) { + ESP_LOGI(TAG, "Already started"); + return ESP_MEDIA_ERR_OK; + } + esp_media_err_t ret = ESP_MEDIA_ERR_OK; + media_lib_mem_t mem_lib = {}; + media_lib_get_mem_lib(&mem_lib); + if (mem_lib.calloc == NULL) { + ESP_LOGE(TAG, "Memory library not install yet"); + return ESP_MEDIA_ERR_INVALID_ARG; + } + mem_trace = (mem_trace_t *) mem_lib.calloc(1, sizeof(mem_trace_t)); + if (mem_trace == NULL) { + return ESP_MEDIA_ERR_NO_MEM; + } + do { + memcpy(&mem_trace->kept, &mem_lib, sizeof(mem_lib)); + if (media_lib_mutex_create(&mem_trace->mutex) != ESP_MEDIA_ERR_OK) { + ret = ESP_MEDIA_ERR_NO_MEM; + break; + } + int n = cfg->record_num; + if (cfg->trace_type & (MEDIA_LIB_MEM_TRACE_MODULE_USAGE | MEDIA_LIB_MEM_TRACE_LEAK)) { + if (n == 0) { + n = MEDIA_LIB_DEFAULT_TRACE_NUM; + } + } + if (n) { + mem_trace->item_size = sizeof(mem_trace_item_t) + cfg->stack_depth * sizeof(void *); + mem_trace->trace_item = (mem_trace_item_t *) mem_trace->kept.calloc(1, mem_trace->item_size * n); + if (mem_trace->trace_item == NULL) { + ret = ESP_MEDIA_ERR_NO_MEM; + break; + } + } + if (cfg->trace_type & MEDIA_LIB_MEM_TRACE_SAVE_HISTORY) { + ret = media_lib_start_mem_his(cfg); + if (ret != ESP_MEDIA_ERR_OK) { + ESP_LOGE(TAG, "Fail to preparing for save history"); + break; + } + } + trace_cfg = *cfg; + if (trace_cfg.stack_depth >= MAX_STACK_DEPTH) { + trace_cfg.stack_depth = MAX_STACK_DEPTH; + } + trace_cfg.record_num = n; + mem_lib.malloc = _malloc; + mem_lib.free = _free; + mem_lib.malloc_align = _malloc_align, + mem_lib.free_align = _free_align, + mem_lib.calloc = _calloc; + mem_lib.realloc = _realloc; + mem_lib.strdup = _strdup; + media_lib_set_mem_lib(&mem_lib); + ESP_LOGI(TAG, "Start memory trace OK eeeeee"); + return ESP_MEDIA_ERR_OK; + } while (0); + media_lib_stop_mem_trace(); + return ret; +} + +void media_lib_stop_mem_trace(void) +{ + if (mem_trace == NULL) { + return; + } + media_lib_mem_trace_type_t trace_type = trace_cfg.trace_type; + if (trace_type) { + trace_cfg.trace_type = MEDIA_LIB_MEM_TRACE_NONE; + media_lib_set_mem_lib(&mem_trace->kept); + } + print_mem_usage(NULL); + if (trace_type & MEDIA_LIB_MEM_TRACE_SAVE_HISTORY) { + media_lib_stop_mem_his(); + } + if (trace_type & MEDIA_LIB_MEM_TRACE_LEAK) { + print_leak(NULL); + } + if (mem_trace->mutex) { + media_lib_mutex_lock(mem_trace->mutex, MEDIA_LIB_MAX_LOCK_TIME); + media_lib_mutex_unlock(mem_trace->mutex); + media_lib_mutex_destroy(mem_trace->mutex); + mem_trace->mutex = NULL; + } + if (mem_trace->module_lists) { + free_module(); + mem_trace->module_lists = NULL; + } + if (mem_trace->trace_item) { + mem_trace->kept.free(mem_trace->trace_item); + mem_trace->trace_item = NULL; + } + mem_trace->kept.free(mem_trace); + mem_trace = NULL; +} + +int media_lib_get_mem_usage(const char *module, uint32_t *size, uint32_t *peak_size) +{ + if (trace_cfg.trace_type == MEDIA_LIB_MEM_TRACE_NONE) { + return ESP_MEDIA_ERR_WRONG_STATE; + } + int ret = ESP_MEDIA_ERR_OK; + media_lib_mutex_lock(mem_trace->mutex, MEDIA_LIB_MAX_LOCK_TIME); + if (module) { + module_mem_info_t *m = get_module(module); + if (m) { + if (size) { + *size = m->mem_usage; + } + if (peak_size) { + *peak_size = m->peak_mem_usage; + } + } else { + ret = ESP_MEDIA_ERR_NOT_FOUND; + } + } else { + if (size) { + *size = mem_trace->mem_usage; + } + if (peak_size) { + *peak_size = mem_trace->peak_mem_usage; + } + } + media_lib_mutex_unlock(mem_trace->mutex); + return ret; +} + +int media_lib_print_leakage(const char *module) +{ + if (trace_cfg.trace_type == MEDIA_LIB_MEM_TRACE_NONE) { + return ESP_MEDIA_ERR_WRONG_STATE; + } + media_lib_mutex_lock(mem_trace->mutex, MEDIA_LIB_MAX_LOCK_TIME); + int leak_size = print_leak(module); + media_lib_mutex_unlock(mem_trace->mutex); + return leak_size; +} + +int media_lib_add_trace_mem(const char *module, void *ptr, int size, uint8_t flag) +{ + if (trace_cfg.trace_type == MEDIA_LIB_MEM_TRACE_NONE) { + return ESP_MEDIA_ERR_WRONG_STATE; + } + if (ptr) { + add_trace(module, ptr, size, flag); + } + return ESP_MEDIA_ERR_OK; +} + +void media_lib_remove_trace_mem(void *ptr) +{ + if (trace_cfg.trace_type == MEDIA_LIB_MEM_TRACE_NONE) { + return; + } + remove_trace(ptr); +} diff --git a/components/third_party/media_lib_sal/mem_trace/mem_trace.pl b/components/third_party/media_lib_sal/mem_trace/mem_trace.pl new file mode 100755 index 0000000..cf9b63d --- /dev/null +++ b/components/third_party/media_lib_sal/mem_trace/mem_trace.pl @@ -0,0 +1,460 @@ +#!/usr/bin/perl +use strict; + +my ($log, $elf); +my $search_func; +my $search_addr; +my $filter_flag = 0; +my $last_malloc_addr; +my %last_malloc_info; + +my %symbol; +my %tree; +my %address; + +my $load_spiffs = 0; +my @spiffs_partition; +my $app_name; +my $addr2line_toolchain = "addr2line"; + +my @os_file = ( + "media_lib_*.c", + "msg_q.c", + "audio_mem.c", + "linear_buffer.c", + "heap.c", + "codec_mem.c" +); + +get_args(); +guess_toolchain(); +check_spiffs(); +covert_os_file(); +parse_file(); + +if ($last_malloc_addr) { + print_malloc_info(); +} else { + gen_report(); +} + +sub help { + print << 'USAGE'; +please set your dump log and elf path: EX: +./mem_stat.pl malloc.log ./build/player_cli.elf +--flag malloc_flag (filter out by flag set by malloc) +--func filename:line (get all memory status from certain function address) +--last_malloc address (get last malloc stack which contain this address) +--load_spiffs (load spiffs files from flash) +USAGE + exit(0); +} + +sub short_path { + my @path = split("/", $_[0]); + my $p = 0; + while ($p <= $#path) { + if ($path[$p] eq '.') { + splice(@path, $p, 1); + } + elsif ($path[$p] eq '..') { + splice(@path, $p-1, 2); + $p -= 1; + } else { + $p++; + } + } + return join("/", @path); +} + +sub get_args { + my $i = 0; + while ($i <= $#ARGV) { + $_ = $ARGV[$i]; + if (/\.log/i) { + $log = $_; + } elsif (/\.elf/) { + $elf = $_; + } elsif (/--load_spiffs/) { + $load_spiffs = 1; + } elsif ($i < $#ARGV) { + $i++; + if (/--flag/) { + $filter_flag = $ARGV[$i]; + } elsif (/--func/) { + $search_func = $ARGV[$i]; + } elsif (/--last_malloc/) { + $last_malloc_addr = hex($ARGV[$i]); + } + } + $i++; + } + return if ($load_spiffs); + unless (defined($log) && defined($elf)) { + help(); + } +} + +sub guess_toolchain { + open(my $H, "build/CMakeCache.txt") || return; + while (<$H>) { + if (/CMAKE_ADDR2LINE:FILEPATH=(.*addr2line)/) { + $addr2line_toolchain = $1; + last; + } + } + close $H; +} + +sub covert_os_file { + map{s/\./\\./} @os_file; + map{s/\*/.*?/} @os_file; + #print "os files: @os_file\n"; +} + +sub is_os_file { + my $f = shift; + for (@os_file) { + if ($f =~ /$_/) { + return 1; + } + } + return 0; +} + +sub add_symbol { + my $addr = shift; + return if (exists $symbol{$addr}); + my $func = `$addr2line_toolchain -e $elf $addr`; + if ($func =~/\//) { + $func = short_path($func); + } + if ($search_func && !$search_addr && $func =~/$search_func$/) { + print "Find $addr mapped to func $func\n"; + $search_addr = $addr; + } + if (($func =~/esp-adf[-\w]+\/(.*?):(\d+)/ || + $func =~/esp-idf\/(.*?):(\d+)/ || + $func =~/esp-adf-libs-source\/(.*?):(\d+)/)) { + $symbol{$addr} = ["root/$1", $2]; + } + elsif (/(\w+.*):(\d+)/){ + $symbol{$addr} = ["root/$1", $2]; + } else { + $symbol{$addr} = []; + } +} + +sub parse_all_pc { + my @stack = @_; + my $search_in_stack = 0; + my $sel_symbol; + my $contain = 0; + for my $addr(@stack) { + add_symbol($addr); + #get parent call + if ($search_func) { + #find address firstly, return if not found + next if (!$search_addr); + if ($addr eq $search_addr) { + $search_in_stack = 1; + next; + } + next unless ($search_in_stack); + } + if ($#{$symbol{$addr}} > 0) { + # not os file or last stack + if ($addr eq $stack[$#stack] || + !is_os_file($symbol{$addr}->[0])) { + $sel_symbol = $symbol{$addr}; + last; + } + } + } + unless ($sel_symbol) { + die "not found symbol @stack\n"; + $sel_symbol = ["root/__undefined_funcs__", 0]; + } + return $sel_symbol; +} + +sub update_last_malloc_info { + my ($addr, $size, $stack) = @_; + if ($last_malloc_addr >= $addr && + $last_malloc_addr <= $addr + $size) { + $last_malloc_info{-addr} = $addr; + $last_malloc_info{-size} = $size; + $last_malloc_info{-stack} = $stack; + $last_malloc_info{-freed} = 0; + } +} + +sub remove_last_malloc { + my $addr = shift; + if ($addr == $last_malloc_info{-addr}) { + $last_malloc_info{-freed} = 1; + } +} + +sub parse_file { + my $act; + my $buf; + open (my $H, $log); + binmode $H; + seek $H, 0, 0; + while (read($H, $act, 1)) { + if ($act eq '+') { + read($H, $buf, 11); + my $h = $buf; + my ($num, $r, $resv, $addr, $size) = unpack("CCCII", $buf); + read($H, $buf, $num*4); + my @stack = map {sprintf "%x", $_} unpack("I*", $buf); + next if ($filter_flag && ($r & $filter_flag) == 0); + if ($last_malloc_addr) { + update_last_malloc_info($addr, $size, [@stack]); + next; + } + if ($addr == 0x3c1b1290) { + print "@stack\n"; + } + my $sel_symbol = parse_all_pc(@stack); + my $leaf = get_leaf(@$sel_symbol); + #malloc case + #mem address size + $address{$addr} = [$leaf, $size];# leaf, size + update_leaf($leaf, $size); + #printf("malloc %x size %d num %d\n", $addr, $size, $num); + } + elsif ($act eq '-') { + read($H, $buf, 5); + my ($n, $addr) = unpack("CI", $buf); + #printf("free %x\n", $addr); + if ($last_malloc_addr) { + remove_last_malloc($addr); + next; + } + if (exists $address{$addr}) { + my $s = $address{$addr}->[1]; + my $p = $address{$addr}->[0]; + update_leaf($p, -$s); + delete $address{$addr}; + } + } else { + my $pos = sprintf "%x", tell $H; + print "File is truncated $act pos $pos\n"; + last; + } + } + close $H; + for (keys %address) { + my $s = $address{$_}->[1]; + printf "Leak addr %x size $s\n", $_; + } +} + +sub print_malloc_info { + if (exists $last_malloc_info{-addr}) { + my $pos = $last_malloc_addr - $last_malloc_info{-addr}; + my $size = $last_malloc_info{-size}; + printf "Get last malloc buffer: %x position:$pos/$size freed:$last_malloc_info{-freed}\n", + $last_malloc_info{-addr}; + for my $addr(@{$last_malloc_info{-stack}}) { + my $func = `addr2line -e $elf $addr`; + if ($func =~/\//) { + $func = short_path($func); + } + print "$func"; + } + } +} + +sub update_leaf { + my ($leaf, $size) = @_; + while ($leaf) { + set_info($leaf, $size); + $leaf = $leaf->{-parent}; + } +} + +sub hex_it { + my $s = shift; + my $r; + for (0..length($s)-1) { + $r .= sprintf "%02x ", ord(substr($s, $_, 1)); + } + $r; +} + +sub get_leaf { + my ($path, $line) = @_; + my @p = split("/", $path); + push @p, $line; + + if (!exists $tree{$p[0]}) { + $tree{$p[0]} = { + -name => $p[0], + -alloc_count => 0, + -free_count => 0, + -total_malloc => 0, + -used => 0, + -max_use => 0, + -child => {}, + }; + } + my $parent = $tree{$p[0]}; + for (1..$#p) { + my $c = $parent->{-child}; + if (!exists($c->{$p[$_]})) { + $c->{$p[$_]} = { + -name => $p[$_], + -alloc_count => 0, + -free_count => 0, + -total_malloc => 0, + -used => 0, + -max_use => 0, + -child => {}, + -parent => $parent, + }; + } + $parent = $c->{$p[$_]}; + } + return $parent; +} + +sub set_info { + my ($c, $size) = @_; + if ($size > 0) { + $c->{-alloc_count}++; + $c->{-total_malloc} += $size; + } + else { + $c->{-free_count}++; + } + #print "$c->{-name} + $size\n"; + $c->{-used} += $size; + if ($c->{-used} > $c->{-max_use}) { + $c->{-max_use} = $c->{-used}; + } +} + +sub sort_by_use { + return sort {$b->{-max_use} <=> $a->{-max_use}} @_; +} + +sub print_leaf { + my ($ident, $p) = @_; + my $leak = $p->{-used} ? " Leak: $p->{-used}" : ""; + print "$ident" . "├── ", $p->{-name}, ":\tmax-use:", $p->{-max_use}, "$leak\n"; + $ident .= "│ "; + for my $c(sort_by_use values %{$p->{-child}}) { + print_leaf($ident, $c); + } +} + +sub gen_report { + my $ident = ""; + for my $c (sort_by_use values(%tree)) { + print_leaf($ident, $c); + } +} + +sub size { + $_ = shift; + if (/(\d+)K/) { + return $1*1024; + } + if (/(\d+)M/) { + return $1*1024*1024; + } + if (/0x/ || /[a-f]/i) { + return hex($_); + } + return $_; +} + +sub walker_file { + my ($f, $func) = @_; + open (my $H, $f) || return; + while (<$H>) { + $func->(); + } + close $H; +} + +sub get_app { + walker_file("CMakeLists.txt", + sub { + if (/project\((\w+)\)/) { + $app_name = $1; + last; + } + }); + die "CMakeLists.txt wrong, run in app folder after build" unless($app_name); +} + +sub spiffs_dump_pre_check { + get_app(); + unless (-x 'mkspiffs') { + print "mkspiffs not found, please install use:\n"; + print "git clone https://github.com/igrr/mkspiffs.git;"; + print "cd mkspiffs; ./build_all_configs.sh;\n"; + print "sudo cp mkspiffs /usr/bin; sudo chmod +x /usr/bin/mkspiffs;\n"; + die; + } + unless (-e "build/$app_name.elf") { + die "Please rebuild for $app_name.elf not existed\n"; + } +} + +sub get_partition_table { + walker_file("idf.py partition-table|", + sub { + if (/\w+,data,spiffs,(\w+),(\w+),/) { + @spiffs_partition = ($1, $2); + @spiffs_partition = map {size($_)} @spiffs_partition; + $spiffs_partition[1] = $spiffs_partition[1] / 4096 * 4096; + @spiffs_partition = map {sprintf("0x%x", $_)} @spiffs_partition; + } + }); + die "Partition table not found" unless(@spiffs_partition); + print "Got partition table @spiffs_partition\n"; +} + +sub dump_spiffs { + if (-e "my_spiffs") { + `rm spiffs.bin; rm -rf my_spiffs`; + } + print `esptool.py -b 460800 --before=default_reset read_flash @spiffs_partition spiffs.bin`; + print `mkspiffs -u my_spiffs spiffs.bin`; + print "\n-------------------------------------------------------------------------\n"; +} + +sub get_log_file { + my $file_name; + walker_file("sdkconfig", + sub { + if (/CONFIG_MEDIA_LIB_MEM_TRACE_SAVE_PATH="\/\w+\/(.*?)"/) { + $file_name = $1; + last; + } + }); + die "CONFIG_MEDIA_LIB_MEM_TRACE_SAVE_PATH not set" unless ($file_name); + return $file_name; +} + +sub check_spiffs { + return unless($load_spiffs); + spiffs_dump_pre_check(); + get_partition_table(); + dump_spiffs(); + my $file_name = get_log_file(); + if (-e "my_spiffs/$file_name") { + $log = "my_spiffs/$file_name"; + } else { + $file_name = uc $file_name; + $log = "my_spiffs/$file_name"; + die "history file not exists" unless(-e $log); + } + $log = "my_spiffs/$file_name"; + $elf = "build/$app_name.elf"; +} diff --git a/components/third_party/media_lib_sal/port/data_queue.c b/components/third_party/media_lib_sal/port/data_queue.c new file mode 100644 index 0000000..0729013 --- /dev/null +++ b/components/third_party/media_lib_sal/port/data_queue.c @@ -0,0 +1,425 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "media_lib_os.h" +#include "data_queue.h" + +#define DATA_Q_ALLOC_HEAD_SIZE (4) +#define DATA_Q_DATA_ARRIVE_BITS (1) +#define DATA_Q_DATA_CONSUME_BITS (2) +#define DATA_Q_USER_FREE_BITS (4) + +#define _SET_BITS(group, bit) media_lib_event_group_set_bits((media_lib_event_grp_handle_t) group, bit) +// Need manual clear bits +#define _WAIT_BITS(group, bit) \ + media_lib_event_group_wait_bits((media_lib_event_grp_handle_t) group, bit, MEDIA_LIB_MAX_LOCK_TIME); \ + media_lib_event_group_clr_bits(group, bit) + +#define _MUTEX_LOCK(mutex) media_lib_mutex_lock((media_lib_mutex_handle_t) mutex, MEDIA_LIB_MAX_LOCK_TIME) +#define _MUTEX_UNLOCK(mutex) media_lib_mutex_unlock((media_lib_mutex_handle_t) mutex) + +static int data_queue_release_user(data_queue_t *q) +{ + _SET_BITS(q->event, DATA_Q_USER_FREE_BITS); + return 0; +} + +static int data_queue_notify_data(data_queue_t *q) +{ + _SET_BITS(q->event, DATA_Q_DATA_ARRIVE_BITS); + return 0; +} + +static int data_queue_wait_data(data_queue_t *q) +{ + q->user++; + _MUTEX_UNLOCK(q->lock); + _WAIT_BITS(q->event, DATA_Q_DATA_ARRIVE_BITS); + _MUTEX_LOCK(q->lock); + int ret = (q->quit) ? -1 : 0; + q->user--; + data_queue_release_user(q); + return ret; +} + +static int data_queue_data_consumed(data_queue_t *q) +{ + _SET_BITS(q->event, DATA_Q_DATA_CONSUME_BITS); + return 0; +} + +static int data_queue_wait_consume(data_queue_t *q) +{ + q->user++; + _MUTEX_UNLOCK(q->lock); + _WAIT_BITS(q->event, DATA_Q_DATA_CONSUME_BITS); + _MUTEX_LOCK(q->lock); + int ret = (q->quit) ? -1 : 0; + q->user--; + data_queue_release_user(q); + return ret; +} + +static int data_queue_wait_user(data_queue_t *q) +{ + _MUTEX_UNLOCK(q->lock); + _WAIT_BITS(q->event, DATA_Q_USER_FREE_BITS); + _MUTEX_LOCK(q->lock); + return 0; +} + +static bool _data_queue_have_data(data_queue_t *q) +{ + if (q->wp == q->rp && q->fill_end == 0) { + return false; + } + return true; +} + +static bool _data_queue_have_data_from_last(data_queue_t *q) +{ + return q->filled ? true : false; +} + +data_queue_t *data_queue_init(int size) +{ + data_queue_t *q = media_lib_calloc(1, sizeof(data_queue_t)); + if (q == NULL) { + return NULL; + } + q->buffer = media_lib_malloc(size); + media_lib_mutex_create(&q->lock); + media_lib_mutex_create(&q->write_lock); + media_lib_event_group_create(&q->event); + if (q->buffer == NULL || q->lock == NULL || q->write_lock == NULL || q->event == NULL) { + data_queue_deinit(q); + return NULL; + } + q->size = size; + return q; +} + +void data_queue_wakeup(data_queue_t *q) +{ + if (q && q->lock) { + _MUTEX_LOCK(q->lock); + q->quit = 1; + // send quit message to let user quit + data_queue_notify_data(q); + data_queue_data_consumed(q); + while (q->user) { + data_queue_wait_user(q); + } + _MUTEX_UNLOCK(q->lock); + } +} + +int data_queue_consume_all(data_queue_t *q) +{ + if (q && q->lock) { + _MUTEX_LOCK(q->lock); + while (_data_queue_have_data(q)) { + if (q->quit) { + break; + } + uint8_t *buffer = (uint8_t *) q->buffer + q->rp; + int size = *((int *) buffer); + if (size < 0 || size >= q->size) { + *(int*)0 = 0; + } + q->rp += size; + q->filled -= size; + if (q->fill_end && q->rp >= q->fill_end) { + q->fill_end = 0; + q->rp = 0; + } + data_queue_data_consumed(q); + } + _MUTEX_UNLOCK(q->lock); + } + return 0; +} + +void data_queue_deinit(data_queue_t *q) +{ + if (q == NULL) { + return; + } + if (q->lock) { + media_lib_mutex_destroy((media_lib_mutex_handle_t) q->lock); + } + if (q->write_lock) { + media_lib_mutex_destroy((media_lib_mutex_handle_t) q->write_lock); + } + if (q->event) { + media_lib_event_group_destroy((media_lib_mutex_handle_t) q->event); + } + if (q->buffer) { + media_lib_free(q->buffer); + } + media_lib_free(q); +} + +/* case 1: [0...rp...wp...size] + * case 2: [0...wp...rp...fillend...size] + * special case: wp == rp fill_end set buffer full else empty + */ +static int get_available_size(data_queue_t *q) +{ + if (q->wp > q->rp) { + return q->size - q->wp; + } + if (q->wp == q->rp) { + if (q->fill_end) { + return 0; + } + return q->size - q->wp; + } + return q->rp - q->wp; +} + +int data_queue_get_available(data_queue_t *q) +{ + if (q == NULL) { + return 0; + } + _MUTEX_LOCK(q->lock); + int avail; + // Handle corner case [0 rp==wp fifo_end] + // Left fifo is not enough but actually fifo is empty + if (q->wp == q->rp && q->fill_end == 0) { + avail = q->size; + } else { + avail = get_available_size(q); + } + if (avail >= DATA_Q_ALLOC_HEAD_SIZE) { + avail -= DATA_Q_ALLOC_HEAD_SIZE; + } else { + avail = 0; + } + _MUTEX_UNLOCK(q->lock); + return avail; +} + +void *data_queue_get_buffer(data_queue_t *q, int size) +{ + int avail = 0; + size += DATA_Q_ALLOC_HEAD_SIZE; + if (q == NULL || size > q->size) { + return NULL; + } + _MUTEX_LOCK(q->write_lock); + _MUTEX_LOCK(q->lock); + while (!q->quit) { + avail = get_available_size(q); + // size not enough + if (avail < size && q->fill_end == 0) { + if (q->wp == q->rp) { + q->wp = q->rp = 0; + } + q->fill_end = q->wp; + q->wp = 0; + avail = get_available_size(q); + } + if (avail >= size) { + uint8_t *buffer = (uint8_t *) q->buffer + q->wp; + q->user++; + _MUTEX_UNLOCK(q->lock); + return buffer + DATA_Q_ALLOC_HEAD_SIZE; + } + int ret = data_queue_wait_consume(q); + if (ret != 0) { + break; + } + } + _MUTEX_UNLOCK(q->lock); + _MUTEX_UNLOCK(q->write_lock); + return NULL; +} + +void *data_queue_get_write_data(data_queue_t *q) +{ + if (q == NULL) { + return NULL; + } + _MUTEX_LOCK(q->lock); + uint8_t *buffer = (uint8_t *) q->buffer + q->wp; + _MUTEX_UNLOCK(q->lock); + return buffer + DATA_Q_ALLOC_HEAD_SIZE; +} + +int data_queue_send_buffer(data_queue_t *q, int size) +{ + int ret = -1; + if (q == NULL) { + return -1; + } + _MUTEX_LOCK(q->lock); + if (size == 0) { + q->user--; + data_queue_release_user(q); + _MUTEX_UNLOCK(q->lock); + _MUTEX_UNLOCK(q->write_lock); + return 0; + } + size += DATA_Q_ALLOC_HEAD_SIZE; + if (get_available_size(q) >= size) { + uint8_t *buffer = (uint8_t *) q->buffer + q->wp; + *((int *) buffer) = size; + if (size < 0 || size > q->size) { + *(int*)0 = 0; + } + q->wp += size; + q->filled += size; + q->user--; + data_queue_notify_data(q); + data_queue_release_user(q); + _MUTEX_UNLOCK(q->lock); + _MUTEX_UNLOCK(q->write_lock); + ret = 0; + } else { + q->user--; + data_queue_release_user(q); + printf("Release for avail %d\n", get_available_size(q)); + _MUTEX_UNLOCK(q->lock); + _MUTEX_UNLOCK(q->write_lock); + } + return ret; +} + +bool data_queue_have_data(data_queue_t *q) +{ + int has_data = false; + if (q == NULL) { + return has_data; + } + _MUTEX_LOCK(q->lock); + if (!q->quit) { + has_data = _data_queue_have_data(q); + } + _MUTEX_UNLOCK(q->lock); + return has_data; +} + +int data_queue_read_lock(data_queue_t *q, void **buffer, int *size) +{ + int ret = -1; + if (q == NULL) { + return -1; + } + _MUTEX_LOCK(q->lock); + while (!q->quit) { + if (_data_queue_have_data_from_last(q) == false) { + if (data_queue_wait_data(q) != 0) { + ret = -1; + break; + } + continue; + } + int cur_rp; + if (q->filled <= q->wp) { + cur_rp = q->wp - q->filled; + } else { + cur_rp = q->wp + q->fill_end - q->filled; + } + uint8_t *data_buffer = (uint8_t *) q->buffer + cur_rp; + int data_size = *((int *) data_buffer); + if (data_size < 0 || data_size >q->size) { + *(int*)0 = 0; + } + q->filled -= data_size; + *buffer = data_buffer + DATA_Q_ALLOC_HEAD_SIZE; + *size = data_size - DATA_Q_ALLOC_HEAD_SIZE; + q->user++; + ret = 0; + break; + } + _MUTEX_UNLOCK(q->lock); + return ret; +} + +int data_queue_peek_unlock(data_queue_t *q) +{ + int ret = -1; + if (q) { + _MUTEX_LOCK(q->lock); + q->user--; + _MUTEX_UNLOCK(q->lock); + return 0; + } + return ret; +} + +int data_queue_read_unlock(data_queue_t *q) +{ + int ret = -1; + if (q) { + _MUTEX_LOCK(q->lock); + if (_data_queue_have_data(q)) { + uint8_t *buffer = (uint8_t *) q->buffer + q->rp; + int size = *((int *) buffer); + if (size < 0 || size >q->size) { + *(int*)0 = 0; + } + q->rp += size; + if (q->fill_end && q->rp >= q->fill_end) { + q->fill_end = 0; + q->rp = 0; + } + q->user--; + data_queue_data_consumed(q); + data_queue_release_user(q); + } + _MUTEX_UNLOCK(q->lock); + return 0; + } + return ret; +} + +int data_queue_query(data_queue_t *q, int *q_num, int *q_size) +{ + if (q) { + _MUTEX_LOCK(q->lock); + *q_num = *q_size = 0; + if (_data_queue_have_data(q)) { + int rp = q->rp; + int ring = q->fill_end; + while (rp != q->wp || ring) { + int size = *(int *) (q->buffer + rp); + if (size < 0 || size > q->size) { + *(int*)0 = 0; + } + rp += size; + if (ring && rp == ring) { + ring = 0; + rp = 0; + } + (*q_num)++; + (*q_size) += size - DATA_Q_ALLOC_HEAD_SIZE; + } + } + _MUTEX_UNLOCK(q->lock); + } + return 0; +} diff --git a/components/third_party/media_lib_sal/port/media_lib_crypt_default.c b/components/third_party/media_lib_sal/port/media_lib_crypt_default.c new file mode 100644 index 0000000..e5b5e91 --- /dev/null +++ b/components/third_party/media_lib_sal/port/media_lib_crypt_default.c @@ -0,0 +1,186 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "esp_log.h" +#include "mbedtls/md5.h" +#include "mbedtls/sha256.h" +#include "media_lib_crypt_reg.h" +#include "media_lib_adapter.h" +#include "media_lib_os.h" +#include "mbedtls/md5.h" +#include "esp_idf_version.h" +#include "aes/esp_aes.h" +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) +#include "mbedtls/compat-2.x.h" +#endif +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)) +#include "aes/esp_aes.h" +#elif (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0)) +#if CONFIG_IDF_TARGET_ESP32 +#include "esp32/aes.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/aes.h" +#endif +#else +#include "hwcrypto/aes.h" +#endif + +#ifdef CONFIG_MEDIA_PROTOCOL_LIB_ENABLE + +#define RETURN_ON_NULL_HANDLE(h) \ + if (h == NULL) { \ + return ESP_ERR_INVALID_ARG; \ + } + +static void _md5_init(media_lib_md5_handle_t *ctx) +{ + mbedtls_md5_context *md5 = + (mbedtls_md5_context *)media_lib_malloc(sizeof(mbedtls_md5_context)); + if (md5) { + mbedtls_md5_init(md5); + *ctx = md5; + } +} + +static void _md5_free(media_lib_md5_handle_t ctx) +{ + if (ctx) { + mbedtls_md5_free((mbedtls_md5_context *)ctx); + media_lib_free(ctx); + } +} + +static int _md5_start(media_lib_md5_handle_t ctx) +{ + RETURN_ON_NULL_HANDLE(ctx); + return mbedtls_md5_starts_ret((mbedtls_md5_context *)ctx); +} + +static int _md5_update(media_lib_md5_handle_t ctx, const unsigned char *input, size_t len) +{ + RETURN_ON_NULL_HANDLE(ctx); + return mbedtls_md5_update_ret((mbedtls_md5_context *)ctx, input, len); +} + +static int _md5_finish(media_lib_md5_handle_t ctx, unsigned char output[16]) +{ + RETURN_ON_NULL_HANDLE(ctx); + return mbedtls_md5_finish_ret((mbedtls_md5_context *)ctx, output); +} + +static void _sha256_init(media_lib_sha256_handle_t *ctx) +{ + mbedtls_sha256_context *sha256 = + (mbedtls_sha256_context*) media_lib_malloc(sizeof(mbedtls_sha256_context)); + if (sha256) { + mbedtls_sha256_init(sha256); + *ctx = sha256; + } +} + +static void _sha256_free(media_lib_sha256_handle_t ctx) +{ + if (ctx) { + mbedtls_sha256_free((mbedtls_sha256_context *)ctx); + media_lib_free(ctx); + } +} + +static int _sha256_start(media_lib_sha256_handle_t ctx) +{ + RETURN_ON_NULL_HANDLE(ctx); + return mbedtls_sha256_starts_ret((mbedtls_sha256_context *)ctx, false); +} + +static int _sha256_update(media_lib_sha256_handle_t ctx, const unsigned char *input, size_t len) +{ + RETURN_ON_NULL_HANDLE(ctx); + return mbedtls_sha256_update_ret((mbedtls_sha256_context *)ctx, input, len); +} + +static int _sha256_finish(media_lib_sha256_handle_t ctx, unsigned char output[16]) +{ + RETURN_ON_NULL_HANDLE(ctx); + return mbedtls_sha256_finish_ret((mbedtls_sha256_context *)ctx, output); +} + +static void _aes_init(media_lib_aes_handle_t *ctx) +{ + esp_aes_context *aes = + (esp_aes_context *)media_lib_malloc(sizeof(esp_aes_context)); + if (aes) { + esp_aes_init(aes); + *ctx = aes; + } +} + +static void _aes_free(media_lib_aes_handle_t ctx) +{ + if (ctx) { + esp_aes_free((esp_aes_context *)ctx); + media_lib_free(ctx); + } +} + +static int _aes_set_key(media_lib_aes_handle_t ctx, uint8_t *key, uint8_t key_bits) +{ + RETURN_ON_NULL_HANDLE(ctx); + int ret = esp_aes_setkey((esp_aes_context *)ctx, key, key_bits); + return ret; +} + +static int _aes_crypt_cbc(media_lib_aes_handle_t ctx, bool decrypt_mode, uint8_t iv[16], uint8_t *input, + size_t size, uint8_t *output) +{ + RETURN_ON_NULL_HANDLE(ctx); + int ret = esp_aes_crypt_cbc( + (esp_aes_context *)ctx, + decrypt_mode ? ESP_AES_DECRYPT : ESP_AES_ENCRYPT, + size, iv, input, output); + return ret; +} + +esp_err_t media_lib_add_default_crypt_adapter(void) +{ + // TODO not support AES on P4 currently + return 0; + media_lib_crypt_t crypt_lib = { + .md5_init = _md5_init, + .md5_free = _md5_free, + .md5_start = _md5_start, + .md5_update = _md5_update, + .md5_finish = _md5_finish, + .sha256_init = _sha256_init, + .sha256_free = _sha256_free, + .sha256_start = _sha256_start, + .sha256_update = _sha256_update, + .sha256_finish = _sha256_finish, + .aes_init = _aes_init, + .aes_free = _aes_free, + .aes_set_key = _aes_set_key, + .aes_crypt_cbc = _aes_crypt_cbc, + }; + return media_lib_crypt_register(&crypt_lib); +} +#endif diff --git a/components/third_party/media_lib_sal/port/media_lib_netif_default.c b/components/third_party/media_lib_sal/port/media_lib_netif_default.c new file mode 100644 index 0000000..5de690c --- /dev/null +++ b/components/third_party/media_lib_sal/port/media_lib_netif_default.c @@ -0,0 +1,104 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2023 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include "esp_log.h" +#include "media_lib_netif_reg.h" +#include "media_lib_adapter.h" +#include "media_lib_os.h" +#include "esp_idf_version.h" +#include "lwip/ip_addr.h" + +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0)) +#include "esp_netif.h" +#else +#include "tcpip_adapter_types.h" +#endif + +#ifdef CONFIG_MEDIA_PROTOCOL_LIB_ENABLE + +static int _get_ipv4_info(media_lib_net_type_t type, media_lib_ipv4_info_t *ip_info) +{ + int ret; +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) + esp_netif_ip_info_t local_ip; + const char* name; + switch (type) { + case MEDIA_LIB_NET_TYPE_STA: + name = "WIFI_STA_DEF"; + break; + case MEDIA_LIB_NET_TYPE_AP: + name = "WIFI_AP_DEF"; + break; + case MEDIA_LIB_NET_TYPE_ETH: + name = "ETH_DEF"; + break; + default: + return ESP_ERR_NOT_SUPPORTED; + } + ret = esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey(name), &local_ip); +#else + tcpip_adapter_ip_info_t local_ip; + tcpip_adapter_if_t if_type; + switch (type) { + case MEDIA_LIB_NET_TYPE_STA: + if_type = TCPIP_ADAPTER_IF_STA; + break; + case MEDIA_LIB_NET_TYPE_AP: + if_type = TCPIP_ADAPTER_IF_AP; + break; + case MEDIA_LIB_NET_TYPE_ETH: + if_type = TCPIP_ADAPTER_IF_ETH; + break; + default: + return ESP_ERR_NOT_SUPPORTED; + } + ret = tcpip_adapter_get_ip_info(if_type, &local_ip); +#endif + if (ret == ESP_OK) { + ip_info->ip.addr = local_ip.ip.addr; + ip_info->netmask.addr = local_ip.netmask.addr; + ip_info->gw.addr = local_ip.gw.addr; + } + return ret; +} + +static char* _ipv4_ntoa(const media_lib_ipv4_addr_t *addr) +{ + if (sizeof(media_lib_ipv4_addr_t) == sizeof(ip4_addr_t)) { + return ip4addr_ntoa((const ip4_addr_t*)addr); + } + return NULL; +} + +esp_err_t media_lib_add_default_netif_adapter(void) +{ + media_lib_netif_t netif_lib = { + .get_ipv4_info = _get_ipv4_info, + .ipv4_ntoa = _ipv4_ntoa, + }; + return media_lib_netif_register(&netif_lib); +} + +#endif diff --git a/components/third_party/media_lib_sal/port/media_lib_os_freertos.c b/components/third_party/media_lib_sal/port/media_lib_os_freertos.c new file mode 100644 index 0000000..d248d59 --- /dev/null +++ b/components/third_party/media_lib_sal/port/media_lib_os_freertos.c @@ -0,0 +1,532 @@ + +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "freertos/task.h" + +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "media_lib_adapter.h" +#include "media_lib_os_reg.h" +#include "esp_idf_version.h" + +#if CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT +#include "freertos/task_snapshot.h" +#endif + +#ifdef __XTENSA__ +#include "esp_debug_helpers.h" +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) +#include "esp_cpu_utils.h" +#endif +#endif +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) +#include "esp_memory_utils.h" +#else +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) +#include "soc/soc_memory_types.h" +#else +#include "soc/soc_memory_layout.h" +#endif +#endif + +#define RETURN_ON_NULL_HANDLE(h) \ + if (h == NULL) { \ + return ESP_ERR_INVALID_ARG; \ + } + +#define TAG "MEDIA_OS" +#define MAX_STACK_SIZE (100*1024) +#define MAX_SEARCH_CODE_LEN (1024) +#define RISC_V_RET_CODE (0x8082) + +#if CONFIG_SPIRAM_BOOT_INIT + +static void *_malloc_in_heap(size_t size) +{ + return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +} + +static void _free_in_heap(void *buf) +{ + heap_caps_free(buf); +} + +static void *_calloc_in_heap(size_t elm, size_t size) +{ + size_t total = elm * size; + void *buf = _malloc_in_heap(total); + if (buf) { + memset(buf, 0, total); + } + return buf; +} + +static void *_realloc_in_heap(void *buf, size_t size) +{ + return heap_caps_realloc(buf, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +} + +static void *_malloc_align_in_heap(size_t size, uint8_t align) +{ + return heap_caps_aligned_alloc(align, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +} + +static char *_strdup_in_heap(const char *str) +{ + if (str) { + int len = strlen(str) + 1; + void *buf = _malloc_in_heap(len); + if (buf) { + memcpy(buf, str, len); + return buf; + } + } + return NULL; +} + +#else + +static void *_malloc_align(size_t size, uint8_t align) +{ + if(!align || ((align & (align - 1)) != 0)) { + return NULL; + } + size += align; + uint8_t *buf = (uint8_t*) malloc(size); + if (buf == NULL) { + return NULL; + } + uint8_t *aligned = (uint8_t*)(((size_t)buf + align) & (~(align-1))); + aligned[-1] = (uint8_t)(size_t)(aligned - buf); + return aligned; +} + +static void _free_align(void *addr) +{ + if (addr == NULL) { + return; + } + uint8_t *aligned = (uint8_t*)addr; + free(aligned - aligned[-1]); +} + +#endif + +#if defined(CONFIG_SPIRAM_BOOT_INIT) && \ + (CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY) +BaseType_t __attribute__((weak)) xTaskCreateRestrictedPinnedToCore( + const TaskParameters_t *const pxTaskDefinition, TaskHandle_t *pxCreatedTask, + const BaseType_t xCoreID) +{ + ESP_LOGE(TAG, + "Not found right %s.\r\nPlease enter IDF-PATH with \"cd " + "$IDF_PATH\" and apply the IDF patch with \"git apply " + "$ADF_PATH/idf_patches/idf_v3.3_freertos.patch\" first\r\n", + __func__); + return pdFALSE; +} +static int _thread_create(media_lib_thread_handle_t *handle, const char *name, + void(*body)(void *arg), void *arg, uint32_t stack_size, + int prio, int core) +{ + StackType_t *task_stack = NULL; + do { +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) + BaseType_t ret = xTaskCreatePinnedToCoreWithCaps(body, name, stack_size, arg, prio, (TaskHandle_t *)handle, + core, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (ret != pdPASS) { + ESP_LOGE(TAG, "Error creating RestrictedPinnedToCore %s", name); + break; + } +#else + task_stack = (StackType_t *) calloc_in_heap(1, stack_size); + TaskParameters_t xRegParameters = {.pvTaskCode = body, + .pcName = name, + .usStackDepth = stack_size, + .pvParameters = arg, + .uxPriority = + prio | portPRIVILEGE_BIT, + .puxStackBuffer = task_stack, + .xRegions = {{ + .pvBaseAddress = 0x00, + .ulLengthInBytes = 0x00, + .ulParameters = 0x00, + }}}; + if (xTaskCreateRestrictedPinnedToCore( + &xRegParameters, (TaskHandle_t *)handle, core) != pdPASS) { + ESP_LOGE(TAG, "Error creating RestrictedPinnedToCore %s", name); + break; + } +#endif + return ESP_OK; + } while (0); + if (task_stack) { + _free_in_heap(task_stack); + } + return ESP_FAIL; +} +#else +static int _thread_create(media_lib_thread_handle_t *handle, const char *name, + void(*body)(void *arg), void *arg, uint32_t stack_size, + int prio, int core) +{ + if (xTaskCreatePinnedToCore(body, name, stack_size, arg, prio, + (TaskHandle_t *)handle, core) != pdPASS) { + ESP_LOGE(TAG, "Fail to create thread %s\n", name); + return ESP_FAIL; + } + return ESP_OK; +} +#endif + +static void _thread_destroy(media_lib_thread_handle_t handle) +{ +#if defined(CONFIG_SPIRAM_BOOT_INIT) && \ + (CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY) && \ + (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) + vTaskDeleteWithCaps((TaskHandle_t)handle); +#else + // allow NULL to destroy self + vTaskDelete((TaskHandle_t)handle); +#endif +} + +static bool _thread_set_priority(media_lib_thread_handle_t handle, int prio) +{ + vTaskPrioritySet((TaskHandle_t)handle, prio); + return true; +} + +static void _thread_sleep(uint32_t ms) +{ + vTaskDelay(ms / portTICK_PERIOD_MS); +} + +static int _sema_create(media_lib_sema_handle_t *sema) +{ + if (sema) { + *sema = (media_lib_sema_handle_t)xSemaphoreCreateCounting(1, 0); + if (*sema != NULL) { + return ESP_OK; + } + } + return ESP_FAIL; +} + +static int _mutex_create(media_lib_mutex_handle_t *mutex) +{ + if (mutex) { + *mutex = (media_lib_mutex_handle_t)xSemaphoreCreateRecursiveMutex(); + if (*mutex != NULL) { + return ESP_OK; + } + } + return ESP_FAIL; +} + +static int _os_lock_timeout(void *lock, uint32_t timeout) +{ + RETURN_ON_NULL_HANDLE(lock); + if (timeout != portMAX_DELAY) { + timeout /= portTICK_PERIOD_MS; + } + return xSemaphoreTake((QueueHandle_t)lock, timeout) ? ESP_OK : ESP_FAIL; +} + +static int _mutex_lock_timeout(media_lib_mutex_handle_t mutex, uint32_t timeout) +{ + RETURN_ON_NULL_HANDLE(mutex); + if (timeout != portMAX_DELAY) { + timeout /= portTICK_PERIOD_MS; + } + return xSemaphoreTakeRecursive(mutex, timeout); +} + +static int _sema_lock_timeout(media_lib_sema_handle_t sema, uint32_t timeout) +{ + return _os_lock_timeout(sema, timeout); +} + +static int _os_unlock(void *lock) +{ + RETURN_ON_NULL_HANDLE(lock); + xSemaphoreGive((QueueHandle_t)lock); + return ESP_OK; +} + +static int _mutex_unlock(media_lib_mutex_handle_t mutex) +{ + RETURN_ON_NULL_HANDLE(mutex); + return xSemaphoreGiveRecursive(mutex); +} + +static int _sema_unlock(media_lib_sema_handle_t sema) +{ + return _os_unlock(sema); +} + +static int _os_lock_destroy(void *lock) +{ + RETURN_ON_NULL_HANDLE(lock); + vSemaphoreDelete((QueueHandle_t)lock); + return ESP_OK; +} + +static int _mutex_destroy(media_lib_mutex_handle_t mutex) +{ + return _os_lock_destroy(mutex); +} + +static int _sema_destroy(media_lib_sema_handle_t sema) +{ + return _os_lock_destroy(sema); +} + +static int _enter_critical(void) +{ + return ESP_OK; +} + +static int _leave_critical(void) +{ + return ESP_OK; +} + +static int _event_group_create(media_lib_event_grp_handle_t *group) +{ + RETURN_ON_NULL_HANDLE(group); + *group = (media_lib_event_grp_handle_t)xEventGroupCreate(); + return ESP_OK; +} + +static uint32_t _event_group_set_bits(media_lib_event_grp_handle_t group, uint32_t bits) +{ + RETURN_ON_NULL_HANDLE(group); + return (uint32_t)xEventGroupSetBits((EventGroupHandle_t)group, bits); +} + +static uint32_t _event_group_clr_bits(media_lib_event_grp_handle_t group, uint32_t bits) +{ + RETURN_ON_NULL_HANDLE(group); + return (uint32_t)xEventGroupClearBits((EventGroupHandle_t)group, bits); +} + +static uint32_t _event_group_wait_bits(media_lib_event_grp_handle_t group, + uint32_t bits, uint32_t timeout) +{ + RETURN_ON_NULL_HANDLE(group); + if (timeout != portMAX_DELAY) { + timeout /= portTICK_PERIOD_MS; + } + return (uint32_t)xEventGroupWaitBits((EventGroupHandle_t)group, bits, false, + true, timeout); +} + +static int _event_group_destroy(media_lib_event_grp_handle_t group) +{ + RETURN_ON_NULL_HANDLE(group); + vEventGroupDelete((EventGroupHandle_t)group); + return ESP_OK; +} + +#ifdef __XTENSA__ + +static int _get_stack_frame(void** addr, int n) +{ + int filled = 0;; +#if CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT + esp_backtrace_frame_t frame = {}; + TaskSnapshot_t snap_shot; + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + vTaskGetSnapshot(cur_task, &snap_shot); + snap_shot.pxTopOfStack = pxTaskGetStackStart(cur_task);; + esp_backtrace_get_start(&(frame.pc), &(frame.sp), &(frame.next_pc)); + + for (int i = 0; i < n; i++) { + esp_backtrace_get_next_frame(&frame); + if (!((uint32_t)frame.sp >= (uint32_t)snap_shot.pxTopOfStack && + ((uint32_t)frame.sp <= (uint32_t)snap_shot.pxEndOfStack))) { + break; + } + addr[filled] = (void*)esp_cpu_process_stack_pc(frame.pc); + if (!esp_ptr_executable((void*)addr[filled])) { + break; + } + filled++; + } +#endif + return filled; +} + +#else + +/** + * @brief Get reserved stack size of a function from `addi` instruction before `ret` + * Limitation: not support dynamic array on stack + * Instruction: addi sp, sp, imm + * Instruction size is 16 or 32 based on imm size + * sp(4:0): 2 + * When size is 16: + * 000|imm(5)|sp(4:0)|imm(4:0)|01 + * 011|imm(9)|00010|imm(4|6|8:7|5)|01 + * When size is 32: + * imm(11:0)|sp(4:0)|000|sp(4:0)|0010011 + */ +static int get_addi_size(uint32_t addi) +{ + if ((addi & 0x10113) == 0x10113) { + return addi >> 20; + } + addi >>= 16; + if ((addi & 0x6103) == 0x101) { + return (addi & 0xfc) >> 2; + } + if ((addi & 0x6103) == 0x6101) { + uint32_t s = 0; + if (addi & 0x40) { + s += 16; + } + if (addi & 0x20) { + s += 64; + } + if (addi & 0x10) { + s += 256; + } + if (addi & 0x08) { + s += 128; + } + if (addi & 0x04) { + s += 32; + } + return s; + } + return 0; +} + +static int _get_stack_frame(void** addr, int n) +{ + int fill = 0; +#if CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT + uint32_t pc, sp; + TaskSnapshot_t snap_shot; + TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); + vTaskGetSnapshot(cur_task, &snap_shot); + snap_shot.pxTopOfStack = pxTaskGetStackStart(cur_task); + asm volatile ("addi %0, sp, 0\n" + "auipc %1, 0\n" + "addi %1, %1, 0\n" + : "=r" (sp), "=r" (pc)); + uint16_t* pc_addr = (uint16_t*)pc; + uint8_t* sp_addr = (uint8_t*)sp; + int depth = 0; + for (int i = 0; i < MAX_SEARCH_CODE_LEN; i++) { + if (pc_addr[i] != RISC_V_RET_CODE) { + continue; + } + uint32_t v = (pc_addr[i-1] << 16) + pc_addr[i-2]; + int s = get_addi_size(v); + if (s == 0) { + break; + } + sp_addr += s; + if (sp_addr >= snap_shot.pxEndOfStack) { + break; + } + uint32_t lr = *((int*)sp_addr - 1) - 4; + if (esp_ptr_executable((void*)lr)) { + depth++; + if (depth >= 2) { + addr[fill++] = (void*) lr; + if (fill >= n) { + break; + } + } + pc_addr = (uint16_t*) lr; + i = 0; + continue; + } + break; + } +#endif + return fill; +} +#endif + +esp_err_t media_lib_add_default_os_adapter(void) +{ + media_lib_os_t os_lib = { +#if CONFIG_SPIRAM_BOOT_INIT + .malloc = _malloc_in_heap, + .free = _free_in_heap, + .malloc_align = _malloc_align_in_heap, + .free_align = _free_in_heap, + .calloc = _calloc_in_heap, + .realloc = _realloc_in_heap, + .strdup = _strdup_in_heap, +#else + .malloc = malloc, + .free = free, + .calloc = calloc, + .realloc = realloc, + .malloc_align = _malloc_align, + .free_align = _free_align, + .strdup = strdup, +#endif + .get_stack_frame = _get_stack_frame, + + .thread_create = _thread_create, + .thread_destroy = _thread_destroy, + .thread_set_prio = _thread_set_priority, + .thread_sleep = _thread_sleep, + + .sema_create = _sema_create, + .sema_lock = _sema_lock_timeout, + .sema_unlock = _sema_unlock, + .sema_destroy = _sema_destroy, + + .mutex_create = _mutex_create, + .mutex_lock = _mutex_lock_timeout, + .mutex_unlock = _mutex_unlock, + .mutex_destroy = _mutex_destroy, + + .enter_critical = _enter_critical, + .leave_critical = _leave_critical, + + .group_create = _event_group_create, + .group_set_bits = _event_group_set_bits, + .group_clr_bits = _event_group_clr_bits, + .group_wait_bits = _event_group_wait_bits, + .group_destroy = _event_group_destroy, + }; + return media_lib_os_register(&os_lib); +} diff --git a/components/third_party/media_lib_sal/port/media_lib_socket_default.c b/components/third_party/media_lib_sal/port/media_lib_socket_default.c new file mode 100644 index 0000000..084d1ca --- /dev/null +++ b/components/third_party/media_lib_sal/port/media_lib_socket_default.c @@ -0,0 +1,110 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in + * which case, it is 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. + * + */ + +#include "media_lib_adapter.h" +#include "media_lib_socket_reg.h" + +#ifdef CONFIG_MEDIA_PROTOCOL_LIB_ENABLE +static ssize_t _readv(int s, const struct iovec *iov, int iovcnt) +{ + return 0; +} + +static ssize_t _recvmsg(int s, struct msghdr *message, int flags) +{ + return recvmsg(s, message, flags); +} + +static const char *_inet_ntop(int af, const void *src, char *dst, + socklen_t size) +{ + return lwip_inet_ntop(af, src, dst, size); +} + +static int _inet_pton(int af, const char *src, void *dst) +{ + return lwip_inet_pton(af, src, dst); +} + +static int _setsockopt(int s, int level, int optname, const void *opval, + socklen_t optlen) +{ + return setsockopt(s, level, optname, opval, optlen); +} + +static int _getsockopt(int s, int level, int optname, void *opval, + socklen_t *optlen) +{ + return getsockopt(s, level, optname, opval, optlen); +} + +static int _getsockname(int s, struct sockaddr *name, socklen_t *namelen) +{ + return getsockname(s, name, namelen); +} + +static int _select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, media_lib_timeval *timeout) { + int ret; + struct timeval tm = { + .tv_sec = timeout->tv_sec, + .tv_usec = timeout->tv_usec, + }; + ret = lwip_select(maxfdp1, readset, writeset, exceptset, &tm); + return ret; +} + +esp_err_t media_lib_add_default_socket_adapter(void) +{ + media_lib_socket_t sock_lib = { + .sock_accept = lwip_accept, + .sock_bind = lwip_bind, + .sock_shutdown = lwip_shutdown, + .sock_close = lwip_close, + .sock_connect = lwip_connect, + .sock_listen = lwip_listen, + .sock_recv = lwip_recv, + .sock_read = lwip_read, + .sock_readv = _readv, + .sock_recvfrom = lwip_recvfrom, + .sock_recvmsg = _recvmsg, + .sock_send = lwip_send, + .sock_sendmsg = lwip_sendmsg, + .sock_sendto = lwip_sendto, + .sock_open = lwip_socket, + .sock_write = lwip_write, + .sock_writev = lwip_writev, + .sock_select = _select, + .sock_ioctl = lwip_ioctl, + .sock_fcntl = lwip_fcntl, + .sock_inet_ntop = _inet_ntop, + .sock_inet_pton = _inet_pton, + .sock_setsockopt = _setsockopt, + .sock_getsockopt = _getsockopt, + .sock_getsockname = _getsockname, + }; + return media_lib_socket_register(&sock_lib); +} +#endif diff --git a/components/third_party/media_lib_sal/port/media_lib_tls_default.c b/components/third_party/media_lib_sal/port/media_lib_tls_default.c new file mode 100644 index 0000000..1f945b5 --- /dev/null +++ b/components/third_party/media_lib_sal/port/media_lib_tls_default.c @@ -0,0 +1,211 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2022 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include "esp_log.h" +#include "media_lib_tls_reg.h" +#include "media_lib_adapter.h" +#include "media_lib_os.h" +#include "esp_tls.h" +#include "esp_log.h" + +#if __has_include("esp_idf_version.h") +#include "esp_idf_version.h" +#else +#define ESP_IDF_VERSION_VAL(major, minor, patch) 1 +#endif + +#ifdef CONFIG_MEDIA_PROTOCOL_LIB_ENABLE + +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) +#define esp_tls_conn_delete esp_tls_conn_destroy +#endif + +#define TAG "TLS_Lib" +typedef struct { + esp_tls_t* tls; + bool is_server; +} media_lib_tls_inst_t; + +static media_lib_tls_handle_t _tls_new(const char *hostname, int hostlen, int port, const media_lib_tls_cfg_t *cfg) +{ + esp_tls_cfg_t tls_cfg = { + .cacert_buf = (const unsigned char *)cfg->cacert_buf, + .cacert_bytes = (unsigned int)cfg->cacert_bytes, + .clientcert_buf = (const unsigned char *)cfg->clientcert_buf, + .clientcert_bytes = (unsigned int)cfg->clientcert_bytes, + .clientkey_buf = (const unsigned char *)cfg->clientkey_buf, + .clientkey_bytes = (unsigned int)cfg->clientkey_bytes, + .clientkey_password = (const unsigned char *)cfg->clientkey_password, + .clientkey_password_len = (unsigned int)cfg->clientkey_password_len, + .non_block = cfg->non_block, + .timeout_ms = cfg->timeout_ms, + .use_global_ca_store = cfg->use_global_ca_store, + .skip_common_name = cfg->skip_common_name, +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)) + .use_secure_element = cfg->use_secure_element, + .crt_bundle_attach = cfg->crt_bundle_attach, +#endif + }; + media_lib_tls_inst_t * tls_lib = calloc(1, sizeof(media_lib_tls_inst_t)); + if (tls_lib == NULL) { + ESP_LOGE(TAG, "No memory for instance"); + return NULL; + } +#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 0, 0)) + esp_tls_t * tls = esp_tls_conn_new(hostname, strlen(hostname), port, &tls_cfg); + if (tls == NULL) { + ESP_LOGE(TAG, "Fail to connect client"); + free(tls_lib); + return NULL; + } +#else + esp_tls_t* tls = esp_tls_init(); + if (tls == NULL || + esp_tls_conn_new_sync(hostname, strlen(hostname), port, &tls_cfg, tls) < 0) { + esp_tls_conn_delete(tls); + free(tls_lib); + ESP_LOGE(TAG, "Fail to connect client"); + return NULL; + } +#endif + tls_lib->tls = tls; + return (media_lib_tls_handle_t)tls_lib; +} + +static media_lib_tls_handle_t _tls_new_server(int fd, const media_lib_tls_server_cfg_t *cfg) +{ +#ifndef CONFIG_ESP_TLS_SERVER + ESP_LOGE(TAG, "Please enable macro CONFIG_ESP_TLS_SERVER"); + return NULL; +#else + esp_tls_cfg_server_t server_cfg = { + .cacert_buf = (const unsigned char *)cfg->cacert_buf, + .cacert_bytes = (unsigned int)cfg->cacert_bytes, + .servercert_buf = (const unsigned char *)cfg->servercert_buf, + .servercert_bytes = (unsigned int)cfg->servercert_bytes, + .serverkey_buf = (const unsigned char *)cfg->serverkey_buf, + .serverkey_bytes = (unsigned int)cfg->serverkey_bytes, + .serverkey_password = (const unsigned char *)cfg->serverkey_password, + .serverkey_password_len = (unsigned int)cfg->serverkey_password_len, + }; + media_lib_tls_inst_t *tls_lib = calloc(1, sizeof(media_lib_tls_inst_t)); + if (tls_lib == NULL) { + ESP_LOGE(TAG, "No memory for instance"); + return NULL; + } + esp_tls_t* tls = esp_tls_init(); + if (tls == NULL || + esp_tls_server_session_create(&server_cfg, fd, tls) < 0) { + ESP_LOGE(TAG, "Fail to create server session"); + if (tls) { + free(tls); + } + free(tls_lib); + return NULL; + } + tls_lib->tls = tls; + tls_lib->is_server = true; + return (media_lib_tls_handle_t)tls_lib; +#endif +} + +static int _tls_write(media_lib_tls_handle_t tls, const void *data, size_t datalen) +{ + if (tls) { + media_lib_tls_inst_t *tls_lib = (media_lib_tls_inst_t *)tls; + return esp_tls_conn_write(tls_lib->tls, data, datalen); + } else { + return ESP_ERR_INVALID_ARG; + } +} + +static int _tls_read(media_lib_tls_handle_t tls, void *data, size_t datalen) +{ + if (tls) { + media_lib_tls_inst_t *tls_lib = (media_lib_tls_inst_t *)tls; + return esp_tls_conn_read(tls_lib->tls, data, datalen); + } else { + return ESP_ERR_INVALID_ARG; + } +} + +static int _tls_getsockfd(media_lib_tls_handle_t tls) +{ + if (tls) { + media_lib_tls_inst_t *tls_lib = (media_lib_tls_inst_t *)tls; +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) + int sock_fd = -1; + esp_tls_get_conn_sockfd(tls_lib->tls, &sock_fd); + return sock_fd; +#else + return tls_lib->tls->sockfd; +#endif + } else { + return ESP_ERR_INVALID_ARG; + } +} + +static int _tls_delete(media_lib_tls_handle_t tls) +{ + if (tls) { + media_lib_tls_inst_t *tls_lib = (media_lib_tls_inst_t *)tls; + if (tls_lib->is_server) { +#ifdef CONFIG_ESP_TLS_SERVER + esp_tls_server_session_delete(tls_lib->tls); +#endif + } else { + esp_tls_conn_delete(tls_lib->tls); + } + free(tls); + } else { + return ESP_ERR_INVALID_ARG; + } + return ESP_OK; +} + +static int _tls_get_bytes_avail(media_lib_tls_handle_t tls) +{ + if (tls) { + media_lib_tls_inst_t *tls_lib = (media_lib_tls_inst_t *)tls; + return esp_tls_get_bytes_avail(tls_lib->tls); + } else { + return ESP_ERR_INVALID_ARG; + } +} + +esp_err_t media_lib_add_default_tls_adapter(void) +{ + media_lib_tls_t tls_lib = { + .tls_new = _tls_new, + .tls_new_server = _tls_new_server, + .tls_write = _tls_write, + .tls_read = _tls_read, + .tls_getsockfd = _tls_getsockfd, + .tls_delete = _tls_delete, + .tls_get_bytes_avail = _tls_get_bytes_avail, + }; + return media_lib_tls_register(&tls_lib); +} +#endif diff --git a/components/third_party/media_lib_sal/port/msg_q.c b/components/third_party/media_lib_sal/port/msg_q.c new file mode 100644 index 0000000..052ba0f --- /dev/null +++ b/components/third_party/media_lib_sal/port/msg_q.c @@ -0,0 +1,300 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include "msg_q.h" +#include "string.h" +#include "stdio.h" +#include "stdlib.h" +#include +#include "pthread.h" +#include "stdbool.h" + +typedef struct msg_q_t { + pthread_mutex_t data_mutex; + pthread_cond_t data_cond; + void** data; + const char* name; + int cur; + int each_size; + int number; + int filled; + bool quit; + bool reset; + int user; +} msg_q_t; + +msg_q_handle_t msg_q_create(int msg_number, int msg_size) { + msg_q_t* q = (msg_q_t*)calloc(1, sizeof(msg_q_t)); + if (msg_size == 0 || msg_number == 0) { + return NULL; + } + if (q) { + q->name = ""; + pthread_mutex_init(&(q->data_mutex), NULL); + pthread_cond_init(&q->data_cond, NULL); + q->data = (void**)malloc(sizeof(void*) * msg_number); + for (int i = 0; i < msg_number; i++) { + q->data[i] = malloc(msg_size); + } + q->number = msg_number; + q->each_size = msg_size; + } + return q; +} + +msg_q_handle_t msg_q_create_by_name(const char* name, int msg_size, int msg_number) { + msg_q_t* q = (msg_q_t*)calloc(1, sizeof(msg_q_t)); + if (msg_size == 0 || msg_number == 0) { + return NULL; + } + if (q) { + q->name = name; + pthread_mutex_init(&(q->data_mutex), NULL); + pthread_cond_init(&q->data_cond, NULL); + q->data = (void**)malloc(sizeof(void*) * msg_number); + for (int i = 0; i < msg_number; i++) { + q->data[i] = malloc(msg_size); + } + q->number = msg_number; + q->each_size = msg_size; + } + return q; +} + +int msg_q_wait_consume(msg_q_handle_t q) { + int ret = -1; + if (q) { + pthread_mutex_lock(&(q->data_mutex)); + if (q->filled) { + q->user++; + pthread_cond_wait(&(q->data_cond), &(q->data_mutex)); + q->user--; + if (q->quit == false && q->reset == false) { + ret = 0; + } + } + else { + ret = 0; + } + pthread_mutex_unlock(&(q->data_mutex)); + } + return ret; +} + +int msg_q_send(msg_q_handle_t q, void* msg, int size) { + if (q) { + int ret = 0; + if (size > q->each_size) { + //printf("msgsize %d too big than %d\n", size, q->each_size); + return -1; + } + pthread_mutex_lock(&(q->data_mutex)); + //printf("msg buffer %s filled %d total:%d\n", q->name, q->filled, q->number); + while (q->quit == false && q->filled >= q->number && q->reset == false) { + //printf("msg buffer %s full\n", q->name); + q->user++; + pthread_cond_wait(&(q->data_cond), &(q->data_mutex)); + q->user--; + } + if (q->reset) { + q->reset = false; + } + if (q->quit == false && q->reset == false) { + int idx = (q->cur + q->filled) % q->number; + memcpy(q->data[idx], msg, size); + q->filled++; + // printf("Send q %s OK have: %d\n", q->name, q->filled); + } + else { + ret = -2; + } + pthread_mutex_unlock(&(q->data_mutex)); + //notify have data + if (ret == 0) { + pthread_cond_signal(&(q->data_cond)); + } + return ret; + } + return -1; +} + +int msg_q_recv(msg_q_handle_t q, void* msg, int size, bool no_wait) { + if (q) { + int ret = 0; + if (size > q->each_size) { + printf("msgsize %d too big than %d\n", size, q->each_size); + return -1; + } + pthread_mutex_lock(&(q->data_mutex)); + while (q->quit == false && q->filled == 0 && q->reset == false) { + if (no_wait) { + pthread_mutex_unlock(&(q->data_mutex)); + return 1; + } + q->user++; + //printf("msg buffer %s empty\n", q->name); + ret = pthread_cond_wait(&(q->data_cond), &(q->data_mutex)); + //printf("msg buffer %s reset:%d\n", q->name, q->reset); + q->user--; + } + if (q->quit == false && q->reset == false) { + memcpy(msg, q->data[q->cur], size); + // printf("Recv q %s OK have: %d\n", q->name, q->filled); + q->filled--; + q->cur++; + q->cur %= q->number; + } + else { + if (q->reset) { + q->reset = false; + } + printf("recv after destroy\n"); + ret = -2; + } + pthread_mutex_unlock(&(q->data_mutex)); + if (ret == 0) { + pthread_cond_signal(&(q->data_cond)); + } + return ret; + } + printf("q not created\n"); + return -1; +} + +int msg_q_add_user(msg_q_handle_t q, int dir) { + if (q) { + pthread_mutex_lock(&(q->data_mutex)); + if (dir) { + q->user++; + } + else { + q->user--; + } + pthread_mutex_unlock(&(q->data_mutex)); + return 0; + } + return -1; +} + +int msg_q_reset(msg_q_handle_t q) { + if (q) { + while (q->user) { + pthread_mutex_lock(&(q->data_mutex)); + //printf("reset msg %s\n", q->name); + q->reset = true; + pthread_cond_broadcast(&(q->data_cond)); + pthread_mutex_unlock(&(q->data_mutex)); + usleep(2000); + } + pthread_mutex_lock(&(q->data_mutex)); + q->cur = 0; + q->filled = 0; + pthread_mutex_unlock(&(q->data_mutex)); + //printf("reset Finished %s\n", q->name); + } + return 0; +} + +int msg_q_wakeup(msg_q_handle_t q) { + if (q) { + pthread_mutex_lock(&(q->data_mutex)); + //printf("reset msg %s\n", q->name); + q->reset = true; + pthread_cond_signal(&(q->data_cond)); + pthread_mutex_unlock(&(q->data_mutex)); + while (q->user) { + usleep(1000); + } + pthread_mutex_lock(&(q->data_mutex)); + q->reset = false; + pthread_mutex_unlock(&(q->data_mutex)); + //printf("reset Finished %s\n", q->name); + } + return 0; +} + +int msg_q_number(msg_q_handle_t q) { + int n = 0; + if (q) { + pthread_mutex_lock(&(q->data_mutex));; + n = q->filled; + pthread_mutex_unlock(&(q->data_mutex)); + } + return n; +} + +#if 0 +int msg_q_sort(msg_q_t*q, msg_q_cmp cmp_func, void* tag) { + if (q) { + pthread_mutex_lock(&(q->data_mutex)); + int total_swap = 0; + if (q->filled > 1) { + int i, j; + for (i = 0; i < q->filled - 1; i++) { + int swap = 0; + for (j = 0; j < q->filled -1 -i; j++) { + int a = (j + q->cur) % q->number; + int b = (j + 1+ q->cur) % q->number; + void* cmp_a = q->data[a]; + void* cmp_b = q->data[b]; + if (cmp_func(cmp_a, cmp_b, tag) > 0) { + q->data[a] = cmp_b; + q->data[b] = cmp_a; + swap++; + } + } + total_swap += swap; + if (swap == 0) break; + } + printf("have video frame:%d swap:%d\n", q->filled, total_swap); + } + pthread_mutex_unlock(&(q->data_mutex)); + } + return 0; +} +#endif + +void msg_q_destroy(msg_q_handle_t q) { + if (q) { + pthread_mutex_lock(&(q->data_mutex)); + q->quit = true; + pthread_cond_signal(&(q->data_cond)); + pthread_mutex_unlock(&(q->data_mutex)); + while (q->user) { + usleep(1000); + } + pthread_mutex_lock(&(q->data_mutex)); + pthread_mutex_unlock(&(q->data_mutex)); + + pthread_mutex_destroy(&(q->data_mutex)); + pthread_cond_destroy(&(q->data_cond)); + int i; + for (i = 0; i < q->number; i++) { + free(q->data[i]); + } + free(q->data); + free(q); + } +} + 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/components/third_party/webrtc_utils/CMakeLists.txt b/components/third_party/webrtc_utils/CMakeLists.txt new file mode 100644 index 0000000..6886388 --- /dev/null +++ b/components/third_party/webrtc_utils/CMakeLists.txt @@ -0,0 +1,10 @@ + +set(COMPONENT_ADD_INCLUDEDIRS .) + +# Edit following two lines to set component requirements (see docs) + +list (APPEND COMPONENT_SRCDIRS .) + +list(APPEND COMPONENT_REQUIRES esp-tls mbedtls esp_netif esp_ringbuf) + +register_component() diff --git a/components/third_party/webrtc_utils/webrtc_utils_time.c b/components/third_party/webrtc_utils/webrtc_utils_time.c new file mode 100644 index 0000000..9efd5b3 --- /dev/null +++ b/components/third_party/webrtc_utils/webrtc_utils_time.c @@ -0,0 +1,70 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#include +#include +#include "esp_netif.h" +#include "esp_netif_sntp.h" + +static const char *TAG = "webrtc_time_utils"; + +#define DATE_TIME_STRING_FORMAT (char *) "%Y%m%dT%H%M%SZ" + +/** + * https://en.wikipedia.org/wiki/ISO_8601 + */ +void webrtc_utils_get_time_iso8601(char time_buf[18]) +{ + time_t now; + time(&now); + strftime(time_buf, 17, DATE_TIME_STRING_FORMAT, gmtime(&now)); +} + +esp_err_t webrtc_utils_wait_for_time_sync(uint32_t timeout_ms) +{ + /* wait for time to be set */ + int retry = 0; + int max_retries = timeout_ms / 2000; /* split the retries in 2 seconds each */ + + while (esp_netif_sntp_sync_wait(pdMS_TO_TICKS(2000)) != ESP_OK && ++retry < max_retries) { + ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, max_retries); + } + + return ESP_OK; +} + +esp_err_t webrtc_utils_time_sync_init() +{ + static bool init_done = false; + if (init_done) { + ESP_LOGW(TAG, "Time sync is done already..."); + return ESP_OK; + } + ESP_LOGI(TAG, "Initializing SNTP"); + esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(2, + ESP_SNTP_SERVER_LIST("time.windows.com", "pool.ntp.org" ) ); + esp_netif_sntp_init(&config); + init_done = true; + return ESP_OK; +} diff --git a/components/third_party/webrtc_utils/webrtc_utils_time.h b/components/third_party/webrtc_utils/webrtc_utils_time.h new file mode 100644 index 0000000..9030827 --- /dev/null +++ b/components/third_party/webrtc_utils/webrtc_utils_time.h @@ -0,0 +1,51 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is 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. + * + */ + +#pragma once + +#include +#include + +/** + * @brief Get current time in IO8601 format + * + * @return char* Time string + * @note Caller is supposed to free the output string + */ +void webrtc_utils_get_time_iso8601(char time_buf[18]); + +/** + * @brief Wait for timeout_ms for time_sync to finish + * + * @param timeout_ms time in ms to wait for time_sync. Note, the actual time spent could be more + * @return esp_err_t ESP_OK if success, apt error otherwise + */ +esp_err_t webrtc_utils_wait_for_time_sync(uint32_t timeout_ms); + +/** + * @brief Init sntp time_sync + * + * @return ESP_OK on success, apt error otherwise + */ +esp_err_t webrtc_utils_time_sync_init(); 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/common/CMakeLists.txt b/examples/common/CMakeLists.txt new file mode 100755 index 0000000..d44c479 --- /dev/null +++ b/examples/common/CMakeLists.txt @@ -0,0 +1,14 @@ + +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 b/examples/common/Kconfig new file mode 100644 index 0000000..7243d20 --- /dev/null +++ b/examples/common/Kconfig @@ -0,0 +1,10 @@ +menu "WebRTC Demo Common" + +config NETWORK_USE_ETHERNET + bool "Network use Ethernet" + default n + depends on IDF_TARGET_ESP32P4 + help + Enable this option to use Ethernet + +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/media_sys.h b/examples/common/media_sys.h new file mode 100644 index 0000000..d52d0af --- /dev/null +++ b/examples/common/media_sys.h @@ -0,0 +1,73 @@ +/* Media system + + 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 "esp_webrtc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Build media system + * + * @param[in] rtc_handle WebRTC handle + * + * @return + * - 0 On success + * - Others Fail to build + */ +int media_sys_buildup(void); + +/** + * @brief Get media provider + * + * @param[out] provider Media provider to be returned + * + * @return + * - 0 On success + * - Others Invalid argument + */ +int media_sys_get_provider(esp_webrtc_media_provider_t *provider); + +/** + * @brief Play captured media directly + * + * @return + * - 0 On success + * - Others Fail to capture or play + */ +int test_capture_to_player(void); + +/** + * @brief Play music + * + * @param[in] data Music data to be played + * @param[in] size Music data size + * @param[in] duration Play duration, when duration over data duration will replay + * + * @return + * - 0 On success + * - Others Fail to play + */ +int play_music(const uint8_t *data, int size, int duration); + +/** + * @brief Stop music + * + * @return + * - 0 On success + * - Others Fail to stop + */ +int stop_music(void); + +#ifdef __cplusplus +} +#endif \ No newline at end of file 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/patch_idf.pl b/examples/common/patch_idf.pl new file mode 100644 index 0000000..4a87ca8 --- /dev/null +++ b/examples/common/patch_idf.pl @@ -0,0 +1,140 @@ +#!/usr/bin/perl + +use Cwd 'abs_path'; +use File::Basename; + +my $idf_path = $ENV{"IDF_PATH"}; +my $cur_dir = dirname(abs_path($0)); + +unless (defined $idf_path) { + die "Need cd \$IDF_PATH and do `. ./export.sh` firstly"; +} +my $freertos_patch_file = "$idf_path/components/freertos/esp_additions/freertos_tasks_c_additions.h"; + +patch_dma2d(); +patch_idf(); +patch_srtp(); + +sub patch_idf { + my $patch_content; + do {local $/ = undef; $patch_content = ;}; + open (my $H, $freertos_patch_file) || die "$freertos_patch_file not exists\n"; + my $code; + while (<$H>) { + $code .= $_; + if (/xTaskCreateRestrictedPinnedToCore/) { + print "Patch already existed for code\n"; + close $H; + return; + } + } + close $H; + + $code .= $patch_content; + + open (my $H, ">$freertos_patch_file"); + print $H $code; + close $H; + print "Patch for IDF success, now continue to build\n"; +} + +sub patch_dma2d { + my $content = 'assert\(dma2d_ll_rx_is_fsm_idle\(group->hal.dev, channel_id\)\)'; + my $dma2d = "$idf_path/components/esp_hw_support/dma/dma2d.c"; + open (my $H, $dma2d) or die "$dma2d file not exists\n"; + my $patched = 0; + my $code = ""; + while (<$H>) { + if (/$content/) { + if (/^ +\/\//) { + print "dma2d already patched\n"; + close $H; + return; + } + if ($patched == 0 && /( +)(.*)/) { + $code .= "$1 //$2\n"; + $patched = 1; + } else { + $code .= $_; + } + } else { + $code .= $_; + } + } + open (my $H, ">$dma2d"); + print $H $code; + close $H; + print "Patch for dma2d success\n"; +} + +sub patch_srtp { + my $content = 'return srtp_cipher_type_test(ct, ct->test_data);'; + my $srtp = "$cur_dir/../../components/srtp/libsrtp/crypto/cipher/cipher.c"; + open (my $H, $srtp) or die "$srtp file not exists\n"; + my $patched = 0; + my $code = ""; + while (<$H>) { + if (index($_, $content) != -1) { + if (/^ +\/\//) { + print "srtp already patched\n"; + close $H; + return; + } + if ($patched == 0 && /( +)(.*)/) { + $code .= "$1// $2\n"; + $code .= "$1" . "return srtp_err_status_ok;\n"; + $patched = 1; + } else { + $code .= $_; + } + } else { + $code .= $_; + } + } + open (my $H, ">$srtp"); + print $H $code; + close $H; + print "Patch for srtp success\n"; +} +__DATA__ +BaseType_t xTaskCreateRestrictedPinnedToCore( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask, const BaseType_t xCoreID) +{ + TCB_t *pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer ); + + if( pxTaskDefinition->puxStackBuffer != NULL ) + { + /* Allocate space for the TCB. Where the memory comes from depends + on the implementation of the port malloc function and whether or + not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + memset( ( void * ) pxNewTCB, 0x00, sizeof( TCB_t ) ); + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + /* Tasks can be created statically or dynamically, so note + this task had a statically allocated stack in case it is + later deleted. The TCB was allocated dynamically. */ + pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions, + xCoreID ); + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + } + + return xReturn; +} \ 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..055d265 --- /dev/null +++ b/examples/voice_agent/CMakeLists.txt @@ -0,0 +1,50 @@ +# 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) + +# include($ENV{ADF_PATH}/CMakeLists.txt) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +if("${IDF_TARGET}" STREQUAL "esp32p4") + set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/ethernet/basic/components/ethernet_init") +endif() + +# Wi-Fi credentials +if(NOT DEFINED ENV{WIFI_SSID}) + message(FATAL_ERROR "WiFi SSID not set in env variable WIFI_SSID") +endif() +if(NOT DEFINED ENV{WIFI_PASSWORD}) + message(FATAL_ERROR "WiFi password not set in env variable WIFI_PASSWORD") +endif() + +add_compile_definitions(WIFI_SSID="$ENV{WIFI_SSID}") +add_compile_definitions(WIFI_PASSWORD="$ENV{WIFI_PASSWORD}") + +# Room connection method +if(DEFINED ENV{LK_TOKEN} AND NOT "$ENV{LK_TOKEN}" STREQUAL "") + # Option A: Pre-generated token. + message(STATUS "Using a pre-generated token and server URL for room connection") + if(NOT DEFINED ENV{LK_SERVER_URL}) + message(FATAL_ERROR "LK_SERVER_URL is required when using a pre-generated token") + endif() + add_compile_definitions(LK_TOKEN="$ENV{LK_TOKEN}") + add_compile_definitions(LK_SERVER_URL="$ENV{LK_SERVER_URL}") +elseif(DEFINED ENV{LK_SANDBOX_ID}) + # Option B: Sandbox token server. + message(STATUS "Using a sandbox token server for room connection") + add_compile_definitions(LK_SANDBOX_ID="$ENV{LK_SANDBOX_ID}") + foreach(var LK_SANDBOX_ROOM_NAME LK_SANDBOX_PARTICIPANT_NAME) + if(DEFINED ENV{${var}}) + add_compile_definitions(${var}="$ENV{${var}}") + else() + add_compile_definitions(${var}=NULL) + endif() + endforeach() +else() + message(FATAL_ERROR + "No method for room connection is configured.\n" + "Please refer to the README.md for setup instructions." + ) +endif() + +project(voice_agent) diff --git a/examples/voice_agent/README.md b/examples/voice_agent/README.md new file mode 100644 index 0000000..5493b4b --- /dev/null +++ b/examples/voice_agent/README.md @@ -0,0 +1,73 @@ +# LiveKit Demo + +This demo showcases how to use [LiveKit](https://livekit.io) on ESP32-series chips, powered by Espressif's hardware-optimized WebRTC and media components. It demonstrates using LiveKit APIs to join a room and exchange real-time data and media. + +## Structure + +Application code under [*main/*](./main/) configures the media system and uses the LiveKit APIs to join a room (see [*livekit.h*](./components/livekit/include/livekit.h)). The API is in early development and may undergo breaking changes. + +The demo is currently configured to use the [ESP32-S3-Korvo-2](https://docs.espressif.com/projects/esp-adf/en/latest/design-guide/dev-boards/user-guide-esp32-s3-korvo-2.html) board, which features AEC to enable echo-free bidirectional audio. To configure the demo for a different board, please refer to the [*codec_board* README](../../components/codec_board/README.md). + +## Sandbox Token Server + +In production, you are responsible for generating JWT-based access tokens to authenticate users. However, to simplify setup, this demo is configured to use sandbox tokens. Create a [Sandbox Token Server](https://cloud.livekit.io/projects/p_/sandbox/templates/token-server) for your LiveKit Cloud project and take note of its ID for the next step. + +## Build + +To build and run the demo, you will need [IDF](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/index.html) release v5.4 or later installed on your system. Configure required settings and build as follows: + +### 1. Set network credentials + +Set your Wi-Fi SSID and password in your environment: +```sh +export WIFI_SSID='your_wifi_network_name' +export WIFI_PASSWORD='your_wifi_password' +``` + +### 2. Choose connection method + +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. For demonstration purposes, choose one of the following options to get the demo up and running quickly: + +#### Option A: Sandbox token server (recommended) + +Create a [Sandbox Token Server](https://cloud.livekit.io/projects/p_/sandbox/templates/token-server) for your LiveKit Cloud project, and export its ID: + +```sh +export LK_SANDBOX_ID="your-sandbox-id" +``` + +(Optional) If you would like the token to be generated with a specific room or participant name, you can do so as follows: + +```sh +export LK_SANDBOX_ROOM_NAME="Meeting" +export LK_SANDBOX_PARTICIPANT_NAME="ESP-32" +``` + +#### Option B: Pre-generated token + +Set your LiveKit server URL and pre-generated token in your environment: + +```sh +export LK_TOKEN="your-jwt-token" +export LK_SERVER_URL="wss://your-livekit-server.com" +``` + +### 3. Set target + +```sh +idf.py set-target esp32s3 +``` + +### 4. Build, flash, and monitor: + +```sh +idf.py -p YOUR_DEVICE_PATH flash monitor +``` + +#### Device path + +To determine the path for your board: + +- 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*). \ 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/README.md b/examples/voice_agent/agent/README.md new file mode 100644 index 0000000..8089565 --- /dev/null +++ b/examples/voice_agent/agent/README.md @@ -0,0 +1,65 @@ +# ESP-32 Example Agent + +A simple example agent built with [LiveKit Agents](https://docs.livekit.io/agents/) to be paired with the ESP-32 [voice chat example](../README.md). The agent is designed to demonstrate hardware interaction in response to user requests: + +1. Read CPU temperature: + +> **User:** What is the current CPU temperature? \ +> **Agent:** The CPU temperature is currently 33°C. + +```mermaid +sequenceDiagram + actor U as User + participant A as Agent + participant E as ESP-32 + autonumber + + U ->> A: "What is the current
CPU temperature?" + A ->> E: RPC invocation, get_cpu_temp + E ->> A: RPC return (e.g. "33.0") + A ->> U: "The current CPU
temperature is 33°C." +``` + +2. Control on-board LED state: + +> **User:** Turn on the blue LED. \ +> **Agent:** The blue LED is now on. + +> **User:** Turn on the yellow LED. \ +> **Agent:** I'm sorry, the board does not have a yellow LED. + +```mermaid +sequenceDiagram + actor U as User + participant A as Agent + participant E as ESP-32 + autonumber + + U ->> A: "Turn on the blue LED." + A ->> E: RPC invocation, set_led_state
{ "color": "blue", state: true } + E ->> A: RPC return + A ->> U: "The blue LED is now on." +``` + +## Usage + +For a quick introduction to LiveKit Agents, please refer to the [docs](https://docs.livekit.io/agents/). Once the agent is running, it will automatically join the same room as the ESP-32 after the device connects. Follow these steps to get up and running: + +1. Set required environment variables: + +```sh +OPENAI_API_KEY= +CARTESIA_API_KEY= +LIVEKIT_API_KEY= +LIVEKIT_API_SECRET= +LIVEKIT_URL= +``` + +2. Run agent + +```sh +python agent.py download-files +python agent.py dev +``` + +3. Launch ESP-32 voice chat example (see [README](../README.md)). 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..d5885d2 --- /dev/null +++ b/examples/voice_agent/agent/uv.lock @@ -0,0 +1,1326 @@ +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-demo-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/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/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/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/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..55a8f61 --- /dev/null +++ b/examples/voice_agent/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" "board.c" "media_setup.c" "livekit_demo.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..efe2140 --- /dev/null +++ b/examples/voice_agent/main/board.c @@ -0,0 +1,49 @@ +#include +#include "esp_log.h" +#include "codec_init.h" +#include "codec_board.h" +#include "esp_codec_dev.h" +#include "sdkconfig.h" +#include "settings.h" +#include "driver/temperature_sensor.h" + +#include "board.h" + +static const char *TAG = "board"; + +static temperature_sensor_handle_t temp_sensor = NULL; + +static void init_temp_sensor(void) +{ + 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)); +} + +void board_init() +{ + ESP_LOGI(TAG, "Initializing board"); + set_codec_board_type(TEST_BOARD_NAME); + // When using performing recording and playback at the same time, + // reuse_dev must be set to false. + codec_init_cfg_t cfg = { +#if CONFIG_IDF_TARGET_ESP32S3 + .in_mode = CODEC_I2S_MODE_TDM, + .in_use_tdm = true, +#endif + .reuse_dev = false + }; + init_codec(&cfg); + init_temp_sensor(); + + board_led_init(); + board_led_set(BOARD_LED_RED, false); + board_led_set(BOARD_LED_BLUE, false); +} + +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..d6e5326 --- /dev/null +++ b/examples/voice_agent/main/board.h @@ -0,0 +1,29 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "settings.h" +#include "media_sys.h" +#include "network.h" +#include "sys_state.h" +#include "esp_webrtc.h" + +typedef enum { + BOARD_LED_RED = 0, + BOARD_LED_BLUE = 1 +} board_led_t; + +/// @brief Initialize board +void board_init(void); + +/// @brief Read the chip's internal temperature in degrees Celsius. +float board_get_temp(void); + +/// @brief Set the state of an on-board LED. +void board_set_led_state(board_led_t led, bool state); + +#ifdef __cplusplus +} +#endif diff --git a/examples/voice_agent/main/idf_component.yml b/examples/voice_agent/main/idf_component.yml new file mode 100644 index 0000000..77e87db --- /dev/null +++ b/examples/voice_agent/main/idf_component.yml @@ -0,0 +1,12 @@ +dependencies: + idf: ">=5.0" + livekit: + path: ../../../components/livekit + codec_board: + path: ../../../components/third_party/codec_board + render_impl: + path: ../../../components/third_party/av_render/render_impl + livekit_sandbox: + path: ../../../components/livekit_sandbox + common: + path: ../../common diff --git a/examples/voice_agent/main/livekit_demo.c b/examples/voice_agent/main/livekit_demo.c new file mode 100644 index 0000000..3b5d7ca --- /dev/null +++ b/examples/voice_agent/main/livekit_demo.c @@ -0,0 +1,135 @@ +#include +#include +#include +#include "livekit_demo.h" +#include "media_setup.h" +#include "codec_init.h" +#include "board.h" +#include "cJSON.h" + +static const char *TAG = "livekit_demo"; + +static livekit_room_handle_t room_handle; + +/// @brief 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) +{ + cJSON *root = cJSON_Parse(invocation->payload); + if (!root) { + livekit_rpc_return_error("Invalid JSON"); + return; + } + 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)) { + livekit_rpc_return_error("Unexpected JSON format"); + cJSON_Delete(root); + return; + } + + const char *color = color_entry->valuestring; + bool state = cJSON_IsTrue(state_entry); + + board_led_t led; + if (strncmp(color, "red", 3) == 0) { + led = BOARD_LED_RED; + } else if (strncmp(color, "blue", 4) == 0) { + led = BOARD_LED_BLUE; + } else { + livekit_rpc_return_error("Unsupported color"); + cJSON_Delete(root); + return; + } + if (board_led_set(led, state) != 0) { + livekit_rpc_return_error("Failed to set LED state"); + cJSON_Delete(root); + return; + } + + livekit_rpc_return_ok(NULL); + cJSON_Delete(root); +} + +/// @brief 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); +} + +int join_room() +{ + 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_setup_get_capturer() + }, + .subscribe = { + .kind = LIVEKIT_MEDIA_TYPE_AUDIO, + .renderer = media_setup_get_renderer() + } + }; + + if (room_handle != NULL) { + ESP_LOGE(TAG, "Room already created"); + return -1; + } + if (livekit_room_create(&room_handle, &room_options) != LIVEKIT_ERR_NONE) { + ESP_LOGE(TAG, "Failed to create room"); + return -1; + } + + 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 LK_SANDBOX_ID + // Option A: Sandbox token server. + livekit_sandbox_res_t res = {}; + livekit_sandbox_options_t gen_options = { + .sandbox_id = LK_SANDBOX_ID, + .room_name = LK_SANDBOX_ROOM_NAME, + .participant_name = LK_SANDBOX_PARTICIPANT_NAME + }; + if (!livekit_sandbox_generate(&gen_options, &res)) { + ESP_LOGE(TAG, "Failed to generate sandbox token"); + return -1; + } + 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, LK_SERVER_URL, LK_TOKEN); +#endif + + if (connect_res != LIVEKIT_ERR_NONE) { + ESP_LOGE(TAG, "Failed to connect to room"); + return -1; + } + return 0; +} + +int leave_room() +{ + if (room_handle == NULL) { + ESP_LOGE(TAG, "Room not created"); + return -1; + } + if (livekit_room_close(room_handle) != LIVEKIT_ERR_NONE) { + ESP_LOGE(TAG, "Failed to leave room"); + return -1; + } + if (livekit_room_destroy(room_handle) != LIVEKIT_ERR_NONE) { + ESP_LOGE(TAG, "Failed to destroy room"); + return -1; + } + room_handle = NULL; + return 0; +} \ No newline at end of file diff --git a/examples/voice_agent/main/livekit_demo.h b/examples/voice_agent/main/livekit_demo.h new file mode 100644 index 0000000..c432d87 --- /dev/null +++ b/examples/voice_agent/main/livekit_demo.h @@ -0,0 +1,16 @@ + +#pragma once + +#include "livekit.h" +#include "livekit_sandbox.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int join_room(); +int leave_room(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/examples/voice_agent/main/main.c b/examples/voice_agent/main/main.c new file mode 100644 index 0000000..b0c145c --- /dev/null +++ b/examples/voice_agent/main/main.c @@ -0,0 +1,154 @@ +/* LiveKit Demo + + 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 "esp_console.h" +#include "media_lib_adapter.h" +#include "media_lib_os.h" +#include "network.h" + +#include "settings.h" +#include "media_setup.h" +#include "board.h" +#include "livekit_demo.h" + +static const char *TAG = "livekit_demo"; + +#define RUN_ASYNC(name, body) \ + void run_async##name(void *arg) \ + { \ + body; \ + media_lib_thread_destroy(NULL); \ + } \ + media_lib_thread_create_from_scheduler(NULL, #name, run_async##name, NULL); + +static int join_room_cmd(int argc, char **argv) +{ + join_room(); + return 0; +} + +static int leave_room_cmd(int argc, char **argv) +{ + RUN_ASYNC(leave, { + leave_room(); + }); + return 0; +} + +static int init_console() +{ + esp_console_repl_t *repl = NULL; + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + repl_config.prompt = "esp>"; + repl_config.task_stack_size = 10 * 1024; + repl_config.task_priority = 22; + repl_config.max_cmdline_length = 1024; + // install console REPL environment +#if CONFIG_ESP_CONSOLE_UART + esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl)); +#elif CONFIG_ESP_CONSOLE_USB_CDC + esp_console_dev_usb_cdc_config_t cdc_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&cdc_config, &repl_config, &repl)); +#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG + esp_console_dev_usb_serial_jtag_config_t usbjtag_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&usbjtag_config, &repl_config, &repl)); +#endif + + esp_console_cmd_t cmds[] = { + { + .command = "join", + .help = "Please enter a room name.\r\n", + .func = join_room_cmd + }, + { + .command = "leave", + .help = "Leave from room\n", + .func = leave_room_cmd, + } + }; + for (int i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) { + ESP_ERROR_CHECK(esp_console_cmd_register(&cmds[i])); + } + ESP_ERROR_CHECK(esp_console_start_repl(repl)); + return 0; +} + +static void thread_scheduler(const char *thread_name, media_lib_thread_cfg_t *thread_cfg) +{ + // TODO: Handle internally + if (strcmp(thread_name, "lk_pub_task") == 0 || + strcmp(thread_name, "lk_sub_task") == 0) { + thread_cfg->stack_size = 25 * 1024; + thread_cfg->priority = 18; + thread_cfg->core_id = 1; + } + if (strcmp(thread_name, "lk_stream") == 0) { + thread_cfg->stack_size = 4 * 1024; + thread_cfg->priority = 15; + thread_cfg->core_id = 1; + } + if (strcmp(thread_name, "start") == 0) { + thread_cfg->stack_size = 6 * 1024; + } + if (strcmp(thread_name, "Adec") == 0) { + thread_cfg->stack_size = 40 * 1024; + thread_cfg->priority = 10; + thread_cfg->core_id = 1; + } + if (strcmp(thread_name, "venc") == 0) { +#if CONFIG_IDF_TARGET_ESP32S3 + thread_cfg->stack_size = 20 * 1024; +#endif + thread_cfg->priority = 10; + } + + // Required for Opus + if (strcmp(thread_name, "aenc") == 0) { + thread_cfg->stack_size = 40 * 1024; + thread_cfg->priority = 10; + } + if (strcmp(thread_name, "SrcRead") == 0) { + thread_cfg->stack_size = 40 * 1024; + thread_cfg->priority = 16; + thread_cfg->core_id = 0; + } + if (strcmp(thread_name, "buffer_in") == 0) { + thread_cfg->stack_size = 6 * 1024; + thread_cfg->priority = 10; + thread_cfg->core_id = 0; + } +} + +static int network_event_handler(bool connected) +{ + // Auto-join when network is connected + if (connected) { + RUN_ASYNC(join, { + join_room(); + }); + } + return 0; +} + +void app_main(void) +{ + esp_log_level_set("*", ESP_LOG_INFO); + media_lib_add_default_adapter(); + media_lib_thread_set_schedule_cb(thread_scheduler); + board_init(); + media_setup_init(); + init_console(); + network_init(WIFI_SSID, WIFI_PASSWORD, network_event_handler); + while (1) + { + media_lib_thread_sleep(2000); + } +} diff --git a/examples/voice_agent/main/media_setup.c b/examples/voice_agent/main/media_setup.c new file mode 100644 index 0000000..84bbb06 --- /dev/null +++ b/examples/voice_agent/main/media_setup.c @@ -0,0 +1,136 @@ +/* Media system + + 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 "codec_init.h" +#include "codec_board.h" + +#include "esp_capture_path_simple.h" +#include "esp_capture_audio_enc.h" +#include "av_render.h" +#include "common.h" +#include "settings.h" +#include "media_lib_os.h" +#include "esp_timer.h" +#include "av_render_default.h" +#include "esp_audio_dec_default.h" +#include "esp_audio_enc_default.h" +#include "esp_capture_defaults.h" +#include "esp_log.h" + +#include "media_setup.h" + +#define RET_ON_NULL(ptr, v) do { \ + if (ptr == NULL) { \ + ESP_LOGE(TAG, "Memory allocate fail on %d", __LINE__); \ + return v; \ + } \ +} while (0) + +#define TAG "MEDIA_SYS" + +typedef struct { + esp_capture_path_handle_t capture_handle; + esp_capture_aenc_if_t *aud_enc; + esp_capture_audio_src_if_t *aud_src; + esp_capture_path_if_t *path_if; +} capture_system_t; + +typedef struct { + audio_render_handle_t audio_render; + av_render_handle_t player; +} player_system_t; + +static capture_system_t capture_sys; +static player_system_t player_sys; + +static int build_capture_system(void) +{ + capture_sys.aud_enc = esp_capture_new_audio_encoder(); + RET_ON_NULL(capture_sys.aud_enc, -1); + + // For S3 when use ES7210 it use TDM mode second channel is reference data + esp_capture_audio_aec_src_cfg_t codec_cfg = { + .record_handle = get_record_handle(), +#if CONFIG_IDF_TARGET_ESP32S3 + .channel = 4, + .channel_mask = 1 | 2, +#endif + }; + // capture_sys.aud_src = esp_capture_new_audio_codec_src(&codec_cfg); + capture_sys.aud_src = esp_capture_new_audio_aec_src(&codec_cfg); + RET_ON_NULL(capture_sys.aud_src, -1); + esp_capture_simple_path_cfg_t simple_cfg = { + .aenc = capture_sys.aud_enc, + }; + capture_sys.path_if = esp_capture_build_simple_path(&simple_cfg); + RET_ON_NULL(capture_sys.path_if, -1); + // Create capture system + esp_capture_cfg_t cfg = { + .sync_mode = ESP_CAPTURE_SYNC_MODE_AUDIO, + .audio_src = capture_sys.aud_src, + .capture_path = capture_sys.path_if, + }; + esp_capture_open(&cfg, &capture_sys.capture_handle); + return 0; +} + +static int build_player_system() +{ + i2s_render_cfg_t i2s_cfg = { + .play_handle = get_playback_handle(), + }; + player_sys.audio_render = av_render_alloc_i2s_render(&i2s_cfg); + if (player_sys.audio_render == NULL) { + ESP_LOGE(TAG, "Fail to create audio render"); + return -1; + } + esp_codec_dev_set_out_vol(i2s_cfg.play_handle, DEFAULT_PLAYBACK_VOL); + av_render_cfg_t render_cfg = { + .audio_render = player_sys.audio_render, + .audio_raw_fifo_size = 8 * 4096, + .audio_render_fifo_size = 100 * 1024, + .allow_drop_data = false, + }; + player_sys.player = av_render_open(&render_cfg); + if (player_sys.player == NULL) { + ESP_LOGE(TAG, "Fail to create player"); + return -1; + } + // When support AEC, reference data is from speaker right channel for ES8311 so must output 2 channel + av_render_audio_frame_info_t aud_info = { + .sample_rate = 16000, + .channel = 2, + .bits_per_sample = 16, + }; + av_render_set_fixed_frame_info(player_sys.player, &aud_info); + return 0; +} + +int media_setup_init(void) +{ + // Register default audio encoder + esp_audio_enc_register_default(); + // Register default audio decoder + esp_audio_dec_register_default(); + // Build capture system + build_capture_system(); + // Build player system + build_player_system(); + return 0; +} + +esp_capture_handle_t media_setup_get_capturer(void) +{ + return capture_sys.capture_handle; +} + +av_render_handle_t media_setup_get_renderer(void) +{ + return player_sys.player; +} \ No newline at end of file diff --git a/examples/voice_agent/main/media_setup.h b/examples/voice_agent/main/media_setup.h new file mode 100644 index 0000000..313fc21 --- /dev/null +++ b/examples/voice_agent/main/media_setup.h @@ -0,0 +1,18 @@ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int media_setup_init(void); +esp_capture_handle_t media_setup_get_capturer(void); +av_render_handle_t media_setup_get_renderer(void); + +#ifdef __cplusplus +} +#endif + diff --git a/examples/voice_agent/main/settings.h b/examples/voice_agent/main/settings.h new file mode 100644 index 0000000..d4ae952 --- /dev/null +++ b/examples/voice_agent/main/settings.h @@ -0,0 +1,49 @@ +/* General settings + + 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 "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Board name setting refer to `codec_board` README.md for more details + */ +#if CONFIG_IDF_TARGET_ESP32P4 +#define TEST_BOARD_NAME "ESP32_P4_DEV_V14" +#else +#define TEST_BOARD_NAME "S3_Korvo_V2" +#endif + +/** + * @brief Video resolution settings + */ +#if CONFIG_IDF_TARGET_ESP32P4 +#define VIDEO_WIDTH 1920 +#define VIDEO_HEIGHT 1080 +#define VIDEO_FPS 25 +#else +#define VIDEO_WIDTH 320 +#define VIDEO_HEIGHT 240 +#define VIDEO_FPS 10 +#endif + + + +/** + * @brief Set default playback volume + */ +#define DEFAULT_PLAYBACK_VOL (85) + +#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..c3bd54a --- /dev/null +++ b/examples/voice_agent/sdkconfig.defaults @@ -0,0 +1,35 @@ + +# 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 \ No newline at end of file diff --git a/examples/voice_agent/sdkconfig.defaults.esp32p4 b/examples/voice_agent/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000..b1a77c1 --- /dev/null +++ b/examples/voice_agent/sdkconfig.defaults.esp32p4 @@ -0,0 +1,33 @@ +# Flash setting +CONFIG_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y + +CONFIG_SPIRAM_SPEED_200M=y + +# If you use serial JTAG turn on this option +#CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y + +# Use camera SC2336 +CONFIG_CAMERA_SC2336=y +CONFIG_CAMERA_SC2336_MIPI_RAW10_1920x1080_25FPS_2_LANE=y + +# Enable ISP pipelines +CONFIG_ESP_VIDEO_ENABLE_ISP_PIPELINE_CONTROLLER=y + +# Set Slave target type +CONFIG_IDF_SLAVE_TARGET="esp32c6" +CONFIG_SLAVE_IDF_TARGET_ESP32C6=y + +# Enable following configuration if support C5 Slave, make sure GPIO matched +# CONFIG_SLAVE_IDF_TARGET_ESP32C5=y +# CONFIG_ESP_HOSTED_SPI_HD_HOST_INTERFACE=y +# CONFIG_ESP_SPI_HD_GPIO_CS=4 +# CONFIG_ESP_SPI_HD_GPIO_CLK=5 +# CONFIG_ESP_SPI_HD_GPIO_D0=20 +# CONFIG_ESP_SPI_HD_GPIO_D1=21 +# CONFIG_ESP_SPI_HD_GPIO_D2=22 +# CONFIG_ESP_SPI_HD_GPIO_D3=23 +# CONFIG_ESP_SPI_HD_GPIO_DATA_READY=32 +# CONFIG_ESP_SPI_HD_GPIO_RESET_SLAVE=33 +# CONFIG_ESP_SPI_HD_FREQ_ESP32XX=40 +# CONFIG_ESP_HOSTED_SPI_HD_PRIV_INTERFACE_4_DATA_LINES=y diff --git a/examples/voice_agent/sdkconfig.defaults.esp32s3 b/examples/voice_agent/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000..361f87b --- /dev/null +++ b/examples/voice_agent/sdkconfig.defaults.esp32s3 @@ -0,0 +1,20 @@ + +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