Skip to content

Commit 098eebf

Browse files
committed
Improve writer
1 parent 8399097 commit 098eebf

File tree

2 files changed

+61
-38
lines changed

2 files changed

+61
-38
lines changed

library/src/main/java/com/sandflow/smpte/mxf/StreamingWriter.java

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,26 @@ public class StreamingWriter {
8181

8282
private abstract class ContainerWriter extends OutputStream {
8383

84+
enum State {
85+
READY,
86+
WRITING
87+
}
88+
8489
private final long bodySID;
8590
private final long indexSID;
8691
private long bytesToWrite;
8792
private long ecOffset = 0;
93+
private State state = State.READY;
8894

8995
ContainerWriter(long bodySID, long indexSID) {
9096
this.bodySID = bodySID;
9197
this.indexSID = indexSID;
9298
}
9399

100+
State getState() {
101+
return this.state;
102+
}
103+
94104
long getIndexSID() {
95105
return this.indexSID;
96106
}
@@ -103,16 +113,16 @@ boolean isActive() {
103113
return StreamingWriter.this.currentContainer == this;
104114
}
105115

106-
long getBytesToWrite() {
107-
return bytesToWrite;
116+
boolean isWriting() {
117+
return this.state.equals(State.WRITING);
108118
}
109119

110-
/*
111-
* TODO: this is super ugly as it interferes with the ability of the GC to write
112-
* stuff to itself
113-
*/
114-
void setBytesToWrite(long bytesToWrite) {
120+
void startWriting(long bytesToWrite) {
121+
if (this.state != State.READY) {
122+
throw new IllegalStateException("ContainerWriter is not in READY state");
123+
}
115124
this.bytesToWrite = bytesToWrite;
125+
this.state = State.WRITING;
116126
}
117127

118128
abstract byte[] drainIndexSegments() throws IOException, MXFException;
@@ -136,25 +146,39 @@ public void write(int b) throws IOException {
136146
if (!this.isActive()) {
137147
throw new IllegalStateException("ContainerWriter is not active");
138148
}
149+
if (this.state != State.WRITING) {
150+
throw new IllegalStateException("ContainerWriter is not in WRITING state");
151+
}
139152
if (this.bytesToWrite - 1 < 0)
140153
throw new EOFException("Attempting to write more bytes than allocated to the container");
141154
StreamingWriter.this.fos.write(b);
142155
this.bytesToWrite--;
143156
this.ecOffset++;
157+
158+
if (this.bytesToWrite == 0) {
159+
this.state = State.READY;
160+
}
144161
}
145162

146163
@Override
147164
public void write(byte[] b, int off, int len) throws IOException {
148165
if (!this.isActive()) {
149166
throw new IllegalStateException("ContainerWriter is not active");
150167
}
168+
if (this.state != State.WRITING) {
169+
throw new IllegalStateException("ContainerWriter is not in WRITING state");
170+
}
151171
if (this.bytesToWrite - len < 0) {
152172
throw new EOFException("Attempting to write more bytes than allocated to the container");
153173
}
154174

155175
StreamingWriter.this.fos.write(b, off, len);
156176
this.bytesToWrite -= len;
157177
this.ecOffset += len;
178+
179+
if (this.bytesToWrite == 0) {
180+
this.state = State.READY;
181+
}
158182
}
159183

160184
@Override
@@ -165,13 +189,14 @@ public void close() throws IOException {
165189
*/
166190
}
167191

168-
static byte[] serializeIndexTableSegment(IndexTableSegment its, EventHandler evthandler) throws IOException, MXFException {
192+
static byte[] serializeIndexTableSegment(IndexTableSegment its, EventHandler evthandler)
193+
throws IOException, MXFException {
169194
/* serialize the index table segment */
170195

171196
/*
172-
* The AtomicReference is necessary since the variable is initialized in the
173-
* inline MXFOutputContext
174-
*/
197+
* The AtomicReference is necessary since the variable is initialized in the
198+
* inline MXFOutputContext
199+
*/
175200
AtomicReference<Set> ars = new AtomicReference<>();
176201
MXFOutputContext ctx = new MXFOutputContext() {
177202

@@ -234,15 +259,9 @@ public AUID getAUID(long localtag) {
234259
*/
235260
class GCClipCBEWriter extends ContainerWriter {
236261

237-
enum State {
238-
READY,
239-
WRITTEN,
240-
DRAINED
241-
}
242-
243262
private long accessUnitSize;
244263
private long accessUnitCount;
245-
private State state = State.READY;
264+
private boolean indexTableFilled = false;
246265

247266
public GCClipCBEWriter(long bodySID, long indexSID) {
248267
super(bodySID, indexSID);
@@ -257,7 +276,9 @@ public GCClipCBEWriter(long bodySID, long indexSID) {
257276
* @throws IOException If an I/O error occurs.
258277
*/
259278
public void nextClip(UL elementKey, long accessUnitSize, long accessUnitCount) throws IOException {
260-
Objects.requireNonNull(elementKey, "Element Key cannot be null");
279+
if (elementKey == null) {
280+
throw new IllegalArgumentException("Element Key cannot be null");
281+
}
261282

262283
if (accessUnitCount < 0) {
263284
throw new IllegalArgumentException("Access Unit Count cannot be negative");
@@ -271,20 +292,19 @@ public void nextClip(UL elementKey, long accessUnitSize, long accessUnitCount) t
271292
throw new IllegalStateException("ContainerWriter is not active");
272293
}
273294

274-
if (this.state != State.READY) {
295+
if (this.getState() != State.READY) {
275296
throw new IllegalStateException("ContainerWriter is not ready for the next clip");
276297
}
277298

278299
long clipSize = accessUnitCount * accessUnitSize;
279300

280301
StreamingWriter.this.fos.writeUL(elementKey);
281302
StreamingWriter.this.fos.writeBERLength(clipSize);
282-
this.setBytesToWrite(clipSize);
303+
this.startWriting(clipSize);
283304

284305
this.accessUnitCount = accessUnitCount;
285306
this.accessUnitSize = accessUnitSize;
286-
287-
this.state = State.WRITTEN;
307+
this.indexTableFilled = true;
288308
}
289309

290310
@Override
@@ -294,10 +314,10 @@ long getDuration() {
294314

295315
@Override
296316
byte[] drainIndexSegments() throws IOException, MXFException {
297-
if (this.state != State.WRITTEN) {
317+
if (!this.indexTableFilled) {
298318
return null;
299319
}
300-
this.state = State.DRAINED;
320+
this.indexTableFilled = false;
301321

302322
var its = new IndexTableSegment();
303323
its.InstanceID = StreamingWriter.this.uidGenerator.generate(this);
@@ -345,7 +365,7 @@ public void nextElement(UL elementKey, long elementLength) throws IOException {
345365
}
346366
StreamingWriter.this.fos.writeUL(elementKey);
347367
StreamingWriter.this.fos.writeBERLength(elementLength);
348-
this.setBytesToWrite(elementLength);
368+
this.startWriting(elementLength);
349369
}
350370

351371
@Override
@@ -420,17 +440,16 @@ public void nextClip(UL elementKey, long clipSize) throws IOException {
420440
* EXCEPTION: ASDCPLib incorrectly includes the Clip KL in the essence container
421441
* offset for IAB files
422442
*/
423-
this.setBytesToWrite(50);
424-
MXFDataOutput mos = new MXFDataOutput(this);
425-
mos.writeUL(elementKey);
426-
mos.writeBERLength(clipSize);
427-
mos.flush();
443+
long curPos = StreamingWriter.this.fos.getWrittenCount();
444+
StreamingWriter.this.fos.writeUL(elementKey);
445+
StreamingWriter.this.fos.writeBERLength(clipSize);
446+
this.setPosition(this.getPosition() + StreamingWriter.this.fos.getWrittenCount() - curPos);
428447
} else {
429448
StreamingWriter.this.fos.writeUL(elementKey);
430449
StreamingWriter.this.fos.writeBERLength(clipSize);
431450
}
432451

433-
this.setBytesToWrite(clipSize);
452+
this.startWriting(clipSize);
434453

435454
this.state = State.WRITTEN;
436455
}
@@ -567,7 +586,7 @@ public void nextElement(UL elementKey, long elementSize) throws IOException {
567586
StreamingWriter.this.fos.writeBERLength(elementSize);
568587
this.setPosition(this.getPosition() + StreamingWriter.this.fos.getWrittenCount() - curPos);
569588

570-
this.setBytesToWrite(elementSize);
589+
this.startWriting(elementSize);
571590
}
572591

573592
@Override
@@ -667,7 +686,8 @@ private enum State {
667686
private final UIDGenerator uidGenerator;
668687

669688
/**
670-
* Instantiates a StreamingWriter. The StreamingWriter makes a copy of the provided preface.
689+
* Instantiates a StreamingWriter. The StreamingWriter makes a copy of the
690+
* provided preface.
671691
*
672692
* @param os Output stream to write the MXF file to.
673693
* @param preface Preface set of the MXF file.
@@ -682,7 +702,8 @@ public StreamingWriter(OutputStream os, Preface preface, EventHandler evthandler
682702
}
683703

684704
/**
685-
* Instantiates a StreamingWriter with a custom UID generator. The StreamingWriter makes a copy of the provided preface.
705+
* Instantiates a StreamingWriter with a custom UID generator. The
706+
* StreamingWriter makes a copy of the provided preface.
686707
*
687708
* @param os Output stream to write the MXF file to.
688709
* @param preface Preface set of the MXF file.
@@ -861,9 +882,8 @@ private void closeCurrentPartition() throws IOException, KLVException, MXFExcept
861882
}
862883

863884
/* are we done with the current partition? */
864-
/* TODO: replace with state check */
865-
if (this.currentContainer.getBytesToWrite() != 0) {
866-
throw new RuntimeException();
885+
if (this.currentContainer.isWriting()) {
886+
throw new IllegalStateException("The current partition cannot be closed because it is still writing");
867887
}
868888
/* do we need to create an index partition for the current essence container */
869889
if (this.currentContainer.getIndexSID() != 0) {

library/src/test/java/com/sandflow/smpte/mxf/ReadWriteTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,14 @@ void testPHDR(TestInfo testInfo) throws Exception {
171171
gc.nextElement(phdrImageElementKey, inReader.getElementLength());
172172
} else if (phdrGSElementKey.equalsIgnoreVersion(elementKey)) {
173173
phdrMetadataPayload = inReader.readNBytes((int) inReader.getElementLength());
174+
assertNotEquals(0, phdrMetadataPayload.length);
175+
continue;
174176
} else {
175177
fail();
176178
}
177179

178180
byte[] buffer = inReader.readNBytes((int) inReader.getElementLength());
181+
assertNotEquals(0, buffer.length);
179182
gc.write(buffer);
180183
}
181184

0 commit comments

Comments
 (0)