www in docker support

This commit is contained in:
MaddoScientisto 2026-04-22 18:41:37 +02:00
commit c227fce036
2145 changed files with 399596 additions and 58 deletions

View file

@ -0,0 +1,64 @@
package org.jcodec.codecs.mjpeg;
import java.nio.ByteBuffer;
public class FrameHeader {
int headerLength;
int bitsPerSample;
int height;
int width;
int nComp;
Component[] components;
public static class Component {
int index;
int subH;
int subV;
int quantTable;
}
public int getHmax() {
int max = 0;
for (int i = 0; i < this.components.length; i++) {
Component c = this.components[i];
max = Math.max(max, c.subH);
}
return max;
}
public int getVmax() {
int max = 0;
for (int i = 0; i < this.components.length; i++) {
Component c = this.components[i];
max = Math.max(max, c.subV);
}
return max;
}
public static FrameHeader read(ByteBuffer is) {
FrameHeader frame = new FrameHeader();
frame.headerLength = is.getShort() & 0xFFFF;
frame.bitsPerSample = is.get() & 0xFF;
frame.height = is.getShort() & 0xFFFF;
frame.width = is.getShort() & 0xFFFF;
frame.nComp = is.get() & 0xFF;
frame.components = new Component[frame.nComp];
for (int i = 0; i < frame.components.length; i++) {
Component c = frame.components[i] = new Component();
c.index = is.get() & 0xFF;
int hv = is.get() & 0xFF;
c.subH = (hv & 0xF0) >>> 4;
c.subV = hv & 0xF;
c.quantTable = is.get() & 0xFF;
}
return frame;
}
}

View file

@ -0,0 +1,65 @@
package org.jcodec.codecs.mjpeg;
import java.nio.ByteBuffer;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.VLC;
public class JPEGBitStream {
private VLC[] huff;
private BitReader _in;
private int[] dcPredictor;
private int lumaLen;
public JPEGBitStream(ByteBuffer b, VLC[] huff, int lumaLen) {
this.dcPredictor = new int[3];
this._in = BitReader.createBitReader(b);
this.huff = huff;
this.lumaLen = lumaLen;
}
public void readMCU(int[][] buf) {
int blk = 0;
for (int i = 0; i < this.lumaLen; i++, blk++) {
buf[blk][0] = readDCValue(this.dcPredictor[0], this.huff[0]);
this.dcPredictor[0] = readDCValue(this.dcPredictor[0], this.huff[0]);
readACValues(buf[blk], this.huff[2]);
}
buf[blk][0] = readDCValue(this.dcPredictor[1], this.huff[1]);
this.dcPredictor[1] = readDCValue(this.dcPredictor[1], this.huff[1]);
readACValues(buf[blk], this.huff[3]);
blk++;
buf[blk][0] = readDCValue(this.dcPredictor[2], this.huff[1]);
this.dcPredictor[2] = readDCValue(this.dcPredictor[2], this.huff[1]);
readACValues(buf[blk], this.huff[3]);
blk++;
}
public int readDCValue(int prevDC, VLC table) {
int code = table.readVLC(this._in);
return (code != 0) ? (toValue(this._in.readNBit(code), code) + prevDC) : prevDC;
}
public void readACValues(int[] target, VLC table) {
int code;
int curOff = 1;
do {
code = table.readVLC(this._in);
if (code == 240) {
curOff += 16;
} else if (code > 0) {
int rle = code >> 4;
curOff += rle;
int len = code & 0xF;
target[curOff] = toValue(this._in.readNBit(len), len);
curOff++;
}
} while (code != 0 && curOff < 64);
}
public final int toValue(int raw, int length) {
return (length >= 1 && raw < 1 << length - 1) ? (-(1 << length) + 1 + raw) : raw;
}
}

View file

