2026-02-24 15:57:32 +08:00

791 lines
27 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (2025) Beijing Volcano Engine Technology Ltd.
// SPDX-License-Identifier: Apache-2.0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <stdbool.h>
#include <cJSON.h>
#include <esp_heap_caps.h>
// 确保在包含volc_rtc.h之前定义ENABLE_RTC_MODE
#ifndef ENABLE_RTC_MODE
#define ENABLE_RTC_MODE
#endif
#include "VolcEngineRTCLite.h"
#include "volc_rtc.h"
#include "volc_platform.h"
#include "util/volc_list.h"
#include "util/volc_log.h"
#include "util/volc_json.h"
#include "base/volc_base.h"
#include "base/volc_device_manager.h"
// 移除多余的ENABLE_RTC_MODE条件编译块因为它已经在头文件中处理
#define MAGIC_CONTROL "ctrl"
#define MAGIC_CONV "conv"
#define MAGIC_LENGTH 4
#define MAGIC_OFFSET 8
const char* interrupt_str = "{\"Command\":\"interrupt\"}";
typedef struct {
bool b_pipeline_started;
bool b_user_joined;
bool b_channel_joined;
bool b_first_keyframe_received;
bool b_fini;
bool b_audio_publish;
bool b_video_publish;
bool b_audio_subscribe;
bool b_video_subscribe;
char* p_appid;
char* p_channel_name;
char* p_user_id;
char* p_remote_user_id;
char* p_token;
void* context;
int audio_codec;
volc_room_info_t info;
volc_msg_cb message_callback;
volc_data_cb data_callback;
byte_rtc_engine_t rtc;
byte_rtc_event_handler_t event_handler;
} rtc_impl_t;
static bool __is_first_keyframe_not_received(rtc_impl_t* rtc, int is_key_frame)
{
return (!rtc->b_first_keyframe_received && !is_key_frame);
}
static int _build_binary_message(const char* magic, const char* message,
uint8_t** out_buf, size_t* out_len) {
size_t magic_len = strlen(magic);
size_t msg_len = strlen(message);
// 分配内存:魔术字 + 4字节长度 + 消息内容
*out_len = magic_len + 4 + msg_len;
*out_buf = (uint8_t*)hal_malloc(*out_len);
if (!*out_buf) {
LOGE("hal_malloc failed");
return -1;
}
// 填充魔术字
memcpy(*out_buf, magic, magic_len);
// 以大端序填充长度
uint8_t* len_ptr = *out_buf + magic_len;
len_ptr[0] = (uint8_t)((msg_len >> 24) & 0xFF); // 最高位字节
len_ptr[1] = (uint8_t)((msg_len >> 16) & 0xFF);
len_ptr[2] = (uint8_t)((msg_len >> 8) & 0xFF);
len_ptr[3] = (uint8_t)(msg_len & 0xFF); // 最低位字节
// 填充消息内容
memcpy(*out_buf + magic_len + 4, message, msg_len);
return 0;
}
static int __rtc_start(rtc_impl_t* rtc, volc_rtc_option_t* option)
{
byte_rtc_room_options_t room_opt = {0};
if (!rtc || !option) {
LOGE("rtc or option is NULL");
return -1;
}
if (!option->p_channel_name || !option->p_uid || !option->p_token) {
LOGE("invalid rtc option: channel(%p) uid(%p) token(%p)", option->p_channel_name, option->p_uid, option->p_token);
return -1;
}
if (strlen(option->p_channel_name) == 0 || strlen(option->p_uid) == 0 || strlen(option->p_token) == 0) {
LOGE("invalid rtc option: empty field");
return -1;
}
room_opt.auto_publish_audio = rtc->b_audio_publish;
room_opt.auto_publish_video = rtc->b_video_publish;
room_opt.auto_subscribe_audio = rtc->b_audio_subscribe;
room_opt.auto_subscribe_video = rtc->b_video_subscribe;
// 记录内存使用情况
size_t heap_free_before = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
size_t spiram_free_before = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
LOGI("Joining channel: %s, uid: %s, token: %s, vpub: %d, vsub: %d, apub: %d, asub: %d",
option->p_channel_name, option->p_uid, option->p_token,
(int)room_opt.auto_publish_video, (int)room_opt.auto_subscribe_video,
(int)room_opt.auto_publish_audio, (int)room_opt.auto_subscribe_audio);
LOGI("Memory before byte_rtc_join_room - Heap: %u bytes, SPIRAM: %u bytes",
(unsigned)heap_free_before, (unsigned)spiram_free_before);
int ret = byte_rtc_join_room(rtc->rtc, option->p_channel_name, option->p_uid, option->p_token, &room_opt);
// 记录内存使用情况
size_t heap_free_after = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
size_t spiram_free_after = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
LOGI("Memory after byte_rtc_join_room - Heap: %u bytes (change: %d), SPIRAM: %u bytes (change: %d)",
(unsigned)heap_free_after, (int)(heap_free_after - heap_free_before),
(unsigned)spiram_free_after, (int)(spiram_free_after - spiram_free_before));
if (ret != 0) {
LOGE("Failed to join room: %d", ret);
return ret;
}
rtc->b_pipeline_started = true;
rtc->p_channel_name = strdup(option->p_channel_name);
if (!rtc->p_channel_name) {
LOGE("Failed to allocate memory for channel name");
return -1;
}
rtc->p_user_id = strdup(option->p_uid);
if (!rtc->p_user_id) {
LOGE("Failed to allocate memory for user ID");
HAL_SAFE_FREE(rtc->p_channel_name);
return -1;
}
return 0;
}
static void __rtc_stop(rtc_impl_t* rtc)
{
if (!rtc) {
LOGE("rtc instance is NULL");
return;
}
if (!rtc->b_pipeline_started) {
LOGI("pipeline not started");
return;
}
int ret = byte_rtc_leave_room(rtc->rtc, rtc->p_channel_name);
if (ret != 0) {
LOGE("Failed to leave room: %d", ret);
return;
}
rtc->b_pipeline_started = false;
HAL_SAFE_FREE(rtc->p_channel_name);
return;
}
// static void __send_message_2_user(rtc_impl_t* rtc, volc_msg_t* msg)
// {
// if (rtc->message_callback) {
// rtc->message_callback(rtc->context, msg);
// }
// }
static void __send_data_2_user(rtc_impl_t* rtc, const void* data, int data_len, volc_data_info_t* info) {
// 添加详细的参数验证,确保回调函数的安全调用
if (!rtc) {
LOGE("__send_data_2_user: rtc is NULL");
return;
}
if (!rtc->data_callback) {
// 没有注册回调函数,这是正常情况,不记录错误
return;
}
if (!data) {
LOGE("__send_data_2_user: data is NULL");
return;
}
if (data_len <= 0) {
LOGE("__send_data_2_user: data_len is <= 0");
return;
}
if (!info) {
LOGE("__send_data_2_user: info is NULL");
return;
}
// 调用数据回调函数
rtc->data_callback(rtc->context, data, data_len, info);
}
// static void _register_message_router(rtc_impl_t* rtc, volc_msg_cb callback)
// {
// rtc->message_callback = callback;
// }
static void _send_message_2_user(rtc_impl_t* rtc, volc_msg_t* msg)
{
if (rtc->message_callback) {
rtc->message_callback(rtc->context, msg);
}
}
static bool _is_target_message(const uint8_t* message, int size, const char* target) {
if (message == NULL || target == NULL || size < MAGIC_LENGTH) {
return false;
}
return memcmp(message, target, MAGIC_LENGTH) == 0;
}
static int _on_conversion_status_message_parsed(const char* json, int len) {
int c = -1;
char* buf = (char*)hal_malloc(len + 1);
if (!buf) return c;
memcpy(buf, json, len);
buf[len] = '\0';
cJSON *root = cJSON_Parse(buf);
if (root != NULL) {
volc_json_read_int(root, "Stage.Code", &c);
cJSON_Delete(root);
}
hal_free(buf);
return c;
}
static void _on_join_channel_success(byte_rtc_engine_t engine, const char* channel, int elapsed_ms, bool rejoin)
{
volc_msg_t msg = {0};
LOGI("join channel success %s elapsed %d ms\n", channel, elapsed_ms);
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
rtc->b_first_keyframe_received = false;
rtc->b_channel_joined = true;
msg.code = VOLC_MSG_CONNECTED;
_send_message_2_user(rtc, &msg);
};
static void _on_user_joined(byte_rtc_engine_t engine, const char* channel, const char* user_name, int elapsed_ms)
{
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
volc_msg_t msg = {0};
LOGI("remote user joined %s:%s elapsed %d ms\n", channel, user_name, elapsed_ms);
rtc->b_user_joined = true;
// 先释放旧的内存
HAL_SAFE_FREE(rtc->p_remote_user_id);
// 分配新内存并检查结果
char* temp = strdup(user_name);
if (temp) {
rtc->p_remote_user_id = temp;
} else {
LOGE("Failed to allocate memory for remote user ID");
}
msg.code = VOLC_MSG_USER_JOINED;
_send_message_2_user(rtc, &msg);
};
static void _on_user_offline(byte_rtc_engine_t engine, const char* channel, const char* user_name, int reason)
{
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
volc_msg_t msg = {0};
LOGI("remote user offline %s:%s reason %d\n", channel, user_name, reason);
rtc->b_user_joined = false;
// 清理远程用户ID避免内存泄漏
HAL_SAFE_FREE(rtc->p_remote_user_id);
msg.code = VOLC_MSG_USER_OFFLINE;
_send_message_2_user(rtc, &msg);
};
static void _on_user_mute_audio(byte_rtc_engine_t engine, const char* channel, const char* user_name, int muted)
{
LOGD("remote user mute audio %s:%s %d\n", channel, user_name, muted);
};
static void _on_user_mute_video(byte_rtc_engine_t engine, const char* channel, const char* user_name, int muted)
{
LOGD("remote user mute video %s:%s %d\n", channel, user_name, muted);
};
static void _on_audio_data(byte_rtc_engine_t engine, const char* channel, const char* user_name, uint16_t sent_ts, audio_data_type_e data_type,
const void* data_ptr, size_t data_len, const uint8_t* extra_info, size_t extra_info_size)
{
// 添加详细的参数验证,防止空指针和无效数据导致崩溃
if (!engine) {
LOGE("_on_audio_data: engine is NULL");
return;
}
if (!channel) {
LOGE("_on_audio_data: channel is NULL");
return;
}
if (!user_name) {
LOGE("_on_audio_data: user_name is NULL");
return;
}
if (!data_ptr) {
LOGE("_on_audio_data: data_ptr is NULL");
return;
}
if (data_len == 0) {
LOGE("_on_audio_data: data_len is 0");
return;
}
volc_data_info_t info = {0};
rtc_impl_t* rtc = (rtc_impl_t*)byte_rtc_get_user_data(engine);
if (NULL == rtc) {
LOGE("_on_audio_data: rtc instance is NULL");
return;
}
if (!rtc->b_pipeline_started) {
LOGE("_on_audio_data: pipeline not started");
return;
}
if (!rtc->b_channel_joined) {
LOGE("_on_audio_data: channel not joined");
return;
}
if (!rtc->b_user_joined) {
LOGE("_on_audio_data: user not joined");
return;
}
info.type = VOLC_DATA_TYPE_AUDIO;
info.info.audio.data_type = (volc_audio_data_type_e) data_type;
// info.info.audio.sent_ts = sent_ts;
// 限制数据大小,防止内存溢出
if (data_len > 16384) { // 16KB的合理限制
LOGW("_on_audio_data: data_len too large (%zu), truncating to 16KB", data_len);
data_len = 16384;
}
__send_data_2_user(rtc, data_ptr, data_len, &info);
};
static void _on_video_data(byte_rtc_engine_t engine, const char* channel, const char* user_name, uint16_t sent_ts, video_data_type_e codec,
int is_key_frame, const void* data_ptr, size_t data_len)
{
volc_data_info_t info = {0};
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
if (NULL == rtc || !rtc->b_pipeline_started || !rtc->b_channel_joined || !rtc->b_user_joined) {
LOGD("pipeline not started or channel not joined or user not joined");
return;
}
if (__is_first_keyframe_not_received(rtc, is_key_frame)) {
LOGD("first keyframe not received, request key frame");
byte_rtc_request_video_key_frame(rtc->rtc, channel, user_name);
return;
}
rtc->b_first_keyframe_received = true;
info.type = VOLC_DATA_TYPE_VIDEO;
info.info.video.data_type = (volc_video_data_type_e) codec;
__send_data_2_user(rtc, data_ptr, data_len, &info);
};
static void _on_channel_error(byte_rtc_engine_t engine, const char* channel, int code, const char* msg)
{
volc_msg_t msg_data = {0};
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
LOGE("channel error %s:%d %s\n", channel, code, msg ? msg : "");
LOGI("channel error heap_free=%u", (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
msg_data.code = code;
char* copied = NULL;
if (msg) {
size_t len = strnlen(msg, 1024);
copied = (char*)hal_malloc(len + 1);
if (copied) {
memcpy(copied, msg, len);
copied[len] = '\0';
msg_data.data.msg = copied;
}
}
_send_message_2_user(rtc, &msg_data);
if (copied) {
hal_free(copied);
}
};
static void _on_global_error(byte_rtc_engine_t engine, int code, const char* message)
{
volc_msg_t msg_data = {0};
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
rtc->b_channel_joined = false;
rtc->b_first_keyframe_received = false;
LOGI("global error %d %s\n", code, message);
LOGI("global error heap_free=%u", (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
msg_data.code = VOLC_MSG_DISCONNECTED;
_send_message_2_user(rtc, &msg_data);
};
static void _on_key_frame_gen_req(byte_rtc_engine_t engine, const char* channel, const char* user_name)
{
LOGI("remote req key frame %s:%s\n", channel, user_name);
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
volc_msg_t msg_data = {0};
msg_data.code = VOLC_MSG_KEY_FRAME_REQ;
_send_message_2_user(rtc, &msg_data);
};
static void _on_target_bitrate_changed(byte_rtc_engine_t engine, const char* channel, uint32_t target_bps)
{
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
volc_msg_t msg_data = {0};
LOGD("target bitrate changed %s %d bps\n", channel, (int)target_bps);
// TODO: do not send it to user
msg_data.code = VOLC_MSG_TARGET_BITRATE_CHANGED;
msg_data.data.target_bitrate = target_bps;
_send_message_2_user(rtc, &msg_data);
};
static void _on_token_privilege_will_expire(byte_rtc_engine_t engine, const char* token)
{
LOGI("\ntoken privilege will expire %s", token);
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
volc_msg_t msg_data = {0};
msg_data.code = VOLC_MSG_TOKEN_EXPIRED;
// TODO: token?
_send_message_2_user(rtc, &msg_data);
};
static void _on_message_received(byte_rtc_engine_t engine, const char* channel_name, const char* src, const uint8_t* message, int size, bool binary)
{
int ret = 0;
volc_msg_t msg = { 0 };
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
volc_data_info_t info = {0};
info.type = VOLC_DATA_TYPE_MESSAGE;
info.info.message.is_binary = binary;
LOGI("message received channel=%s src=%s size=%d binary=%d free_heap=%u", channel_name, src, size, binary, (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
if (NULL == rtc || !rtc->b_pipeline_started || !rtc->b_channel_joined || !rtc->b_user_joined) {
LOGE("pipeline not started or channel not joined or user not joined");
return;
}
if (_is_target_message(message, size, MAGIC_CONV)) {
if (size < MAGIC_OFFSET) {
LOGW("conv message too short: %d", size);
return;
}
int json_len = (message[4] << 24) | (message[5] << 16) | (message[6] << 8) | (message[7]);
if (json_len <= 0 || size < (MAGIC_OFFSET + json_len)) {
LOGW("conv message invalid length: %d size: %d", json_len, size);
return;
}
const char* json_ptr = (const char*)(message + MAGIC_OFFSET);
ret = _on_conversion_status_message_parsed(json_ptr, json_len);
msg.code = VOLC_MSG_CONV_STATUS;
msg.data.conv_status = ret;
_send_message_2_user(rtc, &msg);
return;
}
__send_data_2_user(rtc, message, size, &info);
};
static void _on_message_send_result(byte_rtc_engine_t engine, const char* channel_name, int64_t msgid, int error, const char* extencontent)
{
LOGD("----------------------->MessageSendResult msg id %" PRId64 ", error %d, extencontent %s \n", msgid, error, extencontent);
};
static void _on_license_will_expire(byte_rtc_engine_t engine, int daysleft)
{
LOGI("----------------------->license will expire in %d days \n", daysleft);
volc_msg_t msg_data = {0};
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
msg_data.code = VOLC_MSG_LICENSE_EXPIRED;
_send_message_2_user(rtc, &msg_data);
};
static void _on_fini_notify(byte_rtc_engine_t engine)
{
rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine);
rtc->b_fini = true;
}
static int __rtc_init(rtc_impl_t* engine, cJSON* p_config)
{
int ret = 0;
int video_codec = 0;
int log_level = 0;
int params_cnt = 0;
cJSON* p_params = NULL;
// 初始化结构体中的event_handler字段避免局部变量导致的悬挂指针问题
// 初始化结构体中的event_handler字段避免局部变量导致的悬挂指针问题
memset(&engine->event_handler, 0, sizeof(byte_rtc_event_handler_t));
engine->event_handler.on_global_error = _on_global_error;
engine->event_handler.on_join_room_success = _on_join_channel_success;
engine->event_handler.on_room_error = _on_channel_error;
engine->event_handler.on_user_joined = _on_user_joined;
engine->event_handler.on_user_offline = _on_user_offline;
engine->event_handler.on_user_mute_audio = _on_user_mute_audio;
engine->event_handler.on_user_mute_video = _on_user_mute_video;
engine->event_handler.on_audio_data = _on_audio_data;
engine->event_handler.on_video_data = _on_video_data;
engine->event_handler.on_key_frame_gen_req = _on_key_frame_gen_req;
engine->event_handler.on_target_bitrate_changed = _on_target_bitrate_changed;
engine->event_handler.on_token_privilege_will_expire = _on_token_privilege_will_expire;
engine->event_handler.on_message_received = _on_message_received;
engine->event_handler.on_message_send_result = _on_message_send_result;
engine->event_handler.on_license_expire_warning = _on_license_will_expire;
engine->event_handler.on_fini_notify = _on_fini_notify;
ret = volc_json_read_int(p_config, "audio.codec", &engine->audio_codec);
if (ret != 0 || engine->audio_codec < 0 || engine->audio_codec > AUDIO_CODEC_TYPE_G711U) {
LOGE("volc_rtc_create: read audio_codec failed");
return -1;
}
ret = volc_json_read_int(p_config, "video.codec", &video_codec);
if (ret != 0 || video_codec < 0 || video_codec > VIDEO_CODEC_TYPE_BYTEVC1) {
LOGE("volc_rtc_create: read video_codec failed");
return -1;
}
ret = volc_json_read_int(p_config, "log_level", &log_level);
if (ret != 0) {
log_level = BYTE_RTC_LOG_LEVEL_WARN; // default log level
}
ret = volc_json_read_bool(p_config, "audio.publish", &engine->b_audio_publish);
if (ret != 0) {
engine->b_audio_publish = true; // default to publish audio
}
ret = volc_json_read_bool(p_config, "video.publish", &engine->b_video_publish);
if (ret != 0) {
engine->b_video_publish = false; // default to no publish video
}
ret = volc_json_read_bool(p_config, "audio.subscribe", &engine->b_audio_subscribe);
if (ret != 0) {
engine->b_audio_subscribe = true; // default to subscribe audio
}
ret = volc_json_read_bool(p_config, "video.subscribe", &engine->b_video_subscribe);
if (ret != 0) {
engine->b_video_subscribe = false; // default to no subscribe video
}
engine->rtc = byte_rtc_create(engine->p_appid, &engine->event_handler);
if (!engine->rtc) {
LOGE("volc_rtc_create: byte_rtc_create failed");
return -1;
}
byte_rtc_set_user_data(engine->rtc, (void*) engine);
byte_rtc_set_log_level(engine->rtc, log_level);
ret = volc_json_read_object(p_config, "params", &p_params);
if (0 == ret) {
params_cnt = cJSON_GetArraySize(p_params);
if (params_cnt > 0) {
for (int i = 0; i < params_cnt; i++) {
cJSON* p_param = cJSON_GetArrayItem(p_params, i);
if (p_param && cJSON_IsString(p_param)) {
const char* param_str = cJSON_GetStringValue(p_param);
if (param_str && strlen(param_str) > 0) {
byte_rtc_set_params(engine->rtc, param_str);
LOGI("volc_rtc_create: set param[%d]: %s", i, param_str);
}
}
}
}
cJSON_Delete(p_params);
}
int init_ret = byte_rtc_init(engine->rtc);
if (init_ret != 0) {
LOGE("volc_rtc_create: byte_rtc_init failed with ret=%d", init_ret);
return -1;
}
byte_rtc_set_audio_codec(engine->rtc, engine->audio_codec);
byte_rtc_set_video_codec(engine->rtc, video_codec - 1); // -1 for default codec
return 0;
}
volc_rtc_t volc_rtc_create(const char* appid, void* context, cJSON* p_config, volc_msg_cb message_callback, volc_data_cb data_callback)
{
rtc_impl_t* rtc = (rtc_impl_t*) hal_calloc(1, sizeof(rtc_impl_t));
if (!rtc) {
LOGE("volc_rtc_create: malloc rtc failed");
return NULL;
}
rtc->message_callback = message_callback;
rtc->data_callback = data_callback;
rtc->context = context;
// 检查appid是否为空如果为空则使用默认值
const char* actual_appid = appid;
if (!appid || strlen(appid) == 0) {
LOGW("appid is empty, using default value");
actual_appid = "default_appid";
}
rtc->p_appid = strdup(actual_appid);
if (NULL == rtc->p_appid) {
LOGE("malloc appid memory failed");
goto err_out_label;
}
if (__rtc_init(rtc, p_config) != 0) {
LOGE("volc_rtc_create: rtc init failed");
goto err_out_label;
}
LOGD("rtc create success");
return (volc_rtc_t)rtc;
err_out_label:
HAL_SAFE_FREE(rtc->p_appid);
HAL_SAFE_FREE(rtc);
return NULL;
}
void volc_rtc_destroy(volc_rtc_t handle)
{
rtc_impl_t* rtc = (rtc_impl_t*)handle;
if (!rtc) {
LOGE("rtc instance is NULL");
return;
}
__rtc_stop(rtc);
byte_rtc_fini(rtc->rtc);
while (!rtc->b_fini) {
usleep(1000 * 10);
}
byte_rtc_destroy(rtc->rtc);
HAL_SAFE_FREE(rtc->p_channel_name);
HAL_SAFE_FREE(rtc->p_user_id);
HAL_SAFE_FREE(rtc->p_remote_user_id);
HAL_SAFE_FREE(rtc->p_token);
HAL_SAFE_FREE(rtc);
LOGD("rtc destroy success");
}
// 新增extra_params 用于传递额外的AgentConfig配置参数
int volc_rtc_start(volc_rtc_t rtc, const char* bot_id, volc_iot_info_t* iot_info, const char* extra_params) {
// int ret = 0;
volc_rtc_option_t *opt = NULL;
rtc_impl_t* rtc_impl = (rtc_impl_t*) rtc;
if (!rtc_impl) {
LOGE("rtc instance is NULL");
return -1;
}
if (!iot_info) {
LOGE("iot_info is NULL");
return -1;
}
// 检查bot_id是否为空如果为空则使用默认值
const char* actual_bot_id = bot_id;
if (!bot_id || strlen(bot_id) == 0) {
LOGW("bot_id is empty, using default value");
actual_bot_id = "default_bot";
}
char* task_id = "test";
LOGI("volc_rtc_start: bot_id=%s audio_codec=%d heap_free=%u", actual_bot_id, rtc_impl->audio_codec, (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
// 新增extra_params 用于传递额外的AgentConfig配置参数
if (volc_get_rtc_config(iot_info, rtc_impl->audio_codec, actual_bot_id, task_id, extra_params, &rtc_impl->info)) {
LOGE("get rtc config failed");
return -1;
}
LOGI("volc_get_rtc_config success heap_free=%u", (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
opt = &rtc_impl->info.rtc_opt;
return __rtc_start(rtc_impl, opt);
}
int volc_rtc_stop(volc_rtc_t handle) {
rtc_impl_t* rtc = (rtc_impl_t *)handle;
if (!rtc) {
LOGE("rtc instance is NULL");
return -1;
}
__rtc_stop(rtc);
return 0;
}
int volc_rtc_send(volc_rtc_t handle, const void* data, int size, volc_data_info_t* data_info) {
rtc_impl_t* rtc = (rtc_impl_t *)handle;
audio_frame_info_t audio_info = {0};
video_frame_info_t video_info = {0};
if (NULL == rtc || NULL == data || NULL == data_info) {
LOGE("input args is invalid, rtc(%p), data(%p), data_info(%p)", rtc, data, data_info);
return -1;
}
if (rtc->p_channel_name == NULL || size <= 0) {
LOGE("channel not ready or invalid size");
return -1;
}
LOGD("volc_rtc_send type=%d size=%d heap_free=%u", data_info->type, size, (unsigned)heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
switch (data_info->type) {
case VOLC_DATA_TYPE_AUDIO: {
audio_info.data_type = (audio_data_type_e) data_info->info.audio.data_type;
byte_rtc_send_audio_data(rtc->rtc, rtc->p_channel_name, data, size, &audio_info);
break;
}
case VOLC_DATA_TYPE_VIDEO: {
video_info.data_type = (video_data_type_e) data_info->info.video.data_type;
video_info.stream_type = VIDEO_STREAM_HIGH;
video_info.frame_type = VIDEO_FRAME_AUTO_DETECT;
LOGD("Sending video to channel: %s, data length: %d, data type: %d", rtc->p_channel_name, size, video_info.data_type);
byte_rtc_send_video_data(rtc->rtc, rtc->p_channel_name, data, size, &video_info);
break;
}
case VOLC_DATA_TYPE_MESSAGE: {
if (rtc->p_remote_user_id == NULL) {
LOGW("skip message: remote user not joined");
return -1;
}
LOGD("Sending message to channel: %s, remote user: %s, data length: %d, is_binary: %d",
rtc->p_channel_name, rtc->p_remote_user_id, size, data_info->info.message.is_binary);
byte_rtc_rts_send_message(rtc->rtc, rtc->p_channel_name, rtc->p_remote_user_id, data, size, data_info->info.message.is_binary,
RTS_MESSAGE_RELIABLE);
break;
}
default:
LOGW("unsupported data type: %d", data_info->type);
return -1;
}
return 0;
}
int volc_rtc_interrupt(volc_rtc_t rtc) {
int ret = 0;
uint8_t* msg_ctrl = NULL;
size_t msg_len = 0;
rtc_impl_t* rtc_impl = (rtc_impl_t*) rtc;
volc_data_info_t data_info = {0};
if (!rtc_impl) {
LOGE("rtc instance is NULL");
return -1;
}
if (!rtc_impl->b_user_joined || rtc_impl->p_remote_user_id == NULL) {
LOGW("interrupt skipped: remote user not joined");
return -1;
}
if (_build_binary_message(MAGIC_CONTROL, interrupt_str, &msg_ctrl, &msg_len) != 0) {
LOGE("build control message failed");
return -1;
}
data_info.type = VOLC_DATA_TYPE_MESSAGE;
data_info.info.message.is_binary = true;
if ((ret = volc_rtc_send(rtc, msg_ctrl, msg_len, &data_info)) != 0) {
LOGE("send interrupt message failed");
}
HAL_SAFE_FREE(msg_ctrl);
return ret;
}
int volc_rtc_send_jpg(volc_rtc_t rtc, void* data, int size) {
return 0;
}