From c46c18362c16da3285756a60d1ff02b5b4e1de1b Mon Sep 17 00:00:00 2001 From: Werner Randelshofer Date: Sat, 4 Mar 2023 19:07:55 +0100 Subject: [PATCH 1/3] Improve performance of input and output streams with VarHandle. --- .../monte/media/iff/MC68000InputStream.java | 73 +++----- .../monte/media/iff/MC68000OutputStream.java | 25 ++- .../media/avi/DataChunkOutputStream.java | 37 ++-- .../org/monte/media/io/ByteArray.java | 165 ++++++++++++++++++ .../monte/media/io/ImageInputStreamImpl2.java | 25 +-- .../media/quicktime/DataAtomInputStream.java | 49 ++---- .../media/quicktime/DataAtomOutputStream.java | 73 +++----- .../media/riff/RIFFPrimitivesInputStream.java | 52 ++---- pom.xml | 13 +- 9 files changed, 281 insertions(+), 231 deletions(-) create mode 100644 org.monte.media/src/main/java/org.monte.media/org/monte/media/io/ByteArray.java diff --git a/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/iff/MC68000InputStream.java b/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/iff/MC68000InputStream.java index a988098..2148956 100755 --- a/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/iff/MC68000InputStream.java +++ b/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/iff/MC68000InputStream.java @@ -4,6 +4,8 @@ */ package org.monte.media.iff; +import org.monte.media.io.ByteArray; + import java.io.EOFException; import java.io.FilterInputStream; import java.io.IOException; @@ -21,7 +23,8 @@ public class MC68000InputStream extends FilterInputStream { - private long scan_, mark_; + private long scan, mark; + private byte byteBuffer[] = new byte[8]; /** * Creates a new instance. @@ -42,7 +45,7 @@ public int readUBYTE() if (b0 == -1) { throw new EOFException(); } - scan_ += 1; + scan += 1; return b0 & 0xff; } @@ -53,14 +56,8 @@ public int readUBYTE() */ public short readWORD() throws IOException { - int b0 = in.read(); - int b1 = in.read(); - if (b1 == -1) { - throw new EOFException(); - } - scan_ += 2; - - return (short) (((b0 & 0xff) << 8) | (b1 & 0xff)); + readFully(byteBuffer, 0, 2); + return ByteArray.getShortBE(byteBuffer, 0); } /** @@ -78,19 +75,8 @@ public int readUWORD() */ public int readLONG() throws IOException { - int b0 = in.read(); - int b1 = in.read(); - int b2 = in.read(); - int b3 = in.read(); - if (b3 == -1) { - throw new EOFException(); - } - scan_ += 4; - - return ((b0 & 0xff) << 24) - | ((b1 & 0xff) << 16) - | ((b2 & 0xff) << 8) - | (b3 & 0xff); + readFully(byteBuffer, 0, 4); + return ByteArray.getIntBE(byteBuffer, 0); } /** @@ -99,27 +85,8 @@ public int readLONG() */ public long readINT64() throws IOException { - int b0 = in.read(); - int b1 = in.read(); - int b2 = in.read(); - int b3 = in.read(); - int b4 = in.read(); - int b5 = in.read(); - int b6 = in.read(); - int b7 = in.read(); - if (b7 == -1) { - throw new EOFException(); - } - scan_ += 4; - - return ((b0 & 0xffL) << 56) - | ((b1 & 0xffL) << 48) - | ((b2 & 0xffL) << 40) - | ((b3 & 0xffL) << 32) - | ((b4 & 0xffL) << 24) - | ((b5 & 0xffL) << 16) - | ((b6 & 0xffL) << 8) - | (b7 & 0xffL); + readFully(byteBuffer, 0, 8); + return ByteArray.getLongBE(byteBuffer, 0); } /** @@ -137,7 +104,7 @@ public long readULONG() */ public void align() throws IOException { - if (scan_ % 2 == 1) { + if (scan % 2 == 1) { skipFully(1); } } @@ -147,7 +114,7 @@ public void align() * stream filter). */ public long getScan() { - return scan_; + return scan; } /** @@ -156,7 +123,7 @@ public long getScan() { public int read() throws IOException { int data = in.read(); - scan_++; + scan++; return data; } @@ -172,7 +139,7 @@ public void readFully(byte[] b, int offset, int length) throw new EOFException(); } count += current; - scan_ += current; + scan += current; } } @@ -183,7 +150,7 @@ public int read(byte[] b, int offset, int length) throws IOException { int count = in.read(b, offset, length); if (count > 0) { - scan_ += count; + scan += count; } return count; } @@ -196,7 +163,7 @@ public int read(byte[] b, int offset, int length) */ public void mark(int readlimit) { in.mark(readlimit); - mark_ = scan_; + mark = scan; } /** @@ -208,7 +175,7 @@ public void mark(int readlimit) { public void reset() throws IOException { in.reset(); - scan_ = mark_; + scan = mark; } /** @@ -218,7 +185,7 @@ public void reset() public long skip(long n) throws IOException { long skipped = in.skip(n); - scan_ += skipped; + scan += skipped; return skipped; } @@ -240,7 +207,7 @@ public void skipFully(long n) if (cur == 0) { throw new EOFException(); } - scan_ += total; + scan += total; } /** diff --git a/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/iff/MC68000OutputStream.java b/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/iff/MC68000OutputStream.java index 87d760c..a5769d5 100755 --- a/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/iff/MC68000OutputStream.java +++ b/org.monte.media.amigaatari/src/main/java/org.monte.media.amigaatari/org/monte/media/iff/MC68000OutputStream.java @@ -4,6 +4,8 @@ */ package org.monte.media.iff; +import org.monte.media.io.ByteArray; + import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -20,6 +22,7 @@ public class MC68000OutputStream extends FilterOutputStream { * If this counter overflows, it will be wrapped to Integer.MAX_VALUE. */ protected long written; + private byte byteBuffer[] = new byte[8]; /** * Creates a new instance. @@ -29,35 +32,31 @@ public MC68000OutputStream(OutputStream out) { } public void writeLONG(int v) throws IOException { - out.write((v >>> 24) & 0xFF); - out.write((v >>> 16) & 0xFF); - out.write((v >>> 8) & 0xFF); - out.write((v >>> 0) & 0xFF); + ByteArray.setIntBE(byteBuffer, 0, v); + out.write(byteBuffer, 0, 4); incCount(4); } public void writeULONG(long v) throws IOException { - out.write((int) ((v >>> 24) & 0xFF)); - out.write((int) ((v >>> 16) & 0xFF)); - out.write((int) ((v >>> 8) & 0xFF)); - out.write((int) ((v >>> 0) & 0xFF)); + ByteArray.setIntBE(byteBuffer, 0, (int) v); + out.write(byteBuffer, 0, 4); incCount(4); } public void writeWORD(int v) throws IOException { - out.write((v >>> 8) & 0xFF); - out.write((v >>> 0) & 0xFF); + ByteArray.setShortBE(byteBuffer, 0, (short) v); + out.write(byteBuffer, 0, 2); incCount(2); } public void writeUWORD(int v) throws IOException { - out.write((v >>> 8) & 0xFF); - out.write((v >>> 0) & 0xFF); + ByteArray.setShortBE(byteBuffer, 0, (short) v); + out.write(byteBuffer, 0, 2); incCount(2); } public void writeUBYTE(int v) throws IOException { - out.write((v >>> 0) & 0xFF); + out.write(v & 0xFF); incCount(1); } diff --git a/org.monte.media/src/main/java/org.monte.media/org/monte/media/avi/DataChunkOutputStream.java b/org.monte.media/src/main/java/org.monte.media/org/monte/media/avi/DataChunkOutputStream.java index 32e9de0..a1c1998 100755 --- a/org.monte.media/src/main/java/org.monte.media/org/monte/media/avi/DataChunkOutputStream.java +++ b/org.monte.media/src/main/java/org.monte.media/org/monte/media/avi/DataChunkOutputStream.java @@ -4,6 +4,8 @@ */ package org.monte.media.avi; +import org.monte.media.io.ByteArray; + import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -26,7 +28,7 @@ public class DataChunkOutputStream extends FilterOutputStream { * Whether flush and close request shall be forwarded to underlying stream. */ private boolean forwardFlushAndClose; - + private byte byteBuffer[] = new byte[8]; public DataChunkOutputStream(OutputStream out) { this(out, true); } @@ -107,7 +109,7 @@ public synchronized void write(int b) throws IOException { /** * Writes an int to the underlying output stream as four - * bytes, high byte first. If no exception is thrown, the counter + * bytes, low byte first. If no exception is thrown, the counter * written is incremented by 4. * * @param v an int to be written. @@ -115,10 +117,8 @@ public synchronized void write(int b) throws IOException { * @see java.io.FilterOutputStream#out */ public void writeInt(int v) throws IOException { - out.write((v >>> 0) & 0xff); - out.write((v >>> 8) & 0xff); - out.write((v >>> 16) & 0xff); - out.write((v >>> 24) & 0xff); + ByteArray.setIntLE(byteBuffer, 0, v); + out.write(byteBuffer, 0, 4); incCount(4); } @@ -129,10 +129,8 @@ public void writeInt(int v) throws IOException { * @throws java.io.IOException */ public void writeUInt(long v) throws IOException { - out.write((int) ((v >>> 0) & 0xff)); - out.write((int) ((v >>> 8) & 0xff)); - out.write((int) ((v >>> 16) & 0xff)); - out.write((int) ((v >>> 24) & 0xff)); + ByteArray.setIntLE(byteBuffer, 0, (int) v); + out.write(byteBuffer, 0, 4); incCount(4); } @@ -143,8 +141,8 @@ public void writeUInt(long v) throws IOException { * @throws java.io.IOException */ public void writeShort(int v) throws IOException { - out.write((v >>> 0) & 0xff); - out.write((v >> 8) & 0xff); + ByteArray.setShortLE(byteBuffer, 0, (short) v); + out.write(byteBuffer, 0, 2); incCount(2); } @@ -178,20 +176,14 @@ public void writeInts24(int[] v, int off, int len) throws IOException { } public void writeLong(long v) throws IOException { - out.write((int) (v >>> 0) & 0xff); - out.write((int) (v >>> 8) & 0xff); - out.write((int) (v >>> 16) & 0xff); - out.write((int) (v >>> 24) & 0xff); - out.write((int) (v >>> 32) & 0xff); - out.write((int) (v >>> 40) & 0xff); - out.write((int) (v >>> 48) & 0xff); - out.write((int) (v >>> 56) & 0xff); + ByteArray.setLongLE(byteBuffer, 0, v); + out.write(byteBuffer, 0, 8); incCount(8); } public void writeUShort(int v) throws IOException { - out.write((v >>> 0) & 0xff); - out.write((v >> 8) & 0xff); + ByteArray.setShortLE(byteBuffer, 0, (short) v); + out.write(byteBuffer, 0, 2); incCount(2); } @@ -213,7 +205,6 @@ protected void incCount(int value) { * If the counter overflows, it will be wrapped to Integer.MAX_VALUE. * * @return the value of the written field. - * @see java.io.DataOutputStream#written */ public final long size() { return written; diff --git a/org.monte.media/src/main/java/org.monte.media/org/monte/media/io/ByteArray.java b/org.monte.media/src/main/java/org.monte.media/org/monte/media/io/ByteArray.java new file mode 100644 index 0000000..4af1446 --- /dev/null +++ b/org.monte.media/src/main/java/org.monte.media/org/monte/media/io/ByteArray.java @@ -0,0 +1,165 @@ +/* + * @(#)Main.java + * Copyright © 2023 Werner Randelshofer, Switzerland. MIT License. + */ + +package org.monte.media.io; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; + +/** + * Utility methods for reading/writing primitive values into byte arrays. + */ +public class ByteArray { + private static final VarHandle SHORT_LE = MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle SHORT_BE = MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.BIG_ENDIAN); + private static final VarHandle INT_LE = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle INT_BE = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN); + + private static final VarHandle LONG_LE = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle LONG_BE = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); + + + /** + * Don't let anyone instantiate this class. + */ + private ByteArray() { + + } + + + /** + * Reads a short in big endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @return the value + */ + public static short getShortBE(byte[] array, int offset) { + return (short) SHORT_BE.get(array, offset); + } + + /** + * Reads a short in little endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @return the value + */ + public static short getShortLE(byte[] array, int offset) { + return (short) SHORT_LE.get(array, offset); + } + + /** + * Reads an int in big endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @return the value + */ + public static int getIntBE(byte[] array, int offset) { + return (int) INT_BE.get(array, offset); + } + + /** + * Reads an int in little endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @return the value + */ + public static int getIntLE(byte[] array, int offset) { + return (int) INT_LE.get(array, offset); + } + + /** + * Reads a long in big endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @return the value + */ + public static long getLongBE(byte[] array, int offset) { + return (long) LONG_BE.get(array, offset); + } + + /** + * Reads a long in little endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @return the value + */ + public static long getLongLE(byte[] array, int offset) { + return (long) LONG_LE.get(array, offset); + } + + /** + * Writes a short in big endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @param value the value + */ + public static void setShortBE(byte[] array, int offset, short value) { + SHORT_BE.set(array, offset, value); + } + + /** + * Writes a short in little endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @param value the value + */ + public static void setShortLE(byte[] array, int offset, short value) { + SHORT_LE.set(array, offset, value); + } + + /** + * Writes an int in big endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @param value the value + */ + public static void setIntBE(byte[] array, int offset, int value) { + INT_BE.set(array, offset, value); + } + + /** + * Writes an int in little endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @param value the value + */ + public static void setIntLE(byte[] array, int offset, int value) { + INT_LE.set(array, offset, value); + } + + /** + * Writes a long in big endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @param value the value + */ + public static void setLongBE(byte[] array, int offset, long value) { + LONG_BE.set(array, offset, value); + } + + /** + * Writes a long in little endian order at the specified array offset. + * + * @param array an array + * @param offset the offset + * @param value the value + */ + public static void setLongLE(byte[] array, int offset, long value) { + LONG_LE.set(array, offset, value); + } + +} diff --git a/org.monte.media/src/main/java/org.monte.media/org/monte/media/io/ImageInputStreamImpl2.java b/org.monte.media/src/main/java/org.monte.media/org/monte/media/io/ImageInputStreamImpl2.java index c98bd85..a2645d8 100755 --- a/org.monte.media/src/main/java/org.monte.media/org/monte/media/io/ImageInputStreamImpl2.java +++ b/org.monte.media/src/main/java/org.monte.media/org/monte/media/io/ImageInputStreamImpl2.java @@ -32,28 +32,15 @@ public abstract class ImageInputStreamImpl2 extends ImageInputStreamImpl { @Override public short readShort() throws IOException { readFully(byteBuf, 0, 2); - - if (byteOrder == ByteOrder.BIG_ENDIAN) { - return (short) - (((byteBuf[0] & 0xff) << 8) | ((byteBuf[1] & 0xff) << 0)); - } else { - return (short) - (((byteBuf[1] & 0xff) << 8) | ((byteBuf[0] & 0xff) << 0)); - } + return (byteOrder == ByteOrder.BIG_ENDIAN) + ? ByteArray.getShortBE(byteBuf, 0) + : ByteArray.getShortLE(byteBuf, 0); } public int readInt() throws IOException { readFully(byteBuf, 0, 4); - - if (byteOrder == ByteOrder.BIG_ENDIAN) { - return - (((byteBuf[0] & 0xff) << 24) | ((byteBuf[1] & 0xff) << 16) | - ((byteBuf[2] & 0xff) << 8) | ((byteBuf[3] & 0xff) << 0)); - } else { - return - (((byteBuf[3] & 0xff) << 24) | ((byteBuf[2] & 0xff) << 16) | - ((byteBuf[1] & 0xff) << 8) | ((byteBuf[0] & 0xff) << 0)); - } + return (byteOrder == ByteOrder.BIG_ENDIAN) + ? ByteArray.getIntBE(byteBuf, 0) + : ByteArray.getIntLE(byteBuf, 0); } - } diff --git a/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/DataAtomInputStream.java b/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/DataAtomInputStream.java index 0e336b2..199ebff 100755 --- a/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/DataAtomInputStream.java +++ b/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/DataAtomInputStream.java @@ -4,6 +4,7 @@ */ package org.monte.media.quicktime; +import org.monte.media.io.ByteArray; import org.monte.media.io.ImageInputStreamAdapter; import javax.imageio.stream.ImageInputStream; @@ -23,7 +24,7 @@ public class DataAtomInputStream extends FilterInputStream { protected static final long MAC_TIMESTAMP_EPOCH = new GregorianCalendar(1904, GregorianCalendar.JANUARY, 1).getTimeInMillis(); - private byte readBuffer[] = new byte[8]; + private byte byteBuffer[] = new byte[8]; public DataAtomInputStream(InputStream in) { super(in); @@ -42,25 +43,18 @@ public final byte readByte() throws IOException { } public final short readShort() throws IOException { - readFully(readBuffer, 0, 2); - return (short) ((readBuffer[0] << 8) + (readBuffer[1] << 0)); + readFully(byteBuffer, 0, 2); + return ByteArray.getShortBE(byteBuffer, 0); } public final int readInt() throws IOException { - readFully(readBuffer, 0, 4); - return ((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + (readBuffer[3] << 0)); + readFully(byteBuffer, 0, 4); + return ByteArray.getIntBE(byteBuffer, 0); } public final long readLong() throws IOException { - readFully(readBuffer, 0, 8); - return (((long) readBuffer[0] << 56) - + ((long) (readBuffer[1] & 255) << 48) - + ((long) (readBuffer[2] & 255) << 40) - + ((long) (readBuffer[3] & 255) << 32) - + ((long) (readBuffer[4] & 255) << 24) - + ((readBuffer[5] & 255) << 16) - + ((readBuffer[6] & 255) << 8) - + ((readBuffer[7] & 255) << 0)); + readFully(byteBuffer, 0, 8); + return ByteArray.getLongBE(byteBuffer, 0); } public final int readUByte() throws IOException { @@ -122,7 +116,7 @@ public double readFixed16D16() throws IOException { int wholePart = readUShort(); int fractionPart = readUShort(); - return new Double(wholePart + fractionPart / 65536.0); + return (wholePart + fractionPart) / 65536.0; } /** @@ -133,7 +127,7 @@ public double readFixed2D30() throws IOException { int wholePart = fixed >>> 30; int fractionPart = fixed & 0x3fffffff; - return new Double(wholePart + fractionPart / (double) 0x3fffffff); + return (wholePart + fractionPart) / (double) 0x3fffffff; } /** @@ -144,18 +138,13 @@ public double readFixed8D8() throws IOException { int wholePart = fixed >>> 8; int fractionPart = fixed & 0xff; - return new Double(wholePart + fractionPart / 256f); + return (wholePart + fractionPart) / 256.0; } public String readType() throws IOException { - int id = readInt(); - byte[] b = new byte[4]; - b[0] = (byte) ((id >>> 24) & 0xff); - b[1] = (byte) ((id >>> 16) & 0xff); - b[2] = (byte) ((id >>> 8) & 0xff); - b[3] = (byte) (id & 0xff); + readFully(byteBuffer, 0, 4); try { - return new String(b, "ASCII"); + return new String(byteBuffer, 0, 4, "ASCII"); } catch (UnsupportedEncodingException ex) { InternalError ie = new InternalError("ASCII not supported"); ie.initCause(ex); @@ -172,11 +161,11 @@ public String readPString() throws IOException { if (size < 0) { return ""; } - byte[] b = new byte[size]; - readFully(b); + byte[] b = size <= byteBuffer.length ? byteBuffer : new byte[size]; + readFully(b, 0, size); try { - return new String(b, "ASCII"); + return new String(b, 0, size, "ASCII"); } catch (UnsupportedEncodingException ex) { InternalError ie = new InternalError("ASCII not supported"); ie.initCause(ex); @@ -199,11 +188,11 @@ public String readPString(int fixedSize) throws IOException { skipBytes(fixedSize); return ""; } - byte[] b = new byte[fixedSize]; - readFully(b); + byte[] b = fixedSize <= byteBuffer.length ? byteBuffer : new byte[fixedSize]; + readFully(b, 0, fixedSize); try { - return new String(b, 0, size, "ASCII"); + return new String(b, 0, fixedSize, "ASCII"); } catch (UnsupportedEncodingException ex) { InternalError ie = new InternalError("ASCII not supported"); ie.initCause(ex); diff --git a/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/DataAtomOutputStream.java b/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/DataAtomOutputStream.java index 8b66c8d..08275d5 100755 --- a/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/DataAtomOutputStream.java +++ b/org.monte.media/src/main/java/org.monte.media/org/monte/media/quicktime/DataAtomOutputStream.java @@ -4,6 +4,8 @@ */ package org.monte.media.quicktime; +import org.monte.media.io.ByteArray; + import javax.imageio.stream.ImageOutputStreamImpl; import java.io.FilterOutputStream; import java.io.IOException; @@ -27,6 +29,7 @@ public class DataAtomOutputStream extends FilterOutputStream { * If this counter overflows, it will be wrapped to Integer.MAX_VALUE. */ protected long written; + private byte byteBuffer[] = new byte[8]; public DataAtomOutputStream(OutputStream out) { super(out); @@ -57,7 +60,7 @@ public void writeType(String s) throws IOException { * * @param v a byte value to be written. * @throws IOException if an I/O error occurs. - * @see java.io.FilterOutputStream#out + * @see FilterOutputStream#out */ public final void writeByte(int v) throws IOException { out.write(v); @@ -74,7 +77,7 @@ public final void writeByte(int v) throws IOException { * @param off the start offset in the data. * @param len the number of bytes to write. * @throws IOException if an I/O error occurs. - * @see java.io.FilterOutputStream#out + * @see FilterOutputStream#out */ @Override public synchronized void write(byte b[], int off, int len) @@ -93,7 +96,7 @@ public synchronized void write(byte b[], int off, int len) * * @param b the byte to be written. * @throws IOException if an I/O error occurs. - * @see java.io.FilterOutputStream#out + * @see FilterOutputStream#out */ @Override public synchronized void write(int b) throws IOException { @@ -108,40 +111,33 @@ public synchronized void write(int b) throws IOException { * * @param v an int to be written. * @throws IOException if an I/O error occurs. - * @see java.io.FilterOutputStream#out + * @see FilterOutputStream#out */ public void writeInt(int v) throws IOException { - out.write((v >>> 24) & 0xff); - out.write((v >>> 16) & 0xff); - out.write((v >>> 8) & 0xff); - out.write((v >>> 0) & 0xff); - incCount(4); + ByteArray.setIntBE(byteBuffer, 0, v); + write(byteBuffer, 0, 4); } /** * Writes an unsigned 32 bit integer value. * * @param v The value - * @throws java.io.IOException + * @throws IOException */ public void writeUInt(long v) throws IOException { - out.write((int) ((v >>> 24) & 0xff)); - out.write((int) ((v >>> 16) & 0xff)); - out.write((int) ((v >>> 8) & 0xff)); - out.write((int) ((v >>> 0) & 0xff)); - incCount(4); + ByteArray.setIntBE(byteBuffer, 0, (int) v); + write(byteBuffer, 0, 4); } /** * Writes a signed 16 bit integer value. * * @param v The value - * @throws java.io.IOException + * @throws IOException */ public void writeShort(int v) throws IOException { - out.write((v >> 8) & 0xff); - out.write((v >>> 0) & 0xff); - incCount(2); + ByteArray.setShortBE(byteBuffer, 0, (short) v); + write(byteBuffer, 0, 2); } /** @@ -149,7 +145,7 @@ public void writeShort(int v) throws IOException { * * @param v an int to be written. * @throws IOException if an I/O error occurs. - * @see java.io.FilterOutputStream#out + * @see FilterOutputStream#out */ public void writeBCD2(int v) throws IOException { out.write(((v % 100 / 10) << 4) | (v % 10)); @@ -161,7 +157,7 @@ public void writeBCD2(int v) throws IOException { * * @param v an int to be written. * @throws IOException if an I/O error occurs. - * @see java.io.FilterOutputStream#out + * @see FilterOutputStream#out */ public void writeBCD4(int v) throws IOException { out.write(((v % 10000 / 1000) << 4) | (v % 1000 / 100)); @@ -173,7 +169,7 @@ public void writeBCD4(int v) throws IOException { * Writes a 32-bit Mac timestamp (seconds since 1902). * * @param date - * @throws java.io.IOException + * @throws IOException */ public void writeMacTimestamp(Date date) throws IOException { long millis = date.getTime(); @@ -187,7 +183,7 @@ public void writeMacTimestamp(Date date) throws IOException { * * @param f an int to be written. * @throws IOException if an I/O error occurs. - * @see java.io.FilterOutputStream#out + * @see FilterOutputStream#out */ public void writeFixed16D16(double f) throws IOException { double v = (f >= 0) ? f : -f; @@ -207,7 +203,7 @@ public void writeFixed16D16(double f) throws IOException { * * @param f an int to be written. * @throws IOException if an I/O error occurs. - * @see java.io.FilterOutputStream#out + * @see FilterOutputStream#out */ public void writeFixed2D30(double f) throws IOException { double v = (f >= 0) ? f : -f; @@ -227,7 +223,7 @@ public void writeFixed2D30(double f) throws IOException { * * @param f an int to be written. * @throws IOException if an I/O error occurs. - * @see java.io.FilterOutputStream#out + * @see FilterOutputStream#out */ public void writeFixed8D8(double f) throws IOException { double v = (f >= 0) ? f : -f; @@ -246,7 +242,7 @@ public void writeFixed8D8(double f) throws IOException { * Writes a Pascal String. * * @param s - * @throws java.io.IOException + * @throws IOException */ public void writePString(String s) throws IOException { if (s.length() > 0xffff) { @@ -269,7 +265,7 @@ public void writePString(String s) throws IOException { * * @param s * @param length the fixed size in bytes - * @throws java.io.IOException + * @throws IOException */ public void writePString(String s, int length) throws IOException { if (s.length() > length) { @@ -294,21 +290,13 @@ public void writePString(String s, int length) throws IOException { } public void writeLong(long v) throws IOException { - out.write((int) (v >>> 56) & 0xff); - out.write((int) (v >>> 48) & 0xff); - out.write((int) (v >>> 40) & 0xff); - out.write((int) (v >>> 32) & 0xff); - out.write((int) (v >>> 24) & 0xff); - out.write((int) (v >>> 16) & 0xff); - out.write((int) (v >>> 8) & 0xff); - out.write((int) (v >>> 0) & 0xff); - incCount(8); + ByteArray.setLongBE(byteBuffer, 0, v); + write(byteBuffer, 0, 8); } public void writeUShort(int v) throws IOException { - out.write((v >> 8) & 0xff); - out.write((v >>> 0) & 0xff); - incCount(2); + ByteArray.setShortBE(byteBuffer, 0, (short) v); + write(byteBuffer, 0, 2); } /** @@ -359,13 +347,10 @@ public void writeInts(int[] i, int off, int len) throws IOException { write(b, 0, len * 4); } - private byte[] byteBuf = new byte[3]; public void writeInt24(int v) throws IOException { - byteBuf[0] = (byte) (v >>> 16); - byteBuf[1] = (byte) (v >>> 8); - byteBuf[2] = (byte) (v >>> 0); - write(byteBuf, 0, 3); + ByteArray.setIntBE(byteBuffer, 0, v); + write(byteBuffer, 0, 3); } public void writeInts24(int[] i, int off, int len) throws IOException { diff --git a/org.monte.media/src/main/java/org.monte.media/org/monte/media/riff/RIFFPrimitivesInputStream.java b/org.monte.media/src/main/java/org.monte.media/org/monte/media/riff/RIFFPrimitivesInputStream.java index 88d5caf..a3fdd16 100755 --- a/org.monte.media/src/main/java/org.monte.media/org/monte/media/riff/RIFFPrimitivesInputStream.java +++ b/org.monte.media/src/main/java/org.monte.media/org/monte/media/riff/RIFFPrimitivesInputStream.java @@ -4,6 +4,8 @@ */ package org.monte.media.riff; +import org.monte.media.io.ByteArray; + import java.io.EOFException; import java.io.FilterInputStream; import java.io.IOException; @@ -22,6 +24,7 @@ */ public class RIFFPrimitivesInputStream extends FilterInputStream { private long scan, mark; + private byte byteBuffer[] = new byte[8]; /** * Creates a new instance. @@ -54,15 +57,8 @@ public int readUBYTE() */ public short readWORD() throws IOException { - int b0 = in.read(); - int b1 = in.read(); - - if (b1 == -1) { - throw new EOFException(); - } - scan += 2; - - return (short) (((b0 & 0xff) << 0) | ((b1 & 0xff) << 8)); + readFully(byteBuffer, 0, 2); + return ByteArray.getShortLE(byteBuffer, 0); } /** @@ -80,20 +76,8 @@ public int readUWORD() */ public int readLONG() throws IOException { - int b0 = in.read(); - int b1 = in.read(); - int b2 = in.read(); - int b3 = in.read(); - - if (b3 == -1) { - throw new EOFException(); - } - scan += 4; - - return ((b0 & 0xff) << 0) + - ((b1 & 0xff) << 8) + - ((b2 & 0xff) << 16) + - ((b3 & 0xff) << 24); + readFully(byteBuffer, 0, 4); + return ByteArray.getIntLE(byteBuffer, 0); } /** @@ -110,20 +94,8 @@ public int readLONG() */ public int readFourCC() throws IOException { - int b3 = in.read(); - int b2 = in.read(); - int b1 = in.read(); - int b0 = in.read(); - - if (b0 == -1) { - throw new EOFException(); - } - scan += 4; - - return ((b0 & 0xff) << 0) + - ((b1 & 0xff) << 8) + - ((b2 & 0xff) << 16) + - ((b3 & 0xff) << 24); + readFully(byteBuffer, 0, 4); + return ByteArray.getIntBE(byteBuffer, 0); } /** @@ -140,10 +112,8 @@ public int readFourCC() */ public String readFourCCString() throws IOException { - byte[] buf = new byte[4]; - readFully(buf, 0, 4); - //scan += 4; <- scan is updated by method readFully - return new String(buf, "ASCII"); + readFully(byteBuffer, 0, 4); + return new String(byteBuffer, 0, 4, "ASCII"); } /** diff --git a/pom.xml b/pom.xml index 9b27f9d..59a15f3 100644 --- a/pom.xml +++ b/pom.xml @@ -110,6 +110,11 @@ maven-deploy-plugin 3.1.0 + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + org.apache.maven.plugins maven-javadoc-plugin @@ -258,14 +263,6 @@ - From 909cf2827b42060e6a44ffa8bc574c9225b677ae Mon Sep 17 00:00:00 2001 From: Werner Randelshofer Date: Sun, 5 Mar 2023 11:18:46 +0100 Subject: [PATCH 2/3] Use Arrays.mismatch() in TSCCCodec. --- .../org/monte/demo/aviwriter/Main.java | 1 + .../org/monte/jmftsccdemo/Main.java | 8 ++- .../media/jmf/codec/video/TSCCCodec.java | 3 +- .../av/codec/video/TechSmithCodecCore.java | 72 +++++++------------ 4 files changed, 32 insertions(+), 52 deletions(-) diff --git a/org.monte.demo.aviwriter/src/main/java/org.monte.demo.aviwriter/org/monte/demo/aviwriter/Main.java b/org.monte.demo.aviwriter/src/main/java/org.monte.demo.aviwriter/org/monte/demo/aviwriter/Main.java index 80e8a0c..3da1d6b 100755 --- a/org.monte.demo.aviwriter/src/main/java/org.monte.demo.aviwriter/org/monte/demo/aviwriter/Main.java +++ b/org.monte.demo.aviwriter/src/main/java/org.monte.demo.aviwriter/org/monte/demo/aviwriter/Main.java @@ -60,6 +60,7 @@ public static void main(String[] args) { test(new File("avidemo-raw8.avi"), new Format(EncodingKey, ENCODING_AVI_DIB, DepthKey, 8)); test(new File("avidemo-rle8.avi"), new Format(EncodingKey, ENCODING_AVI_RLE8, DepthKey, 8)); test(new File("avidemo-tscc8.avi"), new Format(EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, DepthKey, 8)); + test(new File("avidemo-tscc16.avi"), new Format(EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, DepthKey, 16)); test(new File("avidemo-raw8gray.avi"), new Format(EncodingKey, ENCODING_AVI_DIB, DepthKey, 8, PixelFormatKey, PixelFormat.GRAY)); test(new File("avidemo-rle8gray.avi"), new Format(EncodingKey, ENCODING_AVI_RLE8, DepthKey, 8, PixelFormatKey, PixelFormat.GRAY)); test(new File("avidemo-tscc8gray.avi"), new Format(EncodingKey, ENCODING_AVI_TECHSMITH_SCREEN_CAPTURE, DepthKey, 8, PixelFormatKey, PixelFormat.GRAY)); diff --git a/org.monte.demo.jmftsccdemo/src/main/java/org.monte.demo.jmftsccdemo/org/monte/jmftsccdemo/Main.java b/org.monte.demo.jmftsccdemo/src/main/java/org.monte.demo.jmftsccdemo/org/monte/jmftsccdemo/Main.java index 94f861d..0f10174 100755 --- a/org.monte.demo.jmftsccdemo/src/main/java/org.monte.demo.jmftsccdemo/org/monte/jmftsccdemo/Main.java +++ b/org.monte.demo.jmftsccdemo/src/main/java/org.monte.demo.jmftsccdemo/org/monte/jmftsccdemo/Main.java @@ -45,7 +45,8 @@ import java.util.Random; /** - * Main. + * Demonstrates how to use the Monte Media {@link TSCCCodec} + * with the Java Media Framework (JMF). * * @author Werner Randelshofer */ @@ -213,7 +214,9 @@ public void run() { private void generateVideos(String path) { try { doGenerateVideo(new File(path, "avidemo-tscc8.avi"), new AviVideoFormat("tscc", null, Format.NOT_SPECIFIED, null, 30f, Format.NOT_SPECIFIED, 8, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, null)); - } catch (IOException | NoProcessorException | NoDataSinkException e) { + //doGenerateVideo(new File(path, "avidemo-tscc16.avi"), new AviVideoFormat("tscc", null, Format.NOT_SPECIFIED, null, 30f, Format.NOT_SPECIFIED, 16, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, null)); + doGenerateVideo(new File(path, "avidemo-tscc24.avi"), new AviVideoFormat("tscc", null, Format.NOT_SPECIFIED, null, 30f, Format.NOT_SPECIFIED, 24, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, null)); + } catch (Throwable e) { e.printStackTrace(); } } @@ -270,6 +273,7 @@ private static void doGenerateVideo(File file, AviVideoFormat format) throws IOE p.close(); sink.close(); } + System.out.println("* Wrote " + file); } private static class ImageStream implements PullBufferStream { diff --git a/org.monte.media.jmf/src/main/java/org.monte.media.jmf/org/monte/media/jmf/codec/video/TSCCCodec.java b/org.monte.media.jmf/src/main/java/org.monte.media.jmf/org/monte/media/jmf/codec/video/TSCCCodec.java index 5867024..502a3ae 100755 --- a/org.monte.media.jmf/src/main/java/org.monte.media.jmf/org/monte/media/jmf/codec/video/TSCCCodec.java +++ b/org.monte.media.jmf/src/main/java/org.monte.media.jmf/org/monte/media/jmf/codec/video/TSCCCodec.java @@ -171,11 +171,10 @@ protected void copyMetaTo(Buffer in, Buffer out) { } protected int encode(Buffer in, Buffer out) { + copyMetaTo(in, out); if (in.isDiscard()) { - out.setDiscard(true); return BUFFER_PROCESSED_OK; } - copyMetaTo(in, out); out.setFormat(outputFormat); SeekableByteArrayOutputStream tmp; diff --git a/org.monte.media/src/main/java/org.monte.media/org/monte/media/av/codec/video/TechSmithCodecCore.java b/org.monte.media/src/main/java/org.monte.media/org/monte/media/av/codec/video/TechSmithCodecCore.java index 2a58636..ddc39cd 100755 --- a/org.monte.media/src/main/java/org.monte.media/org/monte/media/av/codec/video/TechSmithCodecCore.java +++ b/org.monte.media/src/main/java/org.monte.media/org/monte/media/av/codec/video/TechSmithCodecCore.java @@ -12,6 +12,7 @@ import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.Arrays; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; @@ -308,9 +309,8 @@ public boolean decode8(byte[] inDat, int off, int length, byte[] outDat, byte[] } else { // repetition byte v = in.readByte(); - for (int end = xy + opcode; xy < end; xy++) { - outDat[xy] = v; - } + Arrays.fill(outDat, xy, xy + opcode, v); + xy += opcode; } } @@ -400,9 +400,8 @@ public boolean decode8(byte[] inDat, int off, int length, int[] outDat, int[] pr } else { // repetition int v = palette[in.readUnsignedByte()]; - for (int end = xy + opcode; xy < end; xy++) { - outDat[xy] = v; - } + Arrays.fill(outDat, xy, xy + opcode, v); + xy += opcode; } } @@ -467,9 +466,8 @@ public boolean decode24(byte[] inDat, int off, int length, int[] outDat, int[] p } else { // repetition int v = readInt24LE(in); - for (int end = xy + opcode; xy < end; xy++) { - outDat[xy] = v; - } + Arrays.fill(outDat, xy, xy + opcode, v); + xy += opcode; } } @@ -538,9 +536,8 @@ public boolean decode16(byte[] inDat, int off, int length, int[] outDat, int[] p } else { // repetition int v = readRGB555to24(in); - for (int end = xy + opcode; xy < end; xy++) { - outDat[xy] = v; - } + Arrays.fill(outDat, xy, xy + opcode, v); + xy += opcode; } } @@ -577,12 +574,9 @@ public void encodeDelta8(OutputStream out, byte[] data, byte[] prev, int width, int xymax = xy + width; // determine skip count - int skipCount = 0; - for (; xy < xymax; ++xy, ++skipCount) { - if (data[xy] != prev[xy]) { - break; - } - } + int mismatch = Arrays.mismatch(data, xy, xymax, prev, xy, xymax); + int skipCount = mismatch < 0 ? xymax - xy : mismatch; + xy += skipCount; if (skipCount == width) { // => the entire line can be skipped ++verticalOffset; @@ -602,12 +596,8 @@ public void encodeDelta8(OutputStream out, byte[] data, byte[] prev, int width, int repeatCount = 0; for (; xy < xymax; ++xy) { // determine skip count - for (skipCount = 0; xy < xymax; ++xy, ++skipCount) { - if (data[xy] != prev[xy]) { - break; - } - } - xy -= skipCount; + mismatch = Arrays.mismatch(data, xy, xymax, prev, xy, xymax); + skipCount = mismatch < 0 ? xymax - xy : mismatch; // determine repeat count byte v = data[xy]; @@ -1097,12 +1087,9 @@ public void encodeDelta16(OutputStream out, short[] data, short[] prev, int widt int xymax = xy + width; // determine skip count - int skipCount = 0; - for (; xy < xymax; ++xy, ++skipCount) { - if (data[xy] != prev[xy]) { - break; - } - } + int mismatch = Arrays.mismatch(data, xy, xymax, prev, xy, xymax); + int skipCount = mismatch < 0 ? xymax - xy : mismatch; + xy += skipCount; if (skipCount == width) { // => the entire line can be skipped ++verticalOffset; @@ -1122,12 +1109,8 @@ public void encodeDelta16(OutputStream out, short[] data, short[] prev, int widt int repeatCount = 0; for (; xy < xymax; ++xy) { // determine skip count - for (skipCount = 0; xy < xymax; ++xy, ++skipCount) { - if (data[xy] != prev[xy]) { - break; - } - } - xy -= skipCount; + mismatch = Arrays.mismatch(data, xy, xymax, prev, xy, xymax); + skipCount = mismatch < 0 ? xymax - xy : mismatch; // determine repeat count short v = data[xy]; @@ -1317,12 +1300,9 @@ public void encodeDelta24(OutputStream out, int[] data, int[] prev, int width, i int xymax = xy + width; // determine skip count - int skipCount = 0; - for (; xy < xymax; ++xy, ++skipCount) { - if (data[xy] != prev[xy]) { - break; - } - } + int mismatch = Arrays.mismatch(data, xy, xymax, prev, xy, xymax); + int skipCount = mismatch < 0 ? xymax - xy : mismatch; + xy += skipCount; if (skipCount == width) { // => the entire line can be skipped ++verticalOffset; @@ -1342,12 +1322,8 @@ public void encodeDelta24(OutputStream out, int[] data, int[] prev, int width, i int repeatCount = 0; for (; xy < xymax; ++xy) { // determine skip count - for (skipCount = 0; xy < xymax; ++xy, ++skipCount) { - if (data[xy] != prev[xy]) { - break; - } - } - xy -= skipCount; + mismatch = Arrays.mismatch(data, xy, xymax, prev, xy, xymax); + skipCount = mismatch < 0 ? xymax - xy : mismatch; // determine repeat count int v = data[xy]; From e75a4ea698b03a7b9f55e92d1b8bc6b6ad6ac87a Mon Sep 17 00:00:00 2001 From: Werner Randelshofer Date: Sun, 5 Mar 2023 16:02:31 +0100 Subject: [PATCH 3/3] Bump version number to 17.1. --- .idea/misc.xml | 2 +- README.md | 2 +- pom.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.idea/misc.xml b/.idea/misc.xml index fd26528..82dbec8 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index 84098c3..86906b3 100755 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Container formats: | Container | Description | Operations | |-----------|--------------------------------------------|-------------| | RIFF | Microsoft Resource Interchange File Format | Read | -| AVI | Microsoft Audio Video Interchange | Read,Write | +| AVI | Microsoft Audio Video Interchange | Read, Write | | MOV | Apple QuickTime | Write | | JFIF | JPEG File Interchange Format | Read, Write | | MP3 | MP3 Elementary Stream | Read | diff --git a/pom.xml b/pom.xml index 59a15f3..96d219b 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ UTF-8 UTF-8 ${git.commit.time} - 17 + 17.1