# ZLMediaKit API 核心概念与使用指南

## 一、核心概念解析

### 1. 流（Stream）的概念
在 ZLMediaKit 中，**流**是指一个媒体数据源，由四元组唯一标识：
- **schema**: 协议类型（rtsp/rtmp/rtc等）
- **vhost**: 虚拟主机（通常是 `__defaultVhost__`）
- **app**: 应用名（如 live、rtp）
- **stream**: 流ID（如 test、camera01）

**完整流标识**: `schema://vhost/app/stream`
例如: `rtsp://__defaultVhost__/live/test`

### 2. Session（会话）
**Session** 是指客户端与服务器之间的 **TCP/UDP 连接**。
- 每个播放器连接、推流连接都会创建一个 Session
- Session 包含：连接ID、本地端口、对端IP、协议类型等信息
- 通过 `getAllSession` 可以查看所有活跃连接
- 通过 `kick_session` 可以断开指定连接

**Session 与 Stream 的关系**：
- 一个 Stream 可以有多个 Session（多个播放器观看同一个流）
- 一个 Session 只对应一个 Stream

### 3. 拉流代理（StreamProxy）
**拉流代理**是指 ZLMediaKit 作为客户端，从远程服务器拉取流，然后在本地提供服务。

**使用场景**：
- 从其他流媒体服务器拉取流到本地
- 协议转换（拉取 RTMP 流，提供 RTSP/HLS 等多协议播放）
- 流的中转和分发

**API**: `addStreamProxy`
```
GET /index/api/addStreamProxy?
  secret=xxx&
  vhost=__defaultVhost__&
  app=live&
  stream=test&
  url=rtmp://remote.server.com/live/stream
```

**工作原理**：
```
远程服务器 --拉流--> ZLMediaKit --提供多协议--> 播放器
(RTMP源)              (代理)         (RTSP/RTMP/HLS/FLV等)
```

### 4. FFmpeg 拉流代理（FFmpegSource）
**FFmpeg 代理**是通过调用 FFmpeg 进程来拉取流，支持更多格式和协议。

**与普通拉流代理的区别**：
- **StreamProxy**: 使用 ZLMediaKit 内置播放器，性能高，但支持协议有限
- **FFmpegSource**: 使用 FFmpeg，支持任意协议/格式，但性能较低，占用资源多

**使用场景**：
- 拉取 ZLMediaKit 不支持的协议（如某些特殊 HLS、DASH）
- 需要 FFmpeg 的转码能力
- 拉取文件流

**API**: `addFFmpegSource`
```
GET /index/api/addFFmpegSource?
  secret=xxx&
  src_url=http://example.com/live.m3u8&
  dst_url=rtmp://127.0.0.1/live/stream&
  timeout_ms=10000
```

### 5. 推流代理（StreamPusherProxy）
**推流代理**是指将 ZLMediaKit 上的流推送到其他流媒体服务器。

**使用场景**：
- 将本地流推送到 CDN
- 多平台分发（同时推送到多个平台）
- 流的备份

**API**: `addStreamPusherProxy`
```
GET /index/api/addStreamPusherProxy?
  secret=xxx&
  schema=rtmp&
  vhost=__defaultVhost__&
  app=live&
  stream=test&
  dst_url=rtmp://cdn.server.com/live/push
```

### 6. 断流（close_stream）
**断流**是指关闭一个媒体流源，而不是断开客户端连接。

**断流的含义**：
- 停止流的源头（推流端或拉流代理）
- 所有观看该流的播放器会断开
- 流从服务器上移除

**与 kick_session 的区别**：
- `close_stream`: 关闭流源，影响所有观看者
- `kick_session`: 只断开某个客户端连接，不影响流本身

### 7. RTP 服务器
**RTP 服务器**用于接收 GB28181 设备推送的 RTP 流。

**工作流程**：
1. `openRtpServer`: 创建 RTP 接收端口
2. GB28181 设备推送 RTP 流到该端口
3. ZLMediaKit 自动将 RTP 流转换为标准流
4. 可通过 RTSP/RTMP/HLS 等协议播放

---

## 二、完整使用流程示例

