private static void sendHttpResponse(
      ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {

    // ����Ӧ����ͻ���
    if (res.getStatus().code() != 200) {
      ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
      res.content().writeBytes(buf);
      buf.release();
    }

    // ����Ƿ�Keep-Alive���ر�����
    ChannelFuture f = ctx.channel().writeAndFlush(res);
    if (!isKeepAlive(req) || res.getStatus().code() != 200) {
      f.addListener(ChannelFutureListener.CLOSE);
    }
  }
  private void messageReceive(ChannelHandlerContext ctx, DefaultFullHttpResponse response) {
    /** send next cmd * */
    switch (state) {
      case PLAY:
        String rtpInfo = response.headers().get("RTP-Info");

        setState(RTSP.PLAYING);
        break;
      case SETUP:
        // session
        String sessionId = response.headers().get("Session");
        if (null != sessionId) {
          Matcher matcher = Pattern.compile("([^;]+)").matcher(sessionId);
          if (matcher.find()) {
            rtspStack.setSession(matcher.group(1));
          } else {
            rtspStack.setSession(sessionId);
          }
        }

        // transport
        InetSocketAddress remoteHost = (InetSocketAddress) ctx.channel().remoteAddress();
        String transport = response.headers().get("Transport");
        RtpSessionExt rtp = rtspStack.getLastRtpSession();
        Matcher matcher = Pattern.compile("([^\\s=;]+)=(([^-;]+)(-([^;]+))?)").matcher(transport);
        while (matcher.find()) {
          String key = matcher.group(1).toLowerCase();
          if ("server_port".equals(key)) {
            rtp.connectRtp(
                new InetSocketAddress(
                    remoteHost.getHostString(), Integer.valueOf(matcher.group(3))));
            rtp.connectRtcp(
                new InetSocketAddress(
                    remoteHost.getHostString(), Integer.valueOf(matcher.group(5))));
          } else if ("ssrc".equals(key)) {
            rtp.setSsrc(Long.parseLong(matcher.group(2).trim(), 16));
          } else if ("interleaved".equals(key)) {
            rtp.setRtpChunk(Integer.valueOf(matcher.group(3)));
            rtp.setRtcpChunk(Integer.valueOf(matcher.group(5)));
          } else {
            logger.warn("ignored [{}={}]", key, matcher.group(2));
          }
        }

        // next action
        boolean finish = setup(sd, mediaIndex++);
        if (finish) {
          sendPlay();
          setState(RTSP.PLAY);
        }

        break;
      case DESCRIBE:
        if (response.getStatus().code() == HttpResponseStatus.UNAUTHORIZED.code()) {
          sendDescribe(buildAuthorizationString(response));
          break;
        }

        String desciptor = response.content().toString(Charset.forName("UTF8"));
        SessionDescriptionImpl sd = new SessionDescriptionImpl();
        StringTokenizer tokenizer = new StringTokenizer(desciptor);
        while (tokenizer.hasMoreChars()) {
          String line = tokenizer.nextToken();

          try {
            SDPParser paser = ParserFactory.createParser(line);
            if (null != paser) {
              SDPField obj = paser.parse();
              sd.addField(obj);
            }
          } catch (ParseException e) {
            logger.warn("fail parse [{}]", line, e);
          }
        }
        this.sd = sd;

        mediaIndex = 0;
        setup(sd, mediaIndex++);

        // 心跳
        rtspStack
            .getChannel()
            .pipeline()
            .addFirst("ping", new IdleStateHandler(30, 15, 13, TimeUnit.SECONDS));

        //
        rtspStack.setSessionDescription(sd);
        break;
      case OPTIONS:
        if (response.getStatus().code() == HttpResponseStatus.UNAUTHORIZED.code()) {
          sendDescribe(buildAuthorizationString(response));
        } else {
          sendDescribe();
        }

        setState(RTSP.DESCRIBE);
        break;
      case PLAYING:
        break;
      default:
        logger.warn("I dont't Known What to do with {}", response);
        break;
    }
  }