@ -0,0 +1,515 @@
package org.jcodec.codecs.mjpeg;
import org.jcodec.common.io.VLC;
import org.jcodec.common.io.VLCBuilder;
public class JpegConst {
public static final int[] naturalOrder = new int[] {
0, 1, 8, 16, 9, 2, 3, 10, 17, 24,
32, 25, 18, 11, 4, 5, 12, 19, 26, 33,
40, 48, 41, 34, 27, 20, 13, 6, 7, 14,
21, 28, 35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51, 58, 59,
52, 45, 38, 31, 39, 46, 53, 60, 61, 54,
47, 55, 62, 63 };
public static final VLC YDC_DEFAULT;
public static final VLC YAC_DEFAULT;
public static final VLC CDC_DEFAULT;
public static final VLC CAC_DEFAULT;
static {
VLCBuilder bldr1 = new VLCBuilder();
bldr1.set(0, "00");
bldr1.set(1, "010");
bldr1.set(2, "011");
bldr1.set(3, "100");
bldr1.set(4, "101");
bldr1.set(5, "110");
bldr1.set(6, "1110");
bldr1.set(7, "11110");
bldr1.set(8, "111110");
bldr1.set(9, "1111110");
bldr1.set(10, "11111110");
bldr1.set(11, "111111110");
YDC_DEFAULT = bldr1.getVLC();
VLCBuilder bldr2 = new VLCBuilder();
bldr2.set(0, "00");
bldr2.set(1, "01");
bldr2.set(2, "10");
bldr2.set(3, "110");
bldr2.set(4, "1110");
bldr2.set(5, "11110");
bldr2.set(6, "111110");
bldr2.set(7, "1111110");
bldr2.set(8, "11111110");
bldr2.set(9, "111111110");
bldr2.set(10, "1111111110");
bldr2.set(11, "11111111110");
CDC_DEFAULT = bldr2.getVLC();
VLCBuilder bldr3 = new VLCBuilder();
bldr3.set(0, "1010");
bldr3.set(1, "00");
bldr3.set(2, "01");
bldr3.set(3, "100");
bldr3.set(4, "1011");
bldr3.set(5, "11010");
bldr3.set(6, "1111000");
bldr3.set(7, "11111000");
bldr3.set(8, "1111110110");
bldr3.set(9, "1111111110000010");
bldr3.set(10, "1111111110000011");
bldr3.set(17, "1100");
bldr3.set(18, "11011");
bldr3.set(19, "1111001");
bldr3.set(20, "111110110");
bldr3.set(21, "11111110110");
bldr3.set(22, "1111111110000100");
bldr3.set(23, "1111111110000101");
bldr3.set(24, "1111111110000110");
bldr3.set(25, "1111111110000111");
bldr3.set(26, "1111111110001000");
bldr3.set(33, "11100");
bldr3.set(34, "11111001");
bldr3.set(35, "1111110111");
bldr3.set(36, "111111110100");
bldr3.set(37, "1111111110001001");
bldr3.set(38, "1111111110001010");
bldr3.set(39, "1111111110001011");
bldr3.set(40, "1111111110001100");
bldr3.set(41, "1111111110001101");
bldr3.set(42, "1111111110001110");
bldr3.set(49, "111010");
bldr3.set(50, "111110111");
bldr3.set(51, "111111110101");
bldr3.set(52, "1111111110001111");
bldr3.set(53, "1111111110010000");
bldr3.set(54, "1111111110010001");
bldr3.set(55, "1111111110010010");
bldr3.set(56, "1111111110010011");
bldr3.set(57, "1111111110010100");
bldr3.set(58, "1111111110010101");
bldr3.set(65, "111011");
bldr3.set(66, "1111111000");
bldr3.set(67, "1111111110010110");
bldr3.set(68, "1111111110010111");
bldr3.set(69, "1111111110011000");
bldr3.set(70, "1111111110011001");
bldr3.set(71, "1111111110011010");
bldr3.set(72, "1111111110011011");
bldr3.set(73, "1111111110011100");
bldr3.set(74, "1111111110011101");
bldr3.set(81, "1111010");
bldr3.set(82, "11111110111");
bldr3.set(83, "1111111110011110");
bldr3.set(84, "1111111110011111");
bldr3.set(85, "1111111110100000");
bldr3.set(86, "1111111110100001");
bldr3.set(87, "1111111110100010");
bldr3.set(88, "1111111110100011");
bldr3.set(89, "1111111110100100");
bldr3.set(90, "1111111110100101");
bldr3.set(97, "1111011");
bldr3.set(98, "111111110110");
bldr3.set(99, "1111111110100110");
bldr3.set(100, "1111111110100111");
bldr3.set(101, "1111111110101000");
bldr3.set(102, "1111111110101001");
bldr3.set(103, "1111111110101010");
bldr3.set(104, "1111111110101011");
bldr3.set(105, "1111111110101100");
bldr3.set(106, "1111111110101101");
bldr3.set(113, "11111010");
bldr3.set(114, "111111110111");
bldr3.set(115, "1111111110101110");
bldr3.set(116, "1111111110101111");
bldr3.set(117, "1111111110110000");
bldr3.set(118, "1111111110110001");
bldr3.set(119, "1111111110110010");
bldr3.set(120, "1111111110110011");
bldr3.set(121, "1111111110110100");
bldr3.set(122, "1111111110110101");
bldr3.set(129, "111111000");
bldr3.set(130, "111111111000000");
bldr3.set(131, "1111111110110110");
bldr3.set(132, "1111111110110111");
bldr3.set(133, "1111111110111000");
bldr3.set(134, "1111111110111001");
bldr3.set(135, "1111111110111010");
bldr3.set(136, "1111111110111011");
bldr3.set(137, "1111111110111100");
bldr3.set(138, "1111111110111101");
bldr3.set(145, "111111001");
bldr3.set(146, "1111111110111110");
bldr3.set(147, "1111111110111111");
bldr3.set(148, "1111111111000000");
bldr3.set(149, "1111111111000001");
bldr3.set(150, "1111111111000010");
bldr3.set(151, "1111111111000011");
bldr3.set(152, "1111111111000100");
bldr3.set(153, "1111111111000101");
bldr3.set(154, "1111111111000110");
bldr3.set(161, "111111010");
bldr3.set(162, "1111111111000111");
bldr3.set(163, "1111111111001000");
bldr3.set(164, "1111111111001001");
bldr3.set(165, "1111111111001010");
bldr3.set(166, "1111111111001011");
bldr3.set(167, "1111111111001100");
bldr3.set(168, "1111111111001101");
bldr3.set(169, "1111111111001110");
bldr3.set(170, "1111111111001111");
bldr3.set(177, "1111111001");
bldr3.set(178, "1111111111010000");
bldr3.set(179, "1111111111010001");
bldr3.set(180, "1111111111010010");
bldr3.set(181, "1111111111010011");
bldr3.set(182, "1111111111010100");
bldr3.set(183, "1111111111010101");
bldr3.set(184, "1111111111010110");
bldr3.set(185, "1111111111010111");
bldr3.set(186, "1111111111011000");
bldr3.set(193, "1111111010");
bldr3.set(194, "1111111111011001");
bldr3.set(195, "1111111111011010");
bldr3.set(196, "1111111111011011");
bldr3.set(197, "1111111111011100");
bldr3.set(198, "1111111111011101");
bldr3.set(199, "1111111111011110");
bldr3.set(200, "1111111111011111");
bldr3.set(201, "1111111111100000");
bldr3.set(202, "1111111111100001");
bldr3.set(209, "11111111000");
bldr3.set(210, "1111111111100010");
bldr3.set(211, "1111111111100011");
bldr3.set(212, "1111111111100100");
bldr3.set(213, "1111111111100101");
bldr3.set(214, "1111111111100110");
bldr3.set(215, "1111111111100111");
bldr3.set(216, "1111111111101000");
bldr3.set(217, "1111111111101001");
bldr3.set(218, "1111111111101010");
bldr3.set(225, "1111111111101011");
bldr3.set(226, "1111111111101100");
bldr3.set(227, "1111111111101101");
bldr3.set(228, "1111111111101110");
bldr3.set(229, "1111111111101111");
bldr3.set(230, "1111111111110000");
bldr3.set(231, "1111111111110001");
bldr3.set(232, "1111111111110010");
bldr3.set(233, "1111111111110011");
bldr3.set(234, "1111111111110100");
bldr3.set(240, "11111111001");
bldr3.set(241, "1111111111110101");
bldr3.set(242, "1111111111110110");
bldr3.set(243, "1111111111110111");
bldr3.set(244, "1111111111111000");
bldr3.set(245, "1111111111111001");
bldr3.set(246, "1111111111111010");
bldr3.set(247, "1111111111111011");
bldr3.set(248, "1111111111111100");
bldr3.set(249, "1111111111111101");
bldr3.set(250, "1111111111111110");
YAC_DEFAULT = bldr3.getVLC();
VLCBuilder bldr4 = new VLCBuilder();
bldr4.set(0, "00");
bldr4.set(1, "01");
bldr4.set(2, "100");
bldr4.set(3, "1010");
bldr4.set(4, "11000");
bldr4.set(5, "11001");
bldr4.set(6, "111000");
bldr4.set(7, "1111000");
bldr4.set(8, "111110100");
bldr4.set(9, "1111110110");
bldr4.set(10, "111111110100");
bldr4.set(17, "1011");
bldr4.set(18, "111001");
bldr4.set(19, "11110110");
bldr4.set(20, "111110101");
bldr4.set(21, "11111110110");
bldr4.set(22, "111111110101");
bldr4.set(23, "1111111110001000");
bldr4.set(24, "1111111110001001");
bldr4.set(25, "1111111110001010");
bldr4.set(26, "1111111110001011");
bldr4.set(33, "11010");
bldr4.set(34, "11110111");
bldr4.set(35, "1111110111");
bldr4.set(36, "111111110110");
bldr4.set(37, "111111111000010");
bldr4.set(38, "1111111110001100");
bldr4.set(39, "1111111110001101");
bldr4.set(40, "1111111110001110");
bldr4.set(41, "1111111110001111");
bldr4.set(42, "1111111110010000");
bldr4.set(49, "11011");
bldr4.set(50, "11111000");
bldr4.set(51, "1111111000");
bldr4.set(52, "111111110111");
bldr4.set(53, "1111111110010001");
bldr4.set(54, "1111111110010010");
bldr4.set(55, "1111111110010011");
bldr4.set(56, "1111111110010100");
bldr4.set(57, "1111111110010101");
bldr4.set(58, "1111111110010110");
bldr4.set(65, "111010");
bldr4.set(66, "111110110");
bldr4.set(67, "1111111110010111");
bldr4.set(68, "1111111110011000");
bldr4.set(69, "1111111110011001");
bldr4.set(70, "1111111110011010");
bldr4.set(71, "1111111110011011");
bldr4.set(72, "1111111110011100");
bldr4.set(73, "1111111110011101");
bldr4.set(74, "1111111110011110");
bldr4.set(81, "111011");
bldr4.set(82, "1111111001");
bldr4.set(83, "1111111110011111");
bldr4.set(84, "1111111110100000");
bldr4.set(85, "1111111110100001");
bldr4.set(86, "1111111110100010");
bldr4.set(87, "1111111110100011");
bldr4.set(88, "1111111110100100");
bldr4.set(89, "1111111110100101");
bldr4.set(90, "1111111110100110");
bldr4.set(97, "1111001");
bldr4.set(98, "11111110111");
bldr4.set(99, "1111111110100111");
bldr4.set(100, "1111111110101000");
bldr4.set(101, "1111111110101001");
bldr4.set(102, "1111111110101010");
bldr4.set(103, "1111111110101011");
bldr4.set(104, "1111111110101100");
bldr4.set(105, "1111111110101101");
bldr4.set(106, "1111111110101110");
bldr4.set(113, "1111010");
bldr4.set(114, "11111111000");
bldr4.set(115, "1111111110101111");
bldr4.set(116, "1111111110110000");
bldr4.set(117, "1111111110110001");
bldr4.set(118, "1111111110110010");
bldr4.set(119, "1111111110110011");
bldr4.set(120, "1111111110110100");
bldr4.set(121, "1111111110110101");
bldr4.set(122, "1111111110110110");
bldr4.set(129, "11111001");
bldr4.set(130, "1111111110110111");
bldr4.set(131, "1111111110111000");
bldr4.set(132, "1111111110111001");
bldr4.set(133, "1111111110111010");
bldr4.set(134, "1111111110111011");
bldr4.set(135, "1111111110111100");
bldr4.set(136, "1111111110111101");
bldr4.set(137, "1111111110111110");
bldr4.set(138, "1111111110111111");
bldr4.set(145, "111110111");
bldr4.set(146, "1111111111000000");
bldr4.set(147, "1111111111000001");
bldr4.set(148, "1111111111000010");
bldr4.set(149, "1111111111000011");
bldr4.set(150, "1111111111000100");
bldr4.set(151, "1111111111000101");
bldr4.set(152, "1111111111000110");
bldr4.set(153, "1111111111000111");
bldr4.set(154, "1111111111001000");
bldr4.set(161, "111111000");
bldr4.set(162, "1111111111001001");
bldr4.set(163, "1111111111001010");
bldr4.set(164, "1111111111001011");
bldr4.set(165, "1111111111001100");
bldr4.set(166, "1111111111001101");
bldr4.set(167, "1111111111001110");
bldr4.set(168, "1111111111001111");
bldr4.set(169, "1111111111010000");
bldr4.set(170, "1111111111010001");
bldr4.set(177, "111111001");
bldr4.set(178, "1111111111010010");
bldr4.set(179, "1111111111010011");
bldr4.set(180, "1111111111010100");
bldr4.set(181, "1111111111010101");
bldr4.set(182, "1111111111010110");
bldr4.set(183, "1111111111010111");
bldr4.set(184, "1111111111011000");
bldr4.set(185, "1111111111011001");
bldr4.set(186, "1111111111011010");
bldr4.set(193, "111111010");
bldr4.set(194, "1111111111011011");
bldr4.set(195, "1111111111011100");
bldr4.set(196, "1111111111011101");
bldr4.set(197, "1111111111011110");
bldr4.set(198, "1111111111011111");
bldr4.set(199, "1111111111100000");
bldr4.set(200, "1111111111100001");
bldr4.set(201, "1111111111100010");
bldr4.set(202, "1111111111100011");
bldr4.set(209, "11111111001");
bldr4.set(210, "1111111111100100");
bldr4.set(211, "1111111111100101");
bldr4.set(212, "1111111111100110");
bldr4.set(213, "1111111111100111");
bldr4.set(214, "1111111111101000");
bldr4.set(215, "1111111111101001");
bldr4.set(216, "1111111111101010");
bldr4.set(217, "1111111111101011");
bldr4.set(218, "1111111111101100");
bldr4.set(225, "11111111100000");
bldr4.set(226, "1111111111101101");
bldr4.set(227, "1111111111101110");
bldr4.set(228, "1111111111101111");
bldr4.set(229, "1111111111110000");
bldr4.set(230, "1111111111110001");
bldr4.set(231, "1111111111110010");
bldr4.set(232, "1111111111110011");
bldr4.set(233, "1111111111110100");
bldr4.set(234, "1111111111110101");
bldr4.set(240, "1111111010");
bldr4.set(241, "111111111000011");
bldr4.set(242, "1111111111110110");
bldr4.set(243, "1111111111110111");
bldr4.set(244, "1111111111111000");
bldr4.set(245, "1111111111111001");
bldr4.set(246, "1111111111111010");
bldr4.set(247, "1111111111111011");
bldr4.set(248, "1111111111111100");
bldr4.set(249, "1111111111111101");
bldr4.set(250, "1111111111111110");
CAC_DEFAULT = bldr4.getVLC();
}
private static final String[] names = new String[256];
public static final int SOF0 = 192;
public static final int SOF1 = 193;
public static final int SOF2 = 194;
public static final int SOF3 = 195;
public static final int DHT = 196;
public static final int DQT = 219;
public static final int SOS = 218;
public static final int EOI = 217;
public static final int SOI = 216;
public static final int APP0 = 224;
public static final int APP1 = 225;
public static final int APP2 = 226;
public static final int APP3 = 227;
public static final int APP4 = 228;
public static final int APP5 = 229;
public static final int APP6 = 230;
public static final int APP7 = 231;
public static final int APP8 = 232;
public static final int APP9 = 233;
public static final int APPA = 234;
public static final int APPB = 235;
public static final int APPC = 236;
public static final int APPD = 237;
public static final int APPE = 238;
public static final int APPF = 239;
public static final int RST0 = 208;
public static final int RST1 = 209;
public static final int RST2 = 210;
public static final int RST3 = 211;
public static final int RST4 = 212;
public static final int RST5 = 213;
public static final int RST6 = 214;
public static final int RST7 = 215;
public static final int COM = 254;
public static final int DRI = 221;
static {
for (int i = 0; i < names.length; i++)
names[i] = "(0x" + Integer.toHexString(i) + ")";
names[192] = "SOF0";
names[193] = "SOF1";
names[194] = "SOF2";
names[195] = "SOF3";
names[196] = "DHT";
names[219] = "DQT";
names[218] = "SOS";
names[217] = "EOI";
names[216] = "SOI";
names[224] = "APP0";
names[225] = "APP1";
names[226] = "APP2";
names[227] = "APP3";
names[228] = "APP4";
names[229] = "APP5";
names[230] = "APP6";
names[231] = "APP7";
names[232] = "APP8";
names[233] = "APP9";
names[234] = "APPA";
names[235] = "APPB";
names[236] = "APPC";
names[237] = "APPD";
names[238] = "APPE";
names[239] = "APPF";
names[208] = "RST0";
names[209] = "RST1";
names[210] = "RST2";
names[211] = "RST3";
names[212] = "RST4";
names[213] = "RST5";
names[214] = "RST6";
names[215] = "RST7";
names[221] = "DRI";
}
public static String markerToString(int marker) {
return names[marker];
}
public static int[] DEFAULT_QUANT_LUMA = new int[] {
16, 11, 12, 14, 12, 10, 16, 14, 13, 14,
18, 17, 16, 19, 24, 40, 26, 24, 22, 22,
24, 49, 36, 37, 29, 40, 58, 51, 61, 60,
57, 51, 56, 55, 64, 72, 92, 78, 64, 68,
87, 69, 55, 56, 80, 109, 81, 87, 95, 62,
103, 104, 103, 98, 77, 113, 121, 112, 100, 120,
92, 101, 103, 99 };
public static int[] DEFAULT_QUANT_CHROMA = new int[] {
17, 18, 18, 24, 21, 24, 47, 26, 26, 47,
99, 66, 56, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99 };
}

View file

@ -0,0 +1,255 @@
package org.jcodec.codecs.mjpeg;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.jcodec.api.UnhandledStateException;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.VideoDecoder;
import org.jcodec.common.dct.SimpleIDCT10Bit;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.VLC;
import org.jcodec.common.io.VLCBuilder;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Rect;
import org.jcodec.common.model.Size;
import org.jcodec.common.tools.MathUtil;
public class JpegDecoder extends VideoDecoder {
private boolean interlace;
private boolean topFieldFirst;
int[] buf = new int[64];
public void setInterlace(boolean interlace, boolean topFieldFirst) {
this.interlace = interlace;
this.topFieldFirst = topFieldFirst;
}
private Picture decodeScan(ByteBuffer data, FrameHeader header, ScanHeader scan, VLC[] huffTables, int[][] quant, byte[][] data2, int field, int step) {
int blockW = header.getHmax();
int blockH = header.getVmax();
int mcuW = blockW << 3;
int mcuH = blockH << 3;
int width = header.width;
int height = header.height;
int xBlocks = width + mcuW - 1 >> blockW + 2;
int yBlocks = height + mcuH - 1 >> blockH + 2;
int nn = blockW + blockH;
Picture result = new Picture(xBlocks << blockW + 2, yBlocks << blockH + 2, data2, null,
(nn == 4) ? ColorSpace.YUV420J : ((nn == 3) ? ColorSpace.YUV422J : ColorSpace.YUV444J), 0, new Rect(0, 0, width, height));
BitReader bits = BitReader.createBitReader(data);
int[] dcPredictor = { 1024, 1024, 1024 };
for (int by = 0; by < yBlocks; by++) {
for (int bx = 0; bx < xBlocks && bits.moreData(); bx++)
decodeMCU(bits, dcPredictor, quant, huffTables, result, bx, by, blockW, blockH, field, step);
}
return result;
}
private static void putBlock(byte[] plane, int stride, int[] patch, int x, int y, int field, int step) {
int dstride = step * stride;
for (int i = 0, off = field * stride + y * dstride + x, poff = 0; i < 8; i++) {
for (int j = 0; j < 8; j++)
plane[j + off] = (byte)(MathUtil.clip(patch[j + poff], 0, 255) - 128);
off += dstride;
poff += 8;
}
}
private void decodeMCU(BitReader bits, int[] dcPredictor, int[][] quant, VLC[] huff, Picture result, int bx, int by, int blockH, int blockV, int field, int step) {
int sx = bx << blockH - 1;
int sy = by << blockV - 1;
for (int i = 0; i < blockV; i++) {
for (int j = 0; j < blockH; j++)
decodeBlock(bits, dcPredictor, quant, huff, result, this.buf, sx + j << 3, sy + i << 3, 0, 0, field, step);
}
decodeBlock(bits, dcPredictor, quant, huff, result, this.buf, bx << 3, by << 3, 1, 1, field, step);
decodeBlock(bits, dcPredictor, quant, huff, result, this.buf, bx << 3, by << 3, 2, 1, field, step);
}
void decodeBlock(BitReader bits, int[] dcPredictor, int[][] quant, VLC[] huff, Picture result, int[] buf, int blkX, int blkY, int plane, int chroma, int field, int step) {
Arrays.fill(buf, 0);
buf[0] = readDCValue(bits, huff[chroma]) * quant[chroma][0] + dcPredictor[plane];
dcPredictor[plane] = readDCValue(bits, huff[chroma]) * quant[chroma][0] + dcPredictor[plane];
readACValues(bits, buf, huff[chroma + 2], quant[chroma]);
SimpleIDCT10Bit.idct10(buf, 0);
putBlock(result.getPlaneData(plane), result.getPlaneWidth(plane), buf, blkX, blkY, field, step);
}
static int readDCValue(BitReader _in, VLC table) {
int code = table.readVLC16(_in);
return (code != 0) ? toValue(_in.readNBit(code), code) : 0;
}
void readACValues(BitReader _in, int[] target, VLC table, int[] quantTable) {
int code;
int curOff = 1;
do {
code = table.readVLC16(_in);
if (code == 240) {
curOff += 16;
} else if (code > 0) {
int rle = code >> 4;
curOff += rle;
int len = code & 0xF;
target[JpegConst.naturalOrder[curOff]] = toValue(_in.readNBit(len), len) * quantTable[curOff];
curOff++;
}
} while (code != 0 && curOff < 64);
}
static int toValue(int raw, int length) {
return (length >= 1 && raw < 1 << length - 1) ? (-(1 << length) + 1 + raw) : raw;
}
public Picture decodeFrame(ByteBuffer data, byte[][] data2) {
if (this.interlace) {
Picture r1 = decodeField(data, data2, this.topFieldFirst ? 0 : 1, 2);
Picture r2 = decodeField(data, data2, this.topFieldFirst ? 1 : 0, 2);
return Picture.createPicture(r1.getWidth(), r1.getHeight() << 1, data2, r1.getColor());
}
return decodeField(data, data2, 0, 1);
}
public Picture decodeField(ByteBuffer data, byte[][] data2, int field, int step) {
Picture result = null;
FrameHeader header = null;
VLC[] huffTables = { JpegConst.YDC_DEFAULT, JpegConst.CDC_DEFAULT, JpegConst.YAC_DEFAULT, JpegConst.CAC_DEFAULT };
int[][] quant = { JpegConst.DEFAULT_QUANT_LUMA, JpegConst.DEFAULT_QUANT_CHROMA };
ScanHeader scan = null;
boolean skipToNext = false;
while (data.hasRemaining()) {
int marker;
if (!skipToNext) {
marker = data.get() & 0xFF;
} else {
while ((marker = data.get() & 0xFF) != 255);
}
skipToNext = false;
if (marker == 0)
continue;
if (marker != 255)
throw new RuntimeException("@" +
Long.toHexString((long)data.position()) + " Marker expected: 0x" + Integer.toHexString(marker));
int b;
while ((b = data.get() & 0xFF) == 255);
if (b == 192) {
header = FrameHeader.read(data);
continue;
}
if (b == 196) {
int len1 = data.getShort() & 0xFFFF;
ByteBuffer buf = NIOUtils.read(data, len1 - 2);
while (buf.hasRemaining()) {
int tableNo = buf.get() & 0xFF;
huffTables[tableNo & 0x1 | tableNo >> 3 & 0x2] = readHuffmanTable(buf);
}
continue;
}
if (b == 219) {
int len4 = data.getShort() & 0xFFFF;
ByteBuffer buf = NIOUtils.read(data, len4 - 2);
while (buf.hasRemaining()) {
int ind = buf.get() & 0xFF;
quant[ind] = readQuantTable(buf);
}
continue;
}
if (b == 218) {
if (scan != null)
throw new UnhandledStateException("unhandled - more than one scan header");
scan = ScanHeader.read(data);
result = decodeScan(readToMarker(data), header, scan, huffTables, quant, data2, field, step);
continue;
}
if (b == 216 || (b >= 208 && b <= 215)) {
Logger.warn("SOI not supported.");
skipToNext = true;
continue;
}
if (b == 217)
break;
if (b >= 224 && b <= 254) {
int len3 = data.getShort() & 0xFFFF;
NIOUtils.read(data, len3 - 2);
continue;
}
if (b == 221) {
Logger.warn("DRI not supported.");
skipToNext = true;
continue;
}
if (b != 0)
Logger.warn("unhandled marker " + JpegConst.markerToString(b));
skipToNext = true;
}
return result;
}
private static ByteBuffer readToMarker(ByteBuffer data) {
ByteBuffer out = ByteBuffer.allocate(data.remaining());
while (data.hasRemaining()) {
byte b0 = data.get();
if (b0 == -1) {
byte b1 = data.get();
if (b1 == 0) {
out.put((byte)-1);
continue;
}
data.position(data.position() - 2);
break;
}
out.put(b0);
}
out.flip();
return out;
}
private static VLC readHuffmanTable(ByteBuffer data) {
VLCBuilder builder = new VLCBuilder();
byte[] levelSizes = NIOUtils.toArray(NIOUtils.read(data, 16));
int levelStart = 0;
for (int i = 0; i < 16; i++) {
int length = levelSizes[i] & 0xFF;
for (int c = 0; c < length; c++) {
int val = data.get() & 0xFF;
int code = levelStart++;
builder.setInt(code, i + 1, val);
}
levelStart <<= 1;
}
return builder.getVLC();
}
private static int[] readQuantTable(ByteBuffer data) {
int[] result = new int[64];
for (int i = 0; i < 64; i++)
result[i] = data.get() & 0xFF;
return result;
}
public VideoCodecMeta getCodecMeta(ByteBuffer data) {
FrameHeader header = null;
while (data.hasRemaining()) {
while (data.hasRemaining() && (data.get() & 0xFF) != 255);
int type;
while ((type = data.get() & 0xFF) == 255);
if (type == 192) {
header = FrameHeader.read(data);
break;
}
}
if (header != null) {
int blockW = header.getHmax();
int blockH = header.getVmax();
int nn = blockW + blockH;
ColorSpace color = (nn == 4) ? ColorSpace.YUV420J : ((nn == 3) ? ColorSpace.YUV422J : ColorSpace.YUV444J);
return VideoCodecMeta.createSimpleVideoCodecMeta(new Size(header.width, header.height), color);
}
return null;
}
}

View file

@ -0,0 +1,73 @@
package org.jcodec.codecs.mjpeg;
import java.nio.ByteBuffer;
import org.jcodec.common.dct.IDCT2x2;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.VLC;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Rect;
import org.jcodec.common.tools.MathUtil;
public class JpegToThumb2x2 extends JpegDecoder {
private static final int[] mapping2x2 = new int[] {
0, 1, 2, 4, 3, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4 };
void decodeBlock(BitReader bits, int[] dcPredictor, int[][] quant, VLC[] huff, Picture result, int[] buf, int blkX, int blkY, int plane, int chroma, int field, int step) {
buf[3] = 0;
buf[2] = 0;
buf[1] = 0;
buf[0] = readDCValue(bits, huff[chroma]) * quant[chroma][0] + dcPredictor[plane];
dcPredictor[plane] = readDCValue(bits, huff[chroma]) * quant[chroma][0] + dcPredictor[plane];
readACValues(bits, buf, huff[chroma + 2], quant[chroma]);
IDCT2x2.idct(buf, 0);
putBlock2x2(result.getPlaneData(plane), result.getPlaneWidth(plane), buf, blkX, blkY, field, step);
}
private static void putBlock2x2(byte[] plane, int stride, int[] patch, int x, int y, int field, int step) {
stride >>= 2;
int dstride = stride * step;
int off = field * stride + (y >> 2) * dstride + (x >> 2);
plane[off] = (byte)(MathUtil.clip(patch[0], 0, 255) - 128);
plane[off + 1] = (byte)(MathUtil.clip(patch[1], 0, 255) - 128);
plane[off + dstride] = (byte)(MathUtil.clip(patch[2], 0, 255) - 128);
plane[off + dstride + 1] = (byte)(MathUtil.clip(patch[3], 0, 255) - 128);
}
void readACValues(BitReader _in, int[] target, VLC table, int[] quantTable) {
int code;
int curOff = 1;
do {
code = table.readVLC16(_in);
if (code == 240) {
curOff += 16;
} else if (code > 0) {
int rle = code >> 4;
curOff += rle;
int len = code & 0xF;
target[mapping2x2[curOff]] = toValue(_in.readNBit(len), len) * quantTable[curOff];
curOff++;
}
} while (code != 0 && curOff < 5);
if (code != 0)
do {
code = table.readVLC16(_in);
if (code == 240) {
curOff += 16;
} else if (code > 0) {
int rle = code >> 4;
curOff += rle;
int len = code & 0xF;
_in.skip(len);
curOff++;
}
} while (code != 0 && curOff < 64);
}
public Picture decodeField(ByteBuffer data, byte[][] data2, int field, int step) {
Picture res = super.decodeField(data, data2, field, step);
return new Picture(res.getWidth() >> 2, res.getHeight() >> 2, res.getData(), null, res.getColor(), 0, new Rect(0, 0,
res.getCroppedWidth() >> 2, res.getCroppedHeight() >> 2));
}
}

View file

@ -0,0 +1,92 @@
package org.jcodec.codecs.mjpeg;
import java.nio.ByteBuffer;
import org.jcodec.common.dct.IDCT4x4;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.VLC;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Rect;
import org.jcodec.common.tools.MathUtil;
public class JpegToThumb4x4 extends JpegDecoder {
private static final int[] mapping4x4 = new int[] {
0, 1, 4, 8, 5, 2, 3, 6, 9, 12,
16, 13, 10, 7, 16, 16, 16, 11, 14, 16,
16, 16, 16, 16, 15, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16 };
void decodeBlock(BitReader bits, int[] dcPredictor, int[][] quant, VLC[] huff, Picture result, int[] buf, int blkX, int blkY, int plane, int chroma, int field, int step) {
buf[15] = 0;
buf[14] = 0;
buf[13] = 0;
buf[12] = 0;
buf[11] = 0;
buf[10] = 0;
buf[9] = 0;
buf[8] = 0;
buf[7] = 0;
buf[6] = 0;
buf[5] = 0;
buf[4] = 0;
buf[3] = 0;
buf[2] = 0;
buf[1] = 0;
buf[0] = readDCValue(bits, huff[chroma]) * quant[chroma][0] + dcPredictor[plane];
dcPredictor[plane] = readDCValue(bits, huff[chroma]) * quant[chroma][0] + dcPredictor[plane];
readACValues(bits, buf, huff[chroma + 2], quant[chroma]);
IDCT4x4.idct(buf, 0);
putBlock4x4(result.getPlaneData(plane), result.getPlaneWidth(plane), buf, blkX, blkY, field, step);
}
private static void putBlock4x4(byte[] plane, int stride, int[] patch, int x, int y, int field, int step) {
stride >>= 1;
int dstride = step * stride;
int off = field * stride + (y >> 1) * dstride + (x >> 1);
for (int i = 0; i < 16; i += 4) {
plane[off] = (byte)(MathUtil.clip(patch[i], 0, 255) - 128);
plane[off + 1] = (byte)(MathUtil.clip(patch[i + 1], 0, 255) - 128);
plane[off + 2] = (byte)(MathUtil.clip(patch[i + 2], 0, 255) - 128);
plane[off + 3] = (byte)(MathUtil.clip(patch[i + 3], 0, 255) - 128);
off += dstride;
}
}
void readACValues(BitReader _in, int[] target, VLC table, int[] quantTable) {
int code;
int curOff = 1;
do {
code = table.readVLC16(_in);
if (code == 240) {
curOff += 16;
} else if (code > 0) {
int rle = code >> 4;
curOff += rle;
int len = code & 0xF;
target[mapping4x4[curOff]] = toValue(_in.readNBit(len), len) * quantTable[curOff];
curOff++;
}
} while (code != 0 && curOff < 19);
if (code != 0)
do {
code = table.readVLC16(_in);
if (code == 240) {
curOff += 16;
} else if (code > 0) {
int rle = code >> 4;
curOff += rle;
int len = code & 0xF;
_in.skip(len);
curOff++;
}
} while (code != 0 && curOff < 64);
}
public Picture decodeField(ByteBuffer data, byte[][] data2, int field, int step) {
Picture res = super.decodeField(data, data2, field, step);
return new Picture(res.getWidth() >> 1, res.getHeight() >> 1, res.getData(), null, res.getColor(), 0, new Rect(0, 0,
res.getCroppedWidth() >> 1, res.getCroppedHeight() >> 1));
}
}

View file

@ -0,0 +1,51 @@
package org.jcodec.codecs.mjpeg;
import java.nio.ByteBuffer;
public class ScanHeader {
int ls;
int ns;
Component[] components;
int ss;
int se;
int ah;
int al;
public boolean isInterleaved() {
return (this.ns > 1);
}
public static class Component {
int cs;
int td;
int ta;
}
public static ScanHeader read(ByteBuffer bb) {
ScanHeader scan = new ScanHeader();
scan.ls = bb.getShort() & 0xFFFF;
scan.ns = bb.get() & 0xFF;
scan.components = new Component[scan.ns];
for (int i = 0; i < scan.components.length; i++) {
Component c = scan.components[i] = new Component();
c.cs = bb.get() & 0xFF;
int tdta = bb.get() & 0xFF;
c.td = (tdta & 0xF0) >>> 4;
c.ta = tdta & 0xF;
}
scan.ss = bb.get() & 0xFF;
scan.se = bb.get() & 0xFF;
int ahal = bb.get() & 0xFF;
scan.ah = (ahal & 0xF0) >>> 4;
scan.al = ahal & 0xF;
return scan;
}
}

View file

@ -0,0 +1,7 @@
package org.jcodec.codecs.mjpeg.tools;
public class AssertionException extends RuntimeException {
public AssertionException(String string) {
super(string);
}
}

View file

@ -0,0 +1,37 @@
package org.jcodec.codecs.mjpeg.tools;
public class Asserts {
public static void assertEquals(int expected, int actual) {
if (expected != actual)
throw new AssertionException("assert failed: " + expected + " != " + actual);
}
public static void assertInRange(String message, int low, int up, int val) {
if (val < low || val > up)
throw new AssertionException(message);
}
public static void assertEpsilonEqualsInt(int[] expected, int[] actual, int eps) {
if (expected.length != actual.length)
throw new AssertionException("arrays of different size");
for (int i = 0; i < expected.length; i++) {
int e = expected[i];
int a = actual[i];
if (Math.abs(e - a) > eps)
throw new AssertionException("array element " + i + " " + e + " != " + a + " out of expected diff range " + eps);
}
}
public static void assertEpsilonEquals(byte[] expected, byte[] actual, int eps) {
if (expected.length != actual.length)
throw new AssertionException("arrays of different size");
for (int i = 0; i < expected.length; i++) {
int e = expected[i] & 0xFF;
int a = actual[i] & 0xFF;
if (Math.abs(e - a) > eps)
throw new AssertionException("array element out of expected diff range: " +
Math.abs(e - a));
}
}
}