package org.jcodec.player;

import static org.jcodec.common.SoundUtil.toJavax;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.jcodec.common.AudioFormat;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.model.AudioFrame;
import org.jcodec.common.model.Frame;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.RationalLarge;
import org.jcodec.common.model.Size;
import org.jcodec.player.Player.Listener;
import org.jcodec.player.filters.AudioOut;
import org.jcodec.player.filters.MediaInfo.AudioInfo;
import org.jcodec.player.filters.MediaInfo.VideoInfo;
import org.jcodec.player.filters.VideoOutput;
import org.jcodec.player.filters.VideoSource;
import org.jcodec.player.filters.audio.AudioSource;
import org.jcodec.scale.ColorUtil;

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

    private VideoSource videoSource;
    private AudioSource audioSource;
    private VideoOutput vo;
    private AudioOut ao;
    private VideoInfo mi;
    private AudioInfo ai;
    private Picture dst;

    private RationalLarge pts;
    // private int frameNo;
    private byte[][] target;
    private List<Player.Listener> listeners = new ArrayList<Player.Listener>();
    private Timer timer = new Timer();

    private static final ExecutorService executor = Executors.newSingleThreadExecutor();
    private AudioFormat af;

    public Stepper(VideoSource videoSource, AudioSource audioSource, VideoOutput vo, AudioOut ao) throws IOException {
        this.videoSource = videoSource;
        this.vo = vo;
        this.ao = ao;
        this.audioSource = audioSource;
        this.mi = videoSource.getMediaInfo();

        ai = this.audioSource.getAudioInfo();
        af = ai.getFormat();
        ao.open(toJavax(af), af.getFrameSize() * (int) af.getFrameRate());
    }

    private byte[][] createTarget() {
        Size dim = mi.getDim();
        int sz = 2 * dim.getWidth() * dim.getHeight();
        return new byte[][] { new byte[sz], new byte[sz], new byte[sz] };
    }

    private void nextInt() throws IOException {
        // frameNo++;
        // curInt();
        // }

        // private void curInt() throws IOException {
        if (target == null)
            target = createTarget();
        // System.out.println(frameNo);
        // videoSource.gotoFrame(frameNo);
        Frame decode = videoSource.decode(target);
        RationalLarge pts = decode.getPts();
        if (audioSource.drySeek(pts)) {
            audioSource.seek(pts);
            playSound(150);
        }
        show(decode);
    }

    // private void prevInt() throws IOException {
    // frameNo--;
    // curInt();
    // }

    private void show(Frame frame) {
        pts = frame.getPts();
        fireTimeEvent(frame);
        Picture src = frame.getPic();
        if (src.getColor() != vo.getColorSpace()) {
            if (dst == null || dst.getWidth() != src.getWidth() || dst.getHeight() != src.getHeight())
                dst = Picture.create(src.getWidth(), src.getHeight(), vo.getColorSpace());

            ColorUtil.getTransform(src.getColor(), vo.getColorSpace()).transform(src, dst);

            vo.show(dst, frame.getPixelAspect());
        } else {
            vo.show(src, frame.getPixelAspect());
        }
    }

    private void fireTimeEvent(final Frame frame) {
        timer.schedule(new TimerTask() {
            public void run() {
                for (final Listener listener : listeners) {
                    try {
                        listener.timeChanged(frame.getPts(), frame.getFrameNo(), frame.getTapeTimecode());
                    } catch (Throwable t) {
                        t.printStackTrace();
                    }
                }
            }
        }, 0);
    }

    private void playSound(int ms) throws IOException {
        ByteBuffer sound = ByteBuffer.allocate(((int) (ms * af.getFrameRate() / 1000) * af.getFrameSize()));
        ByteBuffer buf = ByteBuffer.allocate(48000 * af.getFrameSize());
        while (sound.remaining() > 0) {
            AudioFrame frame = audioSource.getFrame(buf);
            if (frame == null)
                break;
            ByteBuffer data = frame.getData();
            NIOUtils.write(sound, NIOUtils.read(data, Math.min(data.remaining(), sound.remaining())));
        }
        sound.flip();

        ao.flush();
        ao.resume();
        ao.write(sound);
        ao.drain();
        ao.pause();
    }

    public void next() {
        executor.submit(new Runnable() {
            public void run() {
                try {
                    nextInt();
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }
        });
    }

    public void prev() {
        // executor.submit(new Runnable() {
        // public void run() {
        // try {
        // prevInt();
        // } catch (Throwable t) {
        // t.printStackTrace();
        // }
        // }
        // });
    }

    public RationalLarge getPos() {
        return pts;
    }

    public void gotoFrame(int frame) {
        videoSource.gotoFrame(frame);
        // frameNo = frame;
    }

    public void setListeners(List<Player.Listener> listeners) {
        this.listeners = listeners;
    }
}


package org.jcodec.movtool;

import static org.jcodec.common.ArrayUtil.addAllInt;
import static org.jcodec.common.ArrayUtil.addAllObj;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import org.jcodec.common.ArrayUtil;
import org.jcodec.common.model.Rational;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.ChunkOffsets64Box;
import org.jcodec.containers.mp4.boxes.ChunkOffsetsBox;
import org.jcodec.containers.mp4.boxes.DataRefBox;
import org.jcodec.containers.mp4.boxes.Edit;
import org.jcodec.containers.mp4.boxes.MediaHeaderBox;
import org.jcodec.containers.mp4.boxes.MovieBox;
import org.jcodec.containers.mp4.boxes.MovieHeaderBox;
import org.jcodec.containers.mp4.boxes.NodeBox;
import org.jcodec.containers.mp4.boxes.SampleDescriptionBox;
import org.jcodec.containers.mp4.boxes.SampleEntry;
import org.jcodec.containers.mp4.boxes.SampleSizesBox;
import org.jcodec.containers.mp4.boxes.SampleToChunkBox;
import org.jcodec.containers.mp4.boxes.SampleToChunkBox.SampleToChunkEntry;
import org.jcodec.containers.mp4.boxes.TimeToSampleBox;
import org.jcodec.containers.mp4.boxes.TimeToSampleBox.TimeToSampleEntry;
import org.jcodec.containers.mp4.boxes.TrakBox;

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

    public static class Pair<T> {
        private T a;
        private T b;

        public Pair(T a, T b) {
            this.a = a;
            this.b = b;
        }

        public T getA() {
            return a;
        }

        public T getB() {
            return b;
        }
    }

    public static Pair<List<Edit>> splitEdits(List<Edit> edits, Rational trackByMv, long tvMv) {
        long total = 0;
        List<Edit> l = new ArrayList<Edit>();
        List<Edit> r = new ArrayList<Edit>();
        ListIterator<Edit> lit = edits.listIterator();
        while (lit.hasNext()) {
            Edit edit = lit.next();
            if (total + edit.getDuration() > tvMv) {
                int leftDurMV = (int) (tvMv - total);
                int leftDurMedia = trackByMv.multiplyS(leftDurMV);

                Edit left = new Edit(leftDurMV, edit.getMediaTime(), 1.0f);
                Edit right = new Edit(edit.getDuration() - leftDurMV, leftDurMedia + edit.getMediaTime(), 1.0f);

                lit.remove();
                if (left.getDuration() > 0) {
                    lit.add(left);
                    l.add(left);
                }
                if (right.getDuration() > 0) {
                    lit.add(right);
                    r.add(right);
                }
                break;
            } else {
                l.add(edit);
            }
            total += edit.getDuration();
        }
        while (lit.hasNext()) {
            r.add(lit.next());
        }
        return new Pair<List<Edit>>(l, r);
    }

    /**
     * Splits track on the timevalue specified
     *
     * @param movie
     * @param track
     * @param tvMv
     * @return
     */
    public static Pair<List<Edit>> split(MovieBox movie, TrakBox track, long tvMv) {
        return splitEdits(track.getEdits(), new Rational(track.getTimescale(), movie.getTimescale()), tvMv);
    }

    public static void spread(MovieBox movie, TrakBox track, long tvMv, long durationMv) {
        Pair<List<Edit>> split = split(movie, track, tvMv);
        track.getEdits().add(split.getA().size(), new Edit(durationMv, -1, 1.0f));
    }

    public static void shift(MovieBox movie, TrakBox track, long tvMv) {
        track.getEdits().add(0, new Edit(tvMv, -1, 1.0f));
    }

    public static long[] getTimevalues(TrakBox track) {
        TimeToSampleBox stts = track.getStts();
        int count = 0;
        TimeToSampleEntry[] tts = stts.getEntries();
        for (int i = 0; i < tts.length; i++)
            count += tts[i].getSampleCount();
        long[] tv = new long[count + 1];
        int k = 0;
        for (int i = 0; i < tts.length; i++) {
            for (int j = 0; j < tts[i].getSampleCount(); j++, k++) {
                tv[k + 1] = tv[k] + tts[i].getSampleDuration();
            }
        }
        return tv;
    }

    private static void appendToInternal(MovieBox movie, TrakBox dest, TrakBox src) {
        int off = appendEntries(dest, src);

        appendChunkOffsets(dest, src);
        appendTimeToSamples(dest, src);
        appendSampleToChunk(dest, src, off);
        appendSampleSizes(dest, src);
    }

    private static void updateDuration(TrakBox dest, TrakBox src) {
        MediaHeaderBox mdhd1 = NodeBox.findFirstPath(dest, MediaHeaderBox.class, Box.path("mdia.mdhd"));
        MediaHeaderBox mdhd2 = NodeBox.findFirstPath(src, MediaHeaderBox.class, Box.path("mdia.mdhd"));
        mdhd1.setDuration(mdhd1.getDuration() + mdhd2.getDuration());
    }

    public static void appendTo(MovieBox movie, TrakBox dest, TrakBox src) {
        appendToInternal(movie, dest, src);
        appendEdits(dest, src, dest.getEdits().size());
        updateDuration(dest, src);
    }

    public static void insertTo(MovieBox movie, TrakBox dest, TrakBox src, long tvMv) {
        appendToInternal(movie, dest, src);
        insertEdits(movie, dest, src, tvMv);
        updateDuration(dest, src);
    }

    private static void insertEdits(MovieBox movie, TrakBox dest, TrakBox src, long tvMv) {
        Pair<List<Edit>> split = split(movie, dest, tvMv);
        appendEdits(dest, src, split.getA().size());
    }

    private static void appendEdits(TrakBox dest, TrakBox src, int ind) {
        for (Edit edit : src.getEdits()) {
            edit.shift(dest.getMediaDuration());
        }
        dest.getEdits().addAll(ind, src.getEdits());
        dest.setEdits(dest.getEdits());
    }

    private static void appendSampleSizes(TrakBox trakBox1, TrakBox trakBox2) {
        SampleSizesBox stsz1 = trakBox1.getStsz();
        SampleSizesBox stsz2 = trakBox2.getStsz();
        if (stsz1.getDefaultSize() != stsz2.getDefaultSize())
            throw new IllegalArgumentException("Can't append to track that has different default sample size");
        SampleSizesBox stszr;
        if (stsz1.getDefaultSize() > 0) {
            stszr = SampleSizesBox.createSampleSizesBox(stsz1.getDefaultSize(), stsz1.getCount() + stsz2.getCount());
        } else {
            stszr = SampleSizesBox.createSampleSizesBox2(addAllInt(stsz1.getSizes(), stsz2.getSizes()));
        }
        NodeBox.findFirstPath(trakBox1, NodeBox.class, Box.path("mdia.minf.stbl")).replace("stsz", stszr);
    }

    private static void appendSampleToChunk(TrakBox trakBox1, TrakBox trakBox2, int off) {
        SampleToChunkBox stsc1 = trakBox1.getStsc();
        SampleToChunkBox stsc2 = trakBox2.getStsc();

        SampleToChunkEntry[] orig = stsc2.getSampleToChunk();
        SampleToChunkEntry[] shifted = new SampleToChunkEntry[orig.length];
        for (int i = 0; i < orig.length; i++) {
            shifted[i] = new SampleToChunkEntry(orig[i].getFirst() + stsc1.getSampleToChunk().length,
                    orig[i].getCount(), orig[i].getEntry() + off);
        }
        NodeBox.findFirstPath(trakBox1, NodeBox.class, Box.path("mdia.minf.stbl")).replace("stsc",
                SampleToChunkBox.createSampleToChunkBox((SampleToChunkEntry[]) ArrayUtil.addAllObj(stsc1.getSampleToChunk(), shifted)));
    }

    private static int appendEntries(TrakBox trakBox1, TrakBox trakBox2) {
        appendDrefs(trakBox1, trakBox2);

        SampleEntry[] ent1 = trakBox1.getSampleEntries();
        SampleEntry[] ent2 = trakBox2.getSampleEntries();

        SampleDescriptionBox stsd = SampleDescriptionBox.createSampleDescriptionBox(ent1);
        for (int i = 0; i < ent2.length; i++) {
            SampleEntry se = ent2[i];
            se.setDrefInd((short) (se.getDrefInd() + ent1.length));
            stsd.add(se);
        }
        NodeBox.findFirstPath(trakBox1, NodeBox.class, Box.path("mdia.minf.stbl")).replace("stsd", stsd);
        return ent1.length;
    }

    private static void appendDrefs(TrakBox trakBox1, TrakBox trakBox2) {
        DataRefBox dref1 = NodeBox.findFirstPath(trakBox1, DataRefBox.class, Box.path("mdia.minf.dinf.dref"));
        DataRefBox dref2 = NodeBox.findFirstPath(trakBox2, DataRefBox.class, Box.path("mdia.minf.dinf.dref"));
        dref1.getBoxes().addAll(dref2.getBoxes());
    }

    private static void appendTimeToSamples(TrakBox trakBox1, TrakBox trakBox2) {
        TimeToSampleBox stts1 = trakBox1.getStts();
        TimeToSampleBox stts2 = trakBox2.getStts();
        TimeToSampleBox sttsNew = TimeToSampleBox.createTimeToSampleBox((TimeToSampleEntry[]) addAllObj(stts1.getEntries(),
                stts2.getEntries()));
        NodeBox.findFirstPath(trakBox1, NodeBox.class, Box.path("mdia.minf.stbl")).replace("stts", sttsNew);
    }

    private static void appendChunkOffsets(TrakBox trakBox1, TrakBox trakBox2) {
        ChunkOffsetsBox stco1 = trakBox1.getStco();
        ChunkOffsets64Box co641 = trakBox1.getCo64();
        ChunkOffsetsBox stco2 = trakBox2.getStco();
        ChunkOffsets64Box co642 = trakBox2.getCo64();

        long[] off1 = stco1 == null ? co641.getChunkOffsets() : stco1.getChunkOffsets();
        long[] off2 = stco2 == null ? co642.getChunkOffsets() : stco2.getChunkOffsets();
        NodeBox stbl1 = NodeBox.findFirstPath(trakBox1, NodeBox.class, Box.path("mdia.minf.stbl"));
        stbl1.removeChildren(new String[]{"stco", "co64"});
        stbl1.add(co641 == null && co642 == null ? ChunkOffsetsBox.createChunkOffsetsBox(ArrayUtil.addAllLong(off1, off2)) : ChunkOffsets64Box.createChunkOffsets64Box(ArrayUtil.addAllLong(off1, off2)));
    }

    public static void forceEditList(MovieBox movie, TrakBox trakBox) {
        List<Edit> edits = trakBox.getEdits();
        if (edits == null || edits.size() == 0) {
            MovieHeaderBox mvhd = NodeBox.findFirst(movie, MovieHeaderBox.class, "mvhd");
            edits = new ArrayList<Edit>();
            trakBox.setEdits(edits);
            edits.add(new Edit((int) mvhd.getDuration(), 0, 1.0f));
            trakBox.setEdits(edits);
        }
    }

    public static void forceEditListMov(MovieBox movie) {
        TrakBox[] tracks = movie.getTracks();
        for (int i = 0; i < tracks.length; i++) {
            TrakBox trakBox = tracks[i];
            forceEditList(movie, trakBox);
        }
    }

    public static List<Edit> editsOnEdits(Rational mvByTrack, List<Edit> lower, List<Edit> higher) {
        List<Edit> result = new ArrayList<Edit>();
        List<Edit> next = new ArrayList<Edit>(lower);
        for (Edit edit : higher) {
            long startMv = mvByTrack.multiplyLong(edit.getMediaTime());
            Pair<List<Edit>> split = splitEdits(next, mvByTrack.flip(), startMv);
            Pair<List<Edit>> split2 = splitEdits(split.getB(), mvByTrack.flip(), startMv + edit.getDuration());
            result.addAll(split2.getA());
            next = split2.getB();
        }
        return result;
    }
}


