001package net.bramp.ffmpeg.nut;
002
003import com.google.common.base.MoreObjects;
004import java.io.IOException;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008/** Represents a packet in the NUT multimedia container format. */
009public class Packet {
010
011  static final Logger LOG = LoggerFactory.getLogger(Packet.class);
012
013  /** Defines the startcode values used to identify NUT packet types. */
014  public enum Startcode {
015    MAIN(0x7A561F5F04ADL + (((long) ('N' << 8) + 'M') << 48)),
016    STREAM(0x11405BF2F9DBL + (((long) ('N' << 8) + 'S') << 48)),
017    SYNCPOINT(0xE4ADEECA4569L + (((long) ('N' << 8) + 'K') << 48)),
018    INDEX(0xDD672F23E64EL + (((long) ('N' << 8) + 'X') << 48)),
019    INFO(0xAB68B596BA78L + (((long) ('N' << 8) + 'I') << 48));
020
021    private final long startcode;
022
023    Startcode(long startcode) {
024      this.startcode = startcode;
025    }
026
027    /** Returns the numeric startcode value. */
028    public long value() {
029      return startcode;
030    }
031
032    /** Returns whether this startcode equals the given numeric code. */
033    public boolean equalsCode(long startcode) {
034      return this.startcode == startcode;
035    }
036
037    /**
038     * Returns the Startcode enum for this code.
039     *
040     * @param startcode The numeric code for this Startcode.
041     * @return The Startcode
042     */
043    public static Startcode of(long startcode) {
044      for (Startcode c : Startcode.values()) {
045        if (c.equalsCode(startcode)) {
046          return c;
047        }
048      }
049      return null;
050    }
051
052    /** Returns whether the given value could be a valid NUT start code. */
053    public static boolean isPossibleStartcode(long startcode) {
054      return (startcode & 0xFFL) == 'N';
055    }
056
057    /** Returns a human-readable string representation of the given start code. */
058    public static String toString(long startcode) {
059      Startcode c = of(startcode);
060      if (c != null) {
061        return c.name();
062      }
063      return String.format("%X", startcode);
064    }
065  }
066
067  public final PacketHeader header = new PacketHeader();
068  public final PacketFooter footer = new PacketFooter();
069
070  /** Reads the body of the packet from the input stream. */
071  protected void readBody(NutDataInputStream in) throws IOException {
072    // Default implementation does nothing
073  }
074
075  /** Reads a complete packet including header, body, and footer. */
076  public void read(NutDataInputStream in, long startcode) throws IOException {
077    header.read(in, startcode);
078    readBody(in);
079    seekToPacketFooter(in);
080    footer.read(in);
081  }
082
083  /** Skips forward in the stream to the start of the packet footer. */
084  public void seekToPacketFooter(NutDataInputStream in) throws IOException {
085    long current = in.offset();
086    if (current > header.end) {
087      throw new IOException("Can not seek backwards at:" + current + " end:" + header.end);
088    }
089    // TODO Fix this to not cast longs to ints
090    in.skipBytes((int) (header.end - current));
091  }
092
093  @Override
094  public String toString() {
095    return MoreObjects.toStringHelper(this).add("header", header).add("footer", footer).toString();
096  }
097}