@Override
 public void onData(MessageInfo mi, RTMPCommand command) {
   String handlerName = (String) command.getArguments().get(0);
   if (HANDLER_NAME_SET_DATA_FRAME.equals(handlerName)) {
     metaData = (Map) command.getArguments().get(2);
     System.err.println("metadata: " + command.getArguments());
   } else {
     System.err.println("Unknown data: " + command.getArguments());
   }
 }
  @Override
  public void invokeCommand(MessageInfo mi, RTMPCommand command) {
    // System.err.println("cmd: " + command.getName() + " " + command.getArguments());

    if (command.getName().equals("connect")) {
      cmdConnect(mi, command);

    } else if (command.getName().equals("releaseStream")) {
      sendSuccess(mi, command);

    } else if (command.getName().equals("FCUnpublish")) {
      // sendSuccess(mi, command);
      context.terminate();

    } else if (command.getName().equals("createStream")) {
      cmdCreateStream(mi, command);

    } else if (command.getName().equals("FCPublish")) {
      cmdFCPublish(mi, command);

    } else if (command.getName().equals("publish")) {
      cmdPublish(mi, command);

    } else if (command.getName().equals("deleteStream")) {
      cmdDeleteStream(mi, command);

    } else {
      System.err.println("Unknown command: " + command.getName());
      sendError(mi, command, null, null);
    }
  }
 protected void cmdCreateStream(MessageInfo mi, RTMPCommand command) {
   AMFPacket response = new AMFPacket();
   response.writeString("_result");
   response.writeNumber(command.getTxid());
   response.writeMixed(null);
   response.writeMixed(1d);
   context.writeCommand(mi.chunkStreamID, mi.messageStreamID, response);
 }
 protected void sendError(MessageInfo mi, RTMPCommand command, Object object, Object information) {
   AMFPacket response = new AMFPacket();
   response.writeString("_error");
   response.writeNumber(command.getTxid());
   response.writeMixed(object);
   response.writeMixed(information);
   context.writeCommand(mi.chunkStreamID, mi.messageStreamID, response);
 }
  protected void cmdConnect(MessageInfo mi, RTMPCommand command) {

    AMFPacket response = new AMFPacket();
    response.writeString("_result");
    response.writeNumber(command.getTxid());

    Map<String, Object> arg1 = new HashMap<>();
    arg1.put("fmsVer", "FMS/3,5,5,2004");
    arg1.put("capabilities", 31d);
    arg1.put("mode", 1d);
    response.writeObject(arg1);

    Map<String, Object> arg2 = new HashMap<>();
    arg2.put("level", "status");
    arg2.put("code", "NetConnection.Connect.Success");
    arg2.put("description", "Connection succeeded");
    arg2.put("data", new Object[0]);
    arg2.put("clientId", 1d);
    arg2.put("objectEncoding", 0d);
    response.writeObject(arg2);

    context.writeCommand(mi.chunkStreamID, mi.messageStreamID, response);
  }
  protected void cmdPublish(MessageInfo mi, RTMPCommand command) {

    Object arg1 = command.getArguments().get(0);
    if (!(arg1 instanceof String)) {
      sendSuccess(mi, command);
      return;
    }

    String streamName = (String) command.getArguments().get(0);
    int separatorPos = streamName.indexOf(STREAM_NAME_SEPARATOR);
    if (separatorPos == -1) {
      Map<String, Object> info = new HashMap<>();
      info.put("code", "NetStream.Publish.BadName"); // with lack of better choice
      info.put("level", "error");
      info.put("description", "Stream password required.");
      terminateWithError(mi, command, null, info);
      return;
    }
    String clientStreamID = streamName.substring(0, separatorPos);
    String clientSecret = streamName.substring(separatorPos + 1);

    String streamProperty = props.getProperty("streams." + clientStreamID);
    if (!"true".equals(streamProperty) && !"1".equals(streamProperty)) {
      Map<String, Object> info = new HashMap<>();
      info.put("code", "NetStream.Publish.BadName"); // with lack of better choice
      info.put("level", "error");
      info.put("description", "Stream does not exist.");
      terminateWithError(mi, command, null, info);
      return;
    }

    String streamSectret = props.getProperty("streams." + clientStreamID + ".password");
    if (streamSectret == null) {
      Map<String, Object> info = new HashMap<>();
      info.put("code", "NetStream.Publish.BadName"); // with lack of better choice
      info.put("level", "error");
      info.put("description", "Stream does not exist.");
      terminateWithError(mi, command, null, info);
      return;
    }
    if (!clientSecret.equals(streamSectret)) {
      Map<String, Object> info = new HashMap<>();
      info.put("code", "NetStream.Publish.BadName"); // with lack of better choice
      info.put("level", "error");
      info.put("description", "Authentication error.");
      terminateWithError(mi, command, null, info);
      return;
    }

    // raise limit after successful authentication
    context.getLimit().assemblyBufferSize = 256 * 1024;
    context.getLimit().assemblyBufferCount = 16;
    context.getLimit().chunkStreamCount = 32;

    streamID = clientStreamID;

    AMFPacket response = new AMFPacket();
    response.writeString("onStatus");
    response.writeNumber(fcpublishTxID);

    response.writeMixed(null);

    Map<String, Object> arg2 = new HashMap<>();
    arg2.put("level", "status");
    arg2.put("code", "NetStream.Publish.Start");
    arg2.put("description", "");
    arg2.put("details", command.getArguments().get(0));
    arg2.put("clientId", 1d);
    arg2.put("objectEncoding", 0d);
    response.writeObject(arg2);

    context.writeCommand(mi.chunkStreamID, mi.messageStreamID, response);
  }
 protected void cmdFCPublish(MessageInfo mi, RTMPCommand command) {
   fcpublishTxID = command.getTxid();
   sendSuccess(mi, command);
 }