### 场景 1: GB28181 设备推流
```bash
# 1. 创建 RTP 服务器（监听端口接收 GB28181 推流）
GET /index/api/openRtpServer?
  secret=xxx&
  port=10000&
  tcp_mode=1&
  stream_id=camera01

# 返回: {"code":0, "port":10000}

# 2. GB28181 设备推流到 10000 端口（设备端配置）

# 3. 查看流是否在线
GET /index/api/isMediaOnline?
  secret=xxx&
  schema=rtsp&
  vhost=__defaultVhost__&
  app=rtp&
  stream=camera01

# 4. 获取流信息（包含所有播放地址）
GET /index/api/getMediaInfo?
  secret=xxx&
  schema=rtsp&
  vhost=__defaultVhost__&
  app=rtp&
  stream=camera01

# 返回包含:
# - RTSP: rtsp://server:554/rtp/camera01
# - RTMP: rtmp://server:1935/rtp/camera01
# - HTTP-FLV: http://server:80/rtp/camera01.live.flv
# - HLS: http://server:80/rtp/camera01/hls.m3u8
# - WebRTC: webrtc://server/rtp/camera01
```

### 场景 2: RTMP 推流
```bash
# 1. 使用 OBS 或 FFmpeg 推流
ffmpeg -re -i input.mp4 -c copy -f flv rtmp://server:1935/live/mystream

# 推流地址格式: rtmp://服务器IP:端口/app/stream
# - app: 应用名（如 live）
# - stream: 流ID（如 mystream）

# 2. 查看流列表
GET /index/api/getMediaList?secret=xxx

# 返回所有在线流，包含:
# {
#   "code": 0,
#   "data": [{
#     "app": "live",
#     "stream": "mystream",
#     "vhost": "__defaultVhost__",
#     "schema": "rtmp",
#     "readerCount": 0,  // 观看人数
#     "totalReaderCount": 0,
#     "originType": 0,  // 0=推流，1=拉流代理，2=点播
#     "originUrl": "",
#     "createStamp": 1234567890
#   }]
# }

# 3. 获取该流的详细信息和播放地址
GET /index/api/getMediaInfo?
  secret=xxx&
  schema=rtmp&
  vhost=__defaultVhost__&
  app=live&
  stream=mystream

# 4. 播放地址:
# - RTSP: rtsp://server:554/live/mystream
# - RTMP: rtmp://server:1935/live/mystream
# - HTTP-FLV: http://server:80/live/mystream.live.flv
# - HLS: http://server:80/live/mystream/hls.m3u8
# - WebSocket-FLV: ws://server:80/live/mystream.live.flv
```

### 场景 3: 录制流
```bash
# 1. 开始录制（MP4 格式）
GET /index/api/startRecord?
  secret=xxx&
  type=1&              # 0=HLS, 1=MP4
  vhost=__defaultVhost__&
  app=live&
  stream=mystream&
  max_second=3600      # 每小时切片

# 2. 检查是否正在录制
GET /index/api/isRecording?
  secret=xxx&
  type=1&
  vhost=__defaultVhost__&
  app=live&
  stream=mystream

# 3. 获取录像文件列表
GET /index/api/getMp4RecordFile?
  secret=xxx&
  vhost=__defaultVhost__&
  app=live&
  stream=mystream&
  period=2025-12-09    # 指定日期

# 返回:
# {
#   "code": 0,
#   "data": {
#     "paths": [
#       "/path/to/record/live/mystream/2025-12-09/10-30-00.mp4",
#       "/path/to/record/live/mystream/2025-12-09/11-30-00.mp4"
#     ],
#     "rootPath": "/path/to/record"
#   }
# }

# 4. 停止录制
GET /index/api/stopRecord?
  secret=xxx&
  type=1&
  vhost=__defaultVhost__&
  app=live&
  stream=mystream
```

### 场景 4: 截图
```bash
# 1. 获取实时截图
GET /index/api/getSnap?
  secret=xxx&
  url=rtsp://server/live/mystream&
  timeout_sec=10&
  expire_sec=60        # 缓存时间

# 返回: JPEG 图片数据

# 2. 如果配置了定时截图，查看截图列表
# 截图通常保存在配置的 snap 目录下
# 目录结构: /snap/vhost/app/stream/timestamp.jpg

# 3. 删除截图
GET /index/api/deleteSnapDirectory?
  secret=xxx&
  vhost=__defaultVhost__&
  app=live&
  stream=mystream&
  file=1234567890.jpg  # 可选，不指定则删除整个目录
```

### 场景 5: 下载录像文件
```bash
# 1. 获取录像文件列表（见场景3）
GET /index/api/getMp4RecordFile?
  secret=xxx&
  vhost=__defaultVhost__&
  app=live&
  stream=mystream&
  period=2025-12-09

# 2. 下载指定文件
GET /index/api/downloadFile?
  file_path=/path/to/record/live/mystream/2025-12-09/10-30-00.mp4&
  save_name=mystream_20251209_103000.mp4

# 浏览器会直接下载该文件
```

