package org.jcodec.codecs.h264.decode;
import static java.lang.System.arraycopy;
import static org.jcodec.codecs.h264.H264Utils.Mv.mvC;
import static org.jcodec.codecs.h264.H264Utils.Mv.mvRef;
import static org.jcodec.codecs.h264.H264Utils.Mv.packMv;
import static org.jcodec.common.ArrayUtil.shiftLeft1;
import static org.jcodec.common.tools.MathUtil.clip;

import org.jcodec.codecs.h264.H264Utils.MvList;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.Picture;

public class MBlockDecoderUtils {
    private static boolean debug;
    public static final int NULL_VECTOR = packMv(0, 0, -1);

    public static void debugPrint(Object... arguments) {
        if (debug && arguments.length > 0) {
            if (arguments.length == 1) {
                Logger.debug(""+arguments[0]);
            } else {
                String fmt = (String)arguments[0];
                shiftLeft1(arguments);
                Logger.debug(String.format(fmt, arguments));
            }
        }
    }

    static void collectPredictors(DecoderState sharedState, Picture outMB, int mbX) {
        sharedState.topLeft[0][0] = sharedState.topLine[0][(mbX << 4) + 15];
        sharedState.topLeft[0][1] = outMB.getPlaneData(0)[63];
        sharedState.topLeft[0][2] = outMB.getPlaneData(0)[127];
        sharedState.topLeft[0][3] = outMB.getPlaneData(0)[191];
        arraycopy(outMB.getPlaneData(0), 240, sharedState.topLine[0], mbX << 4, 16);
        copyCol(outMB.getPlaneData(0), 16, 15, 16, sharedState.leftRow[0]);

        collectChromaPredictors(sharedState, outMB, mbX);
    }

    static void collectChromaPredictors(DecoderState sharedState, Picture outMB, int mbX) {
        sharedState.topLeft[1][0] = sharedState.topLine[1][(mbX << 3) + 7];
        sharedState.topLeft[2][0] = sharedState.topLine[2][(mbX << 3) + 7];

        arraycopy(outMB.getPlaneData(1), 56, sharedState.topLine[1], mbX << 3, 8);
        arraycopy(outMB.getPlaneData(2), 56, sharedState.topLine[2], mbX << 3, 8);

        copyCol(outMB.getPlaneData(1), 8, 7, 8, sharedState.leftRow[1]);
        copyCol(outMB.getPlaneData(2), 8, 7, 8, sharedState.leftRow[2]);
    }

    private static void copyCol(byte[] planeData, int n, int off, int stride, byte[] out) {
        for (int i = 0; i < n; i++, off += stride) {
            out[i] = planeData[off];
        }
    }

    static void saveMvsIntra(DeblockerInput di, int mbX, int mbY) {
        for (int j = 0, blkOffY = mbY << 2, blkInd = 0; j < 4; j++, blkOffY++) {
            for (int i = 0, blkOffX = mbX << 2; i < 4; i++, blkOffX++, blkInd++) {
                di.mvs.setMv(blkOffX, blkOffY, 0, NULL_VECTOR);
                di.mvs.setMv(blkOffX, blkOffY, 1, NULL_VECTOR);
            }
        }
    }

    static void mergeResidual(Picture mb, int[][][] residual, int[][] blockLUT, int[][] posLUT) {
        for (int comp = 0; comp < 3; comp++) {
            byte[] to = mb.getPlaneData(comp);
            for (int i = 0; i < to.length; i++) {
                to[i] = (byte) clip(to[i] + residual[comp][blockLUT[comp][i]][posLUT[comp][i]], -128, 127);
            }
        }
    }

    static void saveVect(MvList mv, int list, int from, int to, int vect) {
        for (int i = from; i < to; i++) {
            mv.setMv(i, list, vect);
        }
    }

    /**
     * Calculates median prediction
     * @param a, b, c and d are packed motion vectors
     */
    public static int calcMVPredictionMedian(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb,
            boolean dAvb, int ref, int comp) {

        if (!cAvb) {
            c = d;
            cAvb = dAvb;
        }

        if (aAvb && !bAvb && !cAvb) {
            b = c = a;
            bAvb = cAvb = aAvb;
        }

        a = aAvb ? a : NULL_VECTOR;
        b = bAvb ? b : NULL_VECTOR;
        c = cAvb ? c : NULL_VECTOR;

        if (mvRef(a) == ref && mvRef(b) != ref && mvRef(c) != ref)
            return mvC(a, comp);
        else if (mvRef(b) == ref && mvRef(a) != ref && mvRef(c) != ref)
            return mvC(b, comp);
        else if (mvRef(c) == ref && mvRef(a) != ref && mvRef(b) != ref)
            return mvC(c, comp);

        return mvC(a, comp) + mvC(b, comp) + mvC(c, comp) - min(mvC(a, comp), mvC(b, comp), mvC(c, comp))
                - max(mvC(a, comp), mvC(b, comp), mvC(c, comp));
    }

    public static int max(int x, int x2, int x3) {
        return x > x2 ? (x > x3 ? x : x3) : (x2 > x3 ? x2 : x3);
    }

    public static int min(int x, int x2, int x3) {
        return x < x2 ? (x < x3 ? x : x3) : (x2 < x3 ? x2 : x3);
    }

    static void saveMvs(DeblockerInput di, MvList x, int mbX, int mbY) {
        for (int j = 0, blkOffY = mbY << 2, blkInd = 0; j < 4; j++, blkOffY++) {
            for (int i = 0, blkOffX = mbX << 2; i < 4; i++, blkOffX++, blkInd++) {
                di.mvs.setMv(blkOffX, blkOffY, 0, x.getMv(blkInd, 0));
                di.mvs.setMv(blkOffX, blkOffY, 1, x.getMv(blkInd, 1));
            }
        }
    }

    static void savePrediction8x8(DecoderState sharedState, int mbX, MvList x) {
        sharedState.mvTopLeft.copyPair(0, sharedState.mvTop, (mbX << 2) + 3);
        sharedState.mvLeft.copyPair(0, x, 3);
        sharedState.mvLeft.copyPair(1, x, 7);
        sharedState.mvLeft.copyPair(2, x, 11);
        sharedState.mvLeft.copyPair(3, x, 15);
        sharedState.mvTop.copyPair(mbX << 2, x, 12);
        sharedState.mvTop.copyPair((mbX << 2) + 1, x, 13);
        sharedState.mvTop.copyPair((mbX << 2) + 2, x, 14);
        sharedState.mvTop.copyPair((mbX << 2) + 3, x, 15);
    }

    public static void saveVectIntra(DecoderState sharedState, int mbX) {
        int xx = mbX << 2;

        sharedState.mvTopLeft.copyPair(0, sharedState.mvTop, xx + 3);

        saveVect(sharedState.mvTop, 0, xx, xx + 4, NULL_VECTOR);
        saveVect(sharedState.mvLeft, 0, 0, 4, NULL_VECTOR);
        saveVect(sharedState.mvTop, 1, xx, xx + 4, NULL_VECTOR);
        saveVect(sharedState.mvLeft, 1, 0, 4, NULL_VECTOR);
    }
}


package org.jcodec.codecs.vpx;
import org.jcodec.common.tools.MathUtil;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 *
 * @author The JCodec project
 *
 */
public class VPXBitstream {

    public static final int[] coeffBandMapping = { 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7 };

    private int[][][][] tokenBinProbs;
    private int whtNzLeft = 0;
    private int[] whtNzTop;
    private int[][] dctNzLeft;
    private int[][] dctNzTop;

    public VPXBitstream(int[][][][] tokenBinProbs, int mbWidth) {
        this.dctNzLeft = new int[][] { new int[4], new int[2], new int[2] };
        this.tokenBinProbs = tokenBinProbs;

        whtNzTop = new int[mbWidth];
        dctNzTop = new int[][] { new int[mbWidth << 2], new int[mbWidth << 1], new int[mbWidth << 1] };
    }

    public void encodeCoeffsWHT(VPXBooleanEncoder bc, int[] coeffs, int mbX) {
        int nCoeff = fastCountCoeffWHT(coeffs);
        encodeCoeffs(bc, coeffs, 0, nCoeff, 1, (mbX == 0 || whtNzLeft <= 0 ? 0 : 1) + (whtNzTop[mbX] > 0 ? 1 : 0));
        whtNzLeft = nCoeff;
        whtNzTop[mbX] = nCoeff;
    }

    public void encodeCoeffsDCT15(VPXBooleanEncoder bc, int[] coeffs, int mbX, int blkX, int blkY) {
        int nCoeff = countCoeff(coeffs, 16);
        int blkAbsX = (mbX << 2) + blkX;
        encodeCoeffs(bc, coeffs, 1, nCoeff, 0, (blkAbsX == 0 || dctNzLeft[0][blkY] <= 0 ? 0 : 1) + (dctNzTop[0][blkAbsX] > 0 ? 1 : 0));
        dctNzLeft[0][blkY] = Math.max(nCoeff - 1, 0);
        dctNzTop[0][blkAbsX] = Math.max(nCoeff - 1, 0);
    }

    public void encodeCoeffsDCT16(VPXBooleanEncoder bc, int[] coeffs, int mbX, int blkX, int blkY) {
        int nCoeff = countCoeff(coeffs, 16);
        int blkAbsX = (mbX << 2) + blkX;
        encodeCoeffs(bc, coeffs, 0, nCoeff, 3, (blkAbsX == 0 || dctNzLeft[0][blkY] <= 0 ? 0 : 1) + (dctNzTop[0][blkAbsX] > 0 ? 1 : 0));
        dctNzLeft[0][blkY] = nCoeff;
        dctNzTop[0][blkAbsX] = nCoeff;
    }