package net.sourceforge.jaad.aac;
import org.jcodec.common.logging.Logger;

import net.sourceforge.jaad.aac.filterbank.FilterBank;
import net.sourceforge.jaad.aac.syntax.BitStream;
import net.sourceforge.jaad.aac.syntax.IBitStream;
import net.sourceforge.jaad.aac.syntax.PCE;
import net.sourceforge.jaad.aac.syntax.SyntacticElements;
import net.sourceforge.jaad.aac.syntax.SyntaxConstants;
import net.sourceforge.jaad.aac.transport.ADIFHeader;

/**
 * This class is part of JAAD ( jaadec.sourceforge.net ) that is distributed
 * under the Public Domain license. Code changes provided by the JCodec project
 * are distributed under FreeBSD license.
 *
 * Main AAC decoder class
 *
 * @author in-somnia
 */
public class Decoder implements SyntaxConstants {
    private final AACDecoderConfig config;
    private final SyntacticElements syntacticElements;
    private final FilterBank filterBank;
    private IBitStream _in;
    private ADIFHeader adifHeader;

    /**
     * The methods returns true, if a profile is supported by the decoder.
     *
     * @param profile
     *            an AAC profile
     * @return true if the specified profile can be decoded
     * @see Profile#isDecodingSupported()
     */
    public static boolean canDecode(Profile profile) {
        return profile.isDecodingSupported();
    }

    /**
     * Initializes the decoder with a MP4 decoder specific info.
     *
     * After this the MP4 frames can be passed to the
     * decodeFrame(byte[], SampleBuffer) method to decode them.
     *
     * @param decoderSpecificInfo
     *            a byte array containing the decoder specific info from an MP4
     *            container
     * @throws AACException
     *             if the specified profile is not supported
     */
    public Decoder(byte[] decoderSpecificInfo) throws AACException {
        config = AACDecoderConfig.parseMP4DecoderSpecificInfo(decoderSpecificInfo);
        if (config == null)
            throw new IllegalArgumentException("illegal MP4 decoder specific info");

        if (!canDecode(config.getProfile()))
            throw new AACException("unsupported profile: " + config.getProfile().getDescription());

        syntacticElements = new SyntacticElements(config);
        filterBank = new FilterBank(config.isSmallFrameUsed(), config.getChannelConfiguration().getChannelCount());

        _in = new BitStream();

        Logger.debug("profile: {0}", config.getProfile());
        Logger.debug("sf: {0}", config.getSampleFrequency().getFrequency());
        Logger.debug("channels: {0}", config.getChannelConfiguration().getDescription());
    }

    public AACDecoderConfig getConfig() {
        return config;
    }

    /**
     * Decodes one frame of AAC data in frame mode and returns the raw PCM data.
     *
     * @param frame
     *            the AAC frame
     * @param buffer
     *            a buffer to hold the decoded PCM data
     * @throws AACException
     *             if decoding fails
     */
    public void decodeFrame(byte[] frame, SampleBuffer buffer) throws AACException {
        if (frame != null)
            _in.setData(frame);
        Logger.debug("bits left " + _in.getBitsLeft());
        try {
            decode(buffer);
        } catch (AACException e) {
            if (!e.isEndOfStream())
                throw e;
            else
                Logger.warn("unexpected end of frame");
        }
    }

    private void decode(SampleBuffer buffer) throws AACException {
        if (ADIFHeader.isPresent(_in)) {
            adifHeader = ADIFHeader.readHeader(_in);
            final PCE pce = adifHeader.getFirstPCE();
            config.setProfile(pce.getProfile());
            config.setSampleFrequency(pce.getSampleFrequency());
            config.setChannelConfiguration(ChannelConfiguration.forInt(pce.getChannelCount()));
        }

        if (!canDecode(config.getProfile()))
            throw new AACException("unsupported profile: " + config.getProfile().getDescription());

        syntacticElements.startNewFrame();

        try {
            //1: bitstream parsing and noiseless coding
            syntacticElements.decode(_in);
            //2: spectral processing
            syntacticElements.process(filterBank);
            //3: send to output buffer
            syntacticElements.sendToOutput(buffer);
        } catch (Exception e) {
            buffer.setData(new byte[0], 0, 0, 0, 0);
            throw AACException.wrap(e);
        }
    }
}


package net.sourceforge.jaad.aac;

/**
 * This class is part of JAAD ( jaadec.sourceforge.net ) that is distributed
 * under the Public Domain license. Code changes provided by the JCodec project
 * are distributed under FreeBSD license.
 *
 * The SampleBuffer holds the decoded AAC frame. It contains the raw PCM data
 * and its format.
 * @author in-somnia
 */
public class SampleBuffer {

	private int sampleRate, channels, bitsPerSample;
	private double length, bitrate, encodedBitrate;
	private byte[] data;
	private boolean bigEndian;

	public SampleBuffer() {
		data = new byte[0];
		sampleRate = 0;
		channels = 0;
		bitsPerSample = 0;
		bigEndian = true;
	}

	/**
	 * Returns the buffer's PCM data.
	 * @return the audio data
	 */
	public byte[] getData() {
		return data;
	}

	/**
	 * Returns the data's sample rate.
	 * @return the sample rate
	 */
	public int getSampleRate() {
		return sampleRate;
	}

	/**
	 * Returns the number of channels stored in the data buffer.
	 * @return the number of channels
	 */
	public int getChannels() {
		return channels;
	}

	/**
	 * Returns the number of bits per sample. Usually this is 16, meaning a
	 * sample is stored in two bytes.
	 * @return the number of bits per sample
	 */
	public int getBitsPerSample() {
		return bitsPerSample;
	}

	/**
	 * Returns the length of the current frame in seconds.
	 * length = samplesPerChannel / sampleRate
	 * @return the length in seconds
	 */
	public double getLength() {
		return length;
	}

	/**
	 * Returns the bitrate of the decoded PCM data.
	 * bitrate = (samplesPerChannel * bitsPerSample) / length
	 * @return the bitrate
	 */
	public double getBitrate() {
		return bitrate;
	}

	/**
	 * Returns the AAC bitrate of the current frame.
	 * @return the AAC bitrate
	 */
	public double getEncodedBitrate() {
		return encodedBitrate;
	}

	/**
	 * Indicates the endianness for the data.
	 *
	 * @return true if the data is in big endian, false if it is in little endian
	 */
	public boolean isBigEndian() {
		return bigEndian;
	}

	/**
	 * Sets the endianness for the data.
	 *
	 * @param bigEndian if true the data will be in big endian, else in little
	 * endian
	 */
	public void setBigEndian(boolean bigEndian) {
		if(bigEndian!=this.bigEndian) {
			byte tmp;
			for(int i = 0; i<data.length; i += 2) {
				tmp = data[i];
				data[i] = data[i+1];
				data[i+1] = tmp;
			}
			this.bigEndian = bigEndian;
		}
	}

	public void setData(byte[] data, int sampleRate, int channels, int bitsPerSample, int bitsRead) {
		this.data = data;
		this.sampleRate = sampleRate;
		this.channels = channels;
		this.bitsPerSample = bitsPerSample;

		if(sampleRate==0) {
			length = 0;
			bitrate = 0;
			encodedBitrate = 0;
		}
		else {
			final int bytesPerSample = bitsPerSample/8; //usually 2
			final int samplesPerChannel = data.length/(bytesPerSample*channels); //=1024
			length = (double) samplesPerChannel/(double) sampleRate;
			bitrate = (double) (samplesPerChannel*bitsPerSample*channels)/length;
			encodedBitrate = (double) bitsRead/length;
		}
	}
}


package org.jcodec.codecs.h264.encode;

import static java.lang.Math.abs;
import static org.jcodec.common.tools.MathUtil.clip;

import org.jcodec.codecs.h264.decode.deblock.DeblockingFilter;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 *
 * Contains various deblocking filter routines for deblocking on MB bases
 *
 * @author Stan Vitvitskyy
 *
 */
public class MBDeblocker {

    static int[][] LOOKUP_IDX_P_V = new int[][] { { 3, 7, 11, 15 }, { 0, 4, 8, 12 }, { 1, 5, 9, 13 }, { 2, 6, 10, 14 } };
    static int[][] LOOKUP_IDX_Q_V = new int[][] { { 0, 4, 8, 12 }, { 1, 5, 9, 13 }, { 2, 6, 10, 14 }, { 3, 7, 11, 15 } };
    static int[][] LOOKUP_IDX_P_H = new int[][] { { 12, 13, 14, 15 }, { 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 } };
    static int[][] LOOKUP_IDX_Q_H = new int[][] { { 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 }, { 12, 13, 14, 15 } };

    // Luma Chroma
    // 4444 44
    // 4333 43
    // 4333
    // 4333
    private static int[][] BS_I = { { 4, 4, 4, 4 }, { 3, 3, 3, 3 }, { 3, 3, 3, 3 }, { 3, 3, 3, 3 } };

    /**
     * Deblocks bottom edge of topOutMB, right edge of leftOutMB and left/top
     * and inner block edges of outMB
     *
     * @param curPix
     *            Pixels of the current MB
     * @param leftPix
     *            Pixels of the leftMB
     * @param topPix
     *            Pixels of the tipMB
     *
     * @param vertStrength
     *            Border strengths for vertical edges (filtered first)
     * @param horizStrength
     *            Border strengths for the horizontal edges
     *
     * @param curQp
     *            Current MB's qp
     * @param leftQp
     *            Left MB's qp
     * @param topQp
     *            Top MB's qp
     */
    public void deblockMBGeneric(EncodedMB curMB, EncodedMB leftMB, EncodedMB topMB, int[][] vertStrength,
            int horizStrength[][]) {
        Picture curPix = curMB.getPixels();
        if (leftMB != null) {
            Picture leftPix = leftMB.getPixels();
            int avgQp = MathUtil.clip((leftMB.getQp() + curMB.getQp() + 1) >> 1, 0, 51);
            deblockBorder(vertStrength[0], avgQp, leftPix.getPlaneData(0), 3, curPix.getPlaneData(0), 0, P_POS_V,
                    Q_POS_V, false);
            deblockBorderChroma(vertStrength[0], avgQp, leftPix.getPlaneData(1), 3, curPix.getPlaneData(1), 0,
                    P_POS_V_CHR, Q_POS_V_CHR, false);
            deblockBorderChroma(vertStrength[0], avgQp, leftPix.getPlaneData(2), 3, curPix.getPlaneData(2), 0,
                    P_POS_V_CHR, Q_POS_V_CHR, false);
        }
        for (int i = 0; i < 3; i++) {
            deblockBorder(vertStrength[i + 1], curMB.getQp(), curPix.getPlaneData(0), i, curPix.getPlaneData(0), i + 1,
                    P_POS_V, Q_POS_V, false);
            deblockBorderChroma(vertStrength[i + 1], curMB.getQp(), curPix.getPlaneData(1), i, curPix.getPlaneData(1),
                    i + 1, P_POS_V_CHR, Q_POS_V_CHR, false);
            deblockBorderChroma(vertStrength[i + 1], curMB.getQp(), curPix.getPlaneData(2), i, curPix.getPlaneData(2),
                    i + 1, P_POS_V_CHR, Q_POS_V_CHR, false);
        }

        if (topMB != null) {
            Picture topPix = topMB.getPixels();
            int avgQp = MathUtil.clip((topMB.getQp() + curMB.getQp() + 1) >> 1, 0, 51);
            deblockBorder(horizStrength[0], avgQp, topPix.getPlaneData(0), 3, curPix.getPlaneData(0), 0, P_POS_H,
                    Q_POS_H, true);
            deblockBorderChroma(horizStrength[0], avgQp, topPix.getPlaneData(1), 3, curPix.getPlaneData(1), 0,
                    P_POS_H_CHR, Q_POS_H_CHR, true);
            deblockBorderChroma(horizStrength[0], avgQp, topPix.getPlaneData(2), 3, curPix.getPlaneData(2), 0,
                    P_POS_H_CHR, Q_POS_H_CHR, true);
        }
        for (int i = 0; i < 3; i++) {
            deblockBorder(horizStrength[i + 1], curMB.getQp(), curPix.getPlaneData(0), i, curPix.getPlaneData(0),
                    i + 1, P_POS_H, Q_POS_H, true);
            deblockBorderChroma(horizStrength[i + 1], curMB.getQp(), curPix.getPlaneData(1), i, curPix.getPlaneData(1),
                    i + 1, P_POS_H_CHR, Q_POS_H_CHR, true);
            deblockBorderChroma(horizStrength[i + 1], curMB.getQp(), curPix.getPlaneData(2), i, curPix.getPlaneData(2),
                    i + 1, P_POS_H_CHR, Q_POS_H_CHR, true);
        }
    }

