diff --git a/README_CN.md b/README_CN.md index c835ed2..61f5ca8 100644 --- a/README_CN.md +++ b/README_CN.md @@ -141,6 +141,12 @@ postgres 和 mysql 的格式即: 开启分析后,每路流占用约 200MB 内存 2 核 +注意:开启 AI 分析后,即使没有人观看,系统也会自动保持视频流不关闭以确保 AI 持续分析。 + +> 为什么开启 AI 分析后,即是没人观看,流也不会自动停止 + +ai 分析会拉取一道流,程序会以为有人观看 + > 国标设备在线,通道离线? 属于 ipc 的问题,请检查 ipc 后台注册的 平台 sip_id 和 域是否与 gowvp/owl 一致。 diff --git a/configs/config.toml b/configs/config.toml index 2905b6f..3c11b0f 100644 --- a/configs/config.toml +++ b/configs/config.toml @@ -36,24 +36,14 @@ # 录像存储根目录(相对于工作目录) StorageDir = './configs/recordings' # 录像保留天数(超过则清理) - RetainDays = 7 + RetainDays = 3 # 磁盘使用率阈值(百分比),超过则触发循环覆盖 - DiskUsageThreshold = 99.0 + DiskUsageThreshold = 95.0 # MP4 切片时长(秒) - SegmentSeconds = 60 - # 是否禁用 GB28181 通道录制(true=禁用) - DisabledGB28181 = false - # 是否禁用 RTMP 通道录制(true=禁用) - DisabledRTMP = false - # 是否禁用 RTSP 通道录制(true=禁用) - DisabledRTSP = false - # 是否禁用 ONVIF 通道录制(true=禁用) - DisabledONVIF = false + SegmentSeconds = 300 [Data] # 数据库支持 sqlite/postgres/mysql, 使用 sqlite 时 dsn 应当填写文件存储路径 - # postgres://postgres:123456@127.0.0.1:5432/gb28181?sslmode=disable - # mysql://root:123456@127.0.0.1:5432/gb28181?sslmode=disable [Data.Database] Dsn = './configs/data.db' MaxIdleConns = 10 @@ -77,9 +67,9 @@ # 服务监听的 tcp/udp 端口号 Port = 15060 # gb/t28181 20 位国标 ID - ID = '34020000002000000001' + ID = '34030000002000000003' # 域 - Domain = '3402000000' + Domain = '3403000000' # 注册密码 Password = '' @@ -93,8 +83,8 @@ # 媒体服务器类型 zlm/lalmax Type = 'zlm' # 用于流媒体 webhook 回调 - WebHookIP = '192.168.1.3' + WebHookIP = '192.168.1.5' # 媒体服务器 RTP 端口范围 RTPPortRange = '20000-20100' # 媒体服务器 SDP IP - SDPIP = '192.168.1.3' \ No newline at end of file + SDPIP = '192.168.1.5' diff --git a/internal/core/ipc/protocol.go b/internal/core/ipc/protocol.go index 29403b1..ce2a386 100644 --- a/internal/core/ipc/protocol.go +++ b/internal/core/ipc/protocol.go @@ -156,15 +156,14 @@ func (g Adapter) SaveChannels(channels []*Channel) error { currentChannelIDs = append(currentChannelIDs, channel.ChannelID) if existing, ok := existingMap[channel.ChannelID]; ok { - // 通道已存在,更新信息 + // 只更新设备上报的字段,保留用户手动配置的 EnabledAI / Zones / RecordMode _ = g.store.Channel().Edit(ctx, existing, func(c *Channel) error { c.Name = channel.Name c.IsOnline = channel.IsOnline - - // TODO: 这里需要优化,更新的时候不应该覆盖上次的存储配置 - recordMode := channel.Ext.GetRecordMode() - c.Ext = channel.Ext - c.Ext.RecordMode = recordMode + c.Ext.Manufacturer = channel.Ext.Manufacturer + c.Ext.Firmware = channel.Ext.Firmware + c.Ext.GBVersion = channel.Ext.GBVersion + c.Ext.Model = channel.Ext.Model return nil }, orm.Where("id=?", existing.ID)) } else { diff --git a/internal/web/api/ai_webhook.go b/internal/web/api/ai_webhook.go index 55ef8db..399fc39 100644 --- a/internal/web/api/ai_webhook.go +++ b/internal/web/api/ai_webhook.go @@ -163,9 +163,18 @@ func (a AIWebhookAPI) onStopped(c *gin.Context, in *AIStoppedInput) (AIWebhookOu return newAIWebhookOutputOK(), nil } -// StartAISyncLoop 启动 AI 任务同步协程,每 5 分钟检测一次数据库中 enabled_ai 状态与内存 aiTasks 的差异并同步 +// StartAISyncLoop 启动 AI 任务同步协程,启动后立即执行一次同步,之后每 5 分钟检测一次 +// 立即执行是为了在服务重启后尽快恢复之前开启的 AI 分析任务 func (a *AIWebhookAPI) StartAISyncLoop(ctx context.Context, smsCore sms.Core) { go func() { + // 延迟 30 秒再首次同步,等待设备注册和 catalog 更新完成,避免读到过期状态 + select { + case <-ctx.Done(): + return + case <-time.After(30 * time.Second): + a.syncAITasks(ctx, smsCore) + } + ticker := time.NewTicker(5 * time.Minute) defer ticker.Stop() @@ -187,9 +196,10 @@ func (a *AIWebhookAPI) syncAITasks(ctx context.Context, smsCore sms.Core) { return } - // 查询所有通道 + // 查询所有在线通道 channels, _, err := a.ipcCore.FindChannel(ctx, &ipc.FindChannelInput{ PagerFilter: web.PagerFilter{Page: 1, Size: 999}, + IsOnline: "true", }) if err != nil { a.log.ErrorContext(ctx, "sync ai tasks: find channels failed", "err", err)