---

## 三、API 分类与关系图

### 1. 流管理类
```
getMediaList          # 获取所有流列表
getMediaInfo          # 获取单个流详细信息
isMediaOnline         # 检查流是否在线
close_stream          # 关闭单个流
close_streams         # 批量关闭流
getMediaPlayerList    # 获取流的播放者列表
```

### 2. 连接管理类
```
getAllSession         # 获取所有TCP/UDP连接
kick_session          # 断开单个连接
kick_sessions         # 批量断开连接
```

### 3. 代理管理类
```
# 拉流代理
addStreamProxy        # 添加拉流代理
delStreamProxy        # 删除拉流代理
listStreamProxy       # 列出所有拉流代理
getProxyInfo          # 获取代理详细信息

# 推流代理
addStreamPusherProxy  # 添加推流代理
delStreamPusherProxy  # 删除推流代理
listStreamPusherProxy # 列出所有推流代理
getProxyPusherInfo    # 获取推流代理信息

# FFmpeg 代理
addFFmpegSource       # 添加 FFmpeg 拉流
delFFmpegSource       # 删除 FFmpeg 拉流
listFFmpegSource      # 列出所有 FFmpeg 拉流
```

### 4. RTP/GB28181 类
```
openRtpServer         # 创建 RTP 接收端口
closeRtpServer        # 关闭 RTP 端口
connectRtpServer      # TCP 主动连接模式
listRtpServer         # 列出所有 RTP 服务器
getRtpInfo            # 获取 RTP 推流信息
updateRtpServerSSRC   # 更新 SSRC 过滤
pauseRtpCheck         # 暂停超时检查
resumeRtpCheck        # 恢复超时检查
```

### 5. RTP 推流类
```
startSendRtp          # 主动模式推送 RTP
startSendRtpPassive   # 被动模式推送 RTP
startSendRtpTalk      # 双向对讲
stopSendRtp           # 停止推送 RTP
listRtpSender         # 列出 RTP 发送器
```

### 6. 录制类
```
startRecord           # 开始录制
stopRecord            # 停止录制
isRecording           # 检查录制状态
getMp4RecordFile      # 获取录像文件列表
deleteRecordDirectory # 删除录像目录
setRecordSpeed        # 设置录像播放速度
seekRecordStamp       # 设置录像播放位置
startRecordTask       # 事件录制（前后录制）
```

### 7. 截图类
```
getSnap               # 获取实时截图
deleteSnapDirectory   # 删除截图目录
```

### 8. 文件类
```
loadMP4File           # 点播 MP4 文件
downloadFile          # 下载文件
```

### 9. 系统管理类
```
getApiList            # 获取 API 列表
getServerConfig       # 获取服务器配置
setServerConfig       # 设置服务器配置
restartServer         # 重启服务器
getStatistic          # 获取统计信息
getThreadsLoad        # 获取网络线程负载
getWorkThreadsLoad    # 获取后台线程负载
version               # 获取版本信息
```

---

## 四、关键流程图

### 流的生命周期
```
┌─────────────────────────────────────────────────────────┐
│                     流的来源                              │
├─────────────────────────────────────────────────────────┤
│  1. 推流: RTMP/RTSP/WebRTC 推流                          │
│  2. 拉流代理: addStreamProxy                             │
│  3. FFmpeg 代理: addFFmpegSource                         │
│  4. GB28181: openRtpServer + 设备推流                    │
│  5. 点播: loadMP4File                                    │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│                  流在 ZLMediaKit 中                       │
├─────────────────────────────────────────────────────────┤
│  - 唯一标识: vhost/app/stream                            │
│  - 自动协议转换: RTSP/RTMP/HLS/FLV/WebRTC 等             │
│  - 可被多个客户端同时播放                                 │
│  - 可被录制、截图、转推                                   │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│                     流的消费                              │
├─────────────────────────────────────────────────────────┤
│  1. 播放: 多协议播放地址                                  │
│  2. 录制: startRecord                                    │
│  3. 截图: getSnap                                        │
│  4. 转推: addStreamPusherProxy                           │
│  5. RTP 推送: startSendRtp                               │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│                     流的结束                              │
├─────────────────────────────────────────────────────────┤
│  1. 推流端断开                                            │
│  2. 主动关闭: close_stream                               │
│  3. 无人观看超时（配置决定）                              │
│  4. 删除代理: delStreamProxy/delFFmpegSource             │
└─────────────────────────────────────────────────────────┘
```

