websocket_protocol.cc 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #include "websocket_protocol.h"
  2. #include "board.h"
  3. #include "system_info.h"
  4. #include "application.h"
  5. #include <cstring>
  6. #include <cJSON.h>
  7. #include <esp_log.h>
  8. #include <arpa/inet.h>
  9. #include "assets/lang_config.h"
  10. #define TAG "WS"
  11. WebsocketProtocol::WebsocketProtocol() {
  12. event_group_handle_ = xEventGroupCreate();
  13. }
  14. WebsocketProtocol::~WebsocketProtocol() {
  15. if (websocket_ != nullptr) {
  16. delete websocket_;
  17. }
  18. vEventGroupDelete(event_group_handle_);
  19. }
  20. void WebsocketProtocol::Start() {
  21. }
  22. void WebsocketProtocol::SendAudio(const std::vector<uint8_t>& data) {
  23. if (websocket_ == nullptr) {
  24. return;
  25. }
  26. websocket_->Send(data.data(), data.size(), true);
  27. }
  28. void WebsocketProtocol::SendText(const std::string& text) {
  29. if (websocket_ == nullptr) {
  30. return;
  31. }
  32. if (!websocket_->Send(text)) {
  33. ESP_LOGE(TAG, "Failed to send text: %s", text.c_str());
  34. SetError(Lang::Strings::SERVER_ERROR);
  35. }
  36. }
  37. bool WebsocketProtocol::IsAudioChannelOpened() const {
  38. return websocket_ != nullptr && websocket_->IsConnected() && !error_occurred_ && !IsTimeout();
  39. }
  40. void WebsocketProtocol::CloseAudioChannel() {
  41. if (websocket_ != nullptr) {
  42. delete websocket_;
  43. websocket_ = nullptr;
  44. }
  45. }
  46. bool WebsocketProtocol::OpenAudioChannel() {
  47. if (websocket_ != nullptr) {
  48. delete websocket_;
  49. }
  50. error_occurred_ = false;
  51. std::string url = CONFIG_WEBSOCKET_URL;
  52. std::string token = "Bearer " + std::string(CONFIG_WEBSOCKET_ACCESS_TOKEN);
  53. websocket_ = Board::GetInstance().CreateWebSocket();
  54. websocket_->SetHeader("Authorization", token.c_str());
  55. websocket_->SetHeader("Protocol-Version", "1");
  56. websocket_->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
  57. websocket_->SetHeader("Client-Id", Board::GetInstance().GetUuid().c_str());
  58. websocket_->OnData([this](const char* data, size_t len, bool binary) {
  59. if (binary) {
  60. if (on_incoming_audio_ != nullptr) {
  61. on_incoming_audio_(std::vector<uint8_t>((uint8_t*)data, (uint8_t*)data + len));
  62. }
  63. } else {
  64. // Parse JSON data
  65. auto root = cJSON_Parse(data);
  66. auto type = cJSON_GetObjectItem(root, "type");
  67. if (type != NULL) {
  68. if (strcmp(type->valuestring, "hello") == 0) {
  69. ParseServerHello(root);
  70. } else {
  71. if (on_incoming_json_ != nullptr) {
  72. on_incoming_json_(root);
  73. }
  74. }
  75. } else {
  76. ESP_LOGE(TAG, "Missing message type, data: %s", data);
  77. }
  78. cJSON_Delete(root);
  79. }
  80. last_incoming_time_ = std::chrono::steady_clock::now();
  81. });
  82. websocket_->OnDisconnected([this]() {
  83. ESP_LOGI(TAG, "Websocket disconnected");
  84. if (on_audio_channel_closed_ != nullptr) {
  85. on_audio_channel_closed_();
  86. }
  87. });
  88. if (!websocket_->Connect(url.c_str())) {
  89. ESP_LOGE(TAG, "Failed to connect to websocket server");
  90. SetError(Lang::Strings::SERVER_NOT_FOUND);
  91. return false;
  92. }
  93. // Send hello message to describe the client
  94. // keys: message type, version, audio_params (format, sample_rate, channels)
  95. std::string message = "{";
  96. message += "\"type\":\"hello\",";
  97. message += "\"version\": 1,";
  98. message += "\"transport\":\"websocket\",";
  99. message += "\"audio_params\":{";
  100. message += "\"format\":\"opus\", \"sample_rate\":16000, \"channels\":1, \"frame_duration\":" + std::to_string(OPUS_FRAME_DURATION_MS);
  101. message += "}}";
  102. websocket_->Send(message);
  103. // Wait for server hello
  104. EventBits_t bits = xEventGroupWaitBits(event_group_handle_, WEBSOCKET_PROTOCOL_SERVER_HELLO_EVENT, pdTRUE, pdFALSE, pdMS_TO_TICKS(10000));
  105. if (!(bits & WEBSOCKET_PROTOCOL_SERVER_HELLO_EVENT)) {
  106. ESP_LOGE(TAG, "Failed to receive server hello");
  107. SetError(Lang::Strings::SERVER_TIMEOUT);
  108. return false;
  109. }
  110. if (on_audio_channel_opened_ != nullptr) {
  111. on_audio_channel_opened_();
  112. }
  113. return true;
  114. }
  115. void WebsocketProtocol::ParseServerHello(const cJSON* root) {
  116. auto transport = cJSON_GetObjectItem(root, "transport");
  117. if (transport == nullptr || strcmp(transport->valuestring, "websocket") != 0) {
  118. ESP_LOGE(TAG, "Unsupported transport: %s", transport->valuestring);
  119. return;
  120. }
  121. auto audio_params = cJSON_GetObjectItem(root, "audio_params");
  122. if (audio_params != NULL) {
  123. auto sample_rate = cJSON_GetObjectItem(audio_params, "sample_rate");
  124. if (sample_rate != NULL) {
  125. server_sample_rate_ = sample_rate->valueint;
  126. }
  127. }
  128. xEventGroupSetBits(event_group_handle_, WEBSOCKET_PROTOCOL_SERVER_HELLO_EVENT);
  129. }