    public void encodeCoeffsDCTUV(VPXBooleanEncoder bc, int[] coeffs, int comp, int mbX, int blkX, int blkY) {
        int nCoeff = countCoeff(coeffs, 16);
        int blkAbsX = (mbX << 1) + blkX;
        encodeCoeffs(bc, coeffs, 0, nCoeff, 2, (blkAbsX == 0 || dctNzLeft[comp][blkY] <= 0 ? 0 : 1)
                + (dctNzTop[comp][blkAbsX] > 0 ? 1 : 0));
        dctNzLeft[comp][blkY] = nCoeff;
        dctNzTop[comp][blkAbsX] = nCoeff;
    }

    /**
     * Encodes DCT/WHT coefficients into the provided instance of a boolean
     * encoder
     *
     * @param bc
     * @param coeffs
     */
    public void encodeCoeffs(VPXBooleanEncoder bc, int[] coeffs, int firstCoeff, int nCoeff, int blkType, int ctx) {
        boolean prevZero = false;

        int i;
        for (i = firstCoeff; i < nCoeff; i++) {
            int[] probs = tokenBinProbs[blkType][coeffBandMapping[i]][ctx];

            int coeffAbs = MathUtil.abs(coeffs[i]);
            if (!prevZero)
                bc.writeBit(probs[0], 1);
            if (coeffAbs == 0) {
                bc.writeBit(probs[1], 0);
                ctx = 0;
            } else {
                bc.writeBit(probs[1], 1);
                if (coeffAbs == 1) {
                    bc.writeBit(probs[2], 0);
                    ctx = 1;
                } else {
                    ctx = 2;
                    bc.writeBit(probs[2], 1);
                    if (coeffAbs <= 4) {
                        bc.writeBit(probs[3], 0);
                        if (coeffAbs == 2)
                            bc.writeBit(probs[4], 0);
                        else {
                            bc.writeBit(probs[4], 1);
                            bc.writeBit(probs[5], coeffAbs - 3);
                        }
                    } else {
                        bc.writeBit(probs[3], 1);
                        if (coeffAbs <= 10) {
                            bc.writeBit(probs[6], 0);
                            if (coeffAbs <= 6) {
                                bc.writeBit(probs[7], 0);
                                bc.writeBit(159, coeffAbs - 5);
                            } else {
                                bc.writeBit(probs[7], 1);
                                int d = coeffAbs - 7;
                                bc.writeBit(165, d >> 1);
                                bc.writeBit(145, d & 1);
                            }
                        } else {
                            bc.writeBit(probs[6], 1);
                            if (coeffAbs <= 34) {
                                bc.writeBit(probs[8], 0);
                                if (coeffAbs <= 18) {
                                    bc.writeBit(probs[9], 0);
                                    writeCat3Ext(bc, coeffAbs);
                                } else {
                                    bc.writeBit(probs[9], 1);
                                    writeCat4Ext(bc, coeffAbs);
                                }
                            } else {
                                bc.writeBit(probs[8], 1);
                                if (coeffAbs <= 66) {
                                    bc.writeBit(probs[10], 0);
                                    writeCatExt(bc, coeffAbs, 35, VPXConst.probCoeffExtCat5);
                                } else {
                                    bc.writeBit(probs[10], 1);
                                    writeCatExt(bc, coeffAbs, 67, VPXConst.probCoeffExtCat6);
                                }
                            }
                        }
                    }
                }
                bc.writeBit(128, MathUtil.sign(coeffs[i]));
            }

            prevZero = coeffAbs == 0;
        }
        if (nCoeff < 16) {
            int[] probs = tokenBinProbs[blkType][coeffBandMapping[i]][ctx];
            bc.writeBit(probs[0], 0);
        }
    }

    private static void writeCat3Ext(VPXBooleanEncoder bc, int coeff) {
        int d = coeff - 11;
        bc.writeBit(173, d >> 2);
        bc.writeBit(148, (d >> 1) & 1);
        bc.writeBit(140, d & 1);
    }

    private static void writeCat4Ext(VPXBooleanEncoder bc, int coeff) {
        int d = coeff - 19;
        bc.writeBit(176, d >> 3);
        bc.writeBit(155, (d >> 2) & 1);
        bc.writeBit(140, (d >> 1) & 1);
        bc.writeBit(135, d & 1);
    }

    private static final void writeCatExt(VPXBooleanEncoder bc, int coeff, int catOff, int[] cat) {
        int d = coeff - catOff;
        for (int b = cat.length - 1, i = 0; b >= 0; b--) {
            bc.writeBit(cat[i++], (d >> b) & 1);
        }
    }

    /**
     * Counts number of non-zero coefficients for a WHT block, with shortcut as
     * most of them are likely to be non-zero
     *
     * @param coeffs
     * @return
     */
    private int fastCountCoeffWHT(int[] coeffs) {
        if (coeffs[15] != 0)
            return 16;
        else
            return countCoeff(coeffs, 15);
    }

    /**
     * Counts number of non-zero coefficients
     *
     * @param coeffs
     * @param nCoeff
     * @return
     */
    private int countCoeff(int[] coeffs, int nCoeff) {
        while (nCoeff > 0) {
            --nCoeff;
            if (coeffs[nCoeff] != 0)
                return nCoeff + 1;
        }
        return nCoeff;
    }
}


package org.jcodec.codecs.h264.io.model;
import static org.jcodec.codecs.h264.decode.CAVLCReader.readBool;
import static org.jcodec.codecs.h264.decode.CAVLCReader.readNBit;
import static org.jcodec.codecs.h264.decode.CAVLCReader.readSE;
import static org.jcodec.codecs.h264.decode.CAVLCReader.readUEtrace;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeBool;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeNBit;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeSEtrace;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeTrailingBits;
import static org.jcodec.codecs.h264.io.write.CAVLCWriter.writeUEtrace;
import static org.jcodec.common.model.ColorSpace.MONO;
import static org.jcodec.common.model.ColorSpace.YUV420J;
import static org.jcodec.common.model.ColorSpace.YUV422;
import static org.jcodec.common.model.ColorSpace.YUV444;
import static org.jcodec.platform.Platform.arrayEqualsInt;

import org.jcodec.codecs.h264.H264Const;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.model.ColorSpace;

import java.nio.ByteBuffer;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 *
 * Sequence Parameter Set structure of h264 bitstream
 *
 * capable to serialize and deserialize with CAVLC bitstream
 *
 * @author The JCodec project
 *
 */
public class SeqParameterSet {
    // pic_order_cnt_type
    public int picOrderCntType;
    // field_pic_flag
    public boolean fieldPicFlag;
    // delta_pic_order_always_zero_flag
    public boolean deltaPicOrderAlwaysZeroFlag;
    // mb_adaptive_frame_field_flag
    public boolean mbAdaptiveFrameFieldFlag;
    // direct_8x8_inference_flag
    public boolean direct8x8InferenceFlag;
    // chroma_format_idc
    public ColorSpace chromaFormatIdc;
    // log2_max_frame_num_minus4
    public int log2MaxFrameNumMinus4;
    // log2_max_pic_order_cnt_lsb_minus4
    public int log2MaxPicOrderCntLsbMinus4;
    // pic_height_in_map_units_minus1
    public int picHeightInMapUnitsMinus1;
    // pic_width_in_mbs_minus1
    public int picWidthInMbsMinus1;
    // bit_depth_luma_minus8
    public int bitDepthLumaMinus8;
    // bit_depth_chroma_minus8
    public int bitDepthChromaMinus8;
    // qpprime_y_zero_transform_bypass_flag
    public boolean qpprimeYZeroTransformBypassFlag;
    // profile_idc
    public int profileIdc;
    // constraint_set0_flag
    public boolean constraintSet0Flag;
    // constraint_set1_flag
    public boolean constraintSet1Flag;
    // constraint_set2_flag
    public boolean constraintSet2Flag;
    // constraint_set_3_flag
    public boolean constraintSet3Flag;
    // constraint_set_4_flag
    public boolean constraintSet4Flag;
    // constraint_set_5_flag
    public boolean constraintSet5Flag;
    // level_idc
    public int levelIdc;
    // seq_parameter_set_id
    public int seqParameterSetId;
    /**
     * separate_colour_plane_flag. When a picture is coded using three separate
     * colour planes (separate_colour_plane_flag is equal to 1), a slice
     * contains only macroblocks of one colour component being identified by the
     * corresponding value of colour_plane_id, and each colour component array
     * of a picture consists of slices having the same colour_plane_id value.
     * Coded slices with different values of colour_plane_id within an access
     * unit can be interleaved with each other under the constraint that for
     * each value of colour_plane_id, the coded slice NAL units with that value
     * colour_plane_id shall be in the order of increasing macroblock address
     * for the first macroblock of each coded slice NAL unit.
     */
    public boolean separateColourPlaneFlag;

    /**
     * offset_for_non_ref_pic is used to calculate the picture order count of a
     * non-reference picture as specified in 8.2.1. The value of
     * offset_for_non_ref_pic shall be in the range of -231 to 231 - 1,
     * inclusive.
     */
    public int offsetForNonRefPic;

    /**
     * offset_for_top_to_bottom_field is used to calculate the picture order
     * count of a bottom field as specified in subclause 8.2.1. The value of
     * offset_for_top_to_bottom_field shall be in the range of -231 to 231 - 1,
     * inclusive.
     */
    public int offsetForTopToBottomField;

    // num_ref_frames
    public int numRefFrames;

    /**
     * gaps_in_frame_num_value_allowed_flag specifies the allowed values of
     * frame_num as specified in subclause 7.4.3 and the decoding process in
     * case of an inferred gap between values of frame_num as specified in
     * subclause 8.2.5.2.
     */
    public boolean gapsInFrameNumValueAllowedFlag;