    public void deblockMBI(EncodedMB outMB, EncodedMB leftOutMB, EncodedMB topOutMB) {
        deblockMBGeneric(outMB, leftOutMB, topOutMB, BS_I, BS_I);
    }

    /**
     * Deblocks P-macroblock
     *
     * @param cur
     *            Pixels and parameters of encoded and reconstructed current
     *            macroblock
     * @param left
     *            Pixels and parameters of encoded and reconstructed left
     *            macroblock
     * @param top
     *            Pixels and parameters of encoded and reconstructed top
     *            macroblock
     */
    public void deblockMBP(EncodedMB cur, EncodedMB left, EncodedMB top) {
        int[][] vertStrength = new int[4][4];
        int[][] horizStrength = new int[4][4];

        calcStrengthForBlocks(cur, left, vertStrength, LOOKUP_IDX_P_V, LOOKUP_IDX_Q_V);
        calcStrengthForBlocks(cur, top, horizStrength, LOOKUP_IDX_P_H, LOOKUP_IDX_Q_H);

        deblockMBGeneric(cur, left, top, vertStrength, horizStrength);
    }

    private void deblockBorder(int[] boundary, int qp, byte[] p, int pi, byte[] q, int qi, int[][] pTab, int[][] qTab,
            boolean horiz) {
        int inc1 = horiz ? 16 : 1, inc2 = inc1 * 2, inc3 = inc1 * 3;
        for (int b = 0; b < 4; b++) {
            if (boundary[b] == 4) {
                for (int i = 0, ii = b << 2; i < 4; ++i, ++ii)
                    filterBs4(qp, qp, p, q, pTab[pi][ii] - inc3, pTab[pi][ii] - inc2, pTab[pi][ii] - inc1,
                            pTab[pi][ii], qTab[qi][ii], qTab[qi][ii] + inc1, qTab[qi][ii] + inc2, qTab[qi][ii] + inc3);
            } else if (boundary[b] > 0) {
                for (int i = 0, ii = b << 2; i < 4; ++i, ++ii)
                    filterBs(boundary[b], qp, qp, p, q, pTab[pi][ii] - inc2, pTab[pi][ii] - inc1, pTab[pi][ii],
                            qTab[qi][ii], qTab[qi][ii] + inc1, qTab[qi][ii] + inc2);

            }
        }
    }

    protected void filterBs4Chr(int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p1Idx, int p0Idx,
            int q0Idx, int q1Idx) {
        _filterBs4(indexAlpha, indexBeta, pelsP, pelsQ, -1, -1, p1Idx, p0Idx, q0Idx, q1Idx, -1, -1,
                true);
    }

    protected void filterBsChr(int bs, int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p1Idx, int p0Idx,
            int q0Idx, int q1Idx) {
        _filterBs(bs, indexAlpha, indexBeta, pelsP, pelsQ, -1, p1Idx, p0Idx, q0Idx, q1Idx, -1, true);
    }

    protected void filterBs4(int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p3Idx, int p2Idx, int p1Idx,
            int p0Idx, int q0Idx, int q1Idx, int q2Idx, int q3Idx) {
        _filterBs4(indexAlpha, indexBeta, pelsP, pelsQ, p3Idx, p2Idx, p1Idx, p0Idx, q0Idx, q1Idx,
                q2Idx, q3Idx, false);
    }

    protected void filterBs(int bs, int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p2Idx, int p1Idx,
            int p0Idx, int q0Idx, int q1Idx, int q2Idx) {
        _filterBs(bs, indexAlpha, indexBeta, pelsP, pelsQ, p2Idx, p1Idx, p0Idx, q0Idx, q1Idx, q2Idx,
                false);
    }

    protected void _filterBs4(int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p3Idx, int p2Idx,
            int p1Idx, int p0Idx, int q0Idx, int q1Idx, int q2Idx, int q3Idx, boolean isChroma) {
        int p0 = pelsP[p0Idx];
        int q0 = pelsQ[q0Idx];
        int p1 = pelsP[p1Idx];
        int q1 = pelsQ[q1Idx];

        int alphaThresh = DeblockingFilter.alphaTab[indexAlpha];
        int betaThresh = DeblockingFilter.betaTab[indexBeta];

        boolean filterEnabled = abs(p0 - q0) < alphaThresh && abs(p1 - p0) < betaThresh && abs(q1 - q0) < betaThresh;

        if (!filterEnabled)
            return;

        boolean conditionP, conditionQ;

        if (isChroma) {
            conditionP = false;
            conditionQ = false;
        } else {
            int ap = abs(pelsP[p2Idx] - p0);
            int aq = abs(pelsQ[q2Idx] - q0);

            conditionP = ap < betaThresh && abs(p0 - q0) < ((alphaThresh >> 2) + 2);
            conditionQ = aq < betaThresh && abs(p0 - q0) < ((alphaThresh >> 2) + 2);

        }

        if (conditionP) {
            int p3 = pelsP[p3Idx];
            int p2 = pelsP[p2Idx];

            int p0n = (p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3;
            int p1n = (p2 + p1 + p0 + q0 + 2) >> 2;
            int p2n = (2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3;
            pelsP[p0Idx] = (byte) clip(p0n, -128, 127);
            pelsP[p1Idx] = (byte) clip(p1n, -128, 127);
            pelsP[p2Idx] = (byte) clip(p2n, -128, 127);
        } else {
            int p0n = (2 * p1 + p0 + q1 + 2) >> 2;
            pelsP[p0Idx] = (byte) clip(p0n, -128, 127);
        }

        if (conditionQ && !isChroma) {
            int q2 = pelsQ[q2Idx];
            int q3 = pelsQ[q3Idx];
            int q0n = (p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3;
            int q1n = (p0 + q0 + q1 + q2 + 2) >> 2;
            int q2n = (2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3;
            pelsQ[q0Idx] = (byte) clip(q0n, -128, 127);
            pelsQ[q1Idx] = (byte) clip(q1n, -128, 127);
            pelsQ[q2Idx] = (byte) clip(q2n, -128, 127);
        } else {
            int q0n = (2 * q1 + q0 + p1 + 2) >> 2;
            pelsQ[q0Idx] = (byte) clip(q0n, -128, 127);
        }
    }

    protected void _filterBs(int bs, int indexAlpha, int indexBeta, byte[] pelsP, byte[] pelsQ, int p2Idx, int p1Idx,
            int p0Idx, int q0Idx, int q1Idx, int q2Idx, boolean isChroma) {
        int p1 = pelsP[p1Idx];
        int p0 = pelsP[p0Idx];
        int q0 = pelsQ[q0Idx];
        int q1 = pelsQ[q1Idx];

        int alphaThresh = DeblockingFilter.alphaTab[indexAlpha];
        int betaThresh = DeblockingFilter.betaTab[indexBeta];

        boolean filterEnabled = abs(p0 - q0) < alphaThresh && abs(p1 - p0) < betaThresh && abs(q1 - q0) < betaThresh;

        if (!filterEnabled)
            return;

        int tC0 = DeblockingFilter.tcs[bs - 1][indexAlpha];

        boolean conditionP, conditionQ;
        int tC;
        if (!isChroma) {
            int ap = abs(pelsP[p2Idx] - p0);
            int aq = abs(pelsQ[q2Idx] - q0);
            tC = tC0 + ((ap < betaThresh) ? 1 : 0) + ((aq < betaThresh) ? 1 : 0);
            conditionP = ap < betaThresh;
            conditionQ = aq < betaThresh;
        } else {
            tC = tC0 + 1;
            conditionP = false;
            conditionQ = false;
        }

        int sigma = ((((q0 - p0) << 2) + (p1 - q1) + 4) >> 3);
        sigma = sigma < -tC ? -tC : (sigma > tC ? tC : sigma);

        int p0n = p0 + sigma;
        p0n = p0n < -128 ? -128 : p0n;
        int q0n = q0 - sigma;
        q0n = q0n < -128 ? -128 : q0n;

        if (conditionP) {
            int p2 = pelsP[p2Idx];

            int diff = (p2 + ((p0 + q0 + 1) >> 1) - (p1 << 1)) >> 1;
            diff = diff < -tC0 ? -tC0 : (diff > tC0 ? tC0 : diff);
            int p1n = p1 + diff;
            pelsP[p1Idx] = (byte) clip(p1n, -128, 127);
        }

        if (conditionQ) {
            int q2 = pelsQ[q2Idx];
            int diff = (q2 + ((p0 + q0 + 1) >> 1) - (q1 << 1)) >> 1;
            diff = diff < -tC0 ? -tC0 : (diff > tC0 ? tC0 : diff);
            int q1n = q1 + diff;
            pelsQ[q1Idx] = (byte) clip(q1n, -128, 127);
        }

        pelsQ[q0Idx] = (byte) clip(q0n, -128, 127);
        pelsP[p0Idx] = (byte) clip(p0n, -128, 127);
    }

    private void deblockBorderChroma(int[] boundary, int qp, byte[] p, int pi, byte[] q, int qi, int[][] pTab,
            int[][] qTab, boolean horiz) {
        int inc1 = horiz ? 8 : 1;
        for (int b = 0; b < 4; b++) {
            if (boundary[b] == 4) {
                for (int i = 0, ii = b << 1; i < 2; ++i, ++ii)
                    filterBs4Chr(qp, qp, p, q, pTab[pi][ii] - inc1, pTab[pi][ii], qTab[qi][ii], qTab[qi][ii] + inc1);
            } else if (boundary[b] > 0) {
                for (int i = 0, ii = b << 1; i < 2; ++i, ++ii)
                    filterBsChr(boundary[b], qp, qp, p, q, pTab[pi][ii] - inc1, pTab[pi][ii], qTab[qi][ii],
                            qTab[qi][ii] + inc1);
            }
        }
    }

    private static int[][] buildPPosH() {
        int[][] qPos = new int[4][16];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 16; j++) {
                qPos[i][j] = j + (i << 6) + 48;
            }
        }
        return qPos;
    }

    private static int[][] buildQPosH() {
        int[][] pPos = new int[4][16];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 16; j++) {
                pPos[i][j] = j + (i << 6);
            }
        }
        return pPos;
    }

    private static int[][] buildPPosV() {
        int[][] qPos = new int[4][16];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 16; j++) {
                qPos[i][j] = (j << 4) + (i << 2) + 3;
            }
        }
        return qPos;
    }

    private static int[][] buildQPosV() {
        int[][] pPos = new int[4][16];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 16; j++) {
                pPos[i][j] = (j << 4) + (i << 2);
            }
        }
        return pPos;
    }

    private static int[][] buildPPosHChr() {
        int[][] qPos = new int[4][8];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 8; j++) {
                qPos[i][j] = j + (i << 4) + 8;
            }
        }
        return qPos;
    }

    private static int[][] buildQPosHChr() {
        int[][] pPos = new int[4][8];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 8; j++) {
                pPos[i][j] = j + (i << 4);
            }
        }
        return pPos;
    }

    private static int[][] buildPPosVChr() {
        int[][] qPos = new int[4][8];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 8; j++) {
                qPos[i][j] = (j << 3) + (i << 1) + 1;
            }
        }
        return qPos;
    }

    private static int[][] buildQPosVChr() {
        int[][] pPos = new int[4][8];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 8; j++) {
                pPos[i][j] = (j << 3) + (i << 1);
            }
        }
        return pPos;
    }

    static void calcStrengthForBlocks(EncodedMB cur, EncodedMB other, int[][] outStrength, int[][] LOOKUP_IDX_P,
            int[][] LOOKUP_IDX_Q) {
        if (other != null) {
            for (int i = 0; i < 4; ++i) {
                outStrength[0][i] = other.getType().isIntra() ? 4 : MathUtil.max3(
                        strengthMv(other.getMx()[LOOKUP_IDX_P[0][i]], cur.getMx()[LOOKUP_IDX_Q[0][i]]),
                        strengthMv(other.getMy()[LOOKUP_IDX_P[0][i]], cur.getMy()[LOOKUP_IDX_Q[0][i]]),
                        strengthNc(other.getNc()[LOOKUP_IDX_P[0][i]], cur.getNc()[LOOKUP_IDX_Q[0][i]]));
            }
        }

        for (int i = 1; i < 4; i++) {
            for (int j = 0; j < 4; ++j) {
                outStrength[i][j] = MathUtil.max3(
                        strengthMv(cur.getMx()[LOOKUP_IDX_P[i][j]], cur.getMx()[LOOKUP_IDX_Q[i][j]]),
                        strengthMv(cur.getMy()[LOOKUP_IDX_P[i][j]], cur.getMy()[LOOKUP_IDX_Q[i][j]]),
                        strengthNc(cur.getNc()[LOOKUP_IDX_P[i][j]], cur.getNc()[LOOKUP_IDX_Q[i][j]]));
            }

        }
    }

    private static int strengthNc(int ncA, int ncB) {
        return ncA > 0 || ncB > 0 ? 2 : 0;
    }

    private static int strengthMv(int v0, int v1) {
        return abs(v0 - v1) >= 4 ? 1 : 0;
    }

    private static int[][] P_POS_V = buildPPosV();
    private static int[][] Q_POS_V = buildQPosV();
    private static int[][] P_POS_H = buildPPosH();
    private static int[][] Q_POS_H = buildQPosH();

    private static int[][] P_POS_V_CHR = buildPPosVChr();
    private static int[][] Q_POS_V_CHR = buildQPosVChr();

    private static int[][] P_POS_H_CHR = buildPPosHChr();
    private static int[][] Q_POS_H_CHR = buildQPosHChr();

}


