/** * MXFTechMDExtractor.java * Authors: Francesco Gallo (gallo@eurix.it), Laurent Boch (l.boch@rai.it), Roberto Borgotallo (r.borgotallo@rai.it) * * This file is part of PrestoPRIME Preservation Platform (P4). * * Copyright (C) 2009-2012 EURIX Srl, Torino, Italy * Copyright (C) 2009-2012 RAI CRIT, Torino, Italy * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.prestoprime.plugin.p4.tools; import it.eurix.archtools.tool.AbstractTool; import it.eurix.archtools.tool.ToolException; import java.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; public class MXFTechMDExtractor extends AbstractTool { private Map<String, String> attributeMap; private Map<String, String> propertyMap; private List<String> attributeNames; private static final short SMPTEKEYSIZE = 16; public MXFTechMDExtractor() { super("MXFTechMDExtractor"); init(); } public String getAttributeByName(String name) { return attributeMap.get(name); } public List<String> getSupportedAttributeNames() { return attributeNames; } public void extract(String mxfFile) throws ToolException { try { RandomAccessFile mxfin = new RandomAccessFile(mxfFile, "r"); MXFBuffer mxfbuftmp = new MXFBuffer(2048); mxfbuftmp.loadBuffer(mxfin); // loaded buffer long endofheaderpos = getAllFromPartitionPack(mxfbuftmp); // OperationPattern and EssenceContain into the Map mxfin.seek(0); MXFBuffer mxfbuf = new MXFBuffer((int) endofheaderpos); mxfbuf.loadBuffer(mxfin); getMaterialPackageId(mxfbuf); getAllFromPictureEssenceDescriptor(mxfbuf); getAllFromSoundEssenceDescriptor(mxfbuf); getEditRate(mxfbuf); getDuration(mxfbuf); setPictureDefaultValues(); setVideoActiveLinesPerFrame(); setFrameLayoutName(); } catch (Exception e) { throw new ToolException("Error extracting MD from MXF file: " + mxfFile); } } private void init() { // Load list of MXF properties initPropertyMap(); // Populate attribute map with empty values initAttributeMap(); } private void initPropertyMap() { propertyMap = new LinkedHashMap<String, String>(); // Material Package Identifier (Type UMID) propertyMap.put("materialpackage.id.name", "MaterialPackageID"); propertyMap.put("materialpackage.id.smptekey", "06 0e 2b 34 02 53 01 01 0d 01 01 01 01 01 36 00"); propertyMap.put("umid.localtag", "44 01 00 20"); // Partition Pack // antepenult byte: 02 Header, 03 Body, 04 Footer // penultimate byte: 01 Open and Incomplete, 02 Closed and incomplete, // 03 Open and Complete, 04 Closed and Complete propertyMap.put("partition.pack.header.name", "PartitionPack"); propertyMap.put("partition.pack.key", "06 0e 2b 34 02 05 01 01 0d 01 02 01 01 xx xx 00"); propertyMap.put("partition.pack.smptekey", "06 0e 2b 34 02 05 01 01 0d 01 02 01 01 ff ff 00"); // OperationalPattern // fourth- byte Item Complexity // example for op1A 06 0e 2b 34 04 01 01 01 0d 01 02 01 01 01 09 00 propertyMap.put("operational.pattern.name", "OperationalPattern"); propertyMap.put("operational.pattern.smptekey", "06 0e 2b 34 04 01 01 01 0d 01 02 01 ff ff ff ff"); // EssenceContainers /* * D10 see Mapping S386 * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 01 01 01" D10 50Mbit 625 * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 01 02 01" D10 50Mbit 525 * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 01 03 01" D10 40Mbit 625 * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 01 04 01" D10 40Mbit 525 * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 01 05 01" D10 30Mbit 625 * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 01 06 01" D10 30Mbit 525 * * MPEG see Mapping S381 * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 04 xx xx" MPEG ES * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 07 xx xx" MPEG PES * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 08 xx xx" MPEG PS * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 09 xx xx" MPEG TS * * DV see Mapping S381 --> * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 02 ff ff" * * Uncompressed see Mapping S384 --> * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 05 ff ff" * * D11 see Mapping S387 --> * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 03 ff ff" * * AES see Mapping S382 --> * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 06 01 00" AES-BWF Wave * Framewrapped "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 06 02 00" * AES-BWF Wave Clip Wrapped * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 06 03 00" AES-BWF AES Frame * Wrapped "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 06 04 00" AES-BWF AES * Clip Wrapped "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 06 08 00" * AES-BWF Wave Custom Wrapped * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 06 09 00" AES-BWF AES Custom * Wrapped * * JPEG2000 see Mapping S422 * "06 0e 2b 34 04 01 01 01 0d 01 03 01 02 0c ff ff" */ propertyMap.put("essence.container.name", "EssenceContainers"); propertyMap.put("essence.container.smptekey", "06 0e 2b 34 04 01 01 1ff 0d 01 03 01 02 1ff 1ff 1ff"); // Timeline Track propertyMap.put("timeline.track.smptekey", "06 0e 2b 34 02 53 01 101 0d 01 01 01 01 01 3b 00"); propertyMap.put("editrate.smptekey", "06 0e 2b 34 01 01 01 02 05 30 04 05 00 00 00 00"); propertyMap.put("editrate.localtag", "4b 01 00 08"); propertyMap.put("editrate.name", "EditRate"); // Durations propertyMap.put("structural.component.smptekey", "06 0e 2b 34 02 53 01 101 0d 01 01 01 01 01 02 00"); propertyMap.put("essence.track.smptekey", "06 0e 2b 34 04 01 01 01 01 03 02 02 101 00 00 00"); propertyMap.put("duration.smptekey", "06 0e 2b 34 01 01 01 02 07 02 02 01 01 03 00 00"); propertyMap.put("duration.localtag", "02 02 00 08"); propertyMap.put("duration.name", "Duration"); // Picture EssenceDescriptor propertyMap.put("generic.picture.essence.descriptor.smptekey", "06 0e 2b 34 02 53 01 101 0d 01 01 01 01 01 27 00"); propertyMap.put("cdci.essence.descriptor.smptekey", "06 0e 2b 34 02 53 01 101 0d 01 01 01 01 01 28 00"); propertyMap.put("rgba.essence.descriptor.smptekey", "06 0e 2b 34 02 53 01 101 0d 01 01 01 01 01 29 00"); propertyMap.put("mpeg.essence.descriptor.smptekey", "06 0e 2b 34 02 53 01 101 0d 01 01 01 01 01 51 00"); // Generic Sound EssenceDescriptor propertyMap.put("generic.sound.essence.descriptor.smptekey", "06 0e 2b 34 02 53 01 101 0d 01 01 01 01 01 42 00"); propertyMap.put("wave.audio.essence.descriptor.smptekey", "06 0e 2b 34 02 53 01 101 0d 01 01 01 01 01 48 00"); propertyMap.put("aes3.audio.essence.descriptor.smptekey", "06 0e 2b 34 02 53 01 101 0d 01 01 01 01 01 47 00"); propertyMap.put("wave.audio.physical.descriptor.smptekey", "06 0e 2b 34 02 53 01 101 0d 01 01 01 01 01 50 00"); // Other EssenceDescriptors to be verified propertyMap.put("jpeg200.essence.descriptor.smptekey", "06 0e 2b 34 02 05 01 101 0d 01 02 01 01 01 5a 00"); // ComponentDepth propertyMap.put("componentdepth.name", "ComponentDepth"); propertyMap.put("componentdepth.smptekey", "06 0e 2b 34 01 01 01 02 04 01 05 03 0a 00 00 00"); propertyMap.put("componentdepth.localtag", "33 01 00 04"); // HorizontalSubSampling propertyMap.put("horizontalsubsampling.name", "HorizontalSubSampling"); propertyMap.put("horizontalsubsampling.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 01 05 00 00 00"); propertyMap.put("horizontalsubsampling.localtag", "33 02 00 04"); // VerticalSubSampling propertyMap.put("verticalsubsampling.name", "VerticalSubSampling"); propertyMap.put("verticalsubsampling.smptekey", "06 0e 2b 34 01 01 01 02 04 01 05 01 10 00 00 00"); propertyMap.put("verticalsubsampling.localtag", "33 08 00 04"); // PictureEssenceCoding propertyMap.put("picture.essencecoding.name", "PictureEssenceCoding"); propertyMap.put("picture.essencecoding.smptekey", "06 0e 2b 34 01 01 01 02 04 01 03 01 06 00 00 00"); propertyMap.put("picture.essencecoding.localtag", "32 01 00 10"); // ActiveFormatDescriptor // SMPTE 2016-1 (4 bit encoding) propertyMap.put("activeformatdescriptor.name", "ActiveFormatDescriptor"); propertyMap.put("activeformatdescriptor.smptekey", "06 0e 2b 34 01 01 01 05 04 01 03 02 09 00 00 00"); propertyMap.put("activeformatdescriptor.localtag", "32 18 00 01"); // Generic Picture Essence Descriptors // FrameLayout propertyMap.put("framelayout.name", "FrameLayout"); propertyMap.put("framelayout.smptekey", "06 0e 2b 34 01 01 01 01 04 01 03 01 04 00 00 00"); propertyMap.put("framelayout.localtag", "32 0c 00 01"); propertyMap.put("framelayoutname.name", "FrameLayoutName"); // StoredWidth propertyMap.put("stored.width.name", "StoredWidth"); propertyMap.put("stored.width.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 02 02 00 00 00"); propertyMap.put("stored.width.localtag", "32 03 00 04"); // StoredHeight propertyMap.put("stored.height.name", "StoredHeight"); propertyMap.put("stored.height.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 02 01 00 00 00"); propertyMap.put("stored.height.localtag", "32 02 00 04"); // SampleWidth propertyMap.put("sampled.width.name", "SampledWidth"); propertyMap.put("sampled.width.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 01 08 00 00 00"); propertyMap.put("sampled.width.localtag", "32 05 00 04"); // SampleHeight propertyMap.put("sampled.height.name", "SampledHeight"); propertyMap.put("sampled.height.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 01 07 00 00 00"); propertyMap.put("sampled.height.localtag", "32 04 00 04"); // SampleXOffset propertyMap.put("sampled.xoffset.name", "SampledXOffset"); propertyMap.put("sampled.xoffset.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 01 09 00 00 00"); propertyMap.put("sampled.xoffset.localtag", "32 06 00 04"); // SampleYOffset propertyMap.put("sampled.yoffset.name", "SampledYOffset"); propertyMap.put("sampled.yoffset.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 01 0a 00 00 00"); propertyMap.put("sampled.yoffset.localtag", "32 07 00 04"); // DisplayHeight propertyMap.put("display.height.name", "DisplayHeight"); propertyMap.put("display.height.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 01 0b 00 00 00"); propertyMap.put("display.height.localtag", "32 08 00 04"); // DisplayWidth propertyMap.put("display.width.name", "DisplayWidth"); propertyMap.put("display.width.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 01 0c 00 00 00"); propertyMap.put("display.width.localtag", "32 09 00 04"); // DisplayXOffset propertyMap.put("display.xoffset.name", "DisplayXOffset"); propertyMap.put("display.xoffset.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 01 0d 00 00 00"); propertyMap.put("display.xoffset.localtag", "32 0a 00 04"); // DisplayYOffset propertyMap.put("display.yoffset.name", "DisplayYOffset"); propertyMap.put("display.yoffset.smptekey", "06 0e 2b 34 01 01 01 01 04 01 05 01 0e 00 00 00"); propertyMap.put("display.yoffset.localtag", "32 0b 00 04"); // AspectRatio propertyMap.put("aspectratio.name", "AspectRatio"); propertyMap.put("aspectratio.smptekey", "06 0e 2b 34 01 01 01 01 04 01 01 01 01 00 00 00"); propertyMap.put("aspectratio.localtag", "32 0e 00 08"); // Generic Sound Essence Descriptor // AudioSamplingRate propertyMap.put("audio.samplingrate.name", "AudioSamplingRate"); propertyMap.put("audio.samplingrate.smptekey", "06 0e 2b 34 01 01 01 05 04 02 03 01 01 01 00 00"); propertyMap.put("audio.samplingrate.localtag", "3d 03 00 08"); // ChannelCount propertyMap.put("audio.channelcount.name", "AudioChannelCount"); propertyMap.put("audio.channelcount.smptekey", "06 0e 2b 34 01 01 01 05 04 02 01 01 04 00 00 00"); propertyMap.put("audio.channelcount.localtag", "3d 07 00 04"); // QuantizationBits propertyMap.put("audio.quantizationbits.name", "AudioQuantizationBits"); propertyMap.put("audio.quantizationbits.smptekey", "06 0e 2b 34 01 01 01 04 04 02 03 03 04 00 00 00"); propertyMap.put("audio.quantizationbits.localtag", "3d 01 00 04"); // SoundEssenceCoding propertyMap.put("audio.soundessencecoding.name", "SoundEssenceCoding"); propertyMap.put("audio.soundessencecoding.smptekey", "06 0e 2b 34 01 01 01 02 04 02 04 02 00 00 00 00"); propertyMap.put("audio.soundessencecoding.localtag", "3d 01 00 16"); // VideoActiveLinesPerFrame propertyMap.put("videoactivelinesperframe.name", "VideoActiveLinesPerFrame"); } private void initAttributeMap() { // List of attribute names attributeNames = new ArrayList<String>(); // Hash Table with Attribute Names and Values attributeMap = new HashMap<String, String>(); Set<String> attrSet = propertyMap.keySet(); Iterator<String> attriIter = attrSet.iterator(); while (attriIter.hasNext()) { String attrKey = attriIter.next(); if (attrKey.contains(".name")) { attributeNames.add(propertyMap.get(attrKey)); attributeMap.put(attrKey, null); } } } private long getAllFromPartitionPack(MXFBuffer mxfbuf) throws ToolException { long EndOfHeaderPos; int[] partkey = new int[SMPTEKEYSIZE]; partkey = null; // get PartitionPackKey from properties partkey = initsmptekey(propertyMap.get("partition.pack.smptekey")); if (partkey == null) throw new ToolException("partition.pack.key not found"); if (mxfbuf.checksmptekey(0, partkey, 13)) { int partitionkind = mxfbuf.buf[13]; int partitionstatus = mxfbuf.buf[14]; String partitionPack = ""; switch (partitionkind) { case 2: partitionPack += "Header Partition: "; break; case 3: partitionPack += "Body Partition: "; break; case 4: partitionPack += "Footer Partition: "; break; default: throw new ToolException("parsePartitionPack: Unknown Partition kind (Byte[13] value:" + partitionkind + ")"); } switch (partitionstatus) { case 1: partitionPack += "Open and Incomplete"; break; case 2: partitionPack += "Closed and Incomplete"; break; case 3: partitionPack += "Open and Complete"; break; case 4: partitionPack += "Closed and Complete"; break; default: throw new ToolException("parsePartitionPack: Unknown Partition Status (Byte[14] value:" + partitionstatus + ")"); } attributeMap.put("PartitionPack", partitionPack); } else { throw new ToolException("Partition Pack NOT FOUND. Not an MXF file???"); } int pos = 16; long packlen = mxfbuf.parseBERlength(pos); pos += mxfbuf.BERlengthOffset(pos); pos += 2; pos += 2; long KagSize = mxfbuf.getUint(pos, 4); EndOfHeaderPos = (packlen / KagSize) * KagSize + KagSize; pos += 4; pos += 8; pos += 8; pos += 8; long HeaderByteCount = mxfbuf.getInt(pos, 8); EndOfHeaderPos += HeaderByteCount; pos += 8; pos += 8; pos += 4; pos += 8; pos += 4; int[] opkey = initsmptekey(propertyMap.get("operational.pattern.smptekey")); if (mxfbuf.checksmptekey(pos, opkey, 12)) { pos += 12; int ItemComplexity = mxfbuf.buf[pos++]; int PackageComplexity = mxfbuf.buf[pos++]; String operationalpattern = "OP" + ItemComplexity; switch (PackageComplexity) { case 1: operationalpattern += "a"; break; case 2: operationalpattern += "b"; break; case 3: operationalpattern += "c"; break; } attributeMap.put("OperationalPattern", operationalpattern); logger.debug("Operational Pattern: " + operationalpattern); } else { throw new ToolException("Operational Pattern NOT FOUND. Not an MXF file???"); } pos += 2; // find essence container pos += 8; // EC batch label (8B) long numEC = (packlen + 16 + 8 + mxfbuf.BERlengthOffset(16) - pos) / 16; int[] ECmapkey = initsmptekey(propertyMap.get("essence.container.smptekey")); String EssenceContainer = ""; String EssenceContainers = ""; for (int i = 0; i < numEC; i++) { if (mxfbuf.checksmptekey(pos, ECmapkey, 13)) { // logger.debug("parsePartitionPack: found Essence Container UL "+mxfbuf.buf[pos+13]); switch (mxfbuf.buf[pos + 13]) { case 1: EssenceContainer = "D10 Mapping"; break; case 2: EssenceContainer = "DV Mapping"; break; case 3: EssenceContainer = "D11 Mapping"; break; case 4: EssenceContainer = "MPEG ES Mapping"; break; case 5: EssenceContainer = "Uncompressed Picture Mapping"; break; case 6: EssenceContainer = "AES-BWF Mapping"; break; case 7: EssenceContainer = "MPEG PES Mapping"; break; case 8: EssenceContainer = "MPEG PS Mapping"; break; case 9: EssenceContainer = "MPEG TS Mapping"; break; case 12: // 0x0c EssenceContainer = "JPEG 2000 Picture Element Mapping"; break; case 127: EssenceContainer = "Generic Essence Multiple Mappings"; break; default: EssenceContainer = "Unknown Mapping (" + mxfbuf.buf[pos + 14] + ")" + mxfbuf.getUL(pos, 16); break; } EssenceContainers = EssenceContainers + EssenceContainer + ","; logger.debug("EssenceContainer: " + EssenceContainer); } else { logger.debug("Essence Container not found! " + mxfbuf.getUL(pos, 16)); } pos += 16; } EssenceContainers = EssenceContainers.substring(0, EssenceContainers.length() - 1); attributeMap.put("EssenceContainers", EssenceContainers); return (EndOfHeaderPos); } private void getMaterialPackageId(MXFBuffer mxfbuf) throws Exception { // find MAterial Package ID int[] key = null; int numfound = 0; int pos = 0; int prevpos = pos; long packlen = 0; key = initsmptekey(propertyMap.get("materialpackage.id.smptekey")); boolean done = false; while (!done) { prevpos = pos; pos = mxfbuf.findsmptekey(prevpos, key, mxfbuf.bufreallen - 16, 16); if (pos == -1) { // logger.debug("Found "+ numfound // +" materialpackage.id.smptekey"); done = true; } else { pos += 16; packlen = mxfbuf.parseBERlength(pos); getLocalMDField(mxfbuf, pos, (int) packlen, "umid.localtag", "MaterialPackageID", 4, 32, true); pos += (int) packlen; numfound++; } } } private void getAllFromPictureEssenceDescriptor(MXFBuffer mxfbuf) throws Exception { int[] key = initsmptekey(propertyMap.get("generic.picture.essence.descriptor.smptekey")); int pos = 0; pos = mxfbuf.findsmptekey(pos, key, mxfbuf.bufreallen - 16, 16); if (pos == -1) { key = initsmptekey(propertyMap.get("cdci.essence.descriptor.smptekey")); pos = mxfbuf.findsmptekey(0, key, mxfbuf.bufreallen - 16, 16); if (pos == -1) { key = initsmptekey(propertyMap.get("mpeg.essence.descriptor.smptekey")); pos = mxfbuf.findsmptekey(0, key, mxfbuf.bufreallen - 16, 16); } if (pos == -1) { key = initsmptekey(propertyMap.get("rgba.essence.descriptor.smptekey")); pos = mxfbuf.findsmptekey(0, key, mxfbuf.bufreallen - 16, 16); } } if (pos == -1) { logger.debug("No picture essence descriptor found"); } else { pos += 16; long cdcipacklen = mxfbuf.parseBERlength(pos); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "display.width.localtag", "DisplayWidth", 1, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "display.height.localtag", "DisplayHeight", 1, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "sampled.width.localtag", "SampledWidth", 1, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "sampled.height.localtag", "SampledHeight", 1, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "stored.width.localtag", "StoredWidth", 1, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "stored.height.localtag", "StoredHeight", 1, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "stored.height.localtag", "StoredHeight", 1, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "framelayout.localtag", "FrameLayout", 1, 1); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "activeformatdescriptor.localtag", "ActiveFormatDescriptor", 1, 1); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "componentdepth.localtag", "ComponentDepth", 1, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "horizontalsubsampling.localtag", "HorizontalSubSampling", 1, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "verticalsubsampling.localtag", "VerticalSubSampling", 1, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "aspectratio.localtag", "AspectRatio", 3, 8); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "picture.essencecoding.localtag", "PictureEssenceCoding", 4, 16); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "display.xoffset.localtag", "DisplayXOffset", 2, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "display.yoffset.localtag", "DisplayYOffset", 2, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "sampled.xoffset.localtag", "SampledXOffset", 2, 4); getLocalMDField(mxfbuf, pos, (int) cdcipacklen, "sampled.yoffset.localtag", "SampledYOffset", 2, 4); } } private void getAllFromSoundEssenceDescriptor(MXFBuffer mxfbuf) throws Exception { int[] key = null; int pos = 0; int prevpos = pos; boolean done = false; long packlen = 0; while (!done) { prevpos = pos; key = initsmptekey(propertyMap.get("generic.sound.essence.descriptor.smptekey")); pos = mxfbuf.findsmptekey(pos, key, mxfbuf.bufreallen - 16, 16); if (pos == -1) { key = initsmptekey(propertyMap.get("aes3.audio.essence.descriptor.smptekey")); pos = mxfbuf.findsmptekey(prevpos, key, mxfbuf.bufreallen - 16, 16); if (pos == -1) { key = initsmptekey(propertyMap.get("wave.audio.essence.descriptor.smptekey")); pos = mxfbuf.findsmptekey(prevpos, key, mxfbuf.bufreallen - 16, 16); } } if (pos == -1) { done = true; logger.debug("No generic sound or wave audio essence descriptor found"); } else { pos += 16; packlen = mxfbuf.parseBERlength(pos); getLocalMDField(mxfbuf, pos, (int) packlen, "audio.samplingrate.localtag", "AudioSamplingRate", 3, 8); getLocalMDField(mxfbuf, pos, (int) packlen, "audio.quantizationbits.localtag", "AudioQuantizationBits", 1, 4); getLocalMDField(mxfbuf, pos, (int) packlen, "audio.soundessencecoding.localtag", "SoundEssenceCoding", 4, 16); int AudioChannelCount = 0; if (attributeMap.get("AudioChannelCount") != null) { AudioChannelCount = Integer.parseInt(attributeMap.get("AudioChannelCount")); attributeMap.put("AudioChannelCount", null); } getLocalMDField(mxfbuf, pos, (int) packlen, "audio.channelcount.localtag", "AudioChannelCount", 1, 4); if (attributeMap.get("AudioChannelCount") != null) { AudioChannelCount += Integer.parseInt(attributeMap.get("AudioChannelCount")); } attributeMap.put("AudioChannelCount", Integer.toString(AudioChannelCount)); } pos += (int) packlen; } } private void getEditRate(MXFBuffer mxfbuf) throws Exception { int[] key = null; int numfound = 0; int pos = 0; int prevpos = pos; long packlen = 0; key = initsmptekey((String) propertyMap.get("timeline.track.smptekey")); // TODO find all boolean done = false; while (!done) { prevpos = pos; pos = mxfbuf.findsmptekey(prevpos, key, mxfbuf.bufreallen - 16, 16); if (pos == -1) { done = true; } else { pos += 16; packlen = mxfbuf.parseBERlength(pos); getLocalMDField(mxfbuf, pos, (int) packlen, "editrate.localtag", "EditRate", 3, 8); pos += (int) packlen; } } } private void getDuration(MXFBuffer mxfbuf) throws Exception { int[] key = null; int numfound = 0; int pos = 0; int prevpos = pos; long packlen = 0; key = initsmptekey(propertyMap.get("essence.track.smptekey")); boolean done = false; while (!done) { prevpos = pos; pos = mxfbuf.findsmptekey(prevpos, key, mxfbuf.bufreallen - 16, 16); if (pos == -1) { done = true; } else { pos += 16; packlen = mxfbuf.parseBERlength(pos); getLocalMDField(mxfbuf, pos, (int) packlen, "duration.localtag", "Duration", 1, 8); pos += (int) packlen; } } } private void setPictureDefaultValues() throws Exception { if (attributeMap.get("StoredWidth") != null && attributeMap.get("SampledWidth") == null) { attributeMap.put("SampledWidth", attributeMap.get("StoredWidth")); } if (attributeMap.get("StoredHeight") != null && attributeMap.get("SampledHeight") == null) { attributeMap.put("SampledHeight", attributeMap.get("StoredHeight")); } if (attributeMap.get("SampledWidth") != null && attributeMap.get("DisplayWidth") == null) { attributeMap.put("DisplayWidth", attributeMap.get("SampledWidth")); } if (attributeMap.get("SampledHeight") != null && attributeMap.get("DisplayHeight") == null) { attributeMap.put("DisplayHeight", attributeMap.get("SampledHeight")); } if (attributeMap.get("StoredWidth") != null && attributeMap.get("SampledXOffset") == null) { attributeMap.put("SampledXOffset", "0"); } if (attributeMap.get("SampledWidth") != null && attributeMap.get("DisplayXOffset") == null) { attributeMap.put("DisplayXOffset", "0"); } if (attributeMap.get("StoredHeight") != null && attributeMap.get("SampledYOffset") == null) { attributeMap.put("SampledYOffset", "0"); } if (attributeMap.get("SampledHeight") != null && attributeMap.get("DisplayYOffset") == null) { attributeMap.put("DisplayYOffset", "0"); } } private void setVideoActiveLinesPerFrame() throws Exception { if (attributeMap.get("DisplayHeight") != null && attributeMap.get("FrameLayout") != null) { int framelayout = 0; int numlines = 0; framelayout = Integer.parseInt(attributeMap.get("FrameLayout")); numlines = Integer.parseInt(attributeMap.get("DisplayHeight")); if (framelayout > 0) numlines *= 2; attributeMap.put("VideoActiveLinesPerFrame", Integer.toString(numlines)); } } private void setFrameLayoutName() throws Exception { if (attributeMap.get("FrameLayout") != null) { try { int fl = Integer.parseInt(attributeMap.get("FrameLayout")); switch (fl) { case 0: attributeMap.put("FrameLayoutName", "FULL_FRAME"); break; case 1: attributeMap.put("FrameLayoutName", "SEPARATE_FIELDS"); break; case 2: attributeMap.put("FrameLayoutName", "SINGLE_FIELD"); break; case 3: attributeMap.put("FrameLayoutName", "MIXED_FIELDS"); break; case 4: attributeMap.put("FrameLayoutName", "SEGMENTED_FRAME"); break; default: attributeMap.put("FrameLayoutName", "Unknown value (" + fl + ")"); break; } } catch (Exception e) { throw new ToolException("SetFrameLayoutName: FrameLayout " + attributeMap.get("FrameLayout") + " was not an Integer"); } } } private void getLocalMDField(MXFBuffer mxfbuf, int pos, int cdcilen, String localtag, String MapEntry, int datatype, int datalen) throws ToolException { getLocalMDField(mxfbuf, pos, cdcilen, localtag, MapEntry, datatype, datalen, false); } private void getLocalMDField(MXFBuffer mxfbuf, int pos, int cdcilen, String localtag, String MapEntry, int datatype, int datalen, boolean append) throws ToolException { int[] localkey = null; int locpos = pos; try { localkey = initsmptekey(propertyMap.get(localtag)); } catch (NullPointerException e) { throw new ToolException("Key of local tag " + localtag + " not found. initsmptekey failed"); } locpos = mxfbuf.findsmptekey(pos, localkey, cdcilen, 4); // localtag // (4B) if (locpos > 0) { String oldEntry = attributeMap.get(MapEntry); if (append && oldEntry != null) oldEntry += ","; if (oldEntry == null) append = false; switch (datatype) { case 1: // Uint if (!append) { attributeMap.put(MapEntry, Long.toString(mxfbuf.getUint(locpos + 4, datalen))); } else { attributeMap.put(MapEntry, oldEntry + Long.toString(mxfbuf.getUint(locpos + 4, datalen))); } break; case 2: // Signed Int if (!append) { attributeMap.put(MapEntry, Long.toString(mxfbuf.getInt(locpos + 4, datalen))); } else { attributeMap.put(MapEntry, oldEntry + Long.toString(mxfbuf.getInt(locpos + 4, datalen))); } break; case 3: // Ratio of two Uint long[] myratio = mxfbuf.getRatio(locpos + 4, datalen); String myratiostring = "" + Long.toString(myratio[0]) + ":" + Long.toString(myratio[1]); if (!append) { attributeMap.put(MapEntry, myratiostring); } else { attributeMap.put(MapEntry, oldEntry + myratiostring); } break; case 4: // Universal Label (UL) if (!append) { attributeMap.put(MapEntry, mxfbuf.getUL(locpos + 4, datalen)); } else { attributeMap.put(MapEntry, oldEntry + mxfbuf.getUL(locpos + 4, datalen)); } break; default: break; } String currentEntry = attributeMap.get(MapEntry); if (!append && currentEntry != null && oldEntry != null && !currentEntry.matches(oldEntry)) { // logger.debug("getLocalMDField: "+MapEntry+" has not a single value"+oldEntry+" "+currentEntry); attributeMap.put(MapEntry, "multiple property with different values"); } } } private static int[] initsmptekey(String stringkeyin) { int i = 0; int[] binkeyout = new int[SMPTEKEYSIZE]; String tmp; StringTokenizer st = new StringTokenizer(stringkeyin); while (st.hasMoreTokens() && i < SMPTEKEYSIZE) { tmp = "0x" + st.nextToken().trim(); binkeyout[i] = Integer.decode(tmp); i++; } return (binkeyout); } } class MXFBuffer { public int[] buf = null; public int bufreallen; public MXFBuffer(int bufsize) { buf = new int[bufsize]; for (int i = 0; i < buf.length; i++) { buf[i] = 0; } } public void loadBuffer(RandomAccessFile mxfin) throws ToolException { byte[] readbuf = new byte[buf.length]; try { bufreallen = mxfin.read(readbuf, 0, buf.length); for (int i = 0; i < bufreallen; i++) { // TBC buf[i] = (int) readbuf[i] & 0xFF; } } catch (IOException e) { e.printStackTrace(); throw new ToolException("Error loading MXF buffer..."); } } public boolean checksmptekey(int pos, int[] key, int klen) { // the key must be exactly at the given position, otherwise return false // the key can be masked by if (pos + klen > buf.length) return false; for (int i = 0; i < klen; i++) { if (buf[pos + i] != key[i] && key[i] < 256) { return false; } } return true; } public int findsmptekey(int pos, int[] key, int maxsearch, int klen) { // returns the buffer position to the location where the key was found // otherwise return -1 int ret = -1; for (int i = pos; i < (pos + maxsearch); i++) { if (checksmptekey(i, key, klen)) { return i; } } return ret; } public String getUL(int pos, int len) throws ToolException { String ret = ""; String hex = ""; for (int i = pos; i < pos + len; i++) { hex = Integer.toHexString(buf[i]); if (hex.length() < 2) hex = "0" + hex; // ret += Integer.toHexString((byte)buf[i])+" "; ret += hex + " "; } ret = ret.substring(0, ret.length() - 1);// toglie ultimo spazio return (ret); } public long[] getRatio(int pos, int len) throws ToolException { long[] ret = new long[2]; ret[0] = getUint(pos, len / 2); ret[1] = getUint(pos + len / 2, len / 2); return ret; } public long getUint(int pos, int len) throws ToolException { // read from buffer long ret = 0; int val = 0; for (int i = pos; i < pos + len; i++) { // signed to unsigned val = ((int) buf[i]) & 0xFF; ret = ret * 256 + val; } return (ret); } public long getInt(int pos, int len) throws ToolException { // legge da buffer in posizione pos, len "bytes" come Signed int long ret = 0; int val = 0; for (int i = pos; i < pos + len; i++) { if (i == pos) val = ((int) buf[i]); else val = ((int) buf[i]) & 0xFF; ret = ret * 256 + val; } return (ret); } public long parseBERlength(int pos) throws ToolException { // aims to be according to Annex K of SMPTE 336M-2007 long len = 0; // read 1 byte int n = buf[pos]; if (n < 128) { len = n; } else { n -= 128; len = getUint(pos + 1, n); } return (len); } public int BERlengthOffset(int pos) throws ToolException { int n = buf[pos]; if (n < 128) return (1); else return (n - 128 + 1); } }