8大网盘直链下载助手:解锁全平台高速下载的技术解析与实践指南
2026/4/22 12:56:40
GB28181 协议中,媒体流通常以PS (Program Stream)格式封装在 RTP 包中传输(RTP over UDP 或 TCP)。JavaCV(基于 FFmpeg)可以直接处理 PS 流,但对于 RTP 封装的 PS 流,需要先剥离 RTP 头,组装成完整的 PS 数据,然后喂给 FFmpegFrameGrabber 解码,再用 FFmpegFrameRecorder 转推 RTMP。
PipedOutputStream+PipedInputStream将组装的 PS 数据写入管道。FFmpegFrameGrabber从管道读取 PS 流,设置格式为 “mpegps” 或直接 “ps”。FFmpegFrameRecorder录制抓取的帧到 RTMP URL。<dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.10</version><!-- 最新版 --></dependency>importorg.bytedeco.javacv.*;importorg.bytedeco.javacpp.avcodec;importjava.io.PipedInputStream;importjava.io.PipedOutputStream;importjava.util.concurrent.ConcurrentLinkedDeque;// RTP 接收处理器(示例,用 Netty 或 UDP Socket 替换)publicclassRtpPsParserimplementsRunnable{privatePipedOutputStreampos=newPipedOutputStream();privatePipedInputStreampis=newPipedInputStream(pos,1024*1024);privateConcurrentLinkedDeque<byte[]>packetQueue=newConcurrentLinkedDeque<>();privatebooleanrunning=true;// 假设这里接收到 RTP 数据包(byte[] data)publicvoidonRtpPacket(byte[]data,intoffset,intlen){// 剥离 RTP 头(UDP 示例,TCP 多 +2 字节长度)byte[]psData=newbyte[len-12];System.arraycopy(data,12,psData,0,psData.length);packetQueue.offer(psData);// 简单缓存,实际需按 seq 排序去重}@Overridepublicvoidrun(){while(running){if(!packetQueue.isEmpty()){byte[]psChunk=packetQueue.poll();try{pos.write(psChunk);// 写入管道,组装 PS 流pos.flush();}catch(Exceptione){e.printStackTrace();}}}}publicPipedInputStreamgetInputStream(){returnpis;}}// 主推流类publicclassGb28181ToRtmp{publicstaticvoidmain(String[]args)throwsException{RtpPsParserparser=newRtpPsParser();newThread(parser).start();// Grabber:从管道读取 PS 流FFmpegFrameGrabbergrabber=newFFmpegFrameGrabber(parser.getInputStream());grabber.setFormat("ps");// 或 "mpegps"grabber.setOption("rtsp_transport","tcp");// 如果是 TCP interleavedgrabber.setVideoCodec(avcodec.AV_CODEC_ID_H264);grabber.setFrameRate(25);grabber.setImageWidth(1920);// 根据实际调整grabber.setImageHeight(1080);grabber.start();// Recorder:推 RTMPFFmpegFrameRecorderrecorder=newFFmpegFrameRecorder("rtmp://your-server/live/stream",grabber.getImageWidth(),grabber.getImageHeight());recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);recorder.setFormat("flv");recorder.setFrameRate(grabber.getFrameRate());recorder.setVideoBitrate(2000000);recorder.setVideoOption("preset","veryfast");recorder.setVideoOption("tune","zerolatency");recorder.start();Frameframe;while((frame=grabber.grabFrame())!=null){recorder.record(frame);// 直接转发帧}grabber.stop();recorder.stop();}}SsrcUdpHandler或SsrcTcpHandler类。$+ 长度(2 字节),需额外剥离。ffplay rtp://ip:port(需 SDP)。此方式已验证可行(参考 eguid 等博客),适合学习/小型项目。大规模建议用成熟框架如 ZLMediaKit。需要完整 RTP 解析代码或特定厂商适配,欢迎提供更多细节!