package org.jcodec.api;

import static org.jcodec.common.Codec.H264;
import static org.jcodec.common.Format.MOV;

import java.io.File;
import java.io.IOException;

import org.jcodec.api.transcode.PixelStore;
import org.jcodec.api.transcode.PixelStore.LoanerPicture;
import org.jcodec.api.transcode.PixelStoreImpl;
import org.jcodec.api.transcode.Sink;
import org.jcodec.api.transcode.SinkImpl;
import org.jcodec.api.transcode.VideoFrameWithPacket;
import org.jcodec.common.Codec;
import org.jcodec.common.Format;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Packet;
import org.jcodec.common.model.Packet.FrameType;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Rational;
import org.jcodec.scale.ColorUtil;
import org.jcodec.scale.Transform;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 *
 * Encodes a sequence of images as a video.
 *
 * @author The JCodec project
 */
public class SequenceEncoder {

	private Transform transform;
	private int frameNo;
	private int timestamp;
	private Rational fps;
	private Sink sink;
	private PixelStore pixelStore;

	public static SequenceEncoder createSequenceEncoder(File out, int fps) throws IOException {
		return new SequenceEncoder(NIOUtils.writableChannel(out), Rational.R(fps, 1), MOV, H264, null);
	}

	public static SequenceEncoder create25Fps(File out) throws IOException {
		return new SequenceEncoder(NIOUtils.writableChannel(out), Rational.R(25, 1), MOV, H264, null);
	}

	public static SequenceEncoder create30Fps(File out) throws IOException {
		return new SequenceEncoder(NIOUtils.writableChannel(out), Rational.R(30, 1), MOV, H264, null);
	}

	public static SequenceEncoder create2997Fps(File out) throws IOException {
		return new SequenceEncoder(NIOUtils.writableChannel(out), Rational.R(30000, 1001), MOV, H264, null);
	}

	public static SequenceEncoder create24Fps(File out) throws IOException {
		return new SequenceEncoder(NIOUtils.writableChannel(out), Rational.R(24, 1), MOV, H264, null);
	}

	public static SequenceEncoder createWithFps(SeekableByteChannel out, Rational fps) throws IOException {
		return new SequenceEncoder(out, fps, MOV, H264, null);
	}

	public SequenceEncoder(SeekableByteChannel out, Rational fps, Format outputFormat, Codec outputVideoCodec,
			Codec outputAudioCodec) throws IOException {
		this.fps = fps;

		sink = SinkImpl.createWithStream(out, outputFormat, outputVideoCodec, outputAudioCodec);
		sink.init();

		if (sink.getInputColor() != null)
			transform = ColorUtil.getTransform(ColorSpace.RGB, sink.getInputColor());

		pixelStore = new PixelStoreImpl();
	}

	/**
	 * Encodes a frame into a movie.
	 *
	 * @param pic
	 * @throws IOException
	 */
	public void encodeNativeFrame(Picture pic) throws IOException {
		if (pic.getColor() != ColorSpace.RGB)
			throw new IllegalArgumentException("The input images is expected in RGB color.");

		ColorSpace sinkColor = sink.getInputColor();
		LoanerPicture toEncode;
		if (sinkColor != null) {
			toEncode = pixelStore.getPicture(pic.getWidth(), pic.getHeight(), sinkColor);
			transform.transform(pic, toEncode.getPicture());
		} else {
			toEncode = new LoanerPicture(pic, 0);
		}

		Packet pkt = Packet.createPacket(null, timestamp, fps.getNum(), fps.getDen(), frameNo, FrameType.KEY, null);
		sink.outputVideoFrame(new VideoFrameWithPacket(pkt, toEncode));

		if (sinkColor != null)
			pixelStore.putBack(toEncode);

		timestamp += fps.getDen();
		frameNo++;
	}

	public void finish() throws IOException {
		sink.finish();
	}
}


package org.jcodec.containers.mkv.util;
import java.lang.StringBuilder;
/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed under FreeBSD License
 *
 * EBML IO implementation
 *
 * @author The JCodec project
 *
 */
public class EbmlUtil {

    /**
     * Encodes unsigned integer with given length
     *
     * @param value
     *            unsigned integer to be encoded
     * @param length
     *            ebml sequence length
     * @return
     */
    public static byte[] ebmlEncodeLen(long value, int length) {
        byte[] b = new byte[length];
        for (int idx = 0; idx < length; idx++) {
            // Rightmost bytes should go to end of array to preserve big-endian notation
            b[length - idx - 1] = (byte) ((value >>> (8 * idx)) & 0xFFL);
        }

        b[0] |= 0x80 >>> (length - 1);
        return b;
    }

    /**
     * Encodes unsigned integer value according to ebml convention
     *
     * @param value
     *            unsigned integer to be encoded
     * @return
     */
    public static byte[] ebmlEncode(long value) {
        return ebmlEncodeLen(value, ebmlLength(value));
    }

    public static final byte[] lengthOptions = { 0, (byte) 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };

    /**
     * This method is used mostly during reading EBML bitstream. It asnwers the question "What is the length of an integer (signed/unsigned) encountered in the bitstream"
     *
     * @param b
     * @return
     */
    static public int computeLength(byte b) {
        if (b == 0x00)
            throw new RuntimeException("Invalid head element for ebml sequence");

        int i = 1;
        while ((b & lengthOptions[i]) == 0)
            i++;

        return i;
    }

    public static final long one = 0x7F;
    // 0x3F 0x80
    public static final long two = 0x3F80;
    // 0x1F 0xC0 0x00
    public static final long three = 0x1FC000;
    // 0x0F 0xE0 0x00 0x00
    public static final long four = 0x0FE00000;
    // 0x07 0xF0 0x00 0x00 0x00
    public static final long five = 0x07F0000000L;
    // 0x03 0xF8 0x00 0x00 0x00 0x00
    public static final long six = 0x03F800000000L;
    // 0x01 0xFC 0x00 0x00 0x00 0x00 0x00
    public static final long seven = 0x01FC0000000000L;
    // 0x00 0xFE 0x00 0x00 0x00 0x00 0x00 0x00
    public static final long eight = 0xFE000000000000L;
    public static final long[] ebmlLengthMasks = new long[] { 0, one, two, three, four, five, six, seven, eight };

    /**
     * This method is used mostly during writing EBML bitstream. It answers the following question "How many bytes should be used to encode unsigned integer value"
     *
     * @param v
     *            unsigned integer to be encoded
     * @return
     */
    public static int ebmlLength(long v) {
        if (v == 0)
            return 1;

        int length = 8;

        while (length > 0 && (v & ebmlLengthMasks[length]) == 0)
            length--;

        return length;
    }

    public static String toHexString(byte[] a) {
        StringBuilder sb = new StringBuilder();
        for (byte b : a)
            sb.append(String.format("0x%02x ", b & 0xff));
        return sb.toString();
    }

}


package org.jcodec.codecs.mpa;

import java.util.Arrays;

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

    private static final float factor36pt0 = 0.34729635533386f;
    private static final float factor36pt1 = 1.532088886238f;
    private static final float factor36pt2 = 1.8793852415718f;
    private static final float factor36pt3 = 1.732050808f;
    private static final float factor36pt4 = 1.9696155060244f;
    private static final float factor36pt5 = 1.2855752193731f;
    private static final float factor36pt6 = 0.68404028665134f;

    private static final float[] factor36 = { 0.501909918f, 0.517638090f, 0.551688959f, 0.610387294f, 0.871723397f,
            1.183100792f, 1.931851653f, 5.736856623f };

    private static final float cos075 = 0.991444861f;
    private static final float cos225 = 0.923879532f;
    private static final float cos300 = 0.866025403f;
    private static final float cos375 = 0.793353340f;
    private static final float cos450 = 0.707106781f;
    private static final float cos525 = 0.608761429f;
    private static final float cos600 = 0.500000000f;
    private static final float cos675 = 0.382683432f;
    private static final float cos825 = 0.130526192f;

    private static final float factor12pt0 = 1.931851653f;
    private static final float factor12pt1 = 0.517638090f;
    private static final float[] factor12 = { 0.504314480f, 0.541196100f, 0.630236207f, 0.821339815f, 1.306562965f,
            3.830648788f };

    private static float[] tmp = new float[16];

    //Wrong usage of Javascript keyword:in
    static void oneLong(float[] src, float[] dst) {
        for (int i = 17; i > 0; i--)
            src[i] += src[i - 1];

        for (int i = 17; i > 2; i -= 2)
            src[i] += src[i - 2];

        for (int i = 0, k = 0; i < 2; i++, k += 8) {
            float tmp0 = src[i] + src[i];
            float tmp1 = tmp0 + src[12 + i];
            float tmp2 = src[6 + i] * factor36pt3;

            tmp[k + 0] = tmp1 + src[4 + i] * factor36pt2 + src[8 + i] * factor36pt1 + src[16 + i] * factor36pt0;
            tmp[k + 1] = tmp0 + src[4 + i] - src[8 + i] - src[12 + i] - src[12 + i] - src[16 + i];
            tmp[k + 2] = tmp1 - src[4 + i] * factor36pt0 - src[8 + i] * factor36pt2 + src[16 + i] * factor36pt1;
            tmp[k + 3] = tmp1 - src[4 + i] * factor36pt1 + src[8 + i] * factor36pt0 - src[16 + i] * factor36pt2;

            tmp[k + 4] = src[2 + i] * factor36pt4 + tmp2 + src[10 + i] * factor36pt5 + src[14 + i] * factor36pt6;
            tmp[k + 5] = (src[2 + i] - src[10 + i] - src[14 + i]) * factor36pt3;
            tmp[k + 6] = src[2 + i] * factor36pt5 - tmp2 - src[10 + i] * factor36pt6 + src[14 + i] * factor36pt4;
            tmp[k + 7] = src[2 + i] * factor36pt6 - tmp2 + src[10 + i] * factor36pt4 - src[14 + i] * factor36pt5;
        }

        for (int i = 0, j = 4, k = 8, l = 12; i < 4; i++, j++, k++, l++) {
            float q1 = tmp[i];
            float q2 = tmp[k];
            tmp[i] += tmp[j];
            tmp[j] = q1 - tmp[j];
            tmp[k] = (tmp[k] + tmp[l]) * factor36[i];
            tmp[l] = (q2 - tmp[l]) * factor36[7 - i];
        }

        for (int i = 0; i < 4; i++) {
            dst[26 - i] = tmp[i] + tmp[8 + i];
            dst[8 - i] = tmp[8 + i] - tmp[i];
            dst[27 + i] = dst[26 - i];
            dst[9 + i] = -dst[8 - i];
        }

        for (int i = 0; i < 4; i++) {
            dst[21 - i] = tmp[7 - i] + tmp[15 - i];
            dst[3 - i] = tmp[15 - i] - tmp[7 - i];
            dst[32 + i] = dst[21 - i];
            dst[14 + i] = -dst[3 - i];
        }

        float tmp0 = src[0] - src[4] + src[8] - src[12] + src[16];
        float tmp1 = (src[1] - src[5] + src[9] - src[13] + src[17]) * cos450;
        dst[4] = tmp1 - tmp0;
        dst[13] = -dst[4];
        dst[31] = dst[22] = tmp0 + tmp1;
    }

    //Wrong usage of Javascript keyword:in
    static void threeShort(float[] src, float[] dst) {
        Arrays.fill(dst, 0.0f);

        for (int i = 0, outOff = 0; i < 3; i++, outOff += 6) {
            imdct12(src, dst, outOff, i);
        }
    }

    //Wrong usage of Javascript keyword:in
    private static void imdct12(float[] src, float[] dst, int outOff, int wndIdx) {

        for (int j = 15 + wndIdx, k = 12 + wndIdx; j >= 3 + wndIdx; j -= 3, k -= 3)
            src[j] += src[k];

        src[15 + wndIdx] += src[9 + wndIdx];
        src[9 + wndIdx] += src[3 + wndIdx];

        float pp2 = src[12 + wndIdx] * cos600;
        float pp1 = src[6 + wndIdx] * cos300;
        float sum = src[0 + wndIdx] + pp2;
        tmp[1] = src[wndIdx] - src[12 + wndIdx];
        tmp[0] = sum + pp1;
        tmp[2] = sum - pp1;

        pp2 = src[15 + wndIdx] * cos600;
        pp1 = src[9 + wndIdx] * cos300;
        sum = src[3 + wndIdx] + pp2;
        tmp[4] = src[3 + wndIdx] - src[15 + wndIdx];
        tmp[5] = sum + pp1;
        tmp[3] = sum - pp1;

        tmp[3] *= factor12pt0;
        tmp[4] *= cos450;
        tmp[5] *= factor12pt1;

        float t  = tmp[0];
        tmp[0]  += tmp[5];
        tmp[5]   = t - tmp[5];

        t        = tmp[1];
        tmp[1]  += tmp[4];
        tmp[4]   = t - tmp[4];

        t        = tmp[2];
        tmp[2]  += tmp[3];
        tmp[3]   = t - tmp[3];

        for (int j = 0; j < 6; j++)
            tmp[j] *= factor12[j];

        tmp[8]  = -tmp[0] * cos375;
        tmp[9]  = -tmp[0] * cos525;
        tmp[7]  = -tmp[1] * cos225;
        tmp[10] = -tmp[1] * cos675;
        tmp[6]  = -tmp[2] * cos075;
        tmp[11] = -tmp[2] * cos825;

        tmp[0]  = tmp[3];
        tmp[1]  = tmp[4]  * cos675;
        tmp[2]  = tmp[5]  * cos525;

        tmp[3]  = -tmp[5] * cos375;
        tmp[4]  = -tmp[4] * cos225;
        tmp[5]  = -tmp[0] * cos075;

        tmp[0] *= cos825;

        for (int i = 0, j = outOff + 6; i < 12; i++, j++) {
            dst[j] += tmp[i];
        }
    }
}


