Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.codex-cache/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ docker run -it -p 1935:1935 -p 8080:8080 -p 4433:4433 -p 5544:5544 -p 8083:8083

# 架构

![图片](image/init.png)
![图片](document/images/init.png)

# 支持的协议
## 推流
Expand Down
64 changes: 0 additions & 64 deletions conf/config_test.go

This file was deleted.

15 changes: 12 additions & 3 deletions conf/lalmax.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,19 @@
"secrets": []
}
},
"httpfmp4_config": {
"enable": true
"fmp4_config": {
"http": {
"enable": true
},
"hls": {
"enable": true,
"segment_count": 7,
"segment_duration": 1,
"part_duration": 200,
"low_latency": false
}
},
"hook_config": {
"logic_config": {
"gop_cache_num": 1,
"single_gop_max_frame_num": 0
},
Expand Down
45 changes: 37 additions & 8 deletions conf/config.go → config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ type Config struct {
SrtConfig SrtConfig `json:"srt_config"` // srt配置
RtcConfig RtcConfig `json:"rtc_config"` // rtc配置
HttpConfig HttpConfig `json:"http_config"` // http/https配置
HttpFmp4Config HttpFmp4Config `json:"httpfmp4_config"` // http-fmp4配置
HlsConfig HlsConfig `json:"hls_config"` // hls-fmp4/llhls配置
Fmp4Config Fmp4Config `json:"fmp4_config"` // fmp4配置
GB28181Config GB28181Config `json:"gb28181_config"` // gb28181配置
ServerId string `json:"server_id"` // http 通知唯一标识
HttpNotifyConfig HttpNotifyConfig `json:"http_notify"` // http 通知配置
LalSvrConfigPath string `json:"lal_config_path"` // lal配置文件路径,兼容旧版配置
HookConfig HookConfig `json:"hook_config"` // gop cache配置
LogicConfig LogicConfig `json:"logic_config"` // 扩展流组配置
LalRawContent []byte `json:"-"` // lal 原始配置内容
}

Expand All @@ -43,17 +42,22 @@ type HttpConfig struct {
CtrlAuthWhitelist CtrlAuthWhitelist `json:"ctrl_auth_whitelist"`
}

// CtrlAuthWhitelist 控制类接口鉴权
// CtrlAuthWhitelist 控制类接口鉴权
type CtrlAuthWhitelist struct {
IPs []string // 允许访问的远程 IP,零值时不生效
Secrets []string // 认证信息,零值时不生效
}

type HttpFmp4Config struct {
type Fmp4Config struct {
Http Fmp4HttpConfig `json:"http"`
Hls Fmp4HlsConfig `json:"hls"`
}

type Fmp4HttpConfig struct {
Enable bool `json:"enable"` // http-fmp4使能标志
}

type HlsConfig struct {
type Fmp4HlsConfig struct {
Enable bool `json:"enable"` // hls使能标志
SegmentCount int `json:"segment_count"` // 分片个数,llhls默认7个
SegmentDuration int `json:"segment_duration"` // hls分片时长,默认1s
Expand All @@ -78,8 +82,9 @@ type GB28181Config struct {
type GB28181MediaConfig struct {
MediaIp string `json:"media_ip"` // 流媒体IP,用于在SDP中指定
ListenPort uint16 `json:"listen_port"` // tcp,udp监听端口 默认启动
MultiPortMaxIncrement uint16 `json:"multi_port_max_increment"` //多端口范围 ListenPort+1至ListenPort+MultiPortMax
MultiPortMaxIncrement uint16 `json:"multi_port_max_increment"` // 多端口范围 ListenPort+1至ListenPort+MultiPortMax
}

type HttpNotifyConfig struct {
Enable bool `json:"enable"`
UpdateIntervalSec int `json:"update_interval_sec"`
Expand All @@ -95,7 +100,7 @@ type HttpNotifyConfig struct {
OnHlsMakeTs string `json:"on_hls_make_ts"`
}

type HookConfig struct {
type LogicConfig struct {
GopCacheNum int `json:"gop_cache_num"`
SingleGopMaxFrameNum int `json:"single_gop_max_frame_num"`
}
Expand Down Expand Up @@ -142,6 +147,10 @@ func unmarshalConfig(data []byte, cfg *Config) error {
if err := json.Unmarshal(data, cfg); err != nil {
return err
}
var raw map[string]json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
if cfg.LalSvrConfigPath == "" {
var legacy struct {
LalSvrConfigPath string `json:"lal_config_path:"`
Expand All @@ -151,6 +160,26 @@ func unmarshalConfig(data []byte, cfg *Config) error {
}
cfg.LalSvrConfigPath = legacy.LalSvrConfigPath
}
if _, ok := raw["logic_config"]; !ok {
var legacy struct {
LogicConfig LogicConfig `json:"hook_config"`
}
if err := json.Unmarshal(data, &legacy); err != nil {
return err
}
cfg.LogicConfig = legacy.LogicConfig
}
if _, ok := raw["fmp4_config"]; !ok {
var legacy struct {
Http Fmp4HttpConfig `json:"httpfmp4_config"`
Hls Fmp4HlsConfig `json:"hls_config"`
}
if err := json.Unmarshal(data, &legacy); err != nil {
return err
}
cfg.Fmp4Config.Http = legacy.Http
cfg.Fmp4Config.Hls = legacy.Hls
}
return nil
}

Expand Down
152 changes: 152 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package config

import (
"strings"
"testing"
)

func TestUnmarshalStructuredConfig(t *testing.T) {
raw := []byte(`{
"lalmax": {
"srt_config": {
"enable": true,
"addr": ":6001"
},
"server_id": "lalmax-1"
},
"lal": {
"rtmp": {
"enable": true,
"addr": ":1935"
}
}
}`)

if err := Unmarshal(raw); err != nil {
t.Fatalf("unmarshal structured config: %v", err)
}

cfg := GetConfig()
if !cfg.SrtConfig.Enable || cfg.SrtConfig.Addr != ":6001" {
t.Fatalf("unexpected srt config: %+v", cfg.SrtConfig)
}
if cfg.ServerId != "lalmax-1" {
t.Fatalf("unexpected server id: %s", cfg.ServerId)
}
if !strings.Contains(string(cfg.LalRawContent), `"rtmp"`) {
t.Fatalf("lal raw content not preserved: %s", string(cfg.LalRawContent))
}
}

func TestUnmarshalLegacyConfig(t *testing.T) {
raw := []byte(`{
"srt_config": {
"enable": true,
"addr": ":6001"
},
"httpfmp4_config": {
"enable": true
},
"hls_config": {
"enable": true,
"segment_count": 3,
"segment_duration": 2,
"part_duration": 100,
"low_latency": true
},
"hook_config": {
"gop_cache_num": 3,
"single_gop_max_frame_num": 120
},
"lal_config_path:": "./conf/lalserver.conf.json"
}`)

if err := Unmarshal(raw); err != nil {
t.Fatalf("unmarshal legacy config: %v", err)
}

cfg := GetConfig()
if !cfg.SrtConfig.Enable || cfg.SrtConfig.Addr != ":6001" {
t.Fatalf("unexpected srt config: %+v", cfg.SrtConfig)
}
if cfg.LalSvrConfigPath != "./conf/lalserver.conf.json" {
t.Fatalf("unexpected lal config path: %s", cfg.LalSvrConfigPath)
}
if cfg.LogicConfig.GopCacheNum != 3 || cfg.LogicConfig.SingleGopMaxFrameNum != 120 {
t.Fatalf("unexpected legacy logic config: %+v", cfg.LogicConfig)
}
if !cfg.Fmp4Config.Http.Enable {
t.Fatalf("unexpected legacy fmp4 http config: %+v", cfg.Fmp4Config.Http)
}
if !cfg.Fmp4Config.Hls.Enable || cfg.Fmp4Config.Hls.SegmentCount != 3 || cfg.Fmp4Config.Hls.SegmentDuration != 2 || cfg.Fmp4Config.Hls.PartDuration != 100 || !cfg.Fmp4Config.Hls.LowLatency {
t.Fatalf("unexpected legacy fmp4 hls config: %+v", cfg.Fmp4Config.Hls)
}
if len(cfg.LalRawContent) != 0 {
t.Fatalf("legacy config should not set lal raw content: %s", string(cfg.LalRawContent))
}
}

func TestUnmarshalStructuredFmp4ConfigKeepsExplicitZero(t *testing.T) {
raw := []byte(`{
"lalmax": {
"fmp4_config": {
"http": {
"enable": false
},
"hls": {
"enable": true,
"segment_count": 0,
"segment_duration": 0,
"part_duration": 0,
"low_latency": false
}
},
"httpfmp4_config": {
"enable": true
},
"hls_config": {
"enable": true,
"segment_count": 3,
"segment_duration": 2,
"part_duration": 100,
"low_latency": true
}
}
}`)

if err := Unmarshal(raw); err != nil {
t.Fatalf("unmarshal structured config: %v", err)
}

cfg := GetConfig()
if cfg.Fmp4Config.Http.Enable {
t.Fatalf("explicit fmp4 http config should not be overwritten: %+v", cfg.Fmp4Config.Http)
}
if !cfg.Fmp4Config.Hls.Enable || cfg.Fmp4Config.Hls.SegmentCount != 0 || cfg.Fmp4Config.Hls.SegmentDuration != 0 || cfg.Fmp4Config.Hls.PartDuration != 0 || cfg.Fmp4Config.Hls.LowLatency {
t.Fatalf("explicit fmp4 hls config should not be overwritten: %+v", cfg.Fmp4Config.Hls)
}
}

func TestUnmarshalStructuredLogicConfigKeepsExplicitZero(t *testing.T) {
raw := []byte(`{
"lalmax": {
"logic_config": {
"gop_cache_num": 0,
"single_gop_max_frame_num": 0
},
"hook_config": {
"gop_cache_num": 3,
"single_gop_max_frame_num": 120
}
}
}`)

if err := Unmarshal(raw); err != nil {
t.Fatalf("unmarshal structured config: %v", err)
}

cfg := GetConfig()
if cfg.LogicConfig.GopCacheNum != 0 || cfg.LogicConfig.SingleGopMaxFrameNum != 0 {
t.Fatalf("explicit logic config should not be overwritten: %+v", cfg.LogicConfig)
}
}
Loading
Loading