Skip to content

Commit d3cc2ee

Browse files
Refactoring 'J2KHeaderParameters' into its own class. (#385)
* Refactoring 'J2KHeaderParameters' into its own class. Added methods to obtain an instance of a 'J2KHeaderParameters' class from a CPL descriptor, or an MXF descriptor. Added testing to ensure that these methods produce equivalent results. * Adding comments back in * Addressing nits: renaming validateHT to validateHTConstraints, and adding one more comment
1 parent e316b9b commit d3cc2ee

8 files changed

+510
-224
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
package com.netflix.imflibrary;
2+
3+
import com.netflix.imflibrary.st0377.header.J2KExtendedCapabilities;
4+
import com.netflix.imflibrary.st0377.header.JPEG2000PictureComponent;
5+
import com.netflix.imflibrary.st0377.header.JPEG2000PictureSubDescriptor;
6+
import com.netflix.imflibrary.utils.DOMNodeObjectModel;
7+
8+
import javax.xml.bind.DatatypeConverter;
9+
import java.util.Arrays;
10+
import java.util.List;
11+
import java.util.Objects;
12+
13+
public class J2KHeaderParameters {
14+
15+
public static class CSiz {
16+
public short ssiz;
17+
public short xrsiz;
18+
public short yrsiz;
19+
20+
@Override
21+
public boolean equals(Object o) {
22+
if (this == o) return true;
23+
if (o == null || getClass() != o.getClass()) return false;
24+
CSiz cSiz = (CSiz) o;
25+
return ssiz == cSiz.ssiz && xrsiz == cSiz.xrsiz && yrsiz == cSiz.yrsiz;
26+
}
27+
28+
@Override
29+
public int hashCode() {
30+
return Objects.hash(ssiz, xrsiz, yrsiz);
31+
}
32+
}
33+
34+
public static class CAP {
35+
public long pcap;
36+
public int[] ccap;
37+
38+
@Override
39+
public boolean equals(Object o) {
40+
if (this == o) return true;
41+
if (o == null || getClass() != o.getClass()) return false;
42+
CAP cap = (CAP) o;
43+
return pcap == cap.pcap && Arrays.equals(ccap, cap.ccap);
44+
}
45+
46+
@Override
47+
public int hashCode() {
48+
int result = Objects.hash(pcap);
49+
result = 31 * result + Arrays.hashCode(ccap);
50+
return result;
51+
}
52+
}
53+
54+
public static class COD {
55+
public short scod;
56+
public short progressionOrder;
57+
public int numLayers;
58+
public short multiComponentTransform;
59+
public short numDecompLevels;
60+
public short xcb;
61+
public short ycb;
62+
public short cbStyle;
63+
public short transformation;
64+
public short precinctSizes[];
65+
66+
public short getScod() { return scod; }
67+
68+
@Override
69+
public boolean equals(Object o) {
70+
if (this == o) return true;
71+
if (o == null || getClass() != o.getClass()) return false;
72+
COD cod = (COD) o;
73+
return scod == cod.scod &&
74+
progressionOrder == cod.progressionOrder &&
75+
numLayers == cod.numLayers &&
76+
multiComponentTransform == cod.multiComponentTransform &&
77+
numDecompLevels == cod.numDecompLevels &&
78+
xcb == cod.xcb &&
79+
ycb == cod.ycb &&
80+
cbStyle == cod.cbStyle &&
81+
transformation == cod.transformation &&
82+
Arrays.equals(precinctSizes, cod.precinctSizes);
83+
}
84+
85+
@Override
86+
public int hashCode() {
87+
int result = Objects.hash(scod, progressionOrder, numLayers, multiComponentTransform, numDecompLevels, xcb, ycb, cbStyle, transformation);
88+
result = 31 * result + Arrays.hashCode(precinctSizes);
89+
return result;
90+
}
91+
}
92+
93+
public static class QCD {
94+
public short sqcd;
95+
public int spqcd[];
96+
97+
@Override
98+
public boolean equals(Object o) {
99+
if (this == o) return true;
100+
if (o == null || getClass() != o.getClass()) return false;
101+
QCD qcd = (QCD) o;
102+
return sqcd == qcd.sqcd && Arrays.equals(spqcd, qcd.spqcd);
103+
}
104+
105+
@Override
106+
public int hashCode() {
107+
int result = Objects.hash(sqcd);
108+
result = 31 * result + Arrays.hashCode(spqcd);
109+
return result;
110+
}
111+
}
112+
113+
public Integer rsiz;
114+
public Long xsiz;
115+
public Long ysiz;
116+
public Long xosiz;
117+
public Long yosiz;
118+
public Long xtsiz;
119+
public Long ytsiz;
120+
public Long xtosiz;
121+
public Long ytosiz;
122+
public CSiz[] csiz;
123+
public COD cod;
124+
public QCD qcd;
125+
public CAP cap;
126+
127+
public J2KHeaderParameters() {}
128+
129+
// From CPL Descriptor to common J2KHeaderParameters
130+
public static J2KHeaderParameters fromDOMNode(DOMNodeObjectModel imageEssencedescriptorDOMNode) {
131+
J2KHeaderParameters p = new J2KHeaderParameters();
132+
133+
DOMNodeObjectModel sdNode = imageEssencedescriptorDOMNode.getDOMNode("SubDescriptors");
134+
if (sdNode == null) {
135+
return null;
136+
}
137+
138+
DOMNodeObjectModel j2kNode = sdNode.getDOMNode("JPEG2000SubDescriptor");
139+
if (j2kNode == null) {
140+
return null;
141+
}
142+
143+
p.rsiz = j2kNode.getFieldAsInteger("Rsiz");
144+
if (p.rsiz == null) return null;
145+
146+
p.xsiz = j2kNode.getFieldAsLong("Xsiz");
147+
if (p.xsiz == null) return null;
148+
149+
p.ysiz = j2kNode.getFieldAsLong("Ysiz");
150+
if (p.ysiz == null) return null;
151+
152+
p.xosiz = j2kNode.getFieldAsLong("XOsiz");
153+
if (p.xosiz == null) return null;
154+
155+
p.yosiz = j2kNode.getFieldAsLong("YOsiz");
156+
if (p.yosiz == null) return null;
157+
158+
p.xtsiz = j2kNode.getFieldAsLong("XTsiz");
159+
if (p.xtsiz == null) return null;
160+
161+
p.ytsiz = j2kNode.getFieldAsLong("YTsiz");
162+
if (p.ytsiz == null) return null;
163+
164+
p.xtosiz = j2kNode.getFieldAsLong("XTOsiz");
165+
if (p.xtosiz == null) return null;
166+
167+
p.ytosiz = j2kNode.getFieldAsLong("YTOsiz");
168+
if (p.ytosiz == null) return null;
169+
170+
// CSiz
171+
DOMNodeObjectModel csiziNode = j2kNode.getDOMNode("PictureComponentSizing");
172+
if (csiziNode == null) return null;
173+
174+
List<DOMNodeObjectModel> csizi = csiziNode.getDOMNodes("J2KComponentSizing");
175+
p.csiz = new CSiz[csizi.size()];
176+
for (int i = 0; i < p.csiz.length; i++) {
177+
p.csiz[i] = new CSiz();
178+
179+
Short ssiz = csizi.get(i).getFieldAsShort("Ssiz");
180+
if (ssiz == null) return null;
181+
p.csiz[i].ssiz = ssiz;
182+
183+
Short xrsiz = csizi.get(i).getFieldAsShort("XRSiz");
184+
if (xrsiz == null) return null;
185+
p.csiz[i].xrsiz = xrsiz;
186+
187+
Short yrsiz = csizi.get(i).getFieldAsShort("YRSiz");
188+
if (yrsiz == null) return null;
189+
p.csiz[i].yrsiz = yrsiz;
190+
}
191+
192+
Integer csiz = j2kNode.getFieldAsInteger("Csiz");
193+
if (csiz != p.csiz.length) return null;
194+
195+
// CAP
196+
DOMNodeObjectModel capNode = j2kNode.getDOMNode("J2KExtendedCapabilities");
197+
if (capNode != null) {
198+
Integer pcap = capNode.getFieldAsInteger("Pcap");
199+
if (pcap != null) {
200+
p.cap = new CAP();
201+
p.cap.pcap = pcap;
202+
203+
DOMNodeObjectModel ccapiNode = capNode.getDOMNode("Ccapi");
204+
if (ccapiNode != null) {
205+
List<Integer> values = ccapiNode.getFieldsAsInteger("UInt16");
206+
207+
p.cap.ccap = new int[values.size()];
208+
for (int i = 0; i < p.cap.ccap.length; i++) {
209+
if (values.get(i) == null) return null;
210+
p.cap.ccap[i] = values.get(i);
211+
}
212+
}
213+
214+
int ccapLength = Long.bitCount(p.cap.pcap);
215+
if (ccapLength > 0 && (p.cap.ccap == null || p.cap.ccap.length != ccapLength))
216+
return null;
217+
if (ccapLength == 0 && (p.cap.ccap != null && p.cap.ccap.length != 0))
218+
return null;
219+
} else {
220+
return null;
221+
}
222+
}
223+
224+
// COD
225+
String codString = j2kNode.getFieldAsString("CodingStyleDefault");
226+
if (codString != null && codString.length() >= 20 && (codString.length() % 2 == 0)) {
227+
p.cod = new COD();
228+
p.cod.scod = (short) Integer.parseInt(codString.substring(0, 2), 16);
229+
p.cod.progressionOrder = (short) Integer.parseInt(codString.substring(2, 4), 16);
230+
p.cod.numLayers = Integer.parseInt(codString.substring(4, 8), 16);
231+
p.cod.multiComponentTransform = (short) Integer.parseInt(codString.substring(8, 10), 16);
232+
p.cod.numDecompLevels = (short) Integer.parseInt(codString.substring(10, 12), 16);
233+
p.cod.xcb = (short) (Integer.parseInt(codString.substring(12, 14), 16) + 2);
234+
p.cod.ycb = (short) (Integer.parseInt(codString.substring(14, 16), 16) + 2);
235+
p.cod.cbStyle = (short) Integer.parseInt(codString.substring(16, 18), 16);
236+
p.cod.transformation = (short) Integer.parseInt(codString.substring(18, 20), 16);
237+
238+
p.cod.precinctSizes = new short[(codString.length() - 20) / 2];
239+
for (int i = 0; i < p.cod.precinctSizes.length; i++) {
240+
p.cod.precinctSizes[i] = (short) Integer.parseInt(codString.substring(20 + 2 * i, 22 + 2 * i), 16);
241+
}
242+
}
243+
244+
// QCD
245+
String qcdString = j2kNode.getFieldAsString("QuantizationDefault");
246+
if (qcdString != null && qcdString.length() >= 2 && (qcdString.length() % 2 == 0)) {
247+
p.qcd = new QCD();
248+
p.qcd.sqcd = (short) Integer.parseInt(qcdString.substring(0, 2), 16);
249+
250+
int spqcdSize = (p.qcd.sqcd & 0b11111) == 0 ? 1 : 2;
251+
p.qcd.spqcd = new int[(qcdString.length() - 2) / (2 * spqcdSize)];
252+
for (int i = 0; i < p.qcd.spqcd.length; i++) {
253+
p.qcd.spqcd[i] = Integer.parseInt(qcdString.substring(2 + 2 * spqcdSize * i, 4 + 2 * spqcdSize * i), 16);
254+
}
255+
}
256+
257+
return p;
258+
}
259+
260+
// From MXF Descriptor to common J2KHeaderParameters
261+
public static J2KHeaderParameters fromJPEG2000PictureSubDescriptorBO(JPEG2000PictureSubDescriptor.JPEG2000PictureSubDescriptorBO jpeg2000PictureSubDescriptorBO) {
262+
J2KHeaderParameters p = new J2KHeaderParameters();
263+
264+
p.rsiz = jpeg2000PictureSubDescriptorBO.getRSiz().intValue();
265+
p.xsiz = jpeg2000PictureSubDescriptorBO.getXSiz().longValue();
266+
p.ysiz = jpeg2000PictureSubDescriptorBO.getYSiz().longValue();
267+
p.xosiz = jpeg2000PictureSubDescriptorBO.getXoSiz().longValue();
268+
p.yosiz = jpeg2000PictureSubDescriptorBO.getYoSiz().longValue();
269+
p.xtsiz = jpeg2000PictureSubDescriptorBO.getXtSiz().longValue();
270+
p.ytsiz = jpeg2000PictureSubDescriptorBO.getYtSiz().longValue();
271+
p.xtosiz = jpeg2000PictureSubDescriptorBO.getXtoSiz().longValue();
272+
p.ytosiz = jpeg2000PictureSubDescriptorBO.getYtoSiz().longValue();
273+
274+
// CSiz
275+
List<JPEG2000PictureComponent.JPEG2000PictureComponentBO> subDescriptorCsizi = jpeg2000PictureSubDescriptorBO.getPictureComponentSizing().getEntries();
276+
p.csiz = new CSiz[subDescriptorCsizi.size()];
277+
for (int i = 0; i < p.csiz.length; i++) {
278+
p.csiz[i] = new CSiz();
279+
p.csiz[i].ssiz = subDescriptorCsizi.get(i).getSSiz();
280+
p.csiz[i].xrsiz = subDescriptorCsizi.get(i).getXrSiz();
281+
p.csiz[i].yrsiz = subDescriptorCsizi.get(i).getYrSiz();
282+
}
283+
284+
if (p.csiz.length != jpeg2000PictureSubDescriptorBO.getCSiz()) {
285+
return null;
286+
}
287+
288+
// CAP
289+
J2KExtendedCapabilities j2KExtendedCapabilities = jpeg2000PictureSubDescriptorBO.getJ2kExtendedCapabilities();
290+
List<Short> subDescriptorcCap = j2KExtendedCapabilities.getcCap().getEntries();
291+
p.cap = new CAP();
292+
p.cap.pcap = j2KExtendedCapabilities.getpCap();
293+
p.cap.ccap = new int[subDescriptorcCap.size()];
294+
for (int i = 0; i < p.cap.ccap.length; i++) {
295+
p.cap.ccap[i] = subDescriptorcCap.get(i);
296+
}
297+
298+
int cCapLength = Long.bitCount(p.cap.pcap);
299+
if (cCapLength > 0 && p.cap.ccap.length != cCapLength) {
300+
return null;
301+
}
302+
303+
// COD
304+
String codString = jpeg2000PictureSubDescriptorBO.getCodingStyleDefaultString();
305+
if (codString != null && codString.length() >= 20 && (codString.length() % 2 == 0)) {
306+
p.cod = new COD();
307+
p.cod.scod = (short) Integer.parseInt(codString.substring(0, 2), 16);
308+
p.cod.progressionOrder = (short) Integer.parseInt(codString.substring(2, 4), 16);
309+
p.cod.numLayers = Integer.parseInt(codString.substring(4, 8), 16);
310+
p.cod.multiComponentTransform = (short) Integer.parseInt(codString.substring(8, 10), 16);
311+
p.cod.numDecompLevels = (short) Integer.parseInt(codString.substring(10, 12), 16);
312+
p.cod.xcb = (short) (Integer.parseInt(codString.substring(12, 14), 16) + 2);
313+
p.cod.ycb = (short) (Integer.parseInt(codString.substring(14, 16), 16) + 2);
314+
p.cod.cbStyle = (short) Integer.parseInt(codString.substring(16, 18), 16);
315+
p.cod.transformation = (short) Integer.parseInt(codString.substring(18, 20), 16);
316+
317+
p.cod.precinctSizes = new short[(codString.length() - 20) / 2];
318+
for (int i = 0; i < p.cod.precinctSizes.length; i++) {
319+
p.cod.precinctSizes[i] = (short) Integer.parseInt(codString.substring(20 + 2 * i, 22 + 2 * i), 16);
320+
}
321+
} else {
322+
return null;
323+
}
324+
325+
// QCD
326+
String qcdString = jpeg2000PictureSubDescriptorBO.getQuantisationDefaultString();
327+
if (qcdString != null && qcdString.length() >= 2 && (qcdString.length() % 2 == 0)) {
328+
p.qcd = new QCD();
329+
p.qcd.sqcd = (short) Integer.parseInt(qcdString.substring(0, 2), 16);
330+
331+
int spqcdSize = (p.qcd.sqcd & 0b11111) == 0 ? 1 : 2;
332+
p.qcd.spqcd = new int[(qcdString.length() - 2) / (2 * spqcdSize)];
333+
for (int i = 0; i < p.qcd.spqcd.length; i++) {
334+
p.qcd.spqcd[i] = Integer.parseInt(qcdString.substring(2 + 2 * spqcdSize * i, 4 + 2 * spqcdSize * i), 16);
335+
}
336+
}
337+
338+
return p;
339+
}
340+
341+
@Override
342+
public boolean equals(Object o) {
343+
if (this == o) return true;
344+
if (o == null || getClass() != o.getClass()) return false;
345+
J2KHeaderParameters that = (J2KHeaderParameters) o;
346+
return Objects.equals(rsiz, that.rsiz) &&
347+
Objects.equals(xsiz, that.xsiz) &&
348+
Objects.equals(ysiz, that.ysiz) &&
349+
Objects.equals(xosiz, that.xosiz) &&
350+
Objects.equals(yosiz, that.yosiz) &&
351+
Objects.equals(xtsiz, that.xtsiz) &&
352+
Objects.equals(ytsiz, that.ytsiz) &&
353+
Objects.equals(xtosiz, that.xtosiz) &&
354+
Objects.equals(ytosiz, that.ytosiz) &&
355+
Arrays.equals(csiz, that.csiz) &&
356+
cod.equals(that.cod) &&
357+
qcd.equals(that.qcd) &&
358+
cap.equals(that.cap);
359+
}
360+
361+
@Override
362+
public int hashCode() {
363+
int result = Objects.hash(rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, xtosiz, ytosiz, cod, qcd, cap);
364+
result = 31 * result + Arrays.hashCode(csiz);
365+
return result;
366+
}
367+
}

src/main/java/com/netflix/imflibrary/st0377/header/J2KExtendedCapabilities.java

+8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ public final class J2KExtendedCapabilities {
2020
@MXFProperty(size=4) protected final Integer pCap = null;
2121
@MXFProperty(size=0, depends=true) protected final CompoundDataTypes.MXFCollections.MXFCollection<Short> cCap = null;
2222

23+
public Integer getpCap() {
24+
return pCap;
25+
}
26+
27+
public CompoundDataTypes.MXFCollections.MXFCollection<Short> getcCap() {
28+
return cCap;
29+
}
30+
2331
/**
2432
* Instantiates a new parsed J2KExtendedCapabilities object
2533
*

0 commit comments

Comments
 (0)