    /**
     * frame_mbs_only_flag equal to 0 specifies that coded pictures of the coded
     * video sequence may either be coded fields or coded frames.
     * frame_mbs_only_flag equal to 1 specifies that every coded picture of the
     * coded video sequence is a coded frame containing only frame macroblocks.
     */
    public boolean frameMbsOnlyFlag;

    // frame_cropping_flag
    public boolean frameCroppingFlag;

    // frame_crop_left_offset
    public int frameCropLeftOffset;

    // frame_crop_right_offset
    public int frameCropRightOffset;

    // frame_crop_top_offset
    public int frameCropTopOffset;

    // frame_crop_bottom_offset
    public int frameCropBottomOffset;

    public int[] offsetForRefFrame;
    public VUIParameters vuiParams;
    public int[][] scalingMatrix;

    // num_ref_frames_in_pic_order_cnt_cycle
    public int numRefFramesInPicOrderCntCycle;

    public static ColorSpace getColor(int id) {
        switch (id) {
        case 0:
            return MONO;
        case 1:
            return YUV420J;
        case 2:
            return YUV422;
        case 3:
            return YUV444;
        }
        throw new RuntimeException("Colorspace not supported");
    }

    public static int fromColor(ColorSpace color) {
        if (color == MONO) {
            return 0;
        } else if (color == YUV420J) {
            return 1;
        } else if (color == YUV422) {
            return 2;
        } else if (color == YUV444) {
            return 3;
        }
        throw new RuntimeException("Colorspace not supported");
    }

    public static SeqParameterSet read(ByteBuffer is) {
        BitReader _in = BitReader.createBitReader(is);
        SeqParameterSet sps = new SeqParameterSet();

        sps.profileIdc = readNBit(_in, 8, "SPS: profile_idc");
        sps.constraintSet0Flag = readBool(_in, "SPS: constraint_set_0_flag");
        sps.constraintSet1Flag = readBool(_in, "SPS: constraint_set_1_flag");
        sps.constraintSet2Flag = readBool(_in, "SPS: constraint_set_2_flag");
        sps.constraintSet3Flag = readBool(_in, "SPS: constraint_set_3_flag");
        sps.constraintSet4Flag = readBool(_in, "SPS: constraint_set_4_flag");
        sps.constraintSet5Flag = readBool(_in, "SPS: constraint_set_5_flag");
        readNBit(_in, 2, "SPS: reserved_zero_2bits");
        sps.levelIdc = (int) readNBit(_in, 8, "SPS: level_idc");
        sps.seqParameterSetId = readUEtrace(_in, "SPS: seq_parameter_set_id");

        if (sps.profileIdc == 100 || sps.profileIdc == 110 || sps.profileIdc == 122 || sps.profileIdc == 144) {
            sps.chromaFormatIdc = getColor(readUEtrace(_in, "SPS: chroma_format_idc"));
            if (sps.chromaFormatIdc == YUV444) {
                sps.separateColourPlaneFlag = readBool(_in, "SPS: separate_colour_plane_flag");
            }
            sps.bitDepthLumaMinus8 = readUEtrace(_in, "SPS: bit_depth_luma_minus8");
            sps.bitDepthChromaMinus8 = readUEtrace(_in, "SPS: bit_depth_chroma_minus8");
            sps.qpprimeYZeroTransformBypassFlag = readBool(_in, "SPS: qpprime_y_zero_transform_bypass_flag");
            boolean seqScalingMatrixPresent = readBool(_in, "SPS: seq_scaling_matrix_present_lag");
            if (seqScalingMatrixPresent) {
                readScalingListMatrix(_in, sps);
            }
        } else {
            sps.chromaFormatIdc = YUV420J;
        }
        sps.log2MaxFrameNumMinus4 = readUEtrace(_in, "SPS: log2_max_frame_num_minus4");
        sps.picOrderCntType = readUEtrace(_in, "SPS: pic_order_cnt_type");
        if (sps.picOrderCntType == 0) {
            sps.log2MaxPicOrderCntLsbMinus4 = readUEtrace(_in, "SPS: log2_max_pic_order_cnt_lsb_minus4");
        } else if (sps.picOrderCntType == 1) {
            sps.deltaPicOrderAlwaysZeroFlag = readBool(_in, "SPS: delta_pic_order_always_zero_flag");
            sps.offsetForNonRefPic = readSE(_in, "SPS: offset_for_non_ref_pic");
            sps.offsetForTopToBottomField = readSE(_in, "SPS: offset_for_top_to_bottom_field");
            sps.numRefFramesInPicOrderCntCycle = readUEtrace(_in, "SPS: num_ref_frames_in_pic_order_cnt_cycle");
            sps.offsetForRefFrame = new int[sps.numRefFramesInPicOrderCntCycle];
            for (int i = 0; i < sps.numRefFramesInPicOrderCntCycle; i++) {
                sps.offsetForRefFrame[i] = readSE(_in, "SPS: offsetForRefFrame [" + i + "]");
            }
        }
        sps.numRefFrames = readUEtrace(_in, "SPS: num_ref_frames");
        sps.gapsInFrameNumValueAllowedFlag = readBool(_in, "SPS: gaps_in_frame_num_value_allowed_flag");
        sps.picWidthInMbsMinus1 = readUEtrace(_in, "SPS: pic_width_in_mbs_minus1");
        sps.picHeightInMapUnitsMinus1 = readUEtrace(_in, "SPS: pic_height_in_map_units_minus1");
        sps.frameMbsOnlyFlag = readBool(_in, "SPS: frame_mbs_only_flag");
        if (!sps.frameMbsOnlyFlag) {
            sps.mbAdaptiveFrameFieldFlag = readBool(_in, "SPS: mb_adaptive_frame_field_flag");
        }
        sps.direct8x8InferenceFlag = readBool(_in, "SPS: direct_8x8_inference_flag");
        sps.frameCroppingFlag = readBool(_in, "SPS: frame_cropping_flag");
        if (sps.frameCroppingFlag) {
            sps.frameCropLeftOffset = readUEtrace(_in, "SPS: frame_crop_left_offset");
            sps.frameCropRightOffset = readUEtrace(_in, "SPS: frame_crop_right_offset");
            sps.frameCropTopOffset = readUEtrace(_in, "SPS: frame_crop_top_offset");
            sps.frameCropBottomOffset = readUEtrace(_in, "SPS: frame_crop_bottom_offset");
        }
        boolean vuiParametersPresentFlag = readBool(_in, "SPS: vui_parameters_present_flag");
        if (vuiParametersPresentFlag)
            sps.vuiParams = readVUIParameters(_in);

        return sps;
    }

    public static void writeScalingList(BitWriter out, int[][] scalingMatrix, int which)  {
        // Want to find out if the default scaling list is actually used
        boolean useDefaultScalingMatrixFlag = false;
        switch(which) {
        case 0: // 4x4 intra y
            useDefaultScalingMatrixFlag = arrayEqualsInt(scalingMatrix[which], H264Const.defaultScalingList4x4Intra);
            break;
        case 1:
        case 2:
            useDefaultScalingMatrixFlag = arrayEqualsInt(scalingMatrix[which], scalingMatrix[0]);
            break;
        case 3:
            useDefaultScalingMatrixFlag = arrayEqualsInt(scalingMatrix[which], H264Const.defaultScalingList4x4Inter);
            break;
        case 4:
        case 5:
            useDefaultScalingMatrixFlag = arrayEqualsInt(scalingMatrix[which], scalingMatrix[3]);
            break;
        case 6:
            useDefaultScalingMatrixFlag = arrayEqualsInt(scalingMatrix[which], H264Const.defaultScalingList8x8Intra);
            break;
        case 7:
            useDefaultScalingMatrixFlag = arrayEqualsInt(scalingMatrix[which], H264Const.defaultScalingList8x8Inter);
            break;
        }
        int[] scalingList = scalingMatrix[which];

        if (useDefaultScalingMatrixFlag) {
            writeSEtrace(out, -8, "SPS: ");
            return;
        }

        int lastScale = 8;
        int nextScale = 8;
        for (int j = 0; j < scalingList.length; j++) {
            if (nextScale != 0) {
                int deltaScale = scalingList[j] - lastScale - 256;
                writeSEtrace(out, deltaScale, "SPS: ");
            }
            lastScale = scalingList[j];
        }
    }

    //Wrong usage of Javascript keyword:in
    public static int[] readScalingList(BitReader src, int sizeOfScalingList)  {

        int[] scalingList = new int[sizeOfScalingList];
        int lastScale = 8;
        int nextScale = 8;
        for (int j = 0; j < sizeOfScalingList; j++) {
            if (nextScale != 0) {
                int deltaScale = readSE(src, "deltaScale");
                nextScale = (lastScale + deltaScale + 256) % 256;
                if (j == 0 && nextScale == 0)
                    return null;
            }
            scalingList[j] = nextScale == 0 ? lastScale : nextScale;
            lastScale = scalingList[j];
        }
        return scalingList;
    }

    //Wrong usage of Javascript keyword:in
    private static void readScalingListMatrix(BitReader src, SeqParameterSet sps) {
        sps.scalingMatrix = new int[8][];
        for (int i = 0; i < 8; i++) {
            boolean seqScalingListPresentFlag = readBool(src, "SPS: seqScalingListPresentFlag");
            if (seqScalingListPresentFlag) {
                int scalingListSize = i < 6 ? 16 : 64;
                sps.scalingMatrix[i] = readScalingList(src, scalingListSize);
            }
        }
    }

