/**
   * Configures encoder and muxer state, and prepares the input Surface. Initializes mEncoder,
   * mMuxer, mRecordingSurface, mBufferInfo, mTrackIndex, and mMuxerStarted.
   */
  private void configureMediaCodecEncoder() {
    mBufferInfo = new MediaCodec.BufferInfo();
    MediaFormat format =
        MediaFormat.createVideoFormat(MIME_TYPE, mStreamSize.getWidth(), mStreamSize.getHeight());
    /**
     * Set encoding properties. Failing to specify some of these can cause the MediaCodec
     * configure() call to throw an exception.
     */
    format.setInteger(
        MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
    format.setInteger(MediaFormat.KEY_BIT_RATE, mEncBitRate);
    format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
    Log.i(TAG, "configure video encoding format: " + format);

    // Create/configure a MediaCodec encoder.
    mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
    mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    mRecordingSurface = mEncoder.createInputSurface();
    mEncoder.start();

    String outputFileName = getOutputMediaFileName();
    if (outputFileName == null) {
      throw new IllegalStateException("Failed to get video output file");
    }

    /**
     * Create a MediaMuxer. We can't add the video track and start() the muxer until the encoder
     * starts and notifies the new media format.
     */
    try {
      mMuxer = new MediaMuxer(outputFileName, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
    } catch (IOException ioe) {
      throw new IllegalStateException("MediaMuxer creation failed", ioe);
    }
    mMuxerStarted = false;
  }
  /**
   * Configure stream with a size and encoder mode.
   *
   * @param size Size of recording stream.
   * @param useMediaCodec The encoder for this stream to use, either MediaCodec or MediaRecorder.
   * @param bitRate Bit rate the encoder takes.
   */
  public synchronized void configure(Size size, boolean useMediaCodec, int bitRate) {
    if (getStreamState() == STREAM_STATE_RECORDING) {
      throw new IllegalStateException("Stream can only be configured when stream is in IDLE state");
    }

    boolean isConfigChanged =
        (!mStreamSize.equals(size))
            || (mUseMediaCodec != useMediaCodec)
            || (mEncBitRate != bitRate);

    mStreamSize = size;
    mUseMediaCodec = useMediaCodec;
    mEncBitRate = bitRate;

    if (mUseMediaCodec) {
      if (getStreamState() == STREAM_STATE_CONFIGURED) {
        /**
         * Stream is already configured, need release encoder and muxer first, then reconfigure only
         * if configuration is changed.
         */
        if (!isConfigChanged) {
          /**
           * TODO: this is only the skeleton, it is tricky to implement because muxer need
           * reconfigure always. But muxer is closely coupled with MediaCodec for now because muxer
           * can only be started once format change callback is sent from mediacodec. We need
           * decouple MediaCodec and Muxer for future.
           */
        }
        releaseEncoder();
        releaseMuxer();
        configureMediaCodecEncoder();
      } else {
        configureMediaCodecEncoder();
      }
    } else {
      // TODO: implement MediaRecoder mode.
      Log.w(TAG, "MediaRecorder configure is not implemented yet");
    }

    setStreamState(STREAM_STATE_CONFIGURED);
  }