FFmpeg实现RTSP推拉流

发布时间:2026/8/2 18:43:31
FFmpeg实现RTSP推拉流 RTSPReal Time Streaming Protocol推流与拉流的实现核心在于建立符合RTSP/RTP/RTCP协议栈的客户端与服务器。以下是基于不同场景和工具的实现方法。一、 使用现成服务器与工具快速验证这是最快捷的方式适用于功能验证和简单应用。1. 推流实现推流指将本地视频源文件、摄像头、屏幕发布到RTSP服务器。工具/组件推流命令/方法示例说明FFmpegffmpeg -re -i input.mp4 -c copy -f rtsp rtsp://server_ip:port/live/stream将本地文件以原始编码-c copy推流到RTSP服务器。-re表示按输入文件速率读取。FFmpeg (摄像头)ffmpeg -f dshow -i videoUSB Camera -vcodec libx264 -f rtsp rtsp://server_ip:port/live/stream(Windows)ffmpeg -f v4l2 -i /dev/video0 -vcodec libx264 -f rtsp rtsp://server_ip:port/live/stream(Linux)捕获USB摄像头视频编码为H.264后推流。GStreamergst-launch-1.0 v4l2src ! videoconvert ! x264enc ! rtspclientsink locationrtsp://server_ip:port/live/stream使用GStreamer管道从V4L2设备采集、编码并推流。OpenCV VideoWriterpythonout cv2.VideoWriter(appsrc ! videoconvert ! x264enc ! rtspclientsink locationrtsp://...,cv2.CAP_GSTREAMER, 0, fps, (width, height))#### 2. 拉流实现 拉流指从RTSP服务器获取并播放或处理视频流。 | 工具/组件 | 拉流命令/方法示例 | 说明 | | :--- | :--- | :--- | | **FFplay (FFmpeg)** | ffplay rtsp://server_ip:port/live/stream | 最简单的拉流播放工具。 | | **VLC Media Player** | 媒体 - 打开网络串流 - 输入RTSP URL | 图形化工具支持拉流播放。 | | **OpenCV VideoCapture** | pythonbrcap cv2.VideoCapture(rtsp://server_ip:port/live/stream)brwhile cap.isOpened():br ret, frame cap.read()br # 处理framebr | 在Python中拉流并获取每一帧图像进行处理。 | | **GStreamer** | gst-launch-1.0 rtspsrc locationrtsp://server_ip:port/live/stream ! decodebin ! autovideosink | 使用GStreamer管道拉流并解码显示。 | #### 3. 轻量级RTSP服务器 进行推拉流测试需要一个RTSP服务器。**mediamtx**原rtsp-simple-server是一个极简的选择。 1. **下载并启动服务器** bash # 从 https://github.com/bluenviron/mediamtx/releases 下载对应平台版本 ./mediamtx 服务器默认在 :8554 端口监听。 2. **推流到服务器** bash ffmpeg -re -i input.mp4 -c copy -f rtsp rtsp://localhost:8554/mystream 3. **从服务器拉流** bash ffplay rtsp://localhost:8554/mystream ### 二、 编程实现C/C示例 对于嵌入式或需要深度集成的场景需要编程实现RTSP客户端/服务器。 #### 1. RTSP推流客户端实现要点 一个基本的RTSP推流客户端需要完成以下步骤 c // 伪代码流程1. 建立TCP连接至RTSP服务器默认端口554。 2. 发送DESCRIBE请求获取媒体描述SDP。 3. 发送SETUP请求为音视频流建立传输通道RTP/RTCP端口。 4. 发送PLAY请求开始播放。 // 对于推流ANNOUNCE, RECORD服务器需支持相关方法。 5. 在SETUP建立的UDP或TCP套接字上按照RTP协议封装并发送音视频数据。 将H.264 NALU封装为RTP包。一个NALU可能被分片FU-A或打包成单个RTP包。 RTP头需设置序列号、时间戳、负载类型等。 6. 周期性发送RTCP接收报告RR或发送报告SR。关键代码片段RTP封装H.264 NALU// 假设有一个H.264 NALU (nal_data, nal_len)需要封装为RTP包 void send_rtp_h264_packet(int rtp_sock, struct sockaddr_in *dest_addr, const uint8_t *nal_data, int nal_len, uint16_t *seq_num, uint32_t *timestamp) { uint8_t rtp_packet[1500]; // MTU大小 int rtp_header_len 12; // 1. 构建RTP头 (RFC 3550) rtp_packet[0] 0x80; // V2, P0, X0, CC0 rtp_packet[1] 96; // PT96 (动态负载类型H.264常用) rtp_packet[2] (*seq_num 8) 0xFF; rtp_packet[3] *seq_num 0xFF; (*seq_num); // 设置时间戳 (根据时钟频率递增H.264视频通常为90000 Hz) rtp_packet[4] (*timestamp 24) 0xFF; rtp_packet[5] (*timestamp 16) 0xFF; rtp_packet[6] (*timestamp 8) 0xFF; rtp_packet[7] *timestamp 0xFF; // SSRC等字段省略... // 2. 封装H.264负载 (根据RFC 6184) if (nal_len 1400) { // 单包模式 (Single NALU) memcpy(rtp_packet rtp_header_len, nal_data, nal_len); send_packet(rtp_sock, dest_addr, rtp_packet, rtp_header_len nal_len); } else { // 分片模式 (FU-A) // FU indicator rtp_packet[rtp_header_len] (nal_data[0] 0xE0) | 28; // FU-A type28 // FU header (S|E|R) NALU type int offset 1; // 跳过NALU头 int remaining nal_len - offset; int max_payload_size 1400; int start 1; while (remaining 0) { int payload_size (remaining max_payload_size) ? max_payload_size : remaining; rtp_packet[rtp_header_len 1] (start ? 0x80 : 0x00) | // Start bit ((remaining payload_size 0) ? 0x40 : 0x00) | // End bit (nal_data[0] 0x1F); // NALU type memcpy(rtp_packet rtp_header_len 2, nal_data offset, payload_size); send_packet(rtp_sock, dest_addr, rtp_packet, rtp_header_len 2 payload_size); offset payload_size; remaining - payload_size; start 0; } } }2. RTSP拉流客户端实现要点拉流客户端与推流客户端步骤1-4类似区别在于第5步是接收并解析RTP包重组H.264 NALU。// 伪代码流程 (接收端) 14. 同推流客户端建立RTSP会话。 5. 在SETUP建立的UDP或TCP套接字上循环接收RTP包。 6. 解析RTP头获取序列号、时间戳、负载类型。 7. 根据RFC 6184解析H.264负载 单包模式直接提取NALU。 - FU-A分片模式缓存分片根据起始和结束标记重组完整的NALU。 8. 将重组后的NALU送入解码器如FFmpeg、硬件解码器或进行其他处理。关键代码片段解析RTP并重组H.264typedef struct { uint8_t buffer[MAX_NALU_SIZE]; int size; uint16_t seq; // 期望的下一个序列号 uint32_t timestamp; int fu_a_started; } NaluAssembler; void process_rtp_packet(NaluAssembler *assembler, const uint8_t *rtp_packet, int packet_len) { // 解析RTP头 int payload_type rtp_packet[1] 0x7F; uint16_t seq_num (rtp_packet[2] 8) | rtp_packet[3]; uint32_t timestamp (rtp_packet[4] 24) | (rtp_packet[5] 16) | (rtp_packet[6] 8) | rtp_packet[7]; const uint8_t *payload rtp_packet 12; // 假设无CSRC int payload_size packet_len - 12; if (payload_type ! 96) return; // 非H.264负载 uint8_t nal_unit_type payload[0] 0x1F; if (nal_unit_type 1 nal_unit_type 23) { // 单包NALU deliver_nalu(payload, payload_size, timestamp); } else if (nal_unit_type 28) { // FU-A uint8_t fu_header payload[1]; int start_bit (fu_header 7) 1; int end_bit (fu_header 6) 1; uint8_t reconstructed_nal_type fu_header 0x1F; if (start_bit) { // 开始分片 assembler-size 0; assembler-timestamp timestamp; assembler-fu_a_started 1; // 重组NALU头 assembler-buffer[0] (payload[0] 0xE0) | reconstructed_nal_type; assembler-size 1; } if (assembler-fu_a_started assembler-timestamp timestamp) { // 追加分片数据 memcpy(assembler-buffer assembler-size, payload 2, payload_size2); assembler-size (payload_size - 2); } if (end_bit assembler-fu_a_started assembler-timestamp timestamp) { // 分片结束交付完整NALU deliver_nalu(assembler-buffer, assembler-size, timestamp); assembler-fu_a_started 0; } } // 处理其他类型如STAP-A, MTAP16等 }三、 特定平台实现Android屏幕推流在Android上实现屏幕推流到RTSP服务器涉及屏幕捕获、编码和网络传输。// 核心步骤摘要 // 1. 获取MediaProjection权限创建VirtualDisplay捕获屏幕 MediaProjectionManager projectionManager (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); Intent captureIntent projectionManager.createScreenCaptureIntent(); startActivityForResult(captureIntent, REQUEST_CODE); // 在onActivityResult中 MediaProjection mediaProjection projectionManager.getMediaProjection(resultCode, data); // 配置视频尺寸、DPI等 VirtualDisplay virtualDisplay mediaProjection.createVirtualDisplay(ScreenCapture, width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null); // 2. 配置MediaCodec进行硬件编码H.264 MediaFormat format MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height); format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); MediaCodec encoder MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); Surface inputSurface encoder.createInputSurface(); encoder.start(); // 将VirtualDisplay的surface设置为编码器的inputSurface // virtualDisplay mediaProjection.createVirtualDisplay(..., inputSurface, ...); // 3. 从MediaCodec输出缓冲区获取编码后的H.264数据 MediaCodec.BufferInfo bufferInfo new MediaCodec.BufferInfo(); int outputBufferId encoder.dequeueOutputBuffer(bufferInfo, timeoutUs); if (outputBufferId 0) { ByteBuffer outputBuffer encoder.getOutputBuffer(outputBufferId); byte[] encodedData new byte[bufferInfo.size]; outputBuffer.get(encodedData); // 4. 将encodedDataH.264 NALU通过RTP协议发送至RTSP服务器 //需要实现RTSP客户端协议栈及RTP打包逻辑如第二部分所述 sendRtpPacket(encodedData, bufferInfo.presentationTimeUs); encoder.releaseOutputBuffer(outputBufferId, false); }四、 推拉流与AI处理结合常见场景是拉流-AI推理-推流。import cv2 import subprocess import threading # 1. RTSP拉流 rtsp_pull_url rtsp://server_ip:port/input_stream cap cv2.VideoCapture(rtsp_pull_url) # 2. 准备RTSP推流管道 (使用FFmpeg) rtsp_push_url rtsp://server_ip:port/output_stream command [ffmpeg, -y, -f, rawvideo, -vcodec, rawvideo, -pix_fmt, bgr24, -s, {}x{}.format(width, height), -r, str(fps), -i, -, -c:v, libx264, -pix_fmt, yuv420p, -preset, ultrafast, -f, rtsp, rtsp_push_url] p subprocess.Popen(command, stdinsubprocess.PIPE) while cap.isOpened(): ret, frame cap.read() if not ret: break # 3. AI推理处理 (例如YOLOv8) # results model(frame) # processed_frame plot_boxes(frame, results) processed_frame frame # 此处省略推理步骤 # 4.将处理后的帧推流 p.stdin.write(processed_frame.tobytes()) cap.release() p.stdin.close() p.wait()实现关键点总结协议理解必须理解RTSP信令控制、RTP数据传输、RTCP传输控制协议交互过程。数据封装视频编码数据如H.264 NALU需要按照RFC 6184规范封装成RTP包。注意NALU分片FU-A与组合。格式兼容确保推流端输出的H.264码流格式AVCC或Annex-B与接收端或服务器期望的格式匹配。例如某些解码器可能只支持Annex-B格式。网络适应处理网络抖动、丢包。使用RTCP进行QoS反馈必要时实现丢包重传或前向纠错。性能优化对于高帧率、高分辨率视频采用硬件编解码、多线程处理、零拷贝等技术降低延迟和CPU占用。参考来源h264 NAL解析代码实现rtsp推流拉流的方法(自己总结)Windows、linux一台电脑rtsp推流另一台拉流显示RTMP推流组件EasyRTMP实现内网摄像头RTSP拉流转码RTMP推流到RTMP服务器之Android版屏幕推流方法及其注意点RTMP推流组件EasyRTMP实现内网摄像头RTSP拉流转码RTMP推流到RTMP服务器之Android版屏幕推流方法及其注意点rtsp拉流yolov8推理rtsp推流