    private static VUIParameters readVUIParameters(BitReader _in) {
        VUIParameters vuip = new VUIParameters();
        vuip.aspectRatioInfoPresentFlag = readBool(_in, "VUI: aspect_ratio_info_present_flag");
        if (vuip.aspectRatioInfoPresentFlag) {
            vuip.aspectRatio = AspectRatio.fromValue((int) readNBit(_in, 8, "VUI: aspect_ratio"));
            if (vuip.aspectRatio == AspectRatio.Extended_SAR) {
                vuip.sarWidth = (int) readNBit(_in, 16, "VUI: sar_width");
                vuip.sarHeight = (int) readNBit(_in, 16, "VUI: sar_height");
            }
        }
        vuip.overscanInfoPresentFlag = readBool(_in, "VUI: overscan_info_present_flag");
        if (vuip.overscanInfoPresentFlag) {
            vuip.overscanAppropriateFlag = readBool(_in, "VUI: overscan_appropriate_flag");
        }
        vuip.videoSignalTypePresentFlag = readBool(_in, "VUI: video_signal_type_present_flag");
        if (vuip.videoSignalTypePresentFlag) {
            vuip.videoFormat = (int) readNBit(_in, 3, "VUI: video_format");
            vuip.videoFullRangeFlag = readBool(_in, "VUI: video_full_range_flag");
            vuip.colourDescriptionPresentFlag = readBool(_in, "VUI: colour_description_present_flag");
            if (vuip.colourDescriptionPresentFlag) {
                vuip.colourPrimaries = (int) readNBit(_in, 8, "VUI: colour_primaries");
                vuip.transferCharacteristics = (int) readNBit(_in, 8, "VUI: transfer_characteristics");
                vuip.matrixCoefficients = (int) readNBit(_in, 8, "VUI: matrix_coefficients");
            }
        }
        vuip.chromaLocInfoPresentFlag = readBool(_in, "VUI: chroma_loc_info_present_flag");
        if (vuip.chromaLocInfoPresentFlag) {
            vuip.chromaSampleLocTypeTopField = readUEtrace(_in, "VUI chroma_sample_loc_type_top_field");
            vuip.chromaSampleLocTypeBottomField = readUEtrace(_in, "VUI chroma_sample_loc_type_bottom_field");
        }
        vuip.timingInfoPresentFlag = readBool(_in, "VUI: timing_info_present_flag");
        if (vuip.timingInfoPresentFlag) {
            vuip.numUnitsInTick = (int) readNBit(_in, 32, "VUI: num_units_in_tick");
            vuip.timeScale = (int) readNBit(_in, 32, "VUI: time_scale");
            vuip.fixedFrameRateFlag = readBool(_in, "VUI: fixed_frame_rate_flag");
        }
        boolean nalHRDParametersPresentFlag = readBool(_in, "VUI: nal_hrd_parameters_present_flag");
        if (nalHRDParametersPresentFlag)
            vuip.nalHRDParams = readHRDParameters(_in);
        boolean vclHRDParametersPresentFlag = readBool(_in, "VUI: vcl_hrd_parameters_present_flag");
        if (vclHRDParametersPresentFlag)
            vuip.vclHRDParams = readHRDParameters(_in);
        if (nalHRDParametersPresentFlag || vclHRDParametersPresentFlag) {
            vuip.lowDelayHrdFlag = readBool(_in, "VUI: low_delay_hrd_flag");
        }
        vuip.picStructPresentFlag = readBool(_in, "VUI: pic_struct_present_flag");
        boolean bitstreamRestrictionFlag = readBool(_in, "VUI: bitstream_restriction_flag");
        if (bitstreamRestrictionFlag) {
            vuip.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
            vuip.bitstreamRestriction.motionVectorsOverPicBoundariesFlag = readBool(_in,
                    "VUI: motion_vectors_over_pic_boundaries_flag");
            vuip.bitstreamRestriction.maxBytesPerPicDenom = readUEtrace(_in, "VUI max_bytes_per_pic_denom");
            vuip.bitstreamRestriction.maxBitsPerMbDenom = readUEtrace(_in, "VUI max_bits_per_mb_denom");
            vuip.bitstreamRestriction.log2MaxMvLengthHorizontal = readUEtrace(_in, "VUI log2_max_mv_length_horizontal");
            vuip.bitstreamRestriction.log2MaxMvLengthVertical = readUEtrace(_in, "VUI log2_max_mv_length_vertical");
            vuip.bitstreamRestriction.numReorderFrames = readUEtrace(_in, "VUI num_reorder_frames");
            vuip.bitstreamRestriction.maxDecFrameBuffering = readUEtrace(_in, "VUI max_dec_frame_buffering");
        }

        return vuip;
    }

    private static HRDParameters readHRDParameters(BitReader _in) {
        HRDParameters hrd = new HRDParameters();
        hrd.cpbCntMinus1 = readUEtrace(_in, "SPS: cpb_cnt_minus1");
        hrd.bitRateScale = (int) readNBit(_in, 4, "HRD: bit_rate_scale");
        hrd.cpbSizeScale = (int) readNBit(_in, 4, "HRD: cpb_size_scale");
        hrd.bitRateValueMinus1 = new int[hrd.cpbCntMinus1 + 1];
        hrd.cpbSizeValueMinus1 = new int[hrd.cpbCntMinus1 + 1];
        hrd.cbrFlag = new boolean[hrd.cpbCntMinus1 + 1];

        for (int SchedSelIdx = 0; SchedSelIdx <= hrd.cpbCntMinus1; SchedSelIdx++) {
            hrd.bitRateValueMinus1[SchedSelIdx] = readUEtrace(_in, "HRD: bit_rate_value_minus1");
            hrd.cpbSizeValueMinus1[SchedSelIdx] = readUEtrace(_in, "HRD: cpb_size_value_minus1");
            hrd.cbrFlag[SchedSelIdx] = readBool(_in, "HRD: cbr_flag");
        }
        hrd.initialCpbRemovalDelayLengthMinus1 = (int) readNBit(_in, 5,
                "HRD: initial_cpb_removal_delay_length_minus1");
        hrd.cpbRemovalDelayLengthMinus1 = (int) readNBit(_in, 5, "HRD: cpb_removal_delay_length_minus1");
        hrd.dpbOutputDelayLengthMinus1 = (int) readNBit(_in, 5, "HRD: dpb_output_delay_length_minus1");
        hrd.timeOffsetLength = (int) readNBit(_in, 5, "HRD: time_offset_length");
        return hrd;
    }

    public void write(ByteBuffer out) {
        BitWriter writer = new BitWriter(out);

        writeNBit(writer, profileIdc, 8, "SPS: profile_idc");
        writeBool(writer, constraintSet0Flag, "SPS: constraint_set_0_flag");
        writeBool(writer, constraintSet1Flag, "SPS: constraint_set_1_flag");
        writeBool(writer, constraintSet2Flag, "SPS: constraint_set_2_flag");
        writeBool(writer, constraintSet3Flag, "SPS: constraint_set_3_flag");
        writeBool(writer, constraintSet4Flag, "SPS: constraint_set_4_flag");
        writeBool(writer, constraintSet5Flag, "SPS: constraint_set_5_flag");
        writeNBit(writer, 0, 2, "SPS: reserved");
        writeNBit(writer, levelIdc, 8, "SPS: level_idc");
        writeUEtrace(writer, seqParameterSetId, "SPS: seq_parameter_set_id");

        if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 144) {
            writeUEtrace(writer, fromColor(chromaFormatIdc), "SPS: chroma_format_idc");
            if (chromaFormatIdc == YUV444) {
                writeBool(writer, separateColourPlaneFlag, "SPS: residual_color_transform_flag");
            }
            writeUEtrace(writer, bitDepthLumaMinus8, "SPS: ");
            writeUEtrace(writer, bitDepthChromaMinus8, "SPS: ");
            writeBool(writer, qpprimeYZeroTransformBypassFlag, "SPS: qpprime_y_zero_transform_bypass_flag");
            writeBool(writer, scalingMatrix != null, "SPS: ");
            if (scalingMatrix != null) {
                for (int i = 0; i < 8; i++) {
                    writeBool(writer, scalingMatrix[i] != null, "SPS: ");
                    if (scalingMatrix[i] != null)
                        writeScalingList(writer, scalingMatrix, i);
                }
            }
        }
        writeUEtrace(writer, log2MaxFrameNumMinus4, "SPS: log2_max_frame_num_minus4");
        writeUEtrace(writer, picOrderCntType, "SPS: pic_order_cnt_type");
        if (picOrderCntType == 0) {
            writeUEtrace(writer, log2MaxPicOrderCntLsbMinus4, "SPS: log2_max_pic_order_cnt_lsb_minus4");
        } else if (picOrderCntType == 1) {
            writeBool(writer, deltaPicOrderAlwaysZeroFlag, "SPS: delta_pic_order_always_zero_flag");
            writeSEtrace(writer, offsetForNonRefPic, "SPS: offset_for_non_ref_pic");
            writeSEtrace(writer, offsetForTopToBottomField, "SPS: offset_for_top_to_bottom_field");
            writeUEtrace(writer, offsetForRefFrame.length, "SPS: ");
            for (int i = 0; i < offsetForRefFrame.length; i++)
                writeSEtrace(writer, offsetForRefFrame[i], "SPS: ");
        }
        writeUEtrace(writer, numRefFrames, "SPS: num_ref_frames");
        writeBool(writer, gapsInFrameNumValueAllowedFlag, "SPS: gaps_in_frame_num_value_allowed_flag");
        writeUEtrace(writer, picWidthInMbsMinus1, "SPS: pic_width_in_mbs_minus1");
        writeUEtrace(writer, picHeightInMapUnitsMinus1, "SPS: pic_height_in_map_units_minus1");
        writeBool(writer, frameMbsOnlyFlag, "SPS: frame_mbs_only_flag");
        if (!frameMbsOnlyFlag) {
            writeBool(writer, mbAdaptiveFrameFieldFlag, "SPS: mb_adaptive_frame_field_flag");
        }
        writeBool(writer, direct8x8InferenceFlag, "SPS: direct_8x8_inference_flag");
        writeBool(writer, frameCroppingFlag, "SPS: frame_cropping_flag");
        if (frameCroppingFlag) {
            writeUEtrace(writer, frameCropLeftOffset, "SPS: frame_crop_left_offset");
            writeUEtrace(writer, frameCropRightOffset, "SPS: frame_crop_right_offset");
            writeUEtrace(writer, frameCropTopOffset, "SPS: frame_crop_top_offset");
            writeUEtrace(writer, frameCropBottomOffset, "SPS: frame_crop_bottom_offset");
        }
        writeBool(writer, vuiParams != null, "SPS: ");
        if (vuiParams != null)
            writeVUIParameters(vuiParams, writer);

