1.json和配置参数传递
service.json
│
▼ service::init() 里
xcfgman_defualt().add_config(&s_config, ...)
│
▼ ServiceConfig::from_json
j["gb28181"] → Gb28181Config::from_json
│
▼ 填到 s_config.gb28181.{server_ip, server_port, auth_id, server_id, ...}
│
▼ service::start()
startGb28181Server(s_config.gb28181) // 参数 cfg 就是这些成员
│
▼ 校验 cfg.server_ip.empty() 等
gb28181::start(cfg) // 再拷贝到 g_cfg 给 SIP 用
2.start()初始化代码
start()函数里面创建了------>
gb28181_sip_config_t c{}; 临时变量,将获取得 Gb28181Config& cfg配置参数进行赋值拷贝,c_str() 指向 std::string 内部缓冲区,该缓冲区必须在整个 SIP 运行期间有效。
拷贝到 g_cfg:字符串归 gb28181 模块所有,直到 stop(),指针一直有效。
若只保存 const Gb28181Config& 指向外部的 s_config.gb28181, 上层若 setGb28181Config 改配置或 string 重分配,可能悬空。
所以这是按启动时刻做一份配置快照。
start()代码如下:
bool start(const Gb28181Config& cfg)
{
log_info("[gb28181] start !!!");
std::lock_guard<std::mutex> lock(g_mutex);
if (g_running) return true;
if (!cfg.enable) return true;
g_cfg = cfg;
const std::string local_ip = nl_get_ip();
gb28181_sip_config_t c{};
c.device_id = g_cfg.auth_id.c_str();
c.channel_id = g_cfg.channel_id.c_str();
c.server_id = g_cfg.server_id.c_str();
c.server_domain = g_cfg.server_domain.c_str();
c.server_ip = g_cfg.server_ip.c_str();
c.server_port = g_cfg.server_port;
c.auth_id = g_cfg.auth_id.c_str();
c.auth_password = g_cfg.auth_password.c_str();
c.local_ip = local_ip.c_str();
c.local_port = g_cfg.local_port;
c.register_expires = g_cfg.auth_vaild_time;
c.heartbeat_interval = g_cfg.heartbeat;
g_ctx = gb28181_sip_create(&c, on_event, on_rtp, nullptr);
if (!g_ctx) {
log_error("[gb28181] create context failed");
return false;
}
if (0 != gb28181_sip_start(g_ctx)) {
log_error("[gb28181] start failed");
gb28181_sip_destroy(g_ctx);
g_ctx = nullptr;
return false;
}
if (0 != gb28181_sip_register(g_ctx)) {
log_error("[gb28181] register request failed");
}
log_info("[gb28181] start success");
g_running = true;
g_heartbeat_thread = std::thread(heartbeat_loop);
return true;
}
.参数传递的过程:
service::startGb28181Server(s_config.gb28181)
│
▼ 传引用 cfg
gb28181::start(cfg)
│
▼ g_cfg = cfg (拷贝一份)
后续只读 g_cfg,不依赖 service 是否还持有同一份对象
g_cfg = cfg = 在启动时把配置复制到模块全局变量,给心跳线程、SIP 回调和后续拉流用,并保证 c_str() 指向的字符串在整个运行期有效。
两个结构体的区别与作用:
static gb28181_sip_t* g_ctx = nullptr;
static Gb28181Config g_cfg;
service::startGb28181Server(s_config.gb28181)
│
▼
gb28181::start(cfg)
│
├─ g_cfg = cfg ← 保存 C++ 配置(给本文件里的线程/辅助逻辑)
│
├─ 用 g_cfg 填 gb28181_sip_config_t c(临时,只传指针)
│
└─ g_ctx = gb28181_sip_create(&c, on_event, on_rtp, ...)
│ 内部会再拷贝一份字符串、建 agent/socket/线程
▼
gb28181_sip_start(g_ctx)
gb28181_sip_register(g_ctx)
启动 heartbeat_loop()(读的还是 g_cfg)
|------|-----------------------------|----------------------------------|
| 类型 | C++ 结构体(XCfg 子类) | C 实现的 SIP 上下文(不透明指针) |
| 本质 | 静态参数表(从 json 来) | 运行时对象(堆上 calloc) |
| 内容 | server_ip、auth_id、心跳间隔等配置项 | SIP agent、socket、线程、对话、RTP... |
| 谁定义 | gb28181_config.h | gb28181_sip.cpp 内部 |
| 生命周期 | 模块 static,随 start/stop 覆盖 | gb28181_sip_create → destroy |
| 主要用途 | 上层逻辑读配置(心跳周期、拼 SDP) | 调 SIP API(注册、INVITE、回 200) |
/*****************************************************************/
3.gb28181_sip_create函数
==================>跳转到gb28181_sip_create函数
struct gb28181_sip_t* gb28181_sip_create(const gb28181_sip_config_t* cfg, gb28181_event_cb on_event, gb28181_rtp_cb on_rtp, void* user)
{
gb28181_sip_t* s;
if (!cfg || !cfg->device_id || !cfg->server_ip) return NULL;
s = (gb28181_sip_t*)calloc(1, sizeof(*s));
if (!s) return NULL;
s->cfg = *cfg;
s->device_id = cfg->device_id ? strdup(cfg->device_id) : NULL;
s->channel_id = cfg->channel_id ? strdup(cfg->channel_id) : NULL;
s->server_id = cfg->server_id ? strdup(cfg->server_id) : NULL;
s->server_domain = cfg->server_domain ? strdup(cfg->server_domain) : NULL;
s->server_ip = cfg->server_ip ? strdup(cfg->server_ip) : NULL;
s->auth_id = cfg->auth_id ? strdup(cfg->auth_id) : NULL;
s->auth_password = cfg->auth_password ? strdup(cfg->auth_password) : NULL;
s->local_ip = cfg->local_ip ? strdup(cfg->local_ip) : strdup("0.0.0.0");
s->cfg.device_id = s->device_id;
s->cfg.channel_id = s->channel_id;
s->cfg.server_id = s->server_id;
s->cfg.server_domain = s->server_domain;
s->cfg.server_ip = s->server_ip;
s->cfg.auth_id = s->auth_id;
s->cfg.auth_password = s->auth_password;
s->cfg.local_ip = s->local_ip;
s->on_event = on_event;
s->on_rtp = on_rtp;
s->user = user;
s->udp = socket_invalid;
s->sip_running = 0;
// req_parser parses SIP requests, rep_parser parses SIP responses.
s->req_parser = http_parser_create(HTTP_PARSER_REQUEST, NULL, NULL);
s->rep_parser = http_parser_create(HTTP_PARSER_RESPONSE, NULL, NULL);
if (!s->req_parser || !s->rep_parser) {
gb28181_sip_destroy(s);
return NULL;
}
if (g_sip_timer_ref++ == 0) {
sip_timer_init();
}
memset(&s->uas, 0, sizeof(s->uas));
s->uas.send = uas_send_cb;
s->uas.onregister = on_register_uas;
s->uas.oninvite = on_invite_uas;
s->uas.onack = on_ack_uas;
s->uas.onprack = on_prack_uas;
s->uas.onupdate = on_update_uas;
s->uas.oninfo = on_info_uas;
s->uas.onbye = on_bye_uas;
s->uas.oncancel = on_cancel_uas;
s->uas.onsubscribe = on_subscribe_uas;
s->uas.onnotify = on_notify_uas;
s->uas.onpublish = on_publish_uas;
s->uas.onrefer = on_refer_uas;
s->uas.onmessage = on_message_uas;
s->transport.via = via_cb;
s->transport.send = send_cb;
s->pending_local_rtp_port = 0;
if (!s->device_id || !s->server_ip || !s->local_ip || !s->req_parser || !s->rep_parser) {
gb28181_sip_destroy(s);
return NULL;
}
return s;
}
1.临时 cfg 指向外部字符串
Gb28181Config g_cfg 栈上临时 c (gb28181_sip_config_t)
┌─────────────────┐ ┌───────────────────────────┐
│ server_ip: │ │ server_ip ─────────┐ │
│ std::string │◄───────┤ device_id ───────┐ │ │
│ "192.168.1.10" │ │ server_port:8160 │ │ │
│ auth_id: "35.." │◄───┐ └──────────────────┼─┼──────┘
└─────────────────┘ │ │ │
│ ▼ ▼
c_str() 只传指针,不拷内容
gb28181::start 里:c.server_ip = g_cfg.server_ip.c_str();
→ cfg 里的指针指向 g_cfg 的 string 缓冲区(不是 SIP 自己的内存)。
2.清空结构体
if (!cfg || !cfg->device_id || !cfg->server_ip) return NULL;
│
▼
s = calloc(1, sizeof(gb28181_sip_t)); // s 整块清零
堆上 gb28181_sip_t *s
┌─────────────────────────────────────┐
│ cfg (空/0) │
│ device_id NULL │
│ server_ip NULL │
│ agent, udp, dialogs ... 全是 0 │
└─────────────────────────────────────┘
3.s->cfg = *cfg:浅拷贝(指针仍指向外面)整型(server_port、local_port...)已正确在 s->cfg 里。指针只是复印了地址,还不能长期用。
外部 g_cfg / c 的字符串
┌──────────────┐
│ "192.168..." │
└──────▲───────┘
│
s->cfg │ s->device_id 等此时还是 NULL
┌────────────┼────────────────────────┐
│ server_ip ─┘ (和 cfg 里同一个地址) │ ← 危险:还指着外面
│ server_port = 8160 (数值已拷好) │
│ device_id ─────────► 外面 │
└─────────────────────────────────────┘
4.strdup:在堆上为 s 建自己的字符串:
堆(SIP 模块拥有,destroy 时 free)
s->server_ip ──────► ┌─────────────────┐
s->device_id ──────► │ "350200...01" │ 各自独立副本
s->auth_id ────────► ├─────────────────┤
s->local_ip ───────► │ "192.168.x.x" │ 或 "0.0.0.0"
└─────────────────┘
(与 g_cfg 里的 std::string 内容相同,但地址不同)
每行形如:
s->server_ip = strdup(cfg->server_ip);
│ │
│ └── 从"外面的串"读内容
└── s 自己的 char*,堆上
5.s->cfg.xxx = s->xxx:让嵌套配置指向自有内存
gb28181_sip_t *s
┌─────────────────────────────────────────────────────────┐
│ cfg (gb28181_sip_config_t,嵌在 s 里面) │
│ ┌───────────────────────────────────────────────────┐ │
│ │ server_ip ────┐ │ │
│ │ device_id ──┼─┐ │ │
│ │ server_port │ │ (int,来自 *cfg 拷贝) │ │
│ └─────────────┼─┼─────────────────────────────────┘ │
│ │ │ │
│ server_ip ◄───┘ │ 同一根指针 │
│ device_id ◄─────┘ │
│ channel_id, server_id, auth_id ... 同理 │
│ │
│ on_event, on_rtp, user │
│ udp = invalid, sip_running = 0 │
└─────────────────────────────────────────────────────────┘