### GB28181 完整流程
```
1. 创建 RTP 服务器
   openRtpServer(port=10000, stream_id=camera01)
   ↓
2. 配置 GB28181 设备推流到 10000 端口
   ↓
3. 流自动注册为: rtsp://__defaultVhost__/rtp/camera01
   ↓
4. 查看流信息
   getMediaInfo(schema=rtsp, app=rtp, stream=camera01)
   ↓
5. 获取所有播放地址并播放
   - RTSP: rtsp://server:554/rtp/camera01
   - RTMP: rtmp://server:1935/rtp/camera01
   - HTTP-FLV: http://server/rtp/camera01.live.flv
   - HLS: http://server/rtp/camera01/hls.m3u8
   ↓
6. 录制/截图/转推
   startRecord(app=rtp, stream=camera01)
   getSnap(url=rtsp://server/rtp/camera01)
   addStreamPusherProxy(app=rtp, stream=camera01, dst_url=...)
   ↓
7. 关闭流
   closeRtpServer(stream_id=camera01)
```

---

## 五、常见问题

### Q1: 如何知道流的 stream_id？
**A**:
- **推流时**: stream_id 由推流端指定（推流地址的最后一部分）
- **GB28181**: 在 `openRtpServer` 时指定
- **拉流代理**: 在 `addStreamProxy` 时指定
- **查询**: 使用 `getMediaList` 查看所有在线流

### Q2: 流的各种播放地址如何获取？
**A**: 使用 `getMediaInfo` 接口，返回结果包含所有协议的播放地址。

### Q3: 录像文件保存在哪里？
**A**:
- 默认路径在配置文件中设置（通常是 `./www/record`）
- 目录结构: `record/vhost/app/stream/日期/时间.mp4`
- 可通过 `getMp4RecordFile` 查询

### Q4: 如何实现多平台推流？
**A**: 使用 `addStreamPusherProxy`，同一个流可以添加多个推流代理（不同 dst_url）。

### Q5: 拉流代理和 FFmpeg 代理如何选择？
**A**:
- **优先使用拉流代理**: 性能高，资源占用少
- **使用 FFmpeg 代理的场景**:
  - 源协议 ZLMediaKit 不支持
  - 需要转码
  - 需要 FFmpeg 的特殊功能

### Q6: 如何实现按需拉流？
**A**: 配置 `on_play` hook，当有人播放时才调用 `addStreamProxy` 拉取源流。

---

## 六、最佳实践

### 1. 流命名规范
```
app: 按业务分类（live, record, vod, gb28181）
stream: 使用有意义的ID（camera_01, user_123_live）
```

### 2. 录制策略
```
- 重要流: 实时录制（startRecord）
- 事件流: 使用 startRecordTask（前后录制）
- 定时录制: 通过配置文件设置
```

### 3. 性能优化
```
- 使用拉流代理而非 FFmpeg 代理
- 合理设置录制切片时间
- 使用 HLS 时注意切片数量
- 监控 getThreadsLoad 和 getWorkThreadsLoad
```

### 4. 安全建议
```
- 修改默认 secret
- 启用推流鉴权（on_publish hook）
- 启用播放鉴权（on_play hook）
- 限制 API 访问 IP
```

---

## 七、快速参考

### 推流地址格式
```
RTMP: rtmp://server:1935/app/stream
RTSP: rtsp://server:554/app/stream (需要服务器支持 RTSP 推流)
```

### 播放地址格式
```
RTSP:       rtsp://server:554/app/stream
RTMP:       rtmp://server:1935/app/stream
HTTP-FLV:   http://server:80/app/stream.live.flv
WebSocket-FLV: ws://server:80/app/stream.live.flv
HLS:        http://server:80/app/stream/hls.m3u8
HTTP-TS:    http://server:80/app/stream.live.ts
HTTP-fMP4:  http://server:80/app/stream.live.mp4
WebRTC:     webrtc://server/app/stream
```

### 常用 API 速查
```bash
# 查看所有流
curl "http://server:80/index/api/getMediaList?secret=xxx"

# 查看流信息
curl "http://server:80/index/api/getMediaInfo?secret=xxx&schema=rtsp&vhost=__defaultVhost__&app=live&stream=test"

# 开始录制
curl "http://server:80/index/api/startRecord?secret=xxx&type=1&vhost=__defaultVhost__&app=live&stream=test"

# 获取截图
curl "http://server:80/index/api/getSnap?secret=xxx&url=rtsp://server/live/test&timeout_sec=10" -o snap.jpg

# 关闭流
curl "http://server:80/index/api/close_stream?secret=xxx&schema=rtsp&vhost=__defaultVhost__&app=live&stream=test"
```