package net.sourceforge.jaad.aac.sbr;

import java.util.Arrays;

import net.sourceforge.jaad.aac.AACException;
import net.sourceforge.jaad.aac.SampleFrequency;
import net.sourceforge.jaad.aac.ps.PS;
import net.sourceforge.jaad.aac.syntax.IBitStream;

/**
 * This class is part of JAAD ( jaadec.sourceforge.net ) that is distributed
 * under the Public Domain license. Code changes provided by the JCodec project
 * are distributed under FreeBSD license.
 *
 * @author in-somnia
 */
public class SBR implements SBRConstants, net.sourceforge.jaad.aac.syntax.SyntaxConstants, HuffmanTables {

	private final boolean downSampledSBR;
	final SampleFrequency sample_rate;
	int maxAACLine;

	int rate;
	boolean just_seeked;
	int ret;

	boolean[] amp_res;

	int k0;
	int kx;
	int M;
	int N_master;
	int N_high;
	int N_low;
	int N_Q;
	int[] N_L;
	int[] n;

	int[] f_master;
	int[][] f_table_res;
	int[] f_table_noise;
	int[][] f_table_lim;

	int[] table_map_k_to_g;

	int[] abs_bord_lead;
	int[] abs_bord_trail;
	int[] n_rel_lead;
	int[] n_rel_trail;

	int[] L_E;
	int[] L_E_prev;
	int[] L_Q;

	int[][] t_E;
	int[][] t_Q;
	int[][] f;
	int[] f_prev;

	float[][][] G_temp_prev;
	float[][][] Q_temp_prev;
	int[] GQ_ringbuf_index;

	int[][][] E;
	int[][] E_prev;
	float[][][] E_orig;
	float[][][] E_curr;
	int[][][] Q;
	float[][][] Q_div;
	float[][][] Q_div2;
	int[][] Q_prev;

	int[] l_A;
	int[] l_A_prev;

	int[][] bs_invf_mode;
	int[][] bs_invf_mode_prev;
	float[][] bwArray;
	float[][] bwArray_prev;

	int noPatches;
	int[] patchNoSubbands;
	int[] patchStartSubband;

	int[][] bs_add_harmonic;
	int[][] bs_add_harmonic_prev;

	int[] index_noise_prev;
	int[] psi_is_prev;

	int bs_start_freq_prev;
	int bs_stop_freq_prev;
	int bs_xover_band_prev;
	int bs_freq_scale_prev;
	boolean bs_alter_scale_prev;
	int bs_noise_bands_prev;

	int[] prevEnvIsShort;

	int kx_prev;
	int bsco;
	int bsco_prev;
	int M_prev;

	boolean Reset;
	int frame;
	int header_count;

	boolean stereo;
	AnalysisFilterbank[] qmfa;
	SynthesisFilterbank[] qmfs;

	float[][][][] Xsbr;

	int numTimeSlotsRate;
	int numTimeSlots;
	int tHFGen;
	int tHFAdj;

	PS ps;
	boolean ps_used;
	boolean psResetFlag;

	/* to get it compiling */
	/* we'll see during the coding of all the tools, whether
	 these are all used or not.
	 */
	boolean bs_header_flag;
	int bs_crc_flag;
	int bs_sbr_crc_bits;
	int bs_protocol_version;
	boolean bs_amp_res;
	int bs_start_freq;
	int bs_stop_freq;
	int bs_xover_band;
	int bs_freq_scale;
	boolean bs_alter_scale;
	int bs_noise_bands;
	int bs_limiter_bands;
	int bs_limiter_gains;
	boolean bs_interpol_freq;
	boolean bs_smoothing_mode;
	int bs_samplerate_mode;
	boolean[] bs_add_harmonic_flag;
	boolean[] bs_add_harmonic_flag_prev;
	boolean bs_extended_data;
	int bs_extension_id;
	int bs_extension_data;
	boolean bs_coupling;
	int[] bs_frame_class;
	int[][] bs_rel_bord;
	int[][] bs_rel_bord_0;
	int[][] bs_rel_bord_1;
	int[] bs_pointer;
	int[] bs_abs_bord_0;
	int[] bs_abs_bord_1;
	int[] bs_num_rel_0;
	int[] bs_num_rel_1;
	int[][] bs_df_env;
	int[][] bs_df_noise;

	public SBR(boolean smallFrames, boolean stereo, SampleFrequency sample_rate, boolean downSampledSBR) {
        this.amp_res = new boolean[2];
        this.N_L = new int[4];
        this.n = new int[2];
        this.f_master = new int[64];
        this.f_table_res = new int[2][64];
        this.f_table_noise = new int[64];
        this.f_table_lim = new int[4][64];
        this.table_map_k_to_g = new int[64];
        this.abs_bord_lead = new int[2];
        this.abs_bord_trail = new int[2];
        this.n_rel_lead = new int[2];
        this.n_rel_trail = new int[2];
        this.L_E = new int[2];
        this.L_E_prev = new int[2];
        this.L_Q = new int[2];
        this.t_E = new int[2][MAX_L_E+1];
        this.t_Q = new int[2][3];
        this.f = new int[2][MAX_L_E+1];
        this.f_prev = new int[2];
        this.G_temp_prev = new float[2][5][64];
        this.Q_temp_prev = new float[2][5][64];
        this.GQ_ringbuf_index = new int[2];
        this.E = new int[2][64][MAX_L_E];
        this.E_prev = new int[2][64];
        this.E_orig = new float[2][64][MAX_L_E];
        this.E_curr = new float[2][64][MAX_L_E];
        this.Q = new int[2][64][2];
        this.Q_div = new float[2][64][2];
        this.Q_div2 = new float[2][64][2];
        this.Q_prev = new int[2][64];
        this.l_A = new int[2];
        this.l_A_prev = new int[2];
        this.bs_invf_mode = new int[2][MAX_L_E];
        this.bs_invf_mode_prev = new int[2][MAX_L_E];
        this.bwArray = new float[2][64];
        this.bwArray_prev = new float[2][64];
        this.patchNoSubbands = new int[64];
        this.patchStartSubband = new int[64];
        this.bs_add_harmonic = new int[2][64];
        this.bs_add_harmonic_prev = new int[2][64];
        this.index_noise_prev = new int[2];
        this.psi_is_prev = new int[2];
        this.prevEnvIsShort = new int[2];
        this.qmfa = new AnalysisFilterbank[2];
        this.qmfs = new SynthesisFilterbank[2];
        this.Xsbr = new float[2][MAX_NTSRHFG][64][2];
        this.bs_add_harmonic_flag = new boolean[2];
        this.bs_add_harmonic_flag_prev = new boolean[2];
        this.bs_frame_class = new int[2];
        this.bs_rel_bord = new int[2][9];
        this.bs_rel_bord_0 = new int[2][9];
        this.bs_rel_bord_1 = new int[2][9];
        this.bs_pointer = new int[2];
        this.bs_abs_bord_0 = new int[2];
        this.bs_abs_bord_1 = new int[2];
        this.bs_num_rel_0 = new int[2];
        this.bs_num_rel_1 = new int[2];
        this.bs_df_env = new int[2][9];
        this.bs_df_noise = new int[2][3];

		this.downSampledSBR = downSampledSBR;
		this.stereo = stereo;
		this.sample_rate = sample_rate;

		this.bs_freq_scale = 2;
		this.bs_alter_scale = true;
		this.bs_noise_bands = 2;
		this.bs_limiter_bands = 2;
		this.bs_limiter_gains = 2;
		this.bs_interpol_freq = true;
		this.bs_smoothing_mode = true;
		this.bs_start_freq = 5;
		this.bs_amp_res = true;
		this.bs_samplerate_mode = 1;
		this.prevEnvIsShort[0] = -1;
		this.prevEnvIsShort[1] = -1;
		this.header_count = 0;
		this.Reset = true;

		this.tHFGen = T_HFGEN;
		this.tHFAdj = T_HFADJ;

		this.bsco = 0;
		this.bsco_prev = 0;
		this.M_prev = 0;

		/* force sbr reset */
		this.bs_start_freq_prev = -1;

		if(smallFrames) {
			this.numTimeSlotsRate = RATE*NO_TIME_SLOTS_960;
			this.numTimeSlots = NO_TIME_SLOTS_960;
		}
		else {
			this.numTimeSlotsRate = RATE*NO_TIME_SLOTS;
			this.numTimeSlots = NO_TIME_SLOTS;
		}

		this.GQ_ringbuf_index[0] = 0;
		this.GQ_ringbuf_index[1] = 0;

		if(stereo) {
			/* stereo */
			int j;
			this.qmfa[0] = new AnalysisFilterbank(32);
			this.qmfa[1] = new AnalysisFilterbank(32);
			this.qmfs[0] = new SynthesisFilterbank((downSampledSBR) ? 32 : 64);
			this.qmfs[1] = new SynthesisFilterbank((downSampledSBR) ? 32 : 64);
		}
		else {
			/* mono */
			this.qmfa[0] = new AnalysisFilterbank(32);
			this.qmfs[0] = new SynthesisFilterbank((downSampledSBR) ? 32 : 64);
			this.qmfs[1] = null;
		}
	}

	void sbrReset() {
		int j;
		if(this.qmfa[0]!=null) qmfa[0].reset();
		if(this.qmfa[1]!=null) qmfa[1].reset();
		if(this.qmfs[0]!=null) qmfs[0].reset();
		if(this.qmfs[1]!=null) qmfs[1].reset();

		for(j = 0; j<5; j++) {
			if(this.G_temp_prev[0][j]!=null) Arrays.fill(G_temp_prev[0][j], 0);
			if(this.G_temp_prev[1][j]!=null) Arrays.fill(G_temp_prev[1][j], 0);
			if(this.Q_temp_prev[0][j]!=null) Arrays.fill(Q_temp_prev[0][j], 0);
			if(this.Q_temp_prev[1][j]!=null) Arrays.fill(Q_temp_prev[1][j], 0);
		}

		for(int i = 0; i<40; i++) {
			for(int k = 0; k<64; k++) {
				Xsbr[0][i][j][0] = 0;
				Xsbr[0][i][j][1] = 0;
				Xsbr[1][i][j][0] = 0;
				Xsbr[1][i][j][1] = 0;
			}
		}

		this.GQ_ringbuf_index[0] = 0;
		this.GQ_ringbuf_index[1] = 0;
		this.header_count = 0;
		this.Reset = true;

		this.L_E_prev[0] = 0;
		this.L_E_prev[1] = 0;
		this.bs_freq_scale = 2;
		this.bs_alter_scale = true;
		this.bs_noise_bands = 2;
		this.bs_limiter_bands = 2;
		this.bs_limiter_gains = 2;
		this.bs_interpol_freq = true;
		this.bs_smoothing_mode = true;
		this.bs_start_freq = 5;
		this.bs_amp_res = true;
		this.bs_samplerate_mode = 1;
		this.prevEnvIsShort[0] = -1;
		this.prevEnvIsShort[1] = -1;
		this.bsco = 0;
		this.bsco_prev = 0;
		this.M_prev = 0;
		this.bs_start_freq_prev = -1;

		this.f_prev[0] = 0;
		this.f_prev[1] = 0;
		for(j = 0; j<MAX_M; j++) {
			this.E_prev[0][j] = 0;
			this.Q_prev[0][j] = 0;
			this.E_prev[1][j] = 0;
			this.Q_prev[1][j] = 0;
			this.bs_add_harmonic_prev[0][j] = 0;
			this.bs_add_harmonic_prev[1][j] = 0;
		}
		this.bs_add_harmonic_flag_prev[0] = false;
		this.bs_add_harmonic_flag_prev[1] = false;
	}

	void sbr_reset() {

		/* if these are different from the previous frame: Reset = 1 */
		if((this.bs_start_freq!=this.bs_start_freq_prev)
			||(this.bs_stop_freq!=this.bs_stop_freq_prev)
			||(this.bs_freq_scale!=this.bs_freq_scale_prev)
			||(this.bs_alter_scale!=this.bs_alter_scale_prev)
			||(this.bs_xover_band!=this.bs_xover_band_prev)
			||(this.bs_noise_bands!=this.bs_noise_bands_prev)) {
			this.Reset = true;
		}
		else {
			this.Reset = false;
		}

		this.bs_start_freq_prev = this.bs_start_freq;
		this.bs_stop_freq_prev = this.bs_stop_freq;
		this.bs_freq_scale_prev = this.bs_freq_scale;
		this.bs_alter_scale_prev = this.bs_alter_scale;
		this.bs_xover_band_prev = this.bs_xover_band;
		this.bs_noise_bands_prev = this.bs_noise_bands;
	}

