org.jcodec.codecs.mjpeg.JpegParser.java Source code

Java tutorial

Introduction

Here is the source code for org.jcodec.codecs.mjpeg.JpegParser.java

Source

package org.jcodec.codecs.mjpeg;

import static org.jcodec.codecs.mjpeg.JpegConst.Type.CAC;
import static org.jcodec.codecs.mjpeg.JpegConst.Type.CDC;
import static org.jcodec.codecs.mjpeg.JpegConst.Type.YAC;
import static org.jcodec.codecs.mjpeg.JpegConst.Type.YDC;
import static org.jcodec.codecs.mjpeg.JpegUtils.zigzagDecode;

import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.CountingInputStream;
import org.jcodec.codecs.mjpeg.tools.Asserts;
import org.jcodec.codecs.wav.StringReader;
import org.jcodec.common.io.VLCBuilder;
import org.jcodec.common.tools.Debug;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 * 
 * @author Jay Codec
 * 
 */
public class JpegParser {
    public CodedImage parse(InputStream is) throws IOException {
        CountingInputStream counter = new CountingInputStream(is);
        return parse(new PushbackInputStream(counter, 2), counter);
    }

    public CodedImage parse(PushbackInputStream is, CountingInputStream counter) throws IOException {
        CodedImage image = new CodedImage();
        int curQTable = 0;
        while (true) {
            int marker = is.read();
            if (marker == -1)
                return image;
            if (marker == 0)
                continue;
            if (marker != 0xFF)
                throw new RuntimeException("@" + Long.toHexString(counter.getByteCount()) + " Marker expected: 0x"
                        + Integer.toHexString(marker));

            int b = is.read();
            Debug.trace("%s", Markers.toString(b));
            switch (b) {
            case Markers.SOF0:
                image.frame = FrameHeader.read(is);
                Debug.trace("    %s", image.frame);
                break;
            case Markers.DHT:
                int len1 = readShort(is);
                CountingInputStream cis = new CountingInputStream(is);
                while (cis.getCount() < len1 - 2) {
                    readHuffmanTable(cis, image);
                }
                break;
            case Markers.DQT:
                int len4 = readShort(is);
                CountingInputStream cis1 = new CountingInputStream(is);
                while (cis1.getCount() < len4 - 2) {
                    QuantTable quantTable = readQuantTable(cis1);
                    if (curQTable == 0)
                        image.setQuantLum(quantTable);
                    else
                        image.setQuantChrom(quantTable);
                    curQTable++;
                }
                break;
            case Markers.SOS:
                if (image.scan != null) {
                    throw new IllegalStateException("unhandled - more than one scan header");
                }
                image.scan = ScanHeader.read(is);
                Debug.trace("    %s", image.scan);
                image.setData(readData(is));
                break;
            case Markers.SOI:
                break;
            case Markers.EOI:
                return image;
            case Markers.APP0:
                // int len10 = readShort(is);
                // byte[] id = new byte[4];
                // is.read(id);
                // if (!Arrays.equals(JFIF, id))
                // throw new RuntimeException("Not a JFIF file");
                // is.skip(1);
                //
                // is.skip(2);
                // int units = is.read();
                // int dx = readShort(is);
                // int dy = readShort(is);
                // int tx = is.read();
                // int ty = is.read();
                // is.skip(tx * ty * 3);
                // break;
            case Markers.APP1:
            case Markers.APP2:
            case Markers.APP3:
            case Markers.APP4:
            case Markers.APP5:
            case Markers.APP6:
            case Markers.APP7:
            case Markers.APP8:
            case Markers.APP9:
            case Markers.APPA:
            case Markers.APPB:
            case Markers.APPC:
            case Markers.APPD:
            case Markers.APPE:
            case Markers.APPF:
                int len3 = readShort(is);
                StringReader.sureSkip(is, len3 - 2);
                break;
            case Markers.DRI:
                /*
                 * Lr: Define restart interval segment length  Specifies the
                 * length of the parameters in the DRI segment shown in Figure
                 * B.9 (see B.1.1.4).
                 */
                int lr = readShort(is);
                // Ri: Restart interval  Specifies the number of MCU in the
                // restart interval.
                int ri = readShort(is);
                Debug.trace("DRI Lr: %d Ri: %d", lr, ri);
                // A DRI marker segment with Ri equal to zero shall disable
                // restart intervals for the following scans.
                Asserts.assertEquals(0, ri);
                break;
            default: {
                throw new IllegalStateException("unhandled marker " + Markers.toString(b));
            }
            }
        }
    }

    /**
     * Decodes huffman tables
     * 
     * @param image
     * 
     * @param orig
     * @return
     * @throws IOException
     */
    private void readHuffmanTable(InputStream is, CodedImage image) throws IOException {
        VLCBuilder builder = new VLCBuilder();

        int tableNo = is.read();
        byte[] levelSizes = new byte[16];
        is.read(levelSizes);

        int levelStart = 0;
        for (int i = 0; i < 16; i++) {
            int length = levelSizes[i] & 0xff;
            for (int c = 0; c < length; c++) {
                int val = is.read();
                int code = levelStart++;
                builder.set(code, i + 1, val);
            }
            levelStart <<= 1;
        }

        if (tableNo == YDC.getValue()) {
            image.setYdc(builder.getVLC());
        } else if (tableNo == CDC.getValue()) {
            image.setCdc(builder.getVLC());
        } else if (tableNo == YAC.getValue()) {
            image.setYac(builder.getVLC());
        } else if (tableNo == CAC.getValue()) {
            image.setCac(builder.getVLC());
        } else {
            throw new RuntimeException("Unsupported huffman table index: " + tableNo);
        }
    }

    private QuantTable readQuantTable(InputStream is) throws IOException {
        int ind = is.read();

        int[] result = new int[64];
        for (int i = 0; i < 64; i++) {
            result[i] = is.read();
        }
        return new QuantTable(ind, zigzagDecode(result));
    }

    private byte[] readData(PushbackInputStream is) throws IOException {
        return IOUtils.toByteArray(new ByteStuffingInputStream(is));
    }

    private int readShort(InputStream is) throws IOException {
        int b1 = is.read();
        int b2 = is.read();

        return (b1 << 8) + b2;
    }

}