        writeTrailingBits(writer);
    }

    private void writeVUIParameters(VUIParameters vuip, BitWriter writer) {
        writeBool(writer, vuip.aspectRatioInfoPresentFlag, "VUI: aspect_ratio_info_present_flag");
        if (vuip.aspectRatioInfoPresentFlag) {
            writeNBit(writer, vuip.aspectRatio.getValue(), 8, "VUI: aspect_ratio");
            if (vuip.aspectRatio == AspectRatio.Extended_SAR) {
                writeNBit(writer, vuip.sarWidth, 16, "VUI: sar_width");
                writeNBit(writer, vuip.sarHeight, 16, "VUI: sar_height");
            }
        }
        writeBool(writer, vuip.overscanInfoPresentFlag, "VUI: overscan_info_present_flag");
        if (vuip.overscanInfoPresentFlag) {
            writeBool(writer, vuip.overscanAppropriateFlag, "VUI: overscan_appropriate_flag");
        }
        writeBool(writer, vuip.videoSignalTypePresentFlag, "VUI: video_signal_type_present_flag");
        if (vuip.videoSignalTypePresentFlag) {
            writeNBit(writer, vuip.videoFormat, 3, "VUI: video_format");
            writeBool(writer, vuip.videoFullRangeFlag, "VUI: video_full_range_flag");
            writeBool(writer, vuip.colourDescriptionPresentFlag, "VUI: colour_description_present_flag");
            if (vuip.colourDescriptionPresentFlag) {
                writeNBit(writer, vuip.colourPrimaries, 8, "VUI: colour_primaries");
                writeNBit(writer, vuip.transferCharacteristics, 8, "VUI: transfer_characteristics");
                writeNBit(writer, vuip.matrixCoefficients, 8, "VUI: matrix_coefficients");
            }
        }
        writeBool(writer, vuip.chromaLocInfoPresentFlag, "VUI: chroma_loc_info_present_flag");
        if (vuip.chromaLocInfoPresentFlag) {
            writeUEtrace(writer, vuip.chromaSampleLocTypeTopField, "VUI: chroma_sample_loc_type_top_field");
            writeUEtrace(writer, vuip.chromaSampleLocTypeBottomField, "VUI: chroma_sample_loc_type_bottom_field");
        }
        writeBool(writer, vuip.timingInfoPresentFlag, "VUI: timing_info_present_flag");
        if (vuip.timingInfoPresentFlag) {
            writeNBit(writer, vuip.numUnitsInTick, 32, "VUI: num_units_in_tick");
            writeNBit(writer, vuip.timeScale, 32, "VUI: time_scale");
            writeBool(writer, vuip.fixedFrameRateFlag, "VUI: fixed_frame_rate_flag");
        }
        writeBool(writer, vuip.nalHRDParams != null, "VUI: ");
        if (vuip.nalHRDParams != null) {
            writeHRDParameters(vuip.nalHRDParams, writer);
        }
        writeBool(writer, vuip.vclHRDParams != null, "VUI: ");
        if (vuip.vclHRDParams != null) {
            writeHRDParameters(vuip.vclHRDParams, writer);
        }

        if (vuip.nalHRDParams != null || vuip.vclHRDParams != null) {
            writeBool(writer, vuip.lowDelayHrdFlag, "VUI: low_delay_hrd_flag");
        }
        writeBool(writer, vuip.picStructPresentFlag, "VUI: pic_struct_present_flag");
        writeBool(writer, vuip.bitstreamRestriction != null, "VUI: ");
        if (vuip.bitstreamRestriction != null) {
            writeBool(writer, vuip.bitstreamRestriction.motionVectorsOverPicBoundariesFlag,
                    "VUI: motion_vectors_over_pic_boundaries_flag");
            writeUEtrace(writer, vuip.bitstreamRestriction.maxBytesPerPicDenom, "VUI: max_bytes_per_pic_denom");
            writeUEtrace(writer, vuip.bitstreamRestriction.maxBitsPerMbDenom, "VUI: max_bits_per_mb_denom");
            writeUEtrace(writer, vuip.bitstreamRestriction.log2MaxMvLengthHorizontal,
                    "VUI: log2_max_mv_length_horizontal");
            writeUEtrace(writer, vuip.bitstreamRestriction.log2MaxMvLengthVertical, "VUI: log2_max_mv_length_vertical");
            writeUEtrace(writer, vuip.bitstreamRestriction.numReorderFrames, "VUI: num_reorder_frames");
            writeUEtrace(writer, vuip.bitstreamRestriction.maxDecFrameBuffering, "VUI: max_dec_frame_buffering");
        }

    }

    private void writeHRDParameters(HRDParameters hrd, BitWriter writer) {
        writeUEtrace(writer, hrd.cpbCntMinus1, "HRD: cpb_cnt_minus1");
        writeNBit(writer, hrd.bitRateScale, 4, "HRD: bit_rate_scale");
        writeNBit(writer, hrd.cpbSizeScale, 4, "HRD: cpb_size_scale");

        for (int SchedSelIdx = 0; SchedSelIdx <= hrd.cpbCntMinus1; SchedSelIdx++) {
            writeUEtrace(writer, hrd.bitRateValueMinus1[SchedSelIdx], "HRD: ");
            writeUEtrace(writer, hrd.cpbSizeValueMinus1[SchedSelIdx], "HRD: ");
            writeBool(writer, hrd.cbrFlag[SchedSelIdx], "HRD: ");
        }
        writeNBit(writer, hrd.initialCpbRemovalDelayLengthMinus1, 5,
                "HRD: initial_cpb_removal_delay_length_minus1");
        writeNBit(writer, hrd.cpbRemovalDelayLengthMinus1, 5, "HRD: cpb_removal_delay_length_minus1");
        writeNBit(writer, hrd.dpbOutputDelayLengthMinus1, 5, "HRD: dpb_output_delay_length_minus1");
        writeNBit(writer, hrd.timeOffsetLength, 5, "HRD: time_offset_length");
    }

    public SeqParameterSet copy() {
        ByteBuffer buf = ByteBuffer.allocate(2048);
        write(buf);
        buf.flip();
        return read(buf);
    }

    public int getPicOrderCntType() {
        return picOrderCntType;
    }

    public boolean isFieldPicFlag() {
        return fieldPicFlag;
    }

    public boolean isDeltaPicOrderAlwaysZeroFlag() {
        return deltaPicOrderAlwaysZeroFlag;
    }

    public boolean isMbAdaptiveFrameFieldFlag() {
        return mbAdaptiveFrameFieldFlag;
    }

    public boolean isDirect8x8InferenceFlag() {
        return direct8x8InferenceFlag;
    }

    public ColorSpace getChromaFormatIdc() {
        return chromaFormatIdc;
    }

    public int getLog2MaxFrameNumMinus4() {
        return log2MaxFrameNumMinus4;
    }

    public int getLog2MaxPicOrderCntLsbMinus4() {
        return log2MaxPicOrderCntLsbMinus4;
    }

    public int getPicHeightInMapUnitsMinus1() {
        return picHeightInMapUnitsMinus1;
    }

    public int getPicWidthInMbsMinus1() {
        return picWidthInMbsMinus1;
    }

    public int getBitDepthLumaMinus8() {
        return bitDepthLumaMinus8;
    }

    public int getBitDepthChromaMinus8() {
        return bitDepthChromaMinus8;
    }

    public boolean isQpprimeYZeroTransformBypassFlag() {
        return qpprimeYZeroTransformBypassFlag;
    }

    public int getProfileIdc() {
        return profileIdc;
    }

    public boolean isConstraintSet0Flag() {
        return constraintSet0Flag;
    }

    public boolean isConstraintSet1Flag() {
        return constraintSet1Flag;
    }

    public boolean isConstraintSet2Flag() {
        return constraintSet2Flag;
    }

    public boolean isConstraintSet3Flag() {
        return constraintSet3Flag;
    }

    public boolean isConstraintSet4Flag() {
        return constraintSet4Flag;
    }

    public boolean isConstraintSet5Flag() {
        return constraintSet5Flag;
    }

    public int getLevelIdc() {
        return levelIdc;
    }

    public int getSeqParameterSetId() {
        return seqParameterSetId;
    }

    public boolean isResidualColorTransformFlag() {
        return separateColourPlaneFlag;
    }

    public int getOffsetForNonRefPic() {
        return offsetForNonRefPic;
    }

    public int getOffsetForTopToBottomField() {
        return offsetForTopToBottomField;
    }

    public int getNumRefFrames() {
        return numRefFrames;
    }

    public boolean isGapsInFrameNumValueAllowedFlag() {
        return gapsInFrameNumValueAllowedFlag;
    }

    public boolean isFrameMbsOnlyFlag() {
        return frameMbsOnlyFlag;
    }

    public boolean isFrameCroppingFlag() {
        return frameCroppingFlag;
    }

    public int getFrameCropLeftOffset() {
        return frameCropLeftOffset;
    }

    public int getFrameCropRightOffset() {
        return frameCropRightOffset;
    }

    public int getFrameCropTopOffset() {
        return frameCropTopOffset;
    }

    public int getFrameCropBottomOffset() {
        return frameCropBottomOffset;
    }

    public int[] getOffsetForRefFrame() {
        return offsetForRefFrame;
    }

    public VUIParameters getVuiParams() {
        return vuiParams;
    }

    public int[][] getScalingMatrix() {
        return scalingMatrix;
    }

    public int getNumRefFramesInPicOrderCntCycle() {
        return numRefFramesInPicOrderCntCycle;
    }

    public static int getPicHeightInMbs(SeqParameterSet sps) {
        int picHeightInMbs = (sps.picHeightInMapUnitsMinus1 + 1) << (sps.frameMbsOnlyFlag ? 0 : 1);
        return picHeightInMbs;
    }
}


