FlvHandler.java 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package com.zj.server;
  2. import java.util.HashMap;
  3. import java.util.List;
  4. import java.util.Map;
  5. import com.zj.entity.Camera;
  6. import com.zj.service.CameraRepository;
  7. import com.zj.service.MediaService;
  8. import cn.hutool.core.util.StrUtil;
  9. import io.netty.buffer.Unpooled;
  10. import io.netty.channel.ChannelFutureListener;
  11. import io.netty.channel.ChannelHandlerContext;
  12. import io.netty.channel.SimpleChannelInboundHandler;
  13. import io.netty.handler.codec.http.DefaultFullHttpResponse;
  14. import io.netty.handler.codec.http.DefaultHttpResponse;
  15. import io.netty.handler.codec.http.FullHttpRequest;
  16. import io.netty.handler.codec.http.FullHttpResponse;
  17. import io.netty.handler.codec.http.HttpHeaderNames;
  18. import io.netty.handler.codec.http.HttpHeaderValues;
  19. import io.netty.handler.codec.http.HttpResponse;
  20. import io.netty.handler.codec.http.HttpResponseStatus;
  21. import io.netty.handler.codec.http.HttpVersion;
  22. import io.netty.handler.codec.http.QueryStringDecoder;
  23. import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
  24. import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
  25. import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
  26. import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
  27. import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
  28. import io.netty.handler.codec.http.websocketx.WebSocketFrame;
  29. import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
  30. import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
  31. import io.netty.util.CharsetUtil;
  32. import lombok.extern.slf4j.Slf4j;
  33. // http://localhost:8866/live?url=rtsp://admin:VZCDOY@192.168.2.84:554/Streaming/Channels/102
  34. // ws://localhost:8866/live?url=rtsp://admin:VZCDOY@192.168.2.84:554/Streaming/Channels/102
  35. @Slf4j
  36. public class FlvHandler extends SimpleChannelInboundHandler<Object> {
  37. private MediaService mediaService;
  38. private WebSocketServerHandshaker handshaker;
  39. private CameraRepository cameraRepository;
  40. public FlvHandler(MediaService mediaService, CameraRepository cameraRepository) {
  41. super();
  42. this.mediaService = mediaService;
  43. this.cameraRepository = cameraRepository;
  44. }
  45. @Override
  46. protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
  47. if (msg instanceof FullHttpRequest) {
  48. FullHttpRequest req = (FullHttpRequest) msg;
  49. // Map<String, String> parmMap = new RequestParser(msg).parse();
  50. QueryStringDecoder decoder = new QueryStringDecoder(req.uri());
  51. // 判断请求uri
  52. if (!"/live".equals(decoder.path())) {
  53. log.info("uri有误");
  54. sendError(ctx, HttpResponseStatus.BAD_REQUEST);
  55. return;
  56. }
  57. Map<String, String> parse = parseUrl(req.uri());
  58. String streamUrl = parse.get("url");
  59. String autoCloseStr = parse.get("autoClose");
  60. if (StrUtil.isBlank(streamUrl)) {
  61. log.info("url有误");
  62. sendError(ctx, HttpResponseStatus.BAD_REQUEST);
  63. return;
  64. }
  65. Camera camera = new Camera();
  66. camera.setUrl(streamUrl);
  67. //是否需要自动关闭流
  68. boolean autoClose = true;
  69. if (StrUtil.isNotBlank(autoCloseStr)) {
  70. if ("false".equals(autoCloseStr)) {
  71. autoClose = false;
  72. cameraRepository.add(camera);
  73. }
  74. }
  75. if (!req.decoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade")))) {
  76. // http请求
  77. sendFlvReqHeader(ctx);
  78. mediaService.playForHttp(camera, ctx, autoClose);
  79. } else {
  80. // websocket握手,请求升级
  81. // 参数分别是ws地址,子协议,是否扩展,最大frame长度
  82. WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory(
  83. getWebSocketLocation(req), null, true, 5 * 1024 * 1024);
  84. handshaker = factory.newHandshaker(req);
  85. if (handshaker == null) {
  86. WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
  87. } else {
  88. handshaker.handshake(ctx.channel(), req);
  89. mediaService.playForWs(camera, ctx, autoClose);
  90. }
  91. }
  92. } else if (msg instanceof WebSocketFrame) {
  93. handleWebSocketRequest(ctx, (WebSocketFrame) msg);
  94. }
  95. }
  96. @Override
  97. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  98. // 添加连接
  99. }
  100. @Override
  101. public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  102. // 断开连接
  103. }
  104. /**
  105. * ws握手地址
  106. */
  107. private String getWebSocketLocation(FullHttpRequest request) {
  108. String location = request.headers().get(HttpHeaderNames.HOST) + request.uri();
  109. return "ws://" + location;
  110. }
  111. /**
  112. * 发送req header,告知浏览器是flv格式
  113. *
  114. * @param ctx
  115. */
  116. private void sendFlvReqHeader(ChannelHandlerContext ctx) {
  117. HttpResponse rsp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
  118. rsp.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE)
  119. .set(HttpHeaderNames.CONTENT_TYPE, "video/x-flv").set(HttpHeaderNames.ACCEPT_RANGES, "bytes")
  120. .set(HttpHeaderNames.PRAGMA, "no-cache").set(HttpHeaderNames.CACHE_CONTROL, "no-cache")
  121. .set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED).set(HttpHeaderNames.SERVER, "zhang");
  122. ctx.writeAndFlush(rsp);
  123. }
  124. /**
  125. * 错误请求响应
  126. *
  127. * @param ctx
  128. * @param status
  129. */
  130. private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
  131. FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status,
  132. Unpooled.copiedBuffer("请求地址有误: " + status + "\r\n", CharsetUtil.UTF_8));
  133. response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
  134. ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
  135. }
  136. /**
  137. * websocket处理
  138. *
  139. * @param ctx
  140. * @param frame
  141. */
  142. private void handleWebSocketRequest(ChannelHandlerContext ctx, WebSocketFrame frame) {
  143. // 关闭
  144. if (frame instanceof CloseWebSocketFrame) {
  145. handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
  146. return;
  147. }
  148. // 握手PING/PONG
  149. if (frame instanceof PingWebSocketFrame) {
  150. ctx.write(new PongWebSocketFrame(frame.content().retain()));
  151. return;
  152. }
  153. // 文本
  154. if (frame instanceof TextWebSocketFrame) {
  155. return;
  156. }
  157. if (frame instanceof BinaryWebSocketFrame) {
  158. return;
  159. }
  160. }
  161. private Map<String, String> parseUrl(String url) {
  162. String[] split = url.split("url=");
  163. String urlParent = split[1];
  164. Map<String, String> pMap = new HashMap<String, String>();
  165. //有autoClose
  166. if(urlParent.indexOf("autoClose") != -1) {
  167. String[] urlChild = urlParent.split("\\?");
  168. if(urlChild.length > 1) {
  169. String rUrl = urlChild[0];
  170. String fullParam = urlChild[1];
  171. String[] params = fullParam.split("&");
  172. StringBuffer sb = new StringBuffer();
  173. sb.append(rUrl);
  174. sb.append("?");
  175. for (String param : params) {
  176. if(param.indexOf("autoClose") == -1) {
  177. sb.append(param);
  178. sb.append("&");
  179. } else {
  180. String[] as = param.split("=");
  181. pMap.put(as[0], as[1]);
  182. }
  183. }
  184. int lastIndexOf = sb.toString().lastIndexOf("&");
  185. String xxx = sb.toString().substring(0, lastIndexOf);
  186. pMap.put("url", xxx);
  187. }
  188. } else {
  189. pMap.put("url", urlParent);
  190. }
  191. return pMap;
  192. }
  193. }