/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * A Container that MediaPacket objects can be read from.
 */
public class Demuxer extends Container {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected Demuxer(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.Demuxer_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected Demuxer(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.Demuxer_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(Demuxer obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new Demuxer object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public Demuxer copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new Demuxer(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof Demuxer)
      equal = (((Demuxer)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

  @Override
  public String toString() {
    final StringBuilder b = new StringBuilder(super.toString());
    b.append("[")
    .append("url="+getURL()+";")
    .append("format:"+getFormat()+";")
    .append("]");
    return b.toString();
  }

/**
 * Create a new Demuxer
 */
  public static Demuxer make() {
    long cPtr = VideoJNI.Demuxer_make();
    return (cPtr == 0) ? null : new Demuxer(cPtr, false);
  }

/**
 * Get the current state of the container.
 */
  public Demuxer.State getState() {
    return Demuxer.State.swigToEnum(VideoJNI.Demuxer_getState(swigCPtr, this));
  }

/**
 * Get the DemuxerFormat associated with this Demuxer

 * or null if unknown.
 */
  public DemuxerFormat getFormat() {
    long cPtr = VideoJNI.Demuxer_getFormat(swigCPtr, this);
    return (cPtr == 0) ? null : new DemuxerFormat(cPtr, false);
  }

/**
 * Set the buffer length Humble Video will suggest to FFMPEG for reading inputs.

 *

 * If called when a Container is open, the call is ignored and -1 is returned.

 *

 * @param size The suggested buffer size.

 * @throws InvalidArgument if size <= 0
 */
  public void setInputBufferLength(int size) {
    VideoJNI.Demuxer_setInputBufferLength(swigCPtr, this, size);
  }

/**
 * Return the input buffer length.

 *

 * @return The input buffer length Humble Video told FFMPEG to assume.

 *   0 means FFMPEG should choose it's own

 *   size (and it'll probably be 32768).
 */
  public int getInputBufferLength() {
    return VideoJNI.Demuxer_getInputBufferLength(swigCPtr, this);
  }

/**
 * Open this container and make it ready for reading, optionally

 * reading as far into the container as necessary to find all streams.

 *
The caller must call #close() when done, but if not, the

 * Demuxer will eventually close

 * them later but warn to the logging system.

 *

 * @param url The resource to open; The format of this string is any

 *   url that FFMPEG supports (including additional protocols if added

 *   through the video.customio library).

 *

 * @param format A pointer to a InputFormat object specifying

 *   the format of this container, or null if you want us to guess.

 * @param streamsCanBeAddedDynamically If true, open() will expect that new

 *   streams can be added at any time, even after the format header has been read.

 * @param queryStreamMetaData If true, open() will call #queryStreamMetaData()

 *   on this container, which will potentially block until it has ready

 *   enough data to find all streams in a container.  If false, it will only

 *   block to read a minimal header for this container format.

 * @param options If not null, a set of key-value pairs that will be set on the container immediately

 *   the format is determined.  Some options cannot be set on input formats until

 *   the system has had a chance to determine what the format is, so this

 *   is the only way to set InputFormat-specific options.

 * @param optionsNotSet If not null, on return this KeyValueBag object will be cleared out, and

 *   replace with any key/value pairs that were in options but could not be set on this

 *   Demuxer.

 *

 * @return >= 0 on success; < 0 on error.
 */
  public void open(String url, DemuxerFormat format, boolean streamsCanBeAddedDynamically, boolean queryStreamMetaData, KeyValueBag options, KeyValueBag optionsNotSet) throws java.lang.InterruptedException, java.io.IOException {
    VideoJNI.Demuxer_open(swigCPtr, this, url, DemuxerFormat.getCPtr(format), format, streamsCanBeAddedDynamically, queryStreamMetaData, KeyValueBag.getCPtr(options), options, KeyValueBag.getCPtr(optionsNotSet), optionsNotSet);
  }

/**
 * Close the container.  open() must have been called first, or

 * else an error is returned.

 *

 * If this method exits because of an interruption,

 * all resources will be closed anyway.

 *

 */
  public void close() throws java.lang.InterruptedException, java.io.IOException {
    VideoJNI.Demuxer_close(swigCPtr, this);
  }

/**
 * Get the stream at the given position.

 *

 * @param streamIndex the index of this stream in the container

 * @return The stream at that position in the container, or null if none there.
 */
  public DemuxerStream getStream(int streamIndex) throws java.lang.InterruptedException, java.io.IOException {
    long cPtr = VideoJNI.Demuxer_getStream(swigCPtr, this, streamIndex);
    return (cPtr == 0) ? null : new DemuxerStream(cPtr, false);
  }

/**
 * Reads the next packet in the Demuxer into the Packet.  This method will

 * release any buffers currently held by this packet and allocate

 * new ones.

 *

 * For non-blocking IO data sources, it is possible for this method

 * to return as successful but with no complete packet. In that case

 * the caller should retry again later (think EAGAIN) semantics.

 *

 *

 * @param packet [In/Out] The packet the Demuxer will read into.

 *

 * @return 0 if successful, or <0 if not.

 * @throws RuntimeException if an error occurs except for EOF (in which case <0 returned)

 *         or EAGAIN (in which case 0 returned with an incomplete packet).
 */
  public int read(MediaPacket packet) throws java.lang.InterruptedException, java.io.IOException {
    return VideoJNI.Demuxer_read(swigCPtr, this, MediaPacket.getCPtr(packet), packet);
  }

/**
 * Attempts to read all the meta data in this stream, potentially by reading ahead

 * and decoding packets.

 *

 * Any packets this method reads ahead will be cached and correctly returned when you

 * read packets, but this method can be non-blocking potentially until end of container

 * to get all meta data.  Take care when you call it.

 *

After this method is called, other meta data methods like #getDuration() should

 * work.

 */
  public void queryStreamMetaData() throws java.lang.InterruptedException, java.io.IOException {
    VideoJNI.Demuxer_queryStreamMetaData(swigCPtr, this);
  }

/**
 * Gets the duration, if known, of this container.

 *

 * This will only work for non-streamable containers where Demuxer

 * can calculate the container size.

 *

 * @return The duration, or Global#NO_PTS if not known.
 */
  public long getDuration() {
    return VideoJNI.Demuxer_getDuration(swigCPtr, this);
  }

/**
 * Get the starting timestamp in microseconds of the first packet of the earliest stream in this container.

 *

 * This will only return value values either either (a) for non-streamable

 * containers where Demuxer can calculate the container size or

 * (b) after Demuxer has actually read the

 * first packet from a streamable Demuxer.

 *

 *

 * @return The starting timestamp in microseconds, or Global#NO_PTS if not known.
 */
  public long getStartTime() {
    return VideoJNI.Demuxer_getStartTime(swigCPtr, this);
  }

/**
 * Get the file size in bytes of this container.

 *

 * This will only return a valid value if the container is non-streamed and supports seek.

 *

 * @return The file size in bytes, or <0 on error.
 */
  public long getFileSize() {
    return VideoJNI.Demuxer_getFileSize(swigCPtr, this);
  }

/**
 * Get the calculated overall bit rate of this file.

 *

 * This will only return a valid value if the container is non-streamed and supports seek.

 *

 * @return The overall bit rate in bytes per second, or <0 on error.
 */
  public int getBitRate() {
    return VideoJNI.Demuxer_getBitRate(swigCPtr, this);
  }

/**
 * Get the flags associated with this object.

 *

 * @return The (compacted) value of all flags set.
 */
  public int getFlags() {
    return VideoJNI.Demuxer_getFlags(swigCPtr, this);
  }

/**
 * Set the flags to use with this object.  All values

 * must be ORed (|) together.

 *

 * @see Flags

 *

 * @param newFlags The new set flags for this codec.
 */
  public void setFlags(int newFlags) {
    VideoJNI.Demuxer_setFlags(swigCPtr, this, newFlags);
  }

/**
 * Get the setting for the specified flag

 *

 * @param flag The flag you want to find the setting for

 *

 * @return 0 for false; non-zero for true
 */
  public boolean getFlag(Container.Flag flag) {
    return VideoJNI.Demuxer_getFlag(swigCPtr, this, flag.swigValue());
  }

/**
 * Set the flag.

 *

 * @param flag The flag to set

 * @param value The value to set it to (true or false)
 */
  public void setFlag(Container.Flag flag, boolean value) {
    VideoJNI.Demuxer_setFlag(swigCPtr, this, flag.swigValue(), value);
  }

/**
 * Get the URL the Demuxer was opened with.

 * May return null if unknown.

 * @return the URL opened, or null.
 */
  public String getURL() {
    return VideoJNI.Demuxer_getURL(swigCPtr, this);
  }

/**
 * Get the number of times Demuxer#readNextPacket(Packet)

 * will retry a read if it gets a Error.Type#ERROR_AGAIN

 * value back.

 *

 * Defaults to 1 times.  <0 means it will keep retrying indefinitely.

 *

 * @return the read retry count
 */
  public int getReadRetryCount() {
    return VideoJNI.Demuxer_getReadRetryCount(swigCPtr, this);
  }

/**
 * Sets the read retry count.

 *

 * @see #getReadRetryCount()

 *

 * @param count The read retry count.  <0 means keep trying.
 */
  public void setReadRetryCount(int count) {
    VideoJNI.Demuxer_setReadRetryCount(swigCPtr, this, count);
  }

/**
 * Can streams be added dynamically to this container?

 *

 * @return true if streams can be added dynamically
 */
  public boolean canStreamsBeAddedDynamically() {
    return VideoJNI.Demuxer_canStreamsBeAddedDynamically(swigCPtr, this);
  }

/**
 * Get the KeyValueBag of media MetaData for this object,

 * or null if none.

 *

 * If the Demuxer or IStream object

 * that this KeyValueBag came from was opened

 * for reading, then changes via KeyValueBag#setValue(String, String)

 * will have no effect on the underlying media.

 *

 *

 * If the Demuxer or IStream object

 * that this KeyValueBag came from was opened

 * for writing, then changes via KeyValueBag#setValue(String, String)

 * will have no effect after Demuxer#writeHeader()

 * is called.

 *

 * @return the KeyValueBag.
 */
  public KeyValueBag getMetaData() {
    long cPtr = VideoJNI.Demuxer_getMetaData(swigCPtr, this);
    return (cPtr == 0) ? null : new KeyValueBag(cPtr, false);
  }

/**
 * Forces the Demuxer to assume all audio streams are

 * encoded with the given audio codec when demuxing.

 * @param id The codec id

 * @return < 0 on error (e.g. not an audio codec); >= 0 on success.
 */
  public int setForcedAudioCodec(Codec.ID id) {
    return VideoJNI.Demuxer_setForcedAudioCodec(swigCPtr, this, id.swigValue());
  }

/**
 * Forces the Demuxer to assume all video streams are

 * encoded with the given video codec when demuxing.

 * @param id The codec id

 * @return < 0 on error (e.g. not an video codec); >= 0 on success.
 */
  public int setForcedVideoCodec(Codec.ID id) {
    return VideoJNI.Demuxer_setForcedVideoCodec(swigCPtr, this, id.swigValue());
  }

/**
 * Forces the Demuxer to assume all subtitle streams are

 * encoded with the given subtitle codec when demuxing.

 * @param id The codec id

 * @return < 0 on error (e.g. not an subtitle codec); >= 0 on success.
 */
  public int setForcedSubtitleCodec(Codec.ID id) {
    return VideoJNI.Demuxer_setForcedSubtitleCodec(swigCPtr, this, id.swigValue());
  }

/**
 * Seek to timestamp ts.

 *

 * Seeking will be done so that the point from which all active streams

 * can be presented successfully will be closest to ts and within min/max_ts.

 * Active streams are all streams that have Stream.getDiscardSetting <

 * Codec.DISCARD_ALL.

 *

 * If flags contain SeekFlags.SEEK_BYTE, then all timestamps are in bytes and

 * are the file position (this may not be supported by all demuxers).

 * If flags contain SeekFlags.SEEK_FRAME, then all timestamps are in frames

 * in the stream with stream_index (this may not be supported by all demuxers).

 * Otherwise all timestamps are in units of the stream selected by stream_index

 * or if stream_index is -1, in (1/Global.DEFAULT_PTS_MICROSECONDS} units.

 * If flags contain SeekFlags.SEEK_ANY, then non-keyframes are treated as

 * keyframes (this may not be supported by all demuxers).

 *

 * @param stream_index index of the stream which is used as time base reference

 * @param min_ts smallest acceptable timestamp

 * @param ts target timestamp

 * @param max_ts largest acceptable timestamp

 * @param flags flags

 * @return >=0 on success, error code otherwise

 *

 * Note: This is part of the new seek API which is still under construction.

 *       Thus do not use this yet. It may change at any time, do not expect

 *       ABI compatibility yet!
 */
  public int seek(int stream_index, long min_ts, long ts, long max_ts, int flags) throws java.lang.InterruptedException, java.io.IOException {
    return VideoJNI.Demuxer_seek(swigCPtr, this, stream_index, min_ts, ts, max_ts, flags);
  }

/**
 * Gets the AVFormatContext.max_delay property if possible.

 * @return The max delay, error code otherwise.
 */
  public int getMaxDelay() {
    return VideoJNI.Demuxer_getMaxDelay(swigCPtr, this);
  }

/**
 * Start playing a network source. Call #pause() to pause.
 */
  public void play() throws java.lang.InterruptedException, java.io.IOException {
    VideoJNI.Demuxer_play(swigCPtr, this);
  }

/**
 * Pause a playing network source. Call #play() to unpause.

 *

 * @return 0 on success; <0 if state is not #State.STATE_PLAYING or error.
 */
  public void pause() throws java.lang.InterruptedException, java.io.IOException {
    VideoJNI.Demuxer_pause(swigCPtr, this);
  }

  /**
   * Demuxers can only be in one of these states:
   */
  public enum State {
  /**
   * STATE_INITED: Allocated but open has not been called yet. Transitions to STATE_OPENED when ::open(...)

   *   is successfully called, or STATE_ERROR if ::open(...) has an error.
   */
    STATE_INITED,
  /**
   * STATE_OPENED: Opened and read to read or write data. Transitions to STATE_CLOSED on successful ::close(...)

   *   or STATE_ERROR if ::close(...) has an error.
   */
    STATE_OPENED,
  /**
   * Some Sources can be live (i.e. in Play mode)
   */
    STATE_PLAYING,
  /**
   * Some containers (e.g. networks) can be paused.
   */
    STATE_PAUSED,
  /**
   * STATE_CLOSED: Container is closed and should be discarded.
   */
    STATE_CLOSED,
  /**
   * STATE_ERROR: Container had an error and should be discarded.
   */
    STATE_ERROR,
  ;

    public final int swigValue() {
      return swigValue;
    }

    public static State swigToEnum(int swigValue) {
      State[] swigValues = State.class.getEnumConstants();
      if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
        return swigValues[swigValue];
      for (State swigEnum : swigValues)
        if (swigEnum.swigValue == swigValue)
          return swigEnum;
      throw new IllegalArgumentException("No enum " + State.class + " with value " + swigValue);
    }

    @SuppressWarnings("unused")
    private State() {
      this.swigValue = SwigNext.next++;
    }

    @SuppressWarnings("unused")
    private State(int swigValue) {
      this.swigValue = swigValue;
      SwigNext.next = swigValue+1;
    }

    @SuppressWarnings("unused")
    private State(State swigEnum) {
      this.swigValue = swigEnum.swigValue;
      SwigNext.next = this.swigValue+1;
    }

    private final int swigValue;

    private static class SwigNext {
      private static int next = 0;
    }
  }

  /**
   * Flags that can be bitmasked in the #seek method. If no

   * flag, then key-frame-only/forward seeking is assumed.
   */
  public enum SeekFlag {
  /**
   * Seek backwards.
   */
    SEEK_BACKWARD(VideoJNI.Demuxer_SEEK_BACKWARD_get()),
  /**
   * Seek based on position in bytes.
   */
    SEEK_BYTE(VideoJNI.Demuxer_SEEK_BYTE_get()),
  /**
   * Seek to any frame, even non-keyframes
   */
    SEEK_ANY(VideoJNI.Demuxer_SEEK_ANY_get()),
  /**
   * Seek based on frame number
   */
    SEEK_FRAME(VideoJNI.Demuxer_SEEK_FRAME_get()),
  ;

    public final int swigValue() {
      return swigValue;
    }

    public static SeekFlag swigToEnum(int swigValue) {
      SeekFlag[] swigValues = SeekFlag.class.getEnumConstants();
      if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
        return swigValues[swigValue];
      for (SeekFlag swigEnum : swigValues)
        if (swigEnum.swigValue == swigValue)
          return swigEnum;
      throw new IllegalArgumentException("No enum " + SeekFlag.class + " with value " + swigValue);
    }

    @SuppressWarnings("unused")
    private SeekFlag() {
      this.swigValue = SwigNext.next++;
    }

    @SuppressWarnings("unused")
    private SeekFlag(int swigValue) {
      this.swigValue = swigValue;
      SwigNext.next = swigValue+1;
    }

    @SuppressWarnings("unused")
    private SeekFlag(SeekFlag swigEnum) {
      this.swigValue = swigEnum.swigValue;
      SwigNext.next = this.swigValue+1;
    }

    private final int swigValue;

    private static class SwigNext {
      private static int next = 0;
    }
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * This class wraps represents a Rational number.

 *

 * Video formats often use rational numbers, and converting between

 * them willy nilly can lead to rounding errors, and eventually, out

 * of sync problems.  Therefore we use IRational objects to pass

 * around Rational Numbers and avoid conversion until the very last moment.

 *

 * Note: There are some static convenience methods

 * in this class that start with s*.  They start with s

 * (as opposed to overloading methods (e.g. sAdd(...) vs. add(...)).

 *

 */
public class Rational extends RefCounted {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected Rational(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.Rational_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected Rational(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.Rational_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(Rational obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new Rational object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public Rational copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new Rational(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof Rational)
      equal = (((Rational)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

  /**
   * Prints the contents of this object as a fraction.
   * @return "{@link #getNumerator()}/{@link #getDenominator()}"
   */

  @Override
  public String toString()
  {
    return "" + getNumerator() + "/" + getDenominator();
  }
  /**
   * Is this number positive?
   * @return true if > 0; false if not.
   */
  public boolean isPositive()
  {
    if (getDenominator() == 0)
      return false;
    return getDouble() > 0;
  }
  /**
   * Is this number not null and positive.
   * @param num the number; may be null
   * @return true if not-null and > 0; else false;
   */
  public static boolean positive(Rational num)
  {
    if (num == null)
      return false;
    return num.isPositive();
  }
  /**
   * Is this number negative?
   * @return true if < 0; false if not.
   */
  public boolean isNegative()
  {
    if (getDenominator() == 0)
      return false;
    return getDouble() < 0;
  }
  /**
   * Is this number not null and negative.
   * @param num the number; may be null
   * @return true if not-null and < 0; else false;
   */
  public static boolean negative(Rational num)
  {
    if (num == null)
      return false;
    return num.isNegative();
  }

/**
 * Get the numerator for this rational.

 * @return the numerator.
 */
  public int getNumerator() {
    return VideoJNI.Rational_getNumerator(swigCPtr, this);
  }

/**
 * Get the denominator for this rational.

 * @return the denominator.
 */
  public int getDenominator() {
    return VideoJNI.Rational_getDenominator(swigCPtr, this);
  }

/**
 * Creates a new IRational object by copying (by value) this object.

 *

 * @return the new object
 */
  public Rational copy() {
    long cPtr = VideoJNI.Rational_copy(swigCPtr, this);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Compare a rational to this rational

 * @param other second rational

 * @return 0 if this==other, 1 if this>other and -1 if this<other.
 */
  public int compareTo(Rational other) {
    return VideoJNI.Rational_compareTo(swigCPtr, this, Rational.getCPtr(other), other);
  }

/**
 * Compare two rationals

 * @param a the first rational

 * @param b the second rational

 * @return 0 if a==b, 1 if a>b and -1 if b<a.
 */
  public static int sCompareTo(Rational a, Rational b) {
    return VideoJNI.Rational_sCompareTo(Rational.getCPtr(a), a, Rational.getCPtr(b), b);
  }

/**
 * Rational to double conversion.

 *

 * @return (double) a
 */
  public double getDouble() {
    return VideoJNI.Rational_getDouble(swigCPtr, this);
  }

/**
 * Reduce a fraction to it's lowest common denominators.

 * This is useful for framerate calculations.

 * @param num       the src numerator.

 * @param den       the src denominator.

 * @param max the maximum allowed for nom & den in the reduced fraction.

 * @return 1 if exact, 0 otherwise
 */
  public int reduce(long num, long den, long max) {
    return VideoJNI.Rational_reduce(swigCPtr, this, num, den, max);
  }

/**
 * Reduce a fraction to it's lowest common denominators.

 * This is useful for framerate calculations.

 * @param dst The destination rational

 * @param num       the src numerator.

 * @param den       the src denominator.

 * @param max the maximum allowed for nom & den in the reduced fraction.

 * @return 1 if exact, 0 otherwise
 */
  public static int sReduce(Rational dst, long num, long den, long max) {
    return VideoJNI.Rational_sReduce(Rational.getCPtr(dst), dst, num, den, max);
  }

/**
 * Multiplies this number by arg

 * @param arg number to mulitply by.

 * @return this*arg.
 */
  public Rational multiply(Rational arg) {
    long cPtr = VideoJNI.Rational_multiply(swigCPtr, this, Rational.getCPtr(arg), arg);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Multiples a by b.

 * @param a the first number

 * @param b the second number.

 * @return a*b
 */
  public static Rational sMultiply(Rational a, Rational b) {
    long cPtr = VideoJNI.Rational_sMultiply(Rational.getCPtr(a), a, Rational.getCPtr(b), b);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Divides this rational by arg.

 * @param arg The divisor to use.

 * @return this/arg.
 */
  public Rational divide(Rational arg) {
    long cPtr = VideoJNI.Rational_divide(swigCPtr, this, Rational.getCPtr(arg), arg);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Divides a by b.

 * @param a The first number.

 * b The second number.

 * @return a/b.
 */
  public static Rational sDivide(Rational a, Rational b) {
    long cPtr = VideoJNI.Rational_sDivide(Rational.getCPtr(a), a, Rational.getCPtr(b), b);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Subtracts arg from this rational

 * @param arg The amount to subtract from this.

 * @return this-arg.
 */
  public Rational subtract(Rational arg) {
    long cPtr = VideoJNI.Rational_subtract(swigCPtr, this, Rational.getCPtr(arg), arg);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Subtracts a from b.

 * @param a The first number.

 * b The second number.

 * @return a-b.
 */
  public static Rational sSubtract(Rational a, Rational b) {
    long cPtr = VideoJNI.Rational_sSubtract(Rational.getCPtr(a), a, Rational.getCPtr(b), b);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Adds arg to this rational

 * @param arg The amount to add to this.

 * @return this+arg.
 */
  public Rational add(Rational arg) {
    long cPtr = VideoJNI.Rational_add(swigCPtr, this, Rational.getCPtr(arg), arg);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Adds a to b.

 * @param a The first number.

 * b The second number.

 * @return a+b.
 */
  public static Rational sAdd(Rational a, Rational b) {
    long cPtr = VideoJNI.Rational_sAdd(Rational.getCPtr(a), a, Rational.getCPtr(b), b);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Takes a value scaled in increments of origBase and gives the

 * equivalent value scaled in terms of this Rational.

 *

 * @param origValue The original int64_t value you care about.

 * @param origBase The original base Rational that origValue is scaled with.

 *

 * @return The new integer value, scaled in units of this IRational.
 */
  public long rescale(long origValue, Rational origBase) {
    return VideoJNI.Rational_rescale__SWIG_0(swigCPtr, this, origValue, Rational.getCPtr(origBase), origBase);
  }

/**
 * Takes a value scaled in increments of origBase and gives the

 * equivalent value scaled in terms of this Rational.

 *

 * @param origValue The original int64_t value you care about.

 * @param origBase The original base Rational that origValue is scaled with.

 * @param newBase The rational you want to rescale origValue into.

 *

 * @return The new integer value, scaled in units of this IRational.
 */
  public static long sRescale(long origValue, Rational origBase, Rational newBase) {
    return VideoJNI.Rational_sRescale__SWIG_0(origValue, Rational.getCPtr(origBase), origBase, Rational.getCPtr(newBase), newBase);
  }

/**
 * Get a new rational that will be set to 0/1.

 * The rational will not have #init() called

 * and hence will be modifiable by #setValue(double)

 * until #init() is called.

 * @return a rational number object
 */
  public static Rational make() {
    long cPtr = VideoJNI.Rational_make__SWIG_0();
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Converts a double precision floating point number to a rational.

 * @param d double to convert

 * @return A new Rational; caller must release() when done.
 */
  public static Rational make(double d) {
    long cPtr = VideoJNI.Rational_make__SWIG_1(d);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Creates deep copy of a Rational from another Rational.

 *

 * @param src       The source Rational to copy.

 * @return A new Rational; Returns null

 *         if src is null.
 */
  public static Rational make(Rational src) {
    long cPtr = VideoJNI.Rational_make__SWIG_2(Rational.getCPtr(src), src);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Create a rational from a numerator and denominator.

 *

 * We will always reduce this to the lowest num/den pair

 * we can, but never having den exceed what was passed in.

 *

 * @param num The numerator of the resulting Rational

 * @param den The denominator of the resulting Rational

 *

 * @return A new Rational;
 */
  public static Rational make(int num, int den) {
    long cPtr = VideoJNI.Rational_make__SWIG_3(num, den);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Takes a value scaled in increments of origBase and gives the

 * equivalent value scaled in terms of this Rational.

 *

 * @param origValue The original int64_t value you care about.

 * @param origBase The original base Rational that origValue is scaled with.

 * @param rounding How you want rounding to occur

 * @return The new integer value, scaled in units of this IRational.
 */
  public long rescale(long origValue, Rational origBase, Rational.Rounding rounding) {
    return VideoJNI.Rational_rescale__SWIG_1(swigCPtr, this, origValue, Rational.getCPtr(origBase), origBase, rounding.swigValue());
  }

/**
 * Takes a value scaled in increments of origBase and gives the

 * equivalent value scaled in terms of this Rational.

 *

 * @param origValue The original int64_t value you care about.

 * @param origBase The original base Rational that origValue is scaled with.

 * @param newBase The rational you want to rescale origValue into.

 * @param rounding How you want rounding to occur

 *

 * @return The new integer value, scaled in units of this IRational.
 */
  public static long sRescale(long origValue, Rational origBase, Rational newBase, Rational.Rounding rounding) {
    return VideoJNI.Rational_sRescale__SWIG_1(origValue, Rational.getCPtr(origBase), origBase, Rational.getCPtr(newBase), newBase, rounding.swigValue());
  }

/**
 * Rescales a long value to another long value.

 *

 * This method doesn't use IRational values, but

 * instead uses numerators and denominators

 * passed in by the caller.  It will not result

 * in any memory allocations.

 *

 *

 * @param srcValue The value to rescale.

 * @param dstNumerator The numerator of the units

 *   you want to scale to.  Must be non-zero.

 * @param dstDenominator The denominator of the units

 *   you want to scale to.  Must be non-zero.

 * @param srcNumerator The numerator of the units

 *   srcValue is expressed in.

 *   Must be non-zero.

 * @param srcDenominator The denominator of the units

 *   srcValue is expressed in.

 *   Must be non-zero.

 * @param rounding How you want rounding to occur

 *

 * @return The new integer value, scaled in units of

 *   dstNumerator/dstNumerator, or 0 if there

 *   is a parameter error.
 */
  public static long rescale(long srcValue, int dstNumerator, int dstDenominator, int srcNumerator, int srcDenominator, Rational.Rounding rounding) {
    return VideoJNI.Rational_rescale__SWIG_2(srcValue, dstNumerator, dstDenominator, srcNumerator, srcDenominator, rounding.swigValue());
  }

/**
 * Sets the numerator on this object.

 *

 * If #isFinalized is true, then this method is ignored.

 *

 */
  public void setNumerator(int value) {
    VideoJNI.Rational_setNumerator(swigCPtr, this, value);
  }

/**
 * Sets the denominator on this object.

 *

 * If #isFinalized is true, then this method is ignored.

 *

 */
  public void setDenominator(int value) {
    VideoJNI.Rational_setDenominator(swigCPtr, this, value);
  }

/**
 * Sets the numerator and denominator on this object by

 * reducing the double to the closest integer numerator

 * and denominator.

 *

 * If #isFinalized is true, then this method is ignored.

 *

 */
  public void setValue(double value) {
    VideoJNI.Rational_setValue(swigCPtr, this, value);
  }

/**
 * An alias for #getDouble() but matching JavaBean

 * conventions.
 */
  public double getValue() {
    return VideoJNI.Rational_getValue(swigCPtr, this);
  }

/**
 * Returns true if #init() has been called and

 * this object is now considered finalized and immutable.
 */
  public boolean isFinalized() {
    return VideoJNI.Rational_isFinalized(swigCPtr, this);
  }

/**
 * Marks this object as finalized and immutable.  Any

 * setters called after the first #init() call

 * will be ignored.

 *

 * Most make methods will call this method

 * automatically, with the exception of the blank factory

 * method #make().

 *

 */
  public void init() {
    VideoJNI.Rational_init(swigCPtr, this);
  }

  /**
   * A type to specify how rounding should be done.
   */
  public enum Rounding {
  /**
   * Round toward zero.
   */
    ROUND_ZERO(VideoJNI.Rational_ROUND_ZERO_get()),
  /**
   * Round away from zero.
   */
    ROUND_INF(VideoJNI.Rational_ROUND_INF_get()),
  /**
   * Round toward -infinity.
   */
    ROUND_DOWN(VideoJNI.Rational_ROUND_DOWN_get()),
  /**
   * Round toward +infinity.
   */
    ROUND_UP(VideoJNI.Rational_ROUND_UP_get()),
  /**
   * Round to nearest and halfway cases away from zero.
   */
    ROUND_NEAR_INF(VideoJNI.Rational_ROUND_NEAR_INF_get()),
  /**
   * Flag to pass INT64_MIN/MAX through instead of rescaling, this avoids special cases for AV_NOPTS_VALUE
   */
    ROUND_PASS_MINMAX(VideoJNI.Rational_ROUND_PASS_MINMAX_get()),
  ;

    public final int swigValue() {
      return swigValue;
    }

    public static Rounding swigToEnum(int swigValue) {
      Rounding[] swigValues = Rounding.class.getEnumConstants();
      if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
        return swigValues[swigValue];
      for (Rounding swigEnum : swigValues)
        if (swigEnum.swigValue == swigValue)
          return swigEnum;
      throw new IllegalArgumentException("No enum " + Rounding.class + " with value " + swigValue);
    }

    @SuppressWarnings("unused")
    private Rounding() {
      this.swigValue = SwigNext.next++;
    }

    @SuppressWarnings("unused")
    private Rounding(int swigValue) {
      this.swigValue = swigValue;
      SwigNext.next = swigValue+1;
    }

    @SuppressWarnings("unused")
    private Rounding(Rounding swigEnum) {
      this.swigValue = swigEnum.swigValue;
      SwigNext.next = this.swigValue+1;
    }

    private final int swigValue;

    private static class SwigNext {
      private static int next = 0;
    }
  }

}


/*******************************************************************************
 * Copyright (c) 2014, Art Clarke.  All rights reserved.
 *
 * This file is part of Humble-Video.
 *
 * Humble-Video is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Humble-Video is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Humble-Video.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package io.humble.video.demos;

import io.humble.video.Decoder;
import io.humble.video.Demuxer;
import io.humble.video.DemuxerStream;
import io.humble.video.Global;
import io.humble.video.Media;
import io.humble.video.MediaDescriptor;
import io.humble.video.MediaPacket;
import io.humble.video.MediaPicture;
import io.humble.video.Rational;
import io.humble.video.awt.ImageFrame;
import io.humble.video.awt.MediaPictureConverter;
import io.humble.video.awt.MediaPictureConverterFactory;

import java.awt.image.BufferedImage;
import java.io.IOException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

/**
 * Opens a media file, finds the first video stream, and then plays it.
 * This is meant as a demonstration program to teach the use of the Humble API.
 *

 * Concepts introduced:
 *

 *

 *
MediaPicture: {@link MediaPicture} objects represent uncompressed video in Humble.

 *
Timestamps: All {@link Media} objects in Humble have a timestamp, and this demonstration introduces the concept of having to worry about when to display information.

 *

 *
 *

 * To run from maven, do:
 *

 *

 * mvn install exec:java -Dexec.mainClass="io.humble.video.demos.DecodeAndPlayVideo" -Dexec.args="filename.mp4"
 *

 *
 * @author aclarke
 *
 */
public class DecodeAndPlayVideo {

  /**
   * Opens a file, and plays the video from it on a screen at the right rate.
   * @param filename The file or URL to play.
   */
  private static void playVideo(String filename) throws InterruptedException, IOException {
    /*
     * Start by creating a container object, in this case a demuxer since
     * we are reading, to get video data from.
     */
    Demuxer demuxer = Demuxer.make();

    /*
     * Open the demuxer with the filename passed on.
     */
    demuxer.open(filename, null, false, true, null, null);

    /*
     * Query how many streams the call to open found
     */
    int numStreams = demuxer.getNumStreams();

    /*
     * Iterate through the streams to find the first video stream
     */
    int videoStreamId = -1;
    long streamStartTime = Global.NO_PTS;
    Decoder videoDecoder = null;
    for(int i = 0; i < numStreams; i++)
    {
      final DemuxerStream stream = demuxer.getStream(i);
      streamStartTime = stream.getStartTime();
      final Decoder decoder = stream.getDecoder();
      if (decoder != null && decoder.getCodecType() == MediaDescriptor.Type.MEDIA_VIDEO) {
        videoStreamId = i;
        videoDecoder = decoder;
        // stop at the first one.
        break;
      }
    }
    if (videoStreamId == -1)
      throw new RuntimeException("could not find video stream in container: "+filename);

    /*
     * Now we have found the audio stream in this file.  Let's open up our decoder so it can
     * do work.
     */
    videoDecoder.open(null, null);

    final MediaPicture picture = MediaPicture.make(
        videoDecoder.getWidth(),
        videoDecoder.getHeight(),
        videoDecoder.getPixelFormat());

    /** A converter object we'll use to convert the picture in the video to a BGR_24 format that Java Swing
     * can work with. You can still access the data directly in the MediaPicture if you prefer, but this
     * abstracts away from this demo most of that byte-conversion work. Go read the source code for the
     * converters if you're a glutton for punishment.
     */
    final MediaPictureConverter converter =
        MediaPictureConverterFactory.createConverter(
            MediaPictureConverterFactory.HUMBLE_BGR_24,
            picture);
    BufferedImage image = null;

    /**
     * This is the Window we will display in. See the code for this if you're curious, but to keep this demo clean
     * we're 'simplifying' Java AWT UI updating code. This method just creates a single window on the UI thread, and blocks
     * until it is displayed.
     */
    final ImageFrame window = ImageFrame.make();
    if (window == null) {
      throw new RuntimeException("Attempting this demo on a headless machine, and that will not work. Sad day for you.");
    }

    /**
     * Media playback, like comedy, is all about timing. Here we're going to introduce very very basic
     * timing. This code is deliberately kept simple (i.e. doesn't worry about A/V drift, garbage collection pause time, etc.)
     * because that will quickly make things more complicated.
     *
     * But the basic idea is there are two clocks:
     *

     *
Player Clock: The time that the player sees (relative to the system clock).

     *
Stream Clock: Each stream has its own clock, and the ticks are measured in units of time-bases

     *

     *
     * And we need to convert between the two units of time. Each MediaPicture and MediaAudio object have associated
     * time stamps, and much of the complexity in video players goes into making sure the right picture (or sound) is
     * seen (or heard) at the right time. This is actually very tricky and many folks get it wrong -- watch enough
     * Netflix and you'll see what I mean -- audio and video slightly out of sync. But for this demo, we're erring for
     * 'simplicity' of code, not correctness. It is beyond the scope of this demo to make a full fledged video player.
     */

    // Calculate the time BEFORE we start playing.
    long systemStartTime = System.nanoTime();
    // Set units for the system time, which because we used System.nanoTime will be in nanoseconds.
    final Rational systemTimeBase = Rational.make(1, 1000000000);
    // All the MediaPicture objects decoded from the videoDecoder will share this timebase.
    final Rational streamTimebase = videoDecoder.getTimeBase();

    /**
     * Now, we start walking through the container looking at each packet. This
     * is a decoding loop, and as you work with Humble you'll write a lot
     * of these.
     *
     * Notice how in this loop we reuse all of our objects to avoid
     * reallocating them. Each call to Humble resets objects to avoid
     * unnecessary reallocation.
     */
    final MediaPacket packet = MediaPacket.make();
    while(demuxer.read(packet) >= 0) {
      /**
       * Now we have a packet, let's see if it belongs to our video stream
       */
      if (packet.getStreamIndex() == videoStreamId)
      {
        /**
         * A packet can actually contain multiple sets of samples (or frames of samples
         * in decoding speak).  So, we may need to call decode  multiple
         * times at different offsets in the packet's data.  We capture that here.
         */
        int offset = 0;
        int bytesRead = 0;
        do {
          bytesRead += videoDecoder.decode(picture, packet, offset);
          if (picture.isComplete()) {
            image = displayVideoAtCorrectTime(streamStartTime, picture,
                converter, image, window, systemStartTime, systemTimeBase,
                streamTimebase);
          }
          offset += bytesRead;
        } while (offset < packet.getSize());
      }
    }

    // Some video decoders (especially advanced ones) will cache
    // video data before they begin decoding, so when you are done you need
    // to flush them. The convention to flush Encoders or Decoders in Humble Video
    // is to keep passing in null until incomplete samples or packets are returned.
    do {
      videoDecoder.decode(picture, null, 0);
      if (picture.isComplete()) {
        image = displayVideoAtCorrectTime(streamStartTime, picture, converter,
            image, window, systemStartTime, systemTimeBase, streamTimebase);
      }
    } while (picture.isComplete());

    // It is good practice to close demuxers when you're done to free
    // up file handles. Humble will EVENTUALLY detect if nothing else
    // references this demuxer and close it then, but get in the habit
    // of cleaning up after yourself, and your future girlfriend/boyfriend
    // will appreciate it.
    demuxer.close();

    // similar with the demuxer, for the windowing system, clean up after yourself.
    window.dispose();
  }

  /**
   * Takes the video picture and displays it at the right time.
   */
  private static BufferedImage displayVideoAtCorrectTime(long streamStartTime,
      final MediaPicture picture, final MediaPictureConverter converter,
      BufferedImage image, final ImageFrame window, long systemStartTime,
      final Rational systemTimeBase, final Rational streamTimebase)
      throws InterruptedException {
    long streamTimestamp = picture.getTimeStamp();
    // convert streamTimestamp into system units (i.e. nano-seconds)
    streamTimestamp = systemTimeBase.rescale(streamTimestamp-streamStartTime, streamTimebase);
    // get the current clock time, with our most accurate clock
    long systemTimestamp = System.nanoTime();
    // loop in a sleeping loop until we're within 1 ms of the time for that video frame.
    // a real video player needs to be much more sophisticated than this.
    while (streamTimestamp > (systemTimestamp - systemStartTime + 1000000)) {
      Thread.sleep(1);
      systemTimestamp = System.nanoTime();
    }
    // finally, convert the image from Humble format into Java images.
    image = converter.toImage(image, picture);
    // And ask the UI thread to repaint with the new image.
    window.setImage(image);
    return image;
  }

  /**
   * Takes a media container (file) as the first argument, opens it,
   * opens up a window and plays back the video.
   *
   * @param args Must contain one string which represents a filename
   * @throws IOException
   * @throws InterruptedException
   */
  public static void main(String[] args) throws InterruptedException, IOException
  {
    final Options options = new Options();
    options.addOption("h", "help", false, "displays help");
    options.addOption("v", "version", false, "version of this library");

    final CommandLineParser parser = new org.apache.commons.cli.BasicParser();
    try {
      final CommandLine cmd = parser.parse(options, args);
      if (cmd.hasOption("version")) {
        // let's find what version of the library we're running
        final String version = io.humble.video_native.Version.getVersionInfo();
        System.out.println("Humble Version: " + version);
      } else if (cmd.hasOption("help") || args.length == 0) {
        final HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp(DecodeAndPlayVideo.class.getCanonicalName() + " <filename>", options);
      } else {
        final String[] parsedArgs = cmd.getArgs();
        for(String arg: parsedArgs)
          playVideo(arg);
      }
    } catch (ParseException e) {
      System.err.println("Exception parsing command line: " + e.getLocalizedMessage());
    }
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * A descriptor for the different kinds of BitStreamFilterTypes supported within Humble.

 *

 * @see BitStreamFilter
 */
public class BitStreamFilterType extends RefCounted {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected BitStreamFilterType(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.BitStreamFilterType_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected BitStreamFilterType(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.BitStreamFilterType_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(BitStreamFilterType obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new BitStreamFilterType object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public BitStreamFilterType copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new BitStreamFilterType(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof BitStreamFilterType)
      equal = (((BitStreamFilterType)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

  /**
   * info about this filter object
   * @return information about this object
   */

  @Override
  public String toString()
  {
    StringBuilder result = new StringBuilder();

    result.append(this.getClass().getName()+"@"+hashCode()+"[");
    result.append("name:"+getName()+";");
    result.append("]");
    return result.toString();
  }

    /**
   * Gets a collection of ALL codecs installed on this
   * system.
   *
   * @return A collection of all installed codecs.
   */
  public static java.util.Collection<BitStreamFilterType>
  getInstalledBitStreamFilterTypes()
  {
    java.util.Collection<BitStreamFilterType> retval = new java.util.HashSet<BitStreamFilterType>();
    int count = getNumBitStreamFilterTypes();
    for(int i=0;i<count;i++)
    {
      BitStreamFilterType t = getBitStreamFilterType(i);
      if (t != null)
        retval.add(t);
    }
    return retval;
  }

  public String getName() {
    return VideoJNI.BitStreamFilterType_getName(swigCPtr, this);
  }

  public static int getNumBitStreamFilterTypes() {
    return VideoJNI.BitStreamFilterType_getNumBitStreamFilterTypes();
  }

  public static BitStreamFilterType getBitStreamFilterType(int i) {
    long cPtr = VideoJNI.BitStreamFilterType_getBitStreamFilterType__SWIG_0(i);
    return (cPtr == 0) ? null : new BitStreamFilterType(cPtr, false);
  }

  public static BitStreamFilterType getBitStreamFilterType(String name) {
    long cPtr = VideoJNI.BitStreamFilterType_getBitStreamFilterType__SWIG_1(name);
    return (cPtr == 0) ? null : new BitStreamFilterType(cPtr, false);
  }

}


package io.humble.video.javaxsound;

import io.humble.video.AudioChannel.Layout;
import io.humble.video.AudioFormat.Type;
import io.humble.video.MediaAudio;

/**
 * A factory class for {@link MediaAudioConverter} interfaces.
 * @author aclarke
 *
 */
public class MediaAudioConverterFactory {

  protected MediaAudioConverterFactory() {
  }

  /**
   * Create a converter.
   * @param description A descriptor string for the converter.
   * @param protoAudio A MediaAudio object to copy the input audio parameters from.
   * @return
   */
  public static MediaAudioConverter createConverter(String description, MediaAudio protoAudio)
  {
    return createConverter(description,
        protoAudio.getSampleRate(), protoAudio.getChannelLayout(), protoAudio.getFormat());
  }

  /**
   * A descriptor for the default java audio format we convert to, which is
   * signed 16-bit 2-channel (stereo) audio samples at 22050 samples per second.
   * The converter will convert any type of audio given to it into this format
   * for playback with Java.
   */
  public static final String DEFAULT_JAVA_AUDIO="HUMBLE-STEREO-S16-22050";

  /**
   * Create a converter.
   * @param description A descriptor string for the converter.
   * @param sampleRate Sample rate of MediaAudio objects that will be converted to/from.
   * @param layout Channel layout of MediaAudio objects that will be converted to/from.
   * @param format Sample format of MediaAudio objets that will be converted to/from.
   * @return A converter to use
   */
  public static MediaAudioConverter createConverter(String description,
      int sampleRate, Layout layout, Type format) {
    if (description != DEFAULT_JAVA_AUDIO)
      throw new RuntimeException("Unsupported converter type");
    return new StereoS16AudioConverter(sampleRate, layout, format);
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * Encodes MediaAudio, MediaPicture or MediaSubtitle objects into MediaPacket objects.
 */
public class Encoder extends Coder {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected Encoder(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.Encoder_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected Encoder(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.Encoder_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(Encoder obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new Encoder object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public Encoder copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new Encoder(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof Encoder)
      equal = (((Encoder)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

/**
 * the number of media object the encoder had to drop (i.e. skip

 * encoding) in order to ensure that time stamp values are

 * monotonically increasing.  See https://code.google.com/p/xuggle/issues/detail?id=180

 * for details on why this is.
 */
  public long getNumDroppedFrames() {
    return VideoJNI.Encoder_getNumDroppedFrames(swigCPtr, this);
  }

/**
 * Create a Encoder that will use the given Codec.

 *

 * @return a Encoder

 * @throws InvalidArgument if codec is null or codec cannot decode.
 */
  public static Encoder make(Codec codec) {
    long cPtr = VideoJNI.Encoder_make__SWIG_0(Codec.getCPtr(codec), codec);
    return (cPtr == 0) ? null : new Encoder(cPtr, false);
  }

/**
 * Creates a Encoder from a given Encoder

 * @return a Encoder

 * @throws InvalidArgument if src is null
 */
  public static Encoder make(Coder src) {
    long cPtr = VideoJNI.Encoder_make__SWIG_1(Coder.getCPtr(src), src);
    return (cPtr == 0) ? null : new Encoder(cPtr, false);
  }

/**
 * Open this Coder, using the given bag of Codec-specific options.

 *

 * @param inputOptions If non-null, a bag of codec-specific options.

 * @param unsetOptions If non-null, the bag will be emptied and then filled with

 *                     the options in inputOptions that were not set.
 */
  public void open(KeyValueBag inputOptions, KeyValueBag unsetOptions) {
    VideoJNI.Encoder_open(swigCPtr, this, KeyValueBag.getCPtr(inputOptions), inputOptions, KeyValueBag.getCPtr(unsetOptions), unsetOptions);
  }

/**
 * Encode the given MediaPicture using this encoder.

 *

 * The MediaPicture will allocate a buffer to use internally for this, and

 * will free it when the frame destroys itself.

 *

 * Also, when done in order to flush the encoder, caller should call

 * this method passing in 0 (null) for frame to tell the encoder

 * to flush any data it was keeping a hold of.

 *

 * @param output [out] The packet to encode into.  Caller should check

 *       MediaPacket.isComplete() after call to find out if we had enough

 *       information to encode a full packet.

 * @param picture [in] The picture to encode

 *

 * Note: caller must ensure that output has sufficient space to

 *   contain a full packet. Alas, there is currently no way to

 *   query an encoder to find out the maximum packet size that

 *   can be output (bummer, I know). That leaves the caller two

 *   options. (1) You can call Packet.make() before each encode

 *   call, and then the encoder will automagically create the correct

 *   sized buffer for that call (but if you reuse the packet, it

 *   may be too small for the next caller). Or (2) you  can call

 *   Packet.make(int) with a value that will be larger than your

 *   max packet size (in which case you can reuse the packet).
 */
  public void encodeVideo(MediaPacket output, MediaPicture picture) {
    VideoJNI.Encoder_encodeVideo(swigCPtr, this, MediaPacket.getCPtr(output), output, MediaPicture.getCPtr(picture), picture);
  }

/**
 * Encode the given MediaAudio using this encoder.

 *

 * Callers should call this repeatedly on a set of samples until

 * we consume all the samples.

 *

 * Also, when done in order to flush the encoder, caller should call

 * this method passing in 0 (null) for samples to tell the encoder

 * to flush any data it was keeping a hold of.

 *

 * @param output [out] The packet to encode into.  Caller should check

 *       MediaPacket.isComplete() after call to find out if we had enough

 *       information to encode a full packet.

 * @param samples [in] The samples to consume

 *

 * Note: caller must ensure that output has sufficient space to

 *   contain a full packet. Alas, there is currently no way to

 *   query an encoder to find out the maximum packet size that

 *   can be output (bummer, I know). That leaves the caller two

 *   options. (1) You can call Packet.make() before each encode

 *   call, and then the encoder will automagically create the correct

 *   sized buffer for that call (but if you reuse the packet, it

 *   may be too small for the next caller). Or (2) you  can call

 *   Packet.make(int) with a value that will be larger than your

 *   max packet size (in which case you can reuse the packet).
 */
  public void encodeAudio(MediaPacket output, MediaAudio samples) {
    VideoJNI.Encoder_encodeAudio(swigCPtr, this, MediaPacket.getCPtr(output), output, MediaAudio.getCPtr(samples), samples);
  }

/**
 * Encode the given Media using this encoder.

 *

 * Callers should call this repeatedly on a media object ntil

 * we consume all the media.

 *

 * Also, when done in order to flush the encoder, caller should call

 * this method passing in 0 (null) for media to tell the encoder

 * to flush any data it was keeping a hold of.

 *

 * @param output [out] The packet to encode into.  Caller should check

 *       MediaPacket.isComplete() after call to find out if we had enough

 *       information to encode a full packet.

 *

 *

 * Note: caller must ensure that output has sufficient space to

 *   contain a full packet. Alas, there is currently no way to

 *   query an encoder to find out the maximum packet size that

 *   can be output (bummer, I know). That leaves the caller two

 *   options. (1) You can call Packet.make() before each encode

 *   call, and then the encoder will automagically create the correct

 *   sized buffer for that call (but if you reuse the packet, it

 *   may be too small for the next caller). Or (2) you  can call

 *   Packet.make(int) with a value that will be larger than your

 *   max packet size (in which case you can reuse the packet).

 *

 * @throws throws an exception if getCodecType() and the underlying

 * media object do not align (e.g. if you pass in a MediaPicture but the

 * CodecType is audio data).
 */
  public void encode(MediaPacket output, MediaSampled media) {
    VideoJNI.Encoder_encode(swigCPtr, this, MediaPacket.getCPtr(output), output, MediaSampled.getCPtr(media), media);
  }

}


/*******************************************************************************
 * Copyright (c) 2013, Art Clarke.  All rights reserved.
 *
 * This file is part of Humble-Video.
 *
 * Humble-Video is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Humble-Video is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Humble-Video.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package io.humble.video.awt;

import io.humble.ferry.Buffer;
import io.humble.ferry.JNIReference;
import io.humble.video.MediaPicture;
import io.humble.video.PixelFormat;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBuffer;
import java.awt.image.ColorModel;
import java.awt.color.ColorSpace;
import java.awt.image.ComponentColorModel;
import java.awt.image.SampleModel;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.Raster;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.concurrent.atomic.AtomicReference;

/**
 * A converter to translate {@link MediaPicture}s to and from
 * {@link BufferedImage}s of type {@link BufferedImage#TYPE_3BYTE_BGR}.
 *
 * Note: Package Private
 */

class BgrConverter extends AMediaPictureConverter {
  // band offsets requried by the sample model

  private static final int[] mBandOffsets = { 2, 1, 0 };

  // color space for this converter

  private static final ColorSpace mColorSpace = ColorSpace
      .getInstance(ColorSpace.CS_sRGB);

  // a private copy we use as the resample buffer when converting back and forth. saves time.
  private MediaPicture mResampleMediaPicture;

  /**
   * Construct as converter to translate {@link MediaPicture}s to and from
   * {@link BufferedImage}s of type {@link BufferedImage#TYPE_3BYTE_BGR}.
   *
   * @param pictureType
   *          the picture type recognized by this converter
   * @param pictureWidth
   *          the width of pictures
   * @param pictureHeight
   *          the height of pictures
   * @param imageWidth
   *          the width of images
   * @param imageHeight
   *          the height of images
   */

  public BgrConverter(PixelFormat.Type pictureType, int pictureWidth,
      int pictureHeight, int imageWidth, int imageHeight) {
    super(pictureType, PixelFormat.Type.PIX_FMT_BGR24,
        BufferedImage.TYPE_3BYTE_BGR, pictureWidth, pictureHeight, imageWidth,
        imageHeight);
    mResampleMediaPicture = willResample() ? MediaPicture.make(imageWidth,
        imageHeight, getRequiredPictureType()) : null;
  }

  /** {@inheritDoc} */
  @Override
  public MediaPicture toPicture(MediaPicture output,
      final BufferedImage input, long timestamp) {
    // validate the image

    validateImage(input);

    if (output == null) {
      output = MediaPicture.make(mPictureWidth,
          mPictureHeight, getPictureType());
    }

    // get the image byte buffer buffer

    DataBuffer imageBuffer = input.getRaster().getDataBuffer();
    byte[] imageBytes = null;
    int[] imageInts = null;

    // handle byte buffer case

    if (imageBuffer instanceof DataBufferByte) {
      imageBytes = ((DataBufferByte) imageBuffer).getData();
    }

    // handle integer buffer case

    else if (imageBuffer instanceof DataBufferInt) {
      imageInts = ((DataBufferInt) imageBuffer).getData();
    }

    // if it's some other type, throw

    else {
      throw new IllegalArgumentException(
          "Unsupported BufferedImage data buffer type: "
              + imageBuffer.getDataType());
    }

    // create the video picture and get it's underlying buffer

    final AtomicReference<JNIReference> ref = new AtomicReference<JNIReference>(
        null);
    final MediaPicture picture = willResample() ? mResampleMediaPicture : output;
    try {
      Buffer buffer = picture.getData(0);
      int size = picture.getDataPlaneSize(0);
      ByteBuffer pictureByteBuffer = buffer.getByteBuffer(0,
          size, ref);
      buffer.delete();
      buffer = null;

      if (imageInts != null) {
        pictureByteBuffer.order(ByteOrder.BIG_ENDIAN);
        IntBuffer pictureIntBuffer = pictureByteBuffer.asIntBuffer();
        pictureIntBuffer.put(imageInts);
      } else {
        pictureByteBuffer.put(imageBytes);
      }
      pictureByteBuffer = null;
      picture.setTimeStamp(timestamp);
      picture.setComplete(true);

      // resample as needed
      if (willResample()) {
        resample(output, picture, mToPictureResampler);
      }
      return output;
    } finally {
      if (ref.get() != null)
        ref.get().delete();
    }
  }

  /** {@inheritDoc} */
  @Override
  public BufferedImage toImage(BufferedImage output, final MediaPicture input) {
    validatePicture(input);
    // test that the picture is valid
    if (output == null) {
      final byte[] bytes = new byte[willResample() ? mResampleMediaPicture.getDataPlaneSize(0) : input.getDataPlaneSize(0)];
      // create the data buffer from the bytes

      final DataBufferByte db = new DataBufferByte(bytes, bytes.length);

      // create an a sample model which matches the byte layout of the
      // image data and raster which contains the data which now can be
      // properly interpreted
      int w = mImageWidth;
      int h = mImageHeight;

      final SampleModel sm = new PixelInterleavedSampleModel(
        db.getDataType(), w, h, 3, 3 * w, mBandOffsets);
      final WritableRaster wr = Raster.createWritableRaster(sm, db, null);

      // create a color model

      final ColorModel colorModel = new ComponentColorModel(
        mColorSpace, false, false, ColorModel.OPAQUE, db.getDataType());

      // return a new image created from the color model and raster

      output = new BufferedImage(colorModel, wr, false, null);
    }

    MediaPicture picture;
    // resample as needed
    AtomicReference<JNIReference> ref = new AtomicReference<JNIReference>(null);
    try {
      if (willResample()) {
        picture = resample(mResampleMediaPicture, input, mToImageResampler);
      } else {
        picture = input;
      }

      final Buffer buffer = picture.getData(0);
      final int size = picture.getDataPlaneSize(0);
      final ByteBuffer byteBuf = buffer.getByteBuffer(0,
          size, ref);
      buffer.delete();

      // get the bytes out of the image
      final DataBufferByte db = (DataBufferByte) output.getRaster()
          .getDataBuffer();
      final byte[] bytes = db.getData();
      // and copy them in.
      byteBuf.get(bytes, 0, size);

      // return a new image created from the color model and raster

      return output;
    } finally {
      if (ref.get() != null)
        ref.get().delete();
    }
  }

  public void delete() {
    if (mResampleMediaPicture != null)
      mResampleMediaPicture.delete();
    mResampleMediaPicture = null;

    super.delete();
  }
}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * Meta-data that describes a Codec object.
 */
public class CodecDescriptor extends RefCounted {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected CodecDescriptor(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.CodecDescriptor_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected CodecDescriptor(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.CodecDescriptor_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(CodecDescriptor obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new CodecDescriptor object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public CodecDescriptor copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new CodecDescriptor(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof CodecDescriptor)
      equal = (((CodecDescriptor)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

  /**
    * info about this {@link CodecDescriptor} object
    * @return information about this object
    */

   @Override
   public String toString()
   {
     StringBuilder result = new StringBuilder();

     result.append(this.getClass().getName()+"@"+hashCode()+"[");
     result.append("name:"+getName()+";");
     result.append("longname:"+getName()+";");
     result.append("properties:"+getProperties()+";");
     result.append("]");
     return result.toString();
   }

/**
 * Checks if this codec has the given property.

 * @param p property to check
 */
  public boolean hasProperty(CodecDescriptor.CodecProperty p) {
    return VideoJNI.CodecDescriptor_hasProperty(swigCPtr, this, p.swigValue());
  }

/**
 * Name of the codec. Unique for each codec. Will contain only

 * alphanumeric and _ characters.
 */
  public String getName() {
    return VideoJNI.CodecDescriptor_getName(swigCPtr, this);
  }

/**
 * A more descriptive name for this codec.
 */
  public String getLongName() {
    return VideoJNI.CodecDescriptor_getLongName(swigCPtr, this);
  }

/**
 * A bit mask of Codec.Properties this codec has.
 */
  public int getProperties() {
    return VideoJNI.CodecDescriptor_getProperties(swigCPtr, this);
  }

/**
 * Get the codec ID for this descriptor
 */
  public Codec.ID getId() {
    return Codec.ID.swigToEnum(VideoJNI.CodecDescriptor_getId(swigCPtr, this));
  }

  public MediaDescriptor.Type getType() {
    return MediaDescriptor.Type.swigToEnum(VideoJNI.CodecDescriptor_getType(swigCPtr, this));
  }

/**
 * Get the descriptor for the given id.
 */
  public static CodecDescriptor make(Codec.ID id) {
    long cPtr = VideoJNI.CodecDescriptor_make(id.swigValue());
    return (cPtr == 0) ? null : new CodecDescriptor(cPtr, false);
  }

  /**
   * Properties supported by a codec.
   */
  public enum CodecProperty {
  /**
   * Codec uses only intra compression.

   * Video codecs only.
   */
    PROP_INTRA_ONLY(VideoJNI.CodecDescriptor_PROP_INTRA_ONLY_get()),
  /**
   * Codec supports lossy compression. Audio and video codecs only.

   * Note: a codec may support both lossy and lossless

   * compression modes
   */
    PROP_LOSSY(VideoJNI.CodecDescriptor_PROP_LOSSY_get()),
  /**
   * Codec supports lossless compression. Audio and video codecs only.
   */
    PROP_LOSSLESS(VideoJNI.CodecDescriptor_PROP_LOSSLESS_get()),
  /**
   * Subtitle codec is bitmap based
   */
    PROP_BITMAP_SUB(VideoJNI.CodecDescriptor_PROP_BITMAP_SUB_get()),
  ;

    public final int swigValue() {
      return swigValue;
    }

    public static CodecProperty swigToEnum(int swigValue) {
      CodecProperty[] swigValues = CodecProperty.class.getEnumConstants();
      if (swigValue < swigValues.length && swigValue >= 0 && swigValues[swigValue].swigValue == swigValue)
        return swigValues[swigValue];
      for (CodecProperty swigEnum : swigValues)
        if (swigEnum.swigValue == swigValue)
          return swigEnum;
      throw new IllegalArgumentException("No enum " + CodecProperty.class + " with value " + swigValue);
    }

    @SuppressWarnings("unused")
    private CodecProperty() {
      this.swigValue = SwigNext.next++;
    }

    @SuppressWarnings("unused")
    private CodecProperty(int swigValue) {
      this.swigValue = swigValue;
      SwigNext.next = swigValue+1;
    }

    @SuppressWarnings("unused")
    private CodecProperty(CodecProperty swigEnum) {
      this.swigValue = swigEnum.swigValue;
      SwigNext.next = this.swigValue+1;
    }

    private final int swigValue;

    private static class SwigNext {
      private static int next = 0;
    }
  }

}


/*
 * Copyright (c) 2013-Forward, Andrew "Art" Clarke
 *
 * This file is part of Humble Video.
 *
 * Humble Video is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Humble Video is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Humble Video.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Configuration.java
 *
 *  Created on: Jun 30, 2013
 *      Author: aclarke
 */
package io.humble.video;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A global configuration class for Humble Video
 *

 * This object can help print setting options
 * on {@link Configurable} classes, and also
 * provides a convenient method {@link #configure(Properties, Configurable)}
 * that lets you configure {@link Configurable} objects from
 * a Java properties or FFmpeg preset file.
 *

 * @see #configure(Properties, Configurable)
 * @see Configurable
 *
 * @author aclarke
 *
 */
public class Configuration
{
  static private final Logger log = LoggerFactory.getLogger(Configuration.class);

  /** don't allow construction */
  private Configuration() {}

  /**
   * Print out all configurable options on the {@link Configurable} object.
   *
   * @param stream stream to print to
   * @param configObj {@link Configurable} object.
   */
  public static void printConfigurable(java.io.PrintStream stream,
      Configurable configObj)
  {
    stream.println("=======================================");
    stream.println("  " + configObj.getClass().getName() + " Properties");
    stream.println("=======================================");
    int numOptions = configObj.getNumProperties();

    for(int i = 0; i < numOptions; i++)
    {
      Property prop = configObj.getPropertyMetaData(i);
      printOption(stream, configObj, prop);
    }

  }

  /**
   * Print information about the property on the configurable object.
   *
   * @param stream stream to print to
   * @param configObj configurable object
   * @param prop property on object
   */
  public static void printOption(java.io.PrintStream stream,
      Configurable configObj, Property prop)
  {
    if (prop.getType() != Property.Type.PROPERTY_FLAGS)
    {
      stream.printf("  %s; default= %s; type=%s;\n",
          prop.getName(),
          configObj.getPropertyAsString(prop.getName()),
          prop.getType());
    } else {
      // it's a flag
      stream.printf("  %s; default= %d; valid values=(",
          prop.getName(),
          configObj.getPropertyAsLong(prop.getName()));
      int numSettings = prop.getNumFlagSettings();
      long value = configObj.getPropertyAsLong(prop.getName());
      for(int i = 0; i < numSettings; i++)
      {
        Property fprop = prop.getFlagConstant(i);
        long flagMask = fprop.getDefault();
        boolean isSet = (value & flagMask)>0;
        stream.printf("%s%s; ",
            isSet ? "+" : "-",
                fprop.getName());
      }
      stream.printf("); type=%s;\n", prop.getType());
    }
    stream.printf("    help for %s: %s\n",
        prop.getName(),
        prop.getHelp() == null ? "no help available" : prop.getHelp());
  }

  /**
   * Configures an {@link Configurable} from a set of Java {@link Properties}.
   *

   * Here's some sample code that shows configuring a IStreamCoder
   * from a FFmpeg preset file:
   *

   *

   *   Properties props = new Properties();
   *   props.load(new FileInputStream(file));
   *   IStreamCoder coder = IStreamCoder.make(Direction.ENCODING);
   *   int retval = Configuration.configure(props, coder);
   *   if (retval < 0)
   *      throw new RuntimeException("Could not configure object");
   *

   * @param properties The properties to use.
   * @param config The item to configure.
   */
  @SuppressWarnings("unchecked")
  public static void configure(final Properties properties, final Configurable config)
  {
    for (
        final Enumeration<String> names = (Enumeration<String>) properties.propertyNames();
        names.hasMoreElements();
    )
    {
      final String name = names.nextElement();
      final String value = properties.getProperty(name);
      if (value != null) {
        try {
          config.setProperty(name, value);
        } catch (PropertyNotFoundException e) {
          log.warn("Could not find property on object {}; name=\"{}\"; value=\"{}\"",
              new Object[]{
              config,
              name,
              value
          });
        }
      }
    }
  }
  /**
   * Configures an {@link Configurable} from a file.
   *

   * This is a handy way to configure a Humble Video {@link Configurable}
   * object, and will also work with FFmpeg preset files.
   *

   * @param file A filename for the properties file.
   * @param config The item to configure.
   * @throws IOException
   * @throws FileNotFoundException
   */
  @SuppressWarnings("unchecked")
  public static void configure(final String file, final Configurable config) throws FileNotFoundException, IOException
  {
    Properties props = new Properties();
    props.load(new FileInputStream(file));
    Configuration.configure(props, config);
  }

}


/* ----------------------------------------------------------------------------
 * This file was automatically generated by SWIG (http://www.swig.org).
 * Version 2.0.6
 *
 * Do not make changes to this file unless you know what you are doing--modify
 * the SWIG interface file instead.
 * ----------------------------------------------------------------------------- */

package io.humble.video;
import io.humble.ferry.*;
/**
 * The parent class for all Encoded media data.
 */
public class MediaEncoded extends Media {
  // JNIHelper.swg: Start generated code
  // >>>>>>>>>>>>>>>>>>>>>>>>>>>
  /**
   * This method is only here to use some references and remove
   * a Eclipse compiler warning.
   */
  @SuppressWarnings("unused")
  private void noop()
  {
    Buffer.make(null, 1);
  }

  private volatile long swigCPtr;

  /**
   * Internal Only.
   */
  protected MediaEncoded(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.MediaEncoded_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected MediaEncoded(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.MediaEncoded_SWIGUpcast(cPtr),
     cMemoryOwn, ref);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that obj is proxying for.
   *
   * @param obj The java proxy object for a native object.
   * @return The raw pointer obj is proxying for.
   */
  protected static long getCPtr(MediaEncoded obj) {
    if (obj == null) return 0;
    return obj.getMyCPtr();
  }

  /**
   * Internal Only.  Not part of public API.
   *
   * Get the raw value of the native object that we're proxying for.
   *
   * @return The raw pointer we're proxying for.
   */
  protected long getMyCPtr() {
    if (swigCPtr == 0) throw new IllegalStateException("underlying native object already deleted");
    return swigCPtr;
  }

  /**
   * Create a new MediaEncoded object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public MediaEncoded copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new MediaEncoded(swigCPtr, swigCMemOwn, getJavaRefCount());
  }

  /**
   * Compares two values, returning true if the underlying objects in native code are the same object.
   *
   * That means you can have two different Java objects, but when you do a comparison, you'll find out
   * they are the EXACT same object.
   *
   * @return True if the underlying native object is the same.  False otherwise.
   */
  public boolean equals(Object obj) {
    boolean equal = false;
    if (obj instanceof MediaEncoded)
      equal = (((MediaEncoded)obj).swigCPtr == this.swigCPtr);
    return equal;
  }

  /**
   * Get a hashable value for this object.
   *
   * @return the hashable value.
   */
  public int hashCode() {
     return (int)swigCPtr;
  }

  // <<<<<<<<<<<<<<<<<<<<<<<<<<<
  // JNIHelper.swg: End generated code

/**
 * Set the time base that time stamps of this object are represented in.

 *

 * @param aBase the new time base.  If null an exception is thrown.
 */
  public void setTimeBase(Rational aBase) {
    VideoJNI.MediaEncoded_setTimeBase(swigCPtr, this, Rational.getCPtr(aBase), aBase);
  }

}