package org.jcodec.codecs.h264;
import static java.lang.System.arraycopy;
import static org.jcodec.codecs.h264.H264Utils.escapeNAL;

import org.jcodec.codecs.h264.encode.DumbRateControl;
import org.jcodec.codecs.h264.encode.EncodedMB;
import org.jcodec.codecs.h264.encode.MBEncoderHelper;
import org.jcodec.codecs.h264.encode.MBEncoderI16x16;
import org.jcodec.codecs.h264.encode.MBEncoderP16x16;
import org.jcodec.codecs.h264.encode.MotionEstimator;
import org.jcodec.codecs.h264.encode.RateControl;
import org.jcodec.codecs.h264.io.CAVLC;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.RefPicMarkingIDR;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.codecs.h264.io.write.CAVLCWriter;
import org.jcodec.codecs.h264.io.write.SliceHeaderWriter;
import org.jcodec.common.VideoEncoder;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Size;
import org.jcodec.common.tools.MathUtil;

import java.nio.ByteBuffer;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 *
 * MPEG 4 AVC ( H.264 ) Encoder
 *
 * Conforms to H.264 ( ISO/IEC 14496-10 ) specifications
 *
 * @author The JCodec project
 *
 */
public class H264Encoder extends VideoEncoder {

    // private static final int QP = 20;
    private static final int KEY_INTERVAL_DEFAULT = 25;
    private static final int MOTION_SEARCH_RANGE_DEFAULT = 16;

    public static H264Encoder createH264Encoder() {
        return new H264Encoder(new DumbRateControl());
    }

    private CAVLC[] cavlc;
    private byte[][] leftRow;
    private byte[][] topLine;
    private RateControl rc;
    private int frameNumber;
    private int keyInterval;
    private int motionSearchRange;

    private int maxPOC;

    private int maxFrameNumber;

    private SeqParameterSet sps;

    private PictureParameterSet pps;

    private MBEncoderI16x16 mbEncoderI16x16;

    private MBEncoderP16x16 mbEncoderP16x16;

    private Picture ref;
    private Picture picOut;
    private EncodedMB[] topEncoded;

    private EncodedMB outMB;

    public H264Encoder(RateControl rc) {
        this.rc = rc;
        this.keyInterval = KEY_INTERVAL_DEFAULT;
        this.motionSearchRange = MOTION_SEARCH_RANGE_DEFAULT;
    }

    public int getKeyInterval() {
        return keyInterval;
    }

    public void setKeyInterval(int keyInterval) {
        this.keyInterval = keyInterval;
    }

    public int getMotionSearchRange() {
        return motionSearchRange;
    }

    public void setMotionSearchRange(int motionSearchRange) {
        this.motionSearchRange = motionSearchRange;
    }

    /**
     * Encode this picture into h.264 frame. Frame type will be selected by
     * encoder.
     */
    public EncodedFrame encodeFrame(Picture pic, ByteBuffer _out) {
        if (pic.getColor() != ColorSpace.YUV420J)
            throw new IllegalArgumentException("Input picture color is not supported: " + pic.getColor());

        if (frameNumber >= keyInterval) {
            frameNumber = 0;
        }

        SliceType sliceType = frameNumber == 0 ? SliceType.I : SliceType.P;
        boolean idr = frameNumber == 0;

        ByteBuffer data = doEncodeFrame(pic, _out, idr, frameNumber++, sliceType);
        return new EncodedFrame(data, idr);
    }

    /**
     * Encode this picture as an IDR frame. IDR frame starts a new independently
     * decodeable video sequence
     *
     * @param pic
     * @param _out
     * @return
     */
    public ByteBuffer encodeIDRFrame(Picture pic, ByteBuffer _out) {
        frameNumber = 0;
        return doEncodeFrame(pic, _out, true, frameNumber, SliceType.I);
    }

    /**
     * Encode this picture as a P-frame. P-frame is an frame predicted from one
     * or more of the previosly decoded frame and is usually 10x less in size
     * then the IDR frame.
     *
     * @param pic
     * @param _out
     * @return
     */
    public ByteBuffer encodePFrame(Picture pic, ByteBuffer _out) {
        frameNumber++;
        return doEncodeFrame(pic, _out, true, frameNumber, SliceType.P);
    }

    public ByteBuffer doEncodeFrame(Picture pic, ByteBuffer _out, boolean idr, int frameNumber, SliceType frameType) {
        ByteBuffer dup = _out.duplicate();
        int maxSize = Math.min(dup.remaining(), pic.getWidth() * pic.getHeight());
        maxSize -= (maxSize >>> 6); // 1.5% to account for escaping
        int qp = rc.startPicture(pic.getSize(), maxSize, frameType);

        if (idr) {
            sps = initSPS(new Size(pic.getCroppedWidth(), pic.getCroppedHeight()));
            pps = initPPS();

            maxPOC = 1 << (sps.log2MaxPicOrderCntLsbMinus4 + 4);
            maxFrameNumber = 1 << (sps.log2MaxFrameNumMinus4 + 4);
        }

        if (idr) {
            dup.putInt(0x1);
            new NALUnit(NALUnitType.SPS, 3).write(dup);
            writeSPS(dup, sps);

            dup.putInt(0x1);
            new NALUnit(NALUnitType.PPS, 3).write(dup);
            writePPS(dup, pps);
        }

        int mbWidth = sps.picWidthInMbsMinus1 + 1;
        int mbHeight = sps.picHeightInMapUnitsMinus1 + 1;

        leftRow = new byte[][] { new byte[16], new byte[8], new byte[8] };
        topLine = new byte[][] { new byte[mbWidth << 4], new byte[mbWidth << 3], new byte[mbWidth << 3] };
        picOut = Picture.create(mbWidth << 4, mbHeight << 4, ColorSpace.YUV420J);

        outMB = new EncodedMB();
        topEncoded = new EncodedMB[mbWidth];
        for (int i = 0; i < mbWidth; i++)
            topEncoded[i] = new EncodedMB();

        encodeSlice(sps, pps, pic, dup, idr, frameNumber, frameType, qp);

        putLastMBLine();

        ref = picOut;

        dup.flip();
        return dup;
    }

    private void writePPS(ByteBuffer dup, PictureParameterSet pps) {
        ByteBuffer tmp = ByteBuffer.allocate(1024);
        pps.write(tmp);
        tmp.flip();
        escapeNAL(tmp, dup);
    }

    private void writeSPS(ByteBuffer dup, SeqParameterSet sps) {
        ByteBuffer tmp = ByteBuffer.allocate(1024);
        sps.write(tmp);
        tmp.flip();
        escapeNAL(tmp, dup);
    }

    public PictureParameterSet initPPS() {
        PictureParameterSet pps = new PictureParameterSet();
        pps.picInitQpMinus26 = 0; // start with qp = 26
        return pps;
    }

    public SeqParameterSet initSPS(Size sz) {
        SeqParameterSet sps = new SeqParameterSet();
        sps.picWidthInMbsMinus1 = ((sz.getWidth() + 15) >> 4) - 1;
        sps.picHeightInMapUnitsMinus1 = ((sz.getHeight() + 15) >> 4) - 1;
        sps.chromaFormatIdc = ColorSpace.YUV420J;
        sps.profileIdc = 66;
        sps.levelIdc = 40;
        sps.numRefFrames = 1;
        sps.frameMbsOnlyFlag = true;
        sps.log2MaxFrameNumMinus4 = Math.max(0, MathUtil.log2(keyInterval) - 3);

        int codedWidth = (sps.picWidthInMbsMinus1 + 1) << 4;
        int codedHeight = (sps.picHeightInMapUnitsMinus1 + 1) << 4;
        sps.frameCroppingFlag = codedWidth != sz.getWidth() || codedHeight != sz.getHeight();
        sps.frameCropRightOffset = (codedWidth - sz.getWidth() + 1) >> 1;
        sps.frameCropBottomOffset = (codedHeight - sz.getHeight() + 1) >> 1;

        return sps;
    }

