/**
   * Creates a <tt>MediaFormat</tt> for the specified <tt>encoding</tt>, <tt>clockRate</tt>,
   * <tt>channels</tt> and set of format parameters. If <tt>encoding</tt> is known to this
   * <tt>MediaFormatFactory</tt>, returns a <tt>MediaFormat</tt> which is either an
   * <tt>AudioMediaFormat</tt> or a <tt>VideoMediaFormat</tt> instance. Otherwise, returns
   * <tt>null</tt>.
   *
   * @param encoding the well-known encoding (name) to create a <tt>MediaFormat</tt> for
   * @param clockRate the clock rate in Hz to create a <tt>MediaFormat</tt> for
   * @param frameRate the frame rate in number of frames per second to create a <tt>MediaFormat</tt>
   *     for
   * @param channels the number of available channels (1 for mono, 2 for stereo) if it makes sense
   *     for the <tt>MediaFormat</tt> with the specified <tt>encoding</tt>; otherwise, ignored
   * @param formatParams any codec specific parameters which have been received via SIP/SDP or
   *     XMPP/Jingle
   * @param advancedParams any parameters which have been received via SIP/SDP or XMPP/Jingle
   * @return a <tt>MediaFormat</tt> with the specified <tt>encoding</tt>, <tt>clockRate</tt>,
   *     <tt>channels</tt> and set of format parameters which is either an <tt>AudioMediaFormat</tt>
   *     or a <tt>VideoMediaFormat</tt> instance if <tt>encoding</tt> is known to this
   *     <tt>MediaFormatFactory</tt>; otherwise, <tt>null</tt>
   * @see MediaFormatFactory#createMediaFormat(String, double, int, float, Map, Map)
   */
  public MediaFormat createMediaFormat(
      String encoding,
      double clockRate,
      int channels,
      float frameRate,
      Map<String, String> formatParams,
      Map<String, String> advancedParams) {
    MediaFormat mediaFormat = createMediaFormat(encoding, clockRate, channels, formatParams);

    if (mediaFormat == null) return null;

    /*
     * MediaFormatImpl is immutable so if the caller wants to change the
     * format parameters and/or the advanced attributes, we'll have to
     * create a new MediaFormatImpl.
     */
    Map<String, String> formatParameters = null;
    Map<String, String> advancedParameters = null;

    if ((formatParams != null) && !formatParams.isEmpty()) formatParameters = formatParams;
    if ((advancedParams != null) && !advancedParams.isEmpty()) advancedParameters = advancedParams;

    if ((formatParameters != null) || (advancedParameters != null)) {
      switch (mediaFormat.getMediaType()) {
        case AUDIO:
          mediaFormat =
              new AudioMediaFormatImpl(
                  ((AudioMediaFormatImpl) mediaFormat).getFormat(),
                  formatParameters,
                  advancedParameters);
          break;
        case VIDEO:
          VideoMediaFormatImpl videoMediaFormatImpl = (VideoMediaFormatImpl) mediaFormat;

          /*
           * If the format of VideoMediaFormatImpl is
           * a ParameterizedVideoFormat, it's possible for the format
           * parameters of that ParameterizedVideoFormat and of the new
           * VideoMediaFormatImpl (to be created) to be out of sync. While
           * it's not technically perfect, it should be practically safe
           * for the format parameters which distinguish VideoFormats with
           * the same encoding and clock rate because mediaFormat has
           * already been created in sync with formatParams (with respect
           * to the format parameters which distinguish VideoFormats with
           * the same encoding and clock rate).
           */
          mediaFormat =
              new VideoMediaFormatImpl(
                  videoMediaFormatImpl.getFormat(),
                  videoMediaFormatImpl.getClockRate(),
                  frameRate,
                  formatParameters,
                  advancedParameters);
          break;
        default:
          mediaFormat = null;
      }
    }
    return mediaFormat;
  }
  /**
   * Returns the sequence number to use for a specific RTX packet, which is based on the packet's
   * original sequence number.
   *
   * <p>Because we terminate the RTX format, and with simulcast we might translate RTX packets from
   * multiple SSRCs into the same SSRC, we keep count of the RTX packets (and their sequence
   * numbers) which we sent for each SSRC.
   *
   * @param ssrc the SSRC of the RTX stream for the packet.
   * @return the sequence number which should be used for the next RTX packet sent using SSRC
   *     <tt>ssrc</tt>.
   */
  private int getNextRtxSequenceNumber(long ssrc) {
    Integer seq;
    synchronized (rtxSequenceNumbers) {
      seq = rtxSequenceNumbers.get(ssrc);
      if (seq == null) seq = new Random().nextInt(0xffff);
      else seq++;

      rtxSequenceNumbers.put(ssrc, seq);
    }

    return seq;
  }
  /**
   * Notifies this instance that the dynamic payload types of the associated {@link MediaStream}
   * have changed.
   */
  public void onDynamicPayloadTypesChanged() {
    rtxPayloadType = -1;
    rtxAssociatedPayloadType = -1;

    MediaStream mediaStream = channel.getStream();

    Map<Byte, MediaFormat> mediaFormatMap = mediaStream.getDynamicRTPPayloadTypes();

    Iterator<Map.Entry<Byte, MediaFormat>> it = mediaFormatMap.entrySet().iterator();

    while (it.hasNext() && rtxPayloadType == -1) {
      Map.Entry<Byte, MediaFormat> entry = it.next();
      MediaFormat format = entry.getValue();
      if (!Constants.RTX.equalsIgnoreCase(format.getEncoding())) {
        continue;
      }

      // XXX(gp) we freak out if multiple codecs with RTX support are
      // present.
      rtxPayloadType = entry.getKey();
      rtxAssociatedPayloadType = Byte.parseByte(format.getFormatParameters().get("apt"));
    }
  }