zhibo/.kiro/specs/live-streaming-system/design.md
2025-12-15 11:21:21 +08:00

13 KiB
Raw Blame History

Design Document

Overview

本设计文档描述了基于 SRS 的个人直播系统的技术架构和实现方案。系统采用前后端分离架构,使用 SRS 作为核心流媒体服务器处理视频流的接收和分发Node.js 后端提供业务 APIReact 前端提供用户界面。

技术栈选型

组件 技术选择 理由
流媒体服务器 SRS 5.0 高性能、低延迟、支持多协议、Docker 部署简单
后端框架 Node.js + Express 轻量级、异步处理能力强、生态丰富
前端框架 React 18 组件化开发、生态成熟、性能优秀
视频播放器 flv.js + hls.js flv.js 支持 HTTP-FLV 低延迟播放hls.js 提供 HLS 兼容性
容器化 Docker + Docker Compose 简化部署、环境一致性

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        用户层 (Users)                            │
├─────────────────────────────────────────────────────────────────┤
│  ┌─────────────┐                          ┌─────────────────┐   │
│  │   主播      │                          │     观众        │   │
│  │  (OBS/FFmpeg)│                          │   (浏览器)      │   │
│  └──────┬──────┘                          └────────┬────────┘   │
│         │ RTMP 推流                                │ HTTP 请求   │
│         │ rtmp://host:1935/live/{streamKey}       │            │
└─────────┼─────────────────────────────────────────┼────────────┘
          │                                         │
┌─────────┼─────────────────────────────────────────┼────────────┐
│         ▼                                         ▼            │
│  ┌─────────────────┐                    ┌─────────────────┐    │
│  │   SRS Server    │                    │  React Frontend │    │
│  │   (Docker)      │                    │   (Port 3000)   │    │
│  │                 │                    │                 │    │
│  │ RTMP: 1935      │◄───HTTP-FLV/HLS───│  flv.js/hls.js  │    │
│  │ HTTP: 8080      │                    │                 │    │
│  └────────┬────────┘                    └────────┬────────┘    │
│           │                                      │             │
│           │ HTTP Callback                        │ REST API    │
│           │ (on_publish/on_unpublish)            │             │
│           ▼                                      ▼             │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                   Node.js API Server                     │   │
│  │                      (Port 3001)                         │   │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐  │   │
│  │  │ Room API    │  │ Callback    │  │ Room Store      │  │   │
│  │  │ Controller  │  │ Handler     │  │ (In-Memory)     │  │   │
│  │  └─────────────┘  └─────────────┘  └─────────────────┘  │   │
│  └─────────────────────────────────────────────────────────┘   │
│                        服务层 (Services)                        │
└─────────────────────────────────────────────────────────────────┘

数据流说明

  1. 推流流程: 主播 → RTMP → SRS → HTTP Callback → API Server → 更新房间状态
  2. 观看流程: 观众 → React App → 获取房间信息 → flv.js/hls.js → SRS HTTP-FLV/HLS
  3. 管理流程: 用户 → React App → REST API → API Server → 房间 CRUD

Components and Interfaces

1. SRS 流媒体服务器

职责: 接收 RTMP 推流,转换并分发 HTTP-FLV 和 HLS 流

配置接口:

# srs.conf
listen              1935;
max_connections     1000;
daemon              off;
srs_log_tank        console;

http_server {
    enabled         on;
    listen          8080;
    dir             ./objs/nginx/html;
}

vhost __defaultVhost__ {
    hls {
        enabled         on;
        hls_fragment    2;
        hls_window      10;
        hls_path        ./objs/nginx/html;
        hls_m3u8_file   [app]/[stream].m3u8;
        hls_ts_file     [app]/[stream]-[seq].ts;
    }
    
    http_remux {
        enabled     on;
        mount       [vhost]/[app]/[stream].flv;
    }
    
    http_hooks {
        enabled         on;
        on_publish      http://api-server:3001/api/srs/on_publish;
        on_unpublish    http://api-server:3001/api/srs/on_unpublish;
    }
}

2. API Server (Node.js + Express)

职责: 提供业务 API处理 SRS 回调,管理房间数据

2.1 Room API Controller

// 接口定义
interface RoomController {
  // 获取所有房间
  GET /api/rooms -> Room[]
  
  // 创建房间
  POST /api/rooms { title: string, streamerName: string } -> Room
  
  // 获取单个房间
  GET /api/rooms/:id -> Room
  
  // 删除房间
  DELETE /api/rooms/:id -> { success: boolean }
}

2.2 SRS Callback Handler

// SRS 回调接口
interface SRSCallbackHandler {
  // 推流开始回调
  POST /api/srs/on_publish { 
    action: 'on_publish',
    app: string,      // 应用名,如 'live'
    stream: string    // 流名称,即 streamKey
  } -> { code: 0 }
  
  // 推流结束回调
  POST /api/srs/on_unpublish {
    action: 'on_unpublish', 
    app: string,
    stream: string
  } -> { code: 0 }
}

3. React Frontend

职责: 提供用户界面,包括房间列表、直播播放、房间创建

3.1 组件结构

src/
├── components/
│   ├── RoomList.jsx      # 房间列表组件
│   ├── RoomCard.jsx      # 房间卡片组件
│   ├── LivePlayer.jsx    # 直播播放器组件
│   ├── CreateRoom.jsx    # 创建房间弹窗
│   └── StreamInfo.jsx    # 推流信息展示
├── pages/
│   ├── HomePage.jsx      # 首页(房间列表)
│   └── RoomPage.jsx      # 房间详情页
├── services/
│   └── api.js            # API 调用封装
└── App.jsx               # 应用入口

3.2 LivePlayer 组件接口

interface LivePlayerProps {
  room: Room;
  preferFlv?: boolean;  // 优先使用 FLV低延迟
}

// 播放器内部逻辑
// 1. 检测浏览器是否支持 flv.js (需要 MSE 支持)
// 2. 支持则使用 HTTP-FLV否则降级到 HLS
// 3. 监听播放错误,自动重连

Data Models

Room 数据模型

interface Room {
  id: string;           // 房间唯一标识 (UUID)
  title: string;        // 房间标题
  streamerName: string; // 主播名称
  streamKey: string;    // 推流密钥 (与 id 相同)
  isLive: boolean;      // 是否正在直播
  viewerCount: number;  // 观看人数
  createdAt: string;    // 创建时间 (ISO 8601)
  startedAt?: string;   // 开播时间 (ISO 8601)
}

API 响应格式

// 成功响应
interface SuccessResponse<T> {
  success: true;
  data: T;
}

// 错误响应
interface ErrorResponse {
  success: false;
  error: {
    code: string;
    message: string;
  };
}

// SRS 回调响应
interface SRSCallbackResponse {
  code: number;  // 0 表示成功
}

流地址格式

interface StreamUrls {
  rtmp: string;    // rtmp://host:1935/live/{streamKey}
  flv: string;     // http://host:8080/live/{streamKey}.flv
  hls: string;     // http://host:8080/live/{streamKey}.m3u8
}

Correctness Properties

A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.

Property 1: Room Creation Round-Trip Consistency

For any valid room creation request with non-empty title and streamer name, creating the room and then retrieving it by ID SHALL return a room with matching title, streamer name, a unique stream key, and a valid creation timestamp.

Validates: Requirements 1.1, 1.4

Property 2: New Rooms Start Offline

For any newly created room, the initial isLive status SHALL be false.

Validates: Requirements 1.2

Property 3: Empty Title Rejection

For any room creation request where the title is empty or consists only of whitespace characters, the system SHALL reject the request and return a validation error without creating a room.

Validates: Requirements 1.3

Property 4: Publish Callback Status Transition

For any room that exists in the system, when a publish callback is received with that room's stream key, the room's isLive status SHALL transition to true. When an unpublish callback is received, the status SHALL transition to false.

Validates: Requirements 2.3, 2.5

Property 5: Stream URLs Contain Required Formats

For any room returned by the API, the stream URLs SHALL include both HTTP-FLV (.flv) and HLS (.m3u8) format URLs containing the room's stream key.

Validates: Requirements 3.1

Property 6: Room List Completeness

For any set of created rooms, requesting the room list SHALL return all rooms with their current status, title, and streamer name intact.

Validates: Requirements 4.1, 4.2

Error Handling

API Error Codes

错误码 描述 HTTP 状态码
VALIDATION_ERROR 请求参数验证失败 400
ROOM_NOT_FOUND 房间不存在 404
STREAM_KEY_INVALID 推流密钥无效 401
INTERNAL_ERROR 服务器内部错误 500

错误处理策略

  1. API 层: 统一错误响应格式,包含错误码和消息
  2. SRS 回调: 返回 { code: 0 } 表示成功,非零表示失败
  3. 前端: 显示友好的错误提示,支持重试操作
  4. 播放器: 自动重连机制,最多重试 3 次

Testing Strategy

单元测试

使用 Jest 进行单元测试,覆盖以下模块:

  1. Room Service: 房间 CRUD 操作
  2. Validation: 输入验证逻辑
  3. URL Generator: 流地址生成

属性测试 (Property-Based Testing)

使用 fast-check 库进行属性测试:

  1. Property 1: 房间创建往返一致性
  2. Property 2: 新房间初始状态
  3. Property 3: 空标题拒绝
  4. Property 4: 回调状态转换
  5. Property 5: 流地址格式
  6. Property 6: 房间列表完整性

测试配置

// jest.config.js
module.exports = {
  testEnvironment: 'node',
  testMatch: ['**/*.test.js', '**/*.property.test.js'],
  collectCoverageFrom: ['server/**/*.js'],
  coverageThreshold: {
    global: { branches: 80, functions: 80, lines: 80 }
  }
};

属性测试示例

// 每个属性测试运行 100 次迭代
// 标注格式: **Feature: live-streaming-system, Property {number}: {property_text}**

import fc from 'fast-check';

// **Feature: live-streaming-system, Property 2: New Rooms Start Offline**
test('newly created rooms should have isLive = false', () => {
  fc.assert(
    fc.property(
      fc.string({ minLength: 1 }),  // title
      fc.string({ minLength: 1 }),  // streamerName
      (title, streamerName) => {
        const room = createRoom({ title, streamerName });
        return room.isLive === false;
      }
    ),
    { numRuns: 100 }
  );
});