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,108 @@
package org.jcodec.algo;
import org.jcodec.api.NotSupportedException;
public class DataConvert {
public static int[] from16BE(byte[] b) {
int[] result = new int[b.length >> 1];
int off = 0;
for (int i = 0; i < result.length; i++)
result[i] = (b[off++] & 0xFF) << 8 | b[off++] & 0xFF;
return result;
}
public static int[] from24BE(byte[] b) {
int[] result = new int[b.length / 3];
int off = 0;
for (int i = 0; i < result.length; i++)
result[i] = (b[off++] & 0xFF) << 16 | (b[off++] & 0xFF) << 8 | b[off++] & 0xFF;
return result;
}
public static int[] from16LE(byte[] b) {
int[] result = new int[b.length >> 1];
int off = 0;
for (int i = 0; i < result.length; i++)
result[i] = b[off++] & 0xFF | (b[off++] & 0xFF) << 8;
return result;
}
public static int[] from24LE(byte[] b) {
int[] result = new int[b.length / 3];
int off = 0;
for (int i = 0; i < result.length; i++)
result[i] = b[off++] & 0xFF | (b[off++] & 0xFF) << 8 | (b[off++] & 0xFF) << 16;
return result;
}
public static byte[] to16BE(int[] ia) {
byte[] result = new byte[ia.length << 1];
int off = 0;
for (int i = 0; i < ia.length; i++) {
result[off++] = (byte)(ia[i] >> 8 & 0xFF);
result[off++] = (byte)(ia[i] & 0xFF);
}
return result;
}
public static byte[] to24BE(int[] ia) {
byte[] result = new byte[ia.length * 3];
int off = 0;
for (int i = 0; i < ia.length; i++) {
result[off++] = (byte)(ia[i] >> 16 & 0xFF);
result[off++] = (byte)(ia[i] >> 8 & 0xFF);
result[off++] = (byte)(ia[i] & 0xFF);
}
return result;
}
public static byte[] to16LE(int[] ia) {
byte[] result = new byte[ia.length << 1];
int off = 0;
for (int i = 0; i < ia.length; i++) {
result[off++] = (byte)(ia[i] & 0xFF);
result[off++] = (byte)(ia[i] >> 8 & 0xFF);
}
return result;
}
public static byte[] to24LE(int[] ia) {
byte[] result = new byte[ia.length * 3];
int off = 0;
for (int i = 0; i < ia.length; i++) {
result[off++] = (byte)(ia[i] & 0xFF);
result[off++] = (byte)(ia[i] >> 8 & 0xFF);
result[off++] = (byte)(ia[i] >> 16 & 0xFF);
}
return result;
}
public static int[] fromByte(byte[] b, int depth, boolean isBe) {
if (depth == 24) {
if (isBe)
return from24BE(b);
return from24LE(b);
}
if (depth == 16) {
if (isBe)
return from16BE(b);
return from16LE(b);
}
throw new NotSupportedException("Conversion from " + depth + "bit " + (
isBe ? "big endian" : "little endian") + " is not supported.");
}
public static byte[] toByte(int[] ia, int depth, boolean isBe) {
if (depth == 24) {
if (isBe)
return to24BE(ia);
return to24LE(ia);
}
if (depth == 16) {
if (isBe)
return to16BE(ia);
return to16LE(ia);
}
throw new NotSupportedException("Conversion to " + depth + "bit " + (isBe ? "big endian" : "little endian") + " is not supported.");
}
}

View file

@ -0,0 +1,149 @@
package org.jcodec.algo;
import org.jcodec.common.AudioFormat;
public class SoundFilter {
public static final int[] sine = new int[] {
0, 1, 3, 4, 6, 7, 9, 10, 12, 14,
15, 17, 18, 20, 21, 23, 25, 26, 28, 29,
31, 32, 34, 36, 37, 39, 40, 42, 43, 45,
47, 48, 50, 51, 53, 54, 56, 57, 59, 61,
62, 64, 65, 67, 68, 70, 72, 73, 75, 76,
78, 79, 81, 82, 84, 85, 87, 89, 90, 92,
93, 95, 96, 98, 99, 101, 102, 104, 106, 107,
109, 110, 112, 113, 115, 116, 118, 119, 121, 122,
124, 125, 127, 128, 130, 132, 133, 135, 136, 138,
139, 141, 142, 144, 145, 147, 148, 150, 151, 153,
154, 156, 157, 159, 160, 162, 163, 165, 166, 168,
169, 171, 172, 173, 175, 176, 178, 179, 181, 182,
184, 185, 187, 188, 190, 191, 193, 194, 195, 197,
198, 200, 201, 203, 204, 206, 207, 208, 210, 211,
213, 214, 216, 217, 218, 220, 221, 223, 224, 225,
227, 228, 230, 231, 233, 234, 235, 237, 238, 239,
241, 242, 244, 245, 246, 248, 249, 250, 252, 253,
255, 256, 257, 259, 260, 261, 263, 264, 265, 267,
268, 269, 271, 272, 273, 275, 276, 277, 279, 280,
281, 283, 284, 285, 287, 288, 289, 290, 292, 293,
294, 296, 297, 298, 299, 301, 302, 303, 304, 306,
307, 308, 310, 311, 312, 313, 314, 316, 317, 318,
319, 321, 322, 323, 324, 326, 327, 328, 329, 330,
332, 333, 334, 335, 336, 337, 339, 340, 341, 342,
343, 345, 346, 347, 348, 349, 350, 351, 353, 354,
355, 356, 357, 358, 359, 360, 362, 363, 364, 365,
366, 367, 368, 369, 370, 371, 372, 374, 375, 376,
377, 378, 379, 380, 381, 382, 383, 384, 385, 386,
387, 388, 389, 390, 391, 392, 393, 394, 395, 396,
397, 398, 399, 400, 401, 402, 403, 404, 405, 406,
407, 408, 409, 410, 411, 412, 413, 414, 414, 415,
416, 417, 418, 419, 420, 421, 422, 423, 423, 424,
425, 426, 427, 428, 429, 430, 430, 431, 432, 433,
434, 435, 435, 436, 437, 438, 439, 439, 440, 441,
442, 443, 443, 444, 445, 446, 447, 447, 448, 449,
450, 450, 451, 452, 453, 453, 454, 455, 455, 456,
457, 458, 458, 459, 460, 460, 461, 462, 462, 463,
464, 464, 465, 466, 466, 467, 468, 468, 469, 469,
470, 471, 471, 472, 473, 473, 474, 474, 475, 475,
476, 477, 477, 478, 478, 479, 479, 480, 481, 481,
482, 482, 483, 483, 484, 484, 485, 485, 486, 486,
487, 487, 488, 488, 489, 489, 489, 490, 490, 491,
491, 492, 492, 493, 493, 493, 494, 494, 495, 495,
495, 496, 496, 497, 497, 497, 498, 498, 498, 499,
499, 499, 500, 500, 500, 501, 501, 501, 502, 502,
502, 503, 503, 503, 503, 504, 504, 504, 504, 505,
505, 505, 505, 506, 506, 506, 506, 507, 507, 507,
507, 507, 508, 508, 508, 508, 508, 509, 509, 509,
509, 509, 509, 509, 510, 510, 510, 510, 510, 510,
510, 510, 511, 511, 511, 511, 511, 511, 511, 511,
511, 511, 511, 511, 511, 511, 511, 511, 511, 511,
511, 511 };
public static final int[] linear = new int[] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
280, 281, 282, 283, 284, 285, 286, 287, 288, 289,
290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
320, 321, 322, 323, 324, 325, 326, 327, 328, 329,
330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
340, 341, 342, 343, 344, 345, 346, 347, 348, 349,
350, 351, 352, 353, 354, 355, 356, 357, 358, 359,
360, 361, 362, 363, 364, 365, 366, 367, 368, 369,
370, 371, 372, 373, 374, 375, 376, 377, 378, 379,
380, 381, 382, 383, 384, 385, 386, 387, 388, 389,
390, 391, 392, 393, 394, 395, 396, 397, 398, 399,
400, 401, 402, 403, 404, 405, 406, 407, 408, 409,
410, 411, 412, 413, 414, 415, 416, 417, 418, 419,
420, 421, 422, 423, 424, 425, 426, 427, 428, 429,
430, 431, 432, 433, 434, 435, 436, 437, 438, 439,
440, 441, 442, 443, 444, 445, 446, 447, 448, 449,
450, 451, 452, 453, 454, 455, 456, 457, 458, 459,
460, 461, 462, 463, 464, 465, 466, 467, 468, 469,
470, 471, 472, 473, 474, 475, 476, 477, 478, 479,
480, 481, 482, 483, 484, 485, 486, 487, 488, 489,
490, 491, 492, 493, 494, 495, 496, 497, 498, 499,
500, 501, 502, 503, 504, 505, 506, 507, 508, 509,
510, 511 };
public static void in16BitSignedLE(byte[] b, int nCh, int[] func) {
int to = Math.min(b.length * nCh * 2, func.length);
int off = 0;
for (int i = 0; i < to; i++) {
for (int j = 0; j < nCh; j++) {
short sample = (short)(b[off] & 0xFF | (b[off + 1] & 0xFF) << 8);
sample = (short)(sample * func[i] >> 9);
b[off] = (byte)(sample & 0xFF);
b[off + 1] = (byte)(sample >> 8);
off += 2;
}
}
}
public static void out16BitSignedLE(byte[] b, int nCh, int[] func) {
int to = Math.min(b.length * nCh * 2, func.length);
int off = b.length - to * nCh * 2;
for (int i = 0; i < to; i++) {
for (int j = 0; j < nCh; j++) {
short sample = (short)(b[off] & 0xFF | (b[off + 1] & 0xFF) << 8);
sample = (short)(sample * func[512 - i - 1] >> 9);
b[off] = (byte)(sample & 0xFF);
b[off + 1] = (byte)(sample >> 8);
off += 2;
}
}
}
public static void _in(AudioFormat fmt, byte[] samples, int[] func) {
in16BitSignedLE(samples, fmt.getChannels(), func);
}
public static void out(AudioFormat fmt, byte[] samples, int[] func) {
out16BitSignedLE(samples, fmt.getChannels(), func);
}
}

View file

@ -0,0 +1,7 @@
package org.jcodec.api;
public class FormatException extends RuntimeException {
public FormatException(String msg) {
super(msg);
}
}

View file

@ -0,0 +1,202 @@
package org.jcodec.api;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.jcodec.api.specific.AVCMP4Adaptor;
import org.jcodec.api.specific.ContainerAdaptor;
import org.jcodec.common.Codec;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.Format;
import org.jcodec.common.JCodecUtil;
import org.jcodec.common.SeekableDemuxerTrack;
import org.jcodec.common.io.FileChannelWrapper;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.Packet;
import org.jcodec.common.model.Picture;
import org.jcodec.containers.mp4.demuxer.MP4Demuxer;
public class FrameGrab {
private SeekableDemuxerTrack videoTrack;
private ContainerAdaptor decoder;
private final ThreadLocal<byte[][]> buffers;
public static FrameGrab createFrameGrab(SeekableByteChannel _in) throws IOException, JCodecException {
SeekableDemuxerTrack videoTrack_;
ByteBuffer header = ByteBuffer.allocate(65536);
_in.read(header);
header.flip();
Format detectFormat = JCodecUtil.detectFormatBuffer(header);
if (detectFormat == null)
throw new UnsupportedFormatException("Could not detect the format of the input video.");
if (Format.MOV == detectFormat) {
MP4Demuxer d1 = MP4Demuxer.createMP4Demuxer(_in);
videoTrack_ = (SeekableDemuxerTrack)d1.getVideoTrack();
} else {
if (Format.MPEG_PS == detectFormat)
throw new UnsupportedFormatException("MPEG PS is temporarily unsupported.");
if (Format.MPEG_TS == detectFormat)
throw new UnsupportedFormatException("MPEG TS is temporarily unsupported.");
throw new UnsupportedFormatException("Container format is not supported by JCodec");
}
FrameGrab fg = new FrameGrab(videoTrack_, detectDecoder(videoTrack_));
fg.decodeLeadingFrames();
return fg;
}
public FrameGrab(SeekableDemuxerTrack videoTrack, ContainerAdaptor decoder) {
this.videoTrack = videoTrack;
this.decoder = decoder;
this.buffers = new ThreadLocal<>();
}
private SeekableDemuxerTrack sdt() throws JCodecException {
if (!(this.videoTrack instanceof SeekableDemuxerTrack))
throw new JCodecException("Not a seekable track");
return this.videoTrack;
}
public FrameGrab seekToSecondPrecise(double second) throws IOException, JCodecException {
sdt().seek(second);
decodeLeadingFrames();
return this;
}
public FrameGrab seekToFramePrecise(int frameNumber) throws IOException, JCodecException {
sdt().gotoFrame((long)frameNumber);
decodeLeadingFrames();
return this;
}
public FrameGrab seekToSecondSloppy(double second) throws IOException, JCodecException {
sdt().seek(second);
goToPrevKeyframe();
return this;
}
public FrameGrab seekToFrameSloppy(int frameNumber) throws IOException, JCodecException {
sdt().gotoFrame((long)frameNumber);
goToPrevKeyframe();
return this;
}
private void goToPrevKeyframe() throws IOException, JCodecException {
sdt().gotoFrame((long)detectKeyFrame((int)sdt().getCurFrame()));
}
private void decodeLeadingFrames() throws IOException, JCodecException {
SeekableDemuxerTrack sdt = sdt();
int curFrame = (int)sdt.getCurFrame();
int keyFrame = detectKeyFrame(curFrame);
sdt.gotoFrame((long)keyFrame);
Packet frame = sdt.nextFrame();
if (this.decoder == null)
this.decoder = detectDecoder(sdt);
while (frame.getFrameNo() < (long)curFrame) {
this.decoder.decodeFrame(frame, getBuffer());
frame = sdt.nextFrame();
}
sdt.gotoFrame((long)curFrame);
}
private byte[][] getBuffer() {
byte[][] buf = this.buffers.get();
if (buf == null) {
buf = this.decoder.allocatePicture();
this.buffers.set(buf);
}
return buf;
}
private int detectKeyFrame(int start) throws IOException {
int[] seekFrames = this.videoTrack.getMeta().getSeekFrames();
if (seekFrames == null)
return start;
int prev = seekFrames[0];
for (int i = 1; i < seekFrames.length &&
seekFrames[i] <= start; i++)
prev = seekFrames[i];
return prev;
}
private static ContainerAdaptor detectDecoder(SeekableDemuxerTrack videoTrack) throws JCodecException {
DemuxerTrackMeta meta = videoTrack.getMeta();
if (Codec.H264 == meta.getCodec())
return new AVCMP4Adaptor(meta);
throw new UnsupportedFormatException("Codec is not supported");
}
public PictureWithMetadata getNativeFrameWithMetadata() throws IOException {
Packet frame = this.videoTrack.nextFrame();
if (frame == null)
return null;
Picture picture = this.decoder.decodeFrame(frame, getBuffer());
return new PictureWithMetadata(picture, frame.getPtsD(), frame.getDurationD(), this.videoTrack.getMeta().getOrientation());
}
public Picture getNativeFrame() throws IOException {
Packet frame = this.videoTrack.nextFrame();
if (frame == null)
return null;
return this.decoder.decodeFrame(frame, getBuffer());
}
public static Picture getFrameAtSec(File file, double second) throws IOException, JCodecException {
FileChannelWrapper ch = null;
try {
ch = NIOUtils.readableChannel(file);
return createFrameGrab(ch).seekToSecondPrecise(second).getNativeFrame();
} finally {
NIOUtils.closeQuietly(ch);
}
}
public static Picture getFrameFromChannelAtSec(SeekableByteChannel file, double second) throws JCodecException, IOException {
return createFrameGrab(file).seekToSecondPrecise(second).getNativeFrame();
}
public static Picture getFrameFromFile(File file, int frameNumber) throws IOException, JCodecException {
FileChannelWrapper ch = null;
try {
ch = NIOUtils.readableChannel(file);
return createFrameGrab(ch).seekToFramePrecise(frameNumber).getNativeFrame();
} finally {
NIOUtils.closeQuietly(ch);
}
}
public static Picture getFrameFromChannel(SeekableByteChannel file, int frameNumber) throws JCodecException, IOException {
return createFrameGrab(file).seekToFramePrecise(frameNumber).getNativeFrame();
}
public static Picture getNativeFrameAtFrame(SeekableDemuxerTrack vt, ContainerAdaptor decoder, int frameNumber) throws IOException, JCodecException {
return new FrameGrab(vt, decoder).seekToFramePrecise(frameNumber).getNativeFrame();
}
public static Picture getNativeFrameAtSec(SeekableDemuxerTrack vt, ContainerAdaptor decoder, double second) throws IOException, JCodecException {
return new FrameGrab(vt, decoder).seekToSecondPrecise(second).getNativeFrame();
}
public static Picture getNativeFrameSloppy(SeekableDemuxerTrack vt, ContainerAdaptor decoder, int frameNumber) throws IOException, JCodecException {
return new FrameGrab(vt, decoder).seekToFrameSloppy(frameNumber).getNativeFrame();
}
public static Picture getNativeFrameAtSecSloppy(SeekableDemuxerTrack vt, ContainerAdaptor decoder, double second) throws IOException, JCodecException {
return new FrameGrab(vt, decoder).seekToSecondSloppy(second).getNativeFrame();
}
public MediaInfo getMediaInfo() {
return this.decoder.getMediaInfo();
}
public SeekableDemuxerTrack getVideoTrack() {
return this.videoTrack;
}
public ContainerAdaptor getDecoder() {
return this.decoder;
}
}

View file

@ -0,0 +1,7 @@
package org.jcodec.api;
public class JCodecException extends Exception {
public JCodecException(String arg0) {
super(arg0);
}
}

View file

@ -0,0 +1,19 @@
package org.jcodec.api;
import org.jcodec.common.model.Size;
public class MediaInfo {
private Size dim;
public MediaInfo(Size dim) {
this.dim = dim;
}
public Size getDim() {
return this.dim;
}
public void setDim(Size dim) {
this.dim = dim;
}
}

View file

@ -0,0 +1,7 @@
package org.jcodec.api;
public class NotImplementedException extends RuntimeException {
public NotImplementedException(String string) {
super(string);
}
}

View file

@ -0,0 +1,7 @@
package org.jcodec.api;
public class NotSupportedException extends RuntimeException {
public NotSupportedException(String msg) {
super(msg);
}
}

View file

@ -0,0 +1,41 @@
package org.jcodec.api;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.model.Picture;
public class PictureWithMetadata {
private Picture picture;
private double timestamp;
private double duration;
private DemuxerTrackMeta.Orientation orientation;
public static PictureWithMetadata createPictureWithMetadata(Picture picture, double timestamp, double duration) {
return new PictureWithMetadata(picture, timestamp, duration, DemuxerTrackMeta.Orientation.D_0);
}
public PictureWithMetadata(Picture picture, double timestamp, double duration, DemuxerTrackMeta.Orientation orientation) {
this.picture = picture;
this.timestamp = timestamp;
this.duration = duration;
this.orientation = orientation;
}
public Picture getPicture() {
return this.picture;
}
public double getTimestamp() {
return this.timestamp;
}
public double getDuration() {
return this.duration;
}
public DemuxerTrackMeta.Orientation getOrientation() {
return this.orientation;
}
}

View file

@ -0,0 +1,89 @@
package org.jcodec.api;
import java.io.File;
import java.io.IOException;
import org.jcodec.api.transcode.PixelStore;
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.Picture;
import org.jcodec.common.model.Rational;
import org.jcodec.scale.ColorUtil;
import org.jcodec.scale.Transform;
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), Format.MOV, Codec.H264, null);
}
public static SequenceEncoder create25Fps(File out) throws IOException {
return new SequenceEncoder(NIOUtils.writableChannel(out), Rational.R(25, 1), Format.MOV, Codec.H264, null);
}
public static SequenceEncoder create30Fps(File out) throws IOException {
return new SequenceEncoder(NIOUtils.writableChannel(out), Rational.R(30, 1), Format.MOV, Codec.H264, null);
}
public static SequenceEncoder create2997Fps(File out) throws IOException {
return new SequenceEncoder(NIOUtils.writableChannel(out), Rational.R(30000, 1001), Format.MOV, Codec.H264, null);
}
public static SequenceEncoder create24Fps(File out) throws IOException {
return new SequenceEncoder(NIOUtils.writableChannel(out), Rational.R(24, 1), Format.MOV, Codec.H264, null);
}
public static SequenceEncoder createWithFps(SeekableByteChannel out, Rational fps) throws IOException {
return new SequenceEncoder(out, fps, Format.MOV, Codec.H264, null);
}
public SequenceEncoder(SeekableByteChannel out, Rational fps, Format outputFormat, Codec outputVideoCodec, Codec outputAudioCodec) throws IOException {
this.fps = fps;
this.sink = SinkImpl.createWithStream(out, outputFormat, outputVideoCodec, outputAudioCodec);
this.sink.init();
if (this.sink.getInputColor() != null)
this.transform = ColorUtil.getTransform(ColorSpace.RGB, this.sink.getInputColor());
this.pixelStore = new PixelStoreImpl();
}
public void encodeNativeFrame(Picture pic) throws IOException {
PixelStore.LoanerPicture toEncode;
if (pic.getColor() != ColorSpace.RGB)
throw new IllegalArgumentException("The input images is expected in RGB color.");
ColorSpace sinkColor = this.sink.getInputColor();
if (sinkColor != null) {
toEncode = this.pixelStore.getPicture(pic.getWidth(), pic.getHeight(), sinkColor);
this.transform.transform(pic, toEncode.getPicture());
} else {
toEncode = new PixelStore.LoanerPicture(pic, 0);
}
Packet pkt = Packet.createPacket(null, (long)this.timestamp, this.fps.getNum(), (long)this.fps.getDen(), (long)this.frameNo, Packet.FrameType.KEY, null);
this.sink.outputVideoFrame(new VideoFrameWithPacket(pkt, toEncode));
if (sinkColor != null)
this.pixelStore.putBack(toEncode);
this.timestamp += this.fps.getDen();
this.frameNo++;
}
public void finish() throws IOException {
this.sink.finish();
}
}

View file

@ -0,0 +1,7 @@
package org.jcodec.api;
public class UnhandledStateException extends RuntimeException {
public UnhandledStateException(String string) {
super(string);
}
}

View file

@ -0,0 +1,7 @@
package org.jcodec.api;
public class UnsupportedFormatException extends JCodecException {
public UnsupportedFormatException(String arg0) {
super(arg0);
}
}

View file

@ -0,0 +1,84 @@
package org.jcodec.api.specific;
import java.nio.ByteBuffer;
import org.jcodec.api.MediaInfo;
import org.jcodec.codecs.h264.H264Decoder;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Packet;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Rational;
import org.jcodec.common.model.Size;
import org.jcodec.containers.mp4.MP4Packet;
public class AVCMP4Adaptor implements ContainerAdaptor {
private H264Decoder decoder;
private int curENo;
private Size size;
private DemuxerTrackMeta meta;
public AVCMP4Adaptor(DemuxerTrackMeta meta) {
this.meta = meta;
this.curENo = -1;
calcBufferSize();
}
private void calcBufferSize() {
int w = Integer.MIN_VALUE, h = Integer.MIN_VALUE;
ByteBuffer bb = this.meta.getCodecPrivate().duplicate();
ByteBuffer b;
while ((b = H264Utils.nextNALUnit(bb)) != null) {
NALUnit nu = NALUnit.read(b);
if (nu.type != NALUnitType.SPS)
continue;
SeqParameterSet sps = H264Utils.readSPS(b);
int ww = sps.picWidthInMbsMinus1 + 1;
if (ww > w)
w = ww;
int hh = SeqParameterSet.getPicHeightInMbs(sps);
if (hh > h)
h = hh;
}
this.size = new Size(w << 4, h << 4);
}
public Picture decodeFrame(Packet packet, byte[][] data) {
updateState(packet);
Picture pic = this.decoder.decodeFrame(packet.getData(), data);
Rational pasp = this.meta.getVideoCodecMeta().getPixelAspectRatio();
if (pasp != null);
return pic;
}
private void updateState(Packet packet) {
int eNo = ((MP4Packet)packet).getEntryNo();
if (eNo != this.curENo)
this.curENo = eNo;
if (this.decoder == null)
this.decoder = H264Decoder.createH264DecoderFromCodecPrivate(this.meta.getCodecPrivate());
}
public boolean canSeek(Packet pkt) {
updateState(pkt);
return H264Utils.idrSlice(H264Utils.splitFrame(pkt.getData()));
}
public byte[][] allocatePicture() {
return Picture.create(this.size.getWidth(), this.size.getHeight(), ColorSpace.YUV444).getData();
}
public MediaInfo getMediaInfo() {
return new MediaInfo(this.size);
}
public void shutdownThreadPool() {
this.decoder.shutdownThreadPool();
}
}

View file

@ -0,0 +1,17 @@
package org.jcodec.api.specific;
import org.jcodec.api.MediaInfo;
import org.jcodec.common.model.Packet;
import org.jcodec.common.model.Picture;
public interface ContainerAdaptor {
Picture decodeFrame(Packet paramPacket, byte[][] paramArrayOfbyte);
boolean canSeek(Packet paramPacket);
byte[][] allocatePicture();
MediaInfo getMediaInfo();
void shutdownThreadPool();
}

View file

@ -0,0 +1,36 @@
package org.jcodec.api.specific;
import org.jcodec.api.MediaInfo;
import org.jcodec.common.VideoDecoder;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Packet;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Size;
public class GenericAdaptor implements ContainerAdaptor {
private VideoDecoder decoder;
public GenericAdaptor(VideoDecoder detect) {
this.decoder = detect;
}
public Picture decodeFrame(Packet packet, byte[][] data) {
return this.decoder.decodeFrame(packet.getData(), data);
}
public boolean canSeek(Packet data) {
return true;
}
public MediaInfo getMediaInfo() {
return new MediaInfo(new Size(0, 0));
}
public byte[][] allocatePicture() {
return Picture.create(1920, 1088, ColorSpace.YUV444).getData();
}
public void shutdownThreadPool() {
this.decoder.shutdownThreadPool();
}
}

View file

@ -0,0 +1,23 @@
package org.jcodec.api.transcode;
import org.jcodec.common.model.AudioBuffer;
import org.jcodec.common.model.Packet;
public class AudioFrameWithPacket {
private AudioBuffer audio;
private Packet packet;
public AudioFrameWithPacket(AudioBuffer audio, Packet packet) {
this.audio = audio;
this.packet = packet;
}
public AudioBuffer getAudio() {
return this.audio;
}
public Packet getPacket() {
return this.packet;
}
}

View file

@ -0,0 +1,12 @@
package org.jcodec.api.transcode;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
public interface Filter {
PixelStore.LoanerPicture filter(Picture paramPicture, PixelStore paramPixelStore);
ColorSpace getInputColor();
ColorSpace getOutputColor();
}

View file

@ -0,0 +1,5 @@
package org.jcodec.api.transcode;
public enum Options {
PROFILE, INTERLACED, DOWNSCALE;
}

View file

@ -0,0 +1,12 @@
package org.jcodec.api.transcode;
import java.io.IOException;
import org.jcodec.common.AudioCodecMeta;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.model.Packet;
public interface PacketSink {
void outputVideoPacket(Packet paramPacket, VideoCodecMeta paramVideoCodecMeta) throws IOException;
void outputAudioPacket(Packet paramPacket, AudioCodecMeta paramAudioCodecMeta) throws IOException;
}

View file

@ -0,0 +1,10 @@
package org.jcodec.api.transcode;
import java.io.IOException;
import org.jcodec.common.model.Packet;
public interface PacketSource {
Packet inputVideoPacket() throws IOException;
Packet inputAudioPacket() throws IOException;
}

View file

@ -0,0 +1,43 @@
package org.jcodec.api.transcode;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
public interface PixelStore {
LoanerPicture getPicture(int paramInt1, int paramInt2, ColorSpace paramColorSpace);
void putBack(LoanerPicture paramLoanerPicture);
void retake(LoanerPicture paramLoanerPicture);
public static class LoanerPicture {
private Picture picture;
private int refCnt;
public LoanerPicture(Picture picture, int refCnt) {
this.picture = picture;
this.refCnt = refCnt;
}
public Picture getPicture() {
return this.picture;
}
public int getRefCnt() {
return this.refCnt;
}
public void decRefCnt() {
this.refCnt--;
}
public boolean unused() {
return (this.refCnt <= 0);
}
public void incRefCnt() {
this.refCnt++;
}
}
}

View file

@ -0,0 +1,34 @@
package org.jcodec.api.transcode;
import java.util.ArrayList;
import java.util.List;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
public class PixelStoreImpl implements PixelStore {
private List<Picture> buffers = new ArrayList<>();
public PixelStore.LoanerPicture getPicture(int width, int height, ColorSpace color) {
for (Picture picture : this.buffers) {
if (picture.getWidth() == width && picture.getHeight() == height &&
picture.getColor() == color) {
this.buffers.remove(picture);
return new PixelStore.LoanerPicture(picture, 1);
}
}
return new PixelStore.LoanerPicture(Picture.create(width, height, color), 1);
}
public void putBack(PixelStore.LoanerPicture frame) {
frame.decRefCnt();
if (frame.unused()) {
Picture pixels = frame.getPicture();
pixels.setCrop(null);
this.buffers.add(pixels);
}
}
public void retake(PixelStore.LoanerPicture frame) {
frame.incRefCnt();
}
}

View file

@ -0,0 +1,22 @@
package org.jcodec.api.transcode;
import java.io.IOException;
import org.jcodec.common.model.ColorSpace;
public interface Sink {
void init() throws IOException;
void outputVideoFrame(VideoFrameWithPacket paramVideoFrameWithPacket) throws IOException;
void outputAudioFrame(AudioFrameWithPacket paramAudioFrameWithPacket) throws IOException;
void finish() throws IOException;
ColorSpace getInputColor();
void setOption(Options paramOptions, Object paramObject);
boolean isVideo();
boolean isAudio();
}

View file

@ -0,0 +1,231 @@
package org.jcodec.api.transcode;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.jcodec.codecs.h264.H264Encoder;
import org.jcodec.codecs.png.PNGEncoder;
import org.jcodec.codecs.prores.ProresEncoder;
import org.jcodec.codecs.raw.RAWVideoEncoder;
import org.jcodec.codecs.vpx.IVFMuxer;
import org.jcodec.codecs.vpx.VP8Encoder;
import org.jcodec.codecs.wav.WavMuxer;
import org.jcodec.codecs.y4m.Y4MMuxer;
import org.jcodec.common.AudioCodecMeta;
import org.jcodec.common.AudioEncoder;
import org.jcodec.common.AudioFormat;
import org.jcodec.common.Codec;
import org.jcodec.common.Format;
import org.jcodec.common.Muxer;
import org.jcodec.common.MuxerTrack;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.VideoEncoder;
import org.jcodec.common.io.IOUtils;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.AudioBuffer;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Packet;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Size;
import org.jcodec.containers.imgseq.ImageSequenceMuxer;
import org.jcodec.containers.mkv.muxer.MKVMuxer;
import org.jcodec.containers.mp4.muxer.MP4Muxer;
import org.jcodec.containers.raw.RawMuxer;
public class SinkImpl implements Sink, PacketSink {
private String destName;
private SeekableByteChannel destStream;
private Muxer muxer;
private MuxerTrack videoOutputTrack;
private MuxerTrack audioOutputTrack;
private boolean framesOutput;
private Codec outputVideoCodec;
private Codec outputAudioCodec;
private Format outputFormat;
private final ThreadLocal<ByteBuffer> bufferStore;
private AudioEncoder audioEncoder;
private VideoEncoder videoEncoder;
private String profile;
private boolean interlaced;
public void outputVideoPacket(Packet packet, VideoCodecMeta codecMeta) throws IOException {
if (!this.outputFormat.isVideo())
return;
if (this.videoOutputTrack == null)
this.videoOutputTrack = this.muxer.addVideoTrack(this.outputVideoCodec, codecMeta);
this.videoOutputTrack.addFrame(packet);
this.framesOutput = true;
}
public void outputAudioPacket(Packet audioPkt, AudioCodecMeta audioCodecMeta) throws IOException {
if (!this.outputFormat.isAudio())
return;
if (this.audioOutputTrack == null)
this.audioOutputTrack = this.muxer.addAudioTrack(this.outputAudioCodec, audioCodecMeta);
this.audioOutputTrack.addFrame(audioPkt);
this.framesOutput = true;
}
public void initMuxer() throws IOException {
if (this.destStream == null && this.outputFormat != Format.IMG)
this.destStream = NIOUtils.writableFileChannel(this.destName);
if (Format.MKV == this.outputFormat) {
this.muxer = new MKVMuxer(this.destStream);
} else if (Format.MOV == this.outputFormat) {
this.muxer = MP4Muxer.createMP4MuxerToChannel(this.destStream);
} else if (Format.IVF == this.outputFormat) {
this.muxer = new IVFMuxer(this.destStream);
} else if (Format.IMG == this.outputFormat) {
this.muxer = new ImageSequenceMuxer(this.destName);
} else if (Format.WAV == this.outputFormat) {
this.muxer = new WavMuxer(this.destStream);
} else if (Format.Y4M == this.outputFormat) {
this.muxer = new Y4MMuxer(this.destStream);
} else if (Format.RAW == this.outputFormat) {
this.muxer = new RawMuxer(this.destStream);
} else {
throw new RuntimeException("The output format " + String.valueOf(this.outputFormat) + " is not supported.");
}
}
public void finish() throws IOException {
if (this.framesOutput) {
this.muxer.finish();
} else {
Logger.warn("No frames output.");
}
if (this.destStream != null)
IOUtils.closeQuietly(this.destStream);
}
public SinkImpl(String destName, Format outputFormat, Codec outputVideoCodec, Codec outputAudioCodec) {
if (destName == null && outputFormat == Format.IMG)
throw new IllegalArgumentException("A destination file should be specified for the image muxer.");
this.destName = destName;
this.outputFormat = outputFormat;
this.outputVideoCodec = outputVideoCodec;
this.outputAudioCodec = outputAudioCodec;
this.outputFormat = outputFormat;
this.bufferStore = new ThreadLocal<>();
}
public static SinkImpl createWithStream(SeekableByteChannel destStream, Format outputFormat, Codec outputVideoCodec, Codec outputAudioCodec) {
SinkImpl result = new SinkImpl(null, outputFormat, outputVideoCodec, outputAudioCodec);
result.destStream = destStream;
return result;
}
public void init() throws IOException {
initMuxer();
if (this.outputFormat.isVideo() && this.outputVideoCodec != null)
if (Codec.PRORES == this.outputVideoCodec) {
this.videoEncoder = ProresEncoder.createProresEncoder(this.profile, this.interlaced);
} else if (Codec.H264 == this.outputVideoCodec) {
this.videoEncoder = H264Encoder.createH264Encoder();
} else if (Codec.VP8 == this.outputVideoCodec) {
this.videoEncoder = VP8Encoder.createVP8Encoder(10);
} else if (Codec.PNG == this.outputVideoCodec) {
this.videoEncoder = new PNGEncoder();
} else if (Codec.RAW == this.outputVideoCodec) {
this.videoEncoder = new RAWVideoEncoder();
} else {
throw new RuntimeException("Could not find encoder for the codec: " + String.valueOf(this.outputVideoCodec));
}
}
protected VideoEncoder.EncodedFrame encodeVideo(Picture frame, ByteBuffer _out) {
if (!this.outputFormat.isVideo())
return null;
return this.videoEncoder.encodeFrame(frame, _out);
}
private AudioEncoder createAudioEncoder(Codec codec, AudioFormat format) {
if (codec != Codec.PCM)
throw new RuntimeException("Only PCM audio encoding (RAW audio) is supported.");
return new RawAudioEncoder();
}
private static class RawAudioEncoder implements AudioEncoder {
public ByteBuffer encode(ByteBuffer audioPkt, ByteBuffer buf) {
return audioPkt;
}
}
protected ByteBuffer encodeAudio(AudioBuffer audioBuffer) {
if (this.audioEncoder == null) {
AudioFormat format = audioBuffer.getFormat();
this.audioEncoder = createAudioEncoder(this.outputAudioCodec, format);
}
return this.audioEncoder.encode(audioBuffer.getData(), null);
}
public void setProfile(String profile) {
this.profile = profile;
}
public void setInterlaced(Boolean interlaced) {
this.interlaced = interlaced;
}
public void outputVideoFrame(VideoFrameWithPacket videoFrame) throws IOException {
if (!this.outputFormat.isVideo() || this.outputVideoCodec == null)
return;
ByteBuffer buffer = this.bufferStore.get();
int bufferSize = this.videoEncoder.estimateBufferSize(videoFrame.getFrame().getPicture());
if (buffer == null || bufferSize < buffer.capacity()) {
buffer = ByteBuffer.allocate(bufferSize);
this.bufferStore.set(buffer);
}
buffer.clear();
Picture frame = videoFrame.getFrame().getPicture();
VideoEncoder.EncodedFrame enc = encodeVideo(frame, buffer);
Packet outputVideoPacket = Packet.createPacketWithData(videoFrame.getPacket(), NIOUtils.clone(enc.getData()));
outputVideoPacket.setFrameType(enc.isKeyFrame() ? Packet.FrameType.KEY : Packet.FrameType.INTER);
outputVideoPacket(outputVideoPacket,
VideoCodecMeta.createSimpleVideoCodecMeta(new Size(frame.getWidth(), frame.getHeight()), frame.getColor()));
}
public void outputAudioFrame(AudioFrameWithPacket audioFrame) throws IOException {
if (!this.outputFormat.isAudio() || this.outputAudioCodec == null)
return;
outputAudioPacket(Packet.createPacketWithData(audioFrame.getPacket(), encodeAudio(audioFrame.getAudio())),
AudioCodecMeta.fromAudioFormat(audioFrame.getAudio().getFormat()));
}
public ColorSpace getInputColor() {
if (this.videoEncoder == null)
throw new IllegalStateException("Video encoder has not been initialized, init() must be called before using this class.");
ColorSpace[] colorSpaces = this.videoEncoder.getSupportedColorSpaces();
return (colorSpaces == null) ? null : colorSpaces[0];
}
public void setOption(Options option, Object value) {
if (option == Options.PROFILE) {
this.profile = (String)value;
} else if (option == Options.INTERLACED) {
this.interlaced = (Boolean)value;
}
}
public boolean isVideo() {
return this.outputFormat.isVideo();
}
public boolean isAudio() {
return this.outputFormat.isAudio();
}
}

View file

@ -0,0 +1,29 @@
package org.jcodec.api.transcode;
import java.io.IOException;
import org.jcodec.common.AudioCodecMeta;
import org.jcodec.common.VideoCodecMeta;
public interface Source {
void init(PixelStore paramPixelStore) throws IOException;
void seekFrames(int paramInt) throws IOException;
VideoFrameWithPacket getNextVideoFrame() throws IOException;
AudioFrameWithPacket getNextAudioFrame() throws IOException;
void finish();
boolean haveAudio();
void setOption(Options paramOptions, Object paramObject);
VideoCodecMeta getVideoCodecMeta();
AudioCodecMeta getAudioCodecMeta();
boolean isVideo();
boolean isAudio();
}

View file

@ -0,0 +1,463 @@
package org.jcodec.api.transcode;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import net.sourceforge.jaad.aac.AACException;
import org.jcodec.codecs.aac.AACDecoder;
import org.jcodec.codecs.h264.BufferH264ES;
import org.jcodec.codecs.h264.H264Decoder;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.mjpeg.JpegDecoder;
import org.jcodec.codecs.mjpeg.JpegToThumb2x2;
import org.jcodec.codecs.mjpeg.JpegToThumb4x4;
import org.jcodec.codecs.mpeg12.MPEGDecoder;
import org.jcodec.codecs.mpeg12.Mpeg2Thumb2x2;
import org.jcodec.codecs.mpeg12.Mpeg2Thumb4x4;
import org.jcodec.codecs.mpeg4.MPEG4Decoder;
import org.jcodec.codecs.png.PNGDecoder;
import org.jcodec.codecs.prores.ProresDecoder;
import org.jcodec.codecs.prores.ProresToThumb;
import org.jcodec.codecs.prores.ProresToThumb2x2;
import org.jcodec.codecs.prores.ProresToThumb4x4;
import org.jcodec.codecs.raw.RAWVideoDecoder;
import org.jcodec.codecs.vpx.VP8Decoder;
import org.jcodec.codecs.wav.WavDemuxer;
import org.jcodec.common.AudioCodecMeta;
import org.jcodec.common.AudioDecoder;
import org.jcodec.common.AudioFormat;
import org.jcodec.common.Codec;
import org.jcodec.common.Demuxer;
import org.jcodec.common.DemuxerTrack;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.Format;
import org.jcodec.common.SeekableDemuxerTrack;
import org.jcodec.common.Tuple;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.VideoDecoder;
import org.jcodec.common.io.IOUtils;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.AudioBuffer;
import org.jcodec.common.model.Packet;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Size;
import org.jcodec.containers.imgseq.ImageSequenceDemuxer;
import org.jcodec.containers.mkv.demuxer.MKVDemuxer;
import org.jcodec.containers.mp3.MPEGAudioDemuxer;
import org.jcodec.containers.mp4.demuxer.MP4Demuxer;
import org.jcodec.containers.mps.MPEGDemuxer;
import org.jcodec.containers.mps.MPSDemuxer;
import org.jcodec.containers.mps.MTSDemuxer;
import org.jcodec.containers.webp.WebpDemuxer;
import org.jcodec.containers.y4m.Y4MDemuxer;
public class SourceImpl implements Source, PacketSource {
private String sourceName;
private SeekableByteChannel sourceStream;
private Demuxer demuxVideo;
private Demuxer demuxAudio;
private Format inputFormat;
private DemuxerTrack videoInputTrack;
private DemuxerTrack audioInputTrack;
private Tuple._3<Integer, Integer, Codec> inputVideoCodec;
private Tuple._3<Integer, Integer, Codec> inputAudioCodec;
private List<VideoFrameWithPacket> frameReorderBuffer;
private List<Packet> videoPacketReorderBuffer;
private PixelStore pixelStore;
private VideoCodecMeta videoCodecMeta;
private AudioCodecMeta audioCodecMeta;
private AudioDecoder audioDecoder;
private VideoDecoder videoDecoder;
private int downscale = 1;
public static MPEGDecoder createMpegDecoder(int downscale) {
if (downscale == 2)
return new Mpeg2Thumb4x4();
if (downscale == 4)
return new Mpeg2Thumb2x2();
return new MPEGDecoder();
}
public static ProresDecoder createProresDecoder(int downscale) {
if (2 == downscale)
return new ProresToThumb4x4();
if (4 == downscale)
return new ProresToThumb2x2();
if (8 == downscale)
return new ProresToThumb();
return new ProresDecoder();
}
public void initDemuxer() throws FileNotFoundException, IOException {
if (this.inputFormat != Format.IMG)
this.sourceStream = NIOUtils.readableFileChannel(this.sourceName);
if (Format.MOV == this.inputFormat) {
this.demuxVideo = this.demuxAudio = MP4Demuxer.createMP4Demuxer(this.sourceStream);
} else if (Format.MKV == this.inputFormat) {
this.demuxVideo = this.demuxAudio = new MKVDemuxer(this.sourceStream);
} else if (Format.IMG == this.inputFormat) {
this.demuxVideo = new ImageSequenceDemuxer(this.sourceName, Integer.MAX_VALUE);
} else if (Format.WEBP == this.inputFormat) {
this.demuxVideo = new WebpDemuxer(this.sourceStream);
} else if (Format.MPEG_PS == this.inputFormat) {
this.demuxVideo = this.demuxAudio = new MPSDemuxer(this.sourceStream);
} else if (Format.Y4M == this.inputFormat) {
Y4MDemuxer y4mDemuxer = new Y4MDemuxer(this.sourceStream);
this.demuxVideo = this.demuxAudio = y4mDemuxer;
this.videoInputTrack = y4mDemuxer;
} else if (Format.H264 == this.inputFormat) {
this.demuxVideo = new BufferH264ES(NIOUtils.fetchAllFromChannel(this.sourceStream));
} else if (Format.WAV == this.inputFormat) {
this.demuxAudio = new WavDemuxer(this.sourceStream);
} else if (Format.MPEG_AUDIO == this.inputFormat) {
this.demuxAudio = new MPEGAudioDemuxer(this.sourceStream);
} else if (Format.MPEG_TS == this.inputFormat) {
MTSDemuxer mtsDemuxer = new MTSDemuxer(this.sourceStream);
MPSDemuxer mpsDemuxer = null;
if (this.inputVideoCodec != null) {
mpsDemuxer = new MPSDemuxer(mtsDemuxer.getProgram(((Integer)this.inputVideoCodec.v0).intValue()));
this.videoInputTrack = openTSTrack(mpsDemuxer, (Integer)this.inputVideoCodec.v1);
this.demuxVideo = mpsDemuxer;
}
if (this.inputAudioCodec != null) {
if (this.inputVideoCodec == null || this.inputVideoCodec.v0 != this.inputAudioCodec.v0)
mpsDemuxer = new MPSDemuxer(mtsDemuxer.getProgram(((Integer)this.inputAudioCodec.v0).intValue()));
this.audioInputTrack = openTSTrack(mpsDemuxer, (Integer)this.inputAudioCodec.v1);
this.demuxAudio = mpsDemuxer;
}
for (Iterator<Integer> iterator = mtsDemuxer.getPrograms().iterator(); iterator.hasNext(); ) {
int pid = iterator.next();
if ((this.inputVideoCodec == null || pid != (Integer)this.inputVideoCodec.v0) && (this.inputAudioCodec == null || pid != (Integer)this.inputAudioCodec.v0)) {
Logger.info("Unused program: " + pid);
mtsDemuxer.getProgram(pid).close();
}
}
} else {
throw new RuntimeException("Input format: " + String.valueOf(this.inputFormat) + " is not supported.");
}
if (this.demuxVideo != null && this.inputVideoCodec != null) {
List<? extends DemuxerTrack> videoTracks = this.demuxVideo.getVideoTracks();
if (videoTracks.size() > 0)
this.videoInputTrack = videoTracks.get(((Integer)this.inputVideoCodec.v1).intValue());
}
if (this.demuxAudio != null && this.inputAudioCodec != null) {
List<? extends DemuxerTrack> audioTracks = this.demuxAudio.getAudioTracks();
if (audioTracks.size() > 0)
this.audioInputTrack = audioTracks.get(((Integer)this.inputAudioCodec.v1).intValue());
}
}
protected int seekToKeyFrame(int frame) throws IOException {
if (this.videoInputTrack instanceof SeekableDemuxerTrack) {
SeekableDemuxerTrack seekable = (SeekableDemuxerTrack)this.videoInputTrack;
seekable.gotoSyncFrame((long)frame);
return (int)seekable.getCurFrame();
}
Logger.warn("Can not seek in " + String.valueOf(this.videoInputTrack) + " container.");
return -1;
}
private MPEGDemuxer.MPEGDemuxerTrack openTSTrack(MPSDemuxer demuxerVideo, Integer selectedTrack) {
int trackNo = 0;
for (MPEGDemuxer.MPEGDemuxerTrack track : demuxerVideo.getTracks()) {
if (trackNo == selectedTrack)
return track;
track.ignore();
trackNo++;
}
return null;
}
public Packet inputVideoPacket() throws IOException {
Packet packet;
do {
packet = getNextVideoPacket();
if (packet == null)
continue;
this.videoPacketReorderBuffer.add(packet);
} while (packet != null && this.videoPacketReorderBuffer.size() <= 7);
if (this.videoPacketReorderBuffer.size() == 0)
return null;
Packet out = (Packet)this.videoPacketReorderBuffer.remove(0);
int duration = Integer.MAX_VALUE;
for (Packet packet2 : this.videoPacketReorderBuffer) {
int cand = (int)(packet2.getPts() - out.getPts());
if (cand > 0 && cand < duration)
duration = cand;
}
if (duration != Integer.MAX_VALUE)
out.setDuration((long)duration);
return out;
}
private Packet getNextVideoPacket() throws IOException {
if (this.videoInputTrack == null)
return null;
Packet nextFrame = this.videoInputTrack.nextFrame();
if (this.videoDecoder == null) {
this.videoDecoder = createVideoDecoder((Codec)this.inputVideoCodec.v2, this.downscale, nextFrame.getData(), null);
if (this.videoDecoder != null)
this.videoCodecMeta = this.videoDecoder.getCodecMeta(nextFrame.getData());
}
return nextFrame;
}
public Packet inputAudioPacket() throws IOException {
if (this.audioInputTrack == null)
return null;
Packet audioPkt = this.audioInputTrack.nextFrame();
if (this.audioDecoder == null && audioPkt != null) {
this.audioDecoder = createAudioDecoder(audioPkt.getData());
if (this.audioDecoder != null)
this.audioCodecMeta = this.audioDecoder.getCodecMeta(audioPkt.getData());
}
return audioPkt;
}
public DemuxerTrackMeta getTrackVideoMeta() {
if (this.videoInputTrack == null)
return null;
return this.videoInputTrack.getMeta();
}
public DemuxerTrackMeta getAudioMeta() {
if (this.audioInputTrack == null)
return null;
return this.audioInputTrack.getMeta();
}
public boolean haveAudio() {
return (this.audioInputTrack != null);
}
public void finish() {
if (this.sourceStream != null)
IOUtils.closeQuietly(this.sourceStream);
}
public SourceImpl(String sourceName, Format inputFormat, Tuple._3<Integer, Integer, Codec> inputVideoCodec, Tuple._3<Integer, Integer, Codec> inputAudioCodec) {
this.sourceName = sourceName;
this.inputFormat = inputFormat;
this.inputVideoCodec = inputVideoCodec;
this.inputAudioCodec = inputAudioCodec;
this.frameReorderBuffer = new ArrayList<>();
this.videoPacketReorderBuffer = new ArrayList<>();
}
public void init(PixelStore pixelStore) throws IOException {
this.pixelStore = pixelStore;
initDemuxer();
}
private AudioDecoder createAudioDecoder(ByteBuffer codecPrivate) throws AACException {
if (Codec.AAC == this.inputAudioCodec.v2)
return new AACDecoder(codecPrivate);
if (Codec.PCM == this.inputAudioCodec.v2)
return new RawAudioDecoder(getAudioMeta().getAudioCodecMeta().getFormat());
return null;
}
private VideoDecoder createVideoDecoder(Codec codec, int downscale, ByteBuffer codecPrivate, VideoCodecMeta videoCodecMeta) {
if (Codec.H264 == codec)
return H264Decoder.createH264DecoderFromCodecPrivate(codecPrivate);
if (Codec.PNG == codec)
return new PNGDecoder();
if (Codec.MPEG2 == codec)
return createMpegDecoder(downscale);
if (Codec.PRORES == codec)
return createProresDecoder(downscale);
if (Codec.VP8 == codec)
return new VP8Decoder();
if (Codec.JPEG == codec)
return createJpegDecoder(downscale);
if (Codec.MPEG4 == codec)
return new MPEG4Decoder();
if (Codec.RAW == codec) {
Size dim = videoCodecMeta.getSize();
return new RAWVideoDecoder(dim.getWidth(), dim.getHeight());
}
return null;
}
public Picture decodeVideo(ByteBuffer data, Picture target1) {
return this.videoDecoder.decodeFrame(data, target1.getData());
}
protected ByteBuffer decodeAudio(ByteBuffer audioPkt) throws IOException {
if (this.inputAudioCodec.v2 == Codec.PCM)
return audioPkt;
AudioBuffer decodeFrame = this.audioDecoder.decodeFrame(audioPkt, null);
return decodeFrame.getData();
}
private static class RawAudioDecoder implements AudioDecoder {
private AudioFormat format;
public RawAudioDecoder(AudioFormat format) {
this.format = format;
}
public AudioBuffer decodeFrame(ByteBuffer frame, ByteBuffer dst) throws IOException {
return new AudioBuffer(frame, this.format, frame.remaining() / this.format.getFrameSize());
}
public AudioCodecMeta getCodecMeta(ByteBuffer data) throws IOException {
return AudioCodecMeta.fromAudioFormat(this.format);
}
}
public void seekFrames(int seekFrames) throws IOException {
if (seekFrames == 0)
return;
int skipFrames = seekFrames - seekToKeyFrame(seekFrames);
Packet inVideoPacket;
while (skipFrames > 0 && (inVideoPacket = getNextVideoPacket()) != null) {
PixelStore.LoanerPicture loanerBuffer = getPixelBuffer(inVideoPacket.getData());
Picture decodedFrame = decodeVideo(inVideoPacket.getData(), loanerBuffer.getPicture());
if (decodedFrame == null) {
this.pixelStore.putBack(loanerBuffer);
continue;
}
this.frameReorderBuffer.add(new VideoFrameWithPacket(inVideoPacket, new PixelStore.LoanerPicture(decodedFrame, 1)));
if (this.frameReorderBuffer.size() > 7) {
Collections.sort(this.frameReorderBuffer);
VideoFrameWithPacket removed = (VideoFrameWithPacket)this.frameReorderBuffer.remove(0);
skipFrames--;
if (removed.getFrame() != null)
this.pixelStore.putBack(removed.getFrame());
}
}
}
private void detectFrameType(Packet inVideoPacket) {
if (this.inputVideoCodec.v2 != Codec.H264)
return;
inVideoPacket.setFrameType(
H264Utils.isByteBufferIDRSlice(inVideoPacket.getData()) ? Packet.FrameType.KEY : Packet.FrameType.INTER);
}
protected PixelStore.LoanerPicture getPixelBuffer(ByteBuffer firstFrame) {
VideoCodecMeta videoMeta = getVideoCodecMeta();
Size size = videoMeta.getSize();
return this.pixelStore.getPicture(size.getWidth() + 15 & 0xFFFFFFF0, size.getHeight() + 15 & 0xFFFFFFF0,
videoMeta.getColor());
}
public VideoCodecMeta getVideoCodecMeta() {
if (this.videoCodecMeta != null)
return this.videoCodecMeta;
DemuxerTrackMeta meta = getTrackVideoMeta();
if (meta != null && meta.getVideoCodecMeta() != null)
this.videoCodecMeta = meta.getVideoCodecMeta();
return this.videoCodecMeta;
}
public VideoFrameWithPacket getNextVideoFrame() throws IOException {
Packet inVideoPacket;
while ((inVideoPacket = getNextVideoPacket()) != null) {
if (inVideoPacket.getFrameType() == Packet.FrameType.UNKNOWN)
detectFrameType(inVideoPacket);
Picture decodedFrame = null;
PixelStore.LoanerPicture pixelBuffer = getPixelBuffer(inVideoPacket.getData());
decodedFrame = decodeVideo(inVideoPacket.getData(), pixelBuffer.getPicture());
if (decodedFrame == null) {
this.pixelStore.putBack(pixelBuffer);
continue;
}
this.frameReorderBuffer.add(new VideoFrameWithPacket(inVideoPacket, new PixelStore.LoanerPicture(decodedFrame, 1)));
if (this.frameReorderBuffer.size() > 7)
return removeFirstFixDuration(this.frameReorderBuffer);
}
if (this.frameReorderBuffer.size() > 0)
return removeFirstFixDuration(this.frameReorderBuffer);
return null;
}
private VideoFrameWithPacket removeFirstFixDuration(List<VideoFrameWithPacket> reorderBuffer) {
Collections.sort(reorderBuffer);
VideoFrameWithPacket frame = (VideoFrameWithPacket)reorderBuffer.remove(0);
if (!reorderBuffer.isEmpty()) {
VideoFrameWithPacket nextFrame = reorderBuffer.get(0);
frame.getPacket().setDuration(nextFrame.getPacket().getPts() - frame.getPacket().getPts());
}
return frame;
}
public AudioFrameWithPacket getNextAudioFrame() throws IOException {
AudioBuffer audioBuffer;
Packet audioPkt = inputAudioPacket();
if (audioPkt == null)
return null;
if (this.inputAudioCodec.v2 == Codec.PCM) {
DemuxerTrackMeta audioMeta = getAudioMeta();
audioBuffer = new AudioBuffer(audioPkt.getData(), audioMeta.getAudioCodecMeta().getFormat(), audioMeta.getTotalFrames());
} else {
audioBuffer = this.audioDecoder.decodeFrame(audioPkt.getData(), null);
}
return new AudioFrameWithPacket(audioBuffer, audioPkt);
}
public Tuple._3<Integer, Integer, Codec> getIntputVideoCodec() {
return this.inputVideoCodec;
}
public Tuple._3<Integer, Integer, Codec> getInputAudioCode() {
return this.inputAudioCodec;
}
public void setOption(Options option, Object value) {
if (option == Options.DOWNSCALE)
this.downscale = (Integer)value;
}
public AudioCodecMeta getAudioCodecMeta() {
if (this.audioInputTrack != null && this.audioInputTrack.getMeta() != null &&
this.audioInputTrack.getMeta().getAudioCodecMeta() != null)
return this.audioInputTrack.getMeta().getAudioCodecMeta();
return this.audioCodecMeta;
}
public boolean isVideo() {
if (!this.inputFormat.isVideo())
return false;
List<? extends DemuxerTrack> tracks = this.demuxVideo.getVideoTracks();
return (tracks != null && tracks.size() > 0);
}
public boolean isAudio() {
if (!this.inputFormat.isAudio())
return false;
List<? extends DemuxerTrack> tracks = this.demuxAudio.getAudioTracks();
return (tracks != null && tracks.size() > 0);
}
public static JpegDecoder createJpegDecoder(int downscale) {
if (downscale == 2)
return new JpegToThumb4x4();
if (downscale == 4)
return new JpegToThumb2x2();
return new JpegDecoder();
}
}

View file

@ -0,0 +1,396 @@
package org.jcodec.api.transcode;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jcodec.api.transcode.filters.DumpMvFilter;
import org.jcodec.api.transcode.filters.ScaleFilter;
import org.jcodec.common.Codec;
import org.jcodec.common.Demuxer;
import org.jcodec.common.DemuxerTrack;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.Format;
import org.jcodec.common.JCodecUtil;
import org.jcodec.common.TrackType;
import org.jcodec.common.Tuple;
import org.jcodec.common.logging.LogLevel;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.logging.OutLogSink;
import org.jcodec.common.model.Packet;
import org.jcodec.common.tools.MainUtils;
import org.jcodec.common.tools.MathUtil;
import org.jcodec.platform.Platform;
public class TranscodeMain {
private static final MainUtils.Flag FLAG_INPUT = new MainUtils.Flag("input", "i", "Designates an input argument", MainUtils.FlagType.VOID);
private static final MainUtils.Flag FLAG_MAP_VIDEO = MainUtils.Flag.flag("map:v", "mv", "Map a video from a specified input into this output");
private static final MainUtils.Flag FLAG_MAP_AUDIO = MainUtils.Flag.flag("map:a", "ma", "Map a audio from a specified input into this output");
private static final MainUtils.Flag FLAG_SEEK_FRAMES = MainUtils.Flag.flag("seek-frames", null, "Seek frames");
private static final MainUtils.Flag FLAG_MAX_FRAMES = MainUtils.Flag.flag("max-frames", "limit", "Max frames");
private static final MainUtils.Flag FLAG_AUDIO_CODEC = MainUtils.Flag.flag("codec:audio", "acodec", "Audio codec [default=auto].");
private static final MainUtils.Flag FLAG_VIDEO_CODEC = MainUtils.Flag.flag("codec:video", "vcodec", "Video codec [default=auto].");
private static final MainUtils.Flag FLAG_FORMAT = MainUtils.Flag.flag("format", "f", "Format [default=auto].");
private static final MainUtils.Flag FLAG_PROFILE = MainUtils.Flag.flag("profile", null, "Profile to use (supported by some encoders).");
private static final MainUtils.Flag FLAG_INTERLACED = MainUtils.Flag.flag("interlaced", null, "Encode output as interlaced (supported by Prores encoder).");
private static final MainUtils.Flag FLAG_DUMPMV = MainUtils.Flag.flag("dumpMv", null, "Dump motion vectors (supported by h.264 decoder).");
private static final MainUtils.Flag FLAG_DUMPMVJS = MainUtils.Flag.flag("dumpMvJs", null, "Dump motion vectors in form of JASON file (supported by h.264 decoder).");
private static final MainUtils.Flag FLAG_DOWNSCALE = MainUtils.Flag.flag("downscale", null, "Decode frames in downscale (supported by MPEG, Prores and Jpeg decoders).");
private static final MainUtils.Flag FLAG_VIDEO_FILTER = MainUtils.Flag.flag("videoFilter", "vf", "Contains a comma separated list of video filters with arguments.");
private static final MainUtils.Flag[] ALL_FLAGS = new MainUtils.Flag[] {
FLAG_INPUT, FLAG_FORMAT, FLAG_VIDEO_CODEC, FLAG_AUDIO_CODEC, FLAG_SEEK_FRAMES, FLAG_MAX_FRAMES, FLAG_PROFILE, FLAG_INTERLACED, FLAG_DUMPMV, FLAG_DUMPMVJS,
FLAG_DOWNSCALE, FLAG_MAP_VIDEO, FLAG_MAP_AUDIO, FLAG_VIDEO_FILTER };
private static Map<String, Format> extensionToF = new HashMap<>();
private static Map<String, Codec> extensionToC = new HashMap<>();
private static Map<Format, Codec> videoCodecsForF = new HashMap<>();
private static Map<Format, Codec> audioCodecsForF = new HashMap<>();
private static Set<Codec> supportedDecoders = new HashSet<>();
private static Map<String, Class<? extends Filter>> knownFilters = new HashMap<>();
static {
extensionToF.put("mp3", Format.MPEG_AUDIO);
extensionToF.put("mp2", Format.MPEG_AUDIO);
extensionToF.put("mp1", Format.MPEG_AUDIO);
extensionToF.put("mpg", Format.MPEG_PS);
extensionToF.put("mpeg", Format.MPEG_PS);
extensionToF.put("m2p", Format.MPEG_PS);
extensionToF.put("ps", Format.MPEG_PS);
extensionToF.put("vob", Format.MPEG_PS);
extensionToF.put("evo", Format.MPEG_PS);
extensionToF.put("mod", Format.MPEG_PS);
extensionToF.put("tod", Format.MPEG_PS);
extensionToF.put("ts", Format.MPEG_TS);
extensionToF.put("m2t", Format.MPEG_TS);
extensionToF.put("mp4", Format.MOV);
extensionToF.put("m4a", Format.MOV);
extensionToF.put("m4v", Format.MOV);
extensionToF.put("mov", Format.MOV);
extensionToF.put("3gp", Format.MOV);
extensionToF.put("mkv", Format.MKV);
extensionToF.put("webm", Format.MKV);
extensionToF.put("264", Format.H264);
extensionToF.put("jsv", Format.H264);
extensionToF.put("h264", Format.H264);
extensionToF.put("raw", Format.RAW);
extensionToF.put("", Format.RAW);
extensionToF.put("flv", Format.FLV);
extensionToF.put("avi", Format.AVI);
extensionToF.put("jpg", Format.IMG);
extensionToF.put("jpeg", Format.IMG);
extensionToF.put("png", Format.IMG);
extensionToF.put("mjp", Format.MJPEG);
extensionToF.put("ivf", Format.IVF);
extensionToF.put("y4m", Format.Y4M);
extensionToF.put("wav", Format.WAV);
extensionToC.put("mpg", Codec.MPEG2);
extensionToC.put("mpeg", Codec.MPEG2);
extensionToC.put("m2p", Codec.MPEG2);
extensionToC.put("ps", Codec.MPEG2);
extensionToC.put("vob", Codec.MPEG2);
extensionToC.put("evo", Codec.MPEG2);
extensionToC.put("mod", Codec.MPEG2);
extensionToC.put("tod", Codec.MPEG2);
extensionToC.put("ts", Codec.MPEG2);
extensionToC.put("m2t", Codec.MPEG2);
extensionToC.put("m4a", Codec.AAC);
extensionToC.put("mkv", Codec.H264);
extensionToC.put("webm", Codec.VP8);
extensionToC.put("264", Codec.H264);
extensionToC.put("raw", Codec.RAW);
extensionToC.put("jpg", Codec.JPEG);
extensionToC.put("jpeg", Codec.JPEG);
extensionToC.put("png", Codec.PNG);
extensionToC.put("mjp", Codec.JPEG);
extensionToC.put("y4m", Codec.RAW);
videoCodecsForF.put(Format.MPEG_PS, Codec.MPEG2);
audioCodecsForF.put(Format.MPEG_PS, Codec.MP2);
videoCodecsForF.put(Format.MOV, Codec.H264);
audioCodecsForF.put(Format.MOV, Codec.AAC);
videoCodecsForF.put(Format.MKV, Codec.VP8);
audioCodecsForF.put(Format.MKV, Codec.VORBIS);
audioCodecsForF.put(Format.WAV, Codec.PCM);
videoCodecsForF.put(Format.H264, Codec.H264);
videoCodecsForF.put(Format.RAW, Codec.RAW);
videoCodecsForF.put(Format.FLV, Codec.H264);
videoCodecsForF.put(Format.AVI, Codec.MPEG4);
videoCodecsForF.put(Format.IMG, Codec.PNG);
videoCodecsForF.put(Format.MJPEG, Codec.JPEG);
videoCodecsForF.put(Format.IVF, Codec.VP8);
videoCodecsForF.put(Format.Y4M, Codec.RAW);
supportedDecoders.add(Codec.AAC);
supportedDecoders.add(Codec.H264);
supportedDecoders.add(Codec.JPEG);
supportedDecoders.add(Codec.MPEG2);
supportedDecoders.add(Codec.PCM);
supportedDecoders.add(Codec.PNG);
supportedDecoders.add(Codec.MPEG4);
supportedDecoders.add(Codec.PRORES);
supportedDecoders.add(Codec.RAW);
supportedDecoders.add(Codec.VP8);
supportedDecoders.add(Codec.MP3);
supportedDecoders.add(Codec.MP2);
supportedDecoders.add(Codec.MP1);
knownFilters.put("scale", ScaleFilter.class);
}
public static void main(String[] args) throws Exception {
Logger.addSink(new OutLogSink(System.out, new OutLogSink.SimpleFormat("#message"), LogLevel.INFO));
MainUtils.Cmd cmd = MainUtils.parseArguments(args, ALL_FLAGS);
Transcoder.TranscoderBuilder builder = Transcoder.newTranscoder();
List<Source> sources = new ArrayList<>();
List<Tuple._3<Integer, Integer, Codec>> inputCodecsVideo = new ArrayList<>();
List<Tuple._3<Integer, Integer, Codec>> inputCodecsAudio = new ArrayList<>();
for (int index = 0; index < cmd.argsLength(); index++) {
if (cmd.getBooleanFlagI(index, FLAG_INPUT)) {
Format inputFormat;
Tuple._3<Integer, Integer, Codec> inputCodecVideo = null, inputCodecAudio = null;
String input = cmd.getArg(index);
String inputFormatRaw = cmd.getStringFlagI(index, FLAG_FORMAT);
if (inputFormatRaw == null) {
inputFormat = getFormatFromExtension(input);
if (inputFormat != Format.IMG) {
Format detectFormat = JCodecUtil.detectFormat(new File(input));
if (detectFormat != null)
inputFormat = detectFormat;
}
} else {
inputFormat = Format.valueOf(inputFormatRaw.toUpperCase());
}
if (inputFormat == null) {
Logger.error("Input format could not be detected");
return;
}
Logger.info(String.format("Input stream %d: %s", index, String.valueOf(inputFormat)));
int videoTrackNo = -1;
String inputCodecVideoRaw = cmd.getStringFlagI(index, FLAG_VIDEO_CODEC);
if (inputCodecVideoRaw == null) {
if (inputFormat == Format.IMG) {
inputCodecVideo = Tuple.triple(Integer.valueOf(0), Integer.valueOf(0), getCodecFromExtension(input));
} else if (inputFormat.isVideo()) {
inputCodecVideo = selectSuitableTrack(input, inputFormat, TrackType.VIDEO);
}
} else {
inputCodecVideo = Tuple.triple(Integer.valueOf(0), Integer.valueOf(0), Codec.valueOf(inputCodecVideoRaw.toUpperCase()));
}
if (inputCodecVideo != null)
if (inputFormat == Format.MPEG_TS) {
Logger.info(String.format("Video codec: %s[pid=%d,stream=%d]", String.valueOf(inputCodecVideo.v2), inputCodecVideo.v0, inputCodecVideo.v1));
} else {
Logger.info(String.format("Video codec: %s", String.valueOf(inputCodecVideo.v2)));
}
String inputCodecAudioRaw = cmd.getStringFlagI(index, FLAG_AUDIO_CODEC);
if (inputCodecAudioRaw == null) {
if (inputFormat.isAudio())
inputCodecAudio = selectSuitableTrack(input, inputFormat, TrackType.AUDIO);
} else {
inputCodecAudio = Tuple.triple(Integer.valueOf(0), Integer.valueOf(0), Codec.valueOf(inputCodecAudioRaw.toUpperCase()));
}
if (inputCodecAudio != null)
if (inputFormat == Format.MPEG_TS) {
Logger.info(String.format("Audio codec: %s[pid=%d,stream=%d]", String.valueOf(inputCodecAudio.v2), inputCodecAudio.v0, inputCodecAudio.v1));
} else {
Logger.info(String.format("Audio codec: %s", String.valueOf(inputCodecAudio.v2)));
}
Source source = new SourceImpl(input, inputFormat, inputCodecVideo, inputCodecAudio);
Integer downscale = cmd.getIntegerFlagID(index, FLAG_DOWNSCALE, Integer.valueOf(1));
if (downscale != null && 1 << MathUtil.log2(downscale.intValue()) != downscale) {
Logger.error("Only values [2, 4, 8] are supported for " + String.valueOf(FLAG_DOWNSCALE) + ", the option will have no effect.");
} else {
source.setOption(Options.DOWNSCALE, downscale);
}
source.setOption(Options.PROFILE, cmd.getStringFlagI(index, FLAG_PROFILE));
source.setOption(Options.INTERLACED, cmd.getBooleanFlagID(index, FLAG_INTERLACED, Boolean.valueOf(false)));
sources.add(source);
inputCodecsVideo.add(inputCodecVideo);
inputCodecsAudio.add(inputCodecAudio);
builder.addSource(source);
builder.setSeekFrames(sources.size() - 1, cmd.getIntegerFlagID(index, FLAG_SEEK_FRAMES, Integer.valueOf(0)).intValue())
.setMaxFrames(sources.size() - 1, cmd.getIntegerFlagID(index, FLAG_MAX_FRAMES, Integer.valueOf(Integer.MAX_VALUE)).intValue());
}
}
if (sources.isEmpty()) {
MainUtils.printHelpArgs(ALL_FLAGS, new String[] { "input", "output" });
return;
}
List<Sink> sinks = new ArrayList<>();
for (int i = 0; i < cmd.argsLength(); i++) {
if (!cmd.getBooleanFlagI(i, FLAG_INPUT)) {
Format outputFormat;
String output = cmd.getArg(i);
String outputFormatRaw = cmd.getStringFlagI(i, FLAG_FORMAT);
if (outputFormatRaw == null) {
outputFormat = getFormatFromExtension(output);
} else {
outputFormat = Format.valueOf(outputFormatRaw.toUpperCase());
}
String outputCodecVideoRaw = cmd.getStringFlagI(i, FLAG_VIDEO_CODEC);
Codec outputCodecVideo = null;
boolean videoCopy = false;
if (outputCodecVideoRaw == null) {
outputCodecVideo = getCodecFromExtension(output);
if (outputCodecVideo == null)
outputCodecVideo = getFirstVideoCodecForFormat(outputFormat);
} else if ("copy".equalsIgnoreCase(outputCodecVideoRaw)) {
videoCopy = true;
} else if ("none".equalsIgnoreCase(outputCodecVideoRaw)) {
outputCodecVideo = null;
} else {
outputCodecVideo = Codec.valueOf(outputCodecVideoRaw.toUpperCase());
}
String outputCodecAudioRaw = cmd.getStringFlagI(i, FLAG_AUDIO_CODEC);
Codec outputCodecAudio = null;
boolean audioCopy = false;
if (outputCodecAudioRaw == null) {
if (outputFormat.isAudio())
outputCodecAudio = getFirstAudioCodecForFormat(outputFormat);
} else if ("copy".equalsIgnoreCase(outputCodecAudioRaw)) {
audioCopy = true;
} else if ("none".equalsIgnoreCase(outputCodecVideoRaw)) {
outputCodecAudio = null;
} else {
outputCodecAudio = Codec.valueOf(outputCodecAudioRaw.toUpperCase());
}
int audioMap = cmd.getIntegerFlagID(i, FLAG_MAP_AUDIO, Integer.valueOf(0));
if (audioMap > sources.size())
Logger.error("Can not map audio from source " + audioMap + ", " +
sources.size() + " sources specified.");
int videoMap = cmd.getIntegerFlagID(i, FLAG_MAP_VIDEO, Integer.valueOf(0));
if (videoMap > sources.size())
Logger.error("Can not map video from source " + videoMap + ", " +
sources.size() + " sources specified.");
if (videoCopy) {
Tuple._3<Integer, Integer, Codec> inputCodecVideo = inputCodecsVideo.get(videoMap);
outputCodecVideo = (inputCodecVideo != null) ? (Codec)inputCodecVideo.v2 : null;
}
if (audioCopy) {
Tuple._3<Integer, Integer, Codec> inputCodecAudio = inputCodecsAudio.get(audioMap);
outputCodecAudio = (inputCodecAudio != null) ? (Codec)inputCodecAudio.v2 : null;
}
Sink sink = new SinkImpl(output, outputFormat, outputCodecVideo, outputCodecAudio);
sinks.add(sink);
builder.addSink(sink);
builder.setAudioMapping(audioMap, sinks.size() - 1, audioCopy);
builder.setVideoMapping(videoMap, sinks.size() - 1, videoCopy);
if (cmd.getBooleanFlagI(i, FLAG_DUMPMV)) {
builder.addFilter(sinks.size() - 1, new DumpMvFilter(false));
} else if (cmd.getBooleanFlagI(i, FLAG_DUMPMVJS)) {
builder.addFilter(sinks.size() - 1, new DumpMvFilter(true));
}
String vf = cmd.getStringFlagI(i, FLAG_VIDEO_FILTER);
if (vf != null)
addVideoFilters(vf, builder, sinks.size() - 1);
}
}
if (sources.isEmpty() || sinks.isEmpty()) {
MainUtils.printHelpArgs(ALL_FLAGS, new String[] { "input", "output" });
return;
}
Transcoder transcoder = builder.create();
transcoder.transcode();
}
private static void addVideoFilters(String vf, Transcoder.TranscoderBuilder builder, int sinkIndex) {
if (vf == null)
return;
for (String filter : vf.split(",")) {
String[] parts = filter.split("=");
String filterName = parts[0];
Class<? extends Filter> filterClass = knownFilters.get(filterName);
if (filterClass == null) {
Logger.error("Unknown filter: " + filterName);
throw new RuntimeException("Unknown filter: " + filterName);
}
if (parts.length > 1) {
String filterArgs = parts[1];
String[] split = filterArgs.split(":");
Integer[] params = new Integer[split.length];
for (int i = 0; i < split.length; i++)
params[i] = Integer.parseInt(split[i]);
try {
Filter f = Platform.newInstance(filterClass, params);
builder.addFilter(sinkIndex, f);
} catch (Exception e) {
String message = "The filter " + filterName + " doesn't take " + split.length + " arguments.";
Logger.error(message);
throw new RuntimeException(message);
}
}
}
}
private static Codec getFirstAudioCodecForFormat(Format inputFormat) {
return audioCodecsForF.get(inputFormat);
}
private static Codec getFirstVideoCodecForFormat(Format inputFormat) {
return videoCodecsForF.get(inputFormat);
}
private static Codec detectVideoDecoder(DemuxerTrack track) throws IOException {
DemuxerTrackMeta meta = track.getMeta();
if (meta != null) {
Codec codec = meta.getCodec();
if (codec != null)
return codec;
}
Packet packet = track.nextFrame();
if (packet == null)
return null;
return JCodecUtil.detectDecoder(packet.getData());
}
private static Tuple._3<Integer, Integer, Codec> selectSuitableTrack(String input, Format format, TrackType targetType) throws IOException {
Tuple._2<Integer, Demuxer> demuxerPid;
if (format == Format.MPEG_TS) {
demuxerPid = JCodecUtil.createM2TSDemuxer(new File(input), targetType);
} else {
demuxerPid = Tuple.pair(Integer.valueOf(0), JCodecUtil.createDemuxer(format, new File(input)));
}
if (demuxerPid == null || demuxerPid.v1 == null)
return null;
int trackNo = 0;
List<? extends DemuxerTrack> tracks = (targetType == TrackType.VIDEO) ? ((Demuxer)demuxerPid.v1).getVideoTracks() : (
(Demuxer)demuxerPid.v1).getAudioTracks();
for (DemuxerTrack demuxerTrack : tracks) {
Codec codec = detectVideoDecoder(demuxerTrack);
if (supportedDecoders.contains(codec))
return Tuple.triple((Integer)demuxerPid.v0, Integer.valueOf(trackNo), codec);
trackNo++;
}
return null;
}
private static Format getFormatFromExtension(String output) {
String extension = output.replaceFirst(".*\\.([^\\.]+$)", "$1");
return extensionToF.get(extension);
}
private static Codec getCodecFromExtension(String output) {
String extension = output.replaceFirst(".*\\.([^\\.]+$)", "$1");
return extensionToC.get(extension);
}
}

View file

@ -0,0 +1,413 @@
package org.jcodec.api.transcode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.jcodec.api.transcode.filters.ColorTransformFilter;
import org.jcodec.common.AudioCodecMeta;
import org.jcodec.common.IntArrayList;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Packet;
public class Transcoder {
static final int REORDER_BUFFER_SIZE = 7;
private Source[] sources;
private Sink[] sinks;
private List<Filter>[] extraFilters;
private int[] seekFrames;
private int[] maxFrames;
private Mapping[] videoMappings;
private Mapping[] audioMappings;
private Transcoder(Source[] source, Sink[] sink, Mapping[] videoMappings, Mapping[] audioMappings, List<Filter>[] extraFilters, int[] seekFrames, int[] maxFrames) {
this.extraFilters = extraFilters;
this.videoMappings = videoMappings;
this.audioMappings = audioMappings;
this.seekFrames = seekFrames;
this.maxFrames = maxFrames;
this.sources = source;
this.sinks = sink;
}
private static class Mapping {
private int source;
private boolean copy;
public Mapping(int source, boolean copy) {
this.source = source;
this.copy = copy;
}
}
private static class Stream {
private static final double AUDIO_LEADING_TIME = 0.2D;
private LinkedList<VideoFrameWithPacket> videoQueue;
private LinkedList<AudioFrameWithPacket> audioQueue;
private List<Filter> filters;
private List<Filter> extraFilters;
private Sink sink;
private boolean videoCopy;
private boolean audioCopy;
private PixelStore pixelStore;
private VideoCodecMeta videoCodecMeta;
private AudioCodecMeta audioCodecMeta;
private static final int REORDER_LENGTH = 5;
public Stream(Sink sink, boolean videoCopy, boolean audioCopy, List<Filter> extraFilters, PixelStore pixelStore) {
this.sink = sink;
this.videoCopy = videoCopy;
this.audioCopy = audioCopy;
this.extraFilters = extraFilters;
this.pixelStore = pixelStore;
this.videoQueue = new LinkedList<>();
this.audioQueue = new LinkedList<>();
}
private List<Filter> initColorTransform(ColorSpace sourceColor, List<Filter> extraFilters, Sink sink) {
List<Filter> filters = new ArrayList<>();
for (Filter filter : extraFilters) {
ColorSpace colorSpace = filter.getInputColor();
if (!sourceColor.matches(colorSpace))
filters.add(new ColorTransformFilter(colorSpace));
filters.add(filter);
if (filter.getOutputColor() != ColorSpace.SAME)
sourceColor = filter.getOutputColor();
}
ColorSpace inputColor = sink.getInputColor();
if (inputColor != null && inputColor != sourceColor)
filters.add(new ColorTransformFilter(inputColor));
return filters;
}
public void tryFlushQueues() throws IOException {
if (this.videoQueue.size() <= 0)
return;
if (this.videoCopy && this.videoQueue.size() < 5)
return;
if (!hasLeadingAudio())
return;
VideoFrameWithPacket firstVideoFrame = this.videoQueue.get(0);
if (this.videoCopy)
for (VideoFrameWithPacket videoFrame : this.videoQueue) {
if (videoFrame.getPacket().getFrameNo() < firstVideoFrame.getPacket().getFrameNo())
firstVideoFrame = videoFrame;
}
int aqSize = this.audioQueue.size();
for (int af = 0; af < aqSize; af++) {
AudioFrameWithPacket audioFrame = this.audioQueue.get(0);
if (audioFrame.getPacket().getPtsD() >= firstVideoFrame.getPacket().getPtsD() + 0.2D)
break;
this.audioQueue.remove(0);
if (this.audioCopy && this.sink instanceof PacketSink) {
((PacketSink)this.sink).outputAudioPacket(audioFrame.getPacket(), this.audioCodecMeta);
} else {
this.sink.outputAudioFrame(audioFrame);
}
}
this.videoQueue.remove(firstVideoFrame);
if (this.videoCopy && this.sink instanceof PacketSink) {
((PacketSink)this.sink).outputVideoPacket(firstVideoFrame.getPacket(), this.videoCodecMeta);
} else {
PixelStore.LoanerPicture frame = filterFrame(firstVideoFrame);
this.sink.outputVideoFrame(new VideoFrameWithPacket(firstVideoFrame.getPacket(), frame));
this.pixelStore.putBack(frame);
}
}
private PixelStore.LoanerPicture filterFrame(VideoFrameWithPacket firstVideoFrame) {
PixelStore.LoanerPicture frame = firstVideoFrame.getFrame();
for (Filter filter : this.filters) {
PixelStore.LoanerPicture old = frame;
frame = filter.filter(frame.getPicture(), this.pixelStore);
if (frame == null) {
frame = old;
continue;
}
this.pixelStore.putBack(old);
}
return frame;
}
public void finalFlushQueues() throws IOException {
VideoFrameWithPacket lastVideoFrame = null;
for (VideoFrameWithPacket videoFrame : this.videoQueue) {
if (lastVideoFrame == null || videoFrame.getPacket().getPtsD() >= lastVideoFrame.getPacket().getPtsD())
lastVideoFrame = videoFrame;
}
if (lastVideoFrame != null) {
for (AudioFrameWithPacket audioFrame : this.audioQueue) {
if (audioFrame.getPacket().getPtsD() > lastVideoFrame.getPacket().getPtsD())
break;
if (this.audioCopy && this.sink instanceof PacketSink) {
((PacketSink)this.sink).outputAudioPacket(audioFrame.getPacket(), this.audioCodecMeta);
continue;
}
this.sink.outputAudioFrame(audioFrame);
}
for (VideoFrameWithPacket videoFrame : this.videoQueue) {
if (videoFrame != null) {
if (this.videoCopy && this.sink instanceof PacketSink) {
((PacketSink)this.sink).outputVideoPacket(videoFrame.getPacket(), this.videoCodecMeta);
continue;
}
PixelStore.LoanerPicture frame = filterFrame(videoFrame);
this.sink.outputVideoFrame(new VideoFrameWithPacket(videoFrame.getPacket(), frame));
this.pixelStore.putBack(frame);
}
}
} else {
for (AudioFrameWithPacket audioFrame : this.audioQueue) {
if (this.audioCopy && this.sink instanceof PacketSink) {
((PacketSink)this.sink).outputAudioPacket(audioFrame.getPacket(), this.audioCodecMeta);
continue;
}
this.sink.outputAudioFrame(audioFrame);
}
}
}
public void addVideoPacket(VideoFrameWithPacket videoFrame, VideoCodecMeta meta) {
if (videoFrame.getFrame() != null)
this.pixelStore.retake(videoFrame.getFrame());
this.videoQueue.add(videoFrame);
this.videoCodecMeta = meta;
if (this.filters == null)
this.filters = initColorTransform(this.videoCodecMeta.getColor(), this.extraFilters, this.sink);
}
public void addAudioPacket(AudioFrameWithPacket videoFrame, AudioCodecMeta meta) {
this.audioQueue.add(videoFrame);
this.audioCodecMeta = meta;
}
public boolean needsVideoFrame() {
if (this.videoQueue.size() <= 0)
return true;
if (this.videoCopy && this.videoQueue.size() < 5)
return true;
return false;
}
public boolean hasLeadingAudio() {
VideoFrameWithPacket firstVideoFrame = this.videoQueue.get(0);
for (AudioFrameWithPacket audioFrame : this.audioQueue) {
if (audioFrame.getPacket().getPtsD() >= firstVideoFrame.getPacket().getPtsD() + 0.2D)
return true;
}
return false;
}
}
public void transcode() throws IOException {
PixelStore pixelStore = new PixelStoreImpl();
List<Stream>[] videoStreams = new List[this.sources.length];
List<Stream>[] audioStreams = new List[this.sources.length];
boolean[] decodeVideo = new boolean[this.sources.length];
boolean[] decodeAudio = new boolean[this.sources.length];
boolean[] finishedVideo = new boolean[this.sources.length];
boolean[] finishedAudio = new boolean[this.sources.length];
Stream[] allStreams = new Stream[this.sinks.length];
int[] videoFramesRead = new int[this.sources.length];
for (int k = 0; k < this.sources.length; k++) {
videoStreams[k] = new ArrayList<>();
audioStreams[k] = new ArrayList<>();
}
for (int j = 0; j < this.sinks.length; j++)
this.sinks[j].init();
for (int i = 0; i < this.sources.length; i++) {
this.sources[i].init(pixelStore);
this.sources[i].seekFrames(this.seekFrames[i]);
}
for (int s = 0; s < this.sinks.length; s++) {
Stream stream = new Stream(this.sinks[s], (this.videoMappings[s]).copy, (this.audioMappings[s]).copy, this.extraFilters[s], pixelStore);
allStreams[s] = stream;
if (this.sources[(this.videoMappings[s]).source].isVideo()) {
videoStreams[(this.videoMappings[s]).source].add(stream);
if (!(this.videoMappings[s]).copy)
decodeVideo[(this.videoMappings[s]).source] = true;
} else {
finishedVideo[(this.videoMappings[s]).source] = true;
}
if (this.sources[(this.audioMappings[s]).source].isAudio()) {
audioStreams[(this.audioMappings[s]).source].add(stream);
if (!(this.audioMappings[s]).copy)
decodeAudio[(this.audioMappings[s]).source] = true;
} else {
finishedAudio[(this.audioMappings[s]).source] = true;
}
}
try {
boolean allFinished;
do {
for (int i1 = 0; i1 < this.sources.length; i1++) {
Source source = this.sources[i1];
boolean needsVideoFrame = !finishedVideo[i1];
for (Stream stream : videoStreams[i1])
needsVideoFrame &= (stream.needsVideoFrame() || stream.hasLeadingAudio() || finishedAudio[i1]) ? true : false;
if (needsVideoFrame) {
VideoFrameWithPacket nextVideoFrame;
if (videoFramesRead[i1] >= this.maxFrames[i1]) {
nextVideoFrame = null;
finishedVideo[i1] = true;
} else if (decodeVideo[i1] || !(source instanceof PacketSource)) {
nextVideoFrame = source.getNextVideoFrame();
if (nextVideoFrame == null) {
finishedVideo[i1] = true;
} else {
videoFramesRead[i1] = videoFramesRead[i1] + 1;
printLegend((int)nextVideoFrame.getPacket().getFrameNo(), 0,
nextVideoFrame.getPacket());
}
} else {
Packet packet = ((PacketSource)source).inputVideoPacket();
if (packet == null) {
finishedVideo[i1] = true;
} else {
videoFramesRead[i1] = videoFramesRead[i1] + 1;
}
nextVideoFrame = new VideoFrameWithPacket(packet, null);
}
if (finishedVideo[i1]) {
for (Stream stream : videoStreams[i1]) {
for (int ss = 0; ss < audioStreams.length; ss++)
audioStreams[ss].remove(stream);
}
videoStreams[i1].clear();
}
if (nextVideoFrame != null) {
for (Stream stream : videoStreams[i1])
stream.addVideoPacket(nextVideoFrame, source.getVideoCodecMeta());
if (nextVideoFrame.getFrame() != null)
pixelStore.putBack(nextVideoFrame.getFrame());
}
}
if (!audioStreams[i1].isEmpty()) {
AudioFrameWithPacket nextAudioFrame;
if (decodeAudio[i1] || !(source instanceof PacketSource)) {
nextAudioFrame = source.getNextAudioFrame();
if (nextAudioFrame == null)
finishedAudio[i1] = true;
} else {
Packet packet = ((PacketSource)source).inputAudioPacket();
if (packet == null) {
finishedAudio[i1] = true;
nextAudioFrame = null;
} else {
nextAudioFrame = new AudioFrameWithPacket(null, packet);
}
}
if (nextAudioFrame != null)
for (Stream stream : audioStreams[i1])
stream.addAudioPacket(nextAudioFrame, source.getAudioCodecMeta());
} else {
finishedAudio[i1] = true;
}
}
for (int i2 = 0; i2 < allStreams.length; i2++)
allStreams[i2].tryFlushQueues();
allFinished = true;
for (int i3 = 0; i3 < this.sources.length; i3++)
allFinished &= finishedVideo[i3] & finishedAudio[i3];
} while (!allFinished);
for (int n = 0; n < allStreams.length; n++)
allStreams[n].finalFlushQueues();
} finally {
for (int n = 0; n < this.sources.length; n++)
this.sources[0].finish();
for (int m = 0; m < this.sinks.length; m++)
this.sinks[m].finish();
}
}
private void printLegend(int frameNo, int maxFrames, Packet inVideoPacket) {
if (frameNo % 100 == 0)
System.out.print(String.format("[%6d]\r", frameNo));
}
public static class TranscoderBuilder {
private List<Source> source = new ArrayList<>();
private List<Sink> sink = new ArrayList<>();
private List<List<Filter>> filters = new ArrayList<>();
private IntArrayList seekFrames = new IntArrayList(20);
private IntArrayList maxFrames = new IntArrayList(20);
private List<Transcoder.Mapping> videoMappings = new ArrayList<>();
private List<Transcoder.Mapping> audioMappings = new ArrayList<>();
public TranscoderBuilder addFilter(int sink, Filter filter) {
this.filters.get(sink).add(filter);
return this;
}
public TranscoderBuilder setSeekFrames(int source, int seekFrames) {
this.seekFrames.set(source, seekFrames);
return this;
}
public TranscoderBuilder setMaxFrames(int source, int maxFrames) {
this.maxFrames.set(source, maxFrames);
return this;
}
public TranscoderBuilder addSource(Source source) {
this.source.add(source);
this.seekFrames.add(0);
this.maxFrames.add(Integer.MAX_VALUE);
return this;
}
public TranscoderBuilder addSink(Sink sink) {
this.sink.add(sink);
this.videoMappings.add(new Transcoder.Mapping(0, false));
this.audioMappings.add(new Transcoder.Mapping(0, false));
this.filters.add(new ArrayList<>());
return this;
}
public TranscoderBuilder setVideoMapping(int src, int sink, boolean copy) {
this.videoMappings.set(sink, new Transcoder.Mapping(src, copy));
return this;
}
public TranscoderBuilder setAudioMapping(int src, int sink, boolean copy) {
this.audioMappings.set(sink, new Transcoder.Mapping(src, copy));
return this;
}
public Transcoder create() {
return new Transcoder(this.source.<Source>toArray(new Source[0]), this.sink.<Sink>toArray(new Sink[0]),
this.videoMappings.<Transcoder.Mapping>toArray(new Transcoder.Mapping[0]), this.audioMappings.<Transcoder.Mapping>toArray(new Transcoder.Mapping[0]),
this.filters.<List<Filter>>toArray(new List[0]), this.seekFrames.toArray(), this.maxFrames.toArray());
}
}
public static TranscoderBuilder newTranscoder() {
return new TranscoderBuilder();
}
}

View file

@ -0,0 +1,30 @@
package org.jcodec.api.transcode;
import org.jcodec.common.model.Packet;
public class VideoFrameWithPacket implements Comparable<VideoFrameWithPacket> {
private Packet packet;
private PixelStore.LoanerPicture frame;
public VideoFrameWithPacket(Packet inFrame, PixelStore.LoanerPicture dec2) {
this.packet = inFrame;
this.frame = dec2;
}
public int compareTo(VideoFrameWithPacket arg) {
if (arg == null)
return -1;
long pts1 = this.packet.getPts();
long pts2 = arg.packet.getPts();
return (pts1 > pts2) ? 1 : ((pts1 == pts2) ? 0 : -1);
}
public Packet getPacket() {
return this.packet;
}
public PixelStore.LoanerPicture getFrame() {
return this.frame;
}
}

View file

@ -0,0 +1,38 @@
package org.jcodec.api.transcode.filters;
import org.jcodec.api.transcode.Filter;
import org.jcodec.api.transcode.PixelStore;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.scale.ColorUtil;
import org.jcodec.scale.Transform;
public class ColorTransformFilter implements Filter {
private Transform transform;
private ColorSpace outputColor;
public ColorTransformFilter(ColorSpace outputColor) {
this.outputColor = outputColor;
}
public PixelStore.LoanerPicture filter(Picture picture, PixelStore store) {
if (this.transform == null) {
this.transform = ColorUtil.getTransform(picture.getColor(), this.outputColor);
Logger.debug("Creating transform: " + String.valueOf(this.transform));
}
PixelStore.LoanerPicture outFrame = store.getPicture(picture.getWidth(), picture.getHeight(), this.outputColor);
outFrame.getPicture().setCrop(picture.getCrop());
this.transform.transform(picture, outFrame.getPicture());
return outFrame;
}
public ColorSpace getInputColor() {
return ColorSpace.ANY_PLANAR;
}
public ColorSpace getOutputColor() {
return this.outputColor;
}
}

View file

@ -0,0 +1,20 @@
package org.jcodec.api.transcode.filters;
import org.jcodec.api.transcode.Filter;
import org.jcodec.api.transcode.PixelStore;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
public class CropFilter implements Filter {
public PixelStore.LoanerPicture filter(Picture picture, PixelStore store) {
return null;
}
public ColorSpace getInputColor() {
return null;
}
public ColorSpace getOutputColor() {
return null;
}
}

View file

@ -0,0 +1,86 @@
package org.jcodec.api.transcode.filters;
import org.jcodec.api.transcode.Filter;
import org.jcodec.api.transcode.PixelStore;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
public class DumpMvFilter implements Filter {
private boolean js;
public DumpMvFilter(boolean js) {
this.js = js;
}
public PixelStore.LoanerPicture filter(Picture picture, PixelStore pixelStore) {
Frame dec = (Frame)picture;
if (!this.js) {
dumpMvTxt(dec);
} else {
dumpMvJs(dec);
}
return null;
}
private void dumpMvTxt(Frame dec) {
System.err.println("FRAME ================================================================");
if (dec.getFrameType() == SliceType.I)
return;
H264Utils.MvList2D mvs = dec.getMvs();
for (int i = 0; i < 2; i++) {
System.err.println(((i == 0) ? "BCK" : "FWD") + " ===========================================================================");
for (int blkY = 0; blkY < mvs.getHeight(); blkY++) {
StringBuilder line0 = new StringBuilder();
StringBuilder line1 = new StringBuilder();
StringBuilder line2 = new StringBuilder();
StringBuilder line3 = new StringBuilder();
line0.append("+");
line1.append("|");
line2.append("|");
line3.append("|");
for (int blkX = 0; blkX < mvs.getWidth(); blkX++) {
line0.append("------+");
line1.append(String.format("%6d|", H264Utils.Mv.mvX(mvs.getMv(blkX, blkY, i))));
line2.append(String.format("%6d|", H264Utils.Mv.mvY(mvs.getMv(blkX, blkY, i))));
line3.append(String.format(" %2d|", H264Utils.Mv.mvRef(mvs.getMv(blkX, blkY, i))));
}
System.err.println(line0.toString());
System.err.println(line1.toString());
System.err.println(line2.toString());
System.err.println(line3.toString());
}
if (dec.getFrameType() != SliceType.B)
break;
}
}
private void dumpMvJs(Frame dec) {
System.err.println("{");
if (dec.getFrameType() == SliceType.I)
return;
H264Utils.MvList2D mvs = dec.getMvs();
for (int i = 0; i < 2; i++) {
System.err.println(((i == 0) ? "backRef" : "forwardRef") + ": [");
for (int blkY = 0; blkY < mvs.getHeight(); blkY++) {
for (int blkX = 0; blkX < mvs.getWidth(); blkX++)
System.err.println("{x: " + blkX + ", y: " + blkY + ", mx: " + H264Utils.Mv.mvX(mvs.getMv(blkX, blkY, i)) + ", my: " +
H264Utils.Mv.mvY(mvs.getMv(blkX, blkY, i)) + ", ridx:" + H264Utils.Mv.mvRef(mvs.getMv(blkX, blkY, i)) + "},");
}
System.err.println("],");
if (dec.getFrameType() != SliceType.B)
break;
}
System.err.println("}");
}
public ColorSpace getInputColor() {
return null;
}
public ColorSpace getOutputColor() {
return null;
}
}

View file

@ -0,0 +1,53 @@
package org.jcodec.api.transcode.filters;
import org.jcodec.api.transcode.Filter;
import org.jcodec.api.transcode.PixelStore;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Size;
import org.jcodec.scale.BaseResampler;
import org.jcodec.scale.LanczosResampler;
public class ScaleFilter implements Filter {
private BaseResampler resampler;
private ColorSpace currentColor;
private Size currentSize;
private Size targetSize;
private int width;
private int height;
public ScaleFilter(int width, int height) {
this.width = width;
this.height = height;
}
public Size getTarget() {
return new Size(this.width, this.height);
}
public PixelStore.LoanerPicture filter(Picture picture, PixelStore store) {
Size pictureSize = picture.getSize();
if (this.resampler == null || this.currentColor != picture.getColor() || !pictureSize.equals(this.currentSize)) {
this.currentColor = picture.getColor();
this.currentSize = picture.getSize();
this.targetSize = new Size(this.width & this.currentColor.getWidthMask(), this.height & this.currentColor.getHeightMask());
this.resampler = new LanczosResampler(this.currentSize, this.targetSize);
}
PixelStore.LoanerPicture dest = store.getPicture(this.targetSize.getWidth(), this.targetSize.getHeight(), this.currentColor);
this.resampler.resample(picture, dest.getPicture());
return dest;
}
public ColorSpace getInputColor() {
return ColorSpace.ANY_PLANAR;
}
public ColorSpace getOutputColor() {
return ColorSpace.SAME;
}
}

View file

@ -0,0 +1,78 @@
package org.jcodec.audio;
import java.io.IOException;
import java.nio.FloatBuffer;
public class Audio {
public static void transfer(AudioSource src, AudioSink sink) throws IOException {
filterTransfer(src, new DummyFilter(1), sink);
}
public static void filterTransfer(AudioSource src, AudioFilter filter, AudioSink sink) throws IOException {
if (filter.getNInputs() != 1)
throw new IllegalArgumentException("Audio filter has # inputs != 1");
if (filter.getNOutputs() != 1)
throw new IllegalArgumentException("Audio filter has # outputs != 1");
if (filter.getDelay() != 0)
throw new IllegalArgumentException("Audio filter has delay");
FloatBuffer[] ins = { FloatBuffer.allocate(4096) };
FloatBuffer[] outs = { FloatBuffer.allocate(8192) };
long[] pos = new long[1];
while (src.readFloat(ins[0]) != -1) {
ins[0].flip();
filter.filter(ins, pos, outs);
pos[0] = pos[0] + (long)ins[0].position();
rotate(ins[0]);
outs[0].flip();
sink.writeFloat(outs[0]);
outs[0].clear();
}
}
public static void print(FloatBuffer buf) {
FloatBuffer dup = buf.duplicate();
while (dup.hasRemaining())
System.out.print(String.format("%.3f,", dup.get()));
System.out.println();
}
public static void rotate(FloatBuffer buf) {
int pos;
for (pos = 0; buf.hasRemaining(); pos++)
buf.put(pos, buf.get());
buf.position(pos);
buf.limit(buf.capacity());
}
public static class DummyFilter implements AudioFilter {
private int nInputs;
public DummyFilter(int nInputs) {
this.nInputs = nInputs;
}
public void filter(FloatBuffer[] _in, long[] inPos, FloatBuffer[] out) {
for (int i = 0; i < _in.length; i++) {
if (out[i].remaining() >= _in[i].remaining()) {
out[i].put(_in[i]);
} else {
FloatBuffer duplicate = _in[i].duplicate();
duplicate.limit(_in[i].position() + out[i].remaining());
out[i].put(duplicate);
}
}
}
public int getDelay() {
return 0;
}
public int getNInputs() {
return this.nInputs;
}
public int getNOutputs() {
return this.nInputs;
}
}
}

View file

@ -0,0 +1,13 @@
package org.jcodec.audio;
import java.nio.FloatBuffer;
public interface AudioFilter {
void filter(FloatBuffer[] paramArrayOfFloatBuffer1, long[] paramArrayOflong, FloatBuffer[] paramArrayOfFloatBuffer2);
int getDelay();
int getNInputs();
int getNOutputs();
}

View file

@ -0,0 +1,8 @@
package org.jcodec.audio;
import java.io.IOException;
import java.nio.FloatBuffer;
public interface AudioSink {
void writeFloat(FloatBuffer paramFloatBuffer) throws IOException;
}

View file

@ -0,0 +1,11 @@
package org.jcodec.audio;
import java.io.IOException;
import java.nio.FloatBuffer;
import org.jcodec.common.AudioFormat;
public interface AudioSource {
AudioFormat getFormat();
int readFloat(FloatBuffer paramFloatBuffer) throws IOException;
}

View file

@ -0,0 +1,46 @@
package org.jcodec.audio;
import java.nio.FloatBuffer;
import org.jcodec.common.AudioFormat;
import org.jcodec.common.Preconditions;
public class ChannelMerge implements AudioFilter {
private AudioFormat format;
public ChannelMerge(AudioFormat format) {
this.format = format;
}
public void filter(FloatBuffer[] _in, long[] inPos, FloatBuffer[] out) {
if (_in.length != this.format.getChannels())
throw new IllegalArgumentException("Channel merge must be supplied with " + this.format.getChannels() + " input buffers to hold the channels.");
if (out.length != 1)
throw new IllegalArgumentException("Channel merget invoked on more then one output");
FloatBuffer out0 = out[0];
int min = Integer.MAX_VALUE;
for (int k = 0; k < _in.length; k++) {
if (_in[k].remaining() < min)
min = _in[k].remaining();
}
for (int j = 0; j < _in.length; j++)
Preconditions.checkState((_in[j].remaining() == min));
if (out0.remaining() < min * _in.length)
throw new IllegalArgumentException("Supplied output buffer is not big enough to hold " + min + " * " + _in.length + " = " + min * _in.length + " output samples.");
for (int i = 0; i < min; i++) {
for (int m = 0; m < _in.length; m++)
out0.put(_in[m].get());
}
}
public int getDelay() {
return 0;
}
public int getNInputs() {
return this.format.getChannels();
}
public int getNOutputs() {
return 1;
}
}

View file

@ -0,0 +1,43 @@
package org.jcodec.audio;
import java.nio.FloatBuffer;
import org.jcodec.common.AudioFormat;
public class ChannelSplit implements AudioFilter {
private AudioFormat format;
public ChannelSplit(AudioFormat format) {
this.format = format;
}
public void filter(FloatBuffer[] _in, long[] inPos, FloatBuffer[] out) {
if (_in.length != 1)
throw new IllegalArgumentException("Channel split invoked on more then one input");
if (out.length != this.format.getChannels())
throw new IllegalArgumentException("Channel split must be supplied with " + this.format.getChannels() + " output buffers to hold the channels.");
FloatBuffer in0 = _in[0];
int outSampleCount = in0.remaining() / out.length;
for (int i = 0; i < out.length; i++) {
if (out[i].remaining() < outSampleCount)
throw new IllegalArgumentException("Supplied buffer for " + i + "th channel doesn't have sufficient space to put the samples ( required: " + outSampleCount + ", actual: " + out[i]
.remaining() + ")");
}
while (in0.remaining() >= this.format.getChannels()) {
for (int j = 0; j < out.length; j++)
out[j].put(in0.get());
}
}
public int getDelay() {
return 0;
}
public int getNInputs() {
return 1;
}
public int getNOutputs() {
return this.format.getChannels();
}
}

View file

@ -0,0 +1,47 @@
package org.jcodec.audio;
import java.nio.FloatBuffer;
public abstract class ConvolutionFilter implements AudioFilter {
private double[] kernel;
protected abstract double[] buildKernel();
public void filter(FloatBuffer[] _in, long[] pos, FloatBuffer[] out) {
if (_in.length != 1)
throw new IllegalArgumentException(getClass().getName() + " filter is designed to work only on one input");
if (out.length != 1)
throw new IllegalArgumentException(getClass().getName() + " filter is designed to work only on one output");
FloatBuffer in0 = _in[0];
FloatBuffer out0 = out[0];
if (this.kernel == null)
this.kernel = buildKernel();
if (out0.remaining() < in0.remaining() - this.kernel.length)
throw new IllegalArgumentException("Output buffer is too small");
if (in0.remaining() <= this.kernel.length)
throw new IllegalArgumentException("Input buffer should contain > kernel lenght (" + this.kernel.length + ") samples.");
int halfKernel = this.kernel.length / 2;
int i;
for (i = in0.position() + halfKernel; i < in0.limit() - halfKernel; i++) {
double result = 0.0D;
for (int j = 0; j < this.kernel.length; j++)
result += this.kernel[j] * (double)in0.get(i + j - halfKernel);
out0.put((float)result);
}
in0.position(i - halfKernel);
}
public int getDelay() {
if (this.kernel == null)
this.kernel = buildKernel();
return this.kernel.length / 2;
}
public int getNInputs() {
return 1;
}
public int getNOutputs() {
return 1;
}
}

View file

@ -0,0 +1,81 @@
package org.jcodec.audio;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class FilterGraph implements AudioFilter {
private FilterSocket[] sockets;
public static Factory addLevel(AudioFilter first) {
return new Factory(first);
}
public static class Factory {
private List<FilterSocket> sockets = new ArrayList<>();
protected Factory(AudioFilter firstFilter) {
if (firstFilter.getDelay() != 0) {
this.sockets.add(FilterSocket.createFilterSocket(new Audio.DummyFilter[] { new Audio.DummyFilter(firstFilter.getNInputs()) }));
addLevel(new AudioFilter[] { firstFilter });
} else {
this.sockets.add(FilterSocket.createFilterSocket(new AudioFilter[] { firstFilter }));
}
}
public Factory addLevel(AudioFilter[] filters) {
FilterSocket socket = FilterSocket.createFilterSocket(filters);
socket.allocateBuffers(4096);
this.sockets.add(socket);
return this;
}
public Factory addLevels(AudioFilter filter, int n) {
AudioFilter[] filters = new AudioFilter[n];
Arrays.fill(filters, filter);
return addLevel(filters);
}
public Factory addLevelSpan(AudioFilter filter) {
int prevLevelOuts = this.sockets.get(this.sockets.size() - 1).getTotalOutputs();
if (prevLevelOuts % filter.getNInputs() != 0)
throw new IllegalArgumentException("Can't fill " + prevLevelOuts + " with multiple of " +
filter.getNInputs());
return addLevels(filter, prevLevelOuts / filter.getNInputs());
}
public FilterGraph create() {
return new FilterGraph(this.sockets.<FilterSocket>toArray(new FilterSocket[0]));
}
}
private FilterGraph(FilterSocket[] sockets) {
this.sockets = sockets;
}
public void filter(FloatBuffer[] ins, long[] pos, FloatBuffer[] outs) {
this.sockets[0].setBuffers(ins, pos);
for (int i = 0; i < this.sockets.length; i++) {
FloatBuffer[] curOut = (i < this.sockets.length - 1) ? this.sockets[i + 1].getBuffers() : outs;
this.sockets[i].filter(curOut);
if (i > 0)
this.sockets[i].rotate();
if (i < this.sockets.length - 1)
for (FloatBuffer b : curOut)
b.flip();
}
}
public int getDelay() {
return this.sockets[0].getFilters()[0].getDelay();
}
public int getNInputs() {
return this.sockets[0].getTotalInputs();
}
public int getNOutputs() {
return this.sockets[this.sockets.length - 1].getTotalOutputs();
}
}

View file

@ -0,0 +1,101 @@
package org.jcodec.audio;
import java.nio.FloatBuffer;
import org.jcodec.platform.Platform;
public class FilterSocket {
private FloatBuffer[] buffers;
private long[] positions;
private int[] delays;
private AudioFilter[] filters;
private int totalInputs;
private int totalOutputs;
public static FilterSocket createFilterSocket(AudioFilter[] filters) {
FilterSocket fs = new FilterSocket();
fs.totalInputs = 0;
fs.totalOutputs = 0;
for (int j = 0; j < filters.length; j++) {
fs.totalInputs += filters[j].getNInputs();
fs.totalOutputs += filters[j].getNOutputs();
}
fs.buffers = new FloatBuffer[fs.totalInputs];
fs.positions = new long[fs.totalInputs];
fs.delays = new int[fs.totalInputs];
for (int i = 0, b = 0; i < filters.length; i++) {
for (int k = 0; k < filters[i].getNInputs(); k++, b++)
fs.delays[b] = filters[i].getDelay();
}
fs.filters = filters;
return fs;
}
public void allocateBuffers(int bufferSize) {
for (int i = 0; i < this.totalInputs; i++) {
this.buffers[i] = FloatBuffer.allocate(bufferSize + this.delays[i] * 2);
this.buffers[i].position(this.delays[i]);
}
}
public static FilterSocket createFilterSocket2(AudioFilter filter, FloatBuffer[] buffers, long[] positions) {
FilterSocket fs = new FilterSocket();
fs.filters = new AudioFilter[] { filter };
fs.buffers = buffers;
fs.positions = positions;
fs.delays = new int[] { filter.getDelay() };
fs.totalInputs = filter.getNInputs();
fs.totalOutputs = filter.getNOutputs();
return fs;
}
public void filter(FloatBuffer[] outputs) {
if (outputs.length != this.totalOutputs)
throw new IllegalArgumentException("Can not output to provided filter socket inputs != outputs (" + outputs.length + "!=" + this.totalOutputs + ")");
for (int i = 0, ii = 0, oi = 0; i < this.filters.length; ii += this.filters[i].getNInputs(), oi += this.filters[i]
.getNOutputs(), i++)
this.filters[i].filter(Platform.<FloatBuffer>copyOfRangeO(this.buffers, ii, this.filters[i].getNInputs() + ii),
Platform.copyOfRangeL(this.positions, ii, this.filters[i].getNInputs() + ii),
Platform.<FloatBuffer>copyOfRangeO(outputs, oi, this.filters[i].getNOutputs() + oi));
}
FloatBuffer[] getBuffers() {
return this.buffers;
}
public void rotate() {
for (int i = 0; i < this.buffers.length; i++) {
this.positions[i] = this.positions[i] + (long)this.buffers[i].position();
Audio.rotate(this.buffers[i]);
}
}
public void setBuffers(FloatBuffer[] ins, long[] pos) {
if (ins.length != this.totalInputs)
throw new IllegalArgumentException("Number of input buffers provided is less then the number of filter inputs.");
if (pos.length != this.totalInputs)
throw new IllegalArgumentException("Number of input buffer positions provided is less then the number of filter inputs.");
this.buffers = ins;
this.positions = pos;
}
public int getTotalInputs() {
return this.totalInputs;
}
public int getTotalOutputs() {
return this.totalOutputs;
}
AudioFilter[] getFilters() {
return this.filters;
}
public long[] getPositions() {
return this.positions;
}
}

View file

@ -0,0 +1,65 @@
package org.jcodec.audio;
import java.nio.FloatBuffer;
public class LanczosInterpolator implements AudioFilter {
private double rateStep;
public static double lanczos(double x, int a) {
return (x < (double)-a) ? 0.0D : ((x > (double)a) ? 0.0D : (
(double)a * Math.sin(Math.PI * x) * Math.sin(Math.PI * x / (double)a) / (9.869604401089358D * x * x)));
}
public LanczosInterpolator(int fromRate, int toRate) {
this.rateStep = (double)fromRate / (double)toRate;
}
public void filter(FloatBuffer[] _in, long[] pos, FloatBuffer[] out) {
if (_in.length != 1)
throw new IllegalArgumentException(getClass().getName() + " filter is designed to work only on one input");
if (out.length != 1)
throw new IllegalArgumentException(getClass().getName() + " filter is designed to work only on one output");
FloatBuffer in0 = _in[0];
FloatBuffer out0 = out[0];
if ((double)out0.remaining() < (double)(in0.remaining() - 6) / this.rateStep)
throw new IllegalArgumentException("Output buffer is too small");
if (in0.remaining() <= 6)
throw new IllegalArgumentException("Input buffer should contain > 6 samples.");
for (int outSample = 0;; outSample++) {
double inSample = 3.0D + (double)outSample * this.rateStep + Math.ceil((double)pos[0] / this.rateStep) * this.rateStep - (double)pos[0];
int p0i = (int)Math.floor(inSample);
int q0i = (int)Math.ceil(inSample);
if (p0i >= in0.limit() - 3) {
in0.position(p0i - 3);
break;
}
double p0d = (double)p0i - inSample;
if (p0d < -0.001D) {
double q0d = (double)q0i - inSample;
double p0c = lanczos(p0d, 3);
double q0c = lanczos(q0d, 3);
double p1c = lanczos(p0d - 1.0D, 3);
double q1c = lanczos(q0d + 1.0D, 3);
double p2c = lanczos(p0d - 2.0D, 3);
double q2c = lanczos(q0d + 2.0D, 3);
double factor = 1.0D / (p0c + p1c + p2c + q0c + q1c + q2c);
out0.put(
(float)(((double)in0.get(q0i) * q0c + (double)in0.get(q0i + 1) * q1c + (double)in0.get(q0i + 2) * q2c + (double)in0.get(p0i) * p0c + (double)in0.get(p0i - 1) * p1c + (double)in0.get(p0i - 2) * p2c) * factor));
} else {
out0.put(in0.get(p0i));
}
}
}
public int getDelay() {
return 3;
}
public int getNInputs() {
return 1;
}
public int getNOutputs() {
return 1;
}
}

View file

@ -0,0 +1,38 @@
package org.jcodec.audio;
public class SincLowPassFilter extends ConvolutionFilter {
private int kernelSize;
private double cutoffFreq;
public static SincLowPassFilter createSincLowPassFilter(double cutoffFreq) {
return new SincLowPassFilter(40, cutoffFreq);
}
public static SincLowPassFilter createSincLowPassFilter2(int cutoffFreq, int samplingRate) {
return new SincLowPassFilter(40, (double)cutoffFreq / (double)samplingRate);
}
public SincLowPassFilter(int kernelSize, double cutoffFreq) {
this.kernelSize = kernelSize;
this.cutoffFreq = cutoffFreq;
}
protected double[] buildKernel() {
double[] kernel = new double[this.kernelSize];
double sum = 0.0D;
for (int j = 0; j < this.kernelSize; j++) {
int a = j - this.kernelSize / 2;
if (a != 0) {
kernel[j] = Math.sin(6.283185307179586D * this.cutoffFreq * (double)(j - this.kernelSize / 2)) / (double)(j - this.kernelSize / 2) * (0.54D - 0.46D *
Math.cos(6.283185307179586D * (double)j / (double)this.kernelSize));
} else {
kernel[j] = 6.283185307179586D * this.cutoffFreq;
}
sum += kernel[j];
}
for (int i = 0; i < this.kernelSize; i++)
kernel[i] = kernel[i] / sum;
return kernel;
}
}

View file

@ -0,0 +1,9 @@
package org.jcodec.codecs.aac;
public class AACConts {
public static final short[] AAC_CHANNEL_COUNT = new short[] { 0, 1, 2, 3, 4, 5, 6, 8 };
public static int[] AAC_SAMPLE_RATES = new int[] {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000,
11025, 8000, 7350 };
}

View file

@ -0,0 +1,59 @@
package org.jcodec.codecs.aac;
import java.io.IOException;
import java.nio.ByteBuffer;
import net.sourceforge.jaad.aac.AACException;
import net.sourceforge.jaad.aac.Decoder;
import net.sourceforge.jaad.aac.SampleBuffer;
import org.jcodec.common.AudioCodecMeta;
import org.jcodec.common.AudioDecoder;
import org.jcodec.common.AudioFormat;
import org.jcodec.common.UsedViaReflection;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.AudioBuffer;
public class AACDecoder implements AudioDecoder {
private Decoder decoder;
public AACDecoder(ByteBuffer decoderSpecific) throws AACException {
if (decoderSpecific.remaining() >= 7) {
ADTSParser.Header header = ADTSParser.read(decoderSpecific);
if (header != null)
decoderSpecific = ADTSParser.adtsToStreamInfo(header);
Logger.info("Creating AAC decoder from ADTS header.");
}
this.decoder = new Decoder(NIOUtils.toArray(decoderSpecific));
}
public AudioBuffer decodeFrame(ByteBuffer frame, ByteBuffer dst) throws IOException {
ADTSParser.read(frame);
SampleBuffer sampleBuffer = new SampleBuffer();
this.decoder.decodeFrame(NIOUtils.toArray(frame), sampleBuffer);
if (sampleBuffer.isBigEndian())
sampleBuffer.setBigEndian(false);
return new AudioBuffer(ByteBuffer.wrap(sampleBuffer.getData()), toAudioFormat(sampleBuffer), 0);
}
private AudioFormat toAudioFormat(SampleBuffer sampleBuffer) {
return new AudioFormat(sampleBuffer.getSampleRate(), sampleBuffer.getBitsPerSample(),
sampleBuffer.getChannels(), true, sampleBuffer.isBigEndian());
}
public AudioCodecMeta getCodecMeta(ByteBuffer data) throws IOException {
SampleBuffer sampleBuffer = new SampleBuffer();
this.decoder.decodeFrame(NIOUtils.toArray(data), sampleBuffer);
sampleBuffer.setBigEndian(false);
return AudioCodecMeta.fromAudioFormat(toAudioFormat(sampleBuffer));
}
@UsedViaReflection
public static int probe(ByteBuffer data) {
if (data.remaining() < 7)
return 0;
ADTSParser.Header header = ADTSParser.read(data);
if (header != null)
return 100;
return 0;
}
}

View file

@ -0,0 +1,80 @@
package org.jcodec.codecs.aac;
import java.nio.ByteBuffer;
import org.jcodec.codecs.mpeg4.mp4.EsdsBox;
import org.jcodec.common.AudioFormat;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.model.ChannelLabel;
import org.jcodec.containers.mp4.BoxUtil;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.NodeBox;
import org.jcodec.containers.mp4.boxes.SampleEntry;
public class AACUtils {
public static class AACMetadata {
private AudioFormat format;
private ChannelLabel[] labels;
public AACMetadata(AudioFormat format, ChannelLabel[] labels) {
this.format = format;
this.labels = labels;
}
public AudioFormat getFormat() {
return this.format;
}
public ChannelLabel[] getLabels() {
return this.labels;
}
}
private static int getObjectType(BitReader reader) {
int objectType = reader.readNBit(5);
if (objectType == ObjectType.AOT_ESCAPE.ordinal())
objectType = 32 + reader.readNBit(6);
return objectType;
}
private static ChannelLabel[][] AAC_DEFAULT_CONFIGS = new ChannelLabel[][] { null, new ChannelLabel[] { ChannelLabel.MONO }, new ChannelLabel[] { ChannelLabel.STEREO_LEFT, ChannelLabel.STEREO_RIGHT }, new ChannelLabel[] { ChannelLabel.CENTER, ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT }, new ChannelLabel[] { ChannelLabel.CENTER, ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT, ChannelLabel.REAR_CENTER }, new ChannelLabel[] { ChannelLabel.CENTER, ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT, ChannelLabel.REAR_LEFT, ChannelLabel.REAR_RIGHT }, new ChannelLabel[] { ChannelLabel.CENTER, ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT, ChannelLabel.REAR_LEFT, ChannelLabel.REAR_RIGHT, ChannelLabel.LFE }, new ChannelLabel[] { ChannelLabel.CENTER, ChannelLabel.FRONT_LEFT, ChannelLabel.FRONT_RIGHT, ChannelLabel.SIDE_LEFT, ChannelLabel.SIDE_RIGHT, ChannelLabel.REAR_LEFT, ChannelLabel.REAR_RIGHT, ChannelLabel.LFE } };
public static AACMetadata parseAudioInfo(ByteBuffer privData) {
BitReader reader = BitReader.createBitReader(privData);
int objectType = getObjectType(reader);
int index = reader.readNBit(4);
int sampleRate = (index == 15) ? reader.readNBit(24) : AACConts.AAC_SAMPLE_RATES[index];
int channelConfig = reader.readNBit(4);
if (channelConfig == 0 || channelConfig >= AAC_DEFAULT_CONFIGS.length)
return null;
ChannelLabel[] channels = AAC_DEFAULT_CONFIGS[channelConfig];
return new AACMetadata(new AudioFormat(sampleRate, 16, channels.length, true, false), channels);
}
public static AACMetadata getMetadata(SampleEntry mp4a) {
if (!"mp4a".equals(mp4a.getFourcc()))
throw new IllegalArgumentException("Not mp4a sample entry");
ByteBuffer b = getCodecPrivate(mp4a);
if (b == null)
return null;
return parseAudioInfo(b);
}
public static ByteBuffer getCodecPrivate(SampleEntry mp4a) {
Box.LeafBox b = NodeBox.<Box.LeafBox>findFirst(mp4a, Box.LeafBox.class, "esds");
if (b == null)
b = NodeBox.<Box.LeafBox>findFirstPath(mp4a, Box.LeafBox.class, new String[] { null, "esds" });
if (b == null)
return null;
EsdsBox esds = BoxUtil.<EsdsBox>as(EsdsBox.class, b);
return esds.getStreamInfo();
}
public static ADTSParser.Header streamInfoToADTS(ByteBuffer si, boolean crcAbsent, int numAACFrames, int frameSize) {
BitReader rd = BitReader.createBitReader(si.duplicate());
int objectType = rd.readNBit(5);
int samplingIndex = rd.readNBit(4);
int chanConfig = rd.readNBit(4);
return new ADTSParser.Header(objectType, chanConfig, crcAbsent ? 1 : 0, numAACFrames, samplingIndex, 7 + frameSize);
}
}

View file

@ -0,0 +1,124 @@
package org.jcodec.codecs.aac;
import java.nio.ByteBuffer;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.BitWriter;
public class ADTSParser {
public static ByteBuffer adtsToStreamInfo(Header hdr) {
ByteBuffer si = ByteBuffer.allocate(2);
BitWriter wr = new BitWriter(si);
wr.writeNBit(hdr.getObjectType(), 5);
wr.writeNBit(hdr.getSamplingIndex(), 4);
wr.writeNBit(hdr.getChanConfig(), 4);
wr.flush();
si.clear();
return si;
}
public static class Header {
private int objectType;
private int chanConfig;
private int crcAbsent;
private int numAACFrames;
private int samplingIndex;
private int samples;
private int size;
public Header(int object_type, int chanConfig, int crcAbsent, int numAACFrames, int samplingIndex, int size) {
this.objectType = object_type;
this.chanConfig = chanConfig;
this.crcAbsent = crcAbsent;
this.numAACFrames = numAACFrames;
this.samplingIndex = samplingIndex;
this.size = size;
}
public int getObjectType() {
return this.objectType;
}
public int getChanConfig() {
return this.chanConfig;
}
public int getCrcAbsent() {
return this.crcAbsent;
}
public int getNumAACFrames() {
return this.numAACFrames;
}
public int getSamplingIndex() {
return this.samplingIndex;
}
public int getSamples() {
return this.samples;
}
public int getSize() {
return this.size;
}
public int getSampleRate() {
return AACConts.AAC_SAMPLE_RATES[this.samplingIndex];
}
}
public static Header read(ByteBuffer data) {
ByteBuffer dup = data.duplicate();
BitReader br = BitReader.createBitReader(dup);
if (br.readNBit(12) != 4095)
return null;
int id = br.read1Bit();
int layer = br.readNBit(2);
int crc_abs = br.read1Bit();
int aot = br.readNBit(2);
int sr = br.readNBit(4);
int pb = br.read1Bit();
int ch = br.readNBit(3);
int origCopy = br.read1Bit();
int home = br.read1Bit();
int copy = br.read1Bit();
int copyStart = br.read1Bit();
int size = br.readNBit(13);
if (size < 7)
return null;
int buffer = br.readNBit(11);
int rdb = br.readNBit(2);
br.stop();
data.position(dup.position());
return new Header(aot + 1, ch, crc_abs, rdb + 1, sr, size);
}
public static ByteBuffer write(Header header, ByteBuffer buf) {
ByteBuffer data = buf.duplicate();
BitWriter br = new BitWriter(data);
br.writeNBit(4095, 12);
br.write1Bit(1);
br.writeNBit(0, 2);
br.write1Bit(header.getCrcAbsent());
br.writeNBit(header.getObjectType() - 1, 2);
br.writeNBit(header.getSamplingIndex(), 4);
br.write1Bit(0);
br.writeNBit(header.getChanConfig(), 3);
br.write1Bit(0);
br.write1Bit(0);
br.write1Bit(0);
br.write1Bit(0);
br.writeNBit(header.getSize(), 13);
br.writeNBit(0, 11);
br.writeNBit(header.getNumAACFrames() - 1, 2);
br.flush();
data.flip();
return data;
}
}

View file

@ -0,0 +1,14 @@
package org.jcodec.codecs.aac;
import org.jcodec.codecs.aac.blocks.Block;
import org.jcodec.common.io.BitReader;
public class BlockReader {
public Block nextBlock(BitReader bits) {
BlockType type = BlockType.values()[(int)(long)bits.readNBit(3)];
if (type == BlockType.TYPE_END)
return null;
int id = bits.readNBit(4);
return null;
}
}

View file

@ -0,0 +1,5 @@
package org.jcodec.codecs.aac;
public enum BlockType {
TYPE_SCE, TYPE_CPE, TYPE_CCE, TYPE_LFE, TYPE_DSE, TYPE_PCE, TYPE_FIL, TYPE_END;
}

View file

@ -0,0 +1,12 @@
package org.jcodec.codecs.aac;
import org.jcodec.codecs.aac.blocks.Block;
import org.jcodec.common.io.BitWriter;
public class BlockWriter {
public void nextBlock(BitWriter bits, Block block) {
bits.writeNBit(block.getType().ordinal(), 3);
if (block.getType() == BlockType.TYPE_END)
return;
}
}

View file

@ -0,0 +1,5 @@
package org.jcodec.codecs.aac;
public enum ChannelPosition {
AAC_CHANNEL_FRONT, AAC_CHANNEL_SIDE, AAC_CHANNEL_BACK, AAC_CHANNEL_LFE, AAC_CHANNEL_CC;
}

View file

@ -0,0 +1,5 @@
package org.jcodec.codecs.aac;
public enum ObjectType {
AOT_NULL, AOT_AAC_MAIN, AOT_AAC_LC, AOT_AAC_SSR, AOT_AAC_LTP, AOT_SBR, AOT_AAC_SCALABLE, AOT_TWINVQ, AOT_CELP, AOT_HVXC, CRAP1, CRAP2, AOT_TTSI, AOT_MAINSYNTH, AOT_WAVESYNTH, AOT_MIDI, AOT_SAFX, AOT_ER_AAC_LC, CRAP3, AOT_ER_AAC_LTP, AOT_ER_AAC_SCALABLE, AOT_ER_TWINVQ, AOT_ER_BSAC, AOT_ER_AAC_LD, AOT_ER_CELP, AOT_ER_HVXC, AOT_ER_HILN, AOT_ER_PARAM, AOT_SSC, AOT_PS, AOT_SURROUND, AOT_ESCAPE, AOT_L1, AOT_L2, AOT_L3, AOT_DST, AOT_ALS, AOT_SLS, AOT_SLS_NON_CORE, AOT_ER_AAC_ELD, AOT_SMR_SIMPLE, AOT_SMR_MAIN, AOT_USAC_NOSBR, AOT_SAOC, AOT_LD_SURROUND, AOT_USAC;
}

View file

@ -0,0 +1,5 @@
package org.jcodec.codecs.aac;
public enum Profile {
MAIN, LC, OTHER;
}

View file

@ -0,0 +1,532 @@
package org.jcodec.codecs.aac.blocks;
public class AACTab {
static int[] codes1 = new int[] {
2040, 497, 2045, 1013, 104, 1008, 2039, 492, 2037, 1009,
114, 1012, 116, 17, 118, 491, 108, 1014, 2044, 481,
2033, 496, 97, 502, 2034, 490, 2043, 498, 105, 493,
119, 23, 111, 486, 100, 485, 103, 21, 98, 18,
0, 20, 101, 22, 109, 489, 99, 484, 107, 19,
113, 483, 112, 499, 2046, 487, 2035, 495, 96, 494,
2032, 482, 2042, 1011, 106, 488, 117, 16, 115, 500,
110, 1015, 2038, 480, 2041, 1010, 102, 501, 2047, 503,
2036 };
static int[] bits1 = new int[] {
11, 9, 11, 10, 7, 10, 11, 9, 11, 10,
7, 10, 7, 5, 7, 9, 7, 10, 11, 9,
11, 9, 7, 9, 11, 9, 11, 9, 7, 9,
7, 5, 7, 9, 7, 9, 7, 5, 7, 5,
1, 5, 7, 5, 7, 9, 7, 9, 7, 5,
7, 9, 7, 9, 11, 9, 11, 9, 7, 9,
11, 9, 11, 10, 7, 9, 7, 5, 7, 9,
7, 10, 11, 9, 11, 10, 7, 9, 11, 9,
11 };
static int[] codes2 = new int[] {
499, 111, 509, 235, 35, 234, 503, 232, 506, 242,
45, 112, 32, 6, 43, 110, 40, 233, 505, 102,
248, 231, 27, 241, 500, 107, 501, 236, 42, 108,
44, 10, 39, 103, 26, 245, 36, 8, 31, 9,
0, 7, 29, 11, 48, 239, 28, 100, 30, 12,
41, 243, 47, 240, 508, 113, 498, 244, 33, 230,
247, 104, 504, 238, 34, 101, 49, 2, 38, 237,
37, 106, 507, 114, 510, 105, 46, 246, 511, 109,
502 };
static int[] bits2 = new int[] {
9, 7, 9, 8, 6, 8, 9, 8, 9, 8,
6, 7, 6, 5, 6, 7, 6, 8, 9, 7,
8, 8, 6, 8, 9, 7, 9, 8, 6, 7,
6, 5, 6, 7, 6, 8, 6, 5, 6, 5,
3, 5, 6, 5, 6, 8, 6, 7, 6, 5,
6, 8, 6, 8, 9, 7, 9, 8, 6, 8,
8, 7, 9, 8, 6, 7, 6, 4, 6, 8,
6, 7, 9, 7, 9, 7, 6, 8, 9, 7,
9 };
static int[] codes3 = new int[] {
0, 9, 239, 11, 25, 240, 491, 486, 1010, 10,
53, 495, 52, 55, 489, 493, 487, 1011, 494, 1005,
8186, 492, 498, 2041, 2040, 1016, 4088, 8, 56, 1014,
54, 117, 1009, 1003, 1004, 4084, 24, 118, 2036, 57,
116, 1007, 499, 500, 2038, 488, 1002, 8188, 242, 497,
4091, 1013, 2035, 4092, 238, 1015, 32766, 496, 2037, 32765,
8187, 16378, 65535, 241, 1008, 16380, 490, 1006, 16379, 4086,
4090, 32764, 2034, 4085, 65534, 1012, 2039, 32763, 4087, 4089,
32762 };
static int[] bits3 = new int[] {
1, 4, 8, 4, 5, 8, 9, 9, 10, 4,
6, 9, 6, 6, 9, 9, 9, 10, 9, 10,
13, 9, 9, 11, 11, 10, 12, 4, 6, 10,
6, 7, 10, 10, 10, 12, 5, 7, 11, 6,
7, 10, 9, 9, 11, 9, 10, 13, 8, 9,
12, 10, 11, 12, 8, 10, 15, 9, 11, 15,
13, 14, 16, 8, 10, 14, 9, 10, 14, 12,
12, 15, 11, 12, 16, 10, 11, 15, 12, 12,
15 };
static int[] codes4 = new int[] {
7, 22, 246, 24, 8, 239, 495, 243, 2040, 25,
23, 237, 21, 1, 226, 240, 112, 1008, 494, 241,
2042, 238, 228, 1010, 2038, 1007, 2045, 5, 20, 242,
9, 4, 229, 244, 232, 1012, 6, 2, 231, 3,
0, 107, 227, 105, 499, 235, 230, 1014, 110, 106,
500, 1004, 496, 1017, 245, 236, 2043, 234, 111, 1015,
2041, 1011, 4095, 233, 109, 1016, 108, 104, 501, 1006,
498, 2036, 2039, 1009, 4094, 1005, 497, 2037, 2046, 1013,
2044 };
static int[] bits4 = new int[] {
4, 5, 8, 5, 4, 8, 9, 8, 11, 5,
5, 8, 5, 4, 8, 8, 7, 10, 9, 8,
11, 8, 8, 10, 11, 10, 11, 4, 5, 8,
4, 4, 8, 8, 8, 10, 4, 4, 8, 4,
4, 7, 8, 7, 9, 8, 8, 10, 7, 7,
9, 10, 9, 10, 8, 8, 11, 8, 7, 10,
11, 10, 12, 8, 7, 10, 7, 7, 9, 10,
9, 11, 11, 10, 12, 10, 9, 11, 11, 10,
11 };
static int[] codes5 = new int[] {
8191, 4087, 2036, 2024, 1009, 2030, 2041, 4088, 8189, 4093,
2033, 1000, 488, 240, 492, 1006, 2034, 4090, 4084, 1007,
498, 232, 112, 236, 496, 1002, 2035, 2027, 491, 234,
26, 8, 25, 238, 495, 2029, 1008, 242, 115, 11,
0, 10, 113, 243, 2025, 2031, 494, 239, 24, 9,
27, 235, 489, 2028, 2038, 1003, 499, 237, 114, 233,
497, 1005, 2039, 4086, 2032, 1001, 493, 241, 490, 1004,
2040, 4089, 8188, 4092, 4085, 2026, 1011, 1010, 2037, 4091,
8190 };
static int[] bits5 = new int[] {
13, 12, 11, 11, 10, 11, 11, 12, 13, 12,
11, 10, 9, 8, 9, 10, 11, 12, 12, 10,
9, 8, 7, 8, 9, 10, 11, 11, 9, 8,
5, 4, 5, 8, 9, 11, 10, 8, 7, 4,
1, 4, 7, 8, 11, 11, 9, 8, 5, 4,
5, 8, 9, 11, 11, 10, 9, 8, 7, 8,
9, 10, 11, 12, 11, 10, 9, 8, 9, 10,
11, 12, 13, 12, 12, 11, 10, 10, 11, 12,
13 };
static int[] codes6 = new int[] {
2046, 1021, 497, 491, 500, 490, 496, 1020, 2045, 1014,
485, 234, 108, 113, 104, 240, 486, 1015, 499, 239,
50, 39, 40, 38, 49, 235, 503, 488, 111, 46,
8, 4, 6, 41, 107, 494, 495, 114, 45, 2,
0, 3, 47, 115, 506, 487, 110, 43, 7, 1,
5, 44, 109, 492, 505, 238, 48, 36, 42, 37,
51, 236, 498, 1016, 484, 237, 106, 112, 105, 116,
241, 1018, 2047, 1017, 502, 493, 504, 489, 501, 1019,
2044 };
static int[] bits6 = new int[] {
11, 10, 9, 9, 9, 9, 9, 10, 11, 10,
9, 8, 7, 7, 7, 8, 9, 10, 9, 8,
6, 6, 6, 6, 6, 8, 9, 9, 7, 6,
4, 4, 4, 6, 7, 9, 9, 7, 6, 4,
4, 4, 6, 7, 9, 9, 7, 6, 4, 4,
4, 6, 7, 9, 9, 8, 6, 6, 6, 6,
6, 8, 9, 10, 9, 8, 7, 7, 7, 7,
8, 10, 11, 10, 9, 9, 9, 9, 9, 10,
11 };
static int[] codes7 = new int[] {
0, 5, 55, 116, 242, 491, 1005, 2039, 4, 12,
53, 113, 236, 238, 494, 501, 54, 52, 114, 234,
241, 489, 499, 1013, 115, 112, 235, 240, 497, 496,
1004, 1018, 243, 237, 488, 495, 1007, 1009, 1017, 2043,
493, 239, 490, 498, 1011, 1016, 2041, 2044, 1006, 492,
500, 1012, 1015, 2040, 4093, 4094, 2038, 1008, 1010, 1014,
2042, 2045, 4092, 4095 };
static int[] bits7 = new int[] {
1, 3, 6, 7, 8, 9, 10, 11, 3, 4,
6, 7, 8, 8, 9, 9, 6, 6, 7, 8,
8, 9, 9, 10, 7, 7, 8, 8, 9, 9,
10, 10, 8, 8, 9, 9, 10, 10, 10, 11,
9, 8, 9, 9, 10, 10, 11, 11, 10, 9,
9, 10, 10, 11, 12, 12, 11, 10, 10, 10,
11, 11, 12, 12 };
static int[] codes8 = new int[] {
14, 5, 16, 48, 111, 241, 506, 1022, 3, 0,
4, 18, 44, 106, 117, 248, 15, 2, 6, 20,
46, 105, 114, 245, 47, 17, 19, 42, 50, 108,
236, 250, 113, 43, 45, 49, 109, 112, 242, 505,
239, 104, 51, 107, 110, 238, 249, 1020, 504, 116,
115, 237, 240, 246, 502, 509, 1021, 243, 244, 247,
503, 507, 508, 1023 };
static int[] bits8 = new int[] {
5, 4, 5, 6, 7, 8, 9, 10, 4, 3,
4, 5, 6, 7, 7, 8, 5, 4, 4, 5,
6, 7, 7, 8, 6, 5, 5, 6, 6, 7,
8, 8, 7, 6, 6, 6, 7, 7, 8, 9,
8, 7, 6, 7, 7, 8, 8, 10, 9, 7,
7, 8, 8, 8, 9, 9, 10, 8, 8, 8,
9, 9, 9, 10 };
static int[] codes9 = new int[] {
0, 5, 55, 231, 478, 974, 985, 1992, 1997, 4040,
4061, 8164, 8172, 4, 12, 53, 114, 234, 237, 482,
977, 979, 992, 2008, 4047, 4053, 54, 52, 113, 232,
236, 481, 975, 989, 987, 2000, 4039, 4052, 4068, 230,
112, 233, 477, 483, 978, 988, 1996, 1994, 2014, 4056,
4074, 8155, 479, 235, 476, 486, 981, 990, 1995, 2013,
2012, 4045, 4066, 4071, 8161, 976, 480, 484, 982, 1989,
2001, 2011, 4050, 2016, 4057, 4075, 8163, 8169, 1988, 485,
983, 1990, 1999, 2010, 4043, 4058, 4067, 4073, 8166, 8179,
8183, 2003, 984, 993, 2004, 2009, 4051, 4062, 8157, 8153,
8162, 8170, 8177, 8182, 2002, 980, 986, 1991, 2007, 2018,
4046, 4059, 8152, 8174, 16368, 8180, 16370, 2017, 991, 1993,
2006, 4042, 4048, 4069, 4070, 8171, 8175, 16371, 16372, 16373,
4064, 1998, 2005, 4038, 4049, 4065, 8160, 8168, 8176, 16369,
16376, 16374, 32764, 4072, 2015, 4041, 4055, 4060, 8156, 8159,
8173, 8181, 16377, 16379, 32765, 32766, 8167, 4044, 4054, 4063,
8158, 8154, 8165, 8178, 16378, 16375, 16380, 16381, 32767 };
static int[] bits9 = new int[] {
1, 3, 6, 8, 9, 10, 10, 11, 11, 12,
12, 13, 13, 3, 4, 6, 7, 8, 8, 9,
10, 10, 10, 11, 12, 12, 6, 6, 7, 8,
8, 9, 10, 10, 10, 11, 12, 12, 12, 8,
7, 8, 9, 9, 10, 10, 11, 11, 11, 12,
12, 13, 9, 8, 9, 9, 10, 10, 11, 11,
11, 12, 12, 12, 13, 10, 9, 9, 10, 11,
11, 11, 12, 11, 12, 12, 13, 13, 11, 9,
10, 11, 11, 11, 12, 12, 12, 12, 13, 13,
13, 11, 10, 10, 11, 11, 12, 12, 13, 13,
13, 13, 13, 13, 11, 10, 10, 11, 11, 11,
12, 12, 13, 13, 14, 13, 14, 11, 10, 11,
11, 12, 12, 12, 12, 13, 13, 14, 14, 14,
12, 11, 11, 12, 12, 12, 13, 13, 13, 14,
14, 14, 15, 12, 11, 12, 12, 12, 13, 13,
13, 13, 14, 14, 15, 15, 13, 12, 12, 12,
13, 13, 13, 13, 14, 14, 14, 14, 15 };
static int[] codes10 = new int[] {
34, 8, 29, 38, 95, 211, 463, 976, 983, 1005,
2032, 2038, 4093, 7, 0, 1, 9, 32, 84, 96,
213, 220, 468, 973, 990, 2023, 28, 2, 6, 12,
30, 40, 91, 205, 217, 462, 476, 985, 1009, 37,
11, 10, 13, 36, 87, 97, 204, 221, 460, 478,
979, 999, 93, 33, 31, 35, 39, 89, 100, 216,
223, 466, 482, 989, 1006, 209, 85, 41, 86, 88,
98, 206, 224, 226, 474, 980, 995, 2027, 457, 94,
90, 92, 99, 202, 218, 455, 458, 480, 987, 1000,
2028, 483, 210, 203, 208, 215, 219, 454, 469, 472,
970, 986, 2026, 2033, 481, 212, 207, 214, 222, 225,
464, 470, 977, 981, 1010, 2030, 2043, 1001, 461, 456,
459, 465, 471, 479, 975, 992, 1007, 2022, 2040, 4090,
1003, 477, 467, 473, 475, 978, 972, 988, 1002, 2029,
2035, 2041, 4089, 2034, 974, 484, 971, 984, 982, 994,
997, 2024, 2036, 2037, 2039, 4091, 2042, 1004, 991, 993,
996, 998, 1008, 2025, 2031, 4088, 4094, 4092, 4095 };
static int[] bits10 = new int[] {
6, 5, 6, 6, 7, 8, 9, 10, 10, 10,
11, 11, 12, 5, 4, 4, 5, 6, 7, 7,
8, 8, 9, 10, 10, 11, 6, 4, 5, 5,
6, 6, 7, 8, 8, 9, 9, 10, 10, 6,
5, 5, 5, 6, 7, 7, 8, 8, 9, 9,
10, 10, 7, 6, 6, 6, 6, 7, 7, 8,
8, 9, 9, 10, 10, 8, 7, 6, 7, 7,
7, 8, 8, 8, 9, 10, 10, 11, 9, 7,
7, 7, 7, 8, 8, 9, 9, 9, 10, 10,
11, 9, 8, 8, 8, 8, 8, 9, 9, 9,
10, 10, 11, 11, 9, 8, 8, 8, 8, 8,
9, 9, 10, 10, 10, 11, 11, 10, 9, 9,
9, 9, 9, 9, 10, 10, 10, 11, 11, 12,
10, 9, 9, 9, 9, 10, 10, 10, 10, 11,
11, 11, 12, 11, 10, 9, 10, 10, 10, 10,
10, 11, 11, 11, 11, 12, 11, 10, 10, 10,
10, 10, 10, 11, 11, 12, 12, 12, 12 };
static int[] codes11 = new int[] {
0, 6, 25, 61, 156, 198, 423, 912, 962, 991,
2022, 2035, 4091, 2028, 4090, 4094, 910, 5, 1, 8,
20, 55, 66, 146, 175, 401, 421, 437, 926, 960,
930, 973, 2006, 174, 23, 7, 9, 24, 57, 64,
142, 163, 184, 409, 428, 449, 945, 918, 958, 970,
157, 60, 21, 22, 26, 59, 68, 145, 165, 190,
406, 430, 441, 929, 913, 933, 981, 148, 154, 54,
56, 58, 65, 140, 155, 176, 195, 414, 427, 444,
927, 911, 937, 975, 147, 191, 62, 63, 67, 69,
158, 167, 185, 404, 418, 442, 451, 934, 935, 955,
980, 159, 416, 143, 141, 144, 152, 166, 182, 196,
415, 431, 447, 921, 959, 948, 969, 999, 168, 438,
171, 164, 170, 178, 194, 197, 408, 420, 440, 908,
932, 964, 966, 989, 1000, 173, 943, 402, 189, 188,
398, 407, 410, 419, 433, 909, 920, 951, 979, 977,
987, 2013, 180, 990, 425, 411, 412, 417, 426, 429,
435, 907, 946, 952, 974, 993, 992, 2002, 2021, 183,
2019, 443, 424, 422, 432, 434, 439, 923, 922, 954,
949, 982, 2007, 996, 2008, 2026, 186, 2024, 928, 445,
436, 906, 452, 914, 938, 944, 956, 983, 2004, 2012,
2011, 2005, 2032, 193, 2043, 968, 931, 917, 925, 940,
942, 965, 984, 994, 998, 2020, 2023, 2016, 2025, 2039,
400, 2034, 915, 446, 448, 916, 919, 941, 963, 961,
978, 2010, 2009, 2015, 2027, 2036, 2042, 405, 2040, 957,
924, 939, 936, 947, 953, 976, 995, 997, 2018, 2014,
2029, 2033, 2041, 2044, 403, 4093, 988, 950, 967, 972,
971, 985, 986, 2003, 2017, 2030, 2031, 2037, 2038, 4092,
4095, 413, 450, 181, 161, 150, 151, 149, 153, 160,
162, 172, 169, 177, 179, 187, 192, 399, 4 };
static int[] bits11 = new int[] {
4, 5, 6, 7, 8, 8, 9, 10, 10, 10,
11, 11, 12, 11, 12, 12, 10, 5, 4, 5,
6, 7, 7, 8, 8, 9, 9, 9, 10, 10,
10, 10, 11, 8, 6, 5, 5, 6, 7, 7,
8, 8, 8, 9, 9, 9, 10, 10, 10, 10,
8, 7, 6, 6, 6, 7, 7, 8, 8, 8,
9, 9, 9, 10, 10, 10, 10, 8, 8, 7,
7, 7, 7, 8, 8, 8, 8, 9, 9, 9,
10, 10, 10, 10, 8, 8, 7, 7, 7, 7,
8, 8, 8, 9, 9, 9, 9, 10, 10, 10,
10, 8, 9, 8, 8, 8, 8, 8, 8, 8,
9, 9, 9, 10, 10, 10, 10, 10, 8, 9,
8, 8, 8, 8, 8, 8, 9, 9, 9, 10,
10, 10, 10, 10, 10, 8, 10, 9, 8, 8,
9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
10, 11, 8, 10, 9, 9, 9, 9, 9, 9,
9, 10, 10, 10, 10, 10, 10, 11, 11, 8,
11, 9, 9, 9, 9, 9, 9, 10, 10, 10,
10, 10, 11, 10, 11, 11, 8, 11, 10, 9,
9, 10, 9, 10, 10, 10, 10, 10, 11, 11,
11, 11, 11, 8, 11, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 11, 11, 11, 11, 11,
9, 11, 10, 9, 9, 10, 10, 10, 10, 10,
10, 11, 11, 11, 11, 11, 11, 9, 11, 10,
10, 10, 10, 10, 10, 10, 10, 10, 11, 11,
11, 11, 11, 11, 9, 12, 10, 10, 10, 10,
10, 10, 10, 11, 11, 11, 11, 11, 11, 12,
12, 9, 9, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 9, 5 };
static int[] ff_aac_scalefactor_code = new int[] {
262120, 262118, 262119, 262117, 524277, 524273, 524269, 524278, 524270, 524271,
524272, 524284, 524285, 524287, 524286, 524279, 524280, 524283, 524281, 262116,
524282, 262115, 131055, 131056, 65525, 131054, 65522, 65523, 65524, 65521,
32758, 32759, 16377, 16373, 16375, 16371, 16374, 16370, 8183, 8181,
4089, 4087, 4086, 2041, 4084, 2040, 1017, 1015, 1013, 504,
503, 250, 248, 246, 121, 58, 56, 26, 11, 4,
0, 10, 12, 27, 57, 59, 120, 122, 247, 249,
502, 505, 1012, 1014, 1016, 2037, 2036, 2038, 2039, 4085,
4088, 8180, 8182, 8184, 16376, 16372, 65520, 32756, 65526, 32757,
262114, 524249, 524250, 524251, 524252, 524253, 524254, 524248, 524242, 524243,
524244, 524245, 524246, 524274, 524255, 524263, 524264, 524265, 524266, 524267,
524262, 524256, 524257, 524258, 524259, 524260, 524261, 524247, 524268, 524276,
524275 };
static int[] ff_aac_scalefactor_bits = new int[] {
18, 18, 18, 18, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 18,
19, 18, 17, 17, 16, 17, 16, 16, 16, 16,
15, 15, 14, 14, 14, 14, 14, 14, 13, 13,
12, 12, 12, 11, 12, 11, 10, 10, 10, 9,
9, 8, 8, 8, 7, 6, 6, 5, 4, 3,
1, 4, 4, 5, 6, 6, 7, 7, 8, 8,
9, 9, 10, 10, 10, 11, 11, 11, 11, 12,
12, 13, 13, 13, 14, 14, 16, 15, 16, 15,
18, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19 };
static int[] maxSfbTab = new int[] {
33, 33, 38, 40, 40, 40, 41, 41, 37, 37,
37, 34, 34 };
static float[] ltpCoefTab = new float[] { 0.570829F, 0.696616F, 0.813004F, 0.911304F, 0.9849F, 1.067894F, 1.194601F, 1.369533F };
static final int[] ff_aac_num_swb_1024 = new int[] {
41, 41, 47, 49, 49, 51, 47, 47, 43, 43,
43, 40, 40 };
static final int[] ff_aac_num_swb_128 = new int[] {
12, 12, 12, 14, 14, 14, 15, 15, 15, 15,
15, 15, 15 };
static final int[] swb_offset_1024_96 = new int[] {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36,
40, 44, 48, 52, 56, 64, 72, 80, 88, 96,
108, 120, 132, 144, 156, 172, 188, 212, 240, 276,
320, 384, 448, 512, 576, 640, 704, 768, 832, 896,
960, 1024 };
static final int[] swb_offset_128_96 = new int[] {
0, 4, 8, 12, 16, 20, 24, 32, 40, 48,
64, 92, 128 };
static final int[] swb_offset_1024_64 = new int[] {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36,
40, 44, 48, 52, 56, 64, 72, 80, 88, 100,
112, 124, 140, 156, 172, 192, 216, 240, 268, 304,
344, 384, 424, 464, 504, 544, 584, 624, 664, 704,
744, 784, 824, 864, 904, 944, 984, 1024 };
static final int[] swb_offset_1024_48 = new int[] {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36,
40, 48, 56, 64, 72, 80, 88, 96, 108, 120,
132, 144, 160, 176, 196, 216, 240, 264, 292, 320,
352, 384, 416, 448, 480, 512, 544, 576, 608, 640,
672, 704, 736, 768, 800, 832, 864, 896, 928, 1024 };
static final int[] swb_offset_128_48 = new int[] {
0, 4, 8, 12, 16, 20, 28, 36, 44, 56,
68, 80, 96, 112, 128 };
static final int[] swb_offset_1024_32 = new int[] {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36,
40, 48, 56, 64, 72, 80, 88, 96, 108, 120,
132, 144, 160, 176, 196, 216, 240, 264, 292, 320,
352, 384, 416, 448, 480, 512, 544, 576, 608, 640,
672, 704, 736, 768, 800, 832, 864, 896, 928, 960,
992, 1024 };
static final int[] swb_offset_1024_24 = new int[] {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36,
40, 44, 52, 60, 68, 76, 84, 92, 100, 108,
116, 124, 136, 148, 160, 172, 188, 204, 220, 240,
260, 284, 308, 336, 364, 396, 432, 468, 508, 552,
600, 652, 704, 768, 832, 896, 960, 1024 };
static final int[] swb_offset_128_24 = new int[] {
0, 4, 8, 12, 16, 20, 24, 28, 36, 44,
52, 64, 76, 92, 108, 128 };
static final int[] swb_offset_1024_16 = new int[] {
0, 8, 16, 24, 32, 40, 48, 56, 64, 72,
80, 88, 100, 112, 124, 136, 148, 160, 172, 184,
196, 212, 228, 244, 260, 280, 300, 320, 344, 368,
396, 424, 456, 492, 532, 572, 616, 664, 716, 772,
832, 896, 960, 1024 };
static final int[] swb_offset_128_16 = new int[] {
0, 4, 8, 12, 16, 20, 24, 28, 32, 40,
48, 60, 72, 88, 108, 128 };
static final int[] swb_offset_1024_8 = new int[] {
0, 12, 24, 36, 48, 60, 72, 84, 96, 108,
120, 132, 144, 156, 172, 188, 204, 220, 236, 252,
268, 288, 308, 328, 348, 372, 396, 420, 448, 476,
508, 544, 580, 620, 664, 712, 764, 820, 880, 944,
1024 };
static final int[] swb_offset_128_8 = new int[] {
0, 4, 8, 12, 16, 20, 24, 28, 36, 44,
52, 60, 72, 88, 108, 128 };
static final int[][] ff_swb_offset_1024 = new int[][] {
swb_offset_1024_96, swb_offset_1024_96, swb_offset_1024_64, swb_offset_1024_48, swb_offset_1024_48, swb_offset_1024_32, swb_offset_1024_24, swb_offset_1024_24, swb_offset_1024_16, swb_offset_1024_16,
swb_offset_1024_16, swb_offset_1024_8, swb_offset_1024_8 };
static final int[][] ff_swb_offset_128 = new int[][] {
swb_offset_128_96, swb_offset_128_96, swb_offset_128_96, swb_offset_128_48, swb_offset_128_48, swb_offset_128_48, swb_offset_128_24, swb_offset_128_24, swb_offset_128_16, swb_offset_128_16,
swb_offset_128_16, swb_offset_128_8, swb_offset_128_8 };
static final float[] tns_tmp2_map_1_3 = new float[] { 0.0F, -0.43388373F, 0.6427876F, 0.34202015F };
static final float[] tns_tmp2_map_0_3 = new float[] { 0.0F, -0.43388373F, -0.7818315F, -0.9749279F, 0.9848077F, 0.8660254F, 0.6427876F, 0.34202015F };
static final float[] tns_tmp2_map_1_4 = new float[] { 0.0F, -0.2079117F, -0.40673664F, -0.58778524F, 0.6736956F, 0.52643216F, 0.36124167F, 0.18374951F };
static final float[] tns_tmp2_map_0_4 = new float[] {
0.0F, -0.2079117F, -0.40673664F, -0.58778524F, -0.7431448F, -0.8660254F, -0.95105654F, -0.9945219F, 0.99573416F, 0.9618256F,
0.8951633F, 0.7980172F, 0.6736956F, 0.52643216F, 0.36124167F, 0.18374951F };
static final float[][] tns_tmp2_map = new float[][] { tns_tmp2_map_0_3, tns_tmp2_map_0_4, tns_tmp2_map_1_3, tns_tmp2_map_1_4 };
static float[] codebook_vector0_vals = new float[] { -1.0F, 0.0F, 1.0F };
static int[] codebook_vector02_idx = new int[] {
0, 33088, 33152, 16656, 49744, 49808, 16672, 49760, 49824, 8452,
41540, 41604, 25108, 58196, 58260, 25124, 58212, 58276, 8456, 41544,
41608, 25112, 58200, 58264, 25128, 58216, 58280, 4353, 37441, 37505,
21009, 54097, 54161, 21025, 54113, 54177, 12805, 45893, 45957, 29461,
62549, 62613, 29477, 62565, 62629, 12809, 45897, 45961, 29465, 62553,
62617, 29481, 62569, 62633, 4354, 37442, 37506, 21010, 54098, 54162,
21026, 54114, 54178, 12806, 45894, 45958, 29462, 62550, 62614, 29478,
62566, 62630, 12810, 45898, 45962, 29466, 62554, 62618, 29482, 62570,
62634 };
static float[] codebook_vector4_vals = new float[] { -6.349604F, -4.326749F, -2.5198421F, -1.0F, 0.0F, 1.0F, 2.5198421F, 4.326749F, 6.349604F };
static int[] codebook_vector4_idx = new int[] {
0, 16, 32, 48, 64, 80, 96, 112, 128, 1,
17, 33, 49, 65, 81, 97, 113, 129, 2, 18,
34, 50, 66, 82, 98, 114, 130, 3, 19, 35,
51, 67, 83, 99, 115, 131, 4, 20, 36, 52,
68, 84, 100, 116, 132, 5, 21, 37, 53, 69,
85, 101, 117, 133, 6, 22, 38, 54, 70, 86,
102, 118, 134, 7, 23, 39, 55, 71, 87, 103,
119, 135, 8, 24, 40, 56, 72, 88, 104, 120,
136 };
static int[] codebook_vector6_idx = new int[] {
0, 272, 288, 304, 320, 336, 352, 368, 4353, 529,
545, 561, 577, 593, 609, 625, 4354, 530, 546, 562,
578, 594, 610, 626, 4355, 531, 547, 563, 579, 595,
611, 627, 4356, 532, 548, 564, 580, 596, 612, 628,
4357, 533, 549, 565, 581, 597, 613, 629, 4358, 534,
550, 566, 582, 598, 614, 630, 4359, 535, 551, 567,
583, 599, 615, 631 };
static int[] codebook_vector8_idx = new int[] {
0, 272, 288, 304, 320, 336, 352, 368, 384, 400,
416, 432, 448, 4353, 529, 545, 561, 577, 593, 609,
625, 641, 657, 673, 689, 705, 4354, 530, 546, 562,
578, 594, 610, 626, 642, 658, 674, 690, 706, 4355,
531, 547, 563, 579, 595, 611, 627, 643, 659, 675,
691, 707, 4356, 532, 548, 564, 580, 596, 612, 628,
644, 660, 676, 692, 708, 4357, 533, 549, 565, 581,
597, 613, 629, 645, 661, 677, 693, 709, 4358, 534,
550, 566, 582, 598, 614, 630, 646, 662, 678, 694,
710, 4359, 535, 551, 567, 583, 599, 615, 631, 647,
663, 679, 695, 711, 4360, 536, 552, 568, 584, 600,
616, 632, 648, 664, 680, 696, 712, 4361, 537, 553,
569, 585, 601, 617, 633, 649, 665, 681, 697, 713,
4362, 538, 554, 570, 586, 602, 618, 634, 650, 666,
682, 698, 714, 4363, 539, 555, 571, 587, 603, 619,
635, 651, 667, 683, 699, 715, 4364, 540, 556, 572,
588, 604, 620, 636, 652, 668, 684, 700, 716 };
static float[] codebook_vector10_vals = new float[] {
0.0F, 1.0F, 2.5198421F, 4.326749F, 6.349604F, 8.54988F, 10.902723F, 13.390518F, 16.0F, 18.720755F,
21.544348F, 24.463781F, 27.473143F, 30.56735F, 33.741993F, 36.99318F };
static int[] codebook_vector10_idx = new int[] {
0, 4112, 4128, 4144, 4160, 4176, 4192, 4208, 4224, 4240,
4256, 4272, 4288, 4304, 4320, 4336, 4608, 4097, 8209, 8225,
8241, 8257, 8273, 8289, 8305, 8321, 8337, 8353, 8369, 8385,
8401, 8417, 8433, 8705, 4098, 8210, 8226, 8242, 8258, 8274,
8290, 8306, 8322, 8338, 8354, 8370, 8386, 8402, 8418, 8434,
8706, 4099, 8211, 8227, 8243, 8259, 8275, 8291, 8307, 8323,
8339, 8355, 8371, 8387, 8403, 8419, 8435, 8707, 4100, 8212,
8228, 8244, 8260, 8276, 8292, 8308, 8324, 8340, 8356, 8372,
8388, 8404, 8420, 8436, 8708, 4101, 8213, 8229, 8245, 8261,
8277, 8293, 8309, 8325, 8341, 8357, 8373, 8389, 8405, 8421,
8437, 8709, 4102, 8214, 8230, 8246, 8262, 8278, 8294, 8310,
8326, 8342, 8358, 8374, 8390, 8406, 8422, 8438, 8710, 4103,
8215, 8231, 8247, 8263, 8279, 8295, 8311, 8327, 8343, 8359,
8375, 8391, 8407, 8423, 8439, 8711, 4104, 8216, 8232, 8248,
8264, 8280, 8296, 8312, 8328, 8344, 8360, 8376, 8392, 8408,
8424, 8440, 8712, 4105, 8217, 8233, 8249, 8265, 8281, 8297,
8313, 8329, 8345, 8361, 8377, 8393, 8409, 8425, 8441, 8713,
4106, 8218, 8234, 8250, 8266, 8282, 8298, 8314, 8330, 8346,
8362, 8378, 8394, 8410, 8426, 8442, 8714, 4107, 8219, 8235,
8251, 8267, 8283, 8299, 8315, 8331, 8347, 8363, 8379, 8395,
8411, 8427, 8443, 8715, 4108, 8220, 8236, 8252, 8268, 8284,
8300, 8316, 8332, 8348, 8364, 8380, 8396, 8412, 8428, 8444,
8716, 4109, 8221, 8237, 8253, 8269, 8285, 8301, 8317, 8333,
8349, 8365, 8381, 8397, 8413, 8429, 8445, 8717, 4110, 8222,
8238, 8254, 8270, 8286, 8302, 8318, 8334, 8350, 8366, 8382,
8398, 8414, 8430, 8446, 8718, 4111, 8223, 8239, 8255, 8271,
8287, 8303, 8319, 8335, 8351, 8367, 8383, 8399, 8415, 8431,
8447, 8719, 4352, 8464, 8480, 8496, 8512, 8528, 8544, 8560,
8576, 8592, 8608, 8624, 8640, 8656, 8672, 8688, 8960 };
}

View file

@ -0,0 +1,14 @@
package org.jcodec.codecs.aac.blocks;
import org.jcodec.codecs.aac.BlockType;
import org.jcodec.common.io.BitReader;
public abstract class Block {
private BlockType type;
public BlockType getType() {
return this.type;
}
public abstract void parse(BitReader paramBitReader);
}

View file

@ -0,0 +1,77 @@
package org.jcodec.codecs.aac.blocks;
import org.jcodec.codecs.aac.BlockType;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.VLC;
public class BlockCCE extends Block {
private int coupling_point;
private int num_coupled;
private BlockType[] type;
private int[] id_select;
private int[] ch_select;
private int sign;
private Object scale;
private Object[] cce_scale;
private BlockICS blockICS;
private BlockICS.BandType[] bandType;
static VLC vlc = new VLC(AACTab.ff_aac_scalefactor_code, AACTab.ff_aac_scalefactor_bits);
public BlockCCE(BlockICS.BandType[] bandType) {
this.bandType = bandType;
}
public void parse(BitReader _in) {
int num_gain = 0;
this.coupling_point = 2 * _in.read1Bit();
this.num_coupled = _in.readNBit(3);
for (int i = 0; i <= this.num_coupled; i++) {
num_gain++;
this.type[i] = (_in.read1Bit() != 0) ? BlockType.TYPE_CPE : BlockType.TYPE_SCE;
this.id_select[i] = _in.readNBit(4);
if (this.type[i] == BlockType.TYPE_CPE) {
this.ch_select[i] = _in.readNBit(2);
if (this.ch_select[i] == 3)
num_gain++;
} else {
this.ch_select[i] = 2;
}
}
this.coupling_point += _in.read1Bit() | this.coupling_point >> 1;
this.sign = _in.read1Bit();
this.scale = this.cce_scale[_in.readNBit(2)];
this.blockICS = new BlockICS();
this.blockICS.parse(_in);
for (int c = 0; c < num_gain; c++) {
int idx = 0;
int cge = 1;
int gain = 0;
if (c != 0) {
cge = (this.coupling_point == CouplingPoint.AFTER_IMDCT.ordinal()) ? 1 : _in.read1Bit();
gain = (cge != 0) ? (vlc.readVLC(_in) - 60) : 0;
}
if (this.coupling_point != CouplingPoint.AFTER_IMDCT.ordinal())
for (int g = 0; g < this.blockICS.num_window_groups; g++) {
for (int sfb = 0; sfb < this.blockICS.maxSfb; sfb++, idx++) {
if (this.bandType[idx] != BlockICS.BandType.ZERO_BT &&
cge == 0)
int j = vlc.readVLC(_in) - 60;
}
}
}
}
enum CouplingPoint {
BEFORE_TNS, BETWEEN_TNS_AND_IMDCT, UNDEF, AFTER_IMDCT;
}
}

View file

@ -0,0 +1,29 @@
package org.jcodec.codecs.aac.blocks;
import org.jcodec.common.io.BitReader;
public class BlockCPE extends BlockICS {
private int[] ms_mask;
public void parse(BitReader _in) {
int common_window = _in.read1Bit();
if (common_window != 0) {
parseICSInfo(_in);
int ms_present = _in.readNBit(2);
if (ms_present == 3)
throw new RuntimeException("ms_present = 3 is reserved.");
if (ms_present != 0)
decodeMidSideStereo(_in, ms_present, 0, 0);
}
BlockICS ics1 = new BlockICS();
ics1.parse(_in);
BlockICS ics2 = new BlockICS();
ics2.parse(_in);
}
private void decodeMidSideStereo(BitReader _in, int ms_present, int numWindowGroups, int maxSfb) {
if (ms_present == 1)
for (int idx = 0; idx < numWindowGroups * maxSfb; idx++)
this.ms_mask[idx] = _in.read1Bit();
}
}

View file

@ -0,0 +1,17 @@
package org.jcodec.codecs.aac.blocks;
import org.jcodec.common.io.BitReader;
public class BlockDSE extends Block {
public void parse(BitReader _in) {
int elemType = _in.readNBit(4);
int byte_align = _in.read1Bit();
int count = _in.readNBit(8);
if (count == 255)
count += _in.readNBit(8);
if (byte_align != 0)
_in.align();
if (_in.skip(8 * count) != 8 * count)
throw new RuntimeException("Overread");
}
}

View file

@ -0,0 +1,14 @@
package org.jcodec.codecs.aac.blocks;
import org.jcodec.common.io.BitReader;
public class BlockFil extends Block {
public void parse(BitReader _in) {
int num = _in.readNBit(4);
if (num == 15)
num += _in.readNBit(8) - 1;
if (num > 0 &&
_in.skip(8 * num) != 8 * num)
throw new RuntimeException("Overread");
}
}

View file

@ -0,0 +1,516 @@
package org.jcodec.codecs.aac.blocks;
import org.jcodec.codecs.aac.Profile;
import org.jcodec.codecs.prores.ProresDecoder;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.VLC;
import org.jcodec.common.io.VLCBuilder;
import org.jcodec.common.tools.MathUtil;
public class BlockICS extends Block {
private static VLC vlc = new VLC(AACTab.ff_aac_scalefactor_code, AACTab.ff_aac_scalefactor_bits);
private static VLC[] spectral = new VLC[] {
VLCBuilder.createVLCBuilder(AACTab.codes1, AACTab.bits1, AACTab.codebook_vector02_idx).getVLC(),
VLCBuilder.createVLCBuilder(AACTab.codes2, AACTab.bits2, AACTab.codebook_vector02_idx).getVLC(),
VLCBuilder.createVLCBuilder(AACTab.codes3, AACTab.bits3, AACTab.codebook_vector02_idx).getVLC(),
VLCBuilder.createVLCBuilder(AACTab.codes4, AACTab.bits4, AACTab.codebook_vector02_idx).getVLC(),
VLCBuilder.createVLCBuilder(AACTab.codes5, AACTab.bits5, AACTab.codebook_vector4_idx).getVLC(),
VLCBuilder.createVLCBuilder(AACTab.codes6, AACTab.bits6, AACTab.codebook_vector4_idx).getVLC(),
VLCBuilder.createVLCBuilder(AACTab.codes7, AACTab.bits7, AACTab.codebook_vector6_idx).getVLC(),
VLCBuilder.createVLCBuilder(AACTab.codes8, AACTab.bits8, AACTab.codebook_vector6_idx).getVLC(),
VLCBuilder.createVLCBuilder(AACTab.codes9, AACTab.bits9, AACTab.codebook_vector8_idx).getVLC(),
VLCBuilder.createVLCBuilder(AACTab.codes10, AACTab.bits10, AACTab.codebook_vector8_idx).getVLC(),
VLCBuilder.createVLCBuilder(AACTab.codes11, AACTab.bits11, AACTab.codebook_vector10_idx).getVLC() };
float[][] ff_aac_codebook_vector_vals = new float[][] {
AACTab.codebook_vector0_vals, AACTab.codebook_vector0_vals, AACTab.codebook_vector10_vals, AACTab.codebook_vector10_vals, AACTab.codebook_vector4_vals, AACTab.codebook_vector4_vals, AACTab.codebook_vector10_vals, AACTab.codebook_vector10_vals, AACTab.codebook_vector10_vals, AACTab.codebook_vector10_vals,
AACTab.codebook_vector10_vals };
private int[] group_len = new int[8];
private int[] band_type = new int[120];
private int[] band_type_run_end = new int[120];
private enum WindowSequence {
ONLY_LONG_SEQUENCE, LONG_START_SEQUENCE, EIGHT_SHORT_SEQUENCE, LONG_STOP_SEQUENCE;
}
protected int parseICSInfo(BitReader _in) {
_in.read1Bit();
this.windowSequence = _in.readNBit(2);
int useKbWindow = _in.read1Bit();
this.num_window_groups = 1;
this.group_len[0] = 1;
if (this.windowSequence == WindowSequence.EIGHT_SHORT_SEQUENCE.ordinal()) {
int max_sfb = _in.readNBit(4);
for (int i = 0; i < 7; i++) {
if (_in.read1Bit() != 0) {
this.group_len[this.num_window_groups - 1] = this.group_len[this.num_window_groups - 1] + 1;
} else {
this.num_window_groups++;
this.group_len[this.num_window_groups - 1] = 1;
}
}
this.numSwb = AACTab.ff_aac_num_swb_128[this.samplingIndex];
this.swbOffset = AACTab.ff_swb_offset_128[this.samplingIndex];
this.numWindows = 8;
} else {
this.maxSfb = _in.readNBit(6);
this.numSwb = AACTab.ff_aac_num_swb_1024[this.samplingIndex];
this.swbOffset = AACTab.ff_swb_offset_1024[this.samplingIndex];
this.numWindows = 1;
int predictor_present = _in.read1Bit();
if (predictor_present != 0)
if (this.profile == Profile.MAIN) {
decodePrediction(_in, this.maxSfb);
} else {
if (this.profile == Profile.LC)
throw new RuntimeException("Prediction is not allowed _in AAC-LC.\n");
int ltpPresent = _in.read1Bit();
if (ltpPresent != 0)
decodeLtp(_in, this.maxSfb);
}
}
return 0;
}
private void decodePrediction(BitReader _in, int maxSfb) {
if (_in.read1Bit() != 0)
int i = _in.readNBit(5);
for (int sfb = 0; sfb < Math.min(maxSfb, AACTab.maxSfbTab[this.samplingIndex]); sfb++)
_in.read1Bit();
}
private void decodeLtp(BitReader _in, int maxSfb) {
int lag = _in.readNBit(11);
float coef = AACTab.ltpCoefTab[_in.readNBit(3)];
for (int sfb = 0; sfb < Math.min(maxSfb, 40); sfb++)
_in.read1Bit();
}
private void decodeBandTypes(BitReader _in) {
int idx = 0;
int bits = (this.windowSequence == WindowSequence.EIGHT_SHORT_SEQUENCE.ordinal()) ? 3 : 5;
for (int g = 0; g < this.num_window_groups; g++) {
int k = 0;
while (k < this.maxSfb) {
int sect_end = k;
int sect_band_type = _in.readNBit(4);
if (sect_band_type == 12)
throw new RuntimeException("invalid band type");
int sect_len_incr;
while ((sect_len_incr = _in.readNBit(bits)) == (1 << bits) - 1)
sect_end += sect_len_incr;
sect_end += sect_len_incr;
if (!_in.moreData() || sect_len_incr == (1 << bits) - 1)
throw new RuntimeException("Overread");
if (sect_end > this.maxSfb)
throw new RuntimeException(String.format("Number of bands (%d) exceeds limit (%d).\n", sect_end, this.maxSfb));
for (; k < sect_end; k++) {
this.band_type[idx] = sect_band_type;
this.band_type_run_end[idx++] = sect_end;
}
}
}
}
enum BandType {
ZERO_BT, BT_1, BT_2, BT_3, BT_4, FIRST_PAIR_BT, BT_6, BT_7, BT_8, BT_9, BT_10, ESC_BT, BT_12, NOISE_BT, INTENSITY_BT2, INTENSITY_BT;
}
static float[] ff_aac_pow2sf_tab = new float[428];
private boolean commonWindow;
private boolean scaleFlag;
private Profile profile;
private int samplingIndex;
private static final int MAX_LTP_LONG_SFB = 40;
private int windowSequence;
int num_window_groups;
int maxSfb;
private int globalGain;
private static final int POW_SF2_ZERO = 200;
private double[] sfs;
private int numSwb;
private int[] swbOffset;
private int numWindows;
static {
for (int i = 0; i < 428; i++)
ff_aac_pow2sf_tab[i] = (float)Math.pow(2.0D, (double)(i - 200) / 4.0D);
}
private void decodeScalefactors(BitReader _in) {
int[] offset = { this.globalGain, this.globalGain - 90, 0 };
int noise_flag = 1;
String[] sf_str = { "Global gain", "Noise gain", "Intensity stereo position" };
int idx = 0;
for (int g = 0; g < this.num_window_groups; g++) {
for (int i = 0; i < this.maxSfb; ) {
int run_end = this.band_type_run_end[idx];
if (this.band_type[idx] == BandType.ZERO_BT.ordinal()) {
for (; i < run_end; i++, idx++)
this.sfs[idx] = 0.0D;
continue;
}
if (this.band_type[idx] == BandType.INTENSITY_BT.ordinal() || this.band_type[idx] == BandType.INTENSITY_BT2.ordinal()) {
for (; i < run_end; i++, idx++) {
offset[2] = offset[2] + vlc.readVLC(_in) - 60;
int clipped_offset = MathUtil.clip(offset[2], -155, 100);
if (offset[2] != clipped_offset)
System.out.println(String.format("Intensity stereo position clipped (%d -> %d).\nIf you heard an audible artifact, there may be a bug _in the decoder. ", offset[2],
clipped_offset));
this.sfs[idx] = (double)ff_aac_pow2sf_tab[-clipped_offset + 200];
}
continue;
}
if (this.band_type[idx] == BandType.NOISE_BT.ordinal()) {
for (; i < run_end; i++, idx++) {
if (noise_flag-- > 0) {
offset[1] = offset[1] + _in.readNBit(9) - 256;
} else {
offset[1] = offset[1] + vlc.readVLC(_in) - 60;
}
int clipped_offset = MathUtil.clip(offset[1], -100, 155);
if (offset[1] != clipped_offset)
System.out.println(String.format("Noise gain clipped (%d -> %d).\nIf you heard an audible artifact, there may be a bug _in the decoder. ", offset[1],
clipped_offset));
this.sfs[idx] = (double)-ff_aac_pow2sf_tab[clipped_offset + 200];
}
continue;
}
for (; i < run_end; i++, idx++) {
offset[0] = offset[0] + vlc.readVLC(_in) - 60;
if (offset[0] > 255)
throw new RuntimeException(String.format("%s (%d) out of range.\n", sf_str[0], offset[0]));
this.sfs[idx] = (double)-ff_aac_pow2sf_tab[offset[0] - 100 + 200];
}
}
}
}
public static class Pulse {
private int numPulse;
private int[] pos;
private int[] amp;
public Pulse(int numPulse, int[] pos, int[] amp) {
this.numPulse = numPulse;
this.pos = pos;
this.amp = amp;
}
public int getNumPulse() {
return this.numPulse;
}
public int[] getPos() {
return this.pos;
}
public int[] getAmp() {
return this.amp;
}
}
private Pulse decodePulses(BitReader _in) {
int[] pos = new int[4];
int[] amp = new int[4];
int numPulse = _in.readNBit(2) + 1;
int pulseSwb = _in.readNBit(6);
if (pulseSwb >= this.numSwb)
throw new RuntimeException("pulseSwb >= numSwb");
pos[0] = this.swbOffset[pulseSwb];
pos[0] = pos[0] + _in.readNBit(5);
if (pos[0] > 1023)
throw new RuntimeException("pos[0] > 1023");
amp[0] = _in.readNBit(4);
for (int i = 1; i < numPulse; i++) {
pos[i] = _in.readNBit(5) + pos[i - 1];
if (pos[i] > 1023)
throw new RuntimeException("pos[" + i + "] > 1023");
amp[i] = _in.readNBit(5);
}
return new Pulse(numPulse, pos, amp);
}
public static class Tns {
private int[] nFilt;
private int[][] length;
private int[][] order;
private int[][] direction;
private float[][][] coeff;
public Tns(int[] nFilt, int[][] length, int[][] order, int[][] direction, float[][][] coeff) {
this.nFilt = nFilt;
this.length = length;
this.order = order;
this.direction = direction;
this.coeff = coeff;
}
}
private Tns decodeTns(BitReader _in) {
int is8 = (this.windowSequence == WindowSequence.EIGHT_SHORT_SEQUENCE.ordinal()) ? 1 : 0;
int tns_max_order = (is8 != 0) ? 7 : ((this.profile == Profile.MAIN) ? 20 : 12);
int[] nFilt = new int[this.numWindows];
int[][] length = new int[this.numWindows][2];
int[][] order = new int[this.numWindows][2];
int[][] direction = new int[this.numWindows][2];
float[][][] coeff = new float[this.numWindows][2][1 << 5 - 2 * is8];
for (int w = 0; w < this.numWindows; w++) {
nFilt[w] = _in.readNBit(2 - is8);
if (_in.readNBit(2 - is8) != 0) {
int coefRes = _in.read1Bit();
for (int filt = 0; filt < nFilt[w]; filt++) {
length[w][filt] = _in.readNBit(6 - 2 * is8);
order[w][filt] = _in.readNBit(5 - 2 * is8);
if (_in.readNBit(5 - 2 * is8) > tns_max_order)
throw new RuntimeException(String.format("TNS filter order %d is greater than maximum %d.\n", order[w][filt],
tns_max_order));
if (order[w][filt] != 0) {
direction[w][filt] = _in.read1Bit();
int coefCompress = _in.read1Bit();
int coefLen = coefRes + 3 - coefCompress;
int tmp2_idx = 2 * coefCompress + coefRes;
for (int i = 0; i < order[w][filt]; i++)
coeff[w][filt][i] = AACTab.tns_tmp2_map[tmp2_idx][_in.readNBit(coefLen)];
}
}
}
}
return new Tns(nFilt, length, order, direction, coeff);
}
void VMUL4(float[] result, int idx, float[] v, int code, float scale) {
result[idx] = v[code & 0x3] * scale;
result[idx + 1] = v[code >> 2 & 0x3] * scale;
result[idx + 2] = v[code >> 4 & 0x3] * scale;
result[idx + 3] = v[code >> 6 & 0x3] * scale;
}
void VMUL4S(float[] result, int idx, float[] v, int code, int sign, float scale) {
int nz = code >> 12;
result[idx + 0] = v[idx & 0x3] * scale;
sign <<= nz & 0x1;
nz >>= 1;
result[idx + 1] = v[idx >> 2 & 0x3] * scale;
sign <<= nz & 0x1;
nz >>= 1;
result[idx + 2] = v[idx >> 4 & 0x3] * scale;
sign <<= nz & 0x1;
nz >>= 1;
result[idx + 3] = v[idx >> 6 & 0x3] * scale;
}
void VMUL2(float[] result, int idx, float[] v, int code, float scale) {
result[idx] = v[code & 0xF] * scale;
result[idx + 1] = v[code >> 4 & 0xF] * scale;
}
void VMUL2S(float[] result, int idx, float[] v, int code, int sign, float scale) {
result[idx] = v[code & 0xF] * scale;
result[idx + 1] = v[code >> 4 & 0xF] * scale;
}
private void decodeSpectrum(BitReader _in) {
float[] coef = new float[1024];
int idx = 0;
for (int g = 0; g < this.num_window_groups; g++) {
for (int i = 0; i < this.maxSfb; i++, idx++) {
int cbt_m1 = this.band_type[idx] - 1;
if (cbt_m1 < BandType.INTENSITY_BT2.ordinal() - 1 && cbt_m1 != BandType.NOISE_BT.ordinal() - 1) {
float[] vq = this.ff_aac_codebook_vector_vals[cbt_m1];
VLC vlc = spectral[cbt_m1];
switch (cbt_m1 >> 1) {
case 0:
readBandType1And2(_in, coef, idx, g, i, vq, vlc);
break;
case 1:
readBandType3And4(_in, coef, idx, g, i, vq, vlc);
break;
case 2:
readBandType5And6(_in, coef, idx, g, i, vq, vlc);
break;
case 3:
case 4:
readBandType7Through10(_in, coef, idx, g, i, vq, vlc);
break;
default:
readOther(_in, coef, idx, g, i, vq, vlc);
break;
}
}
}
}
}
private void readBandType3And4(BitReader _in, float[] coef, int idx, int g, int sfb, float[] vq, VLC vlc) {
int g_len = this.group_len[g];
int cfo = this.swbOffset[sfb];
int off_len = this.swbOffset[sfb + 1] - this.swbOffset[sfb];
for (int group = 0; group < g_len; ) {
int cf = cfo;
int len = off_len;
while (true) {
int cb_idx = vlc.readVLC(_in);
int nnz = cb_idx >> 8 & 0xF;
int bits = (nnz == 0) ? 0 : _in.readNBit(nnz);
VMUL4S(coef, cf, vq, cb_idx, bits, (float)this.sfs[idx]);
cf += 4;
len -= 4;
if (len <= 0) {
group++;
cfo += 128;
}
}
}
}
private void readBandType7Through10(BitReader _in, float[] coef, int idx, int g, int sfb, float[] vq, VLC vlc) {
int g_len = this.group_len[g];
int cfo = this.swbOffset[sfb];
int off_len = this.swbOffset[sfb + 1] - this.swbOffset[sfb];
for (int group = 0; group < g_len; ) {
int cf = cfo;
int len = off_len;
while (true) {
int cb_idx = vlc.readVLC(_in);
int nnz = cb_idx >> 8 & 0xF;
int bits = (nnz == 0) ? 0 : (_in.readNBit(nnz) << cb_idx >> 12);
VMUL2S(coef, cf, vq, cb_idx, bits, (float)this.sfs[idx]);
cf += 2;
len -= 2;
if (len <= 0) {
group++;
cfo += 128;
}
}
}
}
private void readOther(BitReader _in, float[] coef, int idx, int g, int sfb, float[] vq, VLC vlc) {
int g_len = this.group_len[g];
int cfo = this.swbOffset[sfb];
int off_len = this.swbOffset[sfb + 1] - this.swbOffset[sfb];
for (int group = 0; group < g_len; ) {
int cf = cfo;
int len = off_len;
while (true) {
int cb_idx = vlc.readVLC(_in);
if (cb_idx != 0) {
int nnz = cb_idx >> 12;
int nzt = cb_idx >> 8;
int bits = _in.readNBit(nnz) << 32 - nnz;
for (int j = 0; j < 2; j++) {
if ((nzt & 1 << j) != 0) {
int b = ProresDecoder.nZeros(_in.checkNBit(14) ^ 0xFFFFFFFF);
if (b > 8)
throw new RuntimeException("error _in spectral data, ESC overflow\n");
_in.skip(b + 1);
b += 4;
int n = (1 << b) + _in.readNBit(b);
coef[cf++] = (float)(MathUtil.cubeRoot(n) | bits & Integer.MIN_VALUE);
bits <<= 1;
} else {
int v = (int)vq[cb_idx & 0xF];
coef[cf++] = (float)(bits & Integer.MIN_VALUE | v);
}
cb_idx >>= 4;
}
cf += 2;
len += 2;
}
if (len <= 0) {
group++;
cfo += 128;
}
}
}
}
private void readBandType1And2(BitReader _in, float[] coef, int idx, int g, int sfb, float[] vq, VLC vlc) {
int g_len = this.group_len[g];
int cfo = this.swbOffset[sfb];
int off_len = this.swbOffset[sfb + 1] - this.swbOffset[sfb];
for (int group = 0; group < g_len; ) {
int cf = cfo;
int len = off_len;
while (true) {
int cb_idx = vlc.readVLC(_in);
VMUL4(coef, cf, vq, cb_idx, (float)this.sfs[idx]);
cf += 4;
len -= 4;
if (len <= 0) {
group++;
cfo += 128;
}
}
}
}
private void readBandType5And6(BitReader _in, float[] coef, int idx, int g, int sfb, float[] vq, VLC vlc) {
int g_len = this.group_len[g];
int cfo = this.swbOffset[sfb];
int off_len = this.swbOffset[sfb + 1] - this.swbOffset[sfb];
for (int group = 0; group < g_len; ) {
int cf = cfo;
int len = off_len;
while (true) {
int cb_idx = vlc.readVLC(_in);
VMUL2(coef, cf, vq, cb_idx, (float)this.sfs[idx]);
cf += 2;
len -= 2;
if (len <= 0) {
group++;
cfo += 128;
}
}
}
}
public void parse(BitReader _in) {
this.globalGain = _in.readNBit(8);
if (!this.commonWindow && !this.scaleFlag)
parseICSInfo(_in);
decodeBandTypes(_in);
decodeScalefactors(_in);
int pulse_present = 0;
if (!this.scaleFlag) {
if ((pulse_present = _in.read1Bit()) != 0) {
if (this.windowSequence == WindowSequence.EIGHT_SHORT_SEQUENCE.ordinal())
throw new RuntimeException("Pulse tool not allowed _in eight short sequence.");
decodePulses(_in);
}
int tns_present;
if ((tns_present = _in.read1Bit()) != 0)
decodeTns(_in);
if (_in.read1Bit() != 0)
throw new RuntimeException("SSR is not supported");
}
decodeSpectrum(_in);
}
}

View file

@ -0,0 +1,73 @@
package org.jcodec.codecs.aac.blocks;
import org.jcodec.codecs.aac.ChannelPosition;
import org.jcodec.common.io.BitReader;
public class BlockPCE extends Block {
private static final int MAX_ELEM_ID = 16;
public static class ChannelMapping {
RawDataBlockType syn_ele;
int someInt;
ChannelPosition position;
}
public void parse(BitReader _in) {
_in.readNBit(2);
int samplingIndex = _in.readNBit(4);
int num_front = _in.readNBit(4);
int num_side = _in.readNBit(4);
int num_back = _in.readNBit(4);
int num_lfe = _in.readNBit(2);
int num_assoc_data = _in.readNBit(3);
int num_cc = _in.readNBit(4);
if (_in.read1Bit() != 0)
_in.readNBit(4);
if (_in.read1Bit() != 0)
_in.readNBit(4);
if (_in.read1Bit() != 0)
_in.readNBit(3);
ChannelMapping[] layout_map = new ChannelMapping[64];
int tags = 0;
decodeChannelMap(layout_map, tags, ChannelPosition.AAC_CHANNEL_FRONT, _in, num_front);
tags = num_front;
decodeChannelMap(layout_map, tags, ChannelPosition.AAC_CHANNEL_SIDE, _in, num_side);
tags += num_side;
decodeChannelMap(layout_map, tags, ChannelPosition.AAC_CHANNEL_BACK, _in, num_back);
tags += num_back;
decodeChannelMap(layout_map, tags, ChannelPosition.AAC_CHANNEL_LFE, _in, num_lfe);
tags += num_lfe;
_in.skip(4 * num_assoc_data);
decodeChannelMap(layout_map, tags, ChannelPosition.AAC_CHANNEL_CC, _in, num_cc);
tags += num_cc;
_in.align();
int comment_len = _in.readNBit(8) * 8;
_in.skip(comment_len);
}
private void decodeChannelMap(ChannelMapping[] layout_map, int offset, ChannelPosition type, BitReader _in, int n) {
while (n-- > 0) {
RawDataBlockType syn_ele = null;
switch (type) {
case AAC_CHANNEL_FRONT:
case AAC_CHANNEL_BACK:
case AAC_CHANNEL_SIDE:
syn_ele = RawDataBlockType.values()[_in.read1Bit()];
break;
case AAC_CHANNEL_CC:
_in.read1Bit();
syn_ele = RawDataBlockType.TYPE_CCE;
break;
case AAC_CHANNEL_LFE:
syn_ele = RawDataBlockType.TYPE_LFE;
break;
}
(layout_map[offset]).syn_ele = syn_ele;
(layout_map[offset]).someInt = _in.readNBit(4);
(layout_map[offset]).position = type;
offset++;
}
}
}

View file

@ -0,0 +1,5 @@
package org.jcodec.codecs.aac.blocks;
public enum RawDataBlockType {
TYPE_SCE, TYPE_CPE, TYPE_CCE, TYPE_LFE, TYPE_DSE, TYPE_PCE, TYPE_FIL, TYPE_END;
}

View file

@ -0,0 +1,115 @@
package org.jcodec.codecs.common.biari;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.jcodec.platform.BaseOutputStream;
public class BitIO {
public static InputBits inputFromStream(InputStream is) {
return new StreamInputBits(is);
}
public static OutputBits outputFromStream(OutputStream out) {
return new StreamOutputBits(out);
}
public static InputBits inputFromArray(byte[] bytes) {
return new StreamInputBits(new ByteArrayInputStream(bytes));
}
public static OutputBits outputFromArray(final byte[] bytes) {
return new StreamOutputBits(new BaseOutputStream() {
int ptr;
protected void writeByte(int b) throws IOException {
if (this.ptr >= bytes.length)
throw new IOException("Buffer is full");
bytes[this.ptr++] = (byte)b;
}
});
}
public static byte[] compressBits(int[] decompressed) {
byte[] compressed = new byte[(decompressed.length >> 3) + 1];
OutputBits out = outputFromArray(compressed);
try {
for (int i = 0; i < decompressed.length; i++) {
int bit = decompressed[i];
out.putBit(bit);
}
} catch (IOException e) {}
return compressed;
}
public static int[] decompressBits(byte[] compressed) {
int[] decompressed = new int[compressed.length << 3];
InputBits inputFromArray = inputFromArray(compressed);
try {
int read;
for (int i = 0; (read = inputFromArray.getBit()) != -1; i++)
decompressed[i] = read;
} catch (IOException e) {}
return decompressed;
}
public static interface InputBits {
int getBit() throws IOException;
}
public static interface OutputBits {
void putBit(int param1Int) throws IOException;
void flush() throws IOException;
}
public static class StreamInputBits implements InputBits {
private InputStream _in;
private int cur;
private int bit;
public StreamInputBits(InputStream _in) {
this._in = _in;
this.bit = 8;
}
public int getBit() throws IOException {
if (this.bit > 7) {
this.cur = this._in.read();
if (this.cur == -1)
return -1;
this.bit = 0;
}
return this.cur >> 7 - this.bit++ & 0x1;
}
}
public static class StreamOutputBits implements OutputBits {
private OutputStream out;
private int cur;
private int bit;
public StreamOutputBits(OutputStream out) {
this.out = out;
}
public void putBit(int symbol) throws IOException {
if (this.bit > 7) {
this.out.write(this.cur);
this.cur = 0;
this.bit = 0;
}
this.cur |= (symbol & 0x1) << 7 - this.bit++;
}
public void flush() throws IOException {
if (this.bit > 0)
this.out.write(this.cur);
}
}
}

View file

@ -0,0 +1,28 @@
package org.jcodec.codecs.common.biari;
public class Context {
private int stateIdx;
private int mps;
public Context(int state, int mps) {
this.stateIdx = state;
this.mps = mps;
}
public int getState() {
return this.stateIdx;
}
public int getMps() {
return this.mps;
}
public void setMps(int mps) {
this.mps = mps;
}
public void setState(int state) {
this.stateIdx = state;
}
}

View file

@ -0,0 +1,42 @@
package org.jcodec.codecs.common.biari;
public class MConst {
public static final int[][] rangeLPS = new int[][] { new int[] {
128, 128, 128, 123, 116, 111, 105, 100, 95, 90,
85, 81, 77, 73, 69, 66, 62, 59, 56, 53,
51, 48, 46, 43, 41, 39, 37, 35, 33, 32,
30, 29, 27, 26, 24, 23, 22, 21, 20, 19,
18, 17, 16, 15, 14, 14, 13, 12, 12, 11,
11, 10, 10, 9, 9, 8, 8, 7, 7, 7,
6, 6, 6, 2 }, new int[] {
176, 167, 158, 150, 142, 135, 128, 122, 116, 110,
104, 99, 94, 89, 85, 80, 76, 72, 69, 65,
62, 59, 56, 53, 50, 48, 45, 43, 41, 39,
37, 35, 33, 31, 30, 28, 27, 26, 24, 23,
22, 21, 20, 19, 18, 17, 16, 15, 14, 14,
13, 12, 12, 11, 11, 10, 9, 9, 9, 8,
8, 7, 7, 2 }, new int[] {
208, 197, 187, 178, 169, 160, 152, 144, 137, 130,
123, 117, 111, 105, 100, 95, 90, 86, 81, 77,
73, 69, 66, 63, 59, 56, 54, 51, 48, 46,
43, 41, 39, 37, 35, 33, 32, 30, 29, 27,
26, 25, 23, 22, 21, 20, 19, 18, 17, 16,
15, 15, 14, 13, 12, 12, 11, 11, 10, 10,
9, 9, 8, 2 }, new int[] {
240, 227, 216, 205, 195, 185, 175, 166, 158, 150,
142, 135, 128, 122, 116, 110, 104, 99, 94, 89,
85, 80, 76, 72, 69, 65, 62, 59, 56, 53,
50, 48, 45, 43, 41, 39, 37, 35, 33, 31,
30, 28, 27, 25, 24, 23, 22, 21, 20, 19,
18, 17, 16, 15, 14, 14, 13, 12, 12, 11,
11, 10, 9, 2 } };
public static final int[] transitLPS = new int[] {
0, 0, 1, 2, 2, 4, 4, 5, 6, 7,
8, 9, 9, 11, 11, 12, 13, 13, 15, 15,
16, 16, 18, 18, 19, 19, 21, 21, 22, 22,
23, 24, 24, 25, 26, 26, 27, 27, 28, 29,
29, 30, 30, 30, 31, 32, 32, 33, 33, 33,
34, 34, 35, 35, 35, 36, 36, 36, 37, 37,
37, 38, 38, 63 };
}

View file

@ -0,0 +1,95 @@
package org.jcodec.codecs.common.biari;
import java.nio.ByteBuffer;
public class MDecoder {
private ByteBuffer _in;
private int range;
private int code;
private int nBitsPending;
private int[][] cm;
public MDecoder(ByteBuffer _in, int[][] cm) {
this._in = _in;
this.range = 510;
this.cm = cm;
initCodeRegister();
}
protected void initCodeRegister() {
readOneByte();
if (this.nBitsPending != 8)
throw new RuntimeException("Empty stream");
this.code <<= 8;
readOneByte();
this.code <<= 1;
this.nBitsPending -= 9;
}
protected void readOneByte() {
if (!this._in.hasRemaining())
return;
int b = this._in.get() & 0xFF;
this.code |= b;
this.nBitsPending += 8;
}
public int decodeBin(int m) {
int bin;
int qIdx = this.range >> 6 & 0x3;
int rLPS = MConst.rangeLPS[qIdx][this.cm[0][m]];
this.range -= rLPS;
int rs8 = this.range << 8;
if (this.code < rs8) {
if (this.cm[0][m] < 62)
this.cm[0][m] = this.cm[0][m] + 1;
renormalize();
bin = this.cm[1][m];
} else {
this.range = rLPS;
this.code -= rs8;
renormalize();
bin = 1 - this.cm[1][m];
if (this.cm[0][m] == 0)
this.cm[1][m] = 1 - this.cm[1][m];
this.cm[0][m] = MConst.transitLPS[this.cm[0][m]];
}
return bin;
}
public int decodeFinalBin() {
this.range -= 2;
if (this.code < this.range << 8) {
renormalize();
return 0;
}
return 1;
}
public int decodeBinBypass() {
this.code <<= 1;
this.nBitsPending--;
if (this.nBitsPending <= 0)
readOneByte();
int tmp = this.code - (this.range << 8);
if (tmp < 0)
return 0;
this.code = tmp;
return 1;
}
private void renormalize() {
while (this.range < 256) {
this.range <<= 1;
this.code <<= 1;
this.code &= 0x1FFFF;
this.nBitsPending--;
if (this.nBitsPending <= 0)
readOneByte();
}
}
}

View file

@ -0,0 +1,122 @@
package org.jcodec.codecs.common.biari;
import java.nio.ByteBuffer;
public class MEncoder {
private ByteBuffer out;
private int range;
private int offset;
private int onesOutstanding;
private boolean zeroBorrowed;
private int outReg;
private int bitsInOutReg;
private int[][] models;
public MEncoder(ByteBuffer out, int[][] models) {
this.range = 510;
this.models = models;
this.out = out;
}
public void encodeBin(int model, int bin) {
int qs = this.range >> 6 & 0x3;
int rangeLPS = MConst.rangeLPS[qs][this.models[0][model]];
this.range -= rangeLPS;
if (bin != this.models[1][model]) {
this.offset += this.range;
this.range = rangeLPS;
if (this.models[0][model] == 0)
this.models[1][model] = 1 - this.models[1][model];
this.models[0][model] = MConst.transitLPS[this.models[0][model]];
} else if (this.models[0][model] < 62) {
this.models[0][model] = this.models[0][model] + 1;
}
renormalize();
}
public void encodeBinBypass(int bin) {
this.offset <<= 1;
if (bin == 1)
this.offset += this.range;
if ((this.offset & 0x400) != 0) {
flushOutstanding(1);
this.offset &= 0x3FF;
} else if ((this.offset & 0x200) != 0) {
this.offset &= 0x1FF;
this.onesOutstanding++;
} else {
flushOutstanding(0);
}
}
public void encodeBinFinal(int bin) {
this.range -= 2;
if (bin == 0) {
renormalize();
} else {
this.offset += this.range;
this.range = 2;
renormalize();
}
}
public void finishEncoding() {
flushOutstanding(this.offset >> 9 & 0x1);
putBit(this.offset >> 8 & 0x1);
stuffBits();
}
private void renormalize() {
while (this.range < 256) {
if (this.offset < 256) {
flushOutstanding(0);
} else if (this.offset < 512) {
this.offset &= 0xFF;
this.onesOutstanding++;
} else {
this.offset &= 0x1FF;
flushOutstanding(1);
}
this.range <<= 1;
this.offset <<= 1;
}
}
private void flushOutstanding(int hasCarry) {
if (this.zeroBorrowed)
putBit(hasCarry);
int trailingBit = 1 - hasCarry;
for (; this.onesOutstanding > 0; this.onesOutstanding--)
putBit(trailingBit);
this.zeroBorrowed = true;
}
private void putBit(int bit) {
this.outReg = this.outReg << 1 | bit;
this.bitsInOutReg++;
if (this.bitsInOutReg == 8) {
this.out.put((byte)this.outReg);
this.outReg = 0;
this.bitsInOutReg = 0;
}
}
private void stuffBits() {
if (this.bitsInOutReg == 0) {
this.out.put(Byte.MIN_VALUE);
} else {
this.outReg = this.outReg << 1 | 0x1;
this.outReg <<= 8 - (this.bitsInOutReg + 1);
this.out.put((byte)this.outReg);
this.outReg = 0;
this.bitsInOutReg = 0;
}
}
}

View file

@ -0,0 +1,31 @@
package org.jcodec.codecs.common.biari;
public class MQConst {
public static final int[] pLps = new int[] {
22017, 13313, 6145, 2753, 1313, 545, 22017, 21505, 18433, 14337,
12289, 9217, 7169, 5633, 22017, 21505, 20737, 18433, 14337, 13313,
12289, 10241, 9217, 8705, 7169, 6145, 5633, 5121, 4609, 4353,
2753, 2497, 2209, 1313, 1089, 673, 545, 321, 273, 133,
73, 37, 21, 9, 5, 1, 22017 };
public static final int[] mpsSwitch = new int[] {
1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0 };
public static final int[] transitLPS = new int[] {
1, 6, 9, 12, 29, 33, 6, 14, 14, 14,
17, 18, 20, 21, 14, 14, 15, 16, 17, 18,
19, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
38, 39, 40, 41, 42, 43, 46 };
public static final int[] transitMPS = new int[] {
1, 2, 3, 4, 5, 38, 7, 8, 9, 10,
11, 12, 13, 29, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 45, 46 };
}

View file

@ -0,0 +1,72 @@
package org.jcodec.codecs.common.biari;
import java.io.IOException;
import java.io.InputStream;
public class MQDecoder {
private int range;
private int value;
private int availableBits;
private int lastByte;
private int decodedBytes;
private InputStream is;
public MQDecoder(InputStream is) throws IOException {
this.is = is;
this.range = 32768;
this.value = 0;
fetchByte();
this.value <<= 8;
fetchByte();
this.value <<= this.availableBits - 1;
this.availableBits = 1;
}
public int decode(Context cm) throws IOException {
int decoded;
int rangeLps = MQConst.pLps[cm.getState()];
if (this.value > rangeLps) {
this.range -= rangeLps;
this.value -= rangeLps;
if (this.range < 32768) {
while (this.range < 32768)
renormalize();
cm.setState(MQConst.transitMPS[cm.getState()]);
}
decoded = cm.getMps();
} else {
this.range = rangeLps;
while (this.range < 32768)
renormalize();
if (MQConst.mpsSwitch[cm.getState()] != 0)
cm.setMps(1 - cm.getMps());
cm.setState(MQConst.transitLPS[cm.getState()]);
decoded = 1 - cm.getMps();
}
return decoded;
}
private void fetchByte() throws IOException {
this.availableBits = 8;
if (this.decodedBytes > 0 && this.lastByte == 255)
this.availableBits = 7;
this.lastByte = this.is.read();
int shiftCarry = 8 - this.availableBits;
this.value += this.lastByte << shiftCarry;
this.decodedBytes++;
}
private void renormalize() throws IOException {
this.value <<= 1;
this.range <<= 1;
this.range &= 0xFFFF;
this.availableBits--;
if (this.availableBits == 0)
fetchByte();
}
}

View file

@ -0,0 +1,115 @@
package org.jcodec.codecs.common.biari;
import java.io.IOException;
import java.io.OutputStream;
public class MQEncoder {
public static final int CARRY_MASK = 134217728;
private int range;
private int offset;
private int bitsToCode;
private long bytesOutput;
private int byteToGo;
private OutputStream out;
public MQEncoder(OutputStream out) {
this.range = 32768;
this.offset = 0;
this.bitsToCode = 12;
this.out = out;
}
public void encode(int symbol, Context cm) throws IOException {
int rangeLps = MQConst.pLps[cm.getState()];
if (symbol == cm.getMps()) {
this.range -= rangeLps;
this.offset += rangeLps;
if (this.range < 32768) {
while (this.range < 32768)
renormalize();
cm.setState(MQConst.transitMPS[cm.getState()]);
}
} else {
this.range = rangeLps;
while (this.range < 32768)
renormalize();
if (MQConst.mpsSwitch[cm.getState()] != 0)
cm.setMps(1 - cm.getMps());
cm.setState(MQConst.transitLPS[cm.getState()]);
}
}
public void finish() throws IOException {
finalizeValue();
this.offset <<= this.bitsToCode;
int bitsToOutput = 12 - this.bitsToCode;
outputByte();
bitsToOutput -= this.bitsToCode;
if (bitsToOutput > 0) {
this.offset <<= this.bitsToCode;
outputByte();
}
this.out.write(this.byteToGo);
}
private void finalizeValue() {
int halfBit = this.offset & 0x8000;
this.offset &= 0xFFFF0000;
if (halfBit == 0) {
this.offset |= 0x8000;
} else {
this.offset += 65536;
}
}
private void renormalize() throws IOException {
this.offset <<= 1;
this.range <<= 1;
this.range = (int)((long)this.range & 0xFFFFL);
this.bitsToCode--;
if (this.bitsToCode == 0)
outputByte();
}
private void outputByte() throws IOException {
if (this.bytesOutput == 0L) {
outputByteNoStuffing();
} else if (this.byteToGo == 255) {
outputByteWithStuffing();
} else if ((this.offset & 0x8000000) != 0) {
this.byteToGo++;
this.offset &= 0x7FFFFFF;
if (this.byteToGo == 255) {
outputByteWithStuffing();
} else {
outputByteNoStuffing();
}
} else {
outputByteNoStuffing();
}
}
private void outputByteWithStuffing() throws IOException {
this.bitsToCode = 7;
if (this.bytesOutput > 0L)
this.out.write(this.byteToGo);
this.byteToGo = this.offset >> 20 & 0xFF;
this.offset &= 0xFFFFF;
this.bytesOutput++;
}
private void outputByteNoStuffing() throws IOException {
this.bitsToCode = 8;
if (this.bytesOutput > 0L)
this.out.write(this.byteToGo);
this.byteToGo = this.offset >> 19 & 0xFF;
this.offset &= 0x7FFFF;
this.bytesOutput++;
}
}

View file

@ -0,0 +1,26 @@
package org.jcodec.codecs.common.biari;
public class Packed4BitList {
private static int[] CLEAR_MASK = new int[] { 268435440, -16, -16, -16, -16, -16, -16 };
public static int _7(int val0, int val1, int val2, int val3, int val4, int val5, int val6) {
return 0x70000000 | (val0 & 0xF) << 24 | (val1 & 0xF) << 20 | (val2 & 0xF) << 16 | (val3 & 0xF) << 12 | (val4 & 0xF) << 8 | (val5 & 0xF) << 4 | val6 & 0xF;
}
public static int _3(int val0, int val1, int val2) {
return _7(val0, val1, val2, 0, 0, 0, 0);
}
public static int set(int list, int val, int n) {
int cnt = list >> 28 & 0xF;
int newc = n + 1;
cnt = (newc > cnt) ? newc : cnt;
return list & CLEAR_MASK[n] | (val & 0xFF) << n << 2 | cnt << 28;
}
public static int get(int list, int n) {
if (n > 6)
return 0;
return list >> n << 2 & 0xFF;
}
}

View file

@ -0,0 +1,45 @@
package org.jcodec.codecs.common.biari;
import java.io.IOException;
public class TreeBinarizer {
private Context[] models;
public TreeBinarizer() {
initContextModels();
}
private void initContextModels() {
this.models = new Context[255];
for (int i = 0; i < 255; i++)
this.models[i] = new Context(0, 0);
}
public void binarize(int symbol, MQEncoder encoder) throws IOException {
int inverted = 0;
int nextModel = 0;
int levelOffset = 0;
for (int i = 0; i < 8; i++) {
int bin = symbol >> 7 - i & 0x1;
encoder.encode(bin, this.models[nextModel]);
inverted |= bin << i;
levelOffset += 1 << i;
nextModel = levelOffset + inverted;
}
}
public int debinarize(MQDecoder decoder) throws IOException {
int symbol = 0;
int inverted = 0;
int nextModel = 0;
int levelOffset = 0;
for (int i = 0; i < 8; i++) {
int bin = decoder.decode(this.models[nextModel]);
symbol |= bin << 7 - i;
inverted |= bin << i;
levelOffset += 1 << i;
nextModel = levelOffset + inverted;
}
return symbol;
}
}

View file

@ -0,0 +1,234 @@
package org.jcodec.codecs.h264;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.jcodec.codecs.h264.decode.SliceHeaderReader;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.RefPicMarking;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.Demuxer;
import org.jcodec.common.DemuxerTrack;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.IntObjectMap;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.model.Packet;
public class BufferH264ES implements DemuxerTrack, Demuxer {
private ByteBuffer bb;
private IntObjectMap<PictureParameterSet> pps;
private IntObjectMap<SeqParameterSet> sps;
private int prevFrameNumOffset;
private int prevFrameNum;
private int prevPicOrderCntMsb;
private int prevPicOrderCntLsb;
private int frameNo;
public BufferH264ES(ByteBuffer bb) {
this.pps = new IntObjectMap<>();
this.sps = new IntObjectMap<>();
this.bb = bb;
this.frameNo = 0;
}
public Packet nextFrame() {
ByteBuffer result = this.bb.duplicate();
NALUnit prevNu = null;
SliceHeader prevSh = null;
while (true) {
this.bb.mark();
ByteBuffer buf = H264Utils.nextNALUnit(this.bb);
if (buf == null)
break;
NALUnit nu = NALUnit.read(buf);
if (nu.type == NALUnitType.IDR_SLICE || nu.type == NALUnitType.NON_IDR_SLICE) {
SliceHeader sh = readSliceHeader(buf, nu);
if (prevNu != null && prevSh != null && !sameFrame(prevNu, nu, prevSh, sh)) {
this.bb.reset();
break;
}
prevSh = sh;
prevNu = nu;
continue;
}
if (nu.type == NALUnitType.PPS) {
PictureParameterSet read = PictureParameterSet.read(buf);
this.pps.put(read.picParameterSetId, read);
continue;
}
if (nu.type == NALUnitType.SPS) {
SeqParameterSet read = SeqParameterSet.read(buf);
this.sps.put(read.seqParameterSetId, read);
}
}
result.limit(this.bb.position());
return (prevSh == null) ? null : detectPoc(result, prevNu, prevSh);
}
private SliceHeader readSliceHeader(ByteBuffer buf, NALUnit nu) {
BitReader br = BitReader.createBitReader(buf);
SliceHeader sh = SliceHeaderReader.readPart1(br);
PictureParameterSet pp = this.pps.get(sh.picParameterSetId);
SliceHeaderReader.readPart2(sh, nu, this.sps.get(pp.seqParameterSetId), pp, br);
return sh;
}
private boolean sameFrame(NALUnit nu1, NALUnit nu2, SliceHeader sh1, SliceHeader sh2) {
if (sh1.picParameterSetId != sh2.picParameterSetId)
return false;
if (sh1.frameNum != sh2.frameNum)
return false;
SeqParameterSet sps = sh1.sps;
if (sps.picOrderCntType == 0 && sh1.picOrderCntLsb != sh2.picOrderCntLsb)
return false;
if (sps.picOrderCntType == 1 && (sh1.deltaPicOrderCnt[0] != sh2.deltaPicOrderCnt[0] || sh1.deltaPicOrderCnt[1] != sh2.deltaPicOrderCnt[1]))
return false;
if ((nu1.nal_ref_idc == 0 || nu2.nal_ref_idc == 0) && nu1.nal_ref_idc != nu2.nal_ref_idc)
return false;
if (((nu1.type == NALUnitType.IDR_SLICE) ? true : false) != ((nu2.type == NALUnitType.IDR_SLICE) ? true : false))
return false;
if (sh1.idrPicId != sh2.idrPicId)
return false;
return true;
}
private Packet detectPoc(ByteBuffer result, NALUnit nu, SliceHeader sh) {
int maxFrameNum = 1 << sh.sps.log2MaxFrameNumMinus4 + 4;
if (detectGap(sh, maxFrameNum))
issueNonExistingPic(sh, maxFrameNum);
int absFrameNum = updateFrameNumber(sh.frameNum, maxFrameNum, detectMMCO5(sh.refPicMarkingNonIDR));
int poc = 0;
if (nu.type == NALUnitType.NON_IDR_SLICE)
poc = calcPoc(absFrameNum, nu, sh);
return new Packet(result, (long)absFrameNum, 1, 1L, (long)this.frameNo++, (nu.type == NALUnitType.IDR_SLICE) ? Packet.FrameType.KEY : Packet.FrameType.INTER, null, poc);
}
private int updateFrameNumber(int frameNo, int maxFrameNum, boolean mmco5) {
int frameNumOffset;
if (this.prevFrameNum > frameNo) {
frameNumOffset = this.prevFrameNumOffset + maxFrameNum;
} else {
frameNumOffset = this.prevFrameNumOffset;
}
int absFrameNum = frameNumOffset + frameNo;
this.prevFrameNum = mmco5 ? 0 : frameNo;
this.prevFrameNumOffset = frameNumOffset;
return absFrameNum;
}
private void issueNonExistingPic(SliceHeader sh, int maxFrameNum) {
int nextFrameNum = (this.prevFrameNum + 1) % maxFrameNum;
this.prevFrameNum = nextFrameNum;
}
private boolean detectGap(SliceHeader sh, int maxFrameNum) {
return (sh.frameNum != this.prevFrameNum && sh.frameNum != (this.prevFrameNum + 1) % maxFrameNum);
}
private int calcPoc(int absFrameNum, NALUnit nu, SliceHeader sh) {
if (sh.sps.picOrderCntType == 0)
return calcPOC0(nu, sh);
if (sh.sps.picOrderCntType == 1)
return calcPOC1(absFrameNum, nu, sh);
return calcPOC2(absFrameNum, nu, sh);
}
private int calcPOC2(int absFrameNum, NALUnit nu, SliceHeader sh) {
if (nu.nal_ref_idc == 0)
return 2 * absFrameNum - 1;
return 2 * absFrameNum;
}
private int calcPOC1(int absFrameNum, NALUnit nu, SliceHeader sh) {
int expectedPicOrderCnt;
if (sh.sps.numRefFramesInPicOrderCntCycle == 0)
absFrameNum = 0;
if (nu.nal_ref_idc == 0 && absFrameNum > 0)
absFrameNum--;
int expectedDeltaPerPicOrderCntCycle = 0;
for (int i = 0; i < sh.sps.numRefFramesInPicOrderCntCycle; i++)
expectedDeltaPerPicOrderCntCycle += sh.sps.offsetForRefFrame[i];
if (absFrameNum > 0) {
int picOrderCntCycleCnt = (absFrameNum - 1) / sh.sps.numRefFramesInPicOrderCntCycle;
int frameNumInPicOrderCntCycle = (absFrameNum - 1) % sh.sps.numRefFramesInPicOrderCntCycle;
expectedPicOrderCnt = picOrderCntCycleCnt * expectedDeltaPerPicOrderCntCycle;
for (int j = 0; j <= frameNumInPicOrderCntCycle; j++)
expectedPicOrderCnt += sh.sps.offsetForRefFrame[j];
} else {
expectedPicOrderCnt = 0;
}
if (nu.nal_ref_idc == 0)
expectedPicOrderCnt += sh.sps.offsetForNonRefPic;
return expectedPicOrderCnt + sh.deltaPicOrderCnt[0];
}
private int calcPOC0(NALUnit nu, SliceHeader sh) {
int picOrderCntMsb;
int pocCntLsb = sh.picOrderCntLsb;
int maxPicOrderCntLsb = 1 << sh.sps.log2MaxPicOrderCntLsbMinus4 + 4;
if (pocCntLsb < this.prevPicOrderCntLsb && this.prevPicOrderCntLsb - pocCntLsb >= maxPicOrderCntLsb / 2) {
picOrderCntMsb = this.prevPicOrderCntMsb + maxPicOrderCntLsb;
} else if (pocCntLsb > this.prevPicOrderCntLsb && pocCntLsb - this.prevPicOrderCntLsb > maxPicOrderCntLsb / 2) {
picOrderCntMsb = this.prevPicOrderCntMsb - maxPicOrderCntLsb;
} else {
picOrderCntMsb = this.prevPicOrderCntMsb;
}
if (nu.nal_ref_idc != 0) {
this.prevPicOrderCntMsb = picOrderCntMsb;
this.prevPicOrderCntLsb = pocCntLsb;
}
return picOrderCntMsb + pocCntLsb;
}
private boolean detectMMCO5(RefPicMarking refPicMarkingNonIDR) {
if (refPicMarkingNonIDR == null)
return false;
RefPicMarking.Instruction[] instructions = refPicMarkingNonIDR.getInstructions();
for (int i = 0; i < instructions.length; i++) {
RefPicMarking.Instruction instr = instructions[i];
if (instr.getType() == RefPicMarking.InstrType.CLEAR)
return true;
}
return false;
}
public SeqParameterSet[] getSps() {
return this.sps.values(new SeqParameterSet[0]);
}
public PictureParameterSet[] getPps() {
return this.pps.values(new PictureParameterSet[0]);
}
public DemuxerTrackMeta getMeta() {
return null;
}
public void close() throws IOException {}
public List<? extends DemuxerTrack> getTracks() {
return getVideoTracks();
}
public List<? extends DemuxerTrack> getVideoTracks() {
List<DemuxerTrack> tracks = new ArrayList<>();
tracks.add(this);
return tracks;
}
public List<? extends DemuxerTrack> getAudioTracks() {
List<DemuxerTrack> tracks = new ArrayList<>();
return tracks;
}
}

View file

@ -0,0 +1,694 @@
package org.jcodec.codecs.h264;
import java.util.Arrays;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.common.io.VLC;
import org.jcodec.common.io.VLCBuilder;
import org.jcodec.common.model.Picture;
public class H264Const {
public static final VLC[] CoeffToken = new VLC[10];
public static final VLC coeffTokenChromaDCY420;
public static final VLC coeffTokenChromaDCY422;
static {
VLCBuilder vbl = new VLCBuilder();
vbl.set(0, "1");
vbl.set(coeffToken(1, 0), "000101");
vbl.set(coeffToken(1, 1), "01");
vbl.set(coeffToken(2, 0), "00000111");
vbl.set(coeffToken(2, 1), "000100");
vbl.set(coeffToken(2, 2), "001");
vbl.set(coeffToken(3, 0), "000000111");
vbl.set(coeffToken(3, 1), "00000110");
vbl.set(coeffToken(3, 2), "0000101");
vbl.set(coeffToken(3, 3), "00011");
vbl.set(coeffToken(4, 0), "0000000111");
vbl.set(coeffToken(4, 1), "000000110");
vbl.set(coeffToken(4, 2), "00000101");
vbl.set(coeffToken(4, 3), "000011");
vbl.set(coeffToken(5, 0), "00000000111");
vbl.set(coeffToken(5, 1), "0000000110");
vbl.set(coeffToken(5, 2), "000000101");
vbl.set(coeffToken(5, 3), "0000100");
vbl.set(coeffToken(6, 0), "0000000001111");
vbl.set(coeffToken(6, 1), "00000000110");
vbl.set(coeffToken(6, 2), "0000000101");
vbl.set(coeffToken(6, 3), "00000100");
vbl.set(coeffToken(7, 0), "0000000001011");
vbl.set(coeffToken(7, 1), "0000000001110");
vbl.set(coeffToken(7, 2), "00000000101");
vbl.set(coeffToken(7, 3), "000000100");
vbl.set(coeffToken(8, 0), "0000000001000");
vbl.set(coeffToken(8, 1), "0000000001010");
vbl.set(coeffToken(8, 2), "0000000001101");
vbl.set(coeffToken(8, 3), "0000000100");
vbl.set(coeffToken(9, 0), "00000000001111");
vbl.set(coeffToken(9, 1), "00000000001110");
vbl.set(coeffToken(9, 2), "0000000001001");
vbl.set(coeffToken(9, 3), "00000000100");
vbl.set(coeffToken(10, 0), "00000000001011");
vbl.set(coeffToken(10, 1), "00000000001010");
vbl.set(coeffToken(10, 2), "00000000001101");
vbl.set(coeffToken(10, 3), "0000000001100");
vbl.set(coeffToken(11, 0), "000000000001111");
vbl.set(coeffToken(11, 1), "000000000001110");
vbl.set(coeffToken(11, 2), "00000000001001");
vbl.set(coeffToken(11, 3), "00000000001100");
vbl.set(coeffToken(12, 0), "000000000001011");
vbl.set(coeffToken(12, 1), "000000000001010");
vbl.set(coeffToken(12, 2), "000000000001101");
vbl.set(coeffToken(12, 3), "00000000001000");
vbl.set(coeffToken(13, 0), "0000000000001111");
vbl.set(coeffToken(13, 1), "000000000000001");
vbl.set(coeffToken(13, 2), "000000000001001");
vbl.set(coeffToken(13, 3), "000000000001100");
vbl.set(coeffToken(14, 0), "0000000000001011");
vbl.set(coeffToken(14, 1), "0000000000001110");
vbl.set(coeffToken(14, 2), "0000000000001101");
vbl.set(coeffToken(14, 3), "000000000001000");
vbl.set(coeffToken(15, 0), "0000000000000111");
vbl.set(coeffToken(15, 1), "0000000000001010");
vbl.set(coeffToken(15, 2), "0000000000001001");
vbl.set(coeffToken(15, 3), "0000000000001100");
vbl.set(coeffToken(16, 0), "0000000000000100");
vbl.set(coeffToken(16, 1), "0000000000000110");
vbl.set(coeffToken(16, 2), "0000000000000101");
vbl.set(coeffToken(16, 3), "0000000000001000");
CoeffToken[0] = CoeffToken[1] = vbl.getVLC();
vbl = new VLCBuilder();
vbl.set(coeffToken(0, 0), "11");
vbl.set(coeffToken(1, 0), "001011");
vbl.set(coeffToken(1, 1), "10");
vbl.set(coeffToken(2, 0), "000111");
vbl.set(coeffToken(2, 1), "00111");
vbl.set(coeffToken(2, 2), "011");
vbl.set(coeffToken(3, 0), "0000111");
vbl.set(coeffToken(3, 1), "001010");
vbl.set(coeffToken(3, 2), "001001");
vbl.set(coeffToken(3, 3), "0101");
vbl.set(coeffToken(4, 0), "00000111");
vbl.set(coeffToken(4, 1), "000110");
vbl.set(coeffToken(4, 2), "000101");
vbl.set(coeffToken(4, 3), "0100");
vbl.set(coeffToken(5, 0), "00000100");
vbl.set(coeffToken(5, 1), "0000110");
vbl.set(coeffToken(5, 2), "0000101");
vbl.set(coeffToken(5, 3), "00110");
vbl.set(coeffToken(6, 0), "000000111");
vbl.set(coeffToken(6, 1), "00000110");
vbl.set(coeffToken(6, 2), "00000101");
vbl.set(coeffToken(6, 3), "001000");
vbl.set(coeffToken(7, 0), "00000001111");
vbl.set(coeffToken(7, 1), "000000110");
vbl.set(coeffToken(7, 2), "000000101");
vbl.set(coeffToken(7, 3), "000100");
vbl.set(coeffToken(8, 0), "00000001011");
vbl.set(coeffToken(8, 1), "00000001110");
vbl.set(coeffToken(8, 2), "00000001101");
vbl.set(coeffToken(8, 3), "0000100");
vbl.set(coeffToken(9, 0), "000000001111");
vbl.set(coeffToken(9, 1), "00000001010");
vbl.set(coeffToken(9, 2), "00000001001");
vbl.set(coeffToken(9, 3), "000000100");
vbl.set(coeffToken(10, 0), "000000001011");
vbl.set(coeffToken(10, 1), "000000001110");
vbl.set(coeffToken(10, 2), "000000001101");
vbl.set(coeffToken(10, 3), "00000001100");
vbl.set(coeffToken(11, 0), "000000001000");
vbl.set(coeffToken(11, 1), "000000001010");
vbl.set(coeffToken(11, 2), "000000001001");
vbl.set(coeffToken(11, 3), "00000001000");
vbl.set(coeffToken(12, 0), "0000000001111");
vbl.set(coeffToken(12, 1), "0000000001110");
vbl.set(coeffToken(12, 2), "0000000001101");
vbl.set(coeffToken(12, 3), "000000001100");
vbl.set(coeffToken(13, 0), "0000000001011");
vbl.set(coeffToken(13, 1), "0000000001010");
vbl.set(coeffToken(13, 2), "0000000001001");
vbl.set(coeffToken(13, 3), "0000000001100");
vbl.set(coeffToken(14, 0), "0000000000111");
vbl.set(coeffToken(14, 1), "00000000001011");
vbl.set(coeffToken(14, 2), "0000000000110");
vbl.set(coeffToken(14, 3), "0000000001000");
vbl.set(coeffToken(15, 0), "00000000001001");
vbl.set(coeffToken(15, 1), "00000000001000");
vbl.set(coeffToken(15, 2), "00000000001010");
vbl.set(coeffToken(15, 3), "0000000000001");
vbl.set(coeffToken(16, 0), "00000000000111");
vbl.set(coeffToken(16, 1), "00000000000110");
vbl.set(coeffToken(16, 2), "00000000000101");
vbl.set(coeffToken(16, 3), "00000000000100");
CoeffToken[2] = CoeffToken[3] = vbl.getVLC();
vbl = new VLCBuilder();
vbl.set(coeffToken(0, 0), "1111");
vbl.set(coeffToken(1, 0), "001111");
vbl.set(coeffToken(1, 1), "1110");
vbl.set(coeffToken(2, 0), "001011");
vbl.set(coeffToken(2, 1), "01111");
vbl.set(coeffToken(2, 2), "1101");
vbl.set(coeffToken(3, 0), "001000");
vbl.set(coeffToken(3, 1), "01100");
vbl.set(coeffToken(3, 2), "01110");
vbl.set(coeffToken(3, 3), "1100");
vbl.set(coeffToken(4, 0), "0001111");
vbl.set(coeffToken(4, 1), "01010");
vbl.set(coeffToken(4, 2), "01011");
vbl.set(coeffToken(4, 3), "1011");
vbl.set(coeffToken(5, 0), "0001011");
vbl.set(coeffToken(5, 1), "01000");
vbl.set(coeffToken(5, 2), "01001");
vbl.set(coeffToken(5, 3), "1010");
vbl.set(coeffToken(6, 0), "0001001");
vbl.set(coeffToken(6, 1), "001110");
vbl.set(coeffToken(6, 2), "001101");
vbl.set(coeffToken(6, 3), "1001");
vbl.set(coeffToken(7, 0), "0001000");
vbl.set(coeffToken(7, 1), "001010");
vbl.set(coeffToken(7, 2), "001001");
vbl.set(coeffToken(7, 3), "1000");
vbl.set(coeffToken(8, 0), "00001111");
vbl.set(coeffToken(8, 1), "0001110");
vbl.set(coeffToken(8, 2), "0001101");
vbl.set(coeffToken(8, 3), "01101");
vbl.set(coeffToken(9, 0), "00001011");
vbl.set(coeffToken(9, 1), "00001110");
vbl.set(coeffToken(9, 2), "0001010");
vbl.set(coeffToken(9, 3), "001100");
vbl.set(coeffToken(10, 0), "000001111");
vbl.set(coeffToken(10, 1), "00001010");
vbl.set(coeffToken(10, 2), "00001101");
vbl.set(coeffToken(10, 3), "0001100");
vbl.set(coeffToken(11, 0), "000001011");
vbl.set(coeffToken(11, 1), "000001110");
vbl.set(coeffToken(11, 2), "00001001");
vbl.set(coeffToken(11, 3), "00001100");
vbl.set(coeffToken(12, 0), "000001000");
vbl.set(coeffToken(12, 1), "000001010");
vbl.set(coeffToken(12, 2), "000001101");
vbl.set(coeffToken(12, 3), "00001000");
vbl.set(coeffToken(13, 0), "0000001101");
vbl.set(coeffToken(13, 1), "000000111");
vbl.set(coeffToken(13, 2), "000001001");
vbl.set(coeffToken(13, 3), "000001100");
vbl.set(coeffToken(14, 0), "0000001001");
vbl.set(coeffToken(14, 1), "0000001100");
vbl.set(coeffToken(14, 2), "0000001011");
vbl.set(coeffToken(14, 3), "0000001010");
vbl.set(coeffToken(15, 0), "0000000101");
vbl.set(coeffToken(15, 1), "0000001000");
vbl.set(coeffToken(15, 2), "0000000111");
vbl.set(coeffToken(15, 3), "0000000110");
vbl.set(coeffToken(16, 0), "0000000001");
vbl.set(coeffToken(16, 1), "0000000100");
vbl.set(coeffToken(16, 2), "0000000011");
vbl.set(coeffToken(16, 3), "0000000010");
CoeffToken[4] = CoeffToken[5] = CoeffToken[6] = CoeffToken[7] = vbl.getVLC();
vbl = new VLCBuilder();
vbl.set(coeffToken(0, 0), "000011");
vbl.set(coeffToken(1, 0), "000000");
vbl.set(coeffToken(1, 1), "000001");
vbl.set(coeffToken(2, 0), "000100");
vbl.set(coeffToken(2, 1), "000101");
vbl.set(coeffToken(2, 2), "000110");
vbl.set(coeffToken(3, 0), "001000");
vbl.set(coeffToken(3, 1), "001001");
vbl.set(coeffToken(3, 2), "001010");
vbl.set(coeffToken(3, 3), "001011");
vbl.set(coeffToken(4, 0), "001100");
vbl.set(coeffToken(4, 1), "001101");
vbl.set(coeffToken(4, 2), "001110");
vbl.set(coeffToken(4, 3), "001111");
vbl.set(coeffToken(5, 0), "010000");
vbl.set(coeffToken(5, 1), "010001");
vbl.set(coeffToken(5, 2), "010010");
vbl.set(coeffToken(5, 3), "010011");
vbl.set(coeffToken(6, 0), "010100");
vbl.set(coeffToken(6, 1), "010101");
vbl.set(coeffToken(6, 2), "010110");
vbl.set(coeffToken(6, 3), "010111");
vbl.set(coeffToken(7, 0), "011000");
vbl.set(coeffToken(7, 1), "011001");
vbl.set(coeffToken(7, 2), "011010");
vbl.set(coeffToken(7, 3), "011011");
vbl.set(coeffToken(8, 0), "011100");
vbl.set(coeffToken(8, 1), "011101");
vbl.set(coeffToken(8, 2), "011110");
vbl.set(coeffToken(8, 3), "011111");
vbl.set(coeffToken(9, 0), "100000");
vbl.set(coeffToken(9, 1), "100001");
vbl.set(coeffToken(9, 2), "100010");
vbl.set(coeffToken(9, 3), "100011");
vbl.set(coeffToken(10, 0), "100100");
vbl.set(coeffToken(10, 1), "100101");
vbl.set(coeffToken(10, 2), "100110");
vbl.set(coeffToken(10, 3), "100111");
vbl.set(coeffToken(11, 0), "101000");
vbl.set(coeffToken(11, 1), "101001");
vbl.set(coeffToken(11, 2), "101010");
vbl.set(coeffToken(11, 3), "101011");
vbl.set(coeffToken(12, 0), "101100");
vbl.set(coeffToken(12, 1), "101101");
vbl.set(coeffToken(12, 2), "101110");
vbl.set(coeffToken(12, 3), "101111");
vbl.set(coeffToken(13, 0), "110000");
vbl.set(coeffToken(13, 1), "110001");
vbl.set(coeffToken(13, 2), "110010");
vbl.set(coeffToken(13, 3), "110011");
vbl.set(coeffToken(14, 0), "110100");
vbl.set(coeffToken(14, 1), "110101");
vbl.set(coeffToken(14, 2), "110110");
vbl.set(coeffToken(14, 3), "110111");
vbl.set(coeffToken(15, 0), "111000");
vbl.set(coeffToken(15, 1), "111001");
vbl.set(coeffToken(15, 2), "111010");
vbl.set(coeffToken(15, 3), "111011");
vbl.set(coeffToken(16, 0), "111100");
vbl.set(coeffToken(16, 1), "111101");
vbl.set(coeffToken(16, 2), "111110");
vbl.set(coeffToken(16, 3), "111111");
CoeffToken[8] = vbl.getVLC();
vbl = new VLCBuilder();
vbl.set(coeffToken(0, 0), "01");
vbl.set(coeffToken(1, 0), "000111");
vbl.set(coeffToken(1, 1), "1");
vbl.set(coeffToken(2, 0), "000100");
vbl.set(coeffToken(2, 1), "000110");
vbl.set(coeffToken(2, 2), "001");
vbl.set(coeffToken(3, 0), "000011");
vbl.set(coeffToken(3, 1), "0000011");
vbl.set(coeffToken(3, 2), "0000010");
vbl.set(coeffToken(3, 3), "000101");
vbl.set(coeffToken(4, 0), "000010");
vbl.set(coeffToken(4, 1), "00000011");
vbl.set(coeffToken(4, 2), "00000010");
vbl.set(coeffToken(4, 3), "0000000");
coeffTokenChromaDCY420 = vbl.getVLC();
vbl = new VLCBuilder();
vbl.set(coeffToken(0, 0), "1");
vbl.set(coeffToken(1, 0), "0001111");
vbl.set(coeffToken(1, 1), "01");
vbl.set(coeffToken(2, 0), "0001110");
vbl.set(coeffToken(2, 1), "0001101");
vbl.set(coeffToken(2, 2), "001");
vbl.set(coeffToken(3, 0), "000000111");
vbl.set(coeffToken(3, 1), "0001100");
vbl.set(coeffToken(3, 2), "0001011");
vbl.set(coeffToken(3, 3), "00001");
vbl.set(coeffToken(4, 0), "000000110");
vbl.set(coeffToken(4, 1), "000000101");
vbl.set(coeffToken(4, 2), "0001010");
vbl.set(coeffToken(4, 3), "000001");
vbl.set(coeffToken(5, 0), "0000000111");
vbl.set(coeffToken(5, 1), "0000000110");
vbl.set(coeffToken(5, 2), "000000100");
vbl.set(coeffToken(5, 3), "0001001");
vbl.set(coeffToken(6, 0), "00000000111");
vbl.set(coeffToken(6, 1), "00000000110");
vbl.set(coeffToken(6, 2), "0000000101");
vbl.set(coeffToken(6, 3), "0001000");
vbl.set(coeffToken(7, 0), "000000000111");
vbl.set(coeffToken(7, 1), "000000000110");
vbl.set(coeffToken(7, 2), "00000000101");
vbl.set(coeffToken(7, 3), "0000000100");
vbl.set(coeffToken(8, 0), "0000000000111");
vbl.set(coeffToken(8, 1), "000000000101");
vbl.set(coeffToken(8, 2), "000000000100");
vbl.set(coeffToken(8, 3), "00000000100");
coeffTokenChromaDCY422 = vbl.getVLC();
}
public static final VLC[] run = new VLC[] { new VLCBuilder()
.set(0, "1").set(1, "0").getVLC(), new VLCBuilder()
.set(0, "1").set(1, "01").set(2, "00").getVLC(), new VLCBuilder()
.set(0, "11").set(1, "10").set(2, "01").set(3, "00").getVLC(), new VLCBuilder()
.set(0, "11").set(1, "10").set(2, "01").set(3, "001").set(4, "000").getVLC(), new VLCBuilder()
.set(0, "11").set(1, "10").set(2, "011").set(3, "010").set(4, "001").set(5, "000")
.getVLC(), new VLCBuilder()
.set(0, "11").set(1, "000").set(2, "001").set(3, "011").set(4, "010").set(5, "101")
.set(6, "100").getVLC(), new VLCBuilder()
.set(0, "111").set(1, "110").set(2, "101").set(3, "100").set(4, "011").set(5, "010")
.set(6, "001").set(7, "0001").set(8, "00001").set(9, "000001").set(10, "0000001")
.set(11, "00000001").set(12, "000000001").set(13, "0000000001").set(14, "00000000001").getVLC() };
public static final VLC[] totalZeros16 = new VLC[] {
new VLCBuilder()
.set(0, "1").set(1, "011").set(2, "010").set(3, "0011").set(4, "0010").set(5, "00011")
.set(6, "00010").set(7, "000011").set(8, "000010").set(9, "0000011").set(10, "0000010")
.set(11, "00000011").set(12, "00000010").set(13, "000000011").set(14, "000000010")
.set(15, "000000001").getVLC(), new VLCBuilder()
.set(0, "111").set(1, "110").set(2, "101").set(3, "100").set(4, "011").set(5, "0101")
.set(6, "0100").set(7, "0011").set(8, "0010").set(9, "00011").set(10, "00010").set(11, "000011")
.set(12, "000010").set(13, "000001").set(14, "000000").getVLC(), new VLCBuilder()
.set(0, "0101").set(1, "111").set(2, "110").set(3, "101").set(4, "0100").set(5, "0011")
.set(6, "100").set(7, "011").set(8, "0010").set(9, "00011").set(10, "00010").set(11, "000001")
.set(12, "00001").set(13, "000000").getVLC(), new VLCBuilder()
.set(0, "00011").set(1, "111").set(2, "0101").set(3, "0100").set(4, "110").set(5, "101")
.set(6, "100").set(7, "0011").set(8, "011").set(9, "0010").set(10, "00010").set(11, "00001")
.set(12, "00000").getVLC(), new VLCBuilder()
.set(0, "0101").set(1, "0100").set(2, "0011").set(3, "111").set(4, "110").set(5, "101")
.set(6, "100").set(7, "011").set(8, "0010").set(9, "00001").set(10, "0001").set(11, "00000")
.getVLC(), new VLCBuilder()
.set(0, "000001").set(1, "00001").set(2, "111").set(3, "110").set(4, "101").set(5, "100")
.set(6, "011").set(7, "010").set(8, "0001").set(9, "001").set(10, "000000").getVLC(), new VLCBuilder()
.set(0, "000001").set(1, "00001").set(2, "101").set(3, "100").set(4, "011").set(5, "11")
.set(6, "010").set(7, "0001").set(8, "001").set(9, "000000").getVLC(), new VLCBuilder()
.set(0, "000001").set(1, "0001").set(2, "00001").set(3, "011").set(4, "11").set(5, "10")
.set(6, "010").set(7, "001").set(8, "000000").getVLC(), new VLCBuilder()
.set(0, "000001").set(1, "000000").set(2, "0001").set(3, "11").set(4, "10").set(5, "001")
.set(6, "01").set(7, "00001").getVLC(), new VLCBuilder()
.set(0, "00001").set(1, "00000").set(2, "001").set(3, "11").set(4, "10").set(5, "01")
.set(6, "0001").getVLC(),
new VLCBuilder()
.set(0, "0000").set(1, "0001").set(2, "001").set(3, "010").set(4, "1").set(5, "011")
.getVLC(), new VLCBuilder()
.set(0, "0000").set(1, "0001").set(2, "01").set(3, "1").set(4, "001").getVLC(), new VLCBuilder()
.set(0, "000").set(1, "001").set(2, "1").set(3, "01").getVLC(), new VLCBuilder()
.set(0, "00").set(1, "01").set(2, "1").getVLC(), new VLCBuilder()
.set(0, "0").set(1, "1").getVLC() };
public static final VLC[] totalZeros4 = new VLC[] { new VLCBuilder().set(0, "1").set(1, "01").set(2, "001").set(3, "000").getVLC(), new VLCBuilder()
.set(0, "1").set(1, "01").set(2, "00").getVLC(), new VLCBuilder()
.set(0, "1").set(1, "0").getVLC() };
public static final VLC[] totalZeros8 = new VLC[] { new VLCBuilder()
.set(0, "1").set(1, "010").set(2, "011").set(3, "0010").set(4, "0011").set(5, "0001")
.set(6, "00001").set(7, "00000").getVLC(), new VLCBuilder()
.set(0, "000").set(1, "01").set(2, "001").set(3, "100").set(4, "101").set(5, "110")
.set(6, "111").getVLC(), new VLCBuilder()
.set(0, "000").set(1, "001").set(2, "01").set(3, "10").set(4, "110").set(5, "111").getVLC(), new VLCBuilder()
.set(0, "110").set(1, "00").set(2, "01").set(3, "10").set(4, "111").getVLC(), new VLCBuilder()
.set(0, "00").set(1, "01").set(2, "10").set(3, "11").getVLC(), new VLCBuilder()
.set(0, "00").set(1, "01").set(2, "1").getVLC(), new VLCBuilder()
.set(0, "0").set(1, "1").getVLC() };
public enum PartPred {
L0, L1, Bi, Direct;
}
public static final PartPred[][] bPredModes = new PartPred[][] {
null, new PartPred[] { PartPred.L0 }, new PartPred[] { PartPred.L1 }, new PartPred[] { PartPred.Bi }, new PartPred[] { PartPred.L0, PartPred.L0 }, new PartPred[] { PartPred.L0, PartPred.L0 }, new PartPred[] { PartPred.L1, PartPred.L1 }, new PartPred[] { PartPred.L1, PartPred.L1 }, new PartPred[] { PartPred.L0, PartPred.L1 }, new PartPred[] { PartPred.L0, PartPred.L1 },
new PartPred[] { PartPred.L1, PartPred.L0 }, new PartPred[] { PartPred.L1, PartPred.L0 }, new PartPred[] { PartPred.L0, PartPred.Bi }, new PartPred[] { PartPred.L0, PartPred.Bi }, new PartPred[] { PartPred.L1, PartPred.Bi }, new PartPred[] { PartPred.L1, PartPred.Bi }, new PartPred[] { PartPred.Bi, PartPred.L0 }, new PartPred[] { PartPred.Bi, PartPred.L0 }, new PartPred[] { PartPred.Bi, PartPred.L1 }, new PartPred[] { PartPred.Bi, PartPred.L1 },
new PartPred[] { PartPred.Bi, PartPred.Bi }, new PartPred[] { PartPred.Bi, PartPred.Bi } };
public static final MBType[] bMbTypes = new MBType[] {
MBType.B_Direct_16x16, MBType.B_L0_16x16, MBType.B_L1_16x16, MBType.B_Bi_16x16, MBType.B_L0_L0_16x8, MBType.B_L0_L0_8x16, MBType.B_L1_L1_16x8, MBType.B_L1_L1_8x16, MBType.B_L0_L1_16x8, MBType.B_L0_L1_8x16,
MBType.B_L1_L0_16x8, MBType.B_L1_L0_8x16, MBType.B_L0_Bi_16x8, MBType.B_L0_Bi_8x16, MBType.B_L1_Bi_16x8, MBType.B_L1_Bi_8x16, MBType.B_Bi_L0_16x8, MBType.B_Bi_L0_8x16, MBType.B_Bi_L1_16x8, MBType.B_Bi_L1_8x16,
MBType.B_Bi_Bi_16x8, MBType.B_Bi_Bi_8x16, MBType.B_8x8 };
public static final int[] bPartW = new int[] {
0, 16, 16, 16, 16, 8, 16, 8, 16, 8,
16, 8, 16, 8, 16, 8, 16, 8, 16, 8,
16, 8 };
public static final int[] bPartH = new int[] {
0, 16, 16, 16, 8, 16, 8, 16, 8, 16,
8, 16, 8, 16, 8, 16, 8, 16, 8, 16,
8, 16 };
public static final int[] BLK_X = new int[] {
0, 4, 0, 4, 8, 12, 8, 12, 0, 4,
0, 4, 8, 12, 8, 12 };
public static final int[] BLK_Y = new int[] {
0, 0, 4, 4, 0, 0, 4, 4, 8, 8,
12, 12, 8, 8, 12, 12 };
public static final int[] BLK_8x8_X = new int[] { 0, 8, 0, 8 };
public static final int[] BLK_8x8_Y = new int[] { 0, 0, 8, 8 };
public static final int[] BLK_INV_MAP = new int[] {
0, 1, 4, 5, 2, 3, 6, 7, 8, 9,
12, 13, 10, 11, 14, 15 };
public static final int[] MB_BLK_OFF_LEFT = new int[] {
0, 1, 0, 1, 2, 3, 2, 3, 0, 1,
0, 1, 2, 3, 2, 3 };
public static final int[] MB_BLK_OFF_TOP = new int[] {
0, 0, 1, 1, 0, 0, 1, 1, 2, 2,
3, 3, 2, 2, 3, 3 };
public static final int[] QP_SCALE_CR = new int[] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
29, 30, 31, 32, 32, 33, 34, 34, 35, 35,
36, 36, 37, 37, 37, 38, 38, 38, 39, 39,
39, 39 };
public static final Picture NO_PIC = Picture.createPicture(0, 0, null, null);
public static final int[] BLK_8x8_MB_OFF_LUMA = new int[] { 0, 8, 128, 136 };
public static final int[] BLK_8x8_MB_OFF_CHROMA = new int[] { 0, 4, 32, 36 };
public static final int[] BLK_4x4_MB_OFF_LUMA = new int[] {
0, 4, 8, 12, 64, 68, 72, 76, 128, 132,
136, 140, 192, 196, 200, 204 };
public static final int[] BLK_8x8_IND = new int[] {
0, 0, 1, 1, 0, 0, 1, 1, 2, 2,
3, 3, 2, 2, 3, 3 };
public static final int[][] BLK8x8_BLOCKS = new int[][] { new int[] { 0, 1, 4, 5 }, new int[] { 2, 3, 6, 7 }, new int[] { 8, 9, 12, 13 }, new int[] { 10, 11, 14, 15 } };
public static final int[][] ARRAY = new int[][] { new int[] { 0 }, new int[] { 1 }, new int[] { 2 }, new int[] { 3 } };
public static final int[] CODED_BLOCK_PATTERN_INTRA_COLOR = new int[] {
47, 31, 15, 0, 23, 27, 29, 30, 7, 11,
13, 14, 39, 43, 45, 46, 16, 3, 5, 10,
12, 19, 21, 26, 28, 35, 37, 42, 44, 1,
2, 4, 8, 17, 18, 20, 24, 6, 9, 22,
25, 32, 33, 34, 36, 40, 38, 41 };
public static final int[] coded_block_pattern_intra_monochrome = new int[] {
15, 0, 7, 11, 13, 14, 3, 5, 10, 12,
1, 2, 4, 8, 6, 9 };
public static final int[] CODED_BLOCK_PATTERN_INTER_COLOR = new int[] {
0, 16, 1, 2, 4, 8, 32, 3, 5, 10,
12, 15, 47, 7, 11, 13, 14, 6, 9, 31,
35, 37, 42, 44, 33, 34, 36, 40, 39, 43,
45, 46, 17, 18, 20, 24, 19, 21, 26, 28,
23, 27, 29, 30, 22, 25, 38, 41 };
private static int[] inverse(int[] arr) {
int[] inv = new int[arr.length];
for (int i = 0; i < inv.length; i++)
inv[arr[i]] = i;
return inv;
}
public static final int[] CODED_BLOCK_PATTERN_INTER_COLOR_INV = inverse(CODED_BLOCK_PATTERN_INTER_COLOR);
public static final int[] coded_block_pattern_inter_monochrome = new int[] {
0, 1, 2, 4, 8, 3, 5, 10, 12, 15,
7, 11, 13, 14, 6, 9 };
public static final int[] sig_coeff_map_8x8 = new int[] {
0, 1, 2, 3, 4, 5, 5, 4, 4, 3,
3, 4, 4, 4, 5, 5, 4, 4, 4, 4,
3, 3, 6, 7, 7, 7, 8, 9, 10, 9,
8, 7, 7, 6, 11, 12, 13, 11, 6, 7,
8, 9, 14, 10, 9, 8, 6, 11, 12, 13,
11, 6, 9, 14, 10, 9, 11, 12, 13, 11,
14, 10, 12 };
public static final int[] sig_coeff_map_8x8_mbaff = new int[] {
0, 1, 1, 2, 2, 3, 3, 4, 5, 6,
7, 7, 7, 8, 4, 5, 6, 9, 10, 10,
8, 11, 12, 11, 9, 9, 10, 10, 8, 11,
12, 11, 9, 9, 10, 10, 8, 11, 12, 11,
9, 9, 10, 10, 8, 13, 13, 9, 9, 10,
10, 8, 13, 13, 9, 9, 10, 10, 14, 14,
14, 14, 14 };
public static final int[] last_sig_coeff_map_8x8 = new int[] {
0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 5, 5,
5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
8, 8, 8 };
public static final int[] identityMapping16 = new int[] {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15 };
public static final int[] identityMapping4 = new int[] { 0, 1, 2, 3 };
public static final PartPred[] bPartPredModes = new PartPred[] {
PartPred.Direct, PartPred.L0, PartPred.L1, PartPred.Bi, PartPred.L0, PartPred.L0, PartPred.L1, PartPred.L1, PartPred.Bi, PartPred.Bi,
PartPred.L0, PartPred.L1, PartPred.Bi };
public static final int[] bSubMbTypes = new int[] {
0, 0, 0, 0, 1, 2, 1, 2, 1, 2,
3, 3, 3 };
public static final int[] LUMA_4x4_BLOCK_LUT = new int[256];
public static final int[] LUMA_4x4_POS_LUT = new int[256];
public static final int[] LUMA_8x8_BLOCK_LUT = new int[256];
public static final int[] LUMA_8x8_POS_LUT = new int[256];
public static final int[] CHROMA_BLOCK_LUT = new int[64];
public static final int[] CHROMA_POS_LUT = new int[64];
public static final int[][] COMP_BLOCK_4x4_LUT = new int[][] { LUMA_4x4_BLOCK_LUT, CHROMA_BLOCK_LUT, CHROMA_BLOCK_LUT };
public static final int[][] COMP_POS_4x4_LUT = new int[][] { LUMA_4x4_POS_LUT, CHROMA_POS_LUT, CHROMA_POS_LUT };
public static final int[][] COMP_BLOCK_8x8_LUT = new int[][] { LUMA_8x8_BLOCK_LUT, CHROMA_BLOCK_LUT, CHROMA_BLOCK_LUT };
public static final int[][] COMP_POS_8x8_LUT = new int[][] { LUMA_8x8_POS_LUT, CHROMA_POS_LUT, CHROMA_POS_LUT };
static {
int[] tmp = new int[16];
for (int j = 0; j < 16; j++) {
for (int k = 0; k < 16; k++)
tmp[k] = k;
putBlk(tmp, BLK_X[j], BLK_Y[j], 4, 4, 16, LUMA_4x4_POS_LUT);
Arrays.fill(tmp, j);
putBlk(tmp, BLK_X[j], BLK_Y[j], 4, 4, 16, LUMA_4x4_BLOCK_LUT);
}
for (int i = 0; i < 4; i++) {
for (int k = 0; k < 16; k++)
tmp[k] = k;
putBlk(tmp, BLK_X[i], BLK_Y[i], 4, 4, 8, CHROMA_POS_LUT);
Arrays.fill(tmp, i);
putBlk(tmp, BLK_X[i], BLK_Y[i], 4, 4, 8, CHROMA_BLOCK_LUT);
}
tmp = new int[64];
for (int blk = 0; blk < 4; blk++) {
for (int k = 0; k < 64; k++)
tmp[k] = k;
putBlk(tmp, BLK_8x8_X[blk], BLK_8x8_Y[blk], 8, 8, 16, LUMA_8x8_POS_LUT);
Arrays.fill(tmp, blk);
putBlk(tmp, BLK_8x8_X[blk], BLK_8x8_Y[blk], 8, 8, 16, LUMA_8x8_BLOCK_LUT);
}
}
private static void putBlk(int[] _in, int blkX, int blkY, int blkW, int blkH, int stride, int[] out) {
for (int line = 0, srcOff = 0, dstOff = blkY * stride + blkX; line < blkH; line++) {
for (int i = 0; i < blkW; i++)
out[dstOff + i] = _in[srcOff + i];
srcOff += blkW;
dstOff += stride;
}
}
private static int[][] buildPixSplitMap4x4() {
int[][] result = {
new int[] {
0, 1, 2, 3, 16, 17, 18, 19, 32, 33,
34, 35, 48, 49, 50, 51 }, new int[16], new int[16], new int[16], new int[16], new int[16], new int[16], new int[16], new int[16], new int[16],
new int[16], new int[16], new int[16], new int[16], new int[16], new int[16] };
for (int blkY = 0, blk = 0, off = 0; blkY < 4; blkY++) {
for (int blkX = 0; blkX < 4; blkX++, blk++, off += 4) {
for (int i = 0; i < 16; i++)
result[blk][i] = result[0][i] + off;
}
off += 48;
}
return result;
}
private static int[][] buildPixSplitMap2x2() {
int[][] result = { new int[] {
0, 1, 2, 3, 8, 9, 10, 11, 16, 17,
18, 19, 24, 25, 26, 27 }, new int[16], new int[16], new int[16] };
for (int blkY = 0, blk = 0, off = 0; blkY < 2; blkY++) {
for (int blkX = 0; blkX < 2; blkX++, blk++, off += 4) {
for (int i = 0; i < 16; i++)
result[blk][i] = result[0][i] + off;
}
off += 24;
}
return result;
}
public static boolean usesList(PartPred pred, int l) {
return (pred == PartPred.Bi) ? true : (((pred == PartPred.L0 && l == 0) || (pred == PartPred.L1 && l == 1)));
}
public static final int coeffToken(int totalCoeff, int trailingOnes) {
return totalCoeff << 4 | trailingOnes;
}
public static final int[][] PIX_MAP_SPLIT_4x4 = buildPixSplitMap4x4();
public static final int[][] PIX_MAP_SPLIT_2x2 = buildPixSplitMap2x2();
public static final int PROFILE_CAVLC_INTRA = 44;
public static final int PROFILE_BASELINE = 66;
public static final int PROFILE_MAIN = 77;
public static final int PROFILE_EXTENDED = 88;
public static final int PROFILE_HIGH = 100;
public static final int PROFILE_HIGH_10 = 110;
public static final int PROFILE_HIGH_422 = 122;
public static final int PROFILE_HIGH_444 = 244;
public static final int[] defaultScalingList4x4Intra = new int[] {
6, 13, 13, 20, 20, 20, 28, 28, 28, 28,
32, 32, 32, 37, 37, 42 };
public static final int[] defaultScalingList4x4Inter = new int[] {
10, 14, 14, 20, 20, 20, 24, 24, 24, 24,
27, 27, 27, 30, 30, 34 };
public static final int[] defaultScalingList8x8Intra = new int[] {
6, 10, 10, 13, 11, 13, 16, 16, 16, 16,
18, 18, 18, 18, 18, 23, 23, 23, 23, 23,
23, 25, 25, 25, 25, 25, 25, 25, 27, 27,
27, 27, 27, 27, 27, 27, 29, 29, 29, 29,
29, 29, 29, 31, 31, 31, 31, 31, 31, 33,
33, 33, 33, 33, 36, 36, 36, 36, 38, 38,
38, 40, 40, 42 };
public static final int[] defaultScalingList8x8Inter = new int[] {
9, 13, 13, 15, 13, 15, 17, 17, 17, 17,
19, 19, 19, 19, 19, 21, 21, 21, 21, 21,
21, 22, 22, 22, 22, 22, 22, 22, 24, 24,
24, 24, 24, 24, 24, 24, 25, 25, 25, 25,
25, 25, 25, 27, 27, 27, 27, 27, 27, 28,
28, 28, 28, 28, 30, 30, 30, 30, 32, 32,
32, 33, 33, 35 };
}

View file

@ -0,0 +1,399 @@
package org.jcodec.codecs.h264;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import org.jcodec.codecs.h264.decode.DeblockerInput;
import org.jcodec.codecs.h264.decode.FrameReader;
import org.jcodec.codecs.h264.decode.SliceDecoder;
import org.jcodec.codecs.h264.decode.SliceHeaderReader;
import org.jcodec.codecs.h264.decode.SliceReader;
import org.jcodec.codecs.h264.decode.deblock.DeblockingFilter;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.RefPicMarking;
import org.jcodec.codecs.h264.io.model.RefPicMarkingIDR;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.IntObjectMap;
import org.jcodec.common.UsedViaReflection;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.VideoDecoder;
import org.jcodec.common.io.BitReader;
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 H264Decoder extends VideoDecoder {
private Frame[] sRefs;
private IntObjectMap<Frame> lRefs;
private List<Frame> pictureBuffer;
private POCManager poc;
private FrameReader reader;
private ExecutorService tp;
private boolean threaded;
public void shutdownThreadPool() {
if (this.threaded)
this.tp.shutdown();
}
public H264Decoder() {
this.pictureBuffer = new ArrayList<>();
this.poc = new POCManager();
this.threaded = (Runtime.getRuntime().availableProcessors() > 1);
if (this.threaded)
this.tp = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(false);
return t;
}
});
this.reader = new FrameReader();
}
public static H264Decoder createH264DecoderFromCodecPrivate(ByteBuffer codecPrivate) {
H264Decoder d = new H264Decoder();
for (ByteBuffer bb : H264Utils.splitFrame(codecPrivate.duplicate())) {
NALUnit nu = NALUnit.read(bb);
if (nu.type == NALUnitType.SPS) {
d.reader.addSps(bb);
continue;
}
if (nu.type == NALUnitType.PPS)
d.reader.addPps(bb);
}
return d;
}
public Frame decodeFrame(ByteBuffer data, byte[][] buffer) {
return decodeFrameFromNals(H264Utils.splitFrame(data), buffer);
}
public Frame decodeFrameFromNals(List<ByteBuffer> nalUnits, byte[][] buffer) {
return new FrameDecoder(this).decodeFrame(nalUnits, buffer);
}
private static final class SliceDecoderRunnable implements Runnable {
private final SliceReader sliceReader;
private final Frame result;
private H264Decoder.FrameDecoder fdec;
private SliceDecoderRunnable(H264Decoder.FrameDecoder fdec, SliceReader sliceReader, Frame result) {
this.fdec = fdec;
this.sliceReader = sliceReader;
this.result = result;
}
public void run() {
SliceDecoder sliceDecoder = new SliceDecoder(this.fdec.activeSps, this.fdec.dec.sRefs, this.fdec.dec.lRefs, this.fdec.di, this.result);
sliceDecoder.decodeFromReader(this.sliceReader);
sliceDecoder = null;
}
}
static class FrameDecoder {
private SeqParameterSet activeSps;
private DeblockingFilter filter;
private SliceHeader firstSliceHeader;
private NALUnit firstNu;
private H264Decoder dec;
private DeblockerInput di;
public FrameDecoder(H264Decoder decoder) {
this.dec = decoder;
}
public Frame decodeFrame(List<ByteBuffer> nalUnits, byte[][] buffer) {
List<SliceReader> sliceReaders = this.dec.reader.readFrame(nalUnits);
if (sliceReaders == null || sliceReaders.size() == 0)
return null;
Frame result = init(sliceReaders.get(0), buffer);
if (this.dec.threaded && sliceReaders.size() > 1) {
List<Future<?>> futures = new ArrayList<>();
for (SliceReader sliceReader : sliceReaders)
futures.add(this.dec.tp.submit(new H264Decoder.SliceDecoderRunnable(this, sliceReader, result)));
for (Future<?> future : futures)
waitForSure(future);
} else {
for (SliceReader sliceReader : sliceReaders)
new SliceDecoder(this.activeSps, this.dec.sRefs, this.dec.lRefs, this.di, result).decodeFromReader(sliceReader);
}
this.filter.deblockFrame(result);
updateReferences(result);
return result;
}
private void waitForSure(Future<?> future) {
try {
future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void updateReferences(Frame picture) {
if (this.firstNu.nal_ref_idc != 0)
if (this.firstNu.type == NALUnitType.IDR_SLICE) {
performIDRMarking(this.firstSliceHeader.refPicMarkingIDR, picture);
} else {
performMarking(this.firstSliceHeader.refPicMarkingNonIDR, picture);
}
}
private Frame init(SliceReader sliceReader, byte[][] buffer) {
this.firstNu = sliceReader.getNALUnit();
this.firstSliceHeader = sliceReader.getSliceHeader();
this.activeSps = this.firstSliceHeader.sps;
validateSupportedFeatures(this.firstSliceHeader.sps, this.firstSliceHeader.pps);
int picWidthInMbs = this.activeSps.picWidthInMbsMinus1 + 1;
if (this.dec.sRefs == null) {
this.dec.sRefs = new Frame[1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4];
this.dec.lRefs = new IntObjectMap<>();
}
this.di = new DeblockerInput(this.activeSps);
Frame result = H264Decoder.createFrame(this.activeSps, buffer, this.firstSliceHeader.frameNum, this.firstSliceHeader.sliceType, this.di.mvs, this.di.refsUsed,
this.dec.poc.calcPOC(this.firstSliceHeader, this.firstNu));
this.filter = new DeblockingFilter(picWidthInMbs, this.activeSps.bitDepthChromaMinus8 + 8, this.di);
return result;
}
private void validateSupportedFeatures(SeqParameterSet sps, PictureParameterSet pps) {
if (sps.mbAdaptiveFrameFieldFlag)
throw new RuntimeException("Unsupported h264 feature: MBAFF.");
if (sps.bitDepthLumaMinus8 != 0 || sps.bitDepthChromaMinus8 != 0)
throw new RuntimeException("Unsupported h264 feature: High bit depth.");
if (sps.chromaFormatIdc != ColorSpace.YUV420J)
throw new RuntimeException("Unsupported h264 feature: " + String.valueOf(sps.chromaFormatIdc) + " color.");
if (!sps.frameMbsOnlyFlag || sps.fieldPicFlag)
throw new RuntimeException("Unsupported h264 feature: interlace.");
if (pps.constrainedIntraPredFlag)
throw new RuntimeException("Unsupported h264 feature: constrained intra prediction.");
if (sps.qpprimeYZeroTransformBypassFlag)
throw new RuntimeException("Unsupported h264 feature: qprime zero transform bypass.");
if (sps.profileIdc != 66 && sps.profileIdc != 77 && sps.profileIdc != 100)
throw new RuntimeException("Unsupported h264 feature: " + sps.profileIdc + " profile.");
}
public void performIDRMarking(RefPicMarkingIDR refPicMarkingIDR, Frame picture) {
clearAll();
this.dec.pictureBuffer.clear();
Frame saved = saveRef(picture);
if (refPicMarkingIDR.isUseForlongTerm()) {
this.dec.lRefs.put(0, saved);
saved.setShortTerm(false);
} else {
this.dec.sRefs[this.firstSliceHeader.frameNum] = saved;
}
}
private Frame saveRef(Frame decoded) {
Frame frame = (this.dec.pictureBuffer.size() > 0) ? (Frame)this.dec.pictureBuffer.remove(0) : Frame.createFrame(decoded);
frame.copyFromFrame(decoded);
return frame;
}
private void releaseRef(Frame picture) {
if (picture != null)
this.dec.pictureBuffer.add(picture);
}
public void clearAll() {
for (int i = 0; i < this.dec.sRefs.length; i++) {
releaseRef(this.dec.sRefs[i]);
this.dec.sRefs[i] = null;
}
int[] keys = this.dec.lRefs.keys();
for (int j = 0; j < keys.length; j++)
releaseRef(this.dec.lRefs.get(keys[j]));
this.dec.lRefs.clear();
}
public void performMarking(RefPicMarking refPicMarking, Frame picture) {
Frame saved = saveRef(picture);
if (refPicMarking != null) {
RefPicMarking.Instruction[] instructions = refPicMarking.getInstructions();
for (int i = 0; i < instructions.length; i++) {
RefPicMarking.Instruction instr = instructions[i];
switch (instr.getType()) {
case REMOVE_SHORT:
unrefShortTerm(instr.getArg1());
break;
case REMOVE_LONG:
unrefLongTerm(instr.getArg1());
break;
case CONVERT_INTO_LONG:
convert(instr.getArg1(), instr.getArg2());
break;
case TRUNK_LONG:
truncateLongTerm(instr.getArg1() - 1);
break;
case CLEAR:
clearAll();
break;
case MARK_LONG:
saveLong(saved, instr.getArg1());
saved = null;
break;
}
}
}
if (saved != null)
saveShort(saved);
int maxFrames = 1 << this.activeSps.log2MaxFrameNumMinus4 + 4;
if (refPicMarking == null) {
int maxShort = Math.max(1, this.activeSps.numRefFrames - this.dec.lRefs.size());
int min = Integer.MAX_VALUE, num = 0, minFn = 0;
for (int i = 0; i < this.dec.sRefs.length; i++) {
if (this.dec.sRefs[i] != null) {
int fnWrap = unwrap(this.firstSliceHeader.frameNum, this.dec.sRefs[i].getFrameNo(), maxFrames);
if (fnWrap < min) {
min = fnWrap;
minFn = this.dec.sRefs[i].getFrameNo();
}
num++;
}
}
if (num > maxShort) {
releaseRef(this.dec.sRefs[minFn]);
this.dec.sRefs[minFn] = null;
}
}
}
private int unwrap(int thisFrameNo, int refFrameNo, int maxFrames) {
return (refFrameNo > thisFrameNo) ? (refFrameNo - maxFrames) : refFrameNo;
}
private void saveShort(Frame saved) {
this.dec.sRefs[this.firstSliceHeader.frameNum] = saved;
}
private void saveLong(Frame saved, int longNo) {
Frame prev = this.dec.lRefs.get(longNo);
if (prev != null)
releaseRef(prev);
saved.setShortTerm(false);
this.dec.lRefs.put(longNo, saved);
}
private void truncateLongTerm(int maxLongNo) {
int[] keys = this.dec.lRefs.keys();
for (int i = 0; i < keys.length; i++) {
if (keys[i] > maxLongNo) {
releaseRef(this.dec.lRefs.get(keys[i]));
this.dec.lRefs.remove(keys[i]);
}
}
}
private void convert(int shortNo, int longNo) {
int ind = MathUtil.wrap(this.firstSliceHeader.frameNum - shortNo, 1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4);
releaseRef(this.dec.lRefs.get(longNo));
this.dec.lRefs.put(longNo, this.dec.sRefs[ind]);
this.dec.sRefs[ind] = null;
this.dec.lRefs.get(longNo).setShortTerm(false);
}
private void unrefLongTerm(int longNo) {
releaseRef(this.dec.lRefs.get(longNo));
this.dec.lRefs.remove(longNo);
}
private void unrefShortTerm(int shortNo) {
int ind = MathUtil.wrap(this.firstSliceHeader.frameNum - shortNo, 1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4);
releaseRef(this.dec.sRefs[ind]);
this.dec.sRefs[ind] = null;
}
}
public static Frame createFrame(SeqParameterSet sps, byte[][] buffer, int frameNum, SliceType frameType, H264Utils.MvList2D mvs, Frame[][][] refsUsed, int POC) {
int width = sps.picWidthInMbsMinus1 + 1 << 4;
int height = SeqParameterSet.getPicHeightInMbs(sps) << 4;
Rect crop = null;
if (sps.frameCroppingFlag) {
int sX = sps.frameCropLeftOffset << 1;
int sY = sps.frameCropTopOffset << 1;
int w = width - (sps.frameCropRightOffset << 1) - sX;
int h = height - (sps.frameCropBottomOffset << 1) - sY;
crop = new Rect(sX, sY, w, h);
}
return new Frame(width, height, buffer, ColorSpace.YUV420, crop, frameNum, frameType, mvs, refsUsed, POC);
}
public void addSps(List<ByteBuffer> spsList) {
this.reader.addSpsList(spsList);
}
public void addPps(List<ByteBuffer> ppsList) {
this.reader.addPpsList(ppsList);
}
@UsedViaReflection
public static int probe(ByteBuffer data) {
boolean validSps = false, validPps = false, validSh = false;
for (ByteBuffer nalUnit : H264Utils.splitFrame(data.duplicate())) {
NALUnit marker = NALUnit.read(nalUnit);
if (marker.type == NALUnitType.IDR_SLICE || marker.type == NALUnitType.NON_IDR_SLICE) {
BitReader reader = BitReader.createBitReader(nalUnit);
validSh = validSh(SliceHeaderReader.readPart1(reader));
break;
}
if (marker.type == NALUnitType.SPS) {
validSps = validSps(SeqParameterSet.read(nalUnit));
continue;
}
if (marker.type == NALUnitType.PPS)
validPps = validPps(PictureParameterSet.read(nalUnit));
}
return (validSh ? 60 : 0) + (validSps ? 20 : 0) + (validPps ? 20 : 0);
}
private static boolean validSh(SliceHeader sh) {
return (sh.firstMbInSlice == 0 && sh.sliceType != null && sh.picParameterSetId < 2);
}
private static boolean validSps(SeqParameterSet sps) {
return (sps.bitDepthChromaMinus8 < 4 && sps.bitDepthLumaMinus8 < 4 && sps.chromaFormatIdc != null && sps.seqParameterSetId < 2 && sps.picOrderCntType <= 2);
}
private static boolean validPps(PictureParameterSet pps) {
return (pps.picInitQpMinus26 <= 26 && pps.seqParameterSetId <= 2 && pps.picParameterSetId <= 2);
}
public VideoCodecMeta getCodecMeta(ByteBuffer data) {
List<ByteBuffer> rawSPS = H264Utils.getRawSPS(data.duplicate());
List<ByteBuffer> rawPPS = H264Utils.getRawPPS(data.duplicate());
if (rawSPS.size() == 0) {
Logger.warn("Can not extract metadata from the packet not containing an SPS.");
return null;
}
SeqParameterSet sps = SeqParameterSet.read(rawSPS.get(0));
Size size = H264Utils.getPicSize(sps);
return VideoCodecMeta.createSimpleVideoCodecMeta(size, ColorSpace.YUV420);
}
}

View file

@ -0,0 +1,392 @@
package org.jcodec.codecs.h264;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import org.jcodec.codecs.h264.decode.DeblockerInput;
import org.jcodec.codecs.h264.decode.FrameReader;
import org.jcodec.codecs.h264.decode.SliceDecoder;
import org.jcodec.codecs.h264.decode.SliceHeaderReader;
import org.jcodec.codecs.h264.decode.SliceReader;
import org.jcodec.codecs.h264.decode.deblock.DeblockingFilter;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.RefPicMarking;
import org.jcodec.codecs.h264.io.model.RefPicMarkingIDR;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.IntObjectMap;
import org.jcodec.common.UsedViaReflection;
import org.jcodec.common.VideoCodecMeta;
import org.jcodec.common.VideoDecoder;
import org.jcodec.common.io.BitReader;
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 H264Decoder2 extends VideoDecoder {
private Frame[] sRefs;
private IntObjectMap<Frame> lRefs;
private List<Frame> pictureBuffer = new ArrayList<>();
private POCManager poc = new POCManager();
private FrameReader reader;
private ExecutorService tp;
private boolean threaded = (Runtime.getRuntime().availableProcessors() > 1);
public H264Decoder2() {
if (this.threaded)
this.tp = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
}
});
this.reader = new FrameReader();
}
public static H264Decoder2 createH264DecoderFromCodecPrivate(ByteBuffer codecPrivate) {
H264Decoder2 d = new H264Decoder2();
for (ByteBuffer bb : H264Utils.splitFrame(codecPrivate.duplicate())) {
NALUnit nu = NALUnit.read(bb);
if (nu.type == NALUnitType.SPS) {
d.reader.addSps(bb);
continue;
}
if (nu.type == NALUnitType.PPS)
d.reader.addPps(bb);
}
return d;
}
public Frame decodeFrame(ByteBuffer data, byte[][] buffer) {
return decodeFrameFromNals(H264Utils.splitFrame(data), buffer);
}
public Frame decodeFrameFromNals(List<ByteBuffer> nalUnits, byte[][] buffer) {
return new FrameDecoder(this).decodeFrame(nalUnits, buffer);
}
private static final class SliceDecoderRunnable implements Runnable {
private final SliceReader sliceReader;
private final Frame result;
private H264Decoder2.FrameDecoder fdec;
private SliceDecoderRunnable(H264Decoder2.FrameDecoder fdec, SliceReader sliceReader, Frame result) {
this.fdec = fdec;
this.sliceReader = sliceReader;
this.result = result;
}
public void run() {
SliceDecoder sliceDecoder = new SliceDecoder(this.fdec.activeSps, this.fdec.dec.sRefs, this.fdec.dec.lRefs, this.fdec.di, this.result);
sliceDecoder.decodeFromReader(this.sliceReader);
sliceDecoder = null;
Thread.currentThread().interrupt();
}
}
static class FrameDecoder {
private SeqParameterSet activeSps;
private DeblockingFilter filter;
private SliceHeader firstSliceHeader;
private NALUnit firstNu;
private H264Decoder2 dec;
private DeblockerInput di;
public FrameDecoder(H264Decoder2 decoder) {
this.dec = decoder;
}
public Frame decodeFrame(List<ByteBuffer> nalUnits, byte[][] buffer) {
List<SliceReader> sliceReaders = this.dec.reader.readFrame(nalUnits);
if (sliceReaders == null || sliceReaders.size() == 0)
return null;
Frame result = init(sliceReaders.get(0), buffer);
if (this.dec.threaded && sliceReaders.size() > 1) {
List<Future<?>> futures = new ArrayList<>();
for (SliceReader sliceReader : sliceReaders)
futures.add(this.dec.tp.submit(new H264Decoder2.SliceDecoderRunnable(this, sliceReader, result)));
for (Future<?> future : futures)
waitForSure(future);
} else {
for (SliceReader sliceReader : sliceReaders)
new SliceDecoder(this.activeSps, this.dec.sRefs, this.dec.lRefs, this.di, result).decodeFromReader(sliceReader);
}
this.filter.deblockFrame(result);
updateReferences(result);
return result;
}
private void waitForSure(Future<?> future) {
try {
future.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void updateReferences(Frame picture) {
if (this.firstNu.nal_ref_idc != 0)
if (this.firstNu.type == NALUnitType.IDR_SLICE) {
performIDRMarking(this.firstSliceHeader.refPicMarkingIDR, picture);
} else {
performMarking(this.firstSliceHeader.refPicMarkingNonIDR, picture);
}
}
private Frame init(SliceReader sliceReader, byte[][] buffer) {
this.firstNu = sliceReader.getNALUnit();
this.firstSliceHeader = sliceReader.getSliceHeader();
this.activeSps = this.firstSliceHeader.sps;
validateSupportedFeatures(this.firstSliceHeader.sps, this.firstSliceHeader.pps);
int picWidthInMbs = this.activeSps.picWidthInMbsMinus1 + 1;
if (this.dec.sRefs == null) {
this.dec.sRefs = new Frame[1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4];
this.dec.lRefs = new IntObjectMap<>();
}
this.di = new DeblockerInput(this.activeSps);
Frame result = H264Decoder2.createFrame(this.activeSps, buffer, this.firstSliceHeader.frameNum, this.firstSliceHeader.sliceType, this.di.mvs, this.di.refsUsed,
this.dec.poc.calcPOC(this.firstSliceHeader, this.firstNu));
this.filter = new DeblockingFilter(picWidthInMbs, this.activeSps.bitDepthChromaMinus8 + 8, this.di);
return result;
}
private void validateSupportedFeatures(SeqParameterSet sps, PictureParameterSet pps) {
if (sps.mbAdaptiveFrameFieldFlag)
throw new RuntimeException("Unsupported h264 feature: MBAFF.");
if (sps.bitDepthLumaMinus8 != 0 || sps.bitDepthChromaMinus8 != 0)
throw new RuntimeException("Unsupported h264 feature: High bit depth.");
if (sps.chromaFormatIdc != ColorSpace.YUV420J)
throw new RuntimeException("Unsupported h264 feature: " + String.valueOf(sps.chromaFormatIdc) + " color.");
if (!sps.frameMbsOnlyFlag || sps.fieldPicFlag)
throw new RuntimeException("Unsupported h264 feature: interlace.");
if (pps.constrainedIntraPredFlag)
throw new RuntimeException("Unsupported h264 feature: constrained intra prediction.");
if (sps.qpprimeYZeroTransformBypassFlag)
throw new RuntimeException("Unsupported h264 feature: qprime zero transform bypass.");
if (sps.profileIdc != 66 && sps.profileIdc != 77 && sps.profileIdc != 100)
throw new RuntimeException("Unsupported h264 feature: " + sps.profileIdc + " profile.");
}
public void performIDRMarking(RefPicMarkingIDR refPicMarkingIDR, Frame picture) {
clearAll();
this.dec.pictureBuffer.clear();
Frame saved = saveRef(picture);
if (refPicMarkingIDR.isUseForlongTerm()) {
this.dec.lRefs.put(0, saved);
saved.setShortTerm(false);
} else {
this.dec.sRefs[this.firstSliceHeader.frameNum] = saved;
}
}
private Frame saveRef(Frame decoded) {
Frame frame = (this.dec.pictureBuffer.size() > 0) ? (Frame)this.dec.pictureBuffer.remove(0) : Frame.createFrame(decoded);
frame.copyFromFrame(decoded);
return frame;
}
private void releaseRef(Frame picture) {
if (picture != null)
this.dec.pictureBuffer.add(picture);
}
public void clearAll() {
for (int i = 0; i < this.dec.sRefs.length; i++) {
releaseRef(this.dec.sRefs[i]);
this.dec.sRefs[i] = null;
}
int[] keys = this.dec.lRefs.keys();
for (int j = 0; j < keys.length; j++)
releaseRef(this.dec.lRefs.get(keys[j]));
this.dec.lRefs.clear();
}
public void performMarking(RefPicMarking refPicMarking, Frame picture) {
Frame saved = saveRef(picture);
if (refPicMarking != null) {
RefPicMarking.Instruction[] instructions = refPicMarking.getInstructions();
for (int i = 0; i < instructions.length; i++) {
RefPicMarking.Instruction instr = instructions[i];
switch (instr.getType()) {
case REMOVE_SHORT:
unrefShortTerm(instr.getArg1());
break;
case REMOVE_LONG:
unrefLongTerm(instr.getArg1());
break;
case CONVERT_INTO_LONG:
convert(instr.getArg1(), instr.getArg2());
break;
case TRUNK_LONG:
truncateLongTerm(instr.getArg1() - 1);
break;
case CLEAR:
clearAll();
break;
case MARK_LONG:
saveLong(saved, instr.getArg1());
saved = null;
break;
}
}
}
if (saved != null)
saveShort(saved);
int maxFrames = 1 << this.activeSps.log2MaxFrameNumMinus4 + 4;
if (refPicMarking == null) {
int maxShort = Math.max(1, this.activeSps.numRefFrames - this.dec.lRefs.size());
int min = Integer.MAX_VALUE, num = 0, minFn = 0;
for (int i = 0; i < this.dec.sRefs.length; i++) {
if (this.dec.sRefs[i] != null) {
int fnWrap = unwrap(this.firstSliceHeader.frameNum, this.dec.sRefs[i].getFrameNo(), maxFrames);
if (fnWrap < min) {
min = fnWrap;
minFn = this.dec.sRefs[i].getFrameNo();
}
num++;
}
}
if (num > maxShort) {
releaseRef(this.dec.sRefs[minFn]);
this.dec.sRefs[minFn] = null;
}
}
}
private int unwrap(int thisFrameNo, int refFrameNo, int maxFrames) {
return (refFrameNo > thisFrameNo) ? (refFrameNo - maxFrames) : refFrameNo;
}
private void saveShort(Frame saved) {
this.dec.sRefs[this.firstSliceHeader.frameNum] = saved;
}
private void saveLong(Frame saved, int longNo) {
Frame prev = this.dec.lRefs.get(longNo);
if (prev != null)
releaseRef(prev);
saved.setShortTerm(false);
this.dec.lRefs.put(longNo, saved);
}
private void truncateLongTerm(int maxLongNo) {
int[] keys = this.dec.lRefs.keys();
for (int i = 0; i < keys.length; i++) {
if (keys[i] > maxLongNo) {
releaseRef(this.dec.lRefs.get(keys[i]));
this.dec.lRefs.remove(keys[i]);
}
}
}
private void convert(int shortNo, int longNo) {
int ind = MathUtil.wrap(this.firstSliceHeader.frameNum - shortNo, 1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4);
releaseRef(this.dec.lRefs.get(longNo));
this.dec.lRefs.put(longNo, this.dec.sRefs[ind]);
this.dec.sRefs[ind] = null;
this.dec.lRefs.get(longNo).setShortTerm(false);
}
private void unrefLongTerm(int longNo) {
releaseRef(this.dec.lRefs.get(longNo));
this.dec.lRefs.remove(longNo);
}
private void unrefShortTerm(int shortNo) {
int ind = MathUtil.wrap(this.firstSliceHeader.frameNum - shortNo, 1 << this.firstSliceHeader.sps.log2MaxFrameNumMinus4 + 4);
releaseRef(this.dec.sRefs[ind]);
this.dec.sRefs[ind] = null;
}
}
public static Frame createFrame(SeqParameterSet sps, byte[][] buffer, int frameNum, SliceType frameType, H264Utils.MvList2D mvs, Frame[][][] refsUsed, int POC) {
int width = sps.picWidthInMbsMinus1 + 1 << 4;
int height = SeqParameterSet.getPicHeightInMbs(sps) << 4;
Rect crop = null;
if (sps.frameCroppingFlag) {
int sX = sps.frameCropLeftOffset << 1;
int sY = sps.frameCropTopOffset << 1;
int w = width - (sps.frameCropRightOffset << 1) - sX;
int h = height - (sps.frameCropBottomOffset << 1) - sY;
crop = new Rect(sX, sY, w, h);
}
return new Frame(width, height, buffer, ColorSpace.YUV420, crop, frameNum, frameType, mvs, refsUsed, POC);
}
public void addSps(List<ByteBuffer> spsList) {
this.reader.addSpsList(spsList);
}
public void addPps(List<ByteBuffer> ppsList) {
this.reader.addPpsList(ppsList);
}
@UsedViaReflection
public static int probe(ByteBuffer data) {
boolean validSps = false, validPps = false, validSh = false;
for (ByteBuffer nalUnit : H264Utils.splitFrame(data.duplicate())) {
NALUnit marker = NALUnit.read(nalUnit);
if (marker.type == NALUnitType.IDR_SLICE || marker.type == NALUnitType.NON_IDR_SLICE) {
BitReader reader = BitReader.createBitReader(nalUnit);
validSh = validSh(SliceHeaderReader.readPart1(reader));
break;
}
if (marker.type == NALUnitType.SPS) {
validSps = validSps(SeqParameterSet.read(nalUnit));
continue;
}
if (marker.type == NALUnitType.PPS)
validPps = validPps(PictureParameterSet.read(nalUnit));
}
return (validSh ? 60 : 0) + (validSps ? 20 : 0) + (validPps ? 20 : 0);
}
private static boolean validSh(SliceHeader sh) {
return (sh.firstMbInSlice == 0 && sh.sliceType != null && sh.picParameterSetId < 2);
}
private static boolean validSps(SeqParameterSet sps) {
return (sps.bitDepthChromaMinus8 < 4 && sps.bitDepthLumaMinus8 < 4 && sps.chromaFormatIdc != null && sps.seqParameterSetId < 2 && sps.picOrderCntType <= 2);
}
private static boolean validPps(PictureParameterSet pps) {
return (pps.picInitQpMinus26 <= 26 && pps.seqParameterSetId <= 2 && pps.picParameterSetId <= 2);
}
public VideoCodecMeta getCodecMeta(ByteBuffer data) {
List<ByteBuffer> rawSPS = H264Utils.getRawSPS(data.duplicate());
List<ByteBuffer> rawPPS = H264Utils.getRawPPS(data.duplicate());
if (rawSPS.size() == 0) {
Logger.warn("Can not extract metadata from the packet not containing an SPS.");
return null;
}
SeqParameterSet sps = SeqParameterSet.read(rawSPS.get(0));
Size size = H264Utils.getPicSize(sps);
return VideoCodecMeta.createSimpleVideoCodecMeta(size, ColorSpace.YUV420);
}
}

View file

@ -0,0 +1,322 @@
package org.jcodec.codecs.h264;
import java.nio.ByteBuffer;
import org.jcodec.codecs.h264.encode.DumbRateControl;
import org.jcodec.codecs.h264.encode.EncodedMB;
import org.jcodec.codecs.h264.encode.MBEncoderHelper;
import org.jcodec.codecs.h264.encode.MBEncoderI16x16;
import org.jcodec.codecs.h264.encode.MBEncoderP16x16;
import org.jcodec.codecs.h264.encode.MotionEstimator;
import org.jcodec.codecs.h264.encode.RateControl;
import org.jcodec.codecs.h264.io.CAVLC;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.RefPicMarkingIDR;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.codecs.h264.io.write.CAVLCWriter;
import org.jcodec.codecs.h264.io.write.SliceHeaderWriter;
import org.jcodec.common.VideoEncoder;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.model.Size;
import org.jcodec.common.tools.MathUtil;
public class H264Encoder extends VideoEncoder {
private static final int KEY_INTERVAL_DEFAULT = 25;
private static final int MOTION_SEARCH_RANGE_DEFAULT = 16;
private CAVLC[] cavlc;
private byte[][] leftRow;
private byte[][] topLine;
private RateControl rc;
private int frameNumber;
private int keyInterval;
private int motionSearchRange;
private int maxPOC;
private int maxFrameNumber;
private SeqParameterSet sps;
private PictureParameterSet pps;
private MBEncoderI16x16 mbEncoderI16x16;
private MBEncoderP16x16 mbEncoderP16x16;
private Picture ref;
private Picture picOut;
private EncodedMB[] topEncoded;
private EncodedMB outMB;
public static H264Encoder createH264Encoder() {
return new H264Encoder(new DumbRateControl());
}
public H264Encoder(RateControl rc) {
this.rc = rc;
this.keyInterval = 25;
this.motionSearchRange = 16;
}
public int getKeyInterval() {
return this.keyInterval;
}
public void setKeyInterval(int keyInterval) {
this.keyInterval = keyInterval;
}
public int getMotionSearchRange() {
return this.motionSearchRange;
}
public void setMotionSearchRange(int motionSearchRange) {
this.motionSearchRange = motionSearchRange;
}
public VideoEncoder.EncodedFrame encodeFrame(Picture pic, ByteBuffer _out) {
if (pic.getColor() != ColorSpace.YUV420J)
throw new IllegalArgumentException("Input picture color is not supported: " + String.valueOf(pic.getColor()));
if (this.frameNumber >= this.keyInterval)
this.frameNumber = 0;
SliceType sliceType = (this.frameNumber == 0) ? SliceType.I : SliceType.P;
boolean idr = (this.frameNumber == 0);
ByteBuffer data = doEncodeFrame(pic, _out, idr, this.frameNumber++, sliceType);
return new VideoEncoder.EncodedFrame(data, idr);
}
public ByteBuffer encodeIDRFrame(Picture pic, ByteBuffer _out) {
this.frameNumber = 0;
return doEncodeFrame(pic, _out, true, this.frameNumber, SliceType.I);
}
public ByteBuffer encodePFrame(Picture pic, ByteBuffer _out) {
this.frameNumber++;
return doEncodeFrame(pic, _out, true, this.frameNumber, SliceType.P);
}
public ByteBuffer doEncodeFrame(Picture pic, ByteBuffer _out, boolean idr, int frameNumber, SliceType frameType) {
ByteBuffer dup = _out.duplicate();
int maxSize = Math.min(dup.remaining(), pic.getWidth() * pic.getHeight());
maxSize -= maxSize >>> 6;
int qp = this.rc.startPicture(pic.getSize(), maxSize, frameType);
if (idr) {
this.sps = initSPS(new Size(pic.getCroppedWidth(), pic.getCroppedHeight()));
this.pps = initPPS();
this.maxPOC = 1 << this.sps.log2MaxPicOrderCntLsbMinus4 + 4;
this.maxFrameNumber = 1 << this.sps.log2MaxFrameNumMinus4 + 4;
}
if (idr) {
dup.putInt(1);
new NALUnit(NALUnitType.SPS, 3).write(dup);
writeSPS(dup, this.sps);
dup.putInt(1);
new NALUnit(NALUnitType.PPS, 3).write(dup);
writePPS(dup, this.pps);
}
int mbWidth = this.sps.picWidthInMbsMinus1 + 1;
int mbHeight = this.sps.picHeightInMapUnitsMinus1 + 1;
this.leftRow = new byte[][] { new byte[16], new byte[8], new byte[8] };
this.topLine = new byte[][] { new byte[mbWidth << 4], new byte[mbWidth << 3], new byte[mbWidth << 3] };
this.picOut = Picture.create(mbWidth << 4, mbHeight << 4, ColorSpace.YUV420J);
this.outMB = new EncodedMB();
this.topEncoded = new EncodedMB[mbWidth];
for (int i = 0; i < mbWidth; i++)
this.topEncoded[i] = new EncodedMB();
encodeSlice(this.sps, this.pps, pic, dup, idr, frameNumber, frameType, qp);
putLastMBLine();
this.ref = this.picOut;
dup.flip();
return dup;
}
private void writePPS(ByteBuffer dup, PictureParameterSet pps) {
ByteBuffer tmp = ByteBuffer.allocate(1024);
pps.write(tmp);
tmp.flip();
H264Utils.escapeNAL(tmp, dup);
}
private void writeSPS(ByteBuffer dup, SeqParameterSet sps) {
ByteBuffer tmp = ByteBuffer.allocate(1024);
sps.write(tmp);
tmp.flip();
H264Utils.escapeNAL(tmp, dup);
}
public PictureParameterSet initPPS() {
PictureParameterSet pps = new PictureParameterSet();
pps.picInitQpMinus26 = 0;
return pps;
}
public SeqParameterSet initSPS(Size sz) {
SeqParameterSet sps = new SeqParameterSet();
sps.picWidthInMbsMinus1 = (sz.getWidth() + 15 >> 4) - 1;
sps.picHeightInMapUnitsMinus1 = (sz.getHeight() + 15 >> 4) - 1;
sps.chromaFormatIdc = ColorSpace.YUV420J;
sps.profileIdc = 66;
sps.levelIdc = 40;
sps.numRefFrames = 1;
sps.frameMbsOnlyFlag = true;
sps.log2MaxFrameNumMinus4 = Math.max(0, MathUtil.log2(this.keyInterval) - 3);
int codedWidth = sps.picWidthInMbsMinus1 + 1 << 4;
int codedHeight = sps.picHeightInMapUnitsMinus1 + 1 << 4;
sps.frameCroppingFlag = (codedWidth != sz.getWidth() || codedHeight != sz.getHeight());
sps.frameCropRightOffset = codedWidth - sz.getWidth() + 1 >> 1;
sps.frameCropBottomOffset = codedHeight - sz.getHeight() + 1 >> 1;
return sps;
}
private void encodeSlice(SeqParameterSet sps, PictureParameterSet pps, Picture pic, ByteBuffer dup, boolean idr, int frameNum, SliceType sliceType, int qp) {
if (idr && sliceType != SliceType.I) {
idr = false;
Logger.warn("Illegal value of idr = true when sliceType != I");
}
this.cavlc = new CAVLC[] { new CAVLC(sps, pps, 2, 2), new CAVLC(sps, pps, 1, 1), new CAVLC(sps, pps, 1, 1) };
this.mbEncoderI16x16 = new MBEncoderI16x16(this.cavlc, this.leftRow, this.topLine);
this.mbEncoderP16x16 = new MBEncoderP16x16(sps, this.ref, this.cavlc, new MotionEstimator(this.motionSearchRange));
dup.putInt(1);
new NALUnit(idr ? NALUnitType.IDR_SLICE : NALUnitType.NON_IDR_SLICE, 3).write(dup);
SliceHeader sh = new SliceHeader();
sh.sliceType = sliceType;
if (idr)
sh.refPicMarkingIDR = new RefPicMarkingIDR(false, false);
sh.pps = pps;
sh.sps = sps;
sh.picOrderCntLsb = (frameNum << 1) % this.maxPOC;
sh.frameNum = frameNum % this.maxFrameNumber;
sh.sliceQpDelta = qp - (pps.picInitQpMinus26 + 26);
ByteBuffer buf = ByteBuffer.allocate(pic.getWidth() * pic.getHeight());
BitWriter sliceData = new BitWriter(buf);
SliceHeaderWriter.write(sh, idr, 2, sliceData);
for (int mbY = 0, mbAddr = 0; mbY < sps.picHeightInMapUnitsMinus1 + 1; mbY++) {
for (int mbX = 0; mbX < sps.picWidthInMbsMinus1 + 1; mbX++, mbAddr++) {
if (sliceType == SliceType.P)
CAVLCWriter.writeUE(sliceData, 0);
MBType mbType = selectMBType(sliceType);
if (mbType == MBType.I_16x16) {
int predMode = this.mbEncoderI16x16.getPredMode(pic, mbX, mbY);
int cbpChroma = this.mbEncoderI16x16.getCbpChroma(pic, mbX, mbY);
int cbpLuma = this.mbEncoderI16x16.getCbpLuma(pic, mbX, mbY);
int i16x16TypeOffset = cbpLuma / 15 * 12 + cbpChroma * 4 + predMode;
int mbTypeOffset = (sliceType == SliceType.P) ? 5 : 0;
CAVLCWriter.writeUE(sliceData, mbTypeOffset + mbType.code() + i16x16TypeOffset);
} else {
CAVLCWriter.writeUE(sliceData, mbType.code());
}
int totalQpDelta = 0;
int qpDelta = this.rc.initialQpDelta();
while (true) {
BitWriter candidate = sliceData.fork();
totalQpDelta += qpDelta;
encodeMacroblock(mbType, pic, mbX, mbY, candidate, qp, totalQpDelta);
qpDelta = this.rc.accept(candidate.position() - sliceData.position());
if (qpDelta != 0)
restoreMacroblock(mbType);
if (qpDelta == 0) {
sliceData = candidate;
qp += totalQpDelta;
collectPredictors(this.outMB.getPixels(), mbX);
addToReference(mbX, mbY);
break;
}
}
}
}
sliceData.write1Bit(1);
sliceData.flush();
buf = sliceData.getBuffer();
buf.flip();
H264Utils.escapeNAL(buf, dup);
}
private void encodeMacroblock(MBType mbType, Picture pic, int mbX, int mbY, BitWriter candidate, int qp, int qpDelta) {
if (mbType == MBType.I_16x16) {
this.mbEncoderI16x16.save();
this.mbEncoderI16x16.encodeMacroblock(pic, mbX, mbY, candidate, this.outMB, (mbX > 0) ? this.topEncoded[mbX - 1] : null,
(mbY > 0) ? this.topEncoded[mbX] : null, qp + qpDelta, qpDelta);
} else if (mbType == MBType.P_16x16) {
this.mbEncoderP16x16.save();
this.mbEncoderP16x16.encodeMacroblock(pic, mbX, mbY, candidate, this.outMB, (mbX > 0) ? this.topEncoded[mbX - 1] : null,
(mbY > 0) ? this.topEncoded[mbX] : null, qp + qpDelta, qpDelta);
} else {
throw new RuntimeException("Macroblock of type " + String.valueOf(mbType) + " is not supported.");
}
}
private void restoreMacroblock(MBType mbType) {
if (mbType == MBType.I_16x16) {
this.mbEncoderI16x16.restore();
} else if (mbType == MBType.P_16x16) {
this.mbEncoderP16x16.restore();
} else {
throw new RuntimeException("Macroblock of type " + String.valueOf(mbType) + " is not supported.");
}
}
private MBType selectMBType(SliceType sliceType) {
if (sliceType == SliceType.I)
return MBType.I_16x16;
if (sliceType == SliceType.P)
return MBType.P_16x16;
throw new RuntimeException("Unsupported slice type");
}
private void addToReference(int mbX, int mbY) {
if (mbY > 0)
MBEncoderHelper.putBlkPic(this.picOut, this.topEncoded[mbX].getPixels(), mbX << 4, mbY - 1 << 4);
EncodedMB tmp = this.topEncoded[mbX];
this.topEncoded[mbX] = this.outMB;
this.outMB = tmp;
}
private void putLastMBLine() {
int mbWidth = this.sps.picWidthInMbsMinus1 + 1;
int mbHeight = this.sps.picHeightInMapUnitsMinus1 + 1;
for (int mbX = 0; mbX < mbWidth; mbX++)
MBEncoderHelper.putBlkPic(this.picOut, this.topEncoded[mbX].getPixels(), mbX << 4, mbHeight - 1 << 4);
}
private void collectPredictors(Picture outMB, int mbX) {
System.arraycopy(outMB.getPlaneData(0), 240, this.topLine[0], mbX << 4, 16);
System.arraycopy(outMB.getPlaneData(1), 56, this.topLine[1], mbX << 3, 8);
System.arraycopy(outMB.getPlaneData(2), 56, this.topLine[2], mbX << 3, 8);
copyCol(outMB.getPlaneData(0), 15, 16, this.leftRow[0]);
copyCol(outMB.getPlaneData(1), 7, 8, this.leftRow[1]);
copyCol(outMB.getPlaneData(2), 7, 8, this.leftRow[2]);
}
private void copyCol(byte[] planeData, int off, int stride, byte[] out) {
for (int i = 0; i < out.length; i++) {
out[i] = planeData[off];
off += stride;
}
}
public ColorSpace[] getSupportedColorSpaces() {
return new ColorSpace[] { ColorSpace.YUV420J };
}
public int estimateBufferSize(Picture frame) {
return Math.max(65536, frame.getWidth() * frame.getHeight() / 2);
}
}

View file

@ -0,0 +1,893 @@
package org.jcodec.codecs.h264;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.jcodec.codecs.h264.decode.SliceHeaderReader;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.codecs.h264.io.write.SliceHeaderWriter;
import org.jcodec.codecs.h264.mp4.AvcCBox;
import org.jcodec.common.IntArrayList;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.io.FileChannelWrapper;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.Size;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.NodeBox;
import org.jcodec.containers.mp4.boxes.SampleEntry;
import org.jcodec.containers.mp4.boxes.VideoSampleEntry;
public class H264Utils {
public static ByteBuffer nextNALUnit(ByteBuffer buf) {
skipToNALUnit(buf);
if (buf.hasArray())
return gotoNALUnitWithArray(buf);
return gotoNALUnit(buf);
}
public static final void skipToNALUnit(ByteBuffer buf) {
if (!buf.hasRemaining())
return;
int val = -1;
while (buf.hasRemaining()) {
val <<= 8;
val |= buf.get() & 0xFF;
if ((val & 0xFFFFFF) == 1) {
buf.position(buf.position());
break;
}
}
}
public static final ByteBuffer gotoNALUnit(ByteBuffer buf) {
if (!buf.hasRemaining())
return null;
int from = buf.position();
ByteBuffer result = buf.slice();
result.order(ByteOrder.BIG_ENDIAN);
int val = -1;
while (buf.hasRemaining()) {
val <<= 8;
val |= buf.get() & 0xFF;
if ((val & 0xFFFFFF) == 1) {
buf.position(buf.position() - ((val == 1) ? 4 : 3));
result.limit(buf.position() - from);
break;
}
}
return result;
}
public static final ByteBuffer gotoNALUnitWithArray(ByteBuffer buf) {
if (!buf.hasRemaining())
return null;
int from = buf.position();
ByteBuffer result = buf.slice();
result.order(ByteOrder.BIG_ENDIAN);
byte[] arr = buf.array();
int pos = from + buf.arrayOffset();
int posFrom = pos;
int lim = buf.limit() + buf.arrayOffset();
while (pos < lim) {
byte b = arr[pos];
if ((b & 0xFE) == 0) {
while (b == 0) {
if (++pos < lim) {
b = arr[pos];
continue;
}
break;
}
if (b == 1 &&
pos - posFrom >= 2 && arr[pos - 1] == 0 && arr[pos - 2] == 0) {
int lenSize = (pos - posFrom >= 3 && arr[pos - 3] == 0) ? 4 : 3;
buf.position(pos + 1 - buf.arrayOffset() - lenSize);
result.limit(buf.position() - from);
return result;
}
}
pos += 3;
}
buf.position(buf.limit());
return result;
}
public static final void unescapeNAL(ByteBuffer _buf) {
if (_buf.remaining() < 2)
return;
ByteBuffer _in = _buf.duplicate();
ByteBuffer out = _buf.duplicate();
byte p1 = _in.get();
out.put(p1);
byte p2 = _in.get();
out.put(p2);
while (_in.hasRemaining()) {
byte b = _in.get();
if (p1 != 0 || p2 != 0 || b != 3)
out.put(b);
p1 = p2;
p2 = b;
}
_buf.limit(out.position());
}
public static final void escapeNALinplace(ByteBuffer src) {
int[] loc = searchEscapeLocations(src);
int old = src.limit();
src.limit(src.limit() + loc.length);
int newPos = src.limit() - 1, oldPos = old - 1, locIdx = loc.length - 1;
for (; newPos >=
src.position(); newPos--, oldPos--) {
src.put(newPos, src.get(oldPos));
if (locIdx >= 0 && loc[locIdx] == oldPos) {
newPos--;
src.put(newPos, (byte)3);
locIdx--;
}
}
}
private static int[] searchEscapeLocations(ByteBuffer src) {
IntArrayList points = IntArrayList.createIntArrayList();
ByteBuffer search = src.duplicate();
int p = search.getShort();
while (search.hasRemaining()) {
byte b = search.get();
if (p == 0 && (b & 0xFFFFFFFC) == 0) {
points.add(search.position() - 1);
p = 3;
}
p = p << 8 & 0xFFFF;
p |= b & 0xFF;
}
int[] array = points.toArray();
return array;
}
public static final void escapeNAL(ByteBuffer src, ByteBuffer dst) {
byte p1 = src.get(), p2 = src.get();
dst.put(p1);
dst.put(p2);
while (src.hasRemaining()) {
byte b = src.get();
if (p1 == 0 && p2 == 0 && (b & 0xFF) <= 3) {
dst.put((byte)3);
p1 = p2;
p2 = 3;
}
dst.put(b);
p1 = p2;
p2 = b;
}
}
public static List<ByteBuffer> splitMOVPacket(ByteBuffer buf, AvcCBox avcC) {
List<ByteBuffer> result = new ArrayList<>();
int nls = avcC.getNalLengthSize();
ByteBuffer dup = buf.duplicate();
while (dup.remaining() >= nls) {
int len = readLen(dup, nls);
if (len == 0)
break;
result.add(NIOUtils.read(dup, len));
}
return result;
}
private static int readLen(ByteBuffer dup, int nls) {
switch (nls) {
case 1:
return dup.get() & 0xFF;
case 2:
return dup.getShort() & 0xFFFF;
case 3:
return (dup.getShort() & 0xFFFF) << 8 | dup.get() & 0xFF;
case 4:
return dup.getInt();
}
throw new IllegalArgumentException("NAL Unit length size can not be " + nls);
}
public static void encodeMOVPacketInplace(ByteBuffer avcFrame) {
ByteBuffer dup = avcFrame.duplicate();
ByteBuffer d1 = avcFrame.duplicate();
int tot = d1.position();
while (true) {
ByteBuffer buf = nextNALUnit(dup);
if (buf == null)
break;
d1.position(tot);
d1.putInt(buf.remaining());
tot += buf.remaining() + 4;
}
}
public static ByteBuffer encodeMOVPacket(ByteBuffer avcFrame) {
ByteBuffer dup = avcFrame.duplicate();
List<ByteBuffer> list = new ArrayList<>();
int totalLen = 0;
ByteBuffer buf;
while ((buf = nextNALUnit(dup)) != null) {
list.add(buf);
totalLen += buf.remaining();
}
ByteBuffer result = ByteBuffer.allocate(list.size() * 4 + totalLen);
for (ByteBuffer byteBuffer : list) {
result.putInt(byteBuffer.remaining());
result.put(byteBuffer);
}
result.flip();
return result;
}
public static ByteBuffer decodeMOVPacket(ByteBuffer result, AvcCBox avcC) {
if (avcC.getNalLengthSize() == 4) {
decodeMOVPacketInplace(result, avcC);
return result;
}
return joinNALUnits(splitMOVPacket(result, avcC));
}
public static void decodeMOVPacketInplace(ByteBuffer result, AvcCBox avcC) {
if (avcC.getNalLengthSize() != 4)
throw new IllegalArgumentException("Can only inplace decode AVC MOV packet with nal_length_size = 4.");
ByteBuffer dup = result.duplicate();
while (dup.remaining() >= 4) {
int size = dup.getInt();
dup.position(dup.position() - 4);
dup.putInt(1);
dup.position(dup.position() + size);
}
}
public static void wipePS(ByteBuffer _in, ByteBuffer out, List<ByteBuffer> spsList, List<ByteBuffer> ppsList) {
ByteBuffer dup = _in.duplicate();
while (dup.hasRemaining()) {
ByteBuffer buf = nextNALUnit(dup);
if (buf == null)
break;
NALUnit nu = NALUnit.read(buf.duplicate());
if (nu.type == NALUnitType.PPS) {
if (ppsList != null)
ppsList.add(NIOUtils.duplicate(buf));
continue;
}
if (nu.type == NALUnitType.SPS) {
if (spsList != null)
spsList.add(NIOUtils.duplicate(buf));
continue;
}
if (out != null) {
out.putInt(1);
out.put(buf);
}
}
if (out != null)
out.flip();
}
public static void wipePSinplace(ByteBuffer _in, Collection<ByteBuffer> spsList, Collection<ByteBuffer> ppsList) {
ByteBuffer dup = _in.duplicate();
while (dup.hasRemaining()) {
ByteBuffer buf = nextNALUnit(dup);
if (buf == null)
break;
NALUnit nu = NALUnit.read(buf);
if (nu.type == NALUnitType.PPS) {
if (ppsList != null)
ppsList.add(NIOUtils.duplicate(buf));
_in.position(dup.position());
continue;
}
if (nu.type == NALUnitType.SPS) {
if (spsList != null)
spsList.add(NIOUtils.duplicate(buf));
_in.position(dup.position());
continue;
}
if (nu.type == NALUnitType.IDR_SLICE || nu.type == NALUnitType.NON_IDR_SLICE)
break;
}
}
public static AvcCBox createAvcC(SeqParameterSet sps, PictureParameterSet pps, int nalLengthSize) {
ByteBuffer serialSps = ByteBuffer.allocate(512);
sps.write(serialSps);
serialSps.flip();
escapeNALinplace(serialSps);
ByteBuffer serialPps = ByteBuffer.allocate(512);
pps.write(serialPps);
serialPps.flip();
escapeNALinplace(serialPps);
AvcCBox avcC = AvcCBox.createAvcCBox(sps.profileIdc, 0, sps.levelIdc, nalLengthSize, Arrays.asList(serialSps),
Arrays.asList(serialPps));
return avcC;
}
public static AvcCBox createAvcCFromList(List<SeqParameterSet> initSPS, List<PictureParameterSet> initPPS, int nalLengthSize) {
List<ByteBuffer> serialSps = saveSPS(initSPS);
List<ByteBuffer> serialPps = savePPS(initPPS);
SeqParameterSet sps = initSPS.get(0);
return AvcCBox.createAvcCBox(sps.profileIdc, 0, sps.levelIdc, nalLengthSize, serialSps, serialPps);
}
public static List<ByteBuffer> savePPS(List<PictureParameterSet> initPPS) {
List<ByteBuffer> serialPps = new ArrayList<>();
for (PictureParameterSet pps : initPPS) {
ByteBuffer bb1 = ByteBuffer.allocate(512);
pps.write(bb1);
bb1.flip();
escapeNALinplace(bb1);
serialPps.add(bb1);
}
return serialPps;
}
public static List<ByteBuffer> saveSPS(List<SeqParameterSet> initSPS) {
List<ByteBuffer> serialSps = new ArrayList<>();
for (SeqParameterSet sps : initSPS) {
ByteBuffer bb1 = ByteBuffer.allocate(512);
sps.write(bb1);
bb1.flip();
escapeNALinplace(bb1);
serialSps.add(bb1);
}
return serialSps;
}
public static SampleEntry createMOVSampleEntryFromBytes(ByteBuffer codecPrivate) {
List<ByteBuffer> rawSPS = getRawSPS(codecPrivate.duplicate());
List<ByteBuffer> rawPPS = getRawPPS(codecPrivate.duplicate());
return createMOVSampleEntryFromSpsPpsList(rawSPS, rawPPS, 4);
}
public static SampleEntry createMOVSampleEntryFromSpsPpsList(List<ByteBuffer> spsList, List<ByteBuffer> ppsList, int nalLengthSize) {
AvcCBox avcC = createAvcCFromPS(spsList, ppsList, nalLengthSize);
return createMOVSampleEntryFromAvcC(avcC);
}
public static AvcCBox createAvcCFromBytes(ByteBuffer codecPrivate) {
List<ByteBuffer> rawSPS = getRawSPS(codecPrivate.duplicate());
List<ByteBuffer> rawPPS = getRawPPS(codecPrivate.duplicate());
return createAvcCFromPS(rawSPS, rawPPS, 4);
}
public static AvcCBox createAvcCFromPS(List<ByteBuffer> spsList, List<ByteBuffer> ppsList, int nalLengthSize) {
SeqParameterSet sps = readSPS(NIOUtils.duplicate(spsList.get(0)));
return AvcCBox.createAvcCBox(sps.profileIdc, 0, sps.levelIdc, nalLengthSize, spsList, ppsList);
}
public static SampleEntry createMOVSampleEntryFromAvcC(AvcCBox avcC) {
SeqParameterSet sps = SeqParameterSet.read(avcC.getSpsList().get(0).duplicate());
int codedWidth = sps.picWidthInMbsMinus1 + 1 << 4;
int codedHeight = SeqParameterSet.getPicHeightInMbs(sps) << 4;
SampleEntry se = VideoSampleEntry.videoSampleEntry("avc1", getPicSize(sps), "JCodec");
se.add(avcC);
return se;
}
public static SampleEntry createMOVSampleEntryFromSpsPps(SeqParameterSet initSPS, PictureParameterSet initPPS, int nalLengthSize) {
ByteBuffer bb1 = ByteBuffer.allocate(512), bb2 = ByteBuffer.allocate(512);
initSPS.write(bb1);
initPPS.write(bb2);
bb1.flip();
bb2.flip();
return createMOVSampleEntryFromBuffer(bb1, bb2, nalLengthSize);
}
public static SampleEntry createMOVSampleEntryFromBuffer(ByteBuffer sps, ByteBuffer pps, int nalLengthSize) {
return createMOVSampleEntryFromSpsPpsList(Arrays.asList(sps),
Arrays.asList(pps), nalLengthSize);
}
public static boolean iFrame(ByteBuffer _data) {
ByteBuffer data = _data.duplicate();
ByteBuffer segment;
while ((segment = nextNALUnit(data)) != null) {
NALUnitType type = (NALUnit.read(segment)).type;
if (type == NALUnitType.IDR_SLICE || type == NALUnitType.NON_IDR_SLICE) {
unescapeNAL(segment);
BitReader reader = BitReader.createBitReader(segment);
SliceHeader part1 = SliceHeaderReader.readPart1(reader);
return (part1.sliceType == SliceType.I);
}
}
return false;
}
public static boolean isByteBufferIDRSlice(ByteBuffer _data) {
ByteBuffer data = _data.duplicate();
ByteBuffer segment;
while ((segment = nextNALUnit(data)) != null) {
if ((NALUnit.read(segment)).type == NALUnitType.IDR_SLICE)
return true;
}
return false;
}
public static boolean idrSlice(List<ByteBuffer> _data) {
for (ByteBuffer segment : _data) {
if ((NALUnit.read(segment.duplicate())).type == NALUnitType.IDR_SLICE)
return true;
}
return false;
}
public static void saveRawFrame(ByteBuffer data, AvcCBox avcC, File f) throws IOException {
SeekableByteChannel raw = NIOUtils.writableChannel(f);
saveStreamParams(avcC, raw);
raw.write(data.duplicate());
raw.close();
}
public static void saveStreamParams(AvcCBox avcC, SeekableByteChannel raw) throws IOException {
ByteBuffer bb = ByteBuffer.allocate(1024);
for (ByteBuffer byteBuffer : avcC.getSpsList()) {
raw.write(ByteBuffer.wrap(new byte[] { (byte)0, (byte)0, (byte)0, (byte)1, (byte)103 }));
escapeNAL(byteBuffer.duplicate(), bb);
bb.flip();
raw.write(bb);
bb.clear();
}
for (ByteBuffer byteBuffer : avcC.getPpsList()) {
raw.write(ByteBuffer.wrap(new byte[] { (byte)0, (byte)0, (byte)0, (byte)1, (byte)104 }));
escapeNAL(byteBuffer.duplicate(), bb);
bb.flip();
raw.write(bb);
bb.clear();
}
}
public static List<ByteBuffer> splitFrame(ByteBuffer frame) {
ArrayList<ByteBuffer> result = new ArrayList<>();
ByteBuffer segment;
while ((segment = nextNALUnit(frame)) != null)
result.add(segment);
return result;
}
public static ByteBuffer joinNALUnits(List<ByteBuffer> nalUnits) {
int size = 0;
for (ByteBuffer nal : nalUnits)
size += 4 + nal.remaining();
ByteBuffer allocate = ByteBuffer.allocate(size);
joinNALUnitsToBuffer(nalUnits, allocate);
return allocate;
}
public static void joinNALUnitsToBuffer(List<ByteBuffer> nalUnits, ByteBuffer out) {
for (ByteBuffer nal : nalUnits) {
out.putInt(1);
out.put(nal.duplicate());
}
}
public static ByteBuffer getAvcCData(AvcCBox avcC) {
ByteBuffer bb = ByteBuffer.allocate(2048);
avcC.doWrite(bb);
bb.flip();
return bb;
}
public static AvcCBox parseAVCC(VideoSampleEntry vse) {
Box lb = NodeBox.findFirst(vse, Box.class, "avcC");
if (lb instanceof AvcCBox)
return (AvcCBox)lb;
return parseAVCCFromBuffer(((Box.LeafBox)lb).getData().duplicate());
}
public static ByteBuffer saveCodecPrivate(List<ByteBuffer> spsList, List<ByteBuffer> ppsList) {
int totalCodecPrivateSize = 0;
for (ByteBuffer byteBuffer : spsList)
totalCodecPrivateSize += byteBuffer.remaining() + 5;
for (ByteBuffer byteBuffer : ppsList)
totalCodecPrivateSize += byteBuffer.remaining() + 5;
ByteBuffer bb = ByteBuffer.allocate(totalCodecPrivateSize);
for (ByteBuffer byteBuffer : spsList) {
bb.putInt(1);
bb.put((byte)103);
bb.put(byteBuffer.duplicate());
}
for (ByteBuffer byteBuffer : ppsList) {
bb.putInt(1);
bb.put((byte)104);
bb.put(byteBuffer.duplicate());
}
bb.flip();
return bb;
}
public static ByteBuffer avcCToAnnexB(AvcCBox avcC) {
return saveCodecPrivate(avcC.getSpsList(), avcC.getPpsList());
}
public static AvcCBox parseAVCCFromBuffer(ByteBuffer bb) {
return AvcCBox.parseAvcCBox(bb);
}
public static ByteBuffer writeSPS(SeqParameterSet sps, int approxSize) {
ByteBuffer output = ByteBuffer.allocate(approxSize + 8);
sps.write(output);
output.flip();
escapeNALinplace(output);
return output;
}
public static SeqParameterSet readSPS(ByteBuffer data) {
ByteBuffer input = NIOUtils.duplicate(data);
unescapeNAL(input);
SeqParameterSet sps = SeqParameterSet.read(input);
return sps;
}
public static ByteBuffer writePPS(PictureParameterSet pps, int approxSize) {
ByteBuffer output = ByteBuffer.allocate(approxSize + 8);
pps.write(output);
output.flip();
escapeNALinplace(output);
return output;
}
public static PictureParameterSet readPPS(ByteBuffer data) {
ByteBuffer input = NIOUtils.duplicate(data);
unescapeNAL(input);
PictureParameterSet pps = PictureParameterSet.read(input);
return pps;
}
public static PictureParameterSet findPPS(List<PictureParameterSet> ppss, int id) {
for (PictureParameterSet pps : ppss) {
if (pps.picParameterSetId == id)
return pps;
}
return null;
}
public static SeqParameterSet findSPS(List<SeqParameterSet> spss, int id) {
for (SeqParameterSet sps : spss) {
if (sps.seqParameterSetId == id)
return sps;
}
return null;
}
public static abstract class SliceHeaderTweaker {
protected List<SeqParameterSet> sps;
protected List<PictureParameterSet> pps;
protected abstract void tweak(SliceHeader param1SliceHeader);
public SliceHeader run(ByteBuffer is, ByteBuffer os, NALUnit nu) {
ByteBuffer nal = os.duplicate();
H264Utils.unescapeNAL(is);
BitReader reader = BitReader.createBitReader(is);
SliceHeader sh = SliceHeaderReader.readPart1(reader);
PictureParameterSet pp = H264Utils.findPPS(this.pps, sh.picParameterSetId);
return part2(is, os, nu, H264Utils.findSPS(this.sps, pp.picParameterSetId), pp, nal, reader, sh);
}
public SliceHeader runSpsPps(ByteBuffer is, ByteBuffer os, NALUnit nu, SeqParameterSet sps, PictureParameterSet pps) {
ByteBuffer nal = os.duplicate();
H264Utils.unescapeNAL(is);
BitReader reader = BitReader.createBitReader(is);
SliceHeader sh = SliceHeaderReader.readPart1(reader);
return part2(is, os, nu, sps, pps, nal, reader, sh);
}
private SliceHeader part2(ByteBuffer is, ByteBuffer os, NALUnit nu, SeqParameterSet sps, PictureParameterSet pps, ByteBuffer nal, BitReader reader, SliceHeader sh) {
BitWriter writer = new BitWriter(os);
SliceHeaderReader.readPart2(sh, nu, sps, pps, reader);
tweak(sh);
SliceHeaderWriter.write(sh, (nu.type == NALUnitType.IDR_SLICE), nu.nal_ref_idc, writer);
if (pps.entropyCodingModeFlag) {
copyDataCABAC(is, os, reader, writer);
} else {
copyDataCAVLC(is, os, reader, writer);
}
nal.limit(os.position());
H264Utils.escapeNALinplace(nal);
os.position(nal.limit());
return sh;
}
private void copyDataCAVLC(ByteBuffer is, ByteBuffer os, BitReader reader, BitWriter writer) {
int wLeft = 8 - writer.curBit();
if (wLeft != 0)
writer.writeNBit(reader.readNBit(wLeft), wLeft);
writer.flush();
int shift = reader.curBit();
if (shift != 0) {
int mShift = 8 - shift;
int inp = reader.readNBit(mShift);
reader.stop();
while (is.hasRemaining()) {
int out = inp << shift;
inp = is.get() & 0xFF;
out |= inp >> mShift;
os.put((byte)out);
}
os.put((byte)(inp << shift));
} else {
reader.stop();
os.put(is);
}
}
private void copyDataCABAC(ByteBuffer is, ByteBuffer os, BitReader reader, BitWriter writer) {
long bp = (long)reader.curBit();
if (bp != 0L) {
long rem = (long)reader.readNBit(8 - (int)bp);
if ((long)((1 << (int)(8L - bp)) - 1) != rem)
throw new RuntimeException("Invalid CABAC padding");
}
if (writer.curBit() != 0)
writer.writeNBit(255, 8 - writer.curBit());
writer.flush();
reader.stop();
os.put(is);
}
}
public static Size getPicSize(SeqParameterSet sps) {
int w = sps.picWidthInMbsMinus1 + 1 << 4;
int h = SeqParameterSet.getPicHeightInMbs(sps) << 4;
if (sps.frameCroppingFlag) {
w -= sps.frameCropLeftOffset + sps.frameCropRightOffset << sps.chromaFormatIdc.compWidth[1];
h -= sps.frameCropTopOffset + sps.frameCropBottomOffset << sps.chromaFormatIdc.compHeight[1];
}
return new Size(w, h);
}
public static List<SeqParameterSet> readSPSFromBufferList(List<ByteBuffer> spsList) {
List<SeqParameterSet> result = new ArrayList<>();
for (ByteBuffer byteBuffer : spsList)
result.add(readSPS(NIOUtils.duplicate(byteBuffer)));
return result;
}
public static List<PictureParameterSet> readPPSFromBufferList(List<ByteBuffer> ppsList) {
List<PictureParameterSet> result = new ArrayList<>();
for (ByteBuffer byteBuffer : ppsList)
result.add(readPPS(NIOUtils.duplicate(byteBuffer)));
return result;
}
public static List<ByteBuffer> writePPSList(List<PictureParameterSet> allPps) {
List<ByteBuffer> result = new ArrayList<>();
for (PictureParameterSet pps : allPps)
result.add(writePPS(pps, 64));
return result;
}
public static List<ByteBuffer> writeSPSList(List<SeqParameterSet> allSps) {
List<ByteBuffer> result = new ArrayList<>();
for (SeqParameterSet sps : allSps)
result.add(writeSPS(sps, 256));
return result;
}
public static void dumpFrame(FileChannelWrapper ch, SeqParameterSet[] values, PictureParameterSet[] values2, List<ByteBuffer> nalUnits) throws IOException {
for (int j = 0; j < values.length; j++) {
SeqParameterSet sps = values[j];
NIOUtils.writeInt(ch, 1);
NIOUtils.writeByte(ch, (byte)103);
ch.write(writeSPS(sps, 128));
}
for (int i = 0; i < values2.length; i++) {
PictureParameterSet pps = values2[i];
NIOUtils.writeInt(ch, 1);
NIOUtils.writeByte(ch, (byte)104);
ch.write(writePPS(pps, 256));
}
for (ByteBuffer byteBuffer : nalUnits) {
NIOUtils.writeInt(ch, 1);
ch.write(byteBuffer.duplicate());
}
}
public static void toNAL(ByteBuffer codecPrivate, SeqParameterSet sps, PictureParameterSet pps) {
ByteBuffer bb1 = ByteBuffer.allocate(512), bb2 = ByteBuffer.allocate(512);
sps.write(bb1);
pps.write(bb2);
bb1.flip();
bb2.flip();
putNAL(codecPrivate, bb1, 103);
putNAL(codecPrivate, bb2, 104);
}
public static void toNALList(ByteBuffer codecPrivate, List<ByteBuffer> spsList2, List<ByteBuffer> ppsList2) {
for (ByteBuffer byteBuffer : spsList2)
putNAL(codecPrivate, byteBuffer, 103);
for (ByteBuffer byteBuffer : ppsList2)
putNAL(codecPrivate, byteBuffer, 104);
}
private static void putNAL(ByteBuffer codecPrivate, ByteBuffer byteBuffer, int nalType) {
ByteBuffer dst = ByteBuffer.allocate(byteBuffer.remaining() * 2);
escapeNAL(byteBuffer, dst);
dst.flip();
codecPrivate.putInt(1);
codecPrivate.put((byte)nalType);
codecPrivate.put(dst);
}
public static List<ByteBuffer> getRawPPS(ByteBuffer codecPrivate) {
return getRawNALUnitsOfType(codecPrivate, NALUnitType.PPS);
}
public static List<ByteBuffer> getRawSPS(ByteBuffer codecPrivate) {
return getRawNALUnitsOfType(codecPrivate, NALUnitType.SPS);
}
public static List<ByteBuffer> getRawNALUnitsOfType(ByteBuffer codecPrivate, NALUnitType type) {
List<ByteBuffer> result = new ArrayList<>();
for (ByteBuffer bb : splitFrame(codecPrivate.duplicate())) {
NALUnit nu = NALUnit.read(bb);
if (nu.type == type)
result.add(bb);
}
return result;
}
public static class Mv {
public static int mvX(int mv) {
return mv << 18 >> 18;
}
public static int mvY(int mv) {
return mv << 6 >> 20;
}
public static int mvRef(int mv) {
return mv >> 26;
}
public static int packMv(int mvx, int mvy, int r) {
return (r & 0x3F) << 26 | (mvy & 0xFFF) << 14 | mvx & 0x3FFF;
}
public static int mvC(int mv, int comp) {
return (comp == 0) ? mvX(mv) : mvY(mv);
}
}
public static class MvList {
private int[] list;
private static final int NA = H264Utils.Mv.packMv(0, 0, -1);
public MvList(int size) {
this.list = new int[size << 1];
clear();
}
public void clear() {
for (int i = 0; i < this.list.length; i += 2) {
this.list[i + 1] = NA;
this.list[i] = NA;
}
}
public int mv0X(int off) {
return H264Utils.Mv.mvX(this.list[off << 1]);
}
public int mv0Y(int off) {
return H264Utils.Mv.mvY(this.list[off << 1]);
}
public int mv0R(int off) {
return H264Utils.Mv.mvRef(this.list[off << 1]);
}
public int mv1X(int off) {
return H264Utils.Mv.mvX(this.list[(off << 1) + 1]);
}
public int mv1Y(int off) {
return H264Utils.Mv.mvY(this.list[(off << 1) + 1]);
}
public int mv1R(int off) {
return H264Utils.Mv.mvRef(this.list[(off << 1) + 1]);
}
public int getMv(int off, int forward) {
return this.list[(off << 1) + forward];
}
public void setMv(int off, int forward, int mv) {
this.list[(off << 1) + forward] = mv;
}
public void setPair(int off, int mv0, int mv1) {
this.list[off << 1] = mv0;
this.list[(off << 1) + 1] = mv1;
}
public void copyPair(int off, MvList other, int otherOff) {
this.list[off << 1] = other.list[otherOff << 1];
this.list[(off << 1) + 1] = other.list[(otherOff << 1) + 1];
}
}
public static class MvList2D {
private int[] list;
private int stride;
private int width;
private int height;
private static final int NA = H264Utils.Mv.packMv(0, 0, -1);
public MvList2D(int width, int height) {
this.list = new int[(width << 1) * height];
this.stride = width << 1;
this.width = width;
this.height = height;
clear();
}
public void clear() {
for (int i = 0; i < this.list.length; i += 2) {
this.list[i + 1] = NA;
this.list[i] = NA;
}
}
public int mv0X(int offX, int offY) {
return H264Utils.Mv.mvX(this.list[(offX << 1) + this.stride * offY]);
}
public int mv0Y(int offX, int offY) {
return H264Utils.Mv.mvY(this.list[(offX << 1) + this.stride * offY]);
}
public int mv0R(int offX, int offY) {
return H264Utils.Mv.mvRef(this.list[(offX << 1) + this.stride * offY]);
}
public int mv1X(int offX, int offY) {
return H264Utils.Mv.mvX(this.list[(offX << 1) + this.stride * offY + 1]);
}
public int mv1Y(int offX, int offY) {
return H264Utils.Mv.mvY(this.list[(offX << 1) + this.stride * offY + 1]);
}
public int mv1R(int offX, int offY) {
return H264Utils.Mv.mvRef(this.list[(offX << 1) + this.stride * offY + 1]);
}
public int getMv(int offX, int offY, int forward) {
return this.list[(offX << 1) + this.stride * offY + forward];
}
public void setMv(int offX, int offY, int forward, int mv) {
this.list[(offX << 1) + this.stride * offY + forward] = mv;
}
public int getHeight() {
return this.height;
}
public int getWidth() {
return this.width;
}
}
}

View file

@ -0,0 +1,9 @@
package org.jcodec.codecs.h264;
public class H264Utils2 {
public static int golomb2Signed(int val) {
int sign = ((val & 0x1) << 1) - 1;
val = ((val >> 1) + (val & 0x1)) * sign;
return val;
}
}

View file

@ -0,0 +1,69 @@
package org.jcodec.codecs.h264;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.RefPicMarking;
import org.jcodec.codecs.h264.io.model.SliceHeader;
public class POCManager {
private int prevPOCMsb;
private int prevPOCLsb;
public int calcPOC(SliceHeader firstSliceHeader, NALUnit firstNu) {
switch (firstSliceHeader.sps.picOrderCntType) {
case 0:
return calcPOC0(firstSliceHeader, firstNu);
case 1:
return calcPOC1(firstSliceHeader, firstNu);
case 2:
return calcPOC2(firstSliceHeader, firstNu);
}
throw new RuntimeException("POC no!!!");
}
private int calcPOC2(SliceHeader firstSliceHeader, NALUnit firstNu) {
return firstSliceHeader.frameNum << 1;
}
private int calcPOC1(SliceHeader firstSliceHeader, NALUnit firstNu) {
return firstSliceHeader.frameNum << 1;
}
private int calcPOC0(SliceHeader firstSliceHeader, NALUnit firstNu) {
int POCMsb;
if (firstNu.type == NALUnitType.IDR_SLICE)
this.prevPOCMsb = this.prevPOCLsb = 0;
int maxPOCLsbDiv2 = 1 << firstSliceHeader.sps.log2MaxPicOrderCntLsbMinus4 + 3, maxPOCLsb = maxPOCLsbDiv2 << 1;
int POCLsb = firstSliceHeader.picOrderCntLsb;
if (POCLsb < this.prevPOCLsb && this.prevPOCLsb - POCLsb >= maxPOCLsbDiv2) {
POCMsb = this.prevPOCMsb + maxPOCLsb;
} else if (POCLsb > this.prevPOCLsb && POCLsb - this.prevPOCLsb > maxPOCLsbDiv2) {
POCMsb = this.prevPOCMsb - maxPOCLsb;
} else {
POCMsb = this.prevPOCMsb;
}
int POC = POCMsb + POCLsb;
if (firstNu.nal_ref_idc > 0)
if (hasMMCO5(firstSliceHeader, firstNu)) {
this.prevPOCMsb = 0;
this.prevPOCLsb = POC;
} else {
this.prevPOCMsb = POCMsb;
this.prevPOCLsb = POCLsb;
}
return POC;
}
private boolean hasMMCO5(SliceHeader firstSliceHeader, NALUnit firstNu) {
if (firstNu.type != NALUnitType.IDR_SLICE && firstSliceHeader.refPicMarkingNonIDR != null) {
RefPicMarking.Instruction[] instructions = firstSliceHeader.refPicMarkingNonIDR.getInstructions();
for (int i = 0; i < instructions.length; i++) {
RefPicMarking.Instruction instruction = instructions[i];
if (instruction.getType() == RefPicMarking.InstrType.CLEAR)
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,780 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class BlockInterpolator {
private int[] tmp1 = new int[1024];
private int[] tmp2 = new int[1024];
private byte[] tmp3 = new byte[1024];
private LumaInterpolator[] safe = initSafe();
private LumaInterpolator[] unsafe = initUnsafe();
public void getBlockLuma(Picture pic, Picture out, int off, int x, int y, int w, int h) {
int xInd = x & 0x3;
int yInd = y & 0x3;
int xFp = x >> 2;
int yFp = y >> 2;
if (xFp < 2 || yFp < 2 || xFp > pic.getWidth() - w - 5 || yFp > pic.getHeight() - h - 5) {
this.unsafe[(yInd << 2) + xInd].getLuma(pic.getData()[0], pic.getWidth(), pic.getHeight(), out.getPlaneData(0), off,
out.getPlaneWidth(0), xFp, yFp, w, h);
} else {
this.safe[(yInd << 2) + xInd].getLuma(pic.getData()[0], pic.getWidth(), pic.getHeight(), out.getPlaneData(0), off,
out.getPlaneWidth(0), xFp, yFp, w, h);
}
}
public static void getBlockChroma(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int xInd = x & 0x7;
int yInd = y & 0x7;
int xFull = x >> 3;
int yFull = y >> 3;
if (xFull < 0 || xFull > picW - blkW - 1 || yFull < 0 || yFull > picH - blkH - 1) {
if (xInd == 0 && yInd == 0) {
getChroma00Unsafe(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, blkW, blkH);
} else if (yInd == 0) {
getChromaX0Unsafe(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, xInd, blkW, blkH);
} else if (xInd == 0) {
getChroma0XUnsafe(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, yInd, blkW, blkH);
} else {
getChromaXXUnsafe(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, xInd, yInd, blkW, blkH);
}
} else if (xInd == 0 && yInd == 0) {
getChroma00(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, blkW, blkH);
} else if (yInd == 0) {
getChromaX0(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, xInd, blkW, blkH);
} else if (xInd == 0) {
getChroma0X(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, yInd, blkW, blkH);
} else {
getChromaXX(pels, picW, picH, blk, blkOff, blkStride, xFull, yFull, xInd, yInd, blkW, blkH);
}
}
static void getLuma00(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
System.arraycopy(pic, off, blk, blkOff, blkW);
off += picW;
blkOff += blkStride;
}
}
static void getLuma00Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(j + y, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = pic[lineStart + MathUtil.clip(x + i, 0, maxW)];
blkOff += blkStride;
}
}
static void getLuma20NoRoundInt(int[] pic, int picW, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
int off1 = -2;
for (int i = 0; i < blkW; i++) {
int a = pic[off + off1] + pic[off + off1 + 5];
int b = pic[off + off1 + 1] + pic[off + off1 + 4];
int c = pic[off + off1 + 2] + pic[off + off1 + 3];
blk[blkOff + i] = a + 5 * ((c << 2) - b);
off1++;
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma20NoRound(byte[] pic, int picW, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
int off1 = -2;
for (int i = 0; i < blkW; i++) {
int a = pic[off + off1] + pic[off + off1 + 5];
int b = pic[off + off1 + 1] + pic[off + off1 + 4];
int c = pic[off + off1 + 2] + pic[off + off1 + 3];
blk[blkOff + i] = a + 5 * ((c << 2) - b);
off1++;
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma20(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
int off1 = -2;
for (int i = 0; i < blkW; i++) {
int a = pic[off + off1] + pic[off + off1 + 5];
int b = pic[off + off1 + 1] + pic[off + off1 + 4];
int c = pic[off + off1 + 2] + pic[off + off1 + 3];
blk[blkOff + i] = (byte)MathUtil.clip(a + 5 * ((c << 2) - b) + 16 >> 5, -128, 127);
off1++;
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma20UnsafeNoRound(byte[] pic, int picW, int picH, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxW = picW - 1;
int maxH = picH - 1;
for (int i = 0; i < blkW; i++) {
int ipos_m2 = MathUtil.clip(x + i - 2, 0, maxW);
int ipos_m1 = MathUtil.clip(x + i - 1, 0, maxW);
int ipos = MathUtil.clip(x + i, 0, maxW);
int ipos_p1 = MathUtil.clip(x + i + 1, 0, maxW);
int ipos_p2 = MathUtil.clip(x + i + 2, 0, maxW);
int ipos_p3 = MathUtil.clip(x + i + 3, 0, maxW);
int boff = blkOff;
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(j + y, 0, maxH) * picW;
int a = pic[lineStart + ipos_m2] + pic[lineStart + ipos_p3];
int b = pic[lineStart + ipos_m1] + pic[lineStart + ipos_p2];
int c = pic[lineStart + ipos] + pic[lineStart + ipos_p1];
blk[boff + i] = a + 5 * ((c << 2) - b);
boff += blkStride;
}
}
}
void getLuma20Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20UnsafeNoRound(pic, picW, picH, this.tmp1, blkOff, blkStride, x, y, blkW, blkH);
for (int i = 0; i < blkW; i++) {
int boff = blkOff;
for (int j = 0; j < blkH; j++) {
blk[boff + i] = (byte)MathUtil.clip(this.tmp1[boff + i] + 16 >> 5, -128, 127);
boff += blkStride;
}
}
}
static void getLuma02NoRoundInt(int[] pic, int picW, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = (y - 2) * picW + x, picWx2 = picW + picW, picWx3 = picWx2 + picW, picWx4 = picWx3 + picW, picWx5 = picWx4 + picW;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int a = pic[off + i] + pic[off + i + picWx5];
int b = pic[off + i + picW] + pic[off + i + picWx4];
int c = pic[off + i + picWx2] + pic[off + i + picWx3];
blk[blkOff + i] = a + 5 * ((c << 2) - b);
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma02NoRound(byte[] pic, int picW, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = (y - 2) * picW + x, picWx2 = picW + picW, picWx3 = picWx2 + picW, picWx4 = picWx3 + picW, picWx5 = picWx4 + picW;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int a = pic[off + i] + pic[off + i + picWx5];
int b = pic[off + i + picW] + pic[off + i + picWx4];
int c = pic[off + i + picWx2] + pic[off + i + picWx3];
blk[blkOff + i] = a + 5 * ((c << 2) - b);
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma02(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = (y - 2) * picW + x, picWx2 = picW + picW, picWx3 = picWx2 + picW, picWx4 = picWx3 + picW, picWx5 = picWx4 + picW;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int a = pic[off + i] + pic[off + i + picWx5];
int b = pic[off + i + picW] + pic[off + i + picWx4];
int c = pic[off + i + picWx2] + pic[off + i + picWx3];
blk[blkOff + i] = (byte)MathUtil.clip(a + 5 * ((c << 2) - b) + 16 >> 5, -128, 127);
}
off += picW;
blkOff += blkStride;
}
}
static void getLuma02UnsafeNoRound(byte[] pic, int picW, int picH, int[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
for (int j = 0; j < blkH; j++) {
int offP0 = MathUtil.clip(y + j - 2, 0, maxH) * picW;
int offP1 = MathUtil.clip(y + j - 1, 0, maxH) * picW;
int offP2 = MathUtil.clip(y + j, 0, maxH) * picW;
int offP3 = MathUtil.clip(y + j + 1, 0, maxH) * picW;
int offP4 = MathUtil.clip(y + j + 2, 0, maxH) * picW;
int offP5 = MathUtil.clip(y + j + 3, 0, maxH) * picW;
for (int i = 0; i < blkW; i++) {
int pres_x = MathUtil.clip(x + i, 0, maxW);
int a = pic[pres_x + offP0] + pic[pres_x + offP5];
int b = pic[pres_x + offP1] + pic[pres_x + offP4];
int c = pic[pres_x + offP2] + pic[pres_x + offP3];
blk[blkOff + i] = a + 5 * ((c << 2) - b);
}
blkOff += blkStride;
}
}
void getLuma02Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma02UnsafeNoRound(pic, picW, picH, this.tmp1, blkOff, blkStride, x, y, blkW, blkH);
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)MathUtil.clip(this.tmp1[blkOff + i] + 16 >> 5, -128, 127);
blkOff += blkStride;
}
}
static void getLuma10(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pic, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[off + i] + 1 >> 1);
off += picW;
blkOff += blkStride;
}
}
void getLuma10Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
getLuma20Unsafe(pic, picW, picH, blk, blkOff, blkStride, x, y, blkW, blkH);
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(j + y, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[lineStart + MathUtil.clip(x + i, 0, maxW)] + 1 >> 1);
blkOff += blkStride;
}
}
static void getLuma30(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pic, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(pic[off + i + 1] + blk[blkOff + i] + 1 >> 1);
off += picW;
blkOff += blkStride;
}
}
void getLuma30Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
getLuma20Unsafe(pic, picW, picH, blk, blkOff, blkStride, x, y, blkW, blkH);
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(j + y, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[lineStart + MathUtil.clip(x + i + 1, 0, maxW)] + 1 >> 1);
blkOff += blkStride;
}
}
static void getLuma01(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma02(pic, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[off + i] + 1 >> 1);
off += picW;
blkOff += blkStride;
}
}
void getLuma01Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
getLuma02Unsafe(pic, picW, picH, blk, blkOff, blkStride, x, y, blkW, blkH);
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(y + j, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[lineStart + MathUtil.clip(x + i, 0, maxW)] + 1 >> 1);
blkOff += blkStride;
}
}
static void getLuma03(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma02(pic, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[off + i + picW] + 1 >> 1);
off += picW;
blkOff += blkStride;
}
}
void getLuma03Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
getLuma02Unsafe(pic, picW, picH, blk, blkOff, blkStride, x, y, blkW, blkH);
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(y + j + 1, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(blk[blkOff + i] + pic[lineStart + MathUtil.clip(x + i, 0, maxW)] + 1 >> 1);
blkOff += blkStride;
}
}
void getLuma21(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20NoRound(pic, picW, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
int off = blkW << 1;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += blkW;
}
}
void getLuma21Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20UnsafeNoRound(pic, picW, imgH, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
int off = blkW << 1;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += blkW;
}
}
void getLuma22(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20NoRound(pic, picW, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
blkOff += blkStride;
}
}
void getLuma22Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20UnsafeNoRound(pic, picW, imgH, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
blkOff += blkStride;
}
}
void getLuma23(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20NoRound(pic, picW, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
int off = blkW << 1;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i + blkW] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += blkW;
}
}
void getLuma23Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20UnsafeNoRound(pic, picW, imgH, this.tmp1, 0, blkW, x, y - 2, blkW, blkH + 7);
getLuma02NoRoundInt(this.tmp1, blkW, this.tmp2, blkOff, blkStride, 0, 2, blkW, blkH);
int off = blkW << 1;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i + blkW] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += blkW;
}
}
void getLuma12(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int tmpW = blkW + 7;
getLuma02NoRound(pic, picW, this.tmp1, 0, tmpW, x - 2, y, tmpW, blkH);
getLuma20NoRoundInt(this.tmp1, tmpW, this.tmp2, blkOff, blkStride, 2, 0, blkW, blkH);
int off = 2;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += tmpW;
}
}
void getLuma12Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int tmpW = blkW + 7;
getLuma02UnsafeNoRound(pic, picW, imgH, this.tmp1, 0, tmpW, x - 2, y, tmpW, blkH);
getLuma20NoRoundInt(this.tmp1, tmpW, this.tmp2, blkOff, blkStride, 2, 0, blkW, blkH);
int off = 2;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += tmpW;
}
}
void getLuma32(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int tmpW = blkW + 7;
getLuma02NoRound(pic, picW, this.tmp1, 0, tmpW, x - 2, y, tmpW, blkH);
getLuma20NoRoundInt(this.tmp1, tmpW, this.tmp2, blkOff, blkStride, 2, 0, blkW, blkH);
int off = 2;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i + 1] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += tmpW;
}
}
void getLuma32Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int tmpW = blkW + 7;
getLuma02UnsafeNoRound(pic, picW, imgH, this.tmp1, 0, tmpW, x - 2, y, tmpW, blkH);
getLuma20NoRoundInt(this.tmp1, tmpW, this.tmp2, blkOff, blkStride, 2, 0, blkW, blkH);
int off = 2;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int rounded = MathUtil.clip(this.tmp2[blkOff + i] + 512 >> 10, -128, 127);
int rounded2 = MathUtil.clip(this.tmp1[off + i + 1] + 16 >> 5, -128, 127);
blk[blkOff + i] = (byte)(rounded + rounded2 + 1 >> 1);
}
blkOff += blkStride;
off += tmpW;
}
}
void getLuma33(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pic, picW, blk, blkOff, blkStride, x, y + 1, blkW, blkH);
getLuma02(pic, picW, this.tmp3, 0, blkW, x + 1, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma33Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20Unsafe(pic, picW, imgH, blk, blkOff, blkStride, x, y + 1, blkW, blkH);
getLuma02Unsafe(pic, picW, imgH, this.tmp3, 0, blkW, x + 1, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma11(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pic, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
getLuma02(pic, picW, this.tmp3, 0, blkW, x, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma11Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20Unsafe(pic, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
getLuma02Unsafe(pic, picW, imgH, this.tmp3, 0, blkW, x, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma13(byte[] pic, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pic, picW, blk, blkOff, blkStride, x, y + 1, blkW, blkH);
getLuma02(pic, picW, this.tmp3, 0, blkW, x, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma13Unsafe(byte[] pic, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20Unsafe(pic, picW, imgH, blk, blkOff, blkStride, x, y + 1, blkW, blkH);
getLuma02Unsafe(pic, picW, imgH, this.tmp3, 0, blkW, x, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma31(byte[] pels, int picW, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
getLuma02(pels, picW, this.tmp3, 0, blkW, x + 1, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
void getLuma31Unsafe(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
getLuma20Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
getLuma02Unsafe(pels, picW, imgH, this.tmp3, 0, blkW, x + 1, y, blkW, blkH);
merge(blk, this.tmp3, blkOff, blkStride, blkW, blkH);
}
private static void merge(byte[] first, byte[] second, int blkOff, int blkStride, int blkW, int blkH) {
int tOff = 0;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
first[blkOff + i] = (byte)(first[blkOff + i] + second[tOff + i] + 1 >> 1);
blkOff += blkStride;
tOff += blkW;
}
}
private static void getChroma00(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int off = y * picW + x;
for (int j = 0; j < blkH; j++) {
System.arraycopy(pic, off, blk, blkOff, blkW);
off += picW;
blkOff += blkStride;
}
}
private static void getChroma00Unsafe(byte[] pic, int picW, int picH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
for (int j = 0; j < blkH; j++) {
int lineStart = MathUtil.clip(j + y, 0, maxH) * picW;
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = pic[lineStart + MathUtil.clip(x + i, 0, maxW)];
blkOff += blkStride;
}
}
private static void getChroma0X(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracY, int blkW, int blkH) {
int w00 = fullY * picW + fullX;
int w01 = w00 + ((fullY < picH - 1) ? picW : 0);
int eMy = 8 - fracY;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(eMy * pels[w00 + i] + fracY * pels[w01 + i] + 4 >> 3);
w00 += picW;
w01 += picW;
blkOff += blkStride;
}
}
private static void getChroma0XUnsafe(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracY, int blkW, int blkH) {
int maxW = picW - 1;
int maxH = picH - 1;
int eMy = 8 - fracY;
for (int j = 0; j < blkH; j++) {
int off00 = MathUtil.clip(fullY + j, 0, maxH) * picW;
int off01 = MathUtil.clip(fullY + j + 1, 0, maxH) * picW;
for (int i = 0; i < blkW; i++) {
int w00 = MathUtil.clip(fullX + i, 0, maxW) + off00;
int w01 = MathUtil.clip(fullX + i, 0, maxW) + off01;
blk[blkOff + i] = (byte)(eMy * pels[w00] + fracY * pels[w01] + 4 >> 3);
}
blkOff += blkStride;
}
}
private static void getChromaX0(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracX, int blkW, int blkH) {
int w00 = fullY * picW + fullX;
int w10 = w00 + ((fullX < picW - 1) ? 1 : 0);
int eMx = 8 - fracX;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(eMx * pels[w00 + i] + fracX * pels[w10 + i] + 4 >> 3);
w00 += picW;
w10 += picW;
blkOff += blkStride;
}
}
private static void getChromaX0Unsafe(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracX, int blkW, int blkH) {
int eMx = 8 - fracX;
int maxW = picW - 1;
int maxH = picH - 1;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int w00 = MathUtil.clip(fullY + j, 0, maxH) * picW + MathUtil.clip(fullX + i, 0, maxW);
int w10 = MathUtil.clip(fullY + j, 0, maxH) * picW + MathUtil.clip(fullX + i + 1, 0, maxW);
blk[blkOff + i] = (byte)(eMx * pels[w00] + fracX * pels[w10] + 4 >> 3);
}
blkOff += blkStride;
}
}
private static void getChromaXX(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracX, int fracY, int blkW, int blkH) {
int w00 = fullY * picW + fullX;
int w01 = w00 + ((fullY < picH - 1) ? picW : 0);
int w10 = w00 + ((fullX < picW - 1) ? 1 : 0);
int w11 = w10 + w01 - w00;
int eMx = 8 - fracX;
int eMy = 8 - fracY;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++)
blk[blkOff + i] = (byte)(eMx * eMy * pels[w00 + i] + fracX * eMy * pels[w10 + i] + eMx * fracY * pels[w01 + i] + fracX * fracY * pels[w11 + i] + 32 >> 6);
blkOff += blkStride;
w00 += picW;
w01 += picW;
w10 += picW;
w11 += picW;
}
}
private static void getChromaXXUnsafe(byte[] pels, int picW, int picH, byte[] blk, int blkOff, int blkStride, int fullX, int fullY, int fracX, int fracY, int blkW, int blkH) {
int maxH = picH - 1;
int maxW = picW - 1;
int eMx = 8 - fracX;
int eMy = 8 - fracY;
for (int j = 0; j < blkH; j++) {
for (int i = 0; i < blkW; i++) {
int w00 = MathUtil.clip(fullY + j, 0, maxH) * picW + MathUtil.clip(fullX + i, 0, maxW);
int w01 = MathUtil.clip(fullY + j + 1, 0, maxH) * picW + MathUtil.clip(fullX + i, 0, maxW);
int w10 = MathUtil.clip(fullY + j, 0, maxH) * picW + MathUtil.clip(fullX + i + 1, 0, maxW);
int w11 = MathUtil.clip(fullY + j + 1, 0, maxH) * picW + MathUtil.clip(fullX + i + 1, 0, maxW);
blk[blkOff + i] = (byte)(eMx * eMy * pels[w00] + fracX * eMy * pels[w10] + eMx * fracY * pels[w01] + fracX * fracY * pels[w11] + 32 >> 6);
}
blkOff += blkStride;
}
}
private LumaInterpolator[] initSafe() {
final BlockInterpolator self = this;
return new LumaInterpolator[] {
new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma00(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma10(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma20(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma30(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma01(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma11(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma21(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma31(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma02(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma12(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
},
new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma22(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma32(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma03(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma13(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma23(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma33(pels, picW, blk, blkOff, blkStride, x, y, blkW, blkH);
}
} };
}
private LumaInterpolator[] initUnsafe() {
final BlockInterpolator self = this;
return new LumaInterpolator[] {
new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
BlockInterpolator.getLuma00Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma10Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma20Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma30Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma01Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma11Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma21Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma31Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma02Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma12Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
},
new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma22Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma32Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma03Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma13Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma23Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
}, new LumaInterpolator() {
public void getLuma(byte[] pels, int picW, int imgH, byte[] blk, int blkOff, int blkStride, int x, int y, int blkW, int blkH) {
self.getLuma33Unsafe(pels, picW, imgH, blk, blkOff, blkStride, x, y, blkW, blkH);
}
} };
}
private static interface LumaInterpolator {
void getLuma(byte[] param1ArrayOfbyte1, int param1Int1, int param1Int2, byte[] param1ArrayOfbyte2, int param1Int3, int param1Int4, int param1Int5, int param1Int6, int param1Int7, int param1Int8);
}
}

View file

@ -0,0 +1,835 @@
package org.jcodec.codecs.h264.decode;
public class CABACContst {
public static int[] cabac_context_init_I_A = new int[] {
20, 2, 3, 20, 2, 3, -28, -23, -6, -1,
7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, -9, 4, 0, -7, 13, 3,
0, 1, 0, -17, -13, 0, -7, -21, -27, -31,
-24, -18, -27, -21, -30, -17, -12, -16, -11, -12,
-2, -15, -13, -3, -8, -10, -30, -1, -6, -7,
-20, -4, -5, -7, -22, -7, -11, -3, -5, -4,
-4, -12, -7, -7, 8, 5, -2, 1, 0, -2,
1, 7, 10, 0, 11, 1, 0, 5, 31, 1,
7, 28, 16, 14, -13, -15, -13, -13, -12, -10,
-16, -10, -7, -13, -19, 1, 0, -5, 18, -8,
-15, 0, -4, 2, -11, -3, 15, -13, 0, 0,
21, -15, 9, 16, 0, 12, 24, 15, 8, 13,
15, 13, 10, 12, 6, 20, 15, 4, 1, 0,
7, 12, 11, 15, 11, 13, 16, 12, 10, 30,
18, 10, 17, 17, 0, 26, 22, 26, 30, 28,
33, 37, 33, 40, 38, 33, 40, 41, 38, 41,
30, 27, 26, 37, 35, 38, 38, 37, 38, 42,
35, 39, 14, 27, 21, 12, 2, -3, -6, -5,
-3, -2, 0, 1, -2, -1, -9, -5, -5, -3,
-2, 0, -16, -8, -10, -6, -10, -12, -15, -10,
-6, -4, -12, -8, -7, -9, -17, -11, -20, -11,
-6, -4, -13, -13, -11, -19, -8, -5, -4, -2,
-3, -13, -10, -12, -13, -14, 0, -6, -6, -8,
0, -1, 0, -2, -2, -5, -3, -4, -9, -1,
0, 3, 10, -7, 15, 14, 16, 12, 1, 20,
18, 5, 1, 10, 17, 9, -12, -11, -16, -7,
-8, -7, -9, -13, 4, -3, -3, -6, 10, -1,
-1, -7, -14, 2, 0, -5, 0, -11, 1, 0,
-14, 3, 4, -1, -13, 11, 5, 12, 15, 6,
7, 12, 18, 13, 13, 15, 12, 13, 15, 14,
14, 17, 17, 24, 21, 25, 31, 22, 19, 14,
10, 7, -2, -4, -3, 9, -12, 36, 36, 32,
37, 44, 34, 34, 40, 33, 35, 33, 38, 33,
23, 13, 29, 26, 22, 31, 35, 34, 34, 36,
34, 32, 35, 34, 39, 30, 34, 29, 19, 31,
31, 25, -17, -20, -18, -11, -15, -14, -26, -15,
-14, 0, -14, -24, -23, -24, -11, 23, 26, 40,
49, 44, 45, 44, 33, 19, -3, -1, 1, 1,
0, -2, 0, 1, 0, -9, -14, -13, -15, -12,
-18, -10, -9, -14, -10, -10, -10, -5, -9, -5,
2, 21, 24, 28, 28, 29, 29, 35, 29, 14,
-17, -12, -16, -11, -12, -2, -15, -13, -3, -8,
-10, -30, -17, -12, -16, -11, -12, -2, -15, -13,
-3, -8, -10, -30, -7, -11, -3, -5, -4, -4,
-12, -7, -7, 8, 5, -2, 1, 0, -2, 1,
7, 10, 0, 11, 1, 0, 5, 31, 1, 7,
28, 16, 14, -13, -15, -13, -13, -12, -10, -16,
-10, -7, -13, -19, 1, 0, -5, 18, -7, -11,
-3, -5, -4, -4, -12, -7, -7, 8, 5, -2,
1, 0, -2, 1, 7, 10, 0, 11, 1, 0,
5, 31, 1, 7, 28, 16, 14, -13, -15, -13,
-13, -12, -10, -16, -10, -7, -13, -19, 1, 0,
-5, 18, 24, 15, 8, 13, 15, 13, 10, 12,
6, 20, 15, 4, 1, 0, 7, 12, 11, 15,
11, 13, 16, 12, 10, 30, 18, 10, 17, 17,
0, 26, 22, 26, 30, 28, 33, 37, 33, 40,
38, 33, 40, 41, 38, 41, 24, 15, 8, 13,
15, 13, 10, 12, 6, 20, 15, 4, 1, 0,
7, 12, 11, 15, 11, 13, 16, 12, 10, 30,
18, 10, 17, 17, 0, 26, 22, 26, 30, 28,
33, 37, 33, 40, 38, 33, 40, 41, 38, 41,
-17, -20, -18, -11, -15, -14, -26, -15, -14, 0,
-14, -24, -23, -24, -11, -14, -13, -15, -12, -18,
-10, -9, -14, -10, -10, -10, -5, -9, -5, 2,
23, 26, 40, 49, 44, 45, 44, 33, 19, 21,
24, 28, 28, 29, 29, 35, 29, 14, -3, -1,
1, 1, 0, -2, 0, 1, 0, -9, -17, -20,
-18, -11, -15, -14, -26, -15, -14, 0, -14, -24,
-23, -24, -11, -14, -13, -15, -12, -18, -10, -9,
-14, -10, -10, -10, -5, -9, -5, 2, 23, 26,
40, 49, 44, 45, 44, 33, 19, 21, 24, 28,
28, 29, 29, 35, 29, 14, -3, -1, 1, 1,
0, -2, 0, 1, 0, -9, -6, -6, -8, 0,
-1, 0, -2, -2, -5, -3, -4, -9, -1, 0,
3, 10, -7, 15, 14, 16, 12, 1, 20, 18,
5, 1, 10, 17, 9, -12, -11, -16, -7, -8,
-7, -9, -13, 4, -3, -3, -6, 10, -1, -1,
-6, -6, -8, 0, -1, 0, -2, -2, -5, -3,
-4, -9, -1, 0, 3, 10, -7, 15, 14, 16,
12, 1, 20, 18, 5, 1, 10, 17, 9, -12,
-11, -16, -7, -8, -7, -9, -13, 4, -3, -3,
-6, 10, -1, -1, 15, 6, 7, 12, 18, 13,
13, 15, 12, 13, 15, 14, 14, 17, 17, 24,
21, 25, 31, 22, 19, 14, 10, 7, -2, -4,
-3, 9, -12, 36, 36, 32, 37, 44, 34, 34,
40, 33, 35, 33, 38, 33, 23, 13, 15, 6,
7, 12, 18, 13, 13, 15, 12, 13, 15, 14,
14, 17, 17, 24, 21, 25, 31, 22, 19, 14,
10, 7, -2, -4, -3, 9, -12, 36, 36, 32,
37, 44, 34, 34, 40, 33, 35, 33, 38, 33,
23, 13, -3, -6, -5, -3, -2, 0, 1, -2,
-1, -9, -5, -5, -3, -2, 0, -16, -8, -10,
-6, -10, -12, -15, -10, -6, -4, -12, -8, -7,
-9, -17, -3, -6, -5, -3, -2, 0, 1, -2,
-1, -9, -5, -5, -3, -2, 0, -16, -8, -10,
-6, -10, -12, -15, -10, -6, -4, -12, -8, -7,
-9, -17, -3, -8, -10, -30, -3, -8, -10, -30,
-3, -8, -10, -30 };
public static int[] cabac_context_init_I_B = new int[] {
-15, 54, 74, -15, 54, 74, 127, 104, 53, 54,
51, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
41, 63, 63, 63, 83, 86, 97, 72, 41, 62,
11, 55, 69, 127, 102, 82, 74, 107, 127, 127,
127, 95, 127, 114, 127, 123, 115, 122, 115, 63,
68, 84, 104, 70, 93, 90, 127, 74, 97, 91,
127, 56, 82, 76, 125, 93, 87, 77, 71, 63,
68, 84, 62, 65, 61, 56, 66, 64, 61, 78,
50, 52, 35, 44, 38, 45, 46, 44, 17, 51,
50, 19, 33, 62, 108, 100, 101, 91, 94, 88,
84, 86, 83, 87, 94, 70, 72, 74, 59, 102,
100, 95, 75, 72, 75, 71, 46, 69, 62, 65,
37, 72, 57, 54, 62, 72, 0, 9, 25, 18,
9, 19, 37, 18, 29, 33, 30, 45, 58, 62,
61, 38, 45, 39, 42, 44, 45, 41, 49, 34,
42, 55, 51, 46, 89, -19, -17, -17, -25, -20,
-23, -27, -23, -28, -17, -11, -15, -6, 1, 17,
-6, 3, 22, -16, -4, -8, -3, 3, 5, 0,
16, 22, 48, 37, 60, 68, 97, 71, 42, 50,
54, 62, 58, 63, 72, 74, 91, 67, 27, 39,
44, 46, 64, 68, 78, 77, 86, 92, 55, 60,
62, 65, 73, 76, 80, 88, 110, 97, 84, 79,
73, 74, 86, 96, 97, 117, 78, 33, 48, 53,
62, 71, 79, 86, 90, 97, 0, 93, 84, 79,
66, 71, 62, 60, 59, 75, 62, 58, 66, 79,
71, 68, 44, 62, 36, 40, 27, 29, 44, 36,
32, 42, 48, 62, 46, 64, 104, 97, 96, 88,
85, 85, 85, 88, 66, 77, 76, 76, 58, 76,
83, 99, 95, 95, 76, 74, 70, 75, 68, 65,
73, 62, 62, 68, 75, 55, 64, 70, 6, 19,
16, 14, 13, 11, 15, 16, 23, 23, 20, 26,
44, 40, 47, 17, 21, 22, 27, 29, 35, 50,
57, 63, 77, 82, 94, 69, 109, -35, -34, -26,
-30, -32, -18, -15, -15, -7, -5, 0, 2, 13,
35, 58, -3, 0, 30, -7, -15, -3, 3, -1,
5, 11, 5, 12, 11, 29, 26, 39, 66, 21,
31, 50, 120, 112, 114, 85, 92, 89, 71, 81,
80, 68, 70, 56, 68, 50, 74, -13, -13, -15,
-14, 3, 6, 34, 54, 82, 75, 23, 34, 43,
54, 55, 61, 64, 68, 92, 106, 97, 90, 90,
88, 73, 79, 86, 73, 70, 69, 66, 64, 58,
59, -10, -11, -8, -1, 3, 9, 20, 36, 67,
123, 115, 122, 115, 63, 68, 84, 104, 70, 93,
90, 127, 123, 115, 122, 115, 63, 68, 84, 104,
70, 93, 90, 127, 93, 87, 77, 71, 63, 68,
84, 62, 65, 61, 56, 66, 64, 61, 78, 50,
52, 35, 44, 38, 45, 46, 44, 17, 51, 50,
19, 33, 62, 108, 100, 101, 91, 94, 88, 84,
86, 83, 87, 94, 70, 72, 74, 59, 93, 87,
77, 71, 63, 68, 84, 62, 65, 61, 56, 66,
64, 61, 78, 50, 52, 35, 44, 38, 45, 46,
44, 17, 51, 50, 19, 33, 62, 108, 100, 101,
91, 94, 88, 84, 86, 83, 87, 94, 70, 72,
74, 59, 0, 9, 25, 18, 9, 19, 37, 18,
29, 33, 30, 45, 58, 62, 61, 38, 45, 39,
42, 44, 45, 41, 49, 34, 42, 55, 51, 46,
89, -19, -17, -17, -25, -20, -23, -27, -23, -28,
-17, -11, -15, -6, 1, 17, 0, 9, 25, 18,
9, 19, 37, 18, 29, 33, 30, 45, 58, 62,
61, 38, 45, 39, 42, 44, 45, 41, 49, 34,
42, 55, 51, 46, 89, -19, -17, -17, -25, -20,
-23, -27, -23, -28, -17, -11, -15, -6, 1, 17,
120, 112, 114, 85, 92, 89, 71, 81, 80, 68,
70, 56, 68, 50, 74, 106, 97, 90, 90, 88,
73, 79, 86, 73, 70, 69, 66, 64, 58, 59,
-13, -13, -15, -14, 3, 6, 34, 54, 82, -10,
-11, -8, -1, 3, 9, 20, 36, 67, 75, 23,
34, 43, 54, 55, 61, 64, 68, 92, 120, 112,
114, 85, 92, 89, 71, 81, 80, 68, 70, 56,
68, 50, 74, 106, 97, 90, 90, 88, 73, 79,
86, 73, 70, 69, 66, 64, 58, 59, -13, -13,
-15, -14, 3, 6, 34, 54, 82, -10, -11, -8,
-1, 3, 9, 20, 36, 67, 75, 23, 34, 43,
54, 55, 61, 64, 68, 92, 93, 84, 79, 66,
71, 62, 60, 59, 75, 62, 58, 66, 79, 71,
68, 44, 62, 36, 40, 27, 29, 44, 36, 32,
42, 48, 62, 46, 64, 104, 97, 96, 88, 85,
85, 85, 88, 66, 77, 76, 76, 58, 76, 83,
93, 84, 79, 66, 71, 62, 60, 59, 75, 62,
58, 66, 79, 71, 68, 44, 62, 36, 40, 27,
29, 44, 36, 32, 42, 48, 62, 46, 64, 104,
97, 96, 88, 85, 85, 85, 88, 66, 77, 76,
76, 58, 76, 83, 6, 19, 16, 14, 13, 11,
15, 16, 23, 23, 20, 26, 44, 40, 47, 17,
21, 22, 27, 29, 35, 50, 57, 63, 77, 82,
94, 69, 109, -35, -34, -26, -30, -32, -18, -15,
-15, -7, -5, 0, 2, 13, 35, 58, 6, 19,
16, 14, 13, 11, 15, 16, 23, 23, 20, 26,
44, 40, 47, 17, 21, 22, 27, 29, 35, 50,
57, 63, 77, 82, 94, 69, 109, -35, -34, -26,
-30, -32, -18, -15, -15, -7, -5, 0, 2, 13,
35, 58, 71, 42, 50, 54, 62, 58, 63, 72,
74, 91, 67, 27, 39, 44, 46, 64, 68, 78,
77, 86, 92, 55, 60, 62, 65, 73, 76, 80,
88, 110, 71, 42, 50, 54, 62, 58, 63, 72,
74, 91, 67, 27, 39, 44, 46, 64, 68, 78,
77, 86, 92, 55, 60, 62, 65, 73, 76, 80,
88, 110, 70, 93, 90, 127, 70, 93, 90, 127,
70, 93, 90, 127 };
public static int[][] cabac_context_init_PB_A = new int[][] { new int[] {
20, 2, 3, 20, 2, 3, -28, -23, -6, -1,
7, 23, 23, 21, 1, 0, -37, 5, -13, -11,
1, 12, -4, 17, 18, 9, 29, 26, 16, 9,
-46, -20, 1, -13, -11, 1, -6, -17, -6, 9,
-3, -6, -11, 6, 7, -5, 2, 0, -3, -10,
5, 4, -3, 0, -7, -5, -4, -5, -7, 1,
0, 0, 0, 0, -9, 4, 0, -7, 13, 3,
0, -4, -3, -27, -28, -25, -23, -28, -20, -16,
-22, -21, -18, -13, -29, -7, -5, -7, -13, -3,
-1, -1, -9, -3, -9, -8, -23, 5, 6, 6,
6, -1, 0, -4, -8, -2, -6, -1, -7, 2,
5, -3, 1, 6, -4, 1, -4, 0, 2, -2,
11, 4, 1, 11, 18, 12, 13, 13, -10, -7,
-2, 13, 9, -7, 9, 2, 5, -2, 0, 0,
-13, -5, -1, 4, -6, 4, 14, 4, 13, 3,
1, 9, 7, 16, 5, 4, 11, -5, -1, 0,
22, 5, 14, -1, 0, 9, 11, 2, 3, 0,
0, 2, 2, 0, 4, 2, 6, 0, 3, 2,
4, 6, 6, 7, 6, 6, 11, 14, 8, -1,
7, -3, 15, 22, -1, 25, 30, 28, 28, 32,
34, 30, 30, 32, 31, 26, 26, 37, 28, 17,
1, 5, 9, 16, 18, 18, 22, 24, 23, 18,
20, 11, 9, 9, -1, -2, -9, -6, -2, 0,
0, -3, -2, -4, -4, -8, -17, -9, 3, 0,
0, 0, -6, -7, -12, -11, -30, 1, -3, -1,
1, 2, -6, 0, 0, -3, -10, 0, -4, 5,
7, 1, -2, -3, -3, -11, 0, 8, 10, 14,
13, 2, 0, -3, -6, -8, 0, -13, -16, -10,
-21, -18, -14, -22, -21, -18, -21, -23, -26, -10,
-12, -5, -9, -22, -5, 9, -4, -10, -1, 7,
9, 5, 12, 15, 18, 17, 10, 7, -1, 7,
8, 9, 6, 2, 13, 10, 6, 5, 13, 4,
6, -2, -2, 6, 10, 9, 12, 3, 14, 10,
-3, 13, 17, 7, 7, 13, 10, 26, 14, 11,
9, 18, 21, 23, 32, 32, 34, 39, 42, 41,
46, 38, 21, 45, 53, 48, 65, 43, 39, 30,
18, 20, 0, -14, -5, -19, -35, 27, 28, 31,
27, 34, 30, 24, 33, 22, 26, 21, 26, 23,
16, 14, 8, 6, 17, 21, 23, 26, 27, 28,
28, 23, 24, 28, 23, 19, 22, 22, 11, 12,
11, 14, -4, -7, -5, -9, -8, -10, -19, -12,
-16, -15, -20, -19, -16, -22, -20, 9, 26, 33,
39, 41, 45, 49, 45, 36, -6, -7, -7, -8,
-5, -12, -6, -5, -8, -8, -5, -6, -10, -7,
-17, -18, -4, -10, -9, -9, -1, -8, -14, 0,
2, 21, 33, 39, 46, 51, 60, 61, 55, 42,
-7, -5, -7, -13, -3, -1, -1, -9, -3, -9,
-8, -23, -7, -5, -7, -13, -3, -1, -1, -9,
-3, -9, -8, -23, -2, -6, -1, -7, 2, 5,
-3, 1, 6, -4, 1, -4, 0, 2, -2, 11,
4, 1, 11, 18, 12, 13, 13, -10, -7, -2,
13, 9, -7, 9, 2, 5, -2, 0, 0, -13,
-5, -1, 4, -6, 4, 14, 4, 13, -2, -6,
-1, -7, 2, 5, -3, 1, 6, -4, 1, -4,
0, 2, -2, 11, 4, 1, 11, 18, 12, 13,
13, -10, -7, -2, 13, 9, -7, 9, 2, 5,
-2, 0, 0, -13, -5, -1, 4, -6, 4, 14,
4, 13, 11, 2, 3, 0, 0, 2, 2, 0,
4, 2, 6, 0, 3, 2, 4, 6, 6, 7,
6, 6, 11, 14, 8, -1, 7, -3, 15, 22,
-1, 25, 30, 28, 28, 32, 34, 30, 30, 32,
31, 26, 26, 37, 28, 17, 11, 2, 3, 0,
0, 2, 2, 0, 4, 2, 6, 0, 3, 2,
4, 6, 6, 7, 6, 6, 11, 14, 8, -1,
7, -3, 15, 22, -1, 25, 30, 28, 28, 32,
34, 30, 30, 32, 31, 26, 26, 37, 28, 17,
-4, -7, -5, -9, -8, -10, -19, -12, -16, -15,
-20, -19, -16, -22, -20, -5, -6, -10, -7, -17,
-18, -4, -10, -9, -9, -1, -8, -14, 0, 2,
9, 26, 33, 39, 41, 45, 49, 45, 36, 21,
33, 39, 46, 51, 60, 61, 55, 42, -6, -7,
-7, -8, -5, -12, -6, -5, -8, -8, -4, -7,
-5, -9, -8, -10, -19, -12, -16, -15, -20, -19,
-16, -22, -20, -5, -6, -10, -7, -17, -18, -4,
-10, -9, -9, -1, -8, -14, 0, 2, 9, 26,
33, 39, 41, 45, 49, 45, 36, 21, 33, 39,
46, 51, 60, 61, 55, 42, -6, -7, -7, -8,
-5, -12, -6, -5, -8, -8, -13, -16, -10, -21,
-18, -14, -22, -21, -18, -21, -23, -26, -10, -12,
-5, -9, -22, -5, 9, -4, -10, -1, 7, 9,
5, 12, 15, 18, 17, 10, 7, -1, 7, 8,
9, 6, 2, 13, 10, 6, 5, 13, 4, 6,
-13, -16, -10, -21, -18, -14, -22, -21, -18, -21,
-23, -26, -10, -12, -5, -9, -22, -5, 9, -4,
-10, -1, 7, 9, 5, 12, 15, 18, 17, 10,
7, -1, 7, 8, 9, 6, 2, 13, 10, 6,
5, 13, 4, 6, 14, 11, 9, 18, 21, 23,
32, 32, 34, 39, 42, 41, 46, 38, 21, 45,
53, 48, 65, 43, 39, 30, 18, 20, 0, -14,
-5, -19, -35, 27, 28, 31, 27, 34, 30, 24,
33, 22, 26, 21, 26, 23, 16, 14, 14, 11,
9, 18, 21, 23, 32, 32, 34, 39, 42, 41,
46, 38, 21, 45, 53, 48, 65, 43, 39, 30,
18, 20, 0, -14, -5, -19, -35, 27, 28, 31,
27, 34, 30, 24, 33, 22, 26, 21, 26, 23,
16, 14, -6, -2, 0, 0, -3, -2, -4, -4,
-8, -17, -9, 3, 0, 0, 0, -6, -7, -12,
-11, -30, 1, -3, -1, 1, 2, -6, 0, 0,
-3, -10, -6, -2, 0, 0, -3, -2, -4, -4,
-8, -17, -9, 3, 0, 0, 0, -6, -7, -12,
-11, -30, 1, -3, -1, 1, 2, -6, 0, 0,
-3, -10, -3, -9, -8, -23, -3, -9, -8, -23,
-3, -9, -8, -23 }, new int[] {
20, 2, 3, 20, 2, 3, -28, -23, -6, -1,
7, 22, 34, 16, -2, 4, -29, 2, -6, -13,
5, 9, -3, 10, 26, 19, 40, 57, 41, 26,
-45, -15, -4, -6, -13, 5, 6, -13, 0, 8,
-2, -5, -10, 2, 2, -3, -3, 1, -3, -6,
0, -3, -7, -5, -1, -1, 1, -2, -5, 0,
0, 0, 0, 0, -9, 4, 0, -7, 13, 3,
13, 7, 2, -39, -18, -17, -26, -35, -24, -23,
-27, -24, -21, -18, -36, 0, -5, -7, -4, 0,
0, -15, -35, -2, -12, -9, -31, 3, 7, 7,
8, -3, 0, -7, -9, -13, -13, -9, -14, -8,
-12, -23, -24, -10, -20, -17, -78, -70, -50, -46,
-4, -5, -4, -8, 2, -1, -7, -6, -8, -34,
-3, 32, 30, -44, 0, -5, 0, -1, -3, -8,
-25, -14, -5, 5, 2, 0, -9, -11, 18, -4,
0, 7, 9, 18, 9, 5, 9, 0, 0, 2,
19, -4, 15, 12, 9, 0, 4, 10, 10, 33,
52, 18, 28, 35, 38, 34, 39, 32, 102, 0,
56, 33, 29, 37, 51, 39, 52, 69, 67, 44,
32, 55, 32, 0, 27, 33, 34, 36, 38, 38,
34, 35, 34, 32, 37, 35, 30, 28, 26, 29,
0, 2, 8, 14, 18, 17, 21, 17, 20, 18,
27, 16, 7, 16, 11, 10, -10, -23, -15, -7,
0, -5, -11, -9, -9, -10, -34, -21, -3, -5,
-7, -11, -15, -17, -25, -25, -28, -11, -10, -10,
-10, -9, -16, -7, -4, -5, -9, 2, -9, 1,
11, 5, -2, -2, 0, -8, 3, 7, 10, 17,
16, 3, -1, -5, -1, -4, 0, -21, -23, -20,
-26, -25, -17, -27, -27, -17, -26, -27, -33, -10,
-14, -8, -17, -28, -6, -2, -4, -9, -8, -1,
5, 1, 9, 0, 1, 7, -7, -6, -16, -2,
2, -6, -3, 2, -3, -3, 0, 9, -1, -2,
-2, -1, -9, 14, 16, 0, 18, 11, 12, 10,
2, 12, 13, 0, 3, 19, 3, 18, 19, 18,
14, 26, 31, 33, 33, 37, 39, 42, 47, 45,
49, 41, 32, 69, 63, 66, 77, 54, 52, 41,
36, 40, 30, 28, 23, 12, 11, 37, 39, 40,
38, 46, 42, 40, 49, 38, 40, 38, 46, 31,
29, 25, 12, 11, 26, 22, 23, 27, 33, 26,
30, 27, 18, 25, 18, 12, 21, 14, 11, 25,
21, 21, -5, -6, -10, -7, -17, -18, -4, -10,
-9, -9, -1, -8, -14, 0, 2, 17, 32, 42,
49, 53, 64, 68, 66, 47, -5, 0, -1, -2,
-2, -9, -6, -4, -4, -7, -3, -3, -7, -6,
-12, -14, -3, -6, -5, -5, 0, -4, -9, 1,
2, 17, 32, 42, 49, 53, 64, 68, 66, 47,
0, -5, -7, -4, 0, 0, -15, -35, -2, -12,
-9, -31, 0, -5, -7, -4, 0, 0, -15, -35,
-2, -12, -9, -31, -13, -13, -9, -14, -8, -12,
-23, -24, -10, -20, -17, -78, -70, -50, -46, -4,
-5, -4, -8, 2, -1, -7, -6, -8, -34, -3,
32, 30, -44, 0, -5, 0, -1, -3, -8, -25,
-14, -5, 5, 2, 0, -9, -11, 18, -13, -13,
-9, -14, -8, -12, -23, -24, -10, -20, -17, -78,
-70, -50, -46, -4, -5, -4, -8, 2, -1, -7,
-6, -8, -34, -3, 32, 30, -44, 0, -5, 0,
-1, -3, -8, -25, -14, -5, 5, 2, 0, -9,
-11, 18, 4, 10, 10, 33, 52, 18, 28, 35,
38, 34, 39, 32, 102, 0, 56, 33, 29, 37,
51, 39, 52, 69, 67, 44, 32, 55, 32, 0,
27, 33, 34, 36, 38, 38, 34, 35, 34, 32,
37, 35, 30, 28, 26, 29, 4, 10, 10, 33,
52, 18, 28, 35, 38, 34, 39, 32, 102, 0,
56, 33, 29, 37, 51, 39, 52, 69, 67, 44,
32, 55, 32, 0, 27, 33, 34, 36, 38, 38,
34, 35, 34, 32, 37, 35, 30, 28, 26, 29,
-5, -6, -10, -7, -17, -18, -4, -10, -9, -9,
-1, -8, -14, 0, 2, -3, -3, -7, -6, -12,
-14, -3, -6, -5, -5, 0, -4, -9, 1, 2,
17, 32, 42, 49, 53, 64, 68, 66, 47, 17,
32, 42, 49, 53, 64, 68, 66, 47, -5, 0,
-1, -2, -2, -9, -6, -4, -4, -7, -5, -6,
-10, -7, -17, -18, -4, -10, -9, -9, -1, -8,
-14, 0, 2, -3, -3, -7, -6, -12, -14, -3,
-6, -5, -5, 0, -4, -9, 1, 2, 17, 32,
42, 49, 53, 64, 68, 66, 47, 17, 32, 42,
49, 53, 64, 68, 66, 47, -5, 0, -1, -2,
-2, -9, -6, -4, -4, -7, -21, -23, -20, -26,
-25, -17, -27, -27, -17, -26, -27, -33, -10, -14,
-8, -17, -28, -6, -2, -4, -9, -8, -1, 5,
1, 9, 0, 1, 7, -7, -6, -16, -2, 2,
-6, -3, 2, -3, -3, 0, 9, -1, -2, -2,
-21, -23, -20, -26, -25, -17, -27, -27, -17, -26,
-27, -33, -10, -14, -8, -17, -28, -6, -2, -4,
-9, -8, -1, 5, 1, 9, 0, 1, 7, -7,
-6, -16, -2, 2, -6, -3, 2, -3, -3, 0,
9, -1, -2, -2, 19, 18, 14, 26, 31, 33,
33, 37, 39, 42, 47, 45, 49, 41, 32, 69,
63, 66, 77, 54, 52, 41, 36, 40, 30, 28,
23, 12, 11, 37, 39, 40, 38, 46, 42, 40,
49, 38, 40, 38, 46, 31, 29, 25, 19, 18,
14, 26, 31, 33, 33, 37, 39, 42, 47, 45,
49, 41, 32, 69, 63, 66, 77, 54, 52, 41,
36, 40, 30, 28, 23, 12, 11, 37, 39, 40,
38, 46, 42, 40, 49, 38, 40, 38, 46, 31,
29, 25, -23, -15, -7, 0, -5, -11, -9, -9,
-10, -34, -21, -3, -5, -7, -11, -15, -17, -25,
-25, -28, -11, -10, -10, -10, -9, -16, -7, -4,
-5, -9, -23, -15, -7, 0, -5, -11, -9, -9,
-10, -34, -21, -3, -5, -7, -11, -15, -17, -25,
-25, -28, -11, -10, -10, -10, -9, -16, -7, -4,
-5, -9, -2, -12, -9, -31, -2, -12, -9, -31,
-2, -12, -9, -31 }, new int[] {
20, 2, 3, 20, 2, 3, -28, -23, -6, -1,
7, 29, 25, 14, -10, -3, -27, 26, -4, -24,
5, 6, -17, 14, 20, 20, 29, 54, 37, 12,
-32, -22, -2, -4, -24, 5, -6, -14, -6, 4,
-11, -15, -21, 19, 20, 4, 6, 1, -5, -13,
5, 6, -3, -1, 3, -4, -2, -12, -7, 1,
0, 0, 0, 0, -9, 4, 0, -7, 13, 3,
7, -9, -20, -36, -17, -14, -25, -25, -12, -17,
-31, -14, -18, -13, -37, 11, 5, 2, 5, -6,
4, -14, -37, -5, -11, -11, -30, 0, -2, 0,
-4, -6, 3, -8, -13, -4, -12, -5, -3, -4,
-8, -16, -9, -1, 5, 4, -4, -2, 2, -1,
-4, -1, 0, -7, -4, -6, -3, -6, 8, -9,
-11, 9, 0, -5, 1, -15, -5, -8, -21, -21,
-13, -25, -29, 9, 17, -8, -5, -2, 13, 3,
-7, 8, -10, 3, -3, -20, 0, 1, -3, -21,
16, -23, 17, 44, 50, -22, 4, 0, 7, 11,
8, 6, 7, 3, 8, 13, 13, 4, 3, 2,
6, 8, 11, 14, 7, 4, 4, 13, 9, 19,
10, 12, 0, 20, 8, 35, 33, 28, 24, 27,
34, 52, 39, 19, 31, 36, 24, 34, 30, 22,
20, 19, 27, 19, 15, 15, 21, 25, 30, 31,
27, 24, 0, 14, 15, 26, -24, -24, -22, -9,
0, 0, -14, -13, -13, -11, -29, -21, -14, -12,
-11, -10, -21, -16, -23, -15, -37, -10, -8, -8,
-8, -7, -14, -10, -9, -12, -18, -4, -22, -16,
-2, 1, -13, -9, -4, -13, -13, -6, -13, -6,
-2, -16, -10, -13, -9, -10, 0, -22, -25, -25,
-27, -19, -23, -25, -26, -24, -28, -31, -37, -10,
-15, -10, -13, -50, -5, 17, -5, -13, -12, -2,
0, -1, 4, -7, 5, 15, 1, 0, -10, 1,
0, 2, 0, -5, 7, 5, 2, 14, 15, 5,
2, -2, -18, 12, 5, -12, 11, 5, 0, 2,
-6, 5, 7, -6, -11, -2, -2, 25, 17, 16,
17, 27, 37, 41, 42, 48, 39, 46, 52, 46,
52, 43, 32, 61, 56, 62, 81, 45, 35, 28,
34, 39, 30, 20, 18, 15, 0, 36, 37, 37,
32, 34, 29, 24, 34, 31, 35, 31, 33, 36,
27, 21, 18, 19, 36, 24, 27, 24, 31, 22,
22, 16, 15, 14, 3, -16, 21, 22, 25, 21,
19, 17, -3, -8, -9, -10, -18, -12, -11, -5,
-17, -14, -16, -8, -14, -9, -11, 9, 30, 31,
33, 33, 31, 37, 31, 20, -9, -7, -8, -11,
-10, -12, -8, -9, -6, -10, -3, -8, -9, -10,
-18, -12, -11, -5, -17, -14, -16, -8, -14, -9,
-11, 9, 30, 31, 33, 33, 31, 37, 31, 20,
11, 5, 2, 5, -6, 4, -14, -37, -5, -11,
-11, -30, 11, 5, 2, 5, -6, 4, -14, -37,
-5, -11, -11, -30, -4, -12, -5, -3, -4, -8,
-16, -9, -1, 5, 4, -4, -2, 2, -1, -4,
-1, 0, -7, -4, -6, -3, -6, 8, -9, -11,
9, 0, -5, 1, -15, -5, -8, -21, -21, -13,
-25, -29, 9, 17, -8, -5, -2, 13, -4, -12,
-5, -3, -4, -8, -16, -9, -1, 5, 4, -4,
-2, 2, -1, -4, -1, 0, -7, -4, -6, -3,
-6, 8, -9, -11, 9, 0, -5, 1, -15, -5,
-8, -21, -21, -13, -25, -29, 9, 17, -8, -5,
-2, 13, 4, 0, 7, 11, 8, 6, 7, 3,
8, 13, 13, 4, 3, 2, 6, 8, 11, 14,
7, 4, 4, 13, 9, 19, 10, 12, 0, 20,
8, 35, 33, 28, 24, 27, 34, 52, 39, 19,
31, 36, 24, 34, 30, 22, 4, 0, 7, 11,
8, 6, 7, 3, 8, 13, 13, 4, 3, 2,
6, 8, 11, 14, 7, 4, 4, 13, 9, 19,
10, 12, 0, 20, 8, 35, 33, 28, 24, 27,
34, 52, 39, 19, 31, 36, 24, 34, 30, 22,
-3, -8, -9, -10, -18, -12, -11, -5, -17, -14,
-16, -8, -14, -9, -11, -3, -8, -9, -10, -18,
-12, -11, -5, -17, -14, -16, -8, -14, -9, -11,
9, 30, 31, 33, 33, 31, 37, 31, 20, 9,
30, 31, 33, 33, 31, 37, 31, 20, -9, -7,
-8, -11, -10, -12, -8, -9, -6, -10, -3, -8,
-9, -10, -18, -12, -11, -5, -17, -14, -16, -8,
-14, -9, -11, -3, -8, -9, -10, -18, -12, -11,
-5, -17, -14, -16, -8, -14, -9, -11, 9, 30,
31, 33, 33, 31, 37, 31, 20, 9, 30, 31,
33, 33, 31, 37, 31, 20, -9, -7, -8, -11,
-10, -12, -8, -9, -6, -10, -22, -25, -25, -27,
-19, -23, -25, -26, -24, -28, -31, -37, -10, -15,
-10, -13, -50, -5, 17, -5, -13, -12, -2, 0,
-1, 4, -7, 5, 15, 1, 0, -10, 1, 0,
2, 0, -5, 7, 5, 2, 14, 15, 5, 2,
-22, -25, -25, -27, -19, -23, -25, -26, -24, -28,
-31, -37, -10, -15, -10, -13, -50, -5, 17, -5,
-13, -12, -2, 0, -1, 4, -7, 5, 15, 1,
0, -10, 1, 0, 2, 0, -5, 7, 5, 2,
14, 15, 5, 2, 17, 16, 17, 27, 37, 41,
42, 48, 39, 46, 52, 46, 52, 43, 32, 61,
56, 62, 81, 45, 35, 28, 34, 39, 30, 20,
18, 15, 0, 36, 37, 37, 32, 34, 29, 24,
34, 31, 35, 31, 33, 36, 27, 21, 17, 16,
17, 27, 37, 41, 42, 48, 39, 46, 52, 46,
52, 43, 32, 61, 56, 62, 81, 45, 35, 28,
34, 39, 30, 20, 18, 15, 0, 36, 37, 37,
32, 34, 29, 24, 34, 31, 35, 31, 33, 36,
27, 21, -24, -22, -9, 0, 0, -14, -13, -13,
-11, -29, -21, -14, -12, -11, -10, -21, -16, -23,
-15, -37, -10, -8, -8, -8, -7, -14, -10, -9,
-12, -18, -24, -22, -9, 0, 0, -14, -13, -13,
-11, -29, -21, -14, -12, -11, -10, -21, -16, -23,
-15, -37, -10, -8, -8, -8, -7, -14, -10, -9,
-12, -18, -5, -11, -11, -30, -5, -11, -11, -30,
-5, -11, -11, -30 } };
public static int[][] cabac_context_init_PB_B = new int[][] { new int[] {
-15, 54, 74, -15, 54, 74, 127, 104, 53, 54,
51, 33, 2, 0, 9, 49, 118, 57, 78, 65,
62, 49, 73, 50, 64, 43, 0, 67, 90, 104,
127, 104, 67, 78, 65, 62, 86, 95, 61, 45,
69, 81, 96, 55, 67, 86, 88, 58, 76, 94,
54, 69, 81, 88, 67, 74, 74, 80, 72, 58,
41, 63, 63, 63, 83, 86, 97, 72, 41, 62,
45, 78, 96, 126, 98, 101, 67, 82, 94, 83,
110, 91, 102, 93, 127, 92, 89, 96, 108, 46,
65, 57, 93, 74, 92, 87, 126, 54, 60, 59,
69, 48, 68, 69, 88, 85, 78, 75, 77, 54,
50, 68, 50, 42, 81, 63, 70, 67, 57, 76,
35, 64, 61, 35, 25, 24, 29, 36, 93, 73,
73, 46, 49, 100, 53, 53, 53, 61, 56, 56,
63, 60, 62, 57, 69, 57, 39, 51, 68, 64,
61, 63, 50, 39, 44, 52, 48, 60, 59, 59,
33, 44, 43, 78, 60, 69, 28, 40, 44, 49,
46, 44, 51, 47, 39, 62, 46, 54, 54, 58,
63, 51, 57, 53, 52, 55, 45, 36, 53, 82,
55, 78, 46, 31, 84, 7, -7, 3, 4, 0,
-1, 6, 6, 9, 19, 27, 30, 20, 34, 70,
67, 59, 67, 30, 32, 35, 29, 31, 38, 43,
41, 63, 59, 64, 94, 89, 108, 76, 44, 45,
52, 64, 59, 70, 75, 82, 102, 77, 24, 42,
48, 55, 59, 71, 83, 87, 119, 58, 29, 36,
38, 43, 55, 58, 64, 74, 90, 70, 29, 31,
42, 59, 58, 72, 81, 97, 58, 5, 14, 18,
27, 40, 58, 70, 79, 85, 0, 106, 106, 87,
114, 110, 98, 110, 106, 103, 107, 108, 112, 96,
95, 91, 93, 94, 86, 67, 80, 85, 70, 60,
58, 61, 50, 50, 49, 54, 41, 46, 51, 49,
52, 41, 47, 55, 41, 44, 50, 53, 49, 63,
64, 69, 59, 70, 44, 31, 43, 53, 34, 38,
52, 40, 32, 44, 38, 50, 57, 43, 11, 14,
11, 11, 9, -2, -15, -15, -21, -23, -33, -31,
-28, -12, 29, -24, -45, -26, -43, -19, -10, 9,
26, 27, 57, 82, 75, 97, 125, 0, 0, -4,
6, 8, 10, 22, 19, 32, 31, 41, 44, 47,
65, 71, 60, 63, 65, 24, 20, 23, 32, 23,
24, 40, 32, 29, 42, 57, 53, 61, 86, 40,
51, 59, 79, 71, 69, 70, 66, 68, 73, 69,
70, 67, 62, 70, 66, 65, 63, -2, -9, -9,
-7, -2, 3, 9, 27, 59, 66, 35, 42, 45,
48, 56, 60, 62, 66, 76, 85, 81, 77, 81,
80, 73, 74, 83, 71, 67, 61, 66, 66, 59,
59, -13, -14, -7, -2, 2, 6, 17, 34, 62,
92, 89, 96, 108, 46, 65, 57, 93, 74, 92,
87, 126, 92, 89, 96, 108, 46, 65, 57, 93,
74, 92, 87, 126, 85, 78, 75, 77, 54, 50,
68, 50, 42, 81, 63, 70, 67, 57, 76, 35,
64, 61, 35, 25, 24, 29, 36, 93, 73, 73,
46, 49, 100, 53, 53, 53, 61, 56, 56, 63,
60, 62, 57, 69, 57, 39, 51, 68, 85, 78,
75, 77, 54, 50, 68, 50, 42, 81, 63, 70,
67, 57, 76, 35, 64, 61, 35, 25, 24, 29,
36, 93, 73, 73, 46, 49, 100, 53, 53, 53,
61, 56, 56, 63, 60, 62, 57, 69, 57, 39,
51, 68, 28, 40, 44, 49, 46, 44, 51, 47,
39, 62, 46, 54, 54, 58, 63, 51, 57, 53,
52, 55, 45, 36, 53, 82, 55, 78, 46, 31,
84, 7, -7, 3, 4, 0, -1, 6, 6, 9,
19, 27, 30, 20, 34, 70, 28, 40, 44, 49,
46, 44, 51, 47, 39, 62, 46, 54, 54, 58,
63, 51, 57, 53, 52, 55, 45, 36, 53, 82,
55, 78, 46, 31, 84, 7, -7, 3, 4, 0,
-1, 6, 6, 9, 19, 27, 30, 20, 34, 70,
79, 71, 69, 70, 66, 68, 73, 69, 70, 67,
62, 70, 66, 65, 63, 85, 81, 77, 81, 80,
73, 74, 83, 71, 67, 61, 66, 66, 59, 59,
-2, -9, -9, -7, -2, 3, 9, 27, 59, -13,
-14, -7, -2, 2, 6, 17, 34, 62, 66, 35,
42, 45, 48, 56, 60, 62, 66, 76, 79, 71,
69, 70, 66, 68, 73, 69, 70, 67, 62, 70,
66, 65, 63, 85, 81, 77, 81, 80, 73, 74,
83, 71, 67, 61, 66, 66, 59, 59, -2, -9,
-9, -7, -2, 3, 9, 27, 59, -13, -14, -7,
-2, 2, 6, 17, 34, 62, 66, 35, 42, 45,
48, 56, 60, 62, 66, 76, 106, 106, 87, 114,
110, 98, 110, 106, 103, 107, 108, 112, 96, 95,
91, 93, 94, 86, 67, 80, 85, 70, 60, 58,
61, 50, 50, 49, 54, 41, 46, 51, 49, 52,
41, 47, 55, 41, 44, 50, 53, 49, 63, 64,
106, 106, 87, 114, 110, 98, 110, 106, 103, 107,
108, 112, 96, 95, 91, 93, 94, 86, 67, 80,
85, 70, 60, 58, 61, 50, 50, 49, 54, 41,
46, 51, 49, 52, 41, 47, 55, 41, 44, 50,
53, 49, 63, 64, 11, 14, 11, 11, 9, -2,
-15, -15, -21, -23, -33, -31, -28, -12, 29, -24,
-45, -26, -43, -19, -10, 9, 26, 27, 57, 82,
75, 97, 125, 0, 0, -4, 6, 8, 10, 22,
19, 32, 31, 41, 44, 47, 65, 71, 11, 14,
11, 11, 9, -2, -15, -15, -21, -23, -33, -31,
-28, -12, 29, -24, -45, -26, -43, -19, -10, 9,
26, 27, 57, 82, 75, 97, 125, 0, 0, -4,
6, 8, 10, 22, 19, 32, 31, 41, 44, 47,
65, 71, 76, 44, 45, 52, 64, 59, 70, 75,
82, 102, 77, 24, 42, 48, 55, 59, 71, 83,
87, 119, 58, 29, 36, 38, 43, 55, 58, 64,
74, 90, 76, 44, 45, 52, 64, 59, 70, 75,
82, 102, 77, 24, 42, 48, 55, 59, 71, 83,
87, 119, 58, 29, 36, 38, 43, 55, 58, 64,
74, 90, 74, 92, 87, 126, 74, 92, 87, 126,
74, 92, 87, 126 }, new int[] {
-15, 54, 74, -15, 54, 74, 127, 104, 53, 54,
51, 25, 0, 0, 9, 41, 118, 65, 71, 79,
52, 50, 70, 54, 34, 22, 0, 2, 36, 69,
127, 101, 76, 71, 79, 52, 69, 90, 52, 43,
69, 82, 96, 59, 75, 87, 100, 56, 74, 85,
59, 81, 86, 95, 66, 77, 70, 86, 72, 61,
41, 63, 63, 63, 83, 86, 97, 72, 41, 62,
15, 51, 80, 127, 91, 96, 81, 98, 102, 97,
119, 99, 110, 102, 127, 80, 89, 94, 92, 39,
65, 84, 127, 73, 104, 91, 127, 55, 56, 55,
61, 53, 68, 74, 88, 103, 91, 89, 92, 76,
87, 110, 105, 78, 112, 99, 127, 127, 127, 127,
66, 78, 71, 72, 59, 55, 70, 75, 89, 119,
75, 20, 22, 127, 54, 61, 58, 60, 61, 67,
84, 74, 65, 52, 57, 61, 69, 70, 55, 71,
58, 61, 41, 25, 32, 43, 47, 44, 51, 46,
38, 66, 38, 42, 34, 89, 45, 28, 31, -11,
-43, 15, 0, -22, -25, 0, -18, -12, -94, 0,
-15, -4, 10, -5, -29, -9, -34, -58, -63, -5,
7, -29, 1, 0, 36, -25, -30, -28, -28, -27,
-18, -16, -14, -8, -6, 0, 10, 18, 25, 41,
75, 72, 77, 35, 31, 35, 30, 45, 42, 45,
26, 54, 66, 56, 73, 67, 116, 112, 71, 61,
53, 66, 77, 80, 84, 87, 127, 101, 39, 53,
61, 75, 77, 91, 107, 111, 122, 76, 44, 52,
57, 58, 72, 69, 69, 74, 86, 66, 34, 32,
31, 52, 55, 67, 73, 89, 52, 4, 8, 8,
19, 37, 61, 73, 70, 78, 0, 126, 124, 110,
126, 124, 105, 121, 117, 102, 117, 116, 122, 95,
100, 95, 111, 114, 89, 80, 82, 85, 81, 72,
64, 67, 56, 69, 69, 69, 69, 67, 77, 64,
61, 67, 64, 57, 65, 66, 62, 51, 66, 71,
75, 70, 72, 60, 37, 47, 35, 37, 41, 41,
48, 41, 41, 59, 50, 40, 66, 50, -6, -6,
0, -12, -16, -25, -22, -28, -30, -30, -42, -36,
-34, -17, 9, -71, -63, -64, -74, -39, -35, -10,
0, -1, 14, 26, 37, 55, 65, -33, -36, -37,
-30, -33, -30, -24, -29, -12, -10, -3, -5, 20,
30, 44, 48, 49, 45, 22, 22, 21, 20, 28,
24, 34, 42, 39, 50, 70, 54, 71, 83, 32,
49, 54, 85, 81, 77, 81, 80, 73, 74, 83,
71, 67, 61, 66, 66, 59, 59, -10, -13, -9,
-5, 0, 3, 10, 27, 57, 71, 24, 36, 42,
52, 57, 63, 65, 67, 82, 81, 76, 72, 78,
72, 68, 70, 76, 66, 62, 57, 61, 60, 54,
58, -10, -13, -9, -5, 0, 3, 10, 27, 57,
80, 89, 94, 92, 39, 65, 84, 127, 73, 104,
91, 127, 80, 89, 94, 92, 39, 65, 84, 127,
73, 104, 91, 127, 103, 91, 89, 92, 76, 87,
110, 105, 78, 112, 99, 127, 127, 127, 127, 66,
78, 71, 72, 59, 55, 70, 75, 89, 119, 75,
20, 22, 127, 54, 61, 58, 60, 61, 67, 84,
74, 65, 52, 57, 61, 69, 70, 55, 103, 91,
89, 92, 76, 87, 110, 105, 78, 112, 99, 127,
127, 127, 127, 66, 78, 71, 72, 59, 55, 70,
75, 89, 119, 75, 20, 22, 127, 54, 61, 58,
60, 61, 67, 84, 74, 65, 52, 57, 61, 69,
70, 55, 45, 28, 31, -11, -43, 15, 0, -22,
-25, 0, -18, -12, -94, 0, -15, -4, 10, -5,
-29, -9, -34, -58, -63, -5, 7, -29, 1, 0,
36, -25, -30, -28, -28, -27, -18, -16, -14, -8,
-6, 0, 10, 18, 25, 41, 45, 28, 31, -11,
-43, 15, 0, -22, -25, 0, -18, -12, -94, 0,
-15, -4, 10, -5, -29, -9, -34, -58, -63, -5,
7, -29, 1, 0, 36, -25, -30, -28, -28, -27,
-18, -16, -14, -8, -6, 0, 10, 18, 25, 41,
85, 81, 77, 81, 80, 73, 74, 83, 71, 67,
61, 66, 66, 59, 59, 81, 76, 72, 78, 72,
68, 70, 76, 66, 62, 57, 61, 60, 54, 58,
-10, -13, -9, -5, 0, 3, 10, 27, 57, -10,
-13, -9, -5, 0, 3, 10, 27, 57, 71, 24,
36, 42, 52, 57, 63, 65, 67, 82, 85, 81,
77, 81, 80, 73, 74, 83, 71, 67, 61, 66,
66, 59, 59, 81, 76, 72, 78, 72, 68, 70,
76, 66, 62, 57, 61, 60, 54, 58, -10, -13,
-9, -5, 0, 3, 10, 27, 57, -10, -13, -9,
-5, 0, 3, 10, 27, 57, 71, 24, 36, 42,
52, 57, 63, 65, 67, 82, 126, 124, 110, 126,
124, 105, 121, 117, 102, 117, 116, 122, 95, 100,
95, 111, 114, 89, 80, 82, 85, 81, 72, 64,
67, 56, 69, 69, 69, 69, 67, 77, 64, 61,
67, 64, 57, 65, 66, 62, 51, 66, 71, 75,
126, 124, 110, 126, 124, 105, 121, 117, 102, 117,
116, 122, 95, 100, 95, 111, 114, 89, 80, 82,
85, 81, 72, 64, 67, 56, 69, 69, 69, 69,
67, 77, 64, 61, 67, 64, 57, 65, 66, 62,
51, 66, 71, 75, -6, -6, 0, -12, -16, -25,
-22, -28, -30, -30, -42, -36, -34, -17, 9, -71,
-63, -64, -74, -39, -35, -10, 0, -1, 14, 26,
37, 55, 65, -33, -36, -37, -30, -33, -30, -24,
-29, -12, -10, -3, -5, 20, 30, 44, -6, -6,
0, -12, -16, -25, -22, -28, -30, -30, -42, -36,
-34, -17, 9, -71, -63, -64, -74, -39, -35, -10,
0, -1, 14, 26, 37, 55, 65, -33, -36, -37,
-30, -33, -30, -24, -29, -12, -10, -3, -5, 20,
30, 44, 112, 71, 61, 53, 66, 77, 80, 84,
87, 127, 101, 39, 53, 61, 75, 77, 91, 107,
111, 122, 76, 44, 52, 57, 58, 72, 69, 69,
74, 86, 112, 71, 61, 53, 66, 77, 80, 84,
87, 127, 101, 39, 53, 61, 75, 77, 91, 107,
111, 122, 76, 44, 52, 57, 58, 72, 69, 69,
74, 86, 73, 104, 91, 127, 73, 104, 91, 127,
73, 104, 91, 127 }, new int[] {
-15, 54, 74, -15, 54, 74, 127, 104, 53, 54,
51, 16, 0, 0, 51, 62, 99, 16, 85, 102,
57, 57, 73, 57, 40, 10, 0, 0, 42, 97,
127, 117, 74, 85, 102, 57, 93, 88, 44, 55,
89, 103, 116, 57, 58, 84, 96, 63, 85, 106,
63, 75, 90, 101, 55, 79, 75, 97, 50, 60,
41, 63, 63, 63, 83, 86, 97, 72, 41, 62,
34, 88, 127, 127, 91, 95, 84, 86, 89, 91,
127, 76, 103, 90, 127, 80, 76, 84, 78, 55,
61, 83, 127, 79, 104, 91, 127, 65, 79, 72,
92, 56, 68, 71, 98, 86, 88, 82, 72, 67,
72, 89, 69, 59, 66, 57, 71, 71, 58, 74,
44, 69, 62, 51, 47, 42, 41, 53, 76, 78,
83, 52, 67, 90, 67, 72, 75, 80, 83, 64,
31, 64, 94, 75, 63, 74, 35, 27, 91, 65,
69, 77, 66, 62, 68, 81, 30, 7, 23, 74,
66, 124, 37, -18, -34, 127, 39, 42, 34, 29,
31, 37, 42, 40, 33, 43, 36, 47, 55, 58,
60, 44, 44, 42, 48, 56, 52, 37, 49, 58,
48, 45, 69, 33, 63, -18, -25, -3, 10, 0,
-14, -44, -24, 17, 25, 29, 33, 15, 20, 73,
34, 31, 44, 16, 36, 36, 28, 21, 20, 12,
16, 42, 93, 56, 57, 38, 127, 115, 82, 62,
53, 59, 85, 89, 94, 92, 127, 100, 57, 67,
71, 77, 85, 88, 104, 98, 127, 82, 48, 61,
66, 70, 75, 79, 83, 92, 108, 79, 69, 75,
58, 58, 78, 83, 81, 99, 81, 38, 62, 58,
59, 73, 76, 86, 83, 87, 0, 127, 127, 120,
127, 114, 117, 118, 117, 113, 118, 120, 124, 94,
102, 99, 106, 127, 92, 57, 86, 94, 91, 77,
71, 73, 64, 81, 64, 57, 67, 68, 67, 68,
77, 64, 68, 78, 55, 59, 65, 54, 44, 60,
70, 76, 86, 70, 64, 70, 55, 56, 69, 65,
74, 54, 54, 76, 82, 77, 77, 42, -13, -9,
-12, -21, -30, -40, -41, -47, -32, -40, -51, -41,
-39, -19, 11, -55, -46, -50, -67, -20, -2, 15,
1, 1, 17, 38, 45, 54, 79, -16, -14, -17,
1, 15, 15, 25, 22, 16, 18, 28, 41, 28,
47, 62, 31, 26, 24, 23, 16, 30, 29, 41,
42, 60, 52, 60, 78, 123, 53, 56, 61, 33,
50, 61, 78, 74, 72, 72, 75, 71, 63, 70,
75, 72, 67, 53, 59, 52, 68, -2, -10, -4,
-1, 7, 12, 23, 38, 64, 71, 37, 44, 49,
56, 59, 63, 67, 68, 79, 78, 74, 72, 72,
75, 71, 63, 70, 75, 72, 67, 53, 59, 52,
68, -2, -10, -4, -1, 7, 12, 23, 38, 64,
80, 76, 84, 78, 55, 61, 83, 127, 79, 104,
91, 127, 80, 76, 84, 78, 55, 61, 83, 127,
79, 104, 91, 127, 86, 88, 82, 72, 67, 72,
89, 69, 59, 66, 57, 71, 71, 58, 74, 44,
69, 62, 51, 47, 42, 41, 53, 76, 78, 83,
52, 67, 90, 67, 72, 75, 80, 83, 64, 31,
64, 94, 75, 63, 74, 35, 27, 91, 86, 88,
82, 72, 67, 72, 89, 69, 59, 66, 57, 71,
71, 58, 74, 44, 69, 62, 51, 47, 42, 41,
53, 76, 78, 83, 52, 67, 90, 67, 72, 75,
80, 83, 64, 31, 64, 94, 75, 63, 74, 35,
27, 91, 39, 42, 34, 29, 31, 37, 42, 40,
33, 43, 36, 47, 55, 58, 60, 44, 44, 42,
48, 56, 52, 37, 49, 58, 48, 45, 69, 33,
63, -18, -25, -3, 10, 0, -14, -44, -24, 17,
25, 29, 33, 15, 20, 73, 39, 42, 34, 29,
31, 37, 42, 40, 33, 43, 36, 47, 55, 58,
60, 44, 44, 42, 48, 56, 52, 37, 49, 58,
48, 45, 69, 33, 63, -18, -25, -3, 10, 0,
-14, -44, -24, 17, 25, 29, 33, 15, 20, 73,
78, 74, 72, 72, 75, 71, 63, 70, 75, 72,
67, 53, 59, 52, 68, 78, 74, 72, 72, 75,
71, 63, 70, 75, 72, 67, 53, 59, 52, 68,
-2, -10, -4, -1, 7, 12, 23, 38, 64, -2,
-10, -4, -1, 7, 12, 23, 38, 64, 71, 37,
44, 49, 56, 59, 63, 67, 68, 79, 78, 74,
72, 72, 75, 71, 63, 70, 75, 72, 67, 53,
59, 52, 68, 78, 74, 72, 72, 75, 71, 63,
70, 75, 72, 67, 53, 59, 52, 68, -2, -10,
-4, -1, 7, 12, 23, 38, 64, -2, -10, -4,
-1, 7, 12, 23, 38, 64, 71, 37, 44, 49,
56, 59, 63, 67, 68, 79, 127, 127, 120, 127,
114, 117, 118, 117, 113, 118, 120, 124, 94, 102,
99, 106, 127, 92, 57, 86, 94, 91, 77, 71,
73, 64, 81, 64, 57, 67, 68, 67, 68, 77,
64, 68, 78, 55, 59, 65, 54, 44, 60, 70,
127, 127, 120, 127, 114, 117, 118, 117, 113, 118,
120, 124, 94, 102, 99, 106, 127, 92, 57, 86,
94, 91, 77, 71, 73, 64, 81, 64, 57, 67,
68, 67, 68, 77, 64, 68, 78, 55, 59, 65,
54, 44, 60, 70, -13, -9, -12, -21, -30, -40,
-41, -47, -32, -40, -51, -41, -39, -19, 11, -55,
-46, -50, -67, -20, -2, 15, 1, 1, 17, 38,
45, 54, 79, -16, -14, -17, 1, 15, 15, 25,
22, 16, 18, 28, 41, 28, 47, 62, -13, -9,
-12, -21, -30, -40, -41, -47, -32, -40, -51, -41,
-39, -19, 11, -55, -46, -50, -67, -20, -2, 15,
1, 1, 17, 38, 45, 54, 79, -16, -14, -17,
1, 15, 15, 25, 22, 16, 18, 28, 41, 28,
47, 62, 115, 82, 62, 53, 59, 85, 89, 94,
92, 127, 100, 57, 67, 71, 77, 85, 88, 104,
98, 127, 82, 48, 61, 66, 70, 75, 79, 83,
92, 108, 115, 82, 62, 53, 59, 85, 89, 94,
92, 127, 100, 57, 67, 71, 77, 85, 88, 104,
98, 127, 82, 48, 61, 66, 70, 75, 79, 83,
92, 108, 79, 104, 91, 127, 79, 104, 91, 127,
79, 104, 91, 127 } };
}

View file

@ -0,0 +1,71 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Utils2;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.tools.Debug;
public class CAVLCReader {
public static int readNBit(BitReader bits, int n, String message) {
int val = bits.readNBit(n);
Debug.trace(message, val);
return val;
}
public static int readUE(BitReader bits) {
int cnt = 0;
while (bits.read1Bit() == 0 && cnt < 32)
cnt++;
int res = 0;
if (cnt > 0) {
long val = (long)bits.readNBit(cnt);
res = (int)((long)((1 << cnt) - 1) + val);
}
return res;
}
public static int readUEtrace(BitReader bits, String message) {
int res = readUE(bits);
Debug.trace(message, res);
return res;
}
public static int readSE(BitReader bits, String message) {
int val = readUE(bits);
val = H264Utils2.golomb2Signed(val);
Debug.trace(message, val);
return val;
}
public static boolean readBool(BitReader bits, String message) {
boolean res = !(bits.read1Bit() == 0);
Debug.trace(message, res ? 1 : 0);
return res;
}
public static int readU(BitReader bits, int i, String string) {
return readNBit(bits, i, string);
}
public static int readTE(BitReader bits, int max) {
if (max > 1)
return readUE(bits);
return (bits.read1Bit() ^ 0xFFFFFFFF) & 0x1;
}
public static int readME(BitReader bits, String string) {
return readUEtrace(bits, string);
}
public static int readZeroBitCount(BitReader bits, String message) {
int count = 0;
while (bits.read1Bit() == 0 && count < 32)
count++;
if (Debug.debug)
Debug.trace(message, String.valueOf(count));
return count;
}
public static boolean moreRBSPData(BitReader bits) {
return (bits.remaining() >= 32 || bits.checkNBit(1) != 1 || bits.checkNBit(24) << 9 != 0);
}
}

View file

@ -0,0 +1,143 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.common.tools.MathUtil;
public class ChromaPredictionBuilder {
public static void predictWithMode(int[][] residual, int chromaMode, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, byte[] pixOut) {
switch (chromaMode) {
case 0:
predictDC(residual, mbX, leftAvailable, topAvailable, leftRow, topLine, pixOut);
break;
case 1:
predictHorizontal(residual, mbX, leftAvailable, leftRow, pixOut);
break;
case 2:
predictVertical(residual, mbX, topAvailable, topLine, pixOut);
break;
case 3:
predictPlane(residual, mbX, leftAvailable, topAvailable, leftRow, topLine, topLeft, pixOut);
break;
}
}
public static void predictDC(int[][] planeData, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] pixOut) {
predictDCInside(planeData, 0, 0, mbX, leftAvailable, topAvailable, leftRow, topLine, pixOut);
predictDCTopBorder(planeData, 1, 0, mbX, leftAvailable, topAvailable, leftRow, topLine, pixOut);
predictDCLeftBorder(planeData, 0, 1, mbX, leftAvailable, topAvailable, leftRow, topLine, pixOut);
predictDCInside(planeData, 1, 1, mbX, leftAvailable, topAvailable, leftRow, topLine, pixOut);
}
public static void predictVertical(int[][] residual, int mbX, boolean topAvailable, byte[] topLine, byte[] pixOut) {
for (int off = 0, j = 0; j < 8; j++) {
for (int i = 0; i < 8; i++, off++)
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + topLine[(mbX << 3) + i], -128, 127);
}
}
public static void predictHorizontal(int[][] residual, int mbX, boolean leftAvailable, byte[] leftRow, byte[] pixOut) {
for (int off = 0, j = 0; j < 8; j++) {
for (int i = 0; i < 8; i++, off++)
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + leftRow[j], -128, 127);
}
}
public static void predictDCInside(int[][] residual, int blkX, int blkY, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] pixOut) {
int s0;
int blkOffX = (blkX << 2) + (mbX << 3), blkOffY = blkY << 2;
if (leftAvailable && topAvailable) {
s0 = 0;
for (int k = 0; k < 4; k++)
s0 += leftRow[k + blkOffY];
for (int i = 0; i < 4; i++)
s0 += topLine[blkOffX + i];
s0 = s0 + 4 >> 3;
} else if (leftAvailable) {
s0 = 0;
for (int i = 0; i < 4; i++)
s0 += leftRow[blkOffY + i];
s0 = s0 + 2 >> 2;
} else if (topAvailable) {
s0 = 0;
for (int i = 0; i < 4; i++)
s0 += topLine[blkOffX + i];
s0 = s0 + 2 >> 2;
} else {
s0 = 0;
}
for (int off = (blkY << 5) + (blkX << 2), j = 0; j < 4; j++, off += 8) {
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + s0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 1]][H264Const.CHROMA_POS_LUT[off + 1]] + s0, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 2]][H264Const.CHROMA_POS_LUT[off + 2]] + s0, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 3]][H264Const.CHROMA_POS_LUT[off + 3]] + s0, -128, 127);
}
}
public static void predictDCTopBorder(int[][] residual, int blkX, int blkY, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] pixOut) {
int s1;
int blkOffX = (blkX << 2) + (mbX << 3), blkOffY = blkY << 2;
if (topAvailable) {
s1 = 0;
for (int i = 0; i < 4; i++)
s1 += topLine[blkOffX + i];
s1 = s1 + 2 >> 2;
} else if (leftAvailable) {
s1 = 0;
for (int i = 0; i < 4; i++)
s1 += leftRow[blkOffY + i];
s1 = s1 + 2 >> 2;
} else {
s1 = 0;
}
for (int off = (blkY << 5) + (blkX << 2), j = 0; j < 4; j++, off += 8) {
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + s1, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 1]][H264Const.CHROMA_POS_LUT[off + 1]] + s1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 2]][H264Const.CHROMA_POS_LUT[off + 2]] + s1, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 3]][H264Const.CHROMA_POS_LUT[off + 3]] + s1, -128, 127);
}
}
public static void predictDCLeftBorder(int[][] residual, int blkX, int blkY, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] pixOut) {
int s2;
int blkOffX = (blkX << 2) + (mbX << 3), blkOffY = blkY << 2;
if (leftAvailable) {
s2 = 0;
for (int i = 0; i < 4; i++)
s2 += leftRow[blkOffY + i];
s2 = s2 + 2 >> 2;
} else if (topAvailable) {
s2 = 0;
for (int i = 0; i < 4; i++)
s2 += topLine[blkOffX + i];
s2 = s2 + 2 >> 2;
} else {
s2 = 0;
}
for (int off = (blkY << 5) + (blkX << 2), j = 0; j < 4; j++, off += 8) {
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + s2, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 1]][H264Const.CHROMA_POS_LUT[off + 1]] + s2, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 2]][H264Const.CHROMA_POS_LUT[off + 2]] + s2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off + 3]][H264Const.CHROMA_POS_LUT[off + 3]] + s2, -128, 127);
}
}
public static void predictPlane(int[][] residual, int mbX, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, byte[] pixOut) {
int H = 0, blkOffX = mbX << 3;
for (int i = 0; i < 3; i++)
H += (i + 1) * (topLine[blkOffX + 4 + i] - topLine[blkOffX + 2 - i]);
H += 4 * (topLine[blkOffX + 7] - topLeft[0]);
int V = 0;
for (int j = 0; j < 3; j++)
V += (j + 1) * (leftRow[4 + j] - leftRow[2 - j]);
V += 4 * (leftRow[7] - topLeft[0]);
int c = 34 * V + 32 >> 6;
int b = 34 * H + 32 >> 6;
int a = 16 * (leftRow[7] + topLine[blkOffX + 7]);
for (int off = 0, k = 0; k < 8; k++) {
for (int m = 0; m < 8; m++, off++) {
int val = a + b * (m - 3) + c * (k - 3) + 16 >> 5;
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.CHROMA_BLOCK_LUT[off]][H264Const.CHROMA_POS_LUT[off]] + MathUtil.clip(val, -128, 127), -128, 127);
}
}
}
}

View file

@ -0,0 +1,421 @@
package org.jcodec.codecs.h264.decode;
import java.util.Arrays;
import org.jcodec.common.ArrayUtil;
public class CoeffTransformer {
public static int[] zigzag4x4 = new int[] {
0, 1, 4, 8, 5, 2, 3, 6, 9, 12,
13, 10, 7, 11, 14, 15 };
public static int[] invZigzag4x4 = new int[16];
static int[][] dequantCoef = new int[][] { new int[] {
10, 13, 10, 13, 13, 16, 13, 16, 10, 13,
10, 13, 13, 16, 13, 16 }, new int[] {
11, 14, 11, 14, 14, 18, 14, 18, 11, 14,
11, 14, 14, 18, 14, 18 }, new int[] {
13, 16, 13, 16, 16, 20, 16, 20, 13, 16,
13, 16, 16, 20, 16, 20 }, new int[] {
14, 18, 14, 18, 18, 23, 18, 23, 14, 18,
14, 18, 18, 23, 18, 23 }, new int[] {
16, 20, 16, 20, 20, 25, 20, 25, 16, 20,
16, 20, 20, 25, 20, 25 }, new int[] {
18, 23, 18, 23, 23, 29, 23, 29, 18, 23,
18, 23, 23, 29, 23, 29 } };
static int[][] dequantCoef8x8 = new int[6][64];
static int[][] initDequantCoeff8x8 = new int[][] { new int[] { 20, 18, 32, 19, 25, 24 }, new int[] { 22, 19, 35, 21, 28, 26 }, new int[] { 26, 23, 42, 24, 33, 31 }, new int[] { 28, 25, 45, 26, 35, 33 }, new int[] { 32, 28, 51, 30, 40, 38 }, new int[] { 36, 32, 58, 34, 46, 43 } };
public static int[] zigzag8x8 = 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 int[] invZigzag8x8 = new int[64];
static {
for (int j = 0; j < 16; j++)
invZigzag4x4[zigzag4x4[j]] = j;
for (int i = 0; i < 64; i++)
invZigzag8x8[zigzag8x8[i]] = i;
}
private static final int[][] quantCoeff = new int[][] { new int[] {
13107, 8066, 13107, 8066, 8066, 5243, 8066, 5243, 13107, 8066,
13107, 8066, 8066, 5243, 8066, 5243 }, new int[] {
11916, 7490, 11916, 7490, 7490, 4660, 7490, 4660, 11916, 7490,
11916, 7490, 7490, 4660, 7490, 4660 }, new int[] {
10082, 6554, 10082, 6554, 6554, 4194, 6554, 4194, 10082, 6554,
10082, 6554, 6554, 4194, 6554, 4194 }, new int[] {
9362, 5825, 9362, 5825, 5825, 3647, 5825, 3647, 9362, 5825,
9362, 5825, 5825, 3647, 5825, 3647 }, new int[] {
8192, 5243, 8192, 5243, 5243, 3355, 5243, 3355, 8192, 5243,
8192, 5243, 5243, 3355, 5243, 3355 }, new int[] {
7282, 4559, 7282, 4559, 4559, 2893, 4559, 2893, 7282, 4559,
7282, 4559, 4559, 2893, 4559, 2893 } };
static {
for (int g = 0; g < 6; g++) {
Arrays.fill(dequantCoef8x8[g], initDequantCoeff8x8[g][5]);
for (int i4 = 0; i4 < 8; i4 += 4) {
for (int i5 = 0; i5 < 8; i5 += 4)
dequantCoef8x8[g][(i4 << 3) + i5] = initDequantCoeff8x8[g][0];
}
for (int i3 = 1; i3 < 8; i3 += 2) {
for (int i5 = 1; i5 < 8; i5 += 2)
dequantCoef8x8[g][(i3 << 3) + i5] = initDequantCoeff8x8[g][1];
}
for (int i2 = 2; i2 < 8; i2 += 4) {
for (int i5 = 2; i5 < 8; i5 += 4)
dequantCoef8x8[g][(i2 << 3) + i5] = initDequantCoeff8x8[g][2];
}
for (int i1 = 0; i1 < 8; i1 += 4) {
for (int i5 = 1; i5 < 8; i5 += 2)
dequantCoef8x8[g][(i1 << 3) + i5] = initDequantCoeff8x8[g][3];
}
for (int n = 1; n < 8; n += 2) {
for (int i5 = 0; i5 < 8; i5 += 4)
dequantCoef8x8[g][(n << 3) + i5] = initDequantCoeff8x8[g][3];
}
for (int m = 0; m < 8; m += 4) {
for (int i5 = 2; i5 < 8; i5 += 4)
dequantCoef8x8[g][(m << 3) + i5] = initDequantCoeff8x8[g][4];
}
for (int k = 2; k < 8; k += 4) {
for (int i5 = 0; i5 < 8; i5 += 4)
dequantCoef8x8[g][(k << 3) + i5] = initDequantCoeff8x8[g][4];
}
}
}
public static final void idct4x4(int[] block) {
_idct4x4(block, block);
}
public static final void _idct4x4(int[] block, int[] out) {
for (int k = 0; k < 16; k += 4) {
int e0 = block[k] + block[k + 2];
int e1 = block[k] - block[k + 2];
int e2 = (block[k + 1] >> 1) - block[k + 3];
int e3 = block[k + 1] + (block[k + 3] >> 1);
out[k] = e0 + e3;
out[k + 1] = e1 + e2;
out[k + 2] = e1 - e2;
out[k + 3] = e0 - e3;
}
for (int j = 0; j < 4; j++) {
int g0 = out[j] + out[j + 8];
int g1 = out[j] - out[j + 8];
int g2 = (out[j + 4] >> 1) - out[j + 12];
int g3 = out[j + 4] + (out[j + 12] >> 1);
out[j] = g0 + g3;
out[j + 4] = g1 + g2;
out[j + 8] = g1 - g2;
out[j + 12] = g0 - g3;
}
for (int i = 0; i < 16; i++)
out[i] = out[i] + 32 >> 6;
}
public static void fdct4x4(int[] block) {
for (int j = 0; j < 16; j += 4) {
int t0 = block[j] + block[j + 3];
int t1 = block[j + 1] + block[j + 2];
int t2 = block[j + 1] - block[j + 2];
int t3 = block[j] - block[j + 3];
block[j] = t0 + t1;
block[j + 1] = (t3 << 1) + t2;
block[j + 2] = t0 - t1;
block[j + 3] = t3 - (t2 << 1);
}
for (int i = 0; i < 4; i++) {
int t0 = block[i] + block[i + 12];
int t1 = block[i + 4] + block[i + 8];
int t2 = block[i + 4] - block[i + 8];
int t3 = block[i] - block[i + 12];
block[i] = t0 + t1;
block[i + 4] = t2 + (t3 << 1);
block[i + 8] = t0 - t1;
block[i + 12] = t3 - (t2 << 1);
}
}
public static void invDC4x4(int[] scaled) {
for (int j = 0; j < 16; j += 4) {
int e0 = scaled[j] + scaled[j + 2];
int e1 = scaled[j] - scaled[j + 2];
int e2 = scaled[j + 1] - scaled[j + 3];
int e3 = scaled[j + 1] + scaled[j + 3];
scaled[j] = e0 + e3;
scaled[j + 1] = e1 + e2;
scaled[j + 2] = e1 - e2;
scaled[j + 3] = e0 - e3;
}
for (int i = 0; i < 4; i++) {
int g0 = scaled[i] + scaled[i + 8];
int g1 = scaled[i] - scaled[i + 8];
int g2 = scaled[i + 4] - scaled[i + 12];
int g3 = scaled[i + 4] + scaled[i + 12];
scaled[i] = g0 + g3;
scaled[i + 4] = g1 + g2;
scaled[i + 8] = g1 - g2;
scaled[i + 12] = g0 - g3;
}
}
public static void fvdDC4x4(int[] scaled) {
for (int j = 0; j < 16; j += 4) {
int t0 = scaled[j] + scaled[j + 3];
int t1 = scaled[j + 1] + scaled[j + 2];
int t2 = scaled[j + 1] - scaled[j + 2];
int t3 = scaled[j] - scaled[j + 3];
scaled[j] = t0 + t1;
scaled[j + 1] = t3 + t2;
scaled[j + 2] = t0 - t1;
scaled[j + 3] = t3 - t2;
}
for (int i = 0; i < 4; i++) {
int t0 = scaled[i] + scaled[i + 12];
int t1 = scaled[i + 4] + scaled[i + 8];
int t2 = scaled[i + 4] - scaled[i + 8];
int t3 = scaled[i] - scaled[i + 12];
scaled[i] = t0 + t1 >> 1;
scaled[i + 4] = t2 + t3 >> 1;
scaled[i + 8] = t0 - t1 >> 1;
scaled[i + 12] = t3 - t2 >> 1;
}
}
public static void dequantizeAC(int[] coeffs, int qp, int[] scalingList) {
int group = qp % 6;
if (scalingList == null) {
int qbits = qp / 6;
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * dequantCoef[group][i] << qbits;
} else if (qp >= 24) {
int qbits = qp / 6 - 4;
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * dequantCoef[group][i] * scalingList[invZigzag4x4[i]] << qbits;
} else {
int qbits = 4 - qp / 6;
int addition = 1 << 3 - qp / 6;
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * scalingList[invZigzag4x4[i]] * dequantCoef[group][i] + addition >> qbits;
}
}
public static void quantizeAC(int[] coeffs, int qp) {
int level = qp / 6;
int offset = qp % 6;
int addition = 682 << qp / 6 + 4;
int qbits = 15 + level;
if (qp < 10) {
for (int i = 0; i < 16; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (Math.min(((coeffs[i] ^ sign) - sign) * quantCoeff[offset][i] + addition >> qbits, 2063) ^ sign) - sign;
}
} else {
for (int i = 0; i < 16; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (((coeffs[i] ^ sign) - sign) * quantCoeff[offset][i] + addition >> qbits ^ sign) - sign;
}
}
}
public static void dequantizeDC4x4(int[] coeffs, int qp, int[] scalingList) {
int group = qp % 6;
if (qp >= 36) {
int qbits = qp / 6 - 6;
if (scalingList == null) {
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * (dequantCoef[group][0] << 4) << qbits;
} else {
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * dequantCoef[group][0] * scalingList[0] << qbits;
}
} else {
int qbits = 6 - qp / 6;
int addition = 1 << 5 - qp / 6;
if (scalingList == null) {
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * (dequantCoef[group][0] << 4) + addition >> qbits;
} else {
for (int i = 0; i < 16; i++)
coeffs[i] = coeffs[i] * dequantCoef[group][0] * scalingList[0] + addition >> qbits;
}
}
}
public static void quantizeDC4x4(int[] coeffs, int qp) {
int level = qp / 6;
int offset = qp % 6;
int addition = 682 << qp / 6 + 5;
int qbits = 16 + level;
if (qp < 10) {
for (int i = 0; i < 16; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (Math.min(((coeffs[i] ^ sign) - sign) * quantCoeff[offset][0] + addition >> qbits, 2063) ^ sign) - sign;
}
} else {
for (int i = 0; i < 16; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (((coeffs[i] ^ sign) - sign) * quantCoeff[offset][0] + addition >> qbits ^ sign) - sign;
}
}
}
public static void invDC2x2(int[] block) {
int t0 = block[0] + block[1];
int t1 = block[0] - block[1];
int t2 = block[2] + block[3];
int t3 = block[2] - block[3];
block[0] = t0 + t2;
block[1] = t1 + t3;
block[2] = t0 - t2;
block[3] = t1 - t3;
}
public static void fvdDC2x2(int[] block) {
invDC2x2(block);
}
public static void dequantizeDC2x2(int[] transformed, int qp, int[] scalingList) {
int group = qp % 6;
if (scalingList == null) {
int shift = qp / 6;
for (int i = 0; i < 4; i++)
transformed[i] = transformed[i] * dequantCoef[group][0] << shift >> 1;
} else if (qp >= 24) {
int qbits = qp / 6 - 4;
for (int i = 0; i < 4; i++)
transformed[i] = transformed[i] * dequantCoef[group][0] * scalingList[0] << qbits >> 1;
} else {
int qbits = 4 - qp / 6;
int addition = 1 << 3 - qp / 6;
for (int i = 0; i < 4; i++)
transformed[i] = transformed[i] * dequantCoef[group][0] * scalingList[0] + addition >> qbits >> 1;
}
}
public static void quantizeDC2x2(int[] coeffs, int qp) {
int level = qp / 6;
int offset = qp % 6;
int addition = 682 << qp / 6 + 5;
int qbits = 16 + level;
if (qp < 4) {
for (int i = 0; i < 4; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (Math.min(((coeffs[i] ^ sign) - sign) * quantCoeff[offset][0] + addition >> qbits, 2063) ^ sign) - sign;
}
} else {
for (int i = 0; i < 4; i++) {
int sign = coeffs[i] >> 31;
coeffs[i] = (((coeffs[i] ^ sign) - sign) * quantCoeff[offset][0] + addition >> qbits ^ sign) - sign;
}
}
}
public static void reorderDC4x4(int[] dc) {
ArrayUtil.swap(dc, 2, 4);
ArrayUtil.swap(dc, 3, 5);
ArrayUtil.swap(dc, 10, 12);
ArrayUtil.swap(dc, 11, 13);
}
public static void fvdDC4x2(int[] dc) {}
public static void quantizeDC4x2(int[] dc, int qp) {}
public static void invDC4x2(int[] dc) {}
public static void dequantizeDC4x2(int[] dc, int qp) {}
public static void dequantizeAC8x8(int[] coeffs, int qp, int[] scalingList) {
int group = qp % 6;
if (qp >= 36) {
int qbits = qp / 6 - 6;
if (scalingList == null) {
for (int i = 0; i < 64; i++)
coeffs[i] = coeffs[i] * dequantCoef8x8[group][i] << 4 << qbits;
} else {
for (int i = 0; i < 64; i++)
coeffs[i] = coeffs[i] * dequantCoef8x8[group][i] * scalingList[invZigzag8x8[i]] << qbits;
}
} else {
int qbits = 6 - qp / 6;
int addition = 1 << 5 - qp / 6;
if (scalingList == null) {
for (int i = 0; i < 64; i++)
coeffs[i] = coeffs[i] * (dequantCoef8x8[group][i] << 4) + addition >> qbits;
} else {
for (int i = 0; i < 64; i++)
coeffs[i] = coeffs[i] * dequantCoef8x8[group][i] * scalingList[invZigzag8x8[i]] + addition >> qbits;
}
}
}
public static void idct8x8(int[] ac) {
int off = 0;
for (int row = 0; row < 8; row++) {
int e0 = ac[off] + ac[off + 4];
int e1 = -ac[off + 3] + ac[off + 5] - ac[off + 7] - (ac[off + 7] >> 1);
int e2 = ac[off] - ac[off + 4];
int e3 = ac[off + 1] + ac[off + 7] - ac[off + 3] - (ac[off + 3] >> 1);
int e4 = (ac[off + 2] >> 1) - ac[off + 6];
int e5 = -ac[off + 1] + ac[off + 7] + ac[off + 5] + (ac[off + 5] >> 1);
int e6 = ac[off + 2] + (ac[off + 6] >> 1);
int e7 = ac[off + 3] + ac[off + 5] + ac[off + 1] + (ac[off + 1] >> 1);
int f0 = e0 + e6;
int f1 = e1 + (e7 >> 2);
int f2 = e2 + e4;
int f3 = e3 + (e5 >> 2);
int f4 = e2 - e4;
int f5 = (e3 >> 2) - e5;
int f6 = e0 - e6;
int f7 = e7 - (e1 >> 2);
ac[off] = f0 + f7;
ac[off + 1] = f2 + f5;
ac[off + 2] = f4 + f3;
ac[off + 3] = f6 + f1;
ac[off + 4] = f6 - f1;
ac[off + 5] = f4 - f3;
ac[off + 6] = f2 - f5;
ac[off + 7] = f0 - f7;
off += 8;
}
for (int col = 0; col < 8; col++) {
int e0 = ac[col] + ac[col + 32];
int e1 = -ac[col + 24] + ac[col + 40] - ac[col + 56] - (ac[col + 56] >> 1);
int e2 = ac[col] - ac[col + 32];
int e3 = ac[col + 8] + ac[col + 56] - ac[col + 24] - (ac[col + 24] >> 1);
int e4 = (ac[col + 16] >> 1) - ac[col + 48];
int e5 = -ac[col + 8] + ac[col + 56] + ac[col + 40] + (ac[col + 40] >> 1);
int e6 = ac[col + 16] + (ac[col + 48] >> 1);
int e7 = ac[col + 24] + ac[col + 40] + ac[col + 8] + (ac[col + 8] >> 1);
int f0 = e0 + e6;
int f1 = e1 + (e7 >> 2);
int f2 = e2 + e4;
int f3 = e3 + (e5 >> 2);
int f4 = e2 - e4;
int f5 = (e3 >> 2) - e5;
int f6 = e0 - e6;
int f7 = e7 - (e1 >> 2);
ac[col] = f0 + f7;
ac[col + 8] = f2 + f5;
ac[col + 16] = f4 + f3;
ac[col + 24] = f6 + f1;
ac[col + 32] = f6 - f1;
ac[col + 40] = f4 - f3;
ac[col + 48] = f2 - f5;
ac[col + 56] = f0 - f7;
}
for (int i = 0; i < 64; i++)
ac[i] = ac[i] + 32 >> 6;
}
}

View file

@ -0,0 +1,35 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
public class DeblockerInput {
public int[][] nCoeff;
public H264Utils.MvList2D mvs;
public MBType[] mbTypes;
public int[][] mbQps;
public boolean[] tr8x8Used;
public Frame[][][] refsUsed;
public SliceHeader[] shs;
public DeblockerInput(SeqParameterSet activeSps) {
int picWidthInMbs = activeSps.picWidthInMbsMinus1 + 1;
int picHeightInMbs = SeqParameterSet.getPicHeightInMbs(activeSps);
this.nCoeff = new int[picHeightInMbs << 2][picWidthInMbs << 2];
this.mvs = new H264Utils.MvList2D(picWidthInMbs << 2, picHeightInMbs << 2);
this.mbTypes = new MBType[picHeightInMbs * picWidthInMbs];
this.tr8x8Used = new boolean[picHeightInMbs * picWidthInMbs];
this.mbQps = new int[3][picHeightInMbs * picWidthInMbs];
this.shs = new SliceHeader[picHeightInMbs * picWidthInMbs];
this.refsUsed = new Frame[picHeightInMbs * picWidthInMbs][][];
}
}

View file

@ -0,0 +1,39 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.ColorSpace;
public class DecoderState {
int[] chromaQpOffset;
int qp;
byte[][] leftRow;
byte[][] topLine;
byte[][] topLeft;
ColorSpace chromaFormat;
H264Utils.MvList mvTop;
H264Utils.MvList mvLeft;
H264Utils.MvList mvTopLeft;
public DecoderState(SliceHeader sh) {
int mbWidth = sh.sps.picWidthInMbsMinus1 + 1;
this
.chromaQpOffset = new int[] { sh.pps.chromaQpIndexOffset, (sh.pps.extended != null) ? sh.pps.extended.secondChromaQpIndexOffset : sh.pps.chromaQpIndexOffset };
this.chromaFormat = sh.sps.chromaFormatIdc;
this.mvTop = new H264Utils.MvList((mbWidth << 2) + 1);
this.mvLeft = new H264Utils.MvList(4);
this.mvTopLeft = new H264Utils.MvList(1);
this.leftRow = new byte[3][16];
this.topLeft = new byte[3][4];
this.topLine = new byte[3][mbWidth << 4];
this.qp = sh.pps.picInitQpMinus26 + 26 + sh.sliceQpDelta;
}
}

View file

@ -0,0 +1,97 @@
package org.jcodec.codecs.h264.decode;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.jcodec.codecs.common.biari.MDecoder;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.decode.aso.MapManager;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.CABAC;
import org.jcodec.codecs.h264.io.CAVLC;
import org.jcodec.codecs.h264.io.model.NALUnit;
import org.jcodec.codecs.h264.io.model.NALUnitType;
import org.jcodec.codecs.h264.io.model.PictureParameterSet;
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.IntObjectMap;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.logging.Logger;
public class FrameReader {
private IntObjectMap<SeqParameterSet> sps = new IntObjectMap<>();
private IntObjectMap<PictureParameterSet> pps = new IntObjectMap<>();
public List<SliceReader> readFrame(List<ByteBuffer> nalUnits) {
List<SliceReader> result = new ArrayList<>();
for (ByteBuffer nalData : nalUnits) {
NALUnit nalUnit = NALUnit.read(nalData);
H264Utils.unescapeNAL(nalData);
if (NALUnitType.SPS == nalUnit.type) {
SeqParameterSet _sps = SeqParameterSet.read(nalData);
this.sps.put(_sps.seqParameterSetId, _sps);
continue;
}
if (NALUnitType.PPS == nalUnit.type) {
PictureParameterSet _pps = PictureParameterSet.read(nalData);
this.pps.put(_pps.picParameterSetId, _pps);
continue;
}
if (NALUnitType.IDR_SLICE == nalUnit.type || NALUnitType.NON_IDR_SLICE == nalUnit.type) {
if (this.sps.size() == 0 || this.pps.size() == 0) {
Logger.warn("Skipping frame as no SPS/PPS have been seen so far...");
return null;
}
result.add(createSliceReader(nalData, nalUnit));
}
}
return result;
}
private SliceReader createSliceReader(ByteBuffer segment, NALUnit nalUnit) {
BitReader _in = BitReader.createBitReader(segment);
SliceHeader sh = SliceHeaderReader.readPart1(_in);
sh.pps = this.pps.get(sh.picParameterSetId);
sh.sps = this.sps.get(sh.pps.seqParameterSetId);
SliceHeaderReader.readPart2(sh, nalUnit, sh.sps, sh.pps, _in);
Mapper mapper = new MapManager(sh.sps, sh.pps).getMapper(sh);
CAVLC[] cavlc = { new CAVLC(sh.sps, sh.pps, 2, 2), new CAVLC(sh.sps, sh.pps, 1, 1), new CAVLC(sh.sps, sh.pps, 1, 1) };
int mbWidth = sh.sps.picWidthInMbsMinus1 + 1;
CABAC cabac = new CABAC(mbWidth);
MDecoder mDecoder = null;
if (sh.pps.entropyCodingModeFlag) {
_in.terminate();
int[][] cm = new int[2][1024];
int qp = sh.pps.picInitQpMinus26 + 26 + sh.sliceQpDelta;
cabac.initModels(cm, sh.sliceType, sh.cabacInitIdc, qp);
mDecoder = new MDecoder(segment, cm);
}
return new SliceReader(sh.pps, cabac, cavlc, mDecoder, _in, mapper, sh, nalUnit);
}
public void addSpsList(List<ByteBuffer> spsList) {
for (ByteBuffer byteBuffer : spsList)
addSps(byteBuffer);
}
public void addSps(ByteBuffer byteBuffer) {
ByteBuffer clone = NIOUtils.clone(byteBuffer);
H264Utils.unescapeNAL(clone);
SeqParameterSet s = SeqParameterSet.read(clone);
this.sps.put(s.seqParameterSetId, s);
}
public void addPpsList(List<ByteBuffer> ppsList) {
for (ByteBuffer byteBuffer : ppsList)
addPps(byteBuffer);
}
public void addPps(ByteBuffer byteBuffer) {
ByteBuffer clone = NIOUtils.clone(byteBuffer);
H264Utils.unescapeNAL(clone);
PictureParameterSet p = PictureParameterSet.read(clone);
this.pps.put(p.picParameterSetId, p);
}
}

View file

@ -0,0 +1,86 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.common.tools.MathUtil;
public class Intra16x16PredictionBuilder {
public static void predictWithMode(int predMode, int[][] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int x, byte[] pixOut) {
switch (predMode) {
case 0:
predictVertical(residual, topAvailable, topLine, x, pixOut);
break;
case 1:
predictHorizontal(residual, leftAvailable, leftRow, x, pixOut);
break;
case 2:
predictDC(residual, leftAvailable, topAvailable, leftRow, topLine, x, pixOut);
break;
case 3:
predictPlane(residual, leftAvailable, topAvailable, leftRow, topLine, topLeft, x, pixOut);
break;
}
}
public static void predictVertical(int[][] residual, boolean topAvailable, byte[] topLine, int x, byte[] pixOut) {
int off = 0;
for (int j = 0; j < 16; j++) {
for (int i = 0; i < 16; i++, off++)
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.LUMA_4x4_BLOCK_LUT[off]][H264Const.LUMA_4x4_POS_LUT[off]] + topLine[x + i], -128, 127);
}
}
public static void predictHorizontal(int[][] residual, boolean leftAvailable, byte[] leftRow, int x, byte[] pixOut) {
int off = 0;
for (int j = 0; j < 16; j++) {
for (int i = 0; i < 16; i++, off++)
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.LUMA_4x4_BLOCK_LUT[off]][H264Const.LUMA_4x4_POS_LUT[off]] + leftRow[j], -128, 127);
}
}
public static void predictDC(int[][] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, int x, byte[] pixOut) {
int s0;
if (leftAvailable && topAvailable) {
s0 = 0;
for (int k = 0; k < 16; k++)
s0 += leftRow[k];
for (int j = 0; j < 16; j++)
s0 += topLine[x + j];
s0 = s0 + 16 >> 5;
} else if (leftAvailable) {
s0 = 0;
for (int j = 0; j < 16; j++)
s0 += leftRow[j];
s0 = s0 + 8 >> 4;
} else if (topAvailable) {
s0 = 0;
for (int j = 0; j < 16; j++)
s0 += topLine[x + j];
s0 = s0 + 8 >> 4;
} else {
s0 = 0;
}
for (int i = 0; i < 256; i++)
pixOut[i] = (byte)MathUtil.clip(residual[H264Const.LUMA_4x4_BLOCK_LUT[i]][H264Const.LUMA_4x4_POS_LUT[i]] + s0, -128, 127);
}
public static void predictPlane(int[][] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int x, byte[] pixOut) {
int H = 0;
for (int i = 0; i < 7; i++)
H += (i + 1) * (topLine[x + 8 + i] - topLine[x + 6 - i]);
H += 8 * (topLine[x + 15] - topLeft[0]);
int V = 0;
for (int j = 0; j < 7; j++)
V += (j + 1) * (leftRow[8 + j] - leftRow[6 - j]);
V += 8 * (leftRow[15] - topLeft[0]);
int c = 5 * V + 32 >> 6;
int b = 5 * H + 32 >> 6;
int a = 16 * (leftRow[15] + topLine[x + 15]);
int off = 0;
for (int k = 0; k < 16; k++) {
for (int m = 0; m < 16; m++, off++) {
int val = MathUtil.clip(a + b * (m - 7) + c * (k - 7) + 16 >> 5, -128, 127);
pixOut[off] = (byte)MathUtil.clip(residual[H264Const.LUMA_4x4_BLOCK_LUT[off]][H264Const.LUMA_4x4_POS_LUT[off]] + val, -128, 127);
}
}
}
}

View file

@ -0,0 +1,292 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.common.tools.MathUtil;
public class Intra4x4PredictionBuilder {
public static void predictWithMode(int mode, int[] residual, boolean leftAvailable, boolean topAvailable, boolean topRightAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int mbOffX, int blkX, int blkY, byte[] pixOut) {
switch (mode) {
case 0:
predictVertical(residual, topAvailable, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 1:
predictHorizontal(residual, leftAvailable, leftRow, mbOffX, blkX, blkY, pixOut);
break;
case 2:
predictDC(residual, leftAvailable, topAvailable, leftRow, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 3:
predictDiagonalDownLeft(residual, topAvailable, topRightAvailable, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 4:
predictDiagonalDownRight(residual, leftAvailable, topAvailable, leftRow, topLine, topLeft, mbOffX, blkX, blkY, pixOut);
break;
case 5:
predictVerticalRight(residual, leftAvailable, topAvailable, leftRow, topLine, topLeft, mbOffX, blkX, blkY, pixOut);
break;
case 6:
predictHorizontalDown(residual, leftAvailable, topAvailable, leftRow, topLine, topLeft, mbOffX, blkX, blkY, pixOut);
break;
case 7:
predictVerticalLeft(residual, topAvailable, topRightAvailable, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 8:
predictHorizontalUp(residual, leftAvailable, leftRow, mbOffX, blkX, blkY, pixOut);
break;
}
int oo1 = mbOffX + blkX;
int off1 = (blkY << 4) + blkX + 3;
topLeft[blkY >> 2] = topLine[oo1 + 3];
leftRow[blkY] = pixOut[off1];
leftRow[blkY + 1] = pixOut[off1 + 16];
leftRow[blkY + 2] = pixOut[off1 + 32];
leftRow[blkY + 3] = pixOut[off1 + 48];
int off2 = (blkY << 4) + blkX + 48;
topLine[oo1] = pixOut[off2];
topLine[oo1 + 1] = pixOut[off2 + 1];
topLine[oo1 + 2] = pixOut[off2 + 2];
topLine[oo1 + 3] = pixOut[off2 + 3];
}
public static void predictVertical(int[] residual, boolean topAvailable, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int pixOff = (blkY << 4) + blkX;
int toff = mbOffX + blkX;
int rOff = 0;
for (int j = 0; j < 4; j++) {
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + topLine[toff], -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + topLine[toff + 1], -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + topLine[toff + 2], -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + topLine[toff + 3], -128, 127);
rOff += 4;
pixOff += 16;
}
}
public static void predictHorizontal(int[] residual, boolean leftAvailable, byte[] leftRow, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int pixOff = (blkY << 4) + blkX;
int rOff = 0;
for (int j = 0; j < 4; j++) {
int l = leftRow[blkY + j];
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + l, -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + l, -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + l, -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + l, -128, 127);
rOff += 4;
pixOff += 16;
}
}
public static void predictDC(int[] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int val;
if (leftAvailable && topAvailable) {
val = leftRow[blkY] + leftRow[blkY + 1] + leftRow[blkY + 2] + leftRow[blkY + 3] + topLine[mbOffX + blkX] + topLine[mbOffX + blkX + 1] + topLine[mbOffX + blkX + 2] + topLine[mbOffX + blkX + 3] + 4 >> 3;
} else if (leftAvailable) {
val = leftRow[blkY] + leftRow[blkY + 1] + leftRow[blkY + 2] + leftRow[blkY + 3] + 2 >> 2;
} else if (topAvailable) {
val = topLine[mbOffX + blkX] + topLine[mbOffX + blkX + 1] + topLine[mbOffX + blkX + 2] + topLine[mbOffX + blkX + 3] + 2 >> 2;
} else {
val = 0;
}
int pixOff = (blkY << 4) + blkX;
int rOff = 0;
for (int j = 0; j < 4; j++) {
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + val, -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + val, -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + val, -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + val, -128, 127);
pixOff += 16;
rOff += 4;
}
}
public static void predictDiagonalDownLeft(int[] residual, boolean topAvailable, boolean topRightAvailable, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int to = mbOffX + blkX;
int tr0 = topLine[to + 3], tr1 = topLine[to + 3], tr2 = topLine[to + 3], tr3 = topLine[to + 3];
if (topRightAvailable) {
tr0 = topLine[to + 4];
tr1 = topLine[to + 5];
tr2 = topLine[to + 6];
tr3 = topLine[to + 7];
}
int c0 = topLine[to] + topLine[to + 2] + (topLine[to + 1] << 1) + 2 >> 2;
int c1 = topLine[to + 1] + topLine[to + 3] + (topLine[to + 2] << 1) + 2 >> 2;
int c2 = topLine[to + 2] + tr0 + (topLine[to + 3] << 1) + 2 >> 2;
int c3 = topLine[to + 3] + tr1 + (tr0 << 1) + 2 >> 2;
int c4 = tr0 + tr2 + (tr1 << 1) + 2 >> 2;
int c5 = tr1 + tr3 + (tr2 << 1) + 2 >> 2;
int c6 = tr2 + 3 * tr3 + 2 >> 2;
int off = (blkY << 4) + blkX;
pixOut[off] = (byte)MathUtil.clip(residual[0] + c0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + c1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + c2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + c3, -128, 127);
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + c1, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + c2, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + c3, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + c4, -128, 127);
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + c2, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + c3, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + c4, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + c5, -128, 127);
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + c3, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + c4, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + c5, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + c6, -128, 127);
}
public static void predictDiagonalDownRight(int[] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int off = (blkY << 4) + blkX;
int c0 = topLine[mbOffX + blkX] + 2 * topLeft[blkY >> 2] + leftRow[blkY] + 2 >> 2;
int c1 = topLeft[blkY >> 2] + (topLine[mbOffX + blkX + 0] << 1) + topLine[mbOffX + blkX + 1] + 2 >> 2;
int c2 = topLine[mbOffX + blkX] + (topLine[mbOffX + blkX + 1] << 1) + topLine[mbOffX + blkX + 2] + 2 >> 2;
int c3 = topLine[mbOffX + blkX + 1] + (topLine[mbOffX + blkX + 2] << 1) + topLine[mbOffX + blkX + 3] + 2 >> 2;
pixOut[off] = (byte)MathUtil.clip(residual[0] + c0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + c1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + c2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + c3, -128, 127);
int c4 = topLeft[blkY >> 2] + (leftRow[blkY] << 1) + leftRow[blkY + 1] + 2 >> 2;
int c6 = topLeft[blkY >> 2] + (topLine[mbOffX + blkX] << 1) + topLine[mbOffX + blkX + 1] + 2 >> 2;
int c7 = topLine[mbOffX + blkX] + (topLine[mbOffX + blkX + 1] << 1) + topLine[mbOffX + blkX + 2] + 2 >> 2;
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + c4, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + c0, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + c6, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + c7, -128, 127);
int c8 = leftRow[blkY + 0] + (leftRow[blkY + 1] << 1) + leftRow[blkY + 2] + 2 >> 2;
int c9 = topLeft[blkY >> 2] + (leftRow[blkY] << 1) + leftRow[blkY + 1] + 2 >> 2;
int c11 = topLeft[blkY >> 2] + (topLine[mbOffX + blkX] << 1) + topLine[mbOffX + blkX + 1] + 2 >> 2;
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + c8, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + c9, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + c0, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + c11, -128, 127);
int c12 = leftRow[blkY + 1] + (leftRow[blkY + 2] << 1) + leftRow[blkY + 3] + 2 >> 2;
int c13 = leftRow[blkY] + (leftRow[blkY + 1] << 1) + leftRow[blkY + 2] + 2 >> 2;
int c14 = topLeft[blkY >> 2] + (leftRow[blkY] << 1) + leftRow[blkY + 1] + 2 >> 2;
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + c12, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + c13, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + c14, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + c0, -128, 127);
}
public static void predictVerticalRight(int[] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int v1 = topLeft[blkY >> 2] + topLine[mbOffX + blkX + 0] + 1 >> 1;
int v2 = topLine[mbOffX + blkX + 0] + topLine[mbOffX + blkX + 1] + 1 >> 1;
int v3 = topLine[mbOffX + blkX + 1] + topLine[mbOffX + blkX + 2] + 1 >> 1;
int v4 = topLine[mbOffX + blkX + 2] + topLine[mbOffX + blkX + 3] + 1 >> 1;
int v5 = leftRow[blkY] + 2 * topLeft[blkY >> 2] + topLine[mbOffX + blkX + 0] + 2 >> 2;
int v6 = topLeft[blkY >> 2] + 2 * topLine[mbOffX + blkX + 0] + topLine[mbOffX + blkX + 1] + 2 >> 2;
int v7 = topLine[mbOffX + blkX + 0] + 2 * topLine[mbOffX + blkX + 1] + topLine[mbOffX + blkX + 2] + 2 >> 2;
int v8 = topLine[mbOffX + blkX + 1] + 2 * topLine[mbOffX + blkX + 2] + topLine[mbOffX + blkX + 3] + 2 >> 2;
int v9 = topLeft[blkY >> 2] + 2 * leftRow[blkY] + leftRow[blkY + 1] + 2 >> 2;
int v10 = leftRow[blkY] + 2 * leftRow[blkY + 1] + leftRow[blkY + 2] + 2 >> 2;
int off = (blkY << 4) + blkX;
pixOut[off] = (byte)MathUtil.clip(residual[0] + v1, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + v2, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + v3, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + v4, -128, 127);
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + v5, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + v6, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + v7, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + v8, -128, 127);
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + v9, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + v1, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + v2, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + v3, -128, 127);
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + v10, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + v5, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + v6, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + v7, -128, 127);
}
public static void predictHorizontalDown(int[] residual, boolean leftAvailable, boolean topAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int c0 = topLeft[blkY >> 2] + leftRow[blkY] + 1 >> 1;
int c1 = leftRow[blkY] + 2 * topLeft[blkY >> 2] + topLine[mbOffX + blkX + 0] + 2 >> 2;
int c2 = topLeft[blkY >> 2] + 2 * topLine[mbOffX + blkX + 0] + topLine[mbOffX + blkX + 1] + 2 >> 2;
int c3 = topLine[mbOffX + blkX + 0] + 2 * topLine[mbOffX + blkX + 1] + topLine[mbOffX + blkX + 2] + 2 >> 2;
int c4 = leftRow[blkY] + leftRow[blkY + 1] + 1 >> 1;
int c5 = topLeft[blkY >> 2] + 2 * leftRow[blkY] + leftRow[blkY + 1] + 2 >> 2;
int c6 = leftRow[blkY + 1] + leftRow[blkY + 2] + 1 >> 1;
int c7 = leftRow[blkY] + 2 * leftRow[blkY + 1] + leftRow[blkY + 2] + 2 >> 2;
int c8 = leftRow[blkY + 2] + leftRow[blkY + 3] + 1 >> 1;
int c9 = leftRow[blkY + 1] + 2 * leftRow[blkY + 2] + leftRow[blkY + 3] + 2 >> 2;
int off = (blkY << 4) + blkX;
pixOut[off] = (byte)MathUtil.clip(residual[0] + c0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + c1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + c2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + c3, -128, 127);
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + c4, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + c5, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + c0, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + c1, -128, 127);
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + c6, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + c7, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + c4, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + c5, -128, 127);
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + c8, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + c9, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + c6, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + c7, -128, 127);
}
public static void predictVerticalLeft(int[] residual, boolean topAvailable, boolean topRightAvailable, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int to = mbOffX + blkX;
int tr0 = topLine[to + 3], tr1 = topLine[to + 3], tr2 = topLine[to + 3];
if (topRightAvailable) {
tr0 = topLine[to + 4];
tr1 = topLine[to + 5];
tr2 = topLine[to + 6];
}
int c0 = topLine[to] + topLine[to + 1] + 1 >> 1;
int c1 = topLine[to + 1] + topLine[to + 2] + 1 >> 1;
int c2 = topLine[to + 2] + topLine[to + 3] + 1 >> 1;
int c3 = topLine[to + 3] + tr0 + 1 >> 1;
int c4 = tr0 + tr1 + 1 >> 1;
int c5 = topLine[to] + 2 * topLine[to + 1] + topLine[to + 2] + 2 >> 2;
int c6 = topLine[to + 1] + 2 * topLine[to + 2] + topLine[to + 3] + 2 >> 2;
int c7 = topLine[to + 2] + 2 * topLine[to + 3] + tr0 + 2 >> 2;
int c8 = topLine[to + 3] + 2 * tr0 + tr1 + 2 >> 2;
int c9 = tr0 + 2 * tr1 + tr2 + 2 >> 2;
int off = (blkY << 4) + blkX;
pixOut[off] = (byte)MathUtil.clip(residual[0] + c0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + c1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + c2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + c3, -128, 127);
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + c5, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + c6, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + c7, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + c8, -128, 127);
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + c1, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + c2, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + c3, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + c4, -128, 127);
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + c6, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + c7, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + c8, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + c9, -128, 127);
}
public static void predictHorizontalUp(int[] residual, boolean leftAvailable, byte[] leftRow, int mbOffX, int blkX, int blkY, byte[] pixOut) {
int c0 = leftRow[blkY] + leftRow[blkY + 1] + 1 >> 1;
int c1 = leftRow[blkY] + (leftRow[blkY + 1] << 1) + leftRow[blkY + 2] + 2 >> 2;
int c2 = leftRow[blkY + 1] + leftRow[blkY + 2] + 1 >> 1;
int c3 = leftRow[blkY + 1] + (leftRow[blkY + 2] << 1) + leftRow[blkY + 3] + 2 >> 2;
int c4 = leftRow[blkY + 2] + leftRow[blkY + 3] + 1 >> 1;
int c5 = leftRow[blkY + 2] + (leftRow[blkY + 3] << 1) + leftRow[blkY + 3] + 2 >> 2;
int c6 = leftRow[blkY + 3];
int off = (blkY << 4) + blkX;
pixOut[off] = (byte)MathUtil.clip(residual[0] + c0, -128, 127);
pixOut[off + 1] = (byte)MathUtil.clip(residual[1] + c1, -128, 127);
pixOut[off + 2] = (byte)MathUtil.clip(residual[2] + c2, -128, 127);
pixOut[off + 3] = (byte)MathUtil.clip(residual[3] + c3, -128, 127);
pixOut[off + 16] = (byte)MathUtil.clip(residual[4] + c2, -128, 127);
pixOut[off + 17] = (byte)MathUtil.clip(residual[5] + c3, -128, 127);
pixOut[off + 18] = (byte)MathUtil.clip(residual[6] + c4, -128, 127);
pixOut[off + 19] = (byte)MathUtil.clip(residual[7] + c5, -128, 127);
pixOut[off + 32] = (byte)MathUtil.clip(residual[8] + c4, -128, 127);
pixOut[off + 33] = (byte)MathUtil.clip(residual[9] + c5, -128, 127);
pixOut[off + 34] = (byte)MathUtil.clip(residual[10] + c6, -128, 127);
pixOut[off + 35] = (byte)MathUtil.clip(residual[11] + c6, -128, 127);
pixOut[off + 48] = (byte)MathUtil.clip(residual[12] + c6, -128, 127);
pixOut[off + 49] = (byte)MathUtil.clip(residual[13] + c6, -128, 127);
pixOut[off + 50] = (byte)MathUtil.clip(residual[14] + c6, -128, 127);
pixOut[off + 51] = (byte)MathUtil.clip(residual[15] + c6, -128, 127);
}
}

View file

@ -0,0 +1,383 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.common.Preconditions;
import org.jcodec.common.tools.MathUtil;
public class Intra8x8PredictionBuilder {
byte[] topBuf = new byte[16];
byte[] leftBuf = new byte[8];
byte[] genBuf = new byte[24];
public void predictWithMode(int mode, int[] residual, boolean leftAvailable, boolean topAvailable, boolean topLeftAvailable, boolean topRightAvailable, byte[] leftRow, byte[] topLine, byte[] topLeft, int mbOffX, int blkX, int blkY, byte[] pixOut) {
switch (mode) {
case 0:
Preconditions.checkState(topAvailable, "");
predictVertical(residual, topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 1:
Preconditions.checkState(leftAvailable, "");
predictHorizontal(residual, topLeftAvailable, topLeft, leftRow, mbOffX, blkX, blkY, pixOut);
break;
case 2:
predictDC(residual, topLeftAvailable, topRightAvailable, leftAvailable, topAvailable, topLeft, leftRow, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 3:
Preconditions.checkState(topAvailable, "");
predictDiagonalDownLeft(residual, topLeftAvailable, topAvailable, topRightAvailable, topLeft, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 4:
Preconditions.checkState((topAvailable && leftAvailable && topLeftAvailable), "");
predictDiagonalDownRight(residual, topRightAvailable, topLeft, leftRow, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 5:
Preconditions.checkState((topAvailable && leftAvailable && topLeftAvailable), "");
predictVerticalRight(residual, topRightAvailable, topLeft, leftRow, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 6:
Preconditions.checkState((topAvailable && leftAvailable && topLeftAvailable), "");
predictHorizontalDown(residual, topRightAvailable, topLeft, leftRow, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 7:
Preconditions.checkState(topAvailable, "");
predictVerticalLeft(residual, topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX, blkX, blkY, pixOut);
break;
case 8:
Preconditions.checkState(leftAvailable, "");
predictHorizontalUp(residual, topLeftAvailable, topLeft, leftRow, mbOffX, blkX, blkY, pixOut);
break;
}
int oo1 = mbOffX + blkX;
int off1 = (blkY << 4) + blkX + 7;
topLeft[blkY >> 2] = topLine[oo1 + 7];
for (int i = 0; i < 8; i++)
leftRow[blkY + i] = pixOut[off1 + (i << 4)];
int off2 = (blkY << 4) + blkX + 112;
for (int j = 0; j < 8; j++)
topLine[oo1 + j] = pixOut[off2 + j];
topLeft[(blkY >> 2) + 1] = leftRow[blkY + 3];
}
private void interpolateTop(boolean topLeftAvailable, boolean topRightAvailable, byte[] topLeft, byte[] topLine, int blkX, int blkY, byte[] out) {
int a = topLeftAvailable ? topLeft[blkY >> 2] : topLine[blkX];
out[0] = (byte)(a + (topLine[blkX] << 1) + topLine[blkX + 1] + 2 >> 2);
int i;
for (i = 1; i < 7; i++)
out[i] = (byte)(topLine[blkX + i - 1] + (topLine[blkX + i] << 1) + topLine[blkX + i + 1] + 2 >> 2);
if (topRightAvailable) {
for (; i < 15; i++)
out[i] = (byte)(topLine[blkX + i - 1] + (topLine[blkX + i] << 1) + topLine[blkX + i + 1] + 2 >> 2);
out[15] = (byte)(topLine[blkX + 14] + (topLine[blkX + 15] << 1) + topLine[blkX + 15] + 2 >> 2);
} else {
out[7] = (byte)(topLine[blkX + 6] + (topLine[blkX + 7] << 1) + topLine[blkX + 7] + 2 >> 2);
for (int j = 8; j < 16; j++)
out[j] = topLine[blkX + 7];
}
}
private void interpolateLeft(boolean topLeftAvailable, byte[] topLeft, byte[] leftRow, int blkY, byte[] out) {
int a = topLeftAvailable ? topLeft[blkY >> 2] : leftRow[0];
out[0] = (byte)(a + (leftRow[blkY] << 1) + leftRow[blkY + 1] + 2 >> 2);
for (int i = 1; i < 7; i++)
out[i] = (byte)(leftRow[blkY + i - 1] + (leftRow[blkY + i] << 1) + leftRow[blkY + i + 1] + 2 >> 2);
out[7] = (byte)(leftRow[blkY + 6] + (leftRow[blkY + 7] << 1) + leftRow[blkY + 7] + 2 >> 2);
}
private int interpolateTopLeft(boolean topAvailable, boolean leftAvailable, byte[] topLeft, byte[] topLine, byte[] leftRow, int mbOffX, int blkX, int blkY) {
int a = topLeft[blkY >> 2];
int b = topAvailable ? topLine[mbOffX + blkX] : a;
int c = leftAvailable ? leftRow[blkY] : a;
int aa = a << 1;
return aa + b + c + 2 >> 2;
}
public void copyAdd(byte[] pred, int srcOff, int[] residual, int pixOff, int rOff, byte[] out) {
out[pixOff] = (byte)MathUtil.clip(residual[rOff] + pred[srcOff], -128, 127);
out[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + pred[srcOff + 1], -128, 127);
out[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + pred[srcOff + 2], -128, 127);
out[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + pred[srcOff + 3], -128, 127);
out[pixOff + 4] = (byte)MathUtil.clip(residual[rOff + 4] + pred[srcOff + 4], -128, 127);
out[pixOff + 5] = (byte)MathUtil.clip(residual[rOff + 5] + pred[srcOff + 5], -128, 127);
out[pixOff + 6] = (byte)MathUtil.clip(residual[rOff + 6] + pred[srcOff + 6], -128, 127);
out[pixOff + 7] = (byte)MathUtil.clip(residual[rOff + 7] + pred[srcOff + 7], -128, 127);
}
public void fillAdd(int[] residual, int pixOff, int val, byte[] pixOut) {
int rOff = 0;
for (int i = 0; i < 8; i++) {
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + val, -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + val, -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + val, -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + val, -128, 127);
pixOut[pixOff + 4] = (byte)MathUtil.clip(residual[rOff + 4] + val, -128, 127);
pixOut[pixOff + 5] = (byte)MathUtil.clip(residual[rOff + 5] + val, -128, 127);
pixOut[pixOff + 6] = (byte)MathUtil.clip(residual[rOff + 6] + val, -128, 127);
pixOut[pixOff + 7] = (byte)MathUtil.clip(residual[rOff + 7] + val, -128, 127);
pixOff += 16;
rOff += 8;
}
}
public void predictVertical(int[] residual, boolean topLeftAvailable, boolean topRightAvailable, byte[] topLeft, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
int pixOff = (blkY << 4) + blkX;
int rOff = 0;
for (int i = 0; i < 8; i++) {
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + this.topBuf[0], -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + this.topBuf[1], -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + this.topBuf[2], -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + this.topBuf[3], -128, 127);
pixOut[pixOff + 4] = (byte)MathUtil.clip(residual[rOff + 4] + this.topBuf[4], -128, 127);
pixOut[pixOff + 5] = (byte)MathUtil.clip(residual[rOff + 5] + this.topBuf[5], -128, 127);
pixOut[pixOff + 6] = (byte)MathUtil.clip(residual[rOff + 6] + this.topBuf[6], -128, 127);
pixOut[pixOff + 7] = (byte)MathUtil.clip(residual[rOff + 7] + this.topBuf[7], -128, 127);
pixOff += 16;
rOff += 8;
}
}
public void predictHorizontal(int[] residual, boolean topLeftAvailable, byte[] topLeft, byte[] leftRow, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateLeft(topLeftAvailable, topLeft, leftRow, blkY, this.leftBuf);
int pixOff = (blkY << 4) + blkX;
int rOff = 0;
for (int i = 0; i < 8; i++) {
pixOut[pixOff] = (byte)MathUtil.clip(residual[rOff] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 1] = (byte)MathUtil.clip(residual[rOff + 1] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 2] = (byte)MathUtil.clip(residual[rOff + 2] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 3] = (byte)MathUtil.clip(residual[rOff + 3] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 4] = (byte)MathUtil.clip(residual[rOff + 4] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 5] = (byte)MathUtil.clip(residual[rOff + 5] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 6] = (byte)MathUtil.clip(residual[rOff + 6] + this.leftBuf[i], -128, 127);
pixOut[pixOff + 7] = (byte)MathUtil.clip(residual[rOff + 7] + this.leftBuf[i], -128, 127);
pixOff += 16;
rOff += 8;
}
}
public void predictDC(int[] residual, boolean topLeftAvailable, boolean topRightAvailable, boolean leftAvailable, boolean topAvailable, byte[] topLeft, byte[] leftRow, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
if (topAvailable && leftAvailable) {
interpolateTop(topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
interpolateLeft(topLeftAvailable, topLeft, leftRow, blkY, this.leftBuf);
int sum1 = this.topBuf[0] + this.topBuf[1] + this.topBuf[2] + this.topBuf[3];
int sum2 = this.topBuf[4] + this.topBuf[5] + this.topBuf[6] + this.topBuf[7];
int sum3 = this.leftBuf[0] + this.leftBuf[1] + this.leftBuf[2] + this.leftBuf[3];
int sum4 = this.leftBuf[4] + this.leftBuf[5] + this.leftBuf[6] + this.leftBuf[7];
fillAdd(residual, (blkY << 4) + blkX, sum1 + sum2 + sum3 + sum4 + 8 >> 4, pixOut);
} else if (leftAvailable) {
interpolateLeft(topLeftAvailable, topLeft, leftRow, blkY, this.leftBuf);
int sum3 = this.leftBuf[0] + this.leftBuf[1] + this.leftBuf[2] + this.leftBuf[3];
int sum4 = this.leftBuf[4] + this.leftBuf[5] + this.leftBuf[6] + this.leftBuf[7];
fillAdd(residual, (blkY << 4) + blkX, sum3 + sum4 + 4 >> 3, pixOut);
} else if (topAvailable) {
interpolateTop(topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
int sum1 = this.topBuf[0] + this.topBuf[1] + this.topBuf[2] + this.topBuf[3];
int sum2 = this.topBuf[4] + this.topBuf[5] + this.topBuf[6] + this.topBuf[7];
fillAdd(residual, (blkY << 4) + blkX, sum1 + sum2 + 4 >> 3, pixOut);
} else {
fillAdd(residual, (blkY << 4) + blkX, 0, pixOut);
}
}
public void predictDiagonalDownLeft(int[] residual, boolean topLeftAvailable, boolean topAvailable, boolean topRightAvailable, byte[] topLeft, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
this.genBuf[0] = (byte)(this.topBuf[0] + this.topBuf[2] + (this.topBuf[1] << 1) + 2 >> 2);
this.genBuf[1] = (byte)(this.topBuf[1] + this.topBuf[3] + (this.topBuf[2] << 1) + 2 >> 2);
this.genBuf[2] = (byte)(this.topBuf[2] + this.topBuf[4] + (this.topBuf[3] << 1) + 2 >> 2);
this.genBuf[3] = (byte)(this.topBuf[3] + this.topBuf[5] + (this.topBuf[4] << 1) + 2 >> 2);
this.genBuf[4] = (byte)(this.topBuf[4] + this.topBuf[6] + (this.topBuf[5] << 1) + 2 >> 2);
this.genBuf[5] = (byte)(this.topBuf[5] + this.topBuf[7] + (this.topBuf[6] << 1) + 2 >> 2);
this.genBuf[6] = (byte)(this.topBuf[6] + this.topBuf[8] + (this.topBuf[7] << 1) + 2 >> 2);
this.genBuf[7] = (byte)(this.topBuf[7] + this.topBuf[9] + (this.topBuf[8] << 1) + 2 >> 2);
this.genBuf[8] = (byte)(this.topBuf[8] + this.topBuf[10] + (this.topBuf[9] << 1) + 2 >> 2);
this.genBuf[9] = (byte)(this.topBuf[9] + this.topBuf[11] + (this.topBuf[10] << 1) + 2 >> 2);
this.genBuf[10] = (byte)(this.topBuf[10] + this.topBuf[12] + (this.topBuf[11] << 1) + 2 >> 2);
this.genBuf[11] = (byte)(this.topBuf[11] + this.topBuf[13] + (this.topBuf[12] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.topBuf[12] + this.topBuf[14] + (this.topBuf[13] << 1) + 2 >> 2);
this.genBuf[13] = (byte)(this.topBuf[13] + this.topBuf[15] + (this.topBuf[14] << 1) + 2 >> 2);
this.genBuf[14] = (byte)(this.topBuf[14] + this.topBuf[15] + (this.topBuf[15] << 1) + 2 >> 2);
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 0, residual, off, 0, pixOut);
copyAdd(this.genBuf, 1, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 2, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 3, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 4, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 5, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 6, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 7, residual, off + 112, 56, pixOut);
}
public void predictDiagonalDownRight(int[] residual, boolean topRightAvailable, byte[] topLeft, byte[] leftRow, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(true, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
interpolateLeft(true, topLeft, leftRow, blkY, this.leftBuf);
int tl = interpolateTopLeft(true, true, topLeft, topLine, leftRow, mbOffX, blkX, blkY);
this.genBuf[0] = (byte)(this.leftBuf[7] + this.leftBuf[5] + (this.leftBuf[6] << 1) + 2 >> 2);
this.genBuf[1] = (byte)(this.leftBuf[6] + this.leftBuf[4] + (this.leftBuf[5] << 1) + 2 >> 2);
this.genBuf[2] = (byte)(this.leftBuf[5] + this.leftBuf[3] + (this.leftBuf[4] << 1) + 2 >> 2);
this.genBuf[3] = (byte)(this.leftBuf[4] + this.leftBuf[2] + (this.leftBuf[3] << 1) + 2 >> 2);
this.genBuf[4] = (byte)(this.leftBuf[3] + this.leftBuf[1] + (this.leftBuf[2] << 1) + 2 >> 2);
this.genBuf[5] = (byte)(this.leftBuf[2] + this.leftBuf[0] + (this.leftBuf[1] << 1) + 2 >> 2);
this.genBuf[6] = (byte)(this.leftBuf[1] + tl + (this.leftBuf[0] << 1) + 2 >> 2);
this.genBuf[7] = (byte)(this.leftBuf[0] + this.topBuf[0] + (tl << 1) + 2 >> 2);
this.genBuf[8] = (byte)(tl + this.topBuf[1] + (this.topBuf[0] << 1) + 2 >> 2);
this.genBuf[9] = (byte)(this.topBuf[0] + this.topBuf[2] + (this.topBuf[1] << 1) + 2 >> 2);
this.genBuf[10] = (byte)(this.topBuf[1] + this.topBuf[3] + (this.topBuf[2] << 1) + 2 >> 2);
this.genBuf[11] = (byte)(this.topBuf[2] + this.topBuf[4] + (this.topBuf[3] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.topBuf[3] + this.topBuf[5] + (this.topBuf[4] << 1) + 2 >> 2);
this.genBuf[13] = (byte)(this.topBuf[4] + this.topBuf[6] + (this.topBuf[5] << 1) + 2 >> 2);
this.genBuf[14] = (byte)(this.topBuf[5] + this.topBuf[7] + (this.topBuf[6] << 1) + 2 >> 2);
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 7, residual, off, 0, pixOut);
copyAdd(this.genBuf, 6, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 5, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 4, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 3, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 2, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 1, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 0, residual, off + 112, 56, pixOut);
}
public void predictVerticalRight(int[] residual, boolean topRightAvailable, byte[] topLeft, byte[] leftRow, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(true, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
interpolateLeft(true, topLeft, leftRow, blkY, this.leftBuf);
int tl = interpolateTopLeft(true, true, topLeft, topLine, leftRow, mbOffX, blkX, blkY);
this.genBuf[0] = (byte)(this.leftBuf[5] + this.leftBuf[3] + (this.leftBuf[4] << 1) + 2 >> 2);
this.genBuf[1] = (byte)(this.leftBuf[3] + this.leftBuf[1] + (this.leftBuf[2] << 1) + 2 >> 2);
this.genBuf[2] = (byte)(this.leftBuf[1] + tl + (this.leftBuf[0] << 1) + 2 >> 2);
this.genBuf[3] = (byte)(tl + this.topBuf[0] + 1 >> 1);
this.genBuf[4] = (byte)(this.topBuf[0] + this.topBuf[1] + 1 >> 1);
this.genBuf[5] = (byte)(this.topBuf[1] + this.topBuf[2] + 1 >> 1);
this.genBuf[6] = (byte)(this.topBuf[2] + this.topBuf[3] + 1 >> 1);
this.genBuf[7] = (byte)(this.topBuf[3] + this.topBuf[4] + 1 >> 1);
this.genBuf[8] = (byte)(this.topBuf[4] + this.topBuf[5] + 1 >> 1);
this.genBuf[9] = (byte)(this.topBuf[5] + this.topBuf[6] + 1 >> 1);
this.genBuf[10] = (byte)(this.topBuf[6] + this.topBuf[7] + 1 >> 1);
this.genBuf[11] = (byte)(this.leftBuf[6] + this.leftBuf[4] + (this.leftBuf[5] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.leftBuf[4] + this.leftBuf[2] + (this.leftBuf[3] << 1) + 2 >> 2);
this.genBuf[13] = (byte)(this.leftBuf[2] + this.leftBuf[0] + (this.leftBuf[1] << 1) + 2 >> 2);
this.genBuf[14] = (byte)(this.leftBuf[0] + this.topBuf[0] + (tl << 1) + 2 >> 2);
this.genBuf[15] = (byte)(tl + this.topBuf[1] + (this.topBuf[0] << 1) + 2 >> 2);
this.genBuf[16] = (byte)(this.topBuf[0] + this.topBuf[2] + (this.topBuf[1] << 1) + 2 >> 2);
this.genBuf[17] = (byte)(this.topBuf[1] + this.topBuf[3] + (this.topBuf[2] << 1) + 2 >> 2);
this.genBuf[18] = (byte)(this.topBuf[2] + this.topBuf[4] + (this.topBuf[3] << 1) + 2 >> 2);
this.genBuf[19] = (byte)(this.topBuf[3] + this.topBuf[5] + (this.topBuf[4] << 1) + 2 >> 2);
this.genBuf[20] = (byte)(this.topBuf[4] + this.topBuf[6] + (this.topBuf[5] << 1) + 2 >> 2);
this.genBuf[21] = (byte)(this.topBuf[5] + this.topBuf[7] + (this.topBuf[6] << 1) + 2 >> 2);
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 3, residual, off, 0, pixOut);
copyAdd(this.genBuf, 14, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 2, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 13, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 1, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 12, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 0, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 11, residual, off + 112, 56, pixOut);
}
public void predictHorizontalDown(int[] residual, boolean topRightAvailable, byte[] topLeft, byte[] leftRow, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(true, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
interpolateLeft(true, topLeft, leftRow, blkY, this.leftBuf);
int tl = interpolateTopLeft(true, true, topLeft, topLine, leftRow, mbOffX, blkX, blkY);
this.genBuf[0] = (byte)(this.leftBuf[7] + this.leftBuf[6] + 1 >> 1);
this.genBuf[1] = (byte)(this.leftBuf[5] + this.leftBuf[7] + (this.leftBuf[6] << 1) + 2 >> 2);
this.genBuf[2] = (byte)(this.leftBuf[6] + this.leftBuf[5] + 1 >> 1);
this.genBuf[3] = (byte)(this.leftBuf[4] + this.leftBuf[6] + (this.leftBuf[5] << 1) + 2 >> 2);
this.genBuf[4] = (byte)(this.leftBuf[5] + this.leftBuf[4] + 1 >> 1);
this.genBuf[5] = (byte)(this.leftBuf[3] + this.leftBuf[5] + (this.leftBuf[4] << 1) + 2 >> 2);
this.genBuf[6] = (byte)(this.leftBuf[4] + this.leftBuf[3] + 1 >> 1);
this.genBuf[7] = (byte)(this.leftBuf[2] + this.leftBuf[4] + (this.leftBuf[3] << 1) + 2 >> 2);
this.genBuf[8] = (byte)(this.leftBuf[3] + this.leftBuf[2] + 1 >> 1);
this.genBuf[9] = (byte)(this.leftBuf[1] + this.leftBuf[3] + (this.leftBuf[2] << 1) + 2 >> 2);
this.genBuf[10] = (byte)(this.leftBuf[2] + this.leftBuf[1] + 1 >> 1);
this.genBuf[11] = (byte)(this.leftBuf[0] + this.leftBuf[2] + (this.leftBuf[1] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.leftBuf[1] + this.leftBuf[0] + 1 >> 1);
this.genBuf[13] = (byte)(tl + this.leftBuf[1] + (this.leftBuf[0] << 1) + 2 >> 2);
this.genBuf[14] = (byte)(this.leftBuf[0] + tl + 1 >> 1);
this.genBuf[15] = (byte)(this.leftBuf[0] + this.topBuf[0] + (tl << 1) + 2 >> 2);
this.genBuf[16] = (byte)(tl + this.topBuf[1] + (this.topBuf[0] << 1) + 2 >> 2);
this.genBuf[17] = (byte)(this.topBuf[0] + this.topBuf[2] + (this.topBuf[1] << 1) + 2 >> 2);
this.genBuf[18] = (byte)(this.topBuf[1] + this.topBuf[3] + (this.topBuf[2] << 1) + 2 >> 2);
this.genBuf[19] = (byte)(this.topBuf[2] + this.topBuf[4] + (this.topBuf[3] << 1) + 2 >> 2);
this.genBuf[20] = (byte)(this.topBuf[3] + this.topBuf[5] + (this.topBuf[4] << 1) + 2 >> 2);
this.genBuf[21] = (byte)(this.topBuf[4] + this.topBuf[6] + (this.topBuf[5] << 1) + 2 >> 2);
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 14, residual, off, 0, pixOut);
copyAdd(this.genBuf, 12, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 10, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 8, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 6, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 4, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 2, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 0, residual, off + 112, 56, pixOut);
}
public void predictVerticalLeft(int[] residual, boolean topLeftAvailable, boolean topRightAvailable, byte[] topLeft, byte[] topLine, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateTop(topLeftAvailable, topRightAvailable, topLeft, topLine, mbOffX + blkX, blkY, this.topBuf);
this.genBuf[0] = (byte)(this.topBuf[0] + this.topBuf[1] + 1 >> 1);
this.genBuf[1] = (byte)(this.topBuf[1] + this.topBuf[2] + 1 >> 1);
this.genBuf[2] = (byte)(this.topBuf[2] + this.topBuf[3] + 1 >> 1);
this.genBuf[3] = (byte)(this.topBuf[3] + this.topBuf[4] + 1 >> 1);
this.genBuf[4] = (byte)(this.topBuf[4] + this.topBuf[5] + 1 >> 1);
this.genBuf[5] = (byte)(this.topBuf[5] + this.topBuf[6] + 1 >> 1);
this.genBuf[6] = (byte)(this.topBuf[6] + this.topBuf[7] + 1 >> 1);
this.genBuf[7] = (byte)(this.topBuf[7] + this.topBuf[8] + 1 >> 1);
this.genBuf[8] = (byte)(this.topBuf[8] + this.topBuf[9] + 1 >> 1);
this.genBuf[9] = (byte)(this.topBuf[9] + this.topBuf[10] + 1 >> 1);
this.genBuf[10] = (byte)(this.topBuf[10] + this.topBuf[11] + 1 >> 1);
this.genBuf[11] = (byte)(this.topBuf[0] + this.topBuf[2] + (this.topBuf[1] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.topBuf[1] + this.topBuf[3] + (this.topBuf[2] << 1) + 2 >> 2);
this.genBuf[13] = (byte)(this.topBuf[2] + this.topBuf[4] + (this.topBuf[3] << 1) + 2 >> 2);
this.genBuf[14] = (byte)(this.topBuf[3] + this.topBuf[5] + (this.topBuf[4] << 1) + 2 >> 2);
this.genBuf[15] = (byte)(this.topBuf[4] + this.topBuf[6] + (this.topBuf[5] << 1) + 2 >> 2);
this.genBuf[16] = (byte)(this.topBuf[5] + this.topBuf[7] + (this.topBuf[6] << 1) + 2 >> 2);
this.genBuf[17] = (byte)(this.topBuf[6] + this.topBuf[8] + (this.topBuf[7] << 1) + 2 >> 2);
this.genBuf[18] = (byte)(this.topBuf[7] + this.topBuf[9] + (this.topBuf[8] << 1) + 2 >> 2);
this.genBuf[19] = (byte)(this.topBuf[8] + this.topBuf[10] + (this.topBuf[9] << 1) + 2 >> 2);
this.genBuf[20] = (byte)(this.topBuf[9] + this.topBuf[11] + (this.topBuf[10] << 1) + 2 >> 2);
this.genBuf[21] = (byte)(this.topBuf[10] + this.topBuf[12] + (this.topBuf[11] << 1) + 2 >> 2);
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 0, residual, off, 0, pixOut);
copyAdd(this.genBuf, 11, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 1, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 12, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 2, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 13, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 3, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 14, residual, off + 112, 56, pixOut);
}
public void predictHorizontalUp(int[] residual, boolean topLeftAvailable, byte[] topLeft, byte[] leftRow, int mbOffX, int blkX, int blkY, byte[] pixOut) {
interpolateLeft(topLeftAvailable, topLeft, leftRow, blkY, this.leftBuf);
this.genBuf[0] = (byte)(this.leftBuf[0] + this.leftBuf[1] + 1 >> 1);
this.genBuf[1] = (byte)(this.leftBuf[2] + this.leftBuf[0] + (this.leftBuf[1] << 1) + 2 >> 2);
this.genBuf[2] = (byte)(this.leftBuf[1] + this.leftBuf[2] + 1 >> 1);
this.genBuf[3] = (byte)(this.leftBuf[3] + this.leftBuf[1] + (this.leftBuf[2] << 1) + 2 >> 2);
this.genBuf[4] = (byte)(this.leftBuf[2] + this.leftBuf[3] + 1 >> 1);
this.genBuf[5] = (byte)(this.leftBuf[4] + this.leftBuf[2] + (this.leftBuf[3] << 1) + 2 >> 2);
this.genBuf[6] = (byte)(this.leftBuf[3] + this.leftBuf[4] + 1 >> 1);
this.genBuf[7] = (byte)(this.leftBuf[5] + this.leftBuf[3] + (this.leftBuf[4] << 1) + 2 >> 2);
this.genBuf[8] = (byte)(this.leftBuf[4] + this.leftBuf[5] + 1 >> 1);
this.genBuf[9] = (byte)(this.leftBuf[6] + this.leftBuf[4] + (this.leftBuf[5] << 1) + 2 >> 2);
this.genBuf[10] = (byte)(this.leftBuf[5] + this.leftBuf[6] + 1 >> 1);
this.genBuf[11] = (byte)(this.leftBuf[7] + this.leftBuf[5] + (this.leftBuf[6] << 1) + 2 >> 2);
this.genBuf[12] = (byte)(this.leftBuf[6] + this.leftBuf[7] + 1 >> 1);
this.genBuf[13] = (byte)(this.leftBuf[6] + this.leftBuf[7] + (this.leftBuf[7] << 1) + 2 >> 2);
this.genBuf[21] = this.leftBuf[7];
this.genBuf[20] = this.leftBuf[7];
this.genBuf[19] = this.leftBuf[7];
this.genBuf[18] = this.leftBuf[7];
this.genBuf[17] = this.leftBuf[7];
this.genBuf[16] = this.leftBuf[7];
this.genBuf[15] = this.leftBuf[7];
this.genBuf[14] = this.leftBuf[7];
int off = (blkY << 4) + blkX;
copyAdd(this.genBuf, 0, residual, off, 0, pixOut);
copyAdd(this.genBuf, 2, residual, off + 16, 8, pixOut);
copyAdd(this.genBuf, 4, residual, off + 32, 16, pixOut);
copyAdd(this.genBuf, 6, residual, off + 48, 24, pixOut);
copyAdd(this.genBuf, 8, residual, off + 64, 32, pixOut);
copyAdd(this.genBuf, 10, residual, off + 80, 40, pixOut);
copyAdd(this.genBuf, 12, residual, off + 96, 48, pixOut);
copyAdd(this.genBuf, 14, residual, off + 112, 56, pixOut);
}
}

View file

@ -0,0 +1,277 @@
package org.jcodec.codecs.h264.decode;
import java.util.Arrays;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.common.model.ColorSpace;
public class MBlock {
public int chromaPredictionMode;
public int mbQPDelta;
public int[] dc;
public int[][][] ac;
public boolean transform8x8Used;
public int[] lumaModes;
public int[] dc1;
public int[] dc2;
public int _cbp;
public int mbType;
public MBType curMbType;
public PB16x16 pb16x16;
public PB168x168 pb168x168;
public PB8x8 pb8x8;
public IPCM ipcm;
public int mbIdx;
public boolean fieldDecoding;
public MBType prevMbType;
public int luma16x16Mode;
public H264Utils.MvList x;
public H264Const.PartPred[] partPreds;
public boolean skipped;
public int[] nCoeff;
public MBlock(ColorSpace chromaFormat) {
this.pb8x8 = new PB8x8();
this.pb16x16 = new PB16x16();
this.pb168x168 = new PB168x168();
this.dc = new int[16];
this.ac = new int[][][] { new int[16][64], new int[4][16], new int[4][16] };
this.lumaModes = new int[16];
this.nCoeff = new int[16];
this.dc1 = new int[16 >> chromaFormat.compWidth[1] >> chromaFormat.compHeight[1]];
this.dc2 = new int[16 >> chromaFormat.compWidth[2] >> chromaFormat.compHeight[2]];
this.ipcm = new IPCM(chromaFormat);
this.x = new H264Utils.MvList(16);
this.partPreds = new H264Const.PartPred[4];
}
public int cbpLuma() {
return this._cbp & 0xF;
}
public int cbpChroma() {
return this._cbp >> 4;
}
public void cbp(int cbpLuma, int cbpChroma) {
this._cbp = cbpLuma & 0xF | cbpChroma << 4;
}
static class PB16x16 {
public int[] refIdx = new int[2];
public int[] mvdX = new int[2];
public int[] mvdY = new int[2];
public void clean() {
this.refIdx[1] = 0;
this.refIdx[0] = 0;
this.mvdX[1] = 0;
this.mvdX[0] = 0;
this.mvdY[1] = 0;
this.mvdY[0] = 0;
}
}
static class PB168x168 {
public int[] refIdx1 = new int[2];
public int[] refIdx2 = new int[2];
public int[] mvdX1 = new int[2];
public int[] mvdY1 = new int[2];
public int[] mvdX2 = new int[2];
public int[] mvdY2 = new int[2];
public void clean() {
this.refIdx1[1] = 0;
this.refIdx1[0] = 0;
this.refIdx2[1] = 0;
this.refIdx2[0] = 0;
this.mvdX1[1] = 0;
this.mvdX1[0] = 0;
this.mvdY1[1] = 0;
this.mvdY1[0] = 0;
this.mvdX2[1] = 0;
this.mvdX2[0] = 0;
this.mvdY2[1] = 0;
this.mvdY2[0] = 0;
}
}
static class PB8x8 {
public int[][] refIdx = new int[2][4];
public int[] subMbTypes = new int[4];
public int[][] mvdX1 = new int[2][4];
public int[][] mvdY1 = new int[2][4];
public int[][] mvdX2 = new int[2][4];
public int[][] mvdY2 = new int[2][4];
public int[][] mvdX3 = new int[2][4];
public int[][] mvdY3 = new int[2][4];
public int[][] mvdX4 = new int[2][4];
public int[][] mvdY4 = new int[2][4];
public void clean() {
this.mvdX1[0][3] = 0;
this.mvdX1[0][2] = 0;
this.mvdX1[0][1] = 0;
this.mvdX1[0][0] = 0;
this.mvdX2[0][3] = 0;
this.mvdX2[0][2] = 0;
this.mvdX2[0][1] = 0;
this.mvdX2[0][0] = 0;
this.mvdX3[0][3] = 0;
this.mvdX3[0][2] = 0;
this.mvdX3[0][1] = 0;
this.mvdX3[0][0] = 0;
this.mvdX4[0][3] = 0;
this.mvdX4[0][2] = 0;
this.mvdX4[0][1] = 0;
this.mvdX4[0][0] = 0;
this.mvdY1[0][3] = 0;
this.mvdY1[0][2] = 0;
this.mvdY1[0][1] = 0;
this.mvdY1[0][0] = 0;
this.mvdY2[0][3] = 0;
this.mvdY2[0][2] = 0;
this.mvdY2[0][1] = 0;
this.mvdY2[0][0] = 0;
this.mvdY3[0][3] = 0;
this.mvdY3[0][2] = 0;
this.mvdY3[0][1] = 0;
this.mvdY3[0][0] = 0;
this.mvdY4[0][3] = 0;
this.mvdY4[0][2] = 0;
this.mvdY4[0][1] = 0;
this.mvdY4[0][0] = 0;
this.mvdX1[1][3] = 0;
this.mvdX1[1][2] = 0;
this.mvdX1[1][1] = 0;
this.mvdX1[1][0] = 0;
this.mvdX2[1][3] = 0;
this.mvdX2[1][2] = 0;
this.mvdX2[1][1] = 0;
this.mvdX2[1][0] = 0;
this.mvdX3[1][3] = 0;
this.mvdX3[1][2] = 0;
this.mvdX3[1][1] = 0;
this.mvdX3[1][0] = 0;
this.mvdX4[1][3] = 0;
this.mvdX4[1][2] = 0;
this.mvdX4[1][1] = 0;
this.mvdX4[1][0] = 0;
this.mvdY1[1][3] = 0;
this.mvdY1[1][2] = 0;
this.mvdY1[1][1] = 0;
this.mvdY1[1][0] = 0;
this.mvdY2[1][3] = 0;
this.mvdY2[1][2] = 0;
this.mvdY2[1][1] = 0;
this.mvdY2[1][0] = 0;
this.mvdY3[1][3] = 0;
this.mvdY3[1][2] = 0;
this.mvdY3[1][1] = 0;
this.mvdY3[1][0] = 0;
this.mvdY4[1][3] = 0;
this.mvdY4[1][2] = 0;
this.mvdY4[1][1] = 0;
this.mvdY4[1][0] = 0;
this.subMbTypes[3] = 0;
this.subMbTypes[2] = 0;
this.subMbTypes[1] = 0;
this.subMbTypes[0] = 0;
this.refIdx[0][3] = 0;
this.refIdx[0][2] = 0;
this.refIdx[0][1] = 0;
this.refIdx[0][0] = 0;
this.refIdx[1][3] = 0;
this.refIdx[1][2] = 0;
this.refIdx[1][1] = 0;
this.refIdx[1][0] = 0;
}
}
static class IPCM {
public int[] samplesLuma = new int[256];
public int[] samplesChroma;
public IPCM(ColorSpace chromaFormat) {
int MbWidthC = 16 >> chromaFormat.compWidth[1];
int MbHeightC = 16 >> chromaFormat.compHeight[1];
this.samplesChroma = new int[2 * MbWidthC * MbHeightC];
}
public void clean() {
Arrays.fill(this.samplesLuma, 0);
Arrays.fill(this.samplesChroma, 0);
}
}
public void clear() {
this.chromaPredictionMode = 0;
this.mbQPDelta = 0;
Arrays.fill(this.dc, 0);
for (int i = 0; i < this.ac.length; i++) {
int[][] aci = this.ac[i];
for (int j = 0; j < aci.length; j++)
Arrays.fill(aci[j], 0);
}
this.transform8x8Used = false;
Arrays.fill(this.lumaModes, 0);
Arrays.fill(this.dc1, 0);
Arrays.fill(this.dc2, 0);
Arrays.fill(this.nCoeff, 0);
this._cbp = 0;
this.mbType = 0;
this.pb16x16.clean();
this.pb168x168.clean();
this.pb8x8.clean();
if (this.curMbType == MBType.I_PCM)
this.ipcm.clean();
this.mbIdx = 0;
this.fieldDecoding = false;
this.prevMbType = null;
this.luma16x16Mode = 0;
this.skipped = false;
this.curMbType = null;
this.x.clear();
this.partPreds[0] = this.partPreds[1] = this.partPreds[2] = this.partPreds[3] = null;
}
}

View file

@ -0,0 +1,256 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class MBlockDecoderBDirect extends MBlockDecoderBase {
private Mapper mapper;
public MBlockDecoderBDirect(Mapper mapper, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
}
public void decode(MBlock mBlock, Picture mb, Frame[][] references) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean lAvb = this.mapper.leftAvailable(mBlock.mbIdx);
boolean tAvb = this.mapper.topAvailable(mBlock.mbIdx);
int mbAddr = this.mapper.getAddress(mBlock.mbIdx);
boolean tlAvb = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean trAvb = this.mapper.topRightAvailable(mBlock.mbIdx);
predictBDirect(references, mbX, mbY, lAvb, tAvb, tlAvb, trAvb, mBlock.x, mBlock.partPreds, mb, H264Const.identityMapping4);
predictChromaInter(references, mBlock.x, mbX << 3, mbY << 3, 1, mb, mBlock.partPreds);
predictChromaInter(references, mBlock.x, mbX << 3, mbY << 3, 2, mb, mBlock.partPreds);
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
this.s.qp = (this.s.qp + mBlock.mbQPDelta + 52) % 52;
this.di.mbQps[0][mbAddr] = this.s.qp;
residualLuma(mBlock, lAvb, tAvb, mbX, mbY);
MBlockDecoderUtils.savePrediction8x8(this.s, mbX, mBlock.x);
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
int qp1 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[0]);
int qp2 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[1]);
decodeChromaResidual(mBlock, lAvb, tAvb, mbX, mbY, qp1, qp2);
this.di.mbQps[1][mbAddr] = qp1;
this.di.mbQps[2][mbAddr] = qp2;
MBlockDecoderUtils.mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? H264Const.COMP_BLOCK_8x8_LUT : H264Const.COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? H264Const.COMP_POS_8x8_LUT : H264Const.COMP_POS_4x4_LUT);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
this.di.mbTypes[mbAddr] = mBlock.curMbType;
this.di.tr8x8Used[mbAddr] = mBlock.transform8x8Used;
}
public void predictBDirect(Frame[][] refs, int mbX, int mbY, boolean lAvb, boolean tAvb, boolean tlAvb, boolean trAvb, H264Utils.MvList x, H264Const.PartPred[] pp, Picture mb, int[] blocks) {
if (this.sh.directSpatialMvPredFlag) {
predictBSpatialDirect(refs, mbX, mbY, lAvb, tAvb, tlAvb, trAvb, x, pp, mb, blocks);
} else {
predictBTemporalDirect(refs, mbX, mbY, lAvb, tAvb, tlAvb, trAvb, x, pp, mb, blocks);
}
}
private void predictBTemporalDirect(Frame[][] refs, int mbX, int mbY, boolean lAvb, boolean tAvb, boolean tlAvb, boolean trAvb, H264Utils.MvList x, H264Const.PartPred[] pp, Picture mb, int[] blocks8x8) {
for (int i = 0; i < blocks8x8.length; i++) {
int blk8x8 = blocks8x8[i];
int blk4x4_0 = H264Const.BLK8x8_BLOCKS[blk8x8][0];
pp[blk8x8] = H264Const.PartPred.Bi;
if (!this.sh.sps.direct8x8InferenceFlag) {
int[] js = H264Const.BLK8x8_BLOCKS[blk8x8];
for (int j = 0; j < js.length; j++) {
int blk4x4 = js[j];
predTemp4x4(refs, mbX, mbY, x, blk4x4);
int blkIndX = blk4x4 & 0x3;
int blkIndY = blk4x4 >> 2;
MBlockDecoderUtils.debugPrint("DIRECT_4x4 [%d, %d]: (%d,%d,%d), (%d,%d,%d)", blkIndY, blkIndX, x.mv0X(blk4x4),
x.mv0Y(blk4x4), x.mv0R(blk4x4), x.mv1X(blk4x4), x.mv1Y(blk4x4), x.mv1R(blk4x4));
int blkPredX = (mbX << 6) + (blkIndX << 4);
int blkPredY = (mbY << 6) + (blkIndY << 4);
this.interpolator.getBlockLuma(refs[0][x.mv0R(blk4x4)], this.mbb[0], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4], blkPredX +
x.mv0X(blk4x4), blkPredY + x.mv0Y(blk4x4), 4, 4);
this.interpolator.getBlockLuma(refs[1][0], this.mbb[1], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4], blkPredX +
x.mv1X(blk4x4), blkPredY + x.mv1Y(blk4x4), 4, 4);
}
} else {
int blk4x4Pred = H264Const.BLK_INV_MAP[blk8x8 * 5];
predTemp4x4(refs, mbX, mbY, x, blk4x4Pred);
propagatePred(x, blk8x8, blk4x4Pred);
int blkIndX = blk4x4_0 & 0x3;
int blkIndY = blk4x4_0 >> 2;
MBlockDecoderUtils.debugPrint("DIRECT_8x8 [%d, %d]: (%d,%d,%d), (%d,%d)", blkIndY, blkIndX, x.mv0X(blk4x4_0),
x.mv0Y(blk4x4_0), x.mv0R(blk4x4_0), x.mv1X(blk4x4_0), x.mv1Y(blk4x4_0), x.mv1R(blk4x4_0));
int blkPredX = (mbX << 6) + (blkIndX << 4);
int blkPredY = (mbY << 6) + (blkIndY << 4);
this.interpolator.getBlockLuma(refs[0][x.mv0R(blk4x4_0)], this.mbb[0], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], blkPredX +
x.mv0X(blk4x4_0), blkPredY + x.mv0Y(blk4x4_0), 8, 8);
this.interpolator.getBlockLuma(refs[1][0], this.mbb[1], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], blkPredX +
x.mv1X(blk4x4_0), blkPredY + x.mv1Y(blk4x4_0), 8, 8);
}
PredictionMerger.mergePrediction(this.sh, x.mv0R(blk4x4_0), x.mv1R(blk4x4_0), H264Const.PartPred.Bi, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], 16, 8, 8,
mb.getPlaneData(0), refs, this.poc);
}
}
private void predTemp4x4(Frame[][] refs, int mbX, int mbY, H264Utils.MvList x, int blk4x4) {
Frame refL0;
int refIdxL0;
int mbWidth = this.sh.sps.picWidthInMbsMinus1 + 1;
Frame picCol = refs[1][0];
int blkIndX = blk4x4 & 0x3;
int blkIndY = blk4x4 >> 2;
int blkPosX = (mbX << 2) + blkIndX;
int blkPosY = (mbY << 2) + blkIndY;
int mvCol = picCol.getMvs().getMv(blkPosX, blkPosY, 0);
if (H264Utils.Mv.mvRef(mvCol) == -1) {
mvCol = picCol.getMvs().getMv(blkPosX, blkPosY, 1);
if (H264Utils.Mv.mvRef(mvCol) == -1) {
refIdxL0 = 0;
refL0 = refs[0][0];
} else {
refL0 = picCol.getRefsUsed()[mbY * mbWidth + mbX][1][H264Utils.Mv.mvRef(mvCol)];
refIdxL0 = findPic(refs[0], refL0);
}
} else {
refL0 = picCol.getRefsUsed()[mbY * mbWidth + mbX][0][H264Utils.Mv.mvRef(mvCol)];
refIdxL0 = findPic(refs[0], refL0);
}
int td = MathUtil.clip(picCol.getPOC() - refL0.getPOC(), -128, 127);
if (!refL0.isShortTerm() || td == 0) {
x.setPair(blk4x4, H264Utils.Mv.packMv(H264Utils.Mv.mvX(mvCol), H264Utils.Mv.mvY(mvCol), refIdxL0), 0);
} else {
int tb = MathUtil.clip(this.poc - refL0.getPOC(), -128, 127);
int tx = (16384 + Math.abs(td / 2)) / td;
int dsf = MathUtil.clip(tb * tx + 32 >> 6, -1024, 1023);
x.setPair(blk4x4, H264Utils.Mv.packMv(dsf * H264Utils.Mv.mvX(mvCol) + 128 >> 8, dsf * H264Utils.Mv.mvY(mvCol) + 128 >> 8, refIdxL0),
H264Utils.Mv.packMv(x.mv0X(blk4x4) - H264Utils.Mv.mvX(mvCol), x.mv0Y(blk4x4) - H264Utils.Mv.mvY(mvCol), 0));
}
}
private int findPic(Frame[] frames, Frame refL0) {
for (int i = 0; i < frames.length; i++) {
if (frames[i] == refL0)
return i;
}
Logger.error("RefPicList0 shall contain refPicCol");
return 0;
}
private void predictBSpatialDirect(Frame[][] refs, int mbX, int mbY, boolean lAvb, boolean tAvb, boolean tlAvb, boolean trAvb, H264Utils.MvList x, H264Const.PartPred[] pp, Picture mb, int[] blocks8x8) {
int a0 = this.s.mvLeft.getMv(0, 0), a1 = this.s.mvLeft.getMv(0, 1);
int b0 = this.s.mvTop.getMv(mbX << 2, 0), b1 = this.s.mvTop.getMv(mbX << 2, 1);
int c0 = this.s.mvTop.getMv((mbX << 2) + 4, 0), c1 = this.s.mvTop.getMv((mbX << 2) + 4, 1);
int d0 = this.s.mvTopLeft.getMv(0, 0);
int d1 = this.s.mvTopLeft.getMv(0, 1);
int refIdxL0 = calcRef(a0, b0, c0, d0, lAvb, tAvb, tlAvb, trAvb, mbX);
int refIdxL1 = calcRef(a1, b1, c1, d1, lAvb, tAvb, tlAvb, trAvb, mbX);
if (refIdxL0 < 0 && refIdxL1 < 0) {
for (int j = 0; j < blocks8x8.length; j++) {
int blk8x8 = blocks8x8[j];
int[] js = H264Const.BLK8x8_BLOCKS[blk8x8];
for (int k = 0; k < js.length; k++) {
int blk4x4 = js[k];
x.setPair(blk4x4, 0, 0);
}
pp[blk8x8] = H264Const.PartPred.Bi;
int blkOffX = (blk8x8 & 0x1) << 5;
int blkOffY = blk8x8 >> 1 << 5;
this.interpolator.getBlockLuma(refs[0][0], this.mbb[0], H264Const.BLK_8x8_MB_OFF_LUMA[blk8x8], (mbX << 6) + blkOffX, (mbY << 6) + blkOffY, 8, 8);
this.interpolator.getBlockLuma(refs[1][0], this.mbb[1], H264Const.BLK_8x8_MB_OFF_LUMA[blk8x8], (mbX << 6) + blkOffX, (mbY << 6) + blkOffY, 8, 8);
PredictionMerger.mergePrediction(this.sh, 0, 0, H264Const.PartPred.Bi, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), H264Const.BLK_8x8_MB_OFF_LUMA[blk8x8], 16, 8, 8,
mb.getPlaneData(0), refs, this.poc);
MBlockDecoderUtils.debugPrint("DIRECT_8x8 [%d, %d]: (0,0,0), (0,0,0)", Integer.valueOf(blk8x8 & 0x2), Integer.valueOf(blk8x8 << 1 & 0x2));
}
return;
}
int mvX0 = MBlockDecoderUtils.calcMVPredictionMedian(a0, b0, c0, d0, lAvb, tAvb, trAvb, tlAvb, refIdxL0, 0);
int mvY0 = MBlockDecoderUtils.calcMVPredictionMedian(a0, b0, c0, d0, lAvb, tAvb, trAvb, tlAvb, refIdxL0, 1);
int mvX1 = MBlockDecoderUtils.calcMVPredictionMedian(a1, b1, c1, d1, lAvb, tAvb, trAvb, tlAvb, refIdxL1, 0);
int mvY1 = MBlockDecoderUtils.calcMVPredictionMedian(a1, b1, c1, d1, lAvb, tAvb, trAvb, tlAvb, refIdxL1, 1);
Frame col = refs[1][0];
H264Const.PartPred partPred = (refIdxL0 >= 0 && refIdxL1 >= 0) ? H264Const.PartPred.Bi : ((refIdxL0 >= 0) ? H264Const.PartPred.L0 : H264Const.PartPred.L1);
for (int i = 0; i < blocks8x8.length; i++) {
int blk8x8 = blocks8x8[i];
int blk4x4_0 = H264Const.BLK8x8_BLOCKS[blk8x8][0];
if (!this.sh.sps.direct8x8InferenceFlag) {
int[] js = H264Const.BLK8x8_BLOCKS[blk8x8];
for (int j = 0; j < js.length; j++) {
int blk4x4 = js[j];
pred4x4(mbX, mbY, x, pp, refIdxL0, refIdxL1, mvX0, mvY0, mvX1, mvY1, col, partPred, blk4x4);
int blkIndX = blk4x4 & 0x3;
int blkIndY = blk4x4 >> 2;
MBlockDecoderUtils.debugPrint("DIRECT_4x4 [%d, %d]: (%d,%d,%d), (%d,%d," + refIdxL1 + ")", blkIndY, blkIndX,
x.mv0X(blk4x4), x.mv0Y(blk4x4), refIdxL0, x.mv1X(blk4x4), x.mv1Y(blk4x4));
int blkPredX = (mbX << 6) + (blkIndX << 4);
int blkPredY = (mbY << 6) + (blkIndY << 4);
if (refIdxL0 >= 0)
this.interpolator.getBlockLuma(refs[0][refIdxL0], this.mbb[0], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4], blkPredX +
x.mv0X(blk4x4), blkPredY + x.mv0Y(blk4x4), 4, 4);
if (refIdxL1 >= 0)
this.interpolator.getBlockLuma(refs[1][refIdxL1], this.mbb[1], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4], blkPredX +
x.mv1X(blk4x4), blkPredY + x.mv1Y(blk4x4), 4, 4);
}
} else {
int blk4x4Pred = H264Const.BLK_INV_MAP[blk8x8 * 5];
pred4x4(mbX, mbY, x, pp, refIdxL0, refIdxL1, mvX0, mvY0, mvX1, mvY1, col, partPred, blk4x4Pred);
propagatePred(x, blk8x8, blk4x4Pred);
int blkIndX = blk4x4_0 & 0x3;
int blkIndY = blk4x4_0 >> 2;
MBlockDecoderUtils.debugPrint("DIRECT_8x8 [%d, %d]: (%d,%d,%d), (%d,%d,%d)", blkIndY, blkIndX, x.mv0X(blk4x4_0),
x.mv0Y(blk4x4_0), refIdxL0, x.mv1X(blk4x4_0), x.mv1Y(blk4x4_0), refIdxL1);
int blkPredX = (mbX << 6) + (blkIndX << 4);
int blkPredY = (mbY << 6) + (blkIndY << 4);
if (refIdxL0 >= 0)
this.interpolator.getBlockLuma(refs[0][refIdxL0], this.mbb[0], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], blkPredX +
x.mv0X(blk4x4_0), blkPredY + x.mv0Y(blk4x4_0), 8, 8);
if (refIdxL1 >= 0)
this.interpolator.getBlockLuma(refs[1][refIdxL1], this.mbb[1], H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], blkPredX +
x.mv1X(blk4x4_0), blkPredY + x.mv1Y(blk4x4_0), 8, 8);
}
PredictionMerger.mergePrediction(this.sh, x.mv0R(blk4x4_0), x.mv1R(blk4x4_0),
(refIdxL0 >= 0) ? ((refIdxL1 >= 0) ? H264Const.PartPred.Bi : H264Const.PartPred.L0) : H264Const.PartPred.L1, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), H264Const.BLK_4x4_MB_OFF_LUMA[blk4x4_0], 16, 8, 8,
mb.getPlaneData(0), refs, this.poc);
}
}
private int calcRef(int a0, int b0, int c0, int d0, boolean lAvb, boolean tAvb, boolean tlAvb, boolean trAvb, int mbX) {
return minPos(minPos(lAvb ? H264Utils.Mv.mvRef(a0) : -1, tAvb ? H264Utils.Mv.mvRef(b0) : -1), trAvb ? H264Utils.Mv.mvRef(c0) : (tlAvb ? H264Utils.Mv.mvRef(d0) : -1));
}
private void propagatePred(H264Utils.MvList x, int blk8x8, int blk4x4Pred) {
int b0 = H264Const.BLK8x8_BLOCKS[blk8x8][0];
int b1 = H264Const.BLK8x8_BLOCKS[blk8x8][1];
int b2 = H264Const.BLK8x8_BLOCKS[blk8x8][2];
int b3 = H264Const.BLK8x8_BLOCKS[blk8x8][3];
x.copyPair(b0, x, blk4x4Pred);
x.copyPair(b1, x, blk4x4Pred);
x.copyPair(b2, x, blk4x4Pred);
x.copyPair(b3, x, blk4x4Pred);
}
private void pred4x4(int mbX, int mbY, H264Utils.MvList x, H264Const.PartPred[] pp, int refL0, int refL1, int mvX0, int mvY0, int mvX1, int mvY1, Frame col, H264Const.PartPred partPred, int blk4x4) {
int blkIndX = blk4x4 & 0x3;
int blkIndY = blk4x4 >> 2;
int blkPosX = (mbX << 2) + blkIndX;
int blkPosY = (mbY << 2) + blkIndY;
int mvCol = col.getMvs().getMv(blkPosX, blkPosY, 0);
if (H264Utils.Mv.mvRef(mvCol) == -1)
mvCol = col.getMvs().getMv(blkPosX, blkPosY, 1);
boolean colZero = (col.isShortTerm() && H264Utils.Mv.mvRef(mvCol) == 0 && MathUtil.abs(H264Utils.Mv.mvX(mvCol)) >> 1 == 0 &&
MathUtil.abs(H264Utils.Mv.mvY(mvCol)) >> 1 == 0);
int x0 = H264Utils.Mv.packMv(0, 0, refL0), x1 = H264Utils.Mv.packMv(0, 0, refL1);
if (refL0 > 0 || !colZero)
x0 = H264Utils.Mv.packMv(mvX0, mvY0, refL0);
if (refL1 > 0 || !colZero)
x1 = H264Utils.Mv.packMv(mvX1, mvY1, refL1);
x.setPair(blk4x4, x0, x1);
pp[H264Const.BLK_8x8_IND[blk4x4]] = partPred;
}
private int minPos(int a, int b) {
return (a >= 0 && b >= 0) ? Math.min(a, b) : Math.max(a, b);
}
}

View file

@ -0,0 +1,186 @@
package org.jcodec.codecs.h264.decode;
import java.util.Arrays;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.MBType;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
import org.jcodec.common.tools.MathUtil;
public class MBlockDecoderBase {
protected DecoderState s;
protected SliceHeader sh;
protected DeblockerInput di;
protected int poc;
protected BlockInterpolator interpolator;
protected Picture[] mbb;
protected int[][] scalingMatrix;
public MBlockDecoderBase(SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
this.interpolator = new BlockInterpolator();
this.s = decoderState;
this.sh = sh;
this.di = di;
this.poc = poc;
this.mbb = new Picture[] { Picture.create(16, 16, this.s.chromaFormat), Picture.create(16, 16, this.s.chromaFormat) };
this.scalingMatrix = initScalingMatrix(sh);
}
void residualLuma(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX, int mbY) {
if (!mBlock.transform8x8Used) {
residualLuma4x4(mBlock);
} else if (this.sh.pps.entropyCodingModeFlag) {
residualLuma8x8CABAC(mBlock);
} else {
residualLuma8x8CAVLC(mBlock);
}
}
private void residualLuma4x4(MBlock mBlock) {
for (int i = 0; i < 16; i++) {
if ((mBlock.cbpLuma() & 1 << i >> 2) != 0) {
CoeffTransformer.dequantizeAC(mBlock.ac[0][i], this.s.qp, getScalingList(mBlock.curMbType.intra ? 0 : 3));
CoeffTransformer.idct4x4(mBlock.ac[0][i]);
}
}
}
protected int[] getScalingList(int which) {
if (this.scalingMatrix == null)
return null;
return this.scalingMatrix[which];
}
protected static int[][] initScalingMatrix(SliceHeader sh2) {
if (sh2.sps.scalingMatrix == null && (sh2.pps.extended == null || sh2.pps.extended.scalingMatrix == null))
return null;
int[][] merged = {
H264Const.defaultScalingList4x4Intra, null, null, H264Const.defaultScalingList4x4Inter, null, null, H264Const.defaultScalingList8x8Intra, H264Const.defaultScalingList8x8Inter, null, null,
null, null };
for (int i = 0; i < 8; i++) {
if (sh2.sps.scalingMatrix != null && sh2.sps.scalingMatrix[i] != null)
merged[i] = sh2.sps.scalingMatrix[i];
if (sh2.pps.extended != null && sh2.pps.extended.scalingMatrix != null && sh2.pps.extended.scalingMatrix[i] != null)
merged[i] = sh2.pps.extended.scalingMatrix[i];
}
if (merged[1] == null)
merged[1] = merged[0];
if (merged[2] == null)
merged[2] = merged[0];
if (merged[4] == null)
merged[4] = merged[3];
if (merged[5] == null)
merged[5] = merged[3];
if (merged[8] == null)
merged[8] = merged[6];
if (merged[10] == null)
merged[10] = merged[6];
if (merged[9] == null)
merged[9] = merged[7];
if (merged[11] == null)
merged[11] = merged[7];
return merged;
}
private void residualLuma8x8CABAC(MBlock mBlock) {
for (int i = 0; i < 4; i++) {
if ((mBlock.cbpLuma() & 1 << i) != 0) {
CoeffTransformer.dequantizeAC8x8(mBlock.ac[0][i], this.s.qp, getScalingList(mBlock.curMbType.intra ? 6 : 7));
CoeffTransformer.idct8x8(mBlock.ac[0][i]);
}
}
}
private void residualLuma8x8CAVLC(MBlock mBlock) {
for (int i = 0; i < 4; i++) {
if ((mBlock.cbpLuma() & 1 << i) != 0) {
CoeffTransformer.dequantizeAC8x8(mBlock.ac[0][i], this.s.qp, getScalingList(mBlock.curMbType.intra ? 6 : 7));
CoeffTransformer.idct8x8(mBlock.ac[0][i]);
}
}
}
public void decodeChroma(MBlock mBlock, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, Picture mb, int qp) {
if (this.s.chromaFormat == ColorSpace.MONO) {
Arrays.fill(mb.getPlaneData(1), (byte)0);
Arrays.fill(mb.getPlaneData(2), (byte)0);
return;
}
int qp1 = calcQpChroma(qp, this.s.chromaQpOffset[0]);
int qp2 = calcQpChroma(qp, this.s.chromaQpOffset[1]);
if (mBlock.cbpChroma() != 0)
decodeChromaResidual(mBlock, leftAvailable, topAvailable, mbX, mbY, qp1, qp2);
int addr = mbY * (this.sh.sps.picWidthInMbsMinus1 + 1) + mbX;
this.di.mbQps[1][addr] = qp1;
this.di.mbQps[2][addr] = qp2;
ChromaPredictionBuilder.predictWithMode(mBlock.ac[1], mBlock.chromaPredictionMode, mbX, leftAvailable, topAvailable, this.s.leftRow[1], this.s.topLine[1], this.s.topLeft[1],
mb.getPlaneData(1));
ChromaPredictionBuilder.predictWithMode(mBlock.ac[2], mBlock.chromaPredictionMode, mbX, leftAvailable, topAvailable, this.s.leftRow[2], this.s.topLine[2], this.s.topLeft[2],
mb.getPlaneData(2));
}
void decodeChromaResidual(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX, int mbY, int crQp1, int crQp2) {
if (mBlock.cbpChroma() != 0) {
if ((mBlock.cbpChroma() & 0x3) > 0) {
chromaDC(mbX, leftAvailable, topAvailable, mBlock.dc1, 1, crQp1, mBlock.curMbType);
chromaDC(mbX, leftAvailable, topAvailable, mBlock.dc2, 2, crQp2, mBlock.curMbType);
}
chromaAC(leftAvailable, topAvailable, mbX, mbY, mBlock.dc1, 1, crQp1, mBlock.curMbType,
((mBlock.cbpChroma() & 0x2) > 0), mBlock.ac[1]);
chromaAC(leftAvailable, topAvailable, mbX, mbY, mBlock.dc2, 2, crQp2, mBlock.curMbType,
((mBlock.cbpChroma() & 0x2) > 0), mBlock.ac[2]);
}
}
private void chromaDC(int mbX, boolean leftAvailable, boolean topAvailable, int[] dc, int comp, int crQp, MBType curMbType) {
CoeffTransformer.invDC2x2(dc);
CoeffTransformer.dequantizeDC2x2(dc, crQp, getScalingList((curMbType.intra ? 6 : 7) + comp * 2));
}
private void chromaAC(boolean leftAvailable, boolean topAvailable, int mbX, int mbY, int[] dc, int comp, int crQp, MBType curMbType, boolean codedAC, int[][] residualOut) {
for (int i = 0; i < dc.length; i++) {
int[] ac = residualOut[i];
if (codedAC)
CoeffTransformer.dequantizeAC(ac, crQp, getScalingList((curMbType.intra ? 0 : 3) + comp));
ac[0] = dc[i];
CoeffTransformer.idct4x4(ac);
}
}
static int calcQpChroma(int qp, int crQpOffset) {
return H264Const.QP_SCALE_CR[MathUtil.clip(qp + crQpOffset, 0, 51)];
}
public void predictChromaInter(Frame[][] refs, H264Utils.MvList vectors, int x, int y, int comp, Picture mb, H264Const.PartPred[] predType) {
for (int blk8x8 = 0; blk8x8 < 4; blk8x8++) {
for (int list = 0; list < 2; list++) {
if (H264Const.usesList(predType[blk8x8], list))
for (int i = 0; i < 4; i++) {
int j = H264Const.BLK_INV_MAP[(blk8x8 << 2) + i];
int mv = vectors.getMv(j, list);
Picture ref = refs[list][H264Utils.Mv.mvRef(mv)];
int blkPox = (j & 0x3) << 1;
int blkPoy = j >> 2 << 1;
int xx = (x + blkPox << 3) + H264Utils.Mv.mvX(mv);
int yy = (y + blkPoy << 3) + H264Utils.Mv.mvY(mv);
BlockInterpolator.getBlockChroma(ref.getPlaneData(comp), ref.getPlaneWidth(comp),
ref.getPlaneHeight(comp), this.mbb[list].getPlaneData(comp), blkPoy * mb.getPlaneWidth(comp) + blkPox,
mb.getPlaneWidth(comp), xx, yy, 2, 2);
}
}
int blk4x4 = H264Const.BLK8x8_BLOCKS[blk8x8][0];
PredictionMerger.mergePrediction(this.sh, vectors.mv0R(blk4x4), vectors.mv1R(blk4x4), predType[blk8x8], comp, this.mbb[0]
.getPlaneData(comp), this.mbb[1].getPlaneData(comp), H264Const.BLK_8x8_MB_OFF_CHROMA[blk8x8],
mb.getPlaneWidth(comp), 4, 4, mb.getPlaneData(comp), refs, this.poc);
}
}
}

View file

@ -0,0 +1,21 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.common.model.Picture;
public class MBlockDecoderIPCM {
private Mapper mapper;
private DecoderState s;
public MBlockDecoderIPCM(Mapper mapper, DecoderState decoderState) {
this.mapper = mapper;
this.s = decoderState;
}
public void decode(MBlock mBlock, Picture mb) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
MBlockDecoderUtils.saveVectIntra(this.s, this.mapper.getMbX(mBlock.mbIdx));
}
}

View file

@ -0,0 +1,233 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.ColorSpace;
import org.jcodec.common.model.Picture;
public class MBlockDecoderInter extends MBlockDecoderBase {
private Mapper mapper;
public MBlockDecoderInter(Mapper mapper, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
}
public void decode16x16(MBlock mBlock, Picture mb, Frame[][] refs, H264Const.PartPred p0) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = this.mapper.topRightAvailable(mBlock.mbIdx);
int address = this.mapper.getAddress(mBlock.mbIdx);
int xx = mbX << 2;
for (int list = 0; list < 2; list++)
predictInter16x16(mBlock, this.mbb[list], refs, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable, topRightAvailable, mBlock.x, xx, list, p0);
PredictionMerger.mergePrediction(this.sh, mBlock.x.mv0R(0), mBlock.x.mv1R(0), p0, 0, this.mbb[0].getPlaneData(0), this.mbb[1]
.getPlaneData(0), 0, 16, 16, 16, mb.getPlaneData(0), refs, this.poc);
mBlock.partPreds[0] = mBlock.partPreds[1] = mBlock.partPreds[2] = mBlock.partPreds[3] = p0;
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 1, mb, mBlock.partPreds);
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 2, mb, mBlock.partPreds);
residualInter(mBlock, refs, leftAvailable, topAvailable, mbX, mbY, this.mapper.getAddress(mBlock.mbIdx));
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
MBlockDecoderUtils.mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? H264Const.COMP_BLOCK_8x8_LUT : H264Const.COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? H264Const.COMP_POS_8x8_LUT : H264Const.COMP_POS_4x4_LUT);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
this.di.mbTypes[address] = mBlock.curMbType;
}
private void predictInter8x16(MBlock mBlock, Picture mb, Picture[][] references, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean trAvailable, H264Utils.MvList x, int list, H264Const.PartPred p0, H264Const.PartPred p1) {
int xx = mbX << 2;
int mvX1 = 0, mvY1 = 0, r1 = -1, mvX2 = 0, mvY2 = 0, r2 = -1;
if (H264Const.usesList(p0, list)) {
int mvpX1 = calcMVPrediction8x16Left(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 2, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, topAvailable, tlAvailable, mBlock.pb168x168.refIdx1[list], 0);
int mvpY1 = calcMVPrediction8x16Left(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 2, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, topAvailable, tlAvailable, mBlock.pb168x168.refIdx1[list], 1);
mvX1 = mBlock.pb168x168.mvdX1[list] + mvpX1;
mvY1 = mBlock.pb168x168.mvdY1[list] + mvpY1;
MBlockDecoderUtils.debugPrint(new Object[] { "MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb168x168.mvdX1[list], mBlock.pb168x168.mvdY1[list],
mvX1, mvY1, mBlock.pb168x168.refIdx1[list] });
this.interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx1[list]], mb, 0, (mbX << 6) + mvX1, (mbY << 6) + mvY1, 8, 16);
r1 = mBlock.pb168x168.refIdx1[list];
}
int v1 = H264Utils.Mv.packMv(mvX1, mvY1, r1);
if (H264Const.usesList(p1, list)) {
int mvpX2 = calcMVPrediction8x16Right(v1, this.s.mvTop.getMv((mbX << 2) + 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTop.getMv((mbX << 2) + 1, list), true, topAvailable, trAvailable, topAvailable, mBlock.pb168x168.refIdx2[list], 0);
int mvpY2 = calcMVPrediction8x16Right(v1, this.s.mvTop.getMv((mbX << 2) + 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTop.getMv((mbX << 2) + 1, list), true, topAvailable, trAvailable, topAvailable, mBlock.pb168x168.refIdx2[list], 1);
mvX2 = mBlock.pb168x168.mvdX2[list] + mvpX2;
mvY2 = mBlock.pb168x168.mvdY2[list] + mvpY2;
MBlockDecoderUtils.debugPrint(new Object[] { "MVP: (" + mvpX2 + ", " + mvpY2 + "), MVD: (" + mBlock.pb168x168.mvdX2[list] + ", " + mBlock.pb168x168.mvdY2[list] + "), MV: (" + mvX2 + "," + mvY2 + "," + mBlock.pb168x168.refIdx2[list] + ")" });
this.interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx2[list]], mb, 8, (mbX << 6) + 32 + mvX2, (mbY << 6) + mvY2, 8, 16);
r2 = mBlock.pb168x168.refIdx2[list];
}
int v2 = H264Utils.Mv.packMv(mvX2, mvY2, r2);
this.s.mvTopLeft.setMv(0, list, this.s.mvTop.getMv(xx + 3, list));
MBlockDecoderUtils.saveVect(this.s.mvTop, list, xx, xx + 2, v1);
MBlockDecoderUtils.saveVect(this.s.mvTop, list, xx + 2, xx + 4, v2);
MBlockDecoderUtils.saveVect(this.s.mvLeft, list, 0, 4, v2);
for (int i = 0; i < 16; i += 4) {
x.setMv(i, list, v1);
x.setMv(i + 1, list, v1);
x.setMv(i + 2, list, v2);
x.setMv(i + 3, list, v2);
}
}
private void predictInter16x8(MBlock mBlock, Picture mb, Picture[][] references, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean trAvailable, int xx, H264Utils.MvList x, H264Const.PartPred p0, H264Const.PartPred p1, int list) {
int mvX1 = 0, mvY1 = 0, mvX2 = 0, mvY2 = 0, r1 = -1, r2 = -1;
if (H264Const.usesList(p0, list)) {
int mvpX1 = calcMVPrediction16x8Top(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, trAvailable, tlAvailable, mBlock.pb168x168.refIdx1[list], 0);
int mvpY1 = calcMVPrediction16x8Top(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, trAvailable, tlAvailable, mBlock.pb168x168.refIdx1[list], 1);
mvX1 = mBlock.pb168x168.mvdX1[list] + mvpX1;
mvY1 = mBlock.pb168x168.mvdY1[list] + mvpY1;
MBlockDecoderUtils.debugPrint(new Object[] { "MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb168x168.mvdX1[list], mBlock.pb168x168.mvdY1[list],
mvX1, mvY1, mBlock.pb168x168.refIdx1[list] });
this.interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx1[list]], mb, 0, (mbX << 6) + mvX1, (mbY << 6) + mvY1, 16, 8);
r1 = mBlock.pb168x168.refIdx1[list];
}
int v1 = H264Utils.Mv.packMv(mvX1, mvY1, r1);
if (H264Const.usesList(p1, list)) {
int mvpX2 = calcMVPrediction16x8Bottom(this.s.mvLeft.getMv(2, list), v1, MBlockDecoderUtils.NULL_VECTOR, this.s.mvLeft.getMv(1, list), leftAvailable, true, false, leftAvailable, mBlock.pb168x168.refIdx2[list], 0);
int mvpY2 = calcMVPrediction16x8Bottom(this.s.mvLeft.getMv(2, list), v1, MBlockDecoderUtils.NULL_VECTOR, this.s.mvLeft.getMv(1, list), leftAvailable, true, false, leftAvailable, mBlock.pb168x168.refIdx2[list], 1);
mvX2 = mBlock.pb168x168.mvdX2[list] + mvpX2;
mvY2 = mBlock.pb168x168.mvdY2[list] + mvpY2;
MBlockDecoderUtils.debugPrint(new Object[] { "MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb168x168.mvdX2[list], mBlock.pb168x168.mvdY2[list],
mvX2, mvY2, mBlock.pb168x168.refIdx2[list] });
this.interpolator.getBlockLuma(references[list][mBlock.pb168x168.refIdx2[list]], mb, 128, (mbX << 6) + mvX2, (mbY << 6) + 32 + mvY2, 16, 8);
r2 = mBlock.pb168x168.refIdx2[list];
}
int v2 = H264Utils.Mv.packMv(mvX2, mvY2, r2);
this.s.mvTopLeft.setMv(0, list, this.s.mvTop.getMv(xx + 3, list));
MBlockDecoderUtils.saveVect(this.s.mvLeft, list, 0, 2, v1);
MBlockDecoderUtils.saveVect(this.s.mvLeft, list, 2, 4, v2);
MBlockDecoderUtils.saveVect(this.s.mvTop, list, xx, xx + 4, v2);
for (int j = 0; j < 8; j++)
x.setMv(j, list, v1);
for (int i = 8; i < 16; i++)
x.setMv(i, list, v2);
}
public void decode16x8(MBlock mBlock, Picture mb, Frame[][] refs, H264Const.PartPred p0, H264Const.PartPred p1) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = this.mapper.topRightAvailable(mBlock.mbIdx);
int address = this.mapper.getAddress(mBlock.mbIdx);
int xx = mbX << 2;
for (int list = 0; list < 2; list++)
predictInter16x8(mBlock, this.mbb[list], refs, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable, topRightAvailable, xx, mBlock.x, p0, p1, list);
PredictionMerger.mergePrediction(this.sh, mBlock.x.mv0R(0), mBlock.x.mv1R(0), p0, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), 0, 16, 16, 8,
mb.getPlaneData(0), refs, this.poc);
PredictionMerger.mergePrediction(this.sh, mBlock.x.mv0R(8), mBlock.x.mv1R(8), p1, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), 128, 16, 16, 8,
mb.getPlaneData(0), refs, this.poc);
mBlock.partPreds[0] = mBlock.partPreds[1] = p0;
mBlock.partPreds[2] = mBlock.partPreds[3] = p1;
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 1, mb, mBlock.partPreds);
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 2, mb, mBlock.partPreds);
residualInter(mBlock, refs, leftAvailable, topAvailable, mbX, mbY, this.mapper.getAddress(mBlock.mbIdx));
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
MBlockDecoderUtils.mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? H264Const.COMP_BLOCK_8x8_LUT : H264Const.COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? H264Const.COMP_POS_8x8_LUT : H264Const.COMP_POS_4x4_LUT);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
this.di.mbTypes[address] = mBlock.curMbType;
}
public void decode8x16(MBlock mBlock, Picture mb, Frame[][] refs, H264Const.PartPred p0, H264Const.PartPred p1) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = this.mapper.topRightAvailable(mBlock.mbIdx);
int address = this.mapper.getAddress(mBlock.mbIdx);
for (int list = 0; list < 2; list++)
predictInter8x16(mBlock, this.mbb[list], refs, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable, topRightAvailable, mBlock.x, list, p0, p1);
PredictionMerger.mergePrediction(this.sh, mBlock.x.mv0R(0), mBlock.x.mv1R(0), p0, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), 0, 16, 8, 16,
mb.getPlaneData(0), refs, this.poc);
PredictionMerger.mergePrediction(this.sh, mBlock.x.mv0R(2), mBlock.x.mv1R(2), p1, 0, this.mbb[0].getPlaneData(0), this.mbb[1].getPlaneData(0), 8, 16, 8, 16,
mb.getPlaneData(0), refs, this.poc);
mBlock.partPreds[0] = mBlock.partPreds[2] = p0;
mBlock.partPreds[1] = mBlock.partPreds[3] = p1;
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 1, mb, mBlock.partPreds);
predictChromaInter(refs, mBlock.x, mbX << 3, mbY << 3, 2, mb, mBlock.partPreds);
residualInter(mBlock, refs, leftAvailable, topAvailable, mbX, mbY, this.mapper.getAddress(mBlock.mbIdx));
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
MBlockDecoderUtils.mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? H264Const.COMP_BLOCK_8x8_LUT : H264Const.COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? H264Const.COMP_POS_8x8_LUT : H264Const.COMP_POS_4x4_LUT);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
this.di.mbTypes[address] = mBlock.curMbType;
}
void predictInter16x16(MBlock mBlock, Picture mb, Picture[][] references, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean trAvailable, H264Utils.MvList x, int xx, int list, H264Const.PartPred curPred) {
int mvX = 0, mvY = 0, r = -1;
if (H264Const.usesList(curPred, list)) {
int mvpX = MBlockDecoderUtils.calcMVPredictionMedian(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, trAvailable, tlAvailable, mBlock.pb16x16.refIdx[list], 0);
int mvpY = MBlockDecoderUtils.calcMVPredictionMedian(this.s.mvLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list),
this.s.mvTop.getMv((mbX << 2) + 4, list), this.s.mvTopLeft.getMv(0, list), leftAvailable, topAvailable, trAvailable, tlAvailable, mBlock.pb16x16.refIdx[list], 1);
mvX = mBlock.pb16x16.mvdX[list] + mvpX;
mvY = mBlock.pb16x16.mvdY[list] + mvpY;
MBlockDecoderUtils.debugPrint(new Object[] { "MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX, mvpY, mBlock.pb16x16.mvdX[list], mBlock.pb16x16.mvdY[list],
mvX, mvY, mBlock.pb16x16.refIdx[list] });
r = mBlock.pb16x16.refIdx[list];
this.interpolator.getBlockLuma(references[list][r], mb, 0, (mbX << 6) + mvX, (mbY << 6) + mvY, 16, 16);
}
int v = H264Utils.Mv.packMv(mvX, mvY, r);
this.s.mvTopLeft.setMv(0, list, this.s.mvTop.getMv(xx + 3, list));
MBlockDecoderUtils.saveVect(this.s.mvTop, list, xx, xx + 4, v);
MBlockDecoderUtils.saveVect(this.s.mvLeft, list, 0, 4, v);
for (int i = 0; i < 16; i++)
x.setMv(i, list, v);
}
private void residualInter(MBlock mBlock, Frame[][] refs, boolean leftAvailable, boolean topAvailable, int mbX, int mbY, int mbAddr) {
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
this.s.qp = (this.s.qp + mBlock.mbQPDelta + 52) % 52;
this.di.mbQps[0][mbAddr] = this.s.qp;
residualLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
if (this.s.chromaFormat != ColorSpace.MONO) {
int qp1 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[0]);
int qp2 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[1]);
decodeChromaResidual(mBlock, leftAvailable, topAvailable, mbX, mbY, qp1, qp2);
this.di.mbQps[1][mbAddr] = qp1;
this.di.mbQps[2][mbAddr] = qp2;
}
this.di.tr8x8Used[mbAddr] = mBlock.transform8x8Used;
}
public int calcMVPrediction16x8Top(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb, boolean dAvb, int refIdx, int comp) {
if (bAvb && H264Utils.Mv.mvRef(b) == refIdx)
return H264Utils.Mv.mvC(b, comp);
return MBlockDecoderUtils.calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
public int calcMVPrediction16x8Bottom(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb, boolean dAvb, int refIdx, int comp) {
if (aAvb && H264Utils.Mv.mvRef(a) == refIdx)
return H264Utils.Mv.mvC(a, comp);
return MBlockDecoderUtils.calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
public int calcMVPrediction8x16Left(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb, boolean dAvb, int refIdx, int comp) {
if (aAvb && H264Utils.Mv.mvRef(a) == refIdx)
return H264Utils.Mv.mvC(a, comp);
return MBlockDecoderUtils.calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
public int calcMVPrediction8x16Right(int a, int b, int c, int d, boolean aAvb, boolean bAvb, boolean cAvb, boolean dAvb, int refIdx, int comp) {
int lc = cAvb ? c : (dAvb ? d : MBlockDecoderUtils.NULL_VECTOR);
if (H264Utils.Mv.mvRef(lc) == refIdx)
return H264Utils.Mv.mvC(lc, comp);
return MBlockDecoderUtils.calcMVPredictionMedian(a, b, c, d, aAvb, bAvb, cAvb, dAvb, refIdx, comp);
}
}

View file

@ -0,0 +1,211 @@
package org.jcodec.codecs.h264.decode;
import java.util.Arrays;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.H264Utils;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.Frame;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.codecs.h264.io.model.SliceType;
import org.jcodec.common.model.Picture;
public class MBlockDecoderInter8x8 extends MBlockDecoderBase {
private Mapper mapper;
private MBlockDecoderBDirect bDirectDecoder;
public MBlockDecoderInter8x8(Mapper mapper, MBlockDecoderBDirect bDirectDecoder, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
this.bDirectDecoder = bDirectDecoder;
}
public void decode(MBlock mBlock, Frame[][] references, Picture mb, SliceType sliceType, boolean ref0) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
int mbAddr = this.mapper.getAddress(mBlock.mbIdx);
boolean topLeftAvailable = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = this.mapper.topRightAvailable(mBlock.mbIdx);
if (sliceType == SliceType.P) {
predict8x8P(mBlock, references[0], mb, ref0, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable, topRightAvailable, mBlock.x, mBlock.partPreds);
} else {
predict8x8B(mBlock, references, mb, ref0, mbX, mbY, leftAvailable, topAvailable, topLeftAvailable, topRightAvailable, mBlock.x, mBlock.partPreds);
}
predictChromaInter(references, mBlock.x, mbX << 3, mbY << 3, 1, mb, mBlock.partPreds);
predictChromaInter(references, mBlock.x, mbX << 3, mbY << 3, 2, mb, mBlock.partPreds);
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
this.s.qp = (this.s.qp + mBlock.mbQPDelta + 52) % 52;
this.di.mbQps[0][mbAddr] = this.s.qp;
residualLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
MBlockDecoderUtils.saveMvs(this.di, mBlock.x, mbX, mbY);
int qp1 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[0]);
int qp2 = calcQpChroma(this.s.qp, this.s.chromaQpOffset[1]);
decodeChromaResidual(mBlock, leftAvailable, topAvailable, mbX, mbY, qp1, qp2);
this.di.mbQps[1][mbAddr] = qp1;
this.di.mbQps[2][mbAddr] = qp2;
MBlockDecoderUtils.mergeResidual(mb, mBlock.ac, mBlock.transform8x8Used ? H264Const.COMP_BLOCK_8x8_LUT : H264Const.COMP_BLOCK_4x4_LUT,
mBlock.transform8x8Used ? H264Const.COMP_POS_8x8_LUT : H264Const.COMP_POS_4x4_LUT);
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
this.di.mbTypes[mbAddr] = mBlock.curMbType;
this.di.tr8x8Used[mbAddr] = mBlock.transform8x8Used;
}
private void predict8x8P(MBlock mBlock, Picture[] references, Picture mb, boolean ref0, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean topRightAvailable, H264Utils.MvList x, H264Const.PartPred[] pp) {
decodeSubMb8x8(mBlock, 0, mBlock.pb8x8.subMbTypes[0], references, mbX << 6, mbY << 6, this.s.mvTopLeft.getMv(0, 0),
this.s.mvTop.getMv(mbX << 2, 0), this.s.mvTop.getMv((mbX << 2) + 1, 0), this.s.mvTop.getMv((mbX << 2) + 2, 0),
this.s.mvLeft.getMv(0, 0), this.s.mvLeft.getMv(1, 0), tlAvailable, topAvailable, topAvailable, leftAvailable, mBlock.x, 0, 1, 4, 5, mBlock.pb8x8.refIdx[0][0], mb, 0, 0);
decodeSubMb8x8(mBlock, 1, mBlock.pb8x8.subMbTypes[1], references, (mbX << 6) + 32, mbY << 6,
this.s.mvTop.getMv((mbX << 2) + 1, 0), this.s.mvTop.getMv((mbX << 2) + 2, 0), this.s.mvTop.getMv((mbX << 2) + 3, 0),
this.s.mvTop.getMv((mbX << 2) + 4, 0), x.getMv(1, 0), x.getMv(5, 0), topAvailable, topAvailable, topRightAvailable, true, x, 2, 3, 6, 7, mBlock.pb8x8.refIdx[0][1], mb, 8, 0);
decodeSubMb8x8(mBlock, 2, mBlock.pb8x8.subMbTypes[2], references, mbX << 6, (mbY << 6) + 32,
this.s.mvLeft.getMv(1, 0), x.getMv(4, 0), x.getMv(5, 0), x.getMv(6, 0), this.s.mvLeft.getMv(2, 0),
this.s.mvLeft.getMv(3, 0), leftAvailable, true, true, leftAvailable, x, 8, 9, 12, 13, mBlock.pb8x8.refIdx[0][2], mb, 128, 0);
decodeSubMb8x8(mBlock, 3, mBlock.pb8x8.subMbTypes[3], references, (mbX << 6) + 32, (mbY << 6) + 32,
x.getMv(5, 0), x.getMv(6, 0), x.getMv(7, 0), MBlockDecoderUtils.NULL_VECTOR, x.getMv(9, 0), x.getMv(13, 0), true, true, false, true, x, 10, 11, 14, 15, mBlock.pb8x8.refIdx[0][3], mb, 136, 0);
for (int i = 0; i < 4; i++) {
int blk4x4 = H264Const.BLK8x8_BLOCKS[i][0];
PredictionMerger.weightPrediction(this.sh, x.mv0R(blk4x4), 0, mb.getPlaneData(0), H264Const.BLK_8x8_MB_OFF_LUMA[i], 16, 8, 8,
mb.getPlaneData(0));
}
MBlockDecoderUtils.savePrediction8x8(this.s, mbX, x);
Arrays.fill(pp, H264Const.PartPred.L0);
}
private void predict8x8B(MBlock mBlock, Frame[][] refs, Picture mb, boolean ref0, int mbX, int mbY, boolean leftAvailable, boolean topAvailable, boolean tlAvailable, boolean topRightAvailable, H264Utils.MvList x, H264Const.PartPred[] p) {
for (int k = 0; k < 4; k++)
p[k] = H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[k]];
for (int i = 0; i < 4; i++) {
if (p[i] == H264Const.PartPred.Direct)
this.bDirectDecoder.predictBDirect(refs, mbX, mbY, leftAvailable, topAvailable, tlAvailable, topRightAvailable, x, p, mb, H264Const.ARRAY[i]);
}
for (int list = 0; list < 2; list++) {
if (H264Const.usesList(H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[0]], list))
decodeSubMb8x8(mBlock, 0, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[0]], refs[list], mbX << 6, mbY << 6,
this.s.mvTopLeft.getMv(0, list), this.s.mvTop.getMv(mbX << 2, list), this.s.mvTop.getMv((mbX << 2) + 1, list),
this.s.mvTop.getMv((mbX << 2) + 2, list), this.s.mvLeft.getMv(0, list), this.s.mvLeft.getMv(1, list), tlAvailable, topAvailable, topAvailable, leftAvailable, x, 0, 1, 4, 5, mBlock.pb8x8.refIdx[list][0], this.mbb[list], 0, list);
if (H264Const.usesList(H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[1]], list))
decodeSubMb8x8(mBlock, 1, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[1]], refs[list], (mbX << 6) + 32, mbY << 6,
this.s.mvTop.getMv((mbX << 2) + 1, list), this.s.mvTop.getMv((mbX << 2) + 2, list),
this.s.mvTop.getMv((mbX << 2) + 3, list), this.s.mvTop.getMv((mbX << 2) + 4, list), x.getMv(1, list),
x.getMv(5, list), topAvailable, topAvailable, topRightAvailable, true, x, 2, 3, 6, 7, mBlock.pb8x8.refIdx[list][1], this.mbb[list], 8, list);
if (H264Const.usesList(H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[2]], list))
decodeSubMb8x8(mBlock, 2, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[2]], refs[list], mbX << 6, (mbY << 6) + 32,
this.s.mvLeft.getMv(1, list), x.getMv(4, list), x.getMv(5, list), x.getMv(6, list),
this.s.mvLeft.getMv(2, list), this.s.mvLeft.getMv(3, list), leftAvailable, true, true, leftAvailable, x, 8, 9, 12, 13, mBlock.pb8x8.refIdx[list][2], this.mbb[list], 128, list);
if (H264Const.usesList(H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[3]], list))
decodeSubMb8x8(mBlock, 3, H264Const.bSubMbTypes[mBlock.pb8x8.subMbTypes[3]], refs[list], (mbX << 6) + 32, (mbY << 6) + 32,
x.getMv(5, list), x.getMv(6, list), x.getMv(7, list), MBlockDecoderUtils.NULL_VECTOR,
x.getMv(9, list), x.getMv(13, list), true, true, false, true, x, 10, 11, 14, 15, mBlock.pb8x8.refIdx[list][3], this.mbb[list], 136, list);
}
for (int j = 0; j < 4; j++) {
int blk4x4 = H264Const.BLK8x8_BLOCKS[j][0];
PredictionMerger.mergePrediction(this.sh, x.mv0R(blk4x4), x.mv1R(blk4x4), H264Const.bPartPredModes[mBlock.pb8x8.subMbTypes[j]], 0, this.mbb[0]
.getPlaneData(0), this.mbb[1].getPlaneData(0), H264Const.BLK_8x8_MB_OFF_LUMA[j], 16, 8, 8,
mb.getPlaneData(0), refs, this.poc);
}
MBlockDecoderUtils.savePrediction8x8(this.s, mbX, x);
}
private void decodeSubMb8x8(MBlock mBlock, int partNo, int subMbType, Picture[] references, int offX, int offY, int tl, int t0, int t1, int tr, int l0, int l1, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, H264Utils.MvList x, int i00, int i01, int i10, int i11, int refIdx, Picture mb, int off, int list) {
switch (subMbType) {
case 3:
decodeSub4x4(mBlock, partNo, references, offX, offY, tl, t0, t1, tr, l0, l1, tlAvb, tAvb, trAvb, lAvb, x, i00, i01, i10, i11, refIdx, mb, off, list);
break;
case 2:
decodeSub4x8(mBlock, partNo, references, offX, offY, tl, t0, t1, tr, l0, tlAvb, tAvb, trAvb, lAvb, x, i00, i01, i10, i11, refIdx, mb, off, list);
break;
case 1:
decodeSub8x4(mBlock, partNo, references, offX, offY, tl, t0, tr, l0, l1, tlAvb, tAvb, trAvb, lAvb, x, i00, i01, i10, i11, refIdx, mb, off, list);
break;
case 0:
decodeSub8x8(mBlock, partNo, references, offX, offY, tl, t0, tr, l0, tlAvb, tAvb, trAvb, lAvb, x, i00, i01, i10, i11, refIdx, mb, off, list);
break;
}
}
private void decodeSub8x8(MBlock mBlock, int partNo, Picture[] references, int offX, int offY, int tl, int t0, int tr, int l0, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, H264Utils.MvList x, int i00, int i01, int i10, int i11, int refIdx, Picture mb, int off, int list) {
int mvpX = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 0);
int mvpY = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 1);
int mv = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX1[list][partNo] + mvpX, mBlock.pb8x8.mvdY1[list][partNo] + mvpY, refIdx);
x.setMv(i00, list, mv);
x.setMv(i01, list, mv);
x.setMv(i10, list, mv);
x.setMv(i11, list, mv);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX, mvpY, mBlock.pb8x8.mvdX1[list][partNo], mBlock.pb8x8.mvdY1[list][partNo],
H264Utils.Mv.mvX(mv), H264Utils.Mv.mvY(mv), refIdx);
this.interpolator.getBlockLuma(references[refIdx], mb, off, offX + H264Utils.Mv.mvX(mv), offY + H264Utils.Mv.mvY(mv), 8, 8);
}
private void decodeSub8x4(MBlock mBlock, int partNo, Picture[] references, int offX, int offY, int tl, int t0, int tr, int l0, int l1, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, H264Utils.MvList x, int i00, int i01, int i10, int i11, int refIdx, Picture mb, int off, int list) {
int mvpX1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 0);
int mvpY1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, tr, tl, lAvb, tAvb, trAvb, tlAvb, refIdx, 1);
int mv1 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX1[list][partNo] + mvpX1, mBlock.pb8x8.mvdY1[list][partNo] + mvpY1, refIdx);
x.setMv(i00, list, mv1);
x.setMv(i01, list, mv1);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb8x8.mvdX1[list][partNo], mBlock.pb8x8.mvdY1[list][partNo],
H264Utils.Mv.mvX(mv1), H264Utils.Mv.mvY(mv1), refIdx);
int mvpX2 = MBlockDecoderUtils.calcMVPredictionMedian(l1, mv1, MBlockDecoderUtils.NULL_VECTOR, l0, lAvb, true, false, lAvb, refIdx, 0);
int mvpY2 = MBlockDecoderUtils.calcMVPredictionMedian(l1, mv1, MBlockDecoderUtils.NULL_VECTOR, l0, lAvb, true, false, lAvb, refIdx, 1);
int mv2 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX2[list][partNo] + mvpX2, mBlock.pb8x8.mvdY2[list][partNo] + mvpY2, refIdx);
x.setMv(i10, list, mv2);
x.setMv(i11, list, mv2);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb8x8.mvdX2[list][partNo], mBlock.pb8x8.mvdY2[list][partNo],
H264Utils.Mv.mvX(mv2), H264Utils.Mv.mvY(mv2), refIdx);
this.interpolator.getBlockLuma(references[refIdx], mb, off, offX + H264Utils.Mv.mvX(mv1), offY + H264Utils.Mv.mvY(mv1), 8, 4);
this.interpolator.getBlockLuma(references[refIdx], mb, off + mb.getWidth() * 4, offX + H264Utils.Mv.mvX(mv2), offY +
H264Utils.Mv.mvY(mv2) + 16, 8, 4);
}
private void decodeSub4x8(MBlock mBlock, int partNo, Picture[] references, int offX, int offY, int tl, int t0, int t1, int tr, int l0, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, H264Utils.MvList x, int i00, int i01, int i10, int i11, int refIdx, Picture mb, int off, int list) {
int mvpX1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 0);
int mvpY1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 1);
int mv1 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX1[list][partNo] + mvpX1, mBlock.pb8x8.mvdY1[list][partNo] + mvpY1, refIdx);
x.setMv(i00, list, mv1);
x.setMv(i10, list, mv1);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb8x8.mvdX1[list][partNo], mBlock.pb8x8.mvdY1[list][partNo],
H264Utils.Mv.mvX(mv1), H264Utils.Mv.mvY(mv1), refIdx);
int mvpX2 = MBlockDecoderUtils.calcMVPredictionMedian(mv1, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 0);
int mvpY2 = MBlockDecoderUtils.calcMVPredictionMedian(mv1, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 1);
int mv2 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX2[list][partNo] + mvpX2, mBlock.pb8x8.mvdY2[list][partNo] + mvpY2, refIdx);
x.setMv(i01, list, mv2);
x.setMv(i11, list, mv2);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb8x8.mvdX2[list][partNo], mBlock.pb8x8.mvdY2[list][partNo],
H264Utils.Mv.mvX(mv2), H264Utils.Mv.mvY(mv2), refIdx);
this.interpolator.getBlockLuma(references[refIdx], mb, off, offX + H264Utils.Mv.mvX(mv1), offY + H264Utils.Mv.mvY(mv1), 4, 8);
this.interpolator.getBlockLuma(references[refIdx], mb, off + 4, offX + H264Utils.Mv.mvX(mv2) + 16, offY + H264Utils.Mv.mvY(mv2), 4, 8);
}
private void decodeSub4x4(MBlock mBlock, int partNo, Picture[] references, int offX, int offY, int tl, int t0, int t1, int tr, int l0, int l1, boolean tlAvb, boolean tAvb, boolean trAvb, boolean lAvb, H264Utils.MvList x, int i00, int i01, int i10, int i11, int refIdx, Picture mb, int off, int list) {
int mvpX1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 0);
int mvpY1 = MBlockDecoderUtils.calcMVPredictionMedian(l0, t0, t1, tl, lAvb, tAvb, tAvb, tlAvb, refIdx, 1);
int mv1 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX1[list][partNo] + mvpX1, mBlock.pb8x8.mvdY1[list][partNo] + mvpY1, refIdx);
x.setMv(i00, list, mv1);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX1, mvpY1, mBlock.pb8x8.mvdX1[list][partNo], mBlock.pb8x8.mvdY1[list][partNo],
H264Utils.Mv.mvX(mv1), H264Utils.Mv.mvY(mv1), refIdx);
int mvpX2 = MBlockDecoderUtils.calcMVPredictionMedian(mv1, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 0);
int mvpY2 = MBlockDecoderUtils.calcMVPredictionMedian(mv1, t1, tr, t0, true, tAvb, trAvb, tAvb, refIdx, 1);
int mv2 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX2[list][partNo] + mvpX2, mBlock.pb8x8.mvdY2[list][partNo] + mvpY2, refIdx);
x.setMv(i01, list, mv2);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX2, mvpY2, mBlock.pb8x8.mvdX2[list][partNo], mBlock.pb8x8.mvdY2[list][partNo],
H264Utils.Mv.mvX(mv2), H264Utils.Mv.mvY(mv2), refIdx);
int mvpX3 = MBlockDecoderUtils.calcMVPredictionMedian(l1, mv1, mv2, l0, lAvb, true, true, lAvb, refIdx, 0);
int mvpY3 = MBlockDecoderUtils.calcMVPredictionMedian(l1, mv1, mv2, l0, lAvb, true, true, lAvb, refIdx, 1);
int mv3 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX3[list][partNo] + mvpX3, mBlock.pb8x8.mvdY3[list][partNo] + mvpY3, refIdx);
x.setMv(i10, list, mv3);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX3, mvpY3, mBlock.pb8x8.mvdX3[list][partNo], mBlock.pb8x8.mvdY3[list][partNo],
H264Utils.Mv.mvX(mv3), H264Utils.Mv.mvY(mv3), refIdx);
int mvpX4 = MBlockDecoderUtils.calcMVPredictionMedian(mv3, mv2, MBlockDecoderUtils.NULL_VECTOR, mv1, true, true, false, true, refIdx, 0);
int mvpY4 = MBlockDecoderUtils.calcMVPredictionMedian(mv3, mv2, MBlockDecoderUtils.NULL_VECTOR, mv1, true, true, false, true, refIdx, 1);
int mv4 = H264Utils.Mv.packMv(mBlock.pb8x8.mvdX4[list][partNo] + mvpX4, mBlock.pb8x8.mvdY4[list][partNo] + mvpY4, refIdx);
x.setMv(i11, list, mv4);
MBlockDecoderUtils.debugPrint("MVP: (%d, %d), MVD: (%d, %d), MV: (%d,%d,%d)", mvpX4, mvpY4, mBlock.pb8x8.mvdX4[list][partNo], mBlock.pb8x8.mvdY4[list][partNo],
H264Utils.Mv.mvX(mv4), H264Utils.Mv.mvY(mv4), refIdx);
this.interpolator.getBlockLuma(references[refIdx], mb, off, offX + H264Utils.Mv.mvX(mv1), offY + H264Utils.Mv.mvY(mv1), 4, 4);
this.interpolator.getBlockLuma(references[refIdx], mb, off + 4, offX + H264Utils.Mv.mvX(mv2) + 16, offY + H264Utils.Mv.mvY(mv2), 4, 4);
this.interpolator.getBlockLuma(references[refIdx], mb, off + mb.getWidth() * 4, offX + H264Utils.Mv.mvX(mv3), offY + H264Utils.Mv.mvY(mv3) + 16, 4, 4);
this.interpolator.getBlockLuma(references[refIdx], mb, off + mb.getWidth() * 4 + 4, offX + H264Utils.Mv.mvX(mv4) + 16, offY +
H264Utils.Mv.mvY(mv4) + 16, 4, 4);
}
}

View file

@ -0,0 +1,45 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.Picture;
public class MBlockDecoderIntra16x16 extends MBlockDecoderBase {
private Mapper mapper;
public MBlockDecoderIntra16x16(Mapper mapper, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
}
public void decode(MBlock mBlock, Picture mb) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
int address = this.mapper.getAddress(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
this.s.qp = (this.s.qp + mBlock.mbQPDelta + 52) % 52;
this.di.mbQps[0][address] = this.s.qp;
residualLumaI16x16(mBlock, leftAvailable, topAvailable, mbX, mbY);
Intra16x16PredictionBuilder.predictWithMode(mBlock.luma16x16Mode, mBlock.ac[0], leftAvailable, topAvailable, this.s.leftRow[0], this.s.topLine[0], this.s.topLeft[0], mbX << 4,
mb.getPlaneData(0));
decodeChroma(mBlock, mbX, mbY, leftAvailable, topAvailable, mb, this.s.qp);
this.di.mbTypes[address] = mBlock.curMbType;
MBlockDecoderUtils.collectPredictors(this.s, mb, mbX);
MBlockDecoderUtils.saveMvsIntra(this.di, mbX, mbY);
MBlockDecoderUtils.saveVectIntra(this.s, this.mapper.getMbX(mBlock.mbIdx));
}
private void residualLumaI16x16(MBlock mBlock, boolean leftAvailable, boolean topAvailable, int mbX, int mbY) {
CoeffTransformer.invDC4x4(mBlock.dc);
int[] scalingList = getScalingList(0);
CoeffTransformer.dequantizeDC4x4(mBlock.dc, this.s.qp, scalingList);
CoeffTransformer.reorderDC4x4(mBlock.dc);
for (int i = 0; i < 16; i++) {
if ((mBlock.cbpLuma() & 1 << i >> 2) != 0)
CoeffTransformer.dequantizeAC(mBlock.ac[0][i], this.s.qp, scalingList);
mBlock.ac[0][i][0] = mBlock.dc[i];
CoeffTransformer.idct4x4(mBlock.ac[0][i]);
}
}
}

View file

@ -0,0 +1,60 @@
package org.jcodec.codecs.h264.decode;
import org.jcodec.codecs.h264.H264Const;
import org.jcodec.codecs.h264.decode.aso.Mapper;
import org.jcodec.codecs.h264.io.model.SliceHeader;
import org.jcodec.common.model.Picture;
public class MBlockDecoderIntraNxN extends MBlockDecoderBase {
private Mapper mapper;
private Intra8x8PredictionBuilder prediction8x8Builder;
public MBlockDecoderIntraNxN(Mapper mapper, SliceHeader sh, DeblockerInput di, int poc, DecoderState decoderState) {
super(sh, di, poc, decoderState);
this.mapper = mapper;
this.prediction8x8Builder = new Intra8x8PredictionBuilder();
}
public void decode(MBlock mBlock, Picture mb) {
int mbX = this.mapper.getMbX(mBlock.mbIdx);
int mbY = this.mapper.getMbY(mBlock.mbIdx);
int mbAddr = this.mapper.getAddress(mBlock.mbIdx);
boolean leftAvailable = this.mapper.leftAvailable(mBlock.mbIdx);
boolean topAvailable = this.mapper.topAvailable(mBlock.mbIdx);
boolean topLeftAvailable = this.mapper.topLeftAvailable(mBlock.mbIdx);
boolean topRightAvailable = this.mapper.topRightAvailable(mBlock.mbIdx);
if (mBlock.cbpLuma() > 0 || mBlock.cbpChroma() > 0)
this.s.qp = (this.s.qp + mBlock.mbQPDelta + 52) % 52;
this.di.mbQps[0][mbAddr] = this.s.qp;
residualLuma(mBlock, leftAvailable, topAvailable, mbX, mbY);
if (!mBlock.transform8x8Used) {
for (int i = 0; i < 16; i++) {
int blkX = (i & 0x3) << 2;
int blkY = i & 0xFFFFFFFC;
int bi = H264Const.BLK_INV_MAP[i];
boolean trAvailable = (((bi == 0 || bi == 1 || bi == 4) && topAvailable) || (bi == 5 && topRightAvailable) || bi == 2 || bi == 6 || bi == 8 || bi == 9 || bi == 10 || bi == 12 || bi == 14);
Intra4x4PredictionBuilder.predictWithMode(mBlock.lumaModes[bi], mBlock.ac[0][bi],
(blkX == 0) ? leftAvailable : true, (blkY == 0) ? topAvailable : true, trAvailable, this.s.leftRow[0], this.s.topLine[0], this.s.topLeft[0], mbX << 4, blkX, blkY,
mb.getPlaneData(0));
}
} else {
for (int i = 0; i < 4; i++) {
int blkX = (i & 0x1) << 1;
int blkY = i & 0x2;
boolean trAvailable = ((i == 0 && topAvailable) || (i == 1 && topRightAvailable) || i == 2);
boolean tlAvailable = (i == 0) ? topLeftAvailable : ((i == 1) ? topAvailable : ((i == 2) ? leftAvailable :
true));
this.prediction8x8Builder.predictWithMode(mBlock.lumaModes[i], mBlock.ac[0][i],
(blkX == 0) ? leftAvailable : true, (blkY == 0) ? topAvailable : true, tlAvailable, trAvailable, this.s.leftRow[0], this.s.topLine[0], this.s.topLeft[0], mbX << 4, blkX << 2, blkY << 2,
mb.getPlaneData(0));
}
}
decodeChroma(mBlock, mbX, mbY, leftAvailable, topAvailable, mb, this.s.qp);
this.di.mbTypes[mbAddr] = mBlock.curMbType;
this.di.tr8x8Used[mbAddr] = mBlock.transform8x8Used;
MBlockDecoderUtils.collectChromaPredictors(this.s, mb, mbX);
MBlockDecoderUtils.saveMvsIntra(this.di, mbX, mbY);
MBlockDecoderUtils.saveVectIntra(this.s, this.mapper.getMbX(mBlock.mbIdx));
}
}

Some files were not shown because too many files have changed in this diff Show more