/** Start the player */
  public synchronized void start() {
    if (!opened) {
      // Player not opened
      return;
    }

    if (started) {
      // Already started
      return;
    }

    // Init NAL
    if (!initNAL()) {
      return;
    }
    nalInit = false;
    timeStamp = 0;

    // Start RTP layer
    rtpSender.startSession();

    // Player is started
    videoStartTime = SystemClock.uptimeMillis();
    started = true;
    frameProcess = new FrameProcess(selectedVideoCodec.getFramerate());
    frameProcess.start();
    notifyPlayerEventStarted();
  }
  /** Close the player */
  public void close() {
    if (!opened) {
      // Already closed
      return;
    }
    // Close the RTP layer
    rtpInput.close();
    rtpSender.stopSession();

    try {
      // Close the video encoder
      NativeH264Encoder.DeinitEncoder();
    } catch (UnsatisfiedLinkError e) {
      if (logger.isActivated()) {
        logger.error("Can't close correctly the encoder", e);
      }
    }

    // Player is closed
    opened = false;
    notifyPlayerEventClosed();
    listeners.clear();
  }
  /**
   * Open the player
   *
   * @param remoteHost Remote host
   * @param remotePort Remote port
   */
  public void open(String remoteHost, int remotePort) {
    if (opened) {
      // Already opened
      return;
    }

    // Check video codec
    if (selectedVideoCodec == null) {
      notifyPlayerEventError("Video codec not selected");
      return;
    }

    // Init video encoder
    try {
      NativeH264EncoderParams nativeH264EncoderParams = new NativeH264EncoderParams();

      // Codec dimensions
      nativeH264EncoderParams.setFrameWidth(selectedVideoCodec.getWidth());
      nativeH264EncoderParams.setFrameHeight(selectedVideoCodec.getHeight());
      nativeH264EncoderParams.setFrameRate(selectedVideoCodec.getFramerate());
      nativeH264EncoderParams.setBitRate(selectedVideoCodec.getBitrate());

      // Codec profile and level
      nativeH264EncoderParams.setProfilesAndLevel(selectedVideoCodec.getCodecParams());

      // Codec settings optimization
      nativeH264EncoderParams.setEncMode(NativeH264EncoderParams.ENCODING_MODE_STREAMING);
      nativeH264EncoderParams.setSceneDetection(false);

      if (logger.isActivated()) {
        logger.info(
            "Init H264Encoder "
                + selectedVideoCodec.getCodecParams()
                + " "
                + selectedVideoCodec.getWidth()
                + "x"
                + selectedVideoCodec.getHeight()
                + " "
                + selectedVideoCodec.getFramerate()
                + " "
                + selectedVideoCodec.getBitrate());
      }
      int result = NativeH264Encoder.InitEncoder(nativeH264EncoderParams);
      if (result != 0) {
        notifyPlayerEventError("Encoder init failed with error code " + result);
        return;
      }
    } catch (UnsatisfiedLinkError e) {
      notifyPlayerEventError(e.getMessage());
      return;
    }

    // Init the RTP layer
    try {
      releasePort();
      rtpSender = new VideoRtpSender(videoFormat, localRtpPort);
      rtpInput = new MediaRtpInput();
      rtpInput.open();
      if (videoRenderer != null) {
        // The video renderer is supposed to be opened and so we used its RTP stream
        if (logger.isActivated()) {
          logger.debug("Player shares the renderer RTP stream");
        }
        rtpSender.prepareSession(
            rtpInput, remoteHost, remotePort, videoRenderer.getRtpInputStream(), this);
      } else {
        // The video renderer doesn't exist and so we create a new RTP stream
        rtpSender.prepareSession(rtpInput, remoteHost, remotePort, this);
      }

    } catch (Exception e) {
      notifyPlayerEventError(e.getMessage());
      return;
    }

    // Player is opened
    opened = true;
    notifyPlayerEventOpened();
  }