	int calc_sbr_tables(int start_freq, int stop_freq,
		int samplerate_mode, int freq_scale,
		boolean alter_scale, int xover_band) {
		int result = 0;
		int k2;

		/* calculate the Master Frequency Table */
		this.k0 = FBT.qmf_start_channel(start_freq, samplerate_mode, this.sample_rate);
		k2 = FBT.qmf_stop_channel(stop_freq, this.sample_rate, this.k0);

		/* check k0 and k2 */
		if(this.sample_rate.getFrequency()>=48000) {
			if((k2-this.k0)>32)
				result += 1;
		}
		else if(this.sample_rate.getFrequency()<=32000) {
			if((k2-this.k0)>48)
				result += 1;
		}
		else { /* (sbr.sample_rate == 44100) */

			if((k2-this.k0)>45)
				result += 1;
		}

		if(freq_scale==0) {
			result += FBT.master_frequency_table_fs0(this, this.k0, k2, alter_scale);
		}
		else {
			result += FBT.master_frequency_table(this, this.k0, k2, freq_scale, alter_scale);
		}
		result += FBT.derived_frequency_table(this, xover_band, k2);

		result = (result>0) ? 1 : 0;

		return result;
	}

	/* table 2 */
	public int decode(IBitStream ld, int cnt) throws AACException {
		int result = 0;
		int num_align_bits = 0;
		long num_sbr_bits1 = ld.getPosition();
		int num_sbr_bits2;

		int saved_start_freq, saved_samplerate_mode;
		int saved_stop_freq, saved_freq_scale;
		int saved_xover_band;
		boolean saved_alter_scale;

		int bs_extension_type = ld.readBits(4);

		if(bs_extension_type==EXT_SBR_DATA_CRC) {
			this.bs_sbr_crc_bits = ld.readBits(10);
		}

		/* save old header values, in case the new ones are corrupted */
		saved_start_freq = this.bs_start_freq;
		saved_samplerate_mode = this.bs_samplerate_mode;
		saved_stop_freq = this.bs_stop_freq;
		saved_freq_scale = this.bs_freq_scale;
		saved_alter_scale = this.bs_alter_scale;
		saved_xover_band = this.bs_xover_band;

		this.bs_header_flag = ld.readBool();

		if(this.bs_header_flag)
			sbr_header(ld);

		/* Reset? */
		sbr_reset();

		/* first frame should have a header */
		//if (!(sbr.frame == 0 && sbr.bs_header_flag == 0))
		if(this.header_count!=0) {
			if(this.Reset||(this.bs_header_flag&&this.just_seeked)) {
				int rt = calc_sbr_tables(this.bs_start_freq, this.bs_stop_freq,
					this.bs_samplerate_mode, this.bs_freq_scale,
					this.bs_alter_scale, this.bs_xover_band);

				/* if an error occured with the new header values revert to the old ones */
				if(rt>0) {
					calc_sbr_tables(saved_start_freq, saved_stop_freq,
						saved_samplerate_mode, saved_freq_scale,
						saved_alter_scale, saved_xover_band);
				}
			}

			if(result==0) {
				result = sbr_data(ld);

				/* sbr_data() returning an error means that there was an error in
				 envelope_time_border_vector().
				 In this case the old time border vector is saved and all the previous
				 data normally read after sbr_grid() is saved.
				 */
				/* to be on the safe side, calculate old sbr tables in case of error */
				if((result>0)
					&&(this.Reset||(this.bs_header_flag&&this.just_seeked))) {
					calc_sbr_tables(saved_start_freq, saved_stop_freq,
						saved_samplerate_mode, saved_freq_scale,
						saved_alter_scale, saved_xover_band);
				}

				/* we should be able to safely set result to 0 now, */
				/* but practise indicates this doesn't work well */
			}
		}
		else {
			result = 1;
		}

		num_sbr_bits2 = (int) (ld.getPosition()-num_sbr_bits1);

		/* check if we read more bits then were available for sbr */
		if(8*cnt<num_sbr_bits2) {
			throw new AACException("frame overread");
			//faad_resetbits(ld, num_sbr_bits1+8*cnt);
			//num_sbr_bits2 = 8*cnt;

			/* turn off PS for the unfortunate case that we randomly read some
			 * PS data that looks correct */
			//this.ps_used = 0;

			/* Make sure it doesn't decode SBR in this frame, or we'll get glitches */
			//return 1;
		}

		{
			/* -4 does not apply, bs_extension_type is re-read in this function */
			num_align_bits = 8*cnt /*- 4*/-num_sbr_bits2;

			while(num_align_bits>7) {
				ld.readBits(8);
				num_align_bits -= 8;
			}
			ld.readBits(num_align_bits);
		}

		return result;
	}

	/* table 3 */
	private void sbr_header(IBitStream ld) throws AACException {
		boolean bs_header_extra_1, bs_header_extra_2;

		this.header_count++;

		this.bs_amp_res = ld.readBool();

		/* bs_start_freq and bs_stop_freq must define a fequency band that does
		 not exceed 48 channels */
		this.bs_start_freq = ld.readBits(4);
		this.bs_stop_freq = ld.readBits(4);
		this.bs_xover_band = ld.readBits(3);
		ld.readBits(2); //reserved
		bs_header_extra_1 = ld.readBool();
		bs_header_extra_2 = ld.readBool();

		if(bs_header_extra_1) {
			this.bs_freq_scale = ld.readBits(2);
			this.bs_alter_scale = ld.readBool();
			this.bs_noise_bands = ld.readBits(2);
		}
		else {
			/* Default values */
			this.bs_freq_scale = 2;
			this.bs_alter_scale = true;
			this.bs_noise_bands = 2;
		}

		if(bs_header_extra_2) {
			this.bs_limiter_bands = ld.readBits(2);
			this.bs_limiter_gains = ld.readBits(2);
			this.bs_interpol_freq = ld.readBool();
			this.bs_smoothing_mode = ld.readBool();
		}
		else {
			/* Default values */
			this.bs_limiter_bands = 2;
			this.bs_limiter_gains = 2;
			this.bs_interpol_freq = true;
			this.bs_smoothing_mode = true;
		}

	}

	/* table 4 */
	private int sbr_data(IBitStream ld) throws AACException {
		int result;

		this.rate = (this.bs_samplerate_mode!=0) ? 2 : 1;

		if(stereo) {
			if((result = sbr_channel_pair_element(ld))>0)
				return result;
		}
		else {
			if((result = sbr_single_channel_element(ld))>0)
				return result;
		}

		return 0;
	}

	/* table 5 */
	private int sbr_single_channel_element(IBitStream ld) throws AACException {
		int result;

		if(ld.readBool()) {
			ld.readBits(4); //reserved
		}

		if((result = sbr_grid(ld, 0))>0)
			return result;

		sbr_dtdf(ld, 0);
		invf_mode(ld, 0);
		sbr_envelope(ld, 0);
		sbr_noise(ld, 0);

		NoiseEnvelope.dequantChannel(this, 0);

		Arrays.fill(bs_add_harmonic[0], 0, 64, 0);
		Arrays.fill(bs_add_harmonic[1], 0, 64, 0);

		this.bs_add_harmonic_flag[0] = ld.readBool();
		if(this.bs_add_harmonic_flag[0])
			sinusoidal_coding(ld, 0);

		this.bs_extended_data = ld.readBool();

		if(this.bs_extended_data) {
			int nr_bits_left;
			int ps_ext_read = 0;
			int cnt = ld.readBits(4);
			if(cnt==15) {
				cnt += ld.readBits(8);
			}

			nr_bits_left = 8*cnt;
			while(nr_bits_left>7) {
				int tmp_nr_bits = 0;

				this.bs_extension_id = ld.readBits(2);
				tmp_nr_bits += 2;

				/* allow only 1 PS extension element per extension data */
				if(this.bs_extension_id==EXTENSION_ID_PS) {
					if(ps_ext_read==0) {
						ps_ext_read = 1;
					}
					else {
						/* to be safe make it 3, will switch to "default"
						 * in sbr_extension() */
						this.bs_extension_id = 3;
					}
				}

				tmp_nr_bits += sbr_extension(ld, this.bs_extension_id, nr_bits_left);

				/* check if the data read is bigger than the number of available bits */
				if(tmp_nr_bits>nr_bits_left)
					return 1;

				nr_bits_left -= tmp_nr_bits;
			}

			/* Corrigendum */
			if(nr_bits_left>0) {
				ld.readBits(nr_bits_left);
			}
		}

		return 0;
	}

	/* table 6 */
	private int sbr_channel_pair_element(IBitStream ld) throws AACException {
		int n, result;

		if(ld.readBool()) {
			//reserved
			ld.readBits(4);
			ld.readBits(4);
		}

		this.bs_coupling = ld.readBool();

		if(this.bs_coupling) {
			if((result = sbr_grid(ld, 0))>0)
				return result;

			/* need to copy some data from left to right */
			this.bs_frame_class[1] = this.bs_frame_class[0];
			this.L_E[1] = this.L_E[0];
			this.L_Q[1] = this.L_Q[0];
			this.bs_pointer[1] = this.bs_pointer[0];

			for(n = 0; n<=this.L_E[0]; n++) {
				this.t_E[1][n] = this.t_E[0][n];
				this.f[1][n] = this.f[0][n];
			}
			for(n = 0; n<=this.L_Q[0]; n++) {
				this.t_Q[1][n] = this.t_Q[0][n];
			}

			sbr_dtdf(ld, 0);
			sbr_dtdf(ld, 1);
			invf_mode(ld, 0);

			/* more copying */
			for(n = 0; n<this.N_Q; n++) {
				this.bs_invf_mode[1][n] = this.bs_invf_mode[0][n];
			}

			sbr_envelope(ld, 0);
			sbr_noise(ld, 0);
			sbr_envelope(ld, 1);
			sbr_noise(ld, 1);

			Arrays.fill(bs_add_harmonic[0], 0, 64, 0);
			Arrays.fill(bs_add_harmonic[1], 0, 64, 0);

			this.bs_add_harmonic_flag[0] = ld.readBool();
			if(this.bs_add_harmonic_flag[0])
				sinusoidal_coding(ld, 0);

			this.bs_add_harmonic_flag[1] = ld.readBool();
			if(this.bs_add_harmonic_flag[1])
				sinusoidal_coding(ld, 1);
		}
		else {
			int[] saved_t_E = new int[6], saved_t_Q = new int[3];
			int saved_L_E = this.L_E[0];
			int saved_L_Q = this.L_Q[0];
			int saved_frame_class = this.bs_frame_class[0];

			for(n = 0; n<saved_L_E; n++) {
				saved_t_E[n] = this.t_E[0][n];
			}
			for(n = 0; n<saved_L_Q; n++) {
				saved_t_Q[n] = this.t_Q[0][n];
			}

			if((result = sbr_grid(ld, 0))>0)
				return result;
			if((result = sbr_grid(ld, 1))>0) {
				/* restore first channel data as well */
				this.bs_frame_class[0] = saved_frame_class;
				this.L_E[0] = saved_L_E;
				this.L_Q[0] = saved_L_Q;
				for(n = 0; n<6; n++) {
					this.t_E[0][n] = saved_t_E[n];
				}
				for(n = 0; n<3; n++) {
					this.t_Q[0][n] = saved_t_Q[n];
				}

				return result;
			}
			sbr_dtdf(ld, 0);
			sbr_dtdf(ld, 1);
			invf_mode(ld, 0);
			invf_mode(ld, 1);
			sbr_envelope(ld, 0);
			sbr_envelope(ld, 1);
			sbr_noise(ld, 0);
			sbr_noise(ld, 1);

			Arrays.fill(bs_add_harmonic[0], 0, 64, 0);
			Arrays.fill(bs_add_harmonic[1], 0, 64, 0);

			this.bs_add_harmonic_flag[0] = ld.readBool();
			if(this.bs_add_harmonic_flag[0])
				sinusoidal_coding(ld, 0);

			this.bs_add_harmonic_flag[1] = ld.readBool();
			if(this.bs_add_harmonic_flag[1])
				sinusoidal_coding(ld, 1);
		}
		NoiseEnvelope.dequantChannel(this, 0);
		NoiseEnvelope.dequantChannel(this, 1);

		if(this.bs_coupling)
			NoiseEnvelope.unmap(this);

		this.bs_extended_data = ld.readBool();
		if(this.bs_extended_data) {
			int nr_bits_left;
			int cnt = ld.readBits(4);
			if(cnt==15) {
				cnt += ld.readBits(8);
			}

			nr_bits_left = 8*cnt;
			while(nr_bits_left>7) {
				int tmp_nr_bits = 0;

				this.bs_extension_id = ld.readBits(2);
				tmp_nr_bits += 2;
				tmp_nr_bits += sbr_extension(ld, this.bs_extension_id, nr_bits_left);

				/* check if the data read is bigger than the number of available bits */
				if(tmp_nr_bits>nr_bits_left)
					return 1;

				nr_bits_left -= tmp_nr_bits;
			}

			/* Corrigendum */
			if(nr_bits_left>0) {
				ld.readBits(nr_bits_left);
			}
		}

		return 0;
	}

	/* integer log[2](x): input range [0,10) */
	private int sbr_log2(int val) {
		int log2tab[] = {0, 0, 1, 2, 2, 3, 3, 3, 3, 4};
		if(val<10&&val>=0)
			return log2tab[val];
		else
			return 0;
	}