    private void encodeSlice(SeqParameterSet sps, PictureParameterSet pps, Picture pic, ByteBuffer dup, boolean idr,
            int frameNum, SliceType sliceType, int qp) {
        if (idr && sliceType != SliceType.I) {
            idr = false;
            Logger.warn("Illegal value of idr = true when sliceType != I");
        }
        cavlc = new CAVLC[] { new CAVLC(sps, pps, 2, 2), new CAVLC(sps, pps, 1, 1), new CAVLC(sps, pps, 1, 1) };
        mbEncoderI16x16 = new MBEncoderI16x16(cavlc, leftRow, topLine);
        mbEncoderP16x16 = new MBEncoderP16x16(sps, ref, cavlc, new MotionEstimator(motionSearchRange));

        dup.putInt(0x1);
        new NALUnit(idr ? NALUnitType.IDR_SLICE : NALUnitType.NON_IDR_SLICE, 3).write(dup);
        SliceHeader sh = new SliceHeader();
        sh.sliceType = sliceType;
        if (idr)
            sh.refPicMarkingIDR = new RefPicMarkingIDR(false, false);
        sh.pps = pps;
        sh.sps = sps;
        sh.picOrderCntLsb = (frameNum << 1) % maxPOC;
        sh.frameNum = frameNum % maxFrameNumber;
        sh.sliceQpDelta = qp - (pps.picInitQpMinus26 + 26);

        ByteBuffer buf = ByteBuffer.allocate(pic.getWidth() * pic.getHeight());
        BitWriter sliceData = new BitWriter(buf);
        SliceHeaderWriter.write(sh, idr, 2, sliceData);

        for (int mbY = 0, mbAddr = 0; mbY < sps.picHeightInMapUnitsMinus1 + 1; mbY++) {
            for (int mbX = 0; mbX < sps.picWidthInMbsMinus1 + 1; mbX++, mbAddr++) {
                if (sliceType == SliceType.P) {
                    CAVLCWriter.writeUE(sliceData, 0); // number of skipped mbs
                }

                MBType mbType = selectMBType(sliceType);

                if (mbType == MBType.I_16x16) {
                    // I16x16 carries part of layout information in the
                    // macroblock type
                    // itself for this reason we'll have to decide it now to
                    // embed into
                    // macroblock type
                    int predMode = mbEncoderI16x16.getPredMode(pic, mbX, mbY);
                    int cbpChroma = mbEncoderI16x16.getCbpChroma(pic, mbX, mbY);
                    int cbpLuma = mbEncoderI16x16.getCbpLuma(pic, mbX, mbY);

                    int i16x16TypeOffset = (cbpLuma / 15) * 12 + cbpChroma * 4 + predMode;
                    int mbTypeOffset = sliceType == SliceType.P ? 5 : 0;

                    CAVLCWriter.writeUE(sliceData, mbTypeOffset + mbType.code() + i16x16TypeOffset);
                } else {
                    CAVLCWriter.writeUE(sliceData, mbType.code());
                }

                BitWriter candidate;
                int totalQpDelta = 0;
                int qpDelta = rc.initialQpDelta();
                do {
                    candidate = sliceData.fork();
                    totalQpDelta += qpDelta;
                    encodeMacroblock(mbType, pic, mbX, mbY, candidate, qp, totalQpDelta);
                    qpDelta = rc.accept(candidate.position() - sliceData.position());
                    if (qpDelta != 0)
                        restoreMacroblock(mbType);
                } while (qpDelta != 0);
                sliceData = candidate;
                qp += totalQpDelta;

                collectPredictors(outMB.getPixels(), mbX);
                addToReference(mbX, mbY);
            }
        }
        sliceData.write1Bit(1);
        sliceData.flush();
        buf = sliceData.getBuffer();
        buf.flip();

        escapeNAL(buf, dup);
    }

    private void encodeMacroblock(MBType mbType, Picture pic, int mbX, int mbY, BitWriter candidate, int qp, int qpDelta) {
        if (mbType == MBType.I_16x16) {
            mbEncoderI16x16.save();
            mbEncoderI16x16.encodeMacroblock(pic, mbX, mbY, candidate, outMB, mbX > 0 ? topEncoded[mbX - 1] : null,
                    mbY > 0 ? topEncoded[mbX] : null, qp + qpDelta, qpDelta);
        } else if (mbType == MBType.P_16x16) {
            mbEncoderP16x16.save();
            mbEncoderP16x16.encodeMacroblock(pic, mbX, mbY, candidate, outMB, mbX > 0 ? topEncoded[mbX - 1] : null,
                    mbY > 0 ? topEncoded[mbX] : null, qp + qpDelta, qpDelta);
        } else
            throw new RuntimeException("Macroblock of type " + mbType + " is not supported.");
    }

    private void restoreMacroblock(MBType mbType) {
        if (mbType == MBType.I_16x16) {
            mbEncoderI16x16.restore();
        } else if (mbType == MBType.P_16x16) {
            mbEncoderP16x16.restore();
        } else
            throw new RuntimeException("Macroblock of type " + mbType + " is not supported.");
    }

    private MBType selectMBType(SliceType sliceType) {
        if (sliceType == SliceType.I)
            return MBType.I_16x16;
        else if (sliceType == SliceType.P)
            return MBType.P_16x16;
        else
            throw new RuntimeException("Unsupported slice type");
    }

    private void addToReference(int mbX, int mbY) {
        if (mbY > 0)
            MBEncoderHelper.putBlkPic(picOut, topEncoded[mbX].getPixels(), mbX << 4, (mbY - 1) << 4);
        EncodedMB tmp = topEncoded[mbX];
        topEncoded[mbX] = outMB;
        outMB = tmp;
    }

    private void putLastMBLine() {
        int mbWidth = sps.picWidthInMbsMinus1 + 1;
        int mbHeight = sps.picHeightInMapUnitsMinus1 + 1;
        for (int mbX = 0; mbX < mbWidth; mbX++)
            MBEncoderHelper.putBlkPic(picOut, topEncoded[mbX].getPixels(), mbX << 4, (mbHeight - 1) << 4);
    }

    private void collectPredictors(Picture outMB, int mbX) {
        arraycopy(outMB.getPlaneData(0), 240, topLine[0], mbX << 4, 16);
        arraycopy(outMB.getPlaneData(1), 56, topLine[1], mbX << 3, 8);
        arraycopy(outMB.getPlaneData(2), 56, topLine[2], mbX << 3, 8);

        copyCol(outMB.getPlaneData(0), 15, 16, leftRow[0]);
        copyCol(outMB.getPlaneData(1), 7, 8, leftRow[1]);
        copyCol(outMB.getPlaneData(2), 7, 8, leftRow[2]);
    }

    private void copyCol(byte[] planeData, int off, int stride, byte[] out) {
        for (int i = 0; i < out.length; i++) {
            out[i] = planeData[off];
            off += stride;
        }
    }

    @Override
    public ColorSpace[] getSupportedColorSpaces() {
        return new ColorSpace[] { ColorSpace.YUV420J };
    }

    @Override
    public int estimateBufferSize(Picture frame) {
        return Math.max(1 << 16, frame.getWidth() * frame.getHeight() / 2);
    }
}


/* ----------------------------------------------------------------------------
 * 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.*;
/**
 * Methods that implement this class can be configured

 * using setProperty and getProperty methods, and expose

 * IProperty meta data about their properties.

 *

 * You can use Configuration#configure(java.util.Properties, Configurable)

 * to easily configure these objects from Java properties or

 * from a FFmpeg preset file.

 *

 *

 * @author aclarke
 */
