protected void prepareVideo(
      IVideoResampler[] iVideoResamplers,
      IVideoPicture[] inputIVideoPictures,
      IVideoPicture[] outputIVideoPictures,
      IStreamCoder inputIStreamCoder,
      IStreamCoder[] outputIStreamCoders,
      IContainer outputIContainer,
      IStream[] outputIStreams,
      ICodec.Type inputICodecType,
      String outputURL,
      int index)
      throws Exception {

    ICodec iCodec = getVideoEncodingICodec(inputICodecType, outputURL);

    if (iCodec == null) {
      throw new RuntimeException(
          "Unable to determine " + inputICodecType + " encoder for " + outputURL);
    }

    IStream outputIStream = outputIContainer.addNewStream(iCodec);

    outputIStreams[index] = outputIStream;

    IStreamCoder outputIStreamCoder = outputIStream.getStreamCoder();

    outputIStreamCoders[index] = outputIStreamCoder;

    int bitRate = inputIStreamCoder.getBitRate();

    if (_log.isInfoEnabled()) {
      _log.info("Original video bitrate " + bitRate);
    }

    bitRate = getVideoBitRate(bitRate);

    if (_log.isInfoEnabled()) {
      _log.info("Modified video bitrate " + bitRate);
    }

    outputIStreamCoder.setBitRate(bitRate);

    IRational iRational = inputIStreamCoder.getFrameRate();

    if (_log.isInfoEnabled()) {
      _log.info(
          "Original frame rate " + iRational.getNumerator() + "/" + iRational.getDenominator());
    }

    iRational = getVideoFrameRate(iRational);

    if (_log.isInfoEnabled()) {
      _log.info(
          "Modified frame rate " + iRational.getNumerator() + "/" + iRational.getDenominator());
    }

    outputIStreamCoder.setFrameRate(iRational);

    if (inputIStreamCoder.getHeight() <= 0) {
      throw new RuntimeException("Unable to determine height for " + _inputURL);
    }

    if (_height == 0) {
      _height = inputIStreamCoder.getHeight();
    }

    outputIStreamCoder.setHeight(_height);

    outputIStreamCoder.setPixelType(Type.YUV420P);
    outputIStreamCoder.setTimeBase(
        IRational.make(iRational.getDenominator(), iRational.getNumerator()));

    if (inputIStreamCoder.getWidth() <= 0) {
      throw new RuntimeException("Unable to determine width for " + _inputURL);
    }

    if (_width == 0) {
      _width = inputIStreamCoder.getWidth();
    }

    outputIStreamCoder.setWidth(_width);

    iVideoResamplers[index] =
        createIVideoResampler(inputIStreamCoder, outputIStreamCoder, _height, _width);

    inputIVideoPictures[index] =
        IVideoPicture.make(
            inputIStreamCoder.getPixelType(),
            inputIStreamCoder.getWidth(),
            inputIStreamCoder.getHeight());
    outputIVideoPictures[index] =
        IVideoPicture.make(
            outputIStreamCoder.getPixelType(),
            outputIStreamCoder.getWidth(),
            outputIStreamCoder.getHeight());

    ICodec.ID iCodecID = iCodec.getID();

    if (iCodecID.equals(ICodec.ID.CODEC_ID_H264)) {
      Configuration.configure(_ffpresetProperties, outputIStreamCoder);
    }
  }
  protected void prepareVideo(
      IVideoResampler[] iVideoResamplers,
      IVideoPicture[] inputIVideoPictures,
      IVideoPicture[] outputIVideoPictures,
      IStreamCoder inputIStreamCoder,
      IStreamCoder[] outputIStreamCoders,
      IContainer outputIContainer,
      IStream[] outputIStreams,
      ICodec.Type inputICodecType,
      String outputURL,
      int index)
      throws Exception {

    IStream outputIStream = outputIContainer.addNewStream(index);

    outputIStreams[index] = outputIStream;

    IStreamCoder outputIStreamCoder = outputIStream.getStreamCoder();

    outputIStreamCoders[index] = outputIStreamCoder;

    int bitRate = inputIStreamCoder.getBitRate();

    if (_log.isInfoEnabled()) {
      _log.info("Original video bitrate " + bitRate);
    }

    if (bitRate == 0) {
      bitRate =
          GetterUtil.getInteger(_videoBitRateMap.get(_outputVideoFormat), _VIDEO_BIT_RATE_DEFAULT);
    } else if (bitRate > _VIDEO_BIT_RATE_MAX) {
      bitRate = _VIDEO_BIT_RATE_MAX;
    }

    if (_log.isInfoEnabled()) {
      _log.info("Modified video bitrate " + bitRate);
    }

    outputIStreamCoder.setBitRate(bitRate);

    ICodec iCodec = ICodec.guessEncodingCodec(null, null, outputURL, null, inputICodecType);

    if (_outputVideoFormat.equals("mp4")) {
      iCodec = ICodec.findEncodingCodec(ICodec.ID.CODEC_ID_H264);
    }

    if (iCodec == null) {
      throw new RuntimeException(
          "Unable to determine " + inputICodecType + " encoder for " + outputURL);
    }

    outputIStreamCoder.setCodec(iCodec);

    IRational iRational = inputIStreamCoder.getFrameRate();

    if (_log.isInfoEnabled()) {
      _log.info(
          "Original frame rate " + iRational.getNumerator() + "/" + iRational.getDenominator());
    }

    if (_videoFrameRateMap.containsKey(_outputVideoFormat)) {
      iRational = _videoFrameRateMap.get(_outputVideoFormat);
    }

    if (_log.isInfoEnabled()) {
      _log.info(
          "Modified frame rate " + iRational.getNumerator() + "/" + iRational.getDenominator());
    }

    outputIStreamCoder.setFrameRate(iRational);

    if (inputIStreamCoder.getHeight() <= 0) {
      throw new RuntimeException("Unable to determine height for " + _inputURL);
    }

    outputIStreamCoder.setHeight(_height);

    outputIStreamCoder.setPixelType(Type.YUV420P);
    outputIStreamCoder.setTimeBase(
        IRational.make(iRational.getDenominator(), iRational.getNumerator()));

    if (inputIStreamCoder.getWidth() <= 0) {
      throw new RuntimeException("Unable to determine width for " + _inputURL);
    }

    outputIStreamCoder.setWidth(_width);

    iVideoResamplers[index] =
        createIVideoResampler(inputIStreamCoder, outputIStreamCoder, _height, _width);

    inputIVideoPictures[index] =
        IVideoPicture.make(
            inputIStreamCoder.getPixelType(),
            inputIStreamCoder.getWidth(),
            inputIStreamCoder.getHeight());
    outputIVideoPictures[index] =
        IVideoPicture.make(
            outputIStreamCoder.getPixelType(),
            outputIStreamCoder.getWidth(),
            outputIStreamCoder.getHeight());

    ICodec.ID iCodecID = iCodec.getID();

    if (iCodecID.equals(ICodec.ID.CODEC_ID_H264)) {
      Configuration.configure(_ffpresetProperties, outputIStreamCoder);
    }
  }
  /**
   * The playSong method is responsible for opening a Xuggler container to play song at provided
   * location.
   *
   * @param songURL The location of the song to play (local file path or url)
   */
  public void playSong(String songURL) {

    IContainer container = IContainer.make();

    IContainerFormat format = IContainerFormat.make();

    // Stream format must currently be mp3
    format.setInputFormat("mp3");

    //		int s = container.setInputBufferLength(6270);
    //
    //		if(s < 0){
    //			logger.warn("Input buffer was not set to desired length");
    //		}

    // Probe size value must be >50 for some reason. Native libraries throw an exception if it's
    // <50. Measured in bytes.
    if (container.setProperty("probesize", 50) < 0) {
      logger.warn("Probe size not set for input container.");
    }

    if (container.setProperty("analyzeduration", 1) < 0) {
      logger.warn("Analyze duration not changed for input container.");
    }

    container.setFlag(IContainer.Flags.FLAG_NONBLOCK, true);

    if (container.open(songURL, Type.READ, format, true, false) < 0) {
      throw new IllegalArgumentException("stream not found");
    }

    int numStreams = container.getNumStreams();

    // long streamRec = System.currentTimeMillis();

    logger.info("Number of Audio streams detected {}", numStreams);

    IPacket packet = IPacket.make();
    IStream stream = null;
    IStreamCoder audioCoder = null;

    Map<Integer, IStreamCoder> knownStreams = new HashMap<Integer, IStreamCoder>();

    long previousValue = 0;

    while (container.readNextPacket(packet) >= 0 && alive) {

      if (packet.isComplete()) {

        if (knownStreams.get(packet.getStreamIndex()) == null) {
          container.queryStreamMetaData(); // This method tends to take awhile when reading a stream
          stream = container.getStream(packet.getStreamIndex());
          knownStreams.put(packet.getStreamIndex(), stream.getStreamCoder());

          audioCoder = knownStreams.get(packet.getStreamIndex());

          audioCoder.setTimeBase(stream.getTimeBase());
        }

        if (!audioCoder.isOpen()) {
          if (audioCoder.open(null, null) < 0) {
            throw new RuntimeException("could not open audio decoder for container");
          }

          openSound(audioCoder);

          // System.out.println("Opening sound  " + (System.currentTimeMillis() - streamRec));
        }

        // System.err.println(audioCoder.getNumDroppedFrames());

        int offset = 0;

        IAudioSamples samples = IAudioSamples.make(1024, audioCoder.getChannels());

        while (offset < packet.getSize() && alive) {

          // Wait until the state is playing
          while (state != PlayBack_State.PLAYING) {

            if (state == PlayBack_State.TEARDOWN) {
              break;
            } else {
              try {
                synchronized (LOCK_OBJECT) {
                  // mLine.drain();
                  mLine.flush();
                  mLine.stop();

                  LOCK_OBJECT.wait();

                  mLine.start();
                }
              } catch (InterruptedException e) {
                logger.error("", e);
              }
            }
          }

          int bytesDecoded = audioCoder.decodeAudio(samples, packet, offset);

          if (bytesDecoded < 0) {
            logger.warn("Error occurred decoding audio");
            break;
            // throw new RuntimeException("got error decoding audio");
          }

          offset += bytesDecoded;

          if (samples.isComplete() && alive) {
            playJavaSound(samples);
          }

          // Send the time stamp to the GUI for updating the progress bar
          long newValue = (long) (packet.getTimeStamp() * packet.getTimeBase().getValue());

          // Update GUI every second that the stream is playing
          if (newValue > previousValue) {
            callback.notifyGUISongProgress(newValue);
            callback.isStreaming(true);
            previousValue = newValue;

            if (newValue == streamInfo.getSongDuration()) {
              alive = false;
            }
          }
        }
      }
    }

    closeJavaSound();

    if (audioCoder != null) {
      audioCoder.close();
      audioCoder = null;
    }
    if (container != null) {
      container.close();
      container = null;
    }
  }