java 结合zlmediakit将rtsp流转换为websocket-flv推送给前端浏览器进行播放的代码
时间: 2023-06-18 13:03:58 浏览: 421
以下是Java结合ZLMediaKit将RTSP流转换为WebSocket-FLV推送给前端浏览器进行播放的代码:
```java
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.util.ReferenceCountUtil;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;
import org.zeromq.ZMsg;
import java.io.IOException;
public class RtspToWebSocketHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger(RtspToWebSocketHandler.class);
private ZMQ.Context context;
private ZMQ.Socket subscriber;
private ZMQ.Socket publisher;
private String publisherAddress;
private String subscriberAddress;
private String rtspUrl;
private String roomId;
private String sdp;
private String tag;
private boolean isPushStream;
public RtspToWebSocketHandler(String publisherAddress, String subscriberAddress, String rtspUrl, String roomId, String sdp, String tag) {
this.publisherAddress = publisherAddress;
this.subscriberAddress = subscriberAddress;
this.rtspUrl = rtspUrl;
this.roomId = roomId;
this.sdp = sdp;
this.tag = tag;
this.isPushStream = false;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (FullHttpRequest) msg);
} else if (msg instanceof WebSocketFrame) {
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
} else {
ReferenceCountUtil.release(msg);
}
}
private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
// Handle a bad request.
if (!req.decoderResult().isSuccess()) {
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
return;
}
// Allow only GET methods.
if (req.method() != GET) {
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
return;
}
// Handshake
WebSocketServerProtocolHandler wsHandler = new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true, 65536);
wsHandler.handshake(ctx.channel(), req);
}
private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
// Check for closing frame
if (frame instanceof CloseWebSocketFrame) {
ctx.close();
return;
}
// Check for ping frame
if (frame instanceof PingWebSocketFrame) {
ctx.write(new PongWebSocketFrame(frame.content().retain()));
return;
}
// Check for binary frame
if (!(frame instanceof BinaryWebSocketFrame)) {
throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass().getName()));
}
// Start pushing stream
if (!isPushStream) {
logger.info("Start pushing stream. roomId: {}", roomId);
startPushStream();
isPushStream = true;
}
// Send WebSocket frame to ZLMediaKit
BinaryWebSocketFrame binaryWebSocketFrame = (BinaryWebSocketFrame) frame;
byte[] data = binaryWebSocketFrame.content().nioBuffer().array();
publisher.sendMore(roomId).send(data);
}
private void startPushStream() {
// Create ZMQ context and sockets
context = ZMQ.context(1);
subscriber = context.socket(ZMQ.SUB);
publisher = context.socket(ZMQ.PUB);
// Connect to subscriber and publisher
subscriber.connect(subscriberAddress);
subscriber.subscribe(tag.getBytes());
publisher.connect(publisherAddress);
// Send stream info to ZLMediaKit
JSONObject jsonObject = new JSONObject();
jsonObject.put("api", "addMediaSource");
jsonObject.put("url", rtspUrl);
jsonObject.put("vhost", "default");
jsonObject.put("enable_rtsp", true);
jsonObject.put("enable_rtp", true);
jsonObject.put("enable_tcp", true);
jsonObject.put("enable_udp", true);
jsonObject.put("timeout_sec", 30);
jsonObject.put("merge", true);
jsonObject.put("hls_enabled", false);
jsonObject.put("mp4_enabled", false);
jsonObject.put("record_enabled", false);
jsonObject.put("broadcast_enabled", true);
jsonObject.put("room_id", roomId);
jsonObject.put("sdp", sdp);
publisher.sendMore(tag).send(jsonObject.toJSONString().getBytes());
logger.info("Send stream info to ZLMediaKit. roomId: {}, rtspUrl: {}", roomId, rtspUrl);
// Start receiving stream from ZLMediaKit
new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
ZMsg zMsg = ZMsg.recvMsg(subscriber);
if (zMsg == null) {
continue;
}
byte[] data = zMsg.getLast().getData();
BinaryWebSocketFrame binaryWebSocketFrame = new BinaryWebSocketFrame(Unpooled.wrappedBuffer(data));
ctx.channel().writeAndFlush(binaryWebSocketFrame);
logger.debug("Received stream from ZLMediaKit. roomId: {}, data length: {}", roomId, data.length);
} catch (IOException e) {
logger.error("Error receiving stream from ZLMediaKit. roomId: {}", roomId, e);
break;
}
}
// Close sockets
subscriber.close();
publisher.close();
context.term();
}).start();
}
private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
// Generate an error page if response getStatus code is not OK (200).
if (res.status().code() != 200) {
ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
res.content().writeBytes(buf);
buf.release();
HttpHeaderUtil.setContentLength(res, res.content().readableBytes());
}
// Send the response and close the connection if necessary.
ChannelFuture f = ctx.channel().writeAndFlush(res);
if (!HttpHeaderUtil.isKeepAlive(req) || res.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
}
```
使用方法:
1. 在Netty的ChannelPipeline中加入RtspToWebSocketHandler。
2. 当前端连接WebSocket时,会触发RtspToWebSocketHandler的channelRead方法,此时需要调用WebSocketServerProtocolHandler的handshake方法进行握手。
3. 当前端发送WebSocket帧时,会触发RtspToWebSocketHandler的channelRead方法,此时会将WebSocket帧发送给ZLMediaKit,ZLMediaKit会将转换后的FLV数据发送给RtspToWebSocketHandler,RtspToWebSocketHandler再将FLV数据发送给前端浏览器进行播放。
4. 当前端关闭WebSocket时,会触发RtspToWebSocketHandler的handleWebSocketFrame方法,此时需要关闭ZMQ的subscriber和publisher。
注意事项:
1. 代码中的ZLMediaKit的接口参数可能与实际情况不符,需要根据实际情况进行修改。
2. 代码中的ZMQ版本为4.x,如果使用的是3.x版本需要进行相应修改。
3. 代码中使用了Fastjson和Netty,需要进行相应依赖的引入。
阅读全文