public class Configurable 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 Configurable(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.Configurable_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected Configurable(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.Configurable_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(Configurable 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 Configurable object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public Configurable copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new Configurable(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 Configurable)
      equal = (((Configurable)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

/**
 * Returns the total number of settable properties on this object

 *

 * @return total number of options (not including constant definitions)
 */
  public int getNumProperties() {
    return VideoJNI.Configurable_getNumProperties(swigCPtr, this);
  }

/**
 * Returns the name of the numbered property.

 *

 * @param propertyNo The property number in the options list.

 *

 * @return a Property value for this properties meta-data
 */
  public Property getPropertyMetaData(int propertyNo) {
    long cPtr = VideoJNI.Configurable_getPropertyMetaData__SWIG_0(swigCPtr, this, propertyNo);
    return (cPtr == 0) ? null : new Property(cPtr, false);
  }

/**
 * Returns the name of the named property.

 *

 * @param name  The property name.

 *

 * @return a Property value for this properties meta-data

 * @throws PropertyNotFoundException if this property is not found.
 */
  public Property getPropertyMetaData(String name) {
    long cPtr = VideoJNI.Configurable_getPropertyMetaData__SWIG_1(swigCPtr, this, name);
    return (cPtr == 0) ? null : new Property(cPtr, false);
  }

/**
 * Sets a property on this Object.

 *

 * All AVOptions supported by the underlying AVClass are supported.

 *

 * @param name The property name.  For example "b" for bit-rate.

 * @param value The value of the property.

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public void setProperty(String name, String value) {
    VideoJNI.Configurable_setProperty__SWIG_0(swigCPtr, this, name, value);
  }

/**
 * Looks up the property 'name' and sets the

 * value of the property to 'value'.

 *

 * @param name name of option

 * @param value Value of option

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public void setProperty(String name, double value) {
    VideoJNI.Configurable_setProperty__SWIG_1(swigCPtr, this, name, value);
  }

/**
 * Looks up the property 'name' and sets the

 * value of the property to 'value'.

 *

 * @param name name of option

 * @param value Value of option

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public void setProperty(String name, long value) {
    VideoJNI.Configurable_setProperty__SWIG_2(swigCPtr, this, name, value);
  }

/**
 * Looks up the property 'name' and sets the

 * value of the property to 'value'.

 *

 * @param name name of option

 * @param value Value of option

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public void setProperty(String name, boolean value) {
    VideoJNI.Configurable_setProperty__SWIG_3(swigCPtr, this, name, value);
  }

/**
 * Looks up the property 'name' and sets the

 * value of the property to 'value'.

 *

 * @param name name of option

 * @param value Value of option

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public void setProperty(String name, Rational value) {
    VideoJNI.Configurable_setProperty__SWIG_4(swigCPtr, this, name, Rational.getCPtr(value), value);
  }

/**
 * Gets a property on this Object.

 *

 * @param name property name

 *

 * @return a string copy of the option value.

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public String getPropertyAsString(String name) {
    return VideoJNI.Configurable_getPropertyAsString(swigCPtr, this, name);
  }

/**
 * Gets the value of this property, and returns as a double;

 *

 * @param name name of option

 *

 * @return double value of property.

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public double getPropertyAsDouble(String name) {
    return VideoJNI.Configurable_getPropertyAsDouble(swigCPtr, this, name);
  }

/**
 * Gets the value of this property, and returns as an long;

 *

 * @param name name of option

 *

 * @return long value of property.

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public long getPropertyAsLong(String name) {
    return VideoJNI.Configurable_getPropertyAsLong(swigCPtr, this, name);
  }

/**
 * Gets the value of this property, and returns as an int;

 *

 * @param name name of option

 *

 * @return int value of property.

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public int getPropertyAsInt(String name) {
    return VideoJNI.Configurable_getPropertyAsInt(swigCPtr, this, name);
  }

/**
 * Gets the value of this property, and returns as an Rational;

 *

 * @param name name of option

 *

 * @return long value of property.

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public Rational getPropertyAsRational(String name) {
    long cPtr = VideoJNI.Configurable_getPropertyAsRational(swigCPtr, this, name);
    return (cPtr == 0) ? null : new Rational(cPtr, false);
  }

/**
 * Gets the value of this property, and returns as a boolean

 *

 * @param name name of option

 *

 * @return boolean value of property.

 *

 * @throws PropertyNotFoundException if this property is not found.
 */
  public boolean getPropertyAsBoolean(String name) {
    return VideoJNI.Configurable_getPropertyAsBoolean(swigCPtr, this, name);
  }

/**
 * Sets all properties in valuesToSet on this Configurable object.

 *

 * @param valuesToSet The set of key-value pairs to try to set

 * @param valuesNotFound If non null will contain all key-values pairs in valuesToSet

 *                       that were not found in context.
 */
  public void setProperty(KeyValueBag valuesToSet, KeyValueBag valuesNotFound) {
    VideoJNI.Configurable_setProperty__SWIG_5(swigCPtr, this, KeyValueBag.getCPtr(valuesToSet), valuesToSet, KeyValueBag.getCPtr(valuesNotFound), valuesNotFound);
  }

}


/* ----------------------------------------------------------------------------
 * 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.*;
/**
 * Parent class for objects that describe different container formats

 * for media (e.g. MP4 vs. FLV).
 */
public class ContainerFormat 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 ContainerFormat(long cPtr, boolean cMemoryOwn) {
    super(VideoJNI.ContainerFormat_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  /**
   * Internal Only.
   */
  protected ContainerFormat(long cPtr, boolean cMemoryOwn,
      java.util.concurrent.atomic.AtomicLong ref)
  {
    super(VideoJNI.ContainerFormat_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(ContainerFormat 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 ContainerFormat object that is actually referring to the
   * exact same underlying native object.
   *
   * @return the new Java object.
   */
  @Override
  public ContainerFormat copyReference() {
    if (swigCPtr == 0)
      return null;
    else
      return new ContainerFormat(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 ContainerFormat)
      equal = (((ContainerFormat)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 format 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("extensions:"+getExtensions()+";");
     result.append("flags:"+getFlags()+";");
     result.append("]");
     return result.toString();
   }
   /**
    * Returns a list of all codecs supported for this Object.
    *
    *

    *
    * If this object hasn't been set up for output, then
    * we return null.
    *
    *

    *
    * @return A list of supported codecs, in decreasing preferred order.
    */
   public java.util.Collection<Codec.ID> getSupportedCodecs()
   {
     final java.util.List<Codec.ID> retval =
       new java.util.LinkedList<Codec.ID>();
     final java.util.Set<Codec.ID> uniqueSet =
       new java.util.HashSet<Codec.ID>();

     int numCodecs = getNumSupportedCodecs();
     for(int i = 0; i < numCodecs; i++)
     {
       Codec.ID id = getSupportedCodecId(i);
       // remove duplicate IDs
       if (id != Codec.ID.CODEC_ID_NONE && !uniqueSet.contains(id))
         retval.add(id);
       uniqueSet.add(id);
     }
     return retval;
   }

   /**
    * Returns a list of all codec tags supported for this container.
    *
    *

    *
    * If this object hasn't been set up for output, then
    * we return null.
    *
    *

    *
    * @return A list of supported codec tags, in decreasing preferred order.
    */
   public java.util.Collection<Long> getSupportedTags()
   {
     final java.util.List<Long> retval =
       new java.util.LinkedList<Long>();
     final java.util.Set<Long> uniqueSet =
       new java.util.HashSet<Long>();

     int numCodecs = getNumSupportedCodecs();
     for(int i = 0; i < numCodecs; i++)
     {
       long tag = getSupportedCodecTag(i);
       Codec.ID id = getSupportedCodecId(i);
       // remove duplicate tags
       if (id != Codec.ID.CODEC_ID_NONE && !uniqueSet.contains(tag))
         retval.add(tag);
       uniqueSet.add(tag);
     }
     return retval;
   }

/**
 * Name for format.
 */
  public String getName() {
    return VideoJNI.ContainerFormat_getName(swigCPtr, this);
  }

/**
 * Descriptive name for the format, meant to be more human-readable

 * than name.
 */
  public String getLongName() {
    return VideoJNI.ContainerFormat_getLongName(swigCPtr, this);
  }

/**
 * A comma-separated list of supported filename extensions
 */
  public String getExtensions() {
    return VideoJNI.ContainerFormat_getExtensions(swigCPtr, this);
  }

/**
 * Flags that tell you what capabilities this format supports.

 *

 * @return a bitmask of Flags
 */
  public int getFlags() {
    return VideoJNI.ContainerFormat_getFlags(swigCPtr, this);
  }

/**
 * Find out if the given Flag is set for this ContainerFormat.
 */
  public boolean getFlag(ContainerFormat.Flag flag) {
    return VideoJNI.ContainerFormat_getFlag(swigCPtr, this, flag.swigValue());
  }

/**
 * Get total number of different codecs this container can output.
 */
  protected int getNumSupportedCodecs() {
    return VideoJNI.ContainerFormat_getNumSupportedCodecs(swigCPtr, this);
  }

/**
 * Get the Codec.ID for the n'th codec supported by this container.

 *

 * @param n The n'th codec supported by this codec. Lower n are higher priority.

 *   n must be < #getNumSupportedCodecs()

 * @return the Codec.ID at the n'th slot, or Codec.ID.CODEC_ID_NONE if none.
 */
  protected Codec.ID getSupportedCodecId(int n) {
    return Codec.ID.swigToEnum(VideoJNI.ContainerFormat_getSupportedCodecId(swigCPtr, this, n));
  }

/**
 * Get the 32-bit Codec Tag for the n'th codec supported by this container.

 *

 * @param n The n'th codec supported by this codec. Lower n are higher priority.

 *   n must be < #getNumSupportedCodecs()

 * @return the codec tag at the n'th slot, or 0 if none.
 */
  protected long getSupportedCodecTag(int n) {
    return VideoJNI.ContainerFormat_getSupportedCodecTag(swigCPtr, this, n);
  }

  /**
   * A series of flags that different ContainerFormats and their subclasses

   * can support.
   */
  public enum Flag {
    INVALID_FLAG(VideoJNI.ContainerFormat_INVALID_FLAG_get()),
  /**
   * This format does not use an on-disk file (e.g. a network format)
   */
    NO_FILE(VideoJNI.ContainerFormat_NO_FILE_get()),
  /**
   * Needs a percent-d in filename.
   */
    NEED_NUMBER(VideoJNI.ContainerFormat_NEED_NUMBER_get()),
  /**
   * Show format stream IDs numbers.
   */
    SHOW_IDS(VideoJNI.ContainerFormat_SHOW_IDS_get()),
  /**
   * Format wants AVPicture structure for raw picture data.
   */
    RAW_PICTURE(VideoJNI.ContainerFormat_RAW_PICTURE_get()),
  /**
   * Format wants global header.
   */
    GLOBAL_HEADER(VideoJNI.ContainerFormat_GLOBAL_HEADER_get()),
  /**
   * Format does not need / have any timestamps.
   */
    NO_TIMESTAMPS(VideoJNI.ContainerFormat_NO_TIMESTAMPS_get()),
  /**
   * Use generic index building code.
   */
    GENERIC_INDEX(VideoJNI.ContainerFormat_GENERIC_INDEX_get()),
  /**
   * Format allows timestamp discontinuities. Note, muxers always require valid (monotone) timestamps
   */
    TIMESTAMP_DISCONTINUITIES(VideoJNI.ContainerFormat_TIMESTAMP_DISCONTINUITIES_get()),
  /**
   * Format allows variable fps.
   */
    VARIABLE_FPS(VideoJNI.ContainerFormat_VARIABLE_FPS_get()),
  /**
   * Format does not need width/height
   */
    NO_DIMENSIONS(VideoJNI.ContainerFormat_NO_DIMENSIONS_get()),
  /**
   * Format does not require any streams
   */
    NO_STREAMS(VideoJNI.ContainerFormat_NO_STREAMS_get()),
  /**
   * Format does not allow to fallback to binary search via read_timestamp
   */
    NO_BINARY_SEARCH(VideoJNI.ContainerFormat_NO_BINARY_SEARCH_get()),
  /**
   * Format does not allow to fallback to generic search
   */
    NO_GENERIC_SEARCH(VideoJNI.ContainerFormat_NO_GENERIC_SEARCH_get()),
  /**
   * Format does not allow seeking by bytes
   */
    NO_BYTE_SEEKING(VideoJNI.ContainerFormat_NO_BYTE_SEEKING_get()),
  /**
   * Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function.
   */
    ALLOW_FLUSH(VideoJNI.ContainerFormat_ALLOW_FLUSH_get()),
  /**
   * Format does not require strictly increasing timestamps, but they must still be monotonic
   */
    NONSTRICT_TIMESTAMPS(VideoJNI.ContainerFormat_NONSTRICT_TIMESTAMPS_get()),
  /**
   * Seeking is based on PTS
   */
    SEEK_TO_PTS(VideoJNI.ContainerFormat_SEEK_TO_PTS_get()),
  ;

    public final int swigValue() {
      return swigValue;
    }

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

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

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

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

    private final int swigValue;

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

}