	/* table 7 */
	private int sbr_grid(IBitStream ld, int ch) throws AACException {
		int i, env, rel, result;
		int bs_abs_bord, bs_abs_bord_1;
		int bs_num_env = 0;
		int saved_L_E = this.L_E[ch];
		int saved_L_Q = this.L_Q[ch];
		int saved_frame_class = this.bs_frame_class[ch];

		this.bs_frame_class[ch] = ld.readBits(2);

		switch(this.bs_frame_class[ch]) {
			case FIXFIX:
				i = ld.readBits(2);

				bs_num_env = Math.min(1<<i, 5);

				i = ld.readBit();
				for(env = 0; env<bs_num_env; env++) {
					this.f[ch][env] = i;
				}

				this.abs_bord_lead[ch] = 0;
				this.abs_bord_trail[ch] = this.numTimeSlots;
				this.n_rel_lead[ch] = bs_num_env-1;
				this.n_rel_trail[ch] = 0;
				break;

			case FIXVAR:
				bs_abs_bord = ld.readBits(2)+this.numTimeSlots;
				bs_num_env = ld.readBits(2)+1;

				for(rel = 0; rel<bs_num_env-1; rel++) {
					this.bs_rel_bord[ch][rel] = 2*ld.readBits(2)+2;
				}
				i = sbr_log2(bs_num_env+1);
				this.bs_pointer[ch] = ld.readBits(i);

				for(env = 0; env<bs_num_env; env++) {
					this.f[ch][bs_num_env-env-1] = ld.readBit();
				}

				this.abs_bord_lead[ch] = 0;
				this.abs_bord_trail[ch] = bs_abs_bord;
				this.n_rel_lead[ch] = 0;
				this.n_rel_trail[ch] = bs_num_env-1;
				break;

			case VARFIX:
				bs_abs_bord = ld.readBits(2);
				bs_num_env = ld.readBits(2)+1;

				for(rel = 0; rel<bs_num_env-1; rel++) {
					this.bs_rel_bord[ch][rel] = 2*ld.readBits(2)+2;
				}
				i = sbr_log2(bs_num_env+1);
				this.bs_pointer[ch] = ld.readBits(i);

				for(env = 0; env<bs_num_env; env++) {
					this.f[ch][env] = ld.readBit();
				}

				this.abs_bord_lead[ch] = bs_abs_bord;
				this.abs_bord_trail[ch] = this.numTimeSlots;
				this.n_rel_lead[ch] = bs_num_env-1;
				this.n_rel_trail[ch] = 0;
				break;

			case VARVAR:
				bs_abs_bord = ld.readBits(2);
				bs_abs_bord_1 = ld.readBits(2)+this.numTimeSlots;
				this.bs_num_rel_0[ch] = ld.readBits(2);
				this.bs_num_rel_1[ch] = ld.readBits(2);

				bs_num_env = Math.min(5, this.bs_num_rel_0[ch]+this.bs_num_rel_1[ch]+1);

				for(rel = 0; rel<this.bs_num_rel_0[ch]; rel++) {
					this.bs_rel_bord_0[ch][rel] = 2*ld.readBits(2)+2;
				}
				for(rel = 0; rel<this.bs_num_rel_1[ch]; rel++) {
					this.bs_rel_bord_1[ch][rel] = 2*ld.readBits(2)+2;
				}
				i = sbr_log2(this.bs_num_rel_0[ch]+this.bs_num_rel_1[ch]+2);
				this.bs_pointer[ch] = ld.readBits(i);

				for(env = 0; env<bs_num_env; env++) {
					this.f[ch][env] = ld.readBit();
				}

				this.abs_bord_lead[ch] = bs_abs_bord;
				this.abs_bord_trail[ch] = bs_abs_bord_1;
				this.n_rel_lead[ch] = this.bs_num_rel_0[ch];
				this.n_rel_trail[ch] = this.bs_num_rel_1[ch];
				break;
		}

		if(this.bs_frame_class[ch]==VARVAR)
			this.L_E[ch] = Math.min(bs_num_env, 5);
		else
			this.L_E[ch] = Math.min(bs_num_env, 4);

		if(this.L_E[ch]<=0)
			return 1;

		if(this.L_E[ch]>1)
			this.L_Q[ch] = 2;
		else
			this.L_Q[ch] = 1;

		/* TODO: this code can probably be integrated into the code above! */
		if((result = TFGrid.envelope_time_border_vector(this, ch))>0) {
			this.bs_frame_class[ch] = saved_frame_class;
			this.L_E[ch] = saved_L_E;
			this.L_Q[ch] = saved_L_Q;
			return result;
		}
		TFGrid.noise_floor_time_border_vector(this, ch);

		return 0;
	}

	/* table 8 */
	private void sbr_dtdf(IBitStream ld, int ch) throws AACException {
		int i;

		for(i = 0; i<this.L_E[ch]; i++) {
			this.bs_df_env[ch][i] = ld.readBit();
		}

		for(i = 0; i<this.L_Q[ch]; i++) {
			this.bs_df_noise[ch][i] = ld.readBit();
		}
	}

	/* table 9 */
	private void invf_mode(IBitStream ld, int ch) throws AACException {
		int n;

		for(n = 0; n<this.N_Q; n++) {
			this.bs_invf_mode[ch][n] = ld.readBits(2);
		}
	}

	private int sbr_extension(IBitStream ld, int bs_extension_id, int num_bits_left) throws AACException {
		int ret;

		switch(bs_extension_id) {
			case EXTENSION_ID_PS:
				if(ps==null) {
					this.ps = new PS(this.sample_rate, this.numTimeSlotsRate);
				}
				if(this.psResetFlag) {
					this.ps.header_read = false;
				}
				ret = ps.decode(ld);

				/* enable PS if and only if: a header has been decoded */
				if(!ps_used&&ps.header_read) {
					this.ps_used = true;
				}

				if(ps.header_read) {
					this.psResetFlag = false;
				}

				return ret;
			default:
				this.bs_extension_data = ld.readBits(6);
				return 6;
		}
	}

	/* table 12 */
	private void sinusoidal_coding(IBitStream ld, int ch) throws AACException {
		int n;

		for(n = 0; n<this.N_high; n++) {
			this.bs_add_harmonic[ch][n] = ld.readBit();
		}
	}
	/* table 10 */

	private void sbr_envelope(IBitStream ld, int ch) throws AACException {
		int env, band;
		int delta = 0;
		int[][] t_huff, f_huff;

		if((this.L_E[ch]==1)&&(this.bs_frame_class[ch]==FIXFIX))
			this.amp_res[ch] = false;
		else
			this.amp_res[ch] = this.bs_amp_res;

		if((this.bs_coupling)&&(ch==1)) {
			delta = 1;
			if(this.amp_res[ch]) {
				t_huff = T_HUFFMAN_ENV_BAL_3_0DB;
				f_huff = F_HUFFMAN_ENV_BAL_3_0DB;
			}
			else {
				t_huff = T_HUFFMAN_ENV_BAL_1_5DB;
				f_huff = F_HUFFMAN_ENV_BAL_1_5DB;
			}
		}
		else {
			delta = 0;
			if(this.amp_res[ch]) {
				t_huff = T_HUFFMAN_ENV_3_0DB;
				f_huff = F_HUFFMAN_ENV_3_0DB;
			}
			else {
				t_huff = T_HUFFMAN_ENV_1_5DB;
				f_huff = F_HUFFMAN_ENV_1_5DB;
			}
		}

		for(env = 0; env<this.L_E[ch]; env++) {
			if(this.bs_df_env[ch][env]==0) {
				if(this.bs_coupling&&(ch==1)) {
					if(this.amp_res[ch]) {
						this.E[ch][0][env] = ld.readBits(5)<<delta;
					}
					else {
						this.E[ch][0][env] = ld.readBits(6)<<delta;
					}
				}
				else {
					if(this.amp_res[ch]) {
						this.E[ch][0][env] = ld.readBits(6)<<delta;
					}
					else {
						this.E[ch][0][env] = ld.readBits(7)<<delta;
					}
				}

				for(band = 1; band<this.n[this.f[ch][env]]; band++) {
					this.E[ch][band][env] = (decodeHuffman(ld, f_huff)<<delta);
				}

			}
			else {
				for(band = 0; band<this.n[this.f[ch][env]]; band++) {
					this.E[ch][band][env] = (decodeHuffman(ld, t_huff)<<delta);
				}
			}
		}

		NoiseEnvelope.extract_envelope_data(this, ch);
	}

	/* table 11 */
	private void sbr_noise(IBitStream ld, int ch) throws AACException {
		int noise, band;
		int delta = 0;
		int[][] t_huff, f_huff;

		if(this.bs_coupling&&(ch==1)) {
			delta = 1;
			t_huff = T_HUFFMAN_NOISE_BAL_3_0DB;
			f_huff = F_HUFFMAN_ENV_BAL_3_0DB;
		}
		else {
			delta = 0;
			t_huff = T_HUFFMAN_NOISE_3_0DB;
			f_huff = F_HUFFMAN_ENV_3_0DB;
		}

		for(noise = 0; noise<this.L_Q[ch]; noise++) {
			if(this.bs_df_noise[ch][noise]==0) {
				if(this.bs_coupling&&(ch==1)) {
					this.Q[ch][0][noise] = ld.readBits(5)<<delta;
				}
				else {
					this.Q[ch][0][noise] = ld.readBits(5)<<delta;
				}
				for(band = 1; band<this.N_Q; band++) {
					this.Q[ch][band][noise] = (decodeHuffman(ld, f_huff)<<delta);
				}
			}
			else {
				for(band = 0; band<this.N_Q; band++) {
					this.Q[ch][band][noise] = (decodeHuffman(ld, t_huff)<<delta);
				}
			}
		}

		NoiseEnvelope.extract_noise_floor_data(this, ch);
	}

	private int decodeHuffman(IBitStream ld, int[][] t_huff) throws AACException {
		int bit;
		int index = 0;

		while(index>=0) {
			bit = ld.readBit();
			index = t_huff[index][bit];
		}

		return index+64;
	}

	private int sbr_save_prev_data(int ch) {
		int i;

		/* save data for next frame */
		this.kx_prev = this.kx;
		this.M_prev = this.M;
		this.bsco_prev = this.bsco;

		this.L_E_prev[ch] = this.L_E[ch];

		/* sbr.L_E[ch] can become 0 on files with bit errors */
		if(this.L_E[ch]<=0)
			return 19;

		this.f_prev[ch] = this.f[ch][this.L_E[ch]-1];
		for(i = 0; i<MAX_M; i++) {
			this.E_prev[ch][i] = this.E[ch][i][this.L_E[ch]-1];
			this.Q_prev[ch][i] = this.Q[ch][i][this.L_Q[ch]-1];
		}

		for(i = 0; i<MAX_M; i++) {
			this.bs_add_harmonic_prev[ch][i] = this.bs_add_harmonic[ch][i];
		}
		this.bs_add_harmonic_flag_prev[ch] = this.bs_add_harmonic_flag[ch];

		if(this.l_A[ch]==this.L_E[ch])
			this.prevEnvIsShort[ch] = 0;
		else
			this.prevEnvIsShort[ch] = -1;

		return 0;
	}

	private void sbr_save_matrix(int ch) {
		int i;

		for(i = 0; i<this.tHFGen; i++) {
			for(int j = 0; j<64; j++) {
				Xsbr[ch][i][j][0] = Xsbr[ch][i+numTimeSlotsRate][j][0];
				Xsbr[ch][i][j][1] = Xsbr[ch][i+numTimeSlotsRate][j][1];
			}
		}
		for(i = this.tHFGen; i<MAX_NTSRHFG; i++) {
			for(int j = 0; j<64; j++) {
				Xsbr[ch][i][j][0] = 0;
				Xsbr[ch][i][j][1] = 0;
			}
		}
	}

	private int sbr_process_channel(float[] channel_buf, float[][][] X,
		int ch, boolean dont_process) {
		int k, l;
		int ret = 0;

		this.bsco = 0;

		/* subband analysis */
		if(dont_process)
			qmfa[ch].sbr_qmf_analysis_32(this, channel_buf, this.Xsbr[ch], this.tHFGen, 32);
		else
			qmfa[ch].sbr_qmf_analysis_32(this, channel_buf, this.Xsbr[ch], this.tHFGen, this.kx);

		if(!dont_process) {
			/* insert high frequencies here */
			/* hf generation using patching */
			HFGeneration.hf_generation(this, this.Xsbr[ch], this.Xsbr[ch], ch);

			/* hf adjustment */
			ret = HFAdjustment.hf_adjustment(this, this.Xsbr[ch], ch);
			if(ret>0) {
				dont_process = true;
			}
		}

		if(this.just_seeked||dont_process) {
			for(l = 0; l<this.numTimeSlotsRate; l++) {
				for(k = 0; k<32; k++) {
					X[l][k][0] = this.Xsbr[ch][l+this.tHFAdj][k][0];
					X[l][k][1] = this.Xsbr[ch][l+this.tHFAdj][k][1];
				}
				for(k = 32; k<64; k++) {
					X[l][k][0] = 0;
					X[l][k][1] = 0;
				}
			}
		}
		else {
			for(l = 0; l<this.numTimeSlotsRate; l++) {
				int kx_band, M_band, bsco_band;

				if(l<this.t_E[ch][0]) {
					kx_band = this.kx_prev;
					M_band = this.M_prev;
					bsco_band = this.bsco_prev;
				}
				else {
					kx_band = this.kx;
					M_band = this.M;
					bsco_band = this.bsco;
				}

				for(k = 0; k<kx_band+bsco_band; k++) {
					X[l][k][0] = this.Xsbr[ch][l+this.tHFAdj][k][0];
					X[l][k][1] = this.Xsbr[ch][l+this.tHFAdj][k][1];
				}
				for(k = kx_band+bsco_band; k<kx_band+M_band; k++) {
					X[l][k][0] = this.Xsbr[ch][l+this.tHFAdj][k][0];
					X[l][k][1] = this.Xsbr[ch][l+this.tHFAdj][k][1];
				}
				for(k = Math.max(kx_band+bsco_band, kx_band+M_band); k<64; k++) {
					X[l][k][0] = 0;
					X[l][k][1] = 0;
				}
			}
		}

		return ret;
	}

	public int _process(float[] left_chan, float[] right_chan,
		boolean just_seeked) {
		boolean dont_process = false;
		int ret = 0;
		float[][][] X = new float[MAX_NTSR][64][2];

		/* case can occur due to bit errors */
		if(!stereo) return 21;

		if(this.ret!=0||(this.header_count==0)) {
			/* don't process just upsample */
			dont_process = true;

			/* Re-activate reset for next frame */
			if(this.ret!=0&&this.Reset)
				this.bs_start_freq_prev = -1;
		}

		if(just_seeked) {
			this.just_seeked = true;
		}
		else {
			this.just_seeked = false;
		}

		this.ret += sbr_process_channel(left_chan, X, 0, dont_process);
		/* subband synthesis */
		if(downSampledSBR) {
			qmfs[0].sbr_qmf_synthesis_32(this, X, left_chan);
		}
		else {
			qmfs[0].sbr_qmf_synthesis_64(this, X, left_chan);
		}

		this.ret += sbr_process_channel(right_chan, X, 1, dont_process);
		/* subband synthesis */
		if(downSampledSBR) {
			qmfs[1].sbr_qmf_synthesis_32(this, X, right_chan);
		}
		else {
			qmfs[1].sbr_qmf_synthesis_64(this, X, right_chan);
		}

		if(this.bs_header_flag)
			this.just_seeked = false;

		if(this.header_count!=0&&this.ret==0) {
			ret = sbr_save_prev_data(0);
			if(ret!=0) return ret;
			ret = sbr_save_prev_data(1);
			if(ret!=0) return ret;
		}

		sbr_save_matrix(0);
		sbr_save_matrix(1);

		this.frame++;

		return 0;
	}

	public int process(float[] channel,
		boolean just_seeked) {
		boolean dont_process = false;
		int ret = 0;
		float[][][] X = new float[MAX_NTSR][64][2];

		/* case can occur due to bit errors */
		if(stereo) return 21;

		if(this.ret!=0||(this.header_count==0)) {
			/* don't process just upsample */
			dont_process = true;

			/* Re-activate reset for next frame */
			if(this.ret!=0&&this.Reset)
				this.bs_start_freq_prev = -1;
		}

		if(just_seeked) {
			this.just_seeked = true;
		}
		else {
			this.just_seeked = false;
		}

		this.ret += sbr_process_channel(channel, X, 0, dont_process);
		/* subband synthesis */
		if(downSampledSBR) {
			qmfs[0].sbr_qmf_synthesis_32(this, X, channel);
		}
		else {
			qmfs[0].sbr_qmf_synthesis_64(this, X, channel);
		}

		if(this.bs_header_flag)
			this.just_seeked = false;

		if(this.header_count!=0&&this.ret==0) {
			ret = sbr_save_prev_data(0);
			if(ret!=0) return ret;
		}

		sbr_save_matrix(0);

		this.frame++;

		return 0;
	}

	public int processPS(float[] left_channel, float[] right_channel,
		boolean just_seeked) {
		int l, k;
		boolean dont_process = false;
		int ret = 0;
		float[][][] X_left = new float[38][64][2];
		float[][][] X_right = new float[38][64][2];

		/* case can occur due to bit errors */
		if(stereo) return 21;

		if(this.ret!=0||(this.header_count==0)) {
			/* don't process just upsample */
			dont_process = true;

			/* Re-activate reset for next frame */
			if(this.ret!=0&&this.Reset)
				this.bs_start_freq_prev = -1;
		}

		if(just_seeked) {
			this.just_seeked = true;
		}
		else {
			this.just_seeked = false;
		}

		if(this.qmfs[1]==null) {
			this.qmfs[1] = new SynthesisFilterbank((downSampledSBR) ? 32 : 64);
		}

		this.ret += sbr_process_channel(left_channel, X_left, 0, dont_process);

		/* copy some extra data for PS */
		for(l = this.numTimeSlotsRate; l<this.numTimeSlotsRate+6; l++) {
			for(k = 0; k<5; k++) {
				X_left[l][k][0] = this.Xsbr[0][this.tHFAdj+l][k][0];
				X_left[l][k][1] = this.Xsbr[0][this.tHFAdj+l][k][1];
			}
		}

		/* perform parametric stereo */
		ps.process(X_left, X_right);

		/* subband synthesis */
		if(downSampledSBR) {
			qmfs[0].sbr_qmf_synthesis_32(this, X_left, left_channel);
			qmfs[1].sbr_qmf_synthesis_32(this, X_right, right_channel);
		}
		else {
			qmfs[0].sbr_qmf_synthesis_64(this, X_left, left_channel);
			qmfs[1].sbr_qmf_synthesis_64(this, X_right, right_channel);
		}

		if(this.bs_header_flag)
			this.just_seeked = false;

		if(this.header_count!=0&&this.ret==0) {
			ret = sbr_save_prev_data(0);
			if(ret!=0) return ret;
		}

		sbr_save_matrix(0);

		this.frame++;

		return 0;
	}

	public boolean isPSUsed() {
		return ps_used;
	}
}


package org.jcodec.containers.mps;
import static org.jcodec.common.Preconditions.checkState;
import static org.jcodec.common.io.NIOUtils.getRel;

import org.jcodec.common.IntArrayList;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.containers.mps.psi.PATSection;
import org.jcodec.containers.mps.psi.PMTSection;
import org.jcodec.containers.mps.psi.PMTSection.PMTStream;
import org.jcodec.containers.mps.psi.PSISection;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 *
 * @author The JCodec project
 *
 */
public class MTSUtils {
    /**
     * Parses PAT ( Program Association Table )
     *
     * @param data
     * @deprecated Use org.jcodec.containers.mps.psi.PAT.parse method instead,
     *             this method will not work correctly for streams with multiple
     *             programs
     * @return Pid of the first PMT found in the PAT
     */
    @Deprecated
    public static int parsePAT(ByteBuffer data) {
        PATSection pat = PATSection.parsePAT(data);
        if (pat.getPrograms().size() > 0)
            return pat.getPrograms().values()[0];
        else
            return -1;
    }

    @Deprecated
    public static PMTSection parsePMT(ByteBuffer data) {
        return PMTSection.parsePMT(data);
    }

    @Deprecated
    public static PSISection parseSection(ByteBuffer data) {
        return PSISection.parsePSI(data);
    }

    private static void parseEsInfo(ByteBuffer read) {

    }

    public static PMTStream[] getProgramGuids(File src) throws IOException {
        SeekableByteChannel ch = null;
        try {
            ch = NIOUtils.readableChannel(src);
            return getProgramGuidsFromChannel(ch);
        } finally {
            NIOUtils.closeQuietly(ch);
        }
    }

    public static PMTStream[] getProgramGuidsFromChannel(SeekableByteChannel _in) throws IOException {
        PMTExtractor ex = new PMTExtractor();
        ex.readTsFile(_in);
        PMTSection pmt = ex.getPmt();
        return pmt.getStreams();
    }

    private static class PMTExtractor extends TSReader {
        public PMTExtractor() {
            super(false);
        }

        private int pmtGuid = -1;
        private PMTSection pmt;

        @Override
        public boolean onPkt(int guid, boolean payloadStart, ByteBuffer tsBuf, long filePos, boolean sectionSyntax,
                ByteBuffer fullPkt) {
            if (guid == 0) {
                pmtGuid = parsePAT(tsBuf);
            } else if (pmtGuid != -1 && guid == pmtGuid) {
                pmt = parsePMT(tsBuf);
                return false;
            }
            return true;
        }

        public PMTSection getPmt() {
            return pmt;
        }
    };

    public static abstract class TSReader {
        private static final int TS_SYNC_MARKER = 0x47;
        private static final int TS_PKT_SIZE = 188;
        // Buffer must have an integral number of MPEG TS packets
        public static final int BUFFER_SIZE = TS_PKT_SIZE << 9;
        private boolean flush;

        public TSReader(boolean flush) {
            this.flush = flush;
        }

        public void readTsFile(SeekableByteChannel ch) throws IOException {
            ch.setPosition(0);
            ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE);

            for (long pos = ch.position(); ch.read(buf) >= TS_PKT_SIZE; pos = ch.position()) {
                long posRem = pos;
                buf.flip();
                while (buf.remaining() >= TS_PKT_SIZE) {
                    ByteBuffer tsBuf = NIOUtils.read(buf, TS_PKT_SIZE);
                    ByteBuffer fullPkt = tsBuf.duplicate();
                    pos += TS_PKT_SIZE;
                    checkState(TS_SYNC_MARKER == (tsBuf.get() & 0xff));
                    int guidFlags = ((tsBuf.get() & 0xff) << 8) | (tsBuf.get() & 0xff);
                    int guid = (int) guidFlags & 0x1fff;

                    int payloadStart = (guidFlags >> 14) & 0x1;
                    int b0 = tsBuf.get() & 0xff;
                    int counter = b0 & 0xf;
                    if ((b0 & 0x20) != 0) {
                        NIOUtils.skip(tsBuf, tsBuf.get() & 0xff);
                    }
                    boolean sectionSyntax = payloadStart == 1 && (getRel(tsBuf, getRel(tsBuf, 0) + 2) & 0x80) == 0x80;
                    if (sectionSyntax) {
                        // Adaptation field
                        NIOUtils.skip(tsBuf, tsBuf.get() & 0xff);
                    }
                    if (!onPkt(guid, payloadStart == 1, tsBuf, pos - tsBuf.remaining(), sectionSyntax, fullPkt))
                        return;
                }
                if (flush) {
                    buf.flip();
                    ch.setPosition(posRem);
                    ch.write(buf);
                }
                buf.clear();
            }
        }

        protected boolean onPkt(int guid, boolean payloadStart, ByteBuffer tsBuf, long filePos, boolean sectionSyntax,
                ByteBuffer fullPkt) {
            // DO NOTHING
            return true;
        }
    }

    public static int getVideoPid(File src) throws IOException {
        for (PMTStream stream : MTSUtils.getProgramGuids(src)) {
            if (stream.getStreamType().isVideo())
                return stream.getPid();
        }

        throw new RuntimeException("No video stream");
    }

    public static int getAudioPid(File src) throws IOException {
        for (PMTStream stream : MTSUtils.getProgramGuids(src)) {
            if (stream.getStreamType().isAudio())
                return stream.getPid();
        }

        throw new RuntimeException("No audio stream");
    }

    public static int[] getMediaPidsFromChannel(SeekableByteChannel src) throws IOException {
        return filterMediaPids(MTSUtils.getProgramGuidsFromChannel(src));
    }

    public static int[] getMediaPids(File src) throws IOException {
        return filterMediaPids(MTSUtils.getProgramGuids(src));
    }

    private static int[] filterMediaPids(PMTStream[] programs) {
        IntArrayList result = IntArrayList.createIntArrayList();
        for (PMTStream stream : programs) {
            if (stream.getStreamType().isVideo() || stream.getStreamType().isAudio())
                result.add(stream.getPid());
        }

        return result.toArray();
    }
}


package org.jcodec.containers.flv;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.containers.flv.FLVTag.Type;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 *
 * FLV ( Flash Media Video ) muxer
 *
 * @author Stan Vitvitskyy
 *
 */
public class FLVWriter {
    // Write buffer, 1M
    private static final int WRITE_BUFFER_SIZE = 0x100000;

    private int startOfLastPacket = 9;
    private SeekableByteChannel out;
    private ByteBuffer writeBuf;

    public FLVWriter(SeekableByteChannel out) {
        this.out = out;
        writeBuf = ByteBuffer.allocate(WRITE_BUFFER_SIZE);
        writeHeader(writeBuf);
    }

    /**
     * Add a packet to the underlying file
     *
     * @param pkt
     * @throws IOException
     */
    public void addPacket(FLVTag pkt) throws IOException {
        if (!writePacket(writeBuf, pkt)) {
            writeBuf.flip();
            startOfLastPacket -= out.write(writeBuf);
            writeBuf.clear();
            if (!writePacket(writeBuf, pkt))
                throw new RuntimeException("Unexpected");
        }
    }

    /**
     * Finish muxing and write the remaining data
     *
     * @throws IOException
     */
    public void finish() throws IOException {
        writeBuf.flip();
        out.write(writeBuf);
    }

    private boolean writePacket(ByteBuffer writeBuf, FLVTag pkt) {
        int pktType = pkt.getType() == Type.VIDEO ? 0x9 : (pkt.getType() == Type.SCRIPT ? 0x12 : 0x8);
        int dataLen = pkt.getData().remaining();

        if (writeBuf.remaining() < 15 + dataLen)
            return false;

        writeBuf.putInt(writeBuf.position() - startOfLastPacket);
        startOfLastPacket = writeBuf.position();

        writeBuf.put((byte) pktType);
        writeBuf.putShort((short) (dataLen >> 8));
        writeBuf.put((byte) (dataLen & 0xff));

        writeBuf.putShort((short) ((pkt.getPts() >> 8) & 0xffff));
        writeBuf.put((byte) (pkt.getPts() & 0xff));
        writeBuf.put((byte) ((pkt.getPts() >> 24) & 0xff));

        writeBuf.putShort((short) 0);
        writeBuf.put((byte) 0);

        NIOUtils.write(writeBuf, pkt.getData().duplicate());

        return true;
    }

    private static void writeHeader(ByteBuffer writeBuf) {
        writeBuf.put((byte) 'F');
        writeBuf.put((byte) 'L');
        writeBuf.put((byte) 'V');
        writeBuf.put((byte) 1);
        writeBuf.put((byte) 5);
        writeBuf.putInt(9);
    }
}