UNPKG

node-media-server

Version:

A Node.js implementation of RTMP Server

527 lines (442 loc) 12.9 kB
# Node-Media-Server 数据流分析 ## 数据流架构总览 Node-Media-Server 采用了分层的数据流架构,确保了高效的媒体数据处理和多协议兼容性。整个数据流从推流端到播放端经历了多个处理阶段。 ## 核心数据结构 ### 1. AVPacket - 统一媒体数据包 AVPacket 是整个系统中最核心的数据结构,用于在不同组件间传递音视频数据: ```javascript class AVPacket { constructor() { this.codec_type = 0; // 编码器类型: 8=音频, 9=视频, 18=脚本 this.codec_id = 0; // 编码器ID this.pts = 0; // 显示时间戳 this.dts = 0; // 解码时间戳 this.size = 0; // 数据大小 this.flags = 0; // 标志位: 1=音频头, 2=视频头, 3=关键帧, 4=普通帧, 5=元数据 this.data = Buffer.alloc(0); // 原始媒体数据 } } ``` ### 2. Session - 会话数据结构 ```javascript class BaseSession { constructor() { this.id = ''; // 唯一会话ID this.ip = ''; // 客户端IP地址 this.protocol = ''; // 协议类型: "rtmp", "flv" this.streamApp = ''; // 应用名称 this.streamName = ''; // 流名称 this.streamPath = ''; // 完整流路径 this.isPublisher = false; // 是否为发布者 this.inBytes = 0; // 输入字节数 this.outBytes = 0; // 输出字节数 this.createTime = 0; // 创建时间 this.endTime = 0; // 结束时间 } } ``` ## 推流数据流分析 ### 1. RTMP推流数据流 ```mermaid flowchart TD A[推流客户端] --> B[TCP Socket] B --> C[RTMP协议解析] C --> D[分块重组] D --> E[RTMP消息解析] E --> F[AMF数据解析] F --> G[创建AVPacket] G --> H[BroadcastServer] H --> I[协议格式转换] I --> J[RTMP格式包] I --> K[FLV格式包] J --> L[RTMP播放器] K --> M[FLV播放器] style A fill:#e1f5fe style L fill:#e8f5e8 style M fill:#fff3e0 style H fill:#f3e5f5 ``` #### 详细数据转换过程: **步骤1: RTMP消息到AVPacket** ```javascript // RTMP Protocol处理 static parserTag(type, time, size, data) { let packet = new AVPacket(); packet.codec_type = type; packet.pts = time; packet.dts = time; packet.size = size; packet.data = data; // 解析视频帧类型和编码器 if (type === FLV_MEDIA_TYPE_VIDEO) { const frameType = data[0] >> 4 & 0b0111; const codecID = data[0] & 0x0f; // 处理现代编码器 (H.265, VP9, AV1) if (isExHeader) { const fourCC = data.subarray(1, 5); if (fourCC === 'av01' || fourCC === 'vp09' || fourCC === 'hvc1') { packet.codec_id = fourCC.readUint32BE(); } } // 设置标志位 if (VideoPacketType === VideoPacketTypeSequenceStart) { packet.flags = 2; // 视频头 } else if (frameType === FLV_FRAME_KEY) { packet.flags = 3; // 关键帧 } else { packet.flags = 4; // 普通帧 } } return packet; } ``` **步骤2: AVPacket到多协议格式** ```javascript // BroadcastServer处理 broadcastMessage(packet) { // 转换为FLV格式 const flvMessage = Flv.createMessage(packet); // 转换为RTMP格式 const rtmpMessage = Rtmp.createMessage(packet); // 根据包类型进行缓存管理 switch (packet.flags) { case 0: // 音频头 this.flvAudioHeader = Buffer.from(flvMessage); this.rtmpAudioHeader = Buffer.from(rtmpMessage); break; case 2: // 视频头 this.flvVideoHeader = Buffer.from(flvMessage); this.rtmpVideoHeader = Buffer.from(rtmpMessage); break; case 3: // 关键帧 - 清理并重建GOP缓存 this.flvGopCache?.clear(); this.rtmpGopCache?.clear(); this.flvGopCache = new Set(); this.rtmpGopCache = new Set(); this.flvGopCache.add(flvMessage); this.rtmpGopCache.add(rtmpMessage); break; } // 广播给所有订阅者 this.subscribers.forEach((session) => { switch (session.protocol) { case "flv": session.sendBuffer(flvMessage); break; case "rtmp": session.sendBuffer(rtmpMessage); break; } }); } ``` ### 2. GOP缓存数据流 GOP (Group of Pictures) 缓存是新播放器快速获得关键帧的重要机制: ```mermaid flowchart LR A[关键帧] --> B[加入GOP缓存] B --> C[缓存限制检查] C --> D{超过4096帧?} D -->|是| E[移除最旧帧] E --> B D -->|否| F[新播放器连接] F --> G[发送完整GOP] G --> H[实时流数据] style A fill:#ffcdd2 style G fill:#c8e6c9 style H fill:#fff9c4 ``` **GOP缓存管理代码:** ```javascript // 在BroadcastServer中管理GOP缓存 broadcastMessage(packet) { // ... 其他处理 ... if (packet.flags === 3) { // 关键帧 // 清理现有缓存 this.flvGopCache?.clear(); this.rtmpGopCache?.clear(); // 重新初始化缓存 this.flvGopCache = new Set(); this.rtmpGopCache = new Set(); this.flvGopCache.add(flvMessage); this.rtmpGopCache.add(rtmpMessage); } else if (packet.flags === 4) { // 普通帧 // 添加到缓存,检查大小限制 if (this.flvGopCache?.size < 4096) { this.flvGopCache?.add(flvMessage); this.rtmpGopCache?.add(rtmpMessage); } } } ``` ## 播放数据流分析 ### 1. 新播放器连接数据流 ```mermaid sequenceDiagram participant Player as 播放器 participant BroadcastServer as 广播服务器 participant GopCache as GOP缓存 participant LiveStream as 实时流 Player->>+BroadcastServer: 请求播放 BroadcastServer->>Player: 发送FLV头部 BroadcastServer->>Player: 发送元数据 BroadcastServer->>Player: 发送音视频头 BroadcastServer->>+GopCache: 获取GOP缓存 GopCache-->>-BroadcastServer: 返回缓存帧 BroadcastServer->>Player: 发送GOP缓存 BroadcastServer->>+LiveStream: 订阅实时流 LiveStream-->>-Player: 实时数据传输 ``` ### 2. HTTP-FLV数据流 ```mermaid flowchart TD A[HTTP请求] --> B[FlvSession创建] B --> C[流路径解析] C --> D[获取BroadcastServer] D --> E[身份验证] E --> F[发送FLV头部] F --> G[发送流元数据] G --> H[发送GOP缓存] H --> I[实时数据流] I --> J[HTTP响应流] style A fill:#e3f2fd style J fill:#e8f5e8 style D fill:#f3e5f5 ``` **HTTP-FLV数据发送实现:** ```javascript // FlvSession中的数据发送 sendBuffer(buffer) { if (this.res.writableEnded) { return; } this.res.write(buffer); this.outBytes += buffer.length; } // 从BroadcastServer接收数据 postPlay(session) { // 发送头部信息 session.sendBuffer(this.flvHeader); if (this.flvMetaData !== null) { session.sendBuffer(this.flvMetaData); } // 发送GOP缓存 if (this.flvGopCache !== null) { this.flvGopCache.forEach((frame) => { session.sendBuffer(frame); }); } // 添加到订阅者列表 this.subscribers.set(session.id, session); } ``` ### 3. WebSocket-FLV数据流 ```mermaid flowchart TD A[WebSocket连接] --> B[FlvSession创建] B --> C[URL路径解析] C --> D[获取BroadcastServer] D --> E[身份验证] E --> F[发送FLV头部] F --> G[发送流元数据] G --> H[发送GOP缓存] H --> I[实时WebSocket消息] style A fill:#e3f2fd style I fill:#fff3e0 style D fill:#f3e5f5 ``` **WebSocket-FLV数据发送实现:** ```javascript // FlvSession中的WebSocket数据发送 sendBuffer(buffer) { if (this.res.readyState !== WebSocket.OPEN) { return; } this.res.send(buffer); this.outBytes += buffer.length; } ``` ## 协议转换数据流 ### 1. RTMP到FLV转换 ```mermaid flowchart LR A[RTMP数据包] --> B[提取媒体数据] B --> C[解析编码器类型] C --> D[创建FLV标签头] D --> E[设置时间戳] E --> F[添加媒体数据] F --> G[添加PreviousTagSize] G --> H[FLV数据包] style A fill:#ffebee style H fill:#e8f5e8 ``` **RTMP到FLV转换代码:** ```javascript // Flv.createMessage实现 static createMessage(avpacket) { const buffer = Buffer.alloc(11 + avpacket.size + 4); // FLV标签头 buffer[0] = avpacket.codec_type; buffer.writeUintBE(avpacket.size, 1, 3); // 数据大小 buffer.writeUintBE(avpacket.dts & 0x00ffffff, 4, 3); // 时间戳低24位 buffer[7] = (avpacket.dts >> 24) & 0xff; // 时间戳扩展 buffer[8] = 0; // StreamID (始终为0) buffer[9] = 0; buffer[10] = 0; // 媒体数据 avpacket.data.copy(buffer, 11, 0, avpacket.size); // PreviousTagSize buffer.writeUint32BE(11 + avpacket.size, 11 + avpacket.size); return buffer; } ``` ### 2. FLV到RTMP转换 ```mermaid flowchart LR A[FLV数据包] --> B[解析标签头] B --> C[提取时间戳] C --> D[提取媒体数据] D --> E[创建RTMP消息] E --> F[设置消息头] F --> G[添加媒体数据] G --> H[RTMP数据包] style A fill:#e8f5e8 style H fill:#ffebee ``` ## 元数据处理流程 ### 1. 推流端元数据 ```mermaid flowchart TD A[推流端元数据] --> B[AMF编码] B --> C[RTMP消息] C --> D[AVPacket创建] D --> E[解码AMF数据] E --> F[提取流信息] F --> G[更新Session信息] G --> H[存储元数据] H --> I[分发到播放器] style A fill:#fff3e0 style G fill:#e1f5fe style I fill:#e8f5e8 ``` **元数据处理实现:** ```javascript // BroadcastServer中的元数据处理 if (packet.flags == 5) { // 元数据包 let metadata = decodeAmf0Data(packet.data); if (this.publisher && metadata.cmd === "@setDataFrame") { // 提取音频参数 this.publisher.audioCodec = metadata.dataObj.audiocodecid; this.publisher.audioChannels = metadata.dataObj.stereo ? 2 : 1; this.publisher.audioSamplerate = metadata.dataObj.audiosamplerate; // 提取视频参数 this.publisher.videoCodec = metadata.dataObj.videocodecid; this.publisher.videoWidth = metadata.dataObj.width; this.publisher.videoHeight = metadata.dataObj.height; this.publisher.videoFramerate = metadata.dataObj.framerate; } } ``` ## 统计和监控数据流 ### 1. 会话统计数据流 ```mermaid flowchart TD A[会话活动] --> B[字节计数更新] B --> C[实时状态更新] C --> D[Context.sessions] D --> E[API统计查询] E --> F[统计响应] F --> G[监控仪表板] style A fill:#e8eaf6 style D fill:#f3e5f5 style G fill:#e8f5e8 ``` ### 2. 系统性能数据流 ```mermaid flowchart LR A[Node.js进程] --> B[内存使用统计] A --> C[CPU使用统计] B --> D[Process.memoryUsage] C --> E[Process.cpuUsage] D --> F[StatsHandler] E --> F F --> G[API响应] G --> H[监控系统] style A fill:#fff3e0 style F fill:#e1f5fe style H fill:#c8e6c9 ``` **统计数据处理:** ```javascript // StatsHandler中的统计收集 getStats() { const memoryUsage = process.memoryUsage(); const cpuUsage = process.cpuUsage(); // 会话统计 const totalSessions = Context.sessions.size; const publishers = Array.from(Context.sessions.values()) .filter(s => s.isPublisher).length; const players = totalSessions - publishers; // 流统计 const totalStreams = Context.broadcasts.size; const activeStreams = Array.from(Context.broadcasts.values()) .filter(b => b.publisher !== null).length; return { system: { uptime: process.uptime(), memory: memoryUsage, cpu: cpuUsage }, sessions: { total: totalSessions, publishers: publishers, players: players }, streams: { total: totalStreams, active: activeStreams } }; } ``` ## 错误处理数据流 ### 1. 会话错误处理 ```mermaid flowchart TD A[会话异常] --> B[错误捕获] B --> C[日志记录] C --> D[资源清理] D --> E[会话移除] E --> F[事件通知] F --> G[状态恢复] style A fill:#ffebee style C fill:#fff3e0 style G fill:#e8f5e8 ``` ### 2. 协议错误处理 ```mermaid flowchart LR A[协议错误] --> B[错误分类] B --> C{致命错误?} C -->|是| D[关闭连接] C -->|否| E[恢复处理] D --> F[会话清理] E --> G[继续处理] style A fill:#ffebee style D fill:#ffcdd2 style G fill:#c8e6c9 ``` ## 数据流优化策略 ### 1. 内存优化 - GOP缓存大小限制 (4096帧) - 及时清理过期会话 - Buffer复用机制 ### 2. 网络优化 - 零拷贝数据传输 - TCP_NODELAY设置 - 缓冲区大小优化 ### 3. 并发优化 - 事件驱动架构 - 非阻塞I/O - 负载均衡分发 这个数据流架构确保了Node-Media-Server能够高效处理大规模实时媒体数据,同时保持低延迟和高吞吐量。