GB28181启动传参的设计

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_createdestroy |
| 主要用途 | 上层逻辑读配置(心跳周期、拼 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                         │
  └─────────────────────────────────────────────────────────┘
相关推荐
剑神一笑1 小时前
Linux systemctl 服务管理命令:从 systemd 架构到实战技巧
linux·服务器·架构
iNeuOS工业互联网1 小时前
iNeuOS_AiInsight·数智灵鉴(Text2SQL/NL2SQL自然语言大模型智能问数),免费下载试用
大数据·数据库·人工智能·智能制造·工业互联网·ineuos
数据库小学妹1 小时前
分布式数据库选型实战:Share-Nothing、Share-Disk、Share-Storage三种架构对比
数据库·经验分享·分布式·架构·dba
艾莉丝努力练剑1 小时前
【Linux网络】传输层协议TCP(六)补充 - 面试题:HTTP 获取网页的完整过程
linux·运维·网络·tcp/ip·计算机网络·http·udp
小马爱打代码1 小时前
基于Redis发布订阅实现轻量级多级缓存方案
数据库·redis·缓存
Leon-Ning Liu1 小时前
【真实经验分享】ORA-03113 ORA-7445[evaopn3()+240]根因定位:从通信中断到内核空指针崩溃的完整排查实录
数据库
norsd1 小时前
CentOS Rocky Linux 设置 ip
linux·tcp/ip·centos
青春之我_XP1 小时前
深度解析 SQL 经典面试题:如何优雅地计算连续登录天数?
数据库·sql·mysql
nnsix1 小时前
Unity 自定义包的 package.json 简单写法
java·服务器·前端