diff --git a/java-library/src/main/java/com/sandflow/smpte/mxf/ClipReader.java b/java-library/src/main/java/com/sandflow/smpte/mxf/ClipReader.java index 278ef73..60a88a3 100644 --- a/java-library/src/main/java/com/sandflow/smpte/mxf/ClipReader.java +++ b/java-library/src/main/java/com/sandflow/smpte/mxf/ClipReader.java @@ -45,6 +45,8 @@ public class ClipReader extends InputStream { final AUID elementKey; final long elementLength; final long essenceOffset; + /* size of the key and length of the clip */ + final long klLength; final RandomAccessFileInfo info; final RandomAccessInputSource source; @@ -67,6 +69,7 @@ public ClipReader(RandomAccessFileInfo info, RandomAccessInputSource source) thr MXFDataInput mis = new MXFDataInput(this.source); this.elementKey = mis.readAUID(); this.elementLength = mis.readBERLength(); + this.klLength = this.source.position() - clipStartPosition; /* * ECXEPTION: Some versions of ASDCPLib index from the start of the K of the @@ -78,7 +81,7 @@ public ClipReader(RandomAccessFileInfo info, RandomAccessInputSource source) thr && this.info.euToECPosition(0) != 0) { this.essenceOffset = 0; } else { - this.essenceOffset = this.source.position() - clipStartPosition; + this.essenceOffset = this.klLength; } this.seek(0); @@ -131,7 +134,8 @@ public void seek(long euPosition) throws IOException { long ecPosition = this.info.euToECPosition(euPosition); long filePosition = this.info.ecToFilePositions(ecPosition) + this.essenceOffset; this.source.position(filePosition); - this.remainingElementBytes = this.elementLength - ecPosition; + /* ecPosition + essenceOffset is relative to the start of the K of the clip */ + this.remainingElementBytes = this.elementLength - (ecPosition + this.essenceOffset - this.klLength); } /** @@ -163,9 +167,12 @@ public int read() throws IOException { */ @Override public int read(byte[] b, int off, int len) throws IOException { - if (this.remainingElementBytes == 0) + if (len == 0) + return 0; + if (this.remainingElementBytes <= 0) return -1; - int r = source.read(b, off, len); + /* do not read past the end of the element */ + int r = source.read(b, off, (int) Math.min(len, this.remainingElementBytes)); this.remainingElementBytes = r == -1 ? 0 : this.remainingElementBytes - r; return r; } @@ -179,9 +186,10 @@ public int read(byte[] b, int off, int len) throws IOException { */ @Override public long skip(long n) throws IOException { - if (this.remainingElementBytes == 0) - return -1; - long s = this.source.skip(n); + if (n <= 0 || this.remainingElementBytes <= 0) + return 0; + /* do not skip past the end of the element */ + long s = Math.max(0, this.source.skip(Math.min(n, this.remainingElementBytes))); this.remainingElementBytes = this.remainingElementBytes - s; return s; } diff --git a/java-library/src/main/java/com/sandflow/smpte/mxf/GenericStreamReader.java b/java-library/src/main/java/com/sandflow/smpte/mxf/GenericStreamReader.java index 65e5b8f..ec4b7f1 100644 --- a/java-library/src/main/java/com/sandflow/smpte/mxf/GenericStreamReader.java +++ b/java-library/src/main/java/com/sandflow/smpte/mxf/GenericStreamReader.java @@ -143,9 +143,12 @@ public int read() throws IOException { */ @Override public int read(byte[] b, int off, int len) throws IOException { - if (this.remainingElementBytes == 0) + if (len == 0) + return 0; + if (this.remainingElementBytes <= 0) return -1; - int r = source.read(b, off, len); + /* do not read past the end of the element */ + int r = source.read(b, off, (int) Math.min(len, this.remainingElementBytes)); this.remainingElementBytes = r == -1 ? 0 : this.remainingElementBytes - r; return r; } @@ -159,9 +162,10 @@ public int read(byte[] b, int off, int len) throws IOException { */ @Override public long skip(long n) throws IOException { - if (this.remainingElementBytes == 0) - return -1; - long s = this.source.skip(n); + if (n <= 0 || this.remainingElementBytes <= 0) + return 0; + /* do not skip past the end of the element */ + long s = Math.max(0, this.source.skip(Math.min(n, this.remainingElementBytes))); this.remainingElementBytes = this.remainingElementBytes - s; return s; } diff --git a/java-library/src/main/java/com/sandflow/smpte/mxf/StreamingReader.java b/java-library/src/main/java/com/sandflow/smpte/mxf/StreamingReader.java index 5ec2729..db95e6d 100644 --- a/java-library/src/main/java/com/sandflow/smpte/mxf/StreamingReader.java +++ b/java-library/src/main/java/com/sandflow/smpte/mxf/StreamingReader.java @@ -158,9 +158,12 @@ public int read(byte[] b, int off, int len) throws IOException { if (this.state != State.IN_PAYLOAD) { throw new RuntimeException(); } - if (this.remainingElementBytes == 0) + if (len == 0) + return 0; + if (this.remainingElementBytes <= 0) return -1; - int r = this.mis.read(b, off, len); + /* do not read past the end of the element */ + int r = this.mis.read(b, off, (int) Math.min(len, this.remainingElementBytes)); this.remainingElementBytes = r == -1 ? 0 : this.remainingElementBytes - r; return r; } @@ -170,9 +173,10 @@ public long skip(long n) throws IOException { if (this.state != State.IN_PAYLOAD) { throw new RuntimeException(); } - if (this.remainingElementBytes == 0) - return -1; - long s = this.mis.skip(n); + if (n <= 0 || this.remainingElementBytes <= 0) + return 0; + /* do not skip past the end of the element */ + long s = Math.max(0, this.mis.skip(Math.min(n, this.remainingElementBytes))); this.remainingElementBytes = this.remainingElementBytes - s; return s; } diff --git a/java-library/src/test/java/com/sandflow/smpte/mxf/RandomAccessReaderTest.java b/java-library/src/test/java/com/sandflow/smpte/mxf/RandomAccessReaderTest.java index 04091a2..e570a1d 100644 --- a/java-library/src/test/java/com/sandflow/smpte/mxf/RandomAccessReaderTest.java +++ b/java-library/src/test/java/com/sandflow/smpte/mxf/RandomAccessReaderTest.java @@ -210,4 +210,38 @@ void testTimedEvents() throws Exception { gsr.close(); } + /* + * Bulk reads and skips must not go past the end of the element + */ + @Test + void testReadPastElementEnd() throws Exception { + File f = new File(ClassLoader.getSystemResource("mxf-files/timed_events.mxf").toURI()); + FileRandomAccessInputSource rais = new FileRandomAccessInputSource(new RandomAccessFile(f, "r")); + + RandomAccessFileInfo fi = new RandomAccessFileInfo(rais, null); + + ClipReader cr = new ClipReader(fi, rais); + assertEquals(840, cr.readAllBytes().length); + assertEquals(0, cr.getRemainingElementBytes()); + assertEquals(-1, cr.read()); + + cr.seek(0); + assertEquals(840, cr.skip(Long.MAX_VALUE)); + assertEquals(0, cr.skip(1)); + assertEquals(-1, cr.read(new byte[16])); + cr.close(); + + GenericStreamReader gsr = new GenericStreamReader(fi, rais); + gsr.seek(12); + assertEquals(81, gsr.readAllBytes().length); + assertEquals(0, gsr.getRemainingElementBytes()); + assertEquals(-1, gsr.read()); + + gsr.seek(12); + assertEquals(81, gsr.skip(Long.MAX_VALUE)); + assertEquals(0, gsr.skip(1)); + assertEquals(-1, gsr.read(new byte[16])); + gsr.close(); + } + } diff --git a/java-library/src/test/java/com/sandflow/smpte/mxf/StreamingReaderTest.java b/java-library/src/test/java/com/sandflow/smpte/mxf/StreamingReaderTest.java index 0fef827..83ae546 100644 --- a/java-library/src/test/java/com/sandflow/smpte/mxf/StreamingReaderTest.java +++ b/java-library/src/test/java/com/sandflow/smpte/mxf/StreamingReaderTest.java @@ -171,6 +171,37 @@ void testTimedEvents() throws Exception { sr.close(); } + /* + * Bulk reads and skips must not go past the end of the element + */ + @Test + void testReadPastElementEnd() throws Exception { + InputStream is = ClassLoader.getSystemResourceAsStream("mxf-files/timed_events.mxf"); + assertNotNull(is); + + new StreamingFileInfo(is, null); + StreamingReader sr = new StreamingReader(is, null); + + /* Timed Events Resource */ + + assertTrue(sr.nextElement()); + assertEquals(840, sr.readAllBytes().length); + assertEquals(-1, sr.read()); + assertEquals(0, sr.skip(1)); + + /* Timed Events Ancillary Resource, in a generic stream */ + + assertTrue(sr.nextElement()); + assertEquals(12L, sr.getSID()); + assertEquals(81, sr.readAllBytes().length); + assertEquals(-1, sr.read(new byte[16])); + assertEquals(0, sr.skip(Long.MAX_VALUE)); + + assertFalse(sr.nextElement()); + + sr.close(); + } + /* * SMPTE ST 2067-204 ADM audio track file, from the Photon test suite * (https://github.com/wruppelx/photon)