From 9e40123c9191f9529409d27c90995c79d2baea8e Mon Sep 17 00:00:00 2001 From: njovic Date: Sat, 31 Aug 2024 01:32:29 +0200 Subject: [PATCH 1/4] Issue #582 | Support for WebM/MKV --- Source/com/drew/imaging/FileType.java | 1 + Source/com/drew/imaging/FileTypeDetector.java | 1 + .../com/drew/imaging/ImageMetadataReader.java | 3 + .../drew/imaging/mkv/MkvMetadataReader.java | 20 ++ Source/com/drew/imaging/mkv/package-info.java | 1 + .../com/drew/metadata/mkv/AudioDirectory.java | 41 +++ Source/com/drew/metadata/mkv/DataParser.java | 68 +++++ .../com/drew/metadata/mkv/EbmlDirectory.java | 35 +++ Source/com/drew/metadata/mkv/EbmlElement.java | 39 +++ Source/com/drew/metadata/mkv/ElementIDs.java | 67 +++++ Source/com/drew/metadata/mkv/MkvReader.java | 240 ++++++++++++++++++ .../metadata/mkv/SegmentInfoDirectory.java | 33 +++ .../com/drew/metadata/mkv/VideoDirectory.java | 50 ++++ .../com/drew/metadata/mkv/package-info.java | 1 + 14 files changed, 600 insertions(+) create mode 100644 Source/com/drew/imaging/mkv/MkvMetadataReader.java create mode 100644 Source/com/drew/imaging/mkv/package-info.java create mode 100644 Source/com/drew/metadata/mkv/AudioDirectory.java create mode 100644 Source/com/drew/metadata/mkv/DataParser.java create mode 100644 Source/com/drew/metadata/mkv/EbmlDirectory.java create mode 100644 Source/com/drew/metadata/mkv/EbmlElement.java create mode 100644 Source/com/drew/metadata/mkv/ElementIDs.java create mode 100644 Source/com/drew/metadata/mkv/MkvReader.java create mode 100644 Source/com/drew/metadata/mkv/SegmentInfoDirectory.java create mode 100644 Source/com/drew/metadata/mkv/VideoDirectory.java create mode 100644 Source/com/drew/metadata/mkv/package-info.java diff --git a/Source/com/drew/imaging/FileType.java b/Source/com/drew/imaging/FileType.java index 9914ac50c..7b4a2c627 100644 --- a/Source/com/drew/imaging/FileType.java +++ b/Source/com/drew/imaging/FileType.java @@ -50,6 +50,7 @@ public enum FileType Avif("AVIF", "AV1 Image File Format", "image/avif", "avif"), Eps("EPS", "Encapsulated PostScript", "application/postscript", "eps", "epsf", "epsi"), Mp3("MP3", "MPEG Audio Layer III", "audio/mpeg", "mp3"), + Mkv("MKV", "Matroska Video Container", "video/x-matroska", "mkv", "webm"), /** Sony camera raw. */ Arw("ARW", "Sony Camera Raw", null, "arw"), diff --git a/Source/com/drew/imaging/FileTypeDetector.java b/Source/com/drew/imaging/FileTypeDetector.java index fda22575e..f324a48fe 100644 --- a/Source/com/drew/imaging/FileTypeDetector.java +++ b/Source/com/drew/imaging/FileTypeDetector.java @@ -103,6 +103,7 @@ public class FileTypeDetector _root.addPath(FileType.Swf, "ZWS".getBytes()); _root.addPath(FileType.Vob, new byte[]{0x00, 0x00, 0x01, (byte)0xBA}); _root.addPath(FileType.Zip, "PK".getBytes()); + _root.addPath(FileType.Mkv, new byte[]{0x1A, 0x45, (byte) 0xDF, (byte) 0xA3}); int bytesNeeded = _root.getMaxDepth(); for (TypeChecker fixedChecker : _fixedCheckers) { diff --git a/Source/com/drew/imaging/ImageMetadataReader.java b/Source/com/drew/imaging/ImageMetadataReader.java index 474300950..075de71bd 100644 --- a/Source/com/drew/imaging/ImageMetadataReader.java +++ b/Source/com/drew/imaging/ImageMetadataReader.java @@ -27,6 +27,7 @@ import com.drew.imaging.heif.HeifMetadataReader; import com.drew.imaging.ico.IcoMetadataReader; import com.drew.imaging.jpeg.JpegMetadataReader; +import com.drew.imaging.mkv.MkvMetadataReader; import com.drew.imaging.mp3.Mp3MetadataReader; import com.drew.imaging.mp4.Mp4MetadataReader; import com.drew.imaging.quicktime.QuickTimeMetadataReader; @@ -182,6 +183,8 @@ public static Metadata readMetadata(@NotNull final InputStream inputStream, fina case Heif: case Avif: return HeifMetadataReader.readMetadata(inputStream); + case Mkv: + return MkvMetadataReader.readMetadata(inputStream); case Unknown: throw new ImageProcessingException("File format could not be determined"); default: diff --git a/Source/com/drew/imaging/mkv/MkvMetadataReader.java b/Source/com/drew/imaging/mkv/MkvMetadataReader.java new file mode 100644 index 000000000..efe118ab5 --- /dev/null +++ b/Source/com/drew/imaging/mkv/MkvMetadataReader.java @@ -0,0 +1,20 @@ +package com.drew.imaging.mkv; + +import com.drew.imaging.heif.HeifReader; +import com.drew.lang.StreamReader; +import com.drew.lang.annotations.NotNull; +import com.drew.metadata.Metadata; +import com.drew.metadata.heif.HeifBoxHandler; +import com.drew.metadata.mkv.MkvReader; + +import java.io.IOException; +import java.io.InputStream; + +public class MkvMetadataReader { + @NotNull + public static Metadata readMetadata(@NotNull InputStream inputStream) throws IOException { + Metadata metadata = new Metadata(); + new MkvReader().extract(new StreamReader(inputStream), metadata); + return metadata; + } +} diff --git a/Source/com/drew/imaging/mkv/package-info.java b/Source/com/drew/imaging/mkv/package-info.java new file mode 100644 index 000000000..4cd58342d --- /dev/null +++ b/Source/com/drew/imaging/mkv/package-info.java @@ -0,0 +1 @@ +package com.drew.imaging.mkv; diff --git a/Source/com/drew/metadata/mkv/AudioDirectory.java b/Source/com/drew/metadata/mkv/AudioDirectory.java new file mode 100644 index 000000000..c9063b8a4 --- /dev/null +++ b/Source/com/drew/metadata/mkv/AudioDirectory.java @@ -0,0 +1,41 @@ +package com.drew.metadata.mkv; + +import com.drew.metadata.Directory; +import com.drew.metadata.TagDescriptor; + +import java.util.HashMap; + +import static com.drew.metadata.mkv.ElementIDs.*; + +public class AudioDirectory extends Directory { + private static final HashMap _tagNameMap = new HashMap<>(); + + public AudioDirectory(){ + this.setDescriptor(new TagDescriptor(this)); + } + static { + + _tagNameMap.put(TRACK_NUMBER, "Track number"); + _tagNameMap.put(TRACK_UID, "Track UID"); + _tagNameMap.put(TRACK_TYPE, "Track type"); + _tagNameMap.put(TAG_LACING, "Tag lacing"); + _tagNameMap.put(CODEC_ID, "Codec ID"); + _tagNameMap.put(LANGUAGE, "Language"); + _tagNameMap.put(LANGUAGE_BCP47, "Language BCP47"); + _tagNameMap.put(DEFAULT_DURATION, "Default duration"); + _tagNameMap.put(CHANNELS, "Channels"); + _tagNameMap.put(SAMPLING_FREQUENCY, "Sampling frequency"); + _tagNameMap.put(BIT_DEPTH, "Bit depth"); + + } + + @Override + public String getName() { + return "Audio"; + } + + @Override + protected HashMap getTagNameMap() { + return _tagNameMap; + } +} diff --git a/Source/com/drew/metadata/mkv/DataParser.java b/Source/com/drew/metadata/mkv/DataParser.java new file mode 100644 index 000000000..8c1497df0 --- /dev/null +++ b/Source/com/drew/metadata/mkv/DataParser.java @@ -0,0 +1,68 @@ +package com.drew.metadata.mkv; + +import com.drew.lang.SequentialReader; + +import java.io.IOException; + +public class DataParser { + private static final long[] VSINT_SUBTR = { 0x3F, 0x1FFF, 0x0FFFFF, 0x07FFFFFF, + 0x03FFFFFFFFL, 0x01FFFFFFFFFFL, + 0x00FFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL }; + + static long doDecodeInteger(final SequentialReader reader, boolean signed) throws IOException { + byte firstByte = reader.getBytes(1)[0]; + int position = 7; + for (; position >= 0; position--) { + if ((firstByte & (1 << position)) != 0) { + break; + } + } + int length = 7 - position; + byte[] values = reader.getBytes(length); + long result = (firstByte & ((1L << position) - 1)) << (length * 8); + for (int i = 1; i <= length; i++) { + result |= ((long) (values[i - 1] & 0xFF) << ((length - i) * 8)); + } + return signed ? result - VSINT_SUBTR[length] : result; + } + + static long decodeInteger(final SequentialReader reader) throws IOException { + return doDecodeInteger(reader, false); + } + + static long decodeSignedInteger(final SequentialReader reader) throws IOException { + return doDecodeInteger(reader, true); + } + + static int getElementId(final SequentialReader reader) throws IOException { + byte firstByte = reader.getBytes(1)[0]; + int position = 7; + for (; position >= 0; position--) { + if ((firstByte & (1 << position)) != 0) { + break; + } + } + int length = 7 - position; + byte[] values = reader.getBytes(length); + int result = ((int) (firstByte & 0xFF)) << (length * 8); + for (int i = 1; i <= length; i++) { + result |= (((int) values[i - 1] & 0xFF) << ((length - i) * 8)); + } + return result; + } + + static long getLong(final SequentialReader reader, long size) throws IOException { + long result = 0L; + for (long i = size - 1; i >= 0; i--){ + result |= (long) (reader.getByte() & 0xFF) << (i * 8); + } + return result; + } + + static byte[] getByteArray(final SequentialReader reader, long size) throws IOException { + return reader.getBytes((int) size); + } + static String getString(final SequentialReader reader, long size) throws IOException{ + return reader.getString((int) size); + } +} diff --git a/Source/com/drew/metadata/mkv/EbmlDirectory.java b/Source/com/drew/metadata/mkv/EbmlDirectory.java new file mode 100644 index 000000000..d9db9f46e --- /dev/null +++ b/Source/com/drew/metadata/mkv/EbmlDirectory.java @@ -0,0 +1,35 @@ +package com.drew.metadata.mkv; + +import com.drew.metadata.Directory; +import com.drew.metadata.TagDescriptor; + +import java.util.HashMap; +import static com.drew.metadata.mkv.ElementIDs.*; + +public class EbmlDirectory extends Directory { + + private static final HashMap _tagNameMap = new HashMap<>(); + static { + _tagNameMap.put(EBML_VERSION, "Version"); + _tagNameMap.put(EBML_READ_VERSION, "Read version"); + _tagNameMap.put(EBML_MAX_ID_LENGTH, "Maximum ID length"); + _tagNameMap.put(EBML_MAX_SIZE_LENGTH, "Maximum size length"); + _tagNameMap.put(DOCTYPE, "Doctype"); + _tagNameMap.put(DOCTYPE_VERSION, "Doctype version"); + _tagNameMap.put(DOCTYPE_READ_VERSION, "Doctype read version"); + } + + public EbmlDirectory() { + this.setDescriptor(new TagDescriptor(this)); + } + + @Override + public String getName() { + return "EBML"; + } + + @Override + protected HashMap getTagNameMap() { + return _tagNameMap; + } +} diff --git a/Source/com/drew/metadata/mkv/EbmlElement.java b/Source/com/drew/metadata/mkv/EbmlElement.java new file mode 100644 index 000000000..6c1d3caad --- /dev/null +++ b/Source/com/drew/metadata/mkv/EbmlElement.java @@ -0,0 +1,39 @@ +package com.drew.metadata.mkv; + +public class EbmlElement { + private final String name; + private final Type type; + private final DirectoryType directory; + + public String toString(){ + return name; + } + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public EbmlElement(String name, Type type) { + this(name, type, DirectoryType.UNKNOWN); + } + public EbmlElement(String name, Type type, DirectoryType directory) { + this.name = name; + this.type = type; + this.directory = directory; + } + + public DirectoryType getDirectory() { + return directory; + } + + public enum Type{ + MASTER, STRING, INTEGER, SIGNED_INTEGER, UTF8, BINARY, VOID, UNKNOWN, FLOAT + } + + public enum DirectoryType{ + EBML, SEGMENT, VIDEO, AUDIO, UNKNOWN + } +} diff --git a/Source/com/drew/metadata/mkv/ElementIDs.java b/Source/com/drew/metadata/mkv/ElementIDs.java new file mode 100644 index 000000000..4dfeb06bd --- /dev/null +++ b/Source/com/drew/metadata/mkv/ElementIDs.java @@ -0,0 +1,67 @@ +package com.drew.metadata.mkv; + +public class ElementIDs { + + static final int EBML_HEADER_ELEMENT = 0x1A45DFA3; + static final int EBML_VERSION = 0x4286; + static final int EBML_READ_VERSION = 0x42F7; + static final int EBML_MAX_ID_LENGTH = 0x42F2; + static final int EBML_MAX_SIZE_LENGTH = 0x42F3; + static final int DOCTYPE = 0x4282; + static final int DOCTYPE_VERSION = 0x4287; + static final int DOCTYPE_READ_VERSION = 0x4285; + static final int SEGMENT = 0x18538067; + static final int SEGMENT_INFO = 0x1549A966; + static final int SEEK_HEAD = 0x114D9B74; + static final int SEEK = 0x4DBB; + static final int SEEK_ID = 0x53AB; + static final int SEEK_POSITION = 0x53AC; + static final int MUXING_APP = 0x4D80; + static final int WRITING_APP = 0x5741; + static final int CODEC_ID = 0x86; + static final int VOID_ELEMENT = 0xEC; + static final int TIMESTAMP_SCALE = 0x2AD7B1; + static final int DURATION = 0x4489; + static final int CLUSTER = 0x1F43B675; + static final int SEGMENT_UUID = 0x73A4; + static final int TRACKS = 0x1654AE6B; + static final int TRACK_ENTRY = 0xAE; + static final int TRACK_NUMBER = 0xD7; + static final int TRACK_UID = 0x73C5; + static final int TRACK_TYPE = 0x83; + static final int TAG_LACING = 0x9C; + static final int AUDIO = 0xE1; + static final int CHANNELS = 0x9F; + static final int SAMPLING_FREQUENCY = 0xB5; + static final int BIT_DEPTH = 0x6264; + static final int CODEC_PRIVATE = 0x63A2; + static final int CUES = 0x1C53BB6B; + static final int LANGUAGE = 0x22B59C; + static final int LANGUAGE_BCP47 = 0x22B59D; + static final int DEFAULT_DURATION = 0x23E383; + static final int VIDEO = 0xE0; + static final int DISPLAY_WIDTH = 0x54B0; + static final int DISPLAY_HEIGHT = 0x54BA; + static final int DISPLAY_UNIT = 0x54B2; + static final int PIXEL_WIDTH = 0xB0; + static final int PIXEL_HEIGHT = 0xBA; + static final int FLAG_INTERLACED = 0x9A; + static final int COLOR = 0x55B0; + static final int TRANSFER_CHARACTERISTICS = 0x55BA; + static final int MATRIX_COEFFICIENTS = 0x55B1; + static final int PRIMARIES = 0x55BB; + static final int RANGE = 0x55B9; + static final int CHROMA_SITING_HORZ = 0x55B7; + static final int CHROMA_SITING_VERT = 0x55B8; + static final int CODEC_DELAY = 0x56AA; + static final int SEEK_PRE_ROLL = 0x56BB; + static final int TAGS = 0x1254C367; + static final int TAG = 0x7373; + static final int TARGETS = 0x63C0; + static final int SIMPLE_TAG = 0x67C8; + static final int TAG_NAME = 0x45A3; + static final int TAG_LANGUAGE = 0x447A; + static final int TAG_STRING = 0x4487; + static final int TAG_LANGUAGE_BCP47 = 0x447B; + static final int TAG_TRACK_UID = 0x63C5; +} diff --git a/Source/com/drew/metadata/mkv/MkvReader.java b/Source/com/drew/metadata/mkv/MkvReader.java new file mode 100644 index 000000000..e8e0075da --- /dev/null +++ b/Source/com/drew/metadata/mkv/MkvReader.java @@ -0,0 +1,240 @@ +package com.drew.metadata.mkv; + +import com.drew.lang.SequentialReader; +import com.drew.lang.StreamReader; +import com.drew.metadata.Directory; +import com.drew.metadata.Metadata; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.drew.metadata.mkv.EbmlElement.DirectoryType.EBML; +import static com.drew.metadata.mkv.EbmlElement.Type.*; +import static com.drew.metadata.mkv.DataParser.decodeInteger; + +public class MkvReader { + + private static final Map ELEMENTS = new HashMap<>(); + + static { + // VOID is legit element type in Matroska spec, we're abusing it here to skip parts we don't need + ELEMENTS.put(ElementIDs.EBML_HEADER_ELEMENT, new EbmlElement("EBML_HEADER", MASTER)); + ELEMENTS.put(ElementIDs.EBML_VERSION, new EbmlElement("EBML_VERSION", INTEGER, EBML)); + ELEMENTS.put(ElementIDs.EBML_READ_VERSION, new EbmlElement("EBML_READ_VERSION", INTEGER, EBML)); + ELEMENTS.put(ElementIDs.EBML_MAX_ID_LENGTH, new EbmlElement("EBML_MAX_ID_LENGTH", INTEGER, EBML)); + ELEMENTS.put(ElementIDs.EBML_MAX_SIZE_LENGTH, new EbmlElement("EBML_MAX_SIZE_LENGTH", INTEGER, EBML)); + ELEMENTS.put(ElementIDs.DOCTYPE, new EbmlElement("DOCTYPE", STRING, EBML)); + ELEMENTS.put(ElementIDs.DOCTYPE_VERSION, new EbmlElement("DOCTYPE_VERSION", INTEGER, EBML)); + ELEMENTS.put(ElementIDs.DOCTYPE_READ_VERSION, new EbmlElement("DOCTYPE_READ_VERSION", INTEGER, EBML)); + ELEMENTS.put(ElementIDs.SEGMENT, new EbmlElement("SEGMENT", MASTER)); + ELEMENTS.put(ElementIDs.SEGMENT_INFO, new EbmlElement("SEGMENT_INFO", MASTER)); + ELEMENTS.put(ElementIDs.SEEK_HEAD, new EbmlElement("SEEK_HEAD", VOID)); + ELEMENTS.put(ElementIDs.SEEK, new EbmlElement("SEEK", MASTER)); + ELEMENTS.put(ElementIDs.SEEK_ID, new EbmlElement("SEEK_ID", BINARY)); + ELEMENTS.put(ElementIDs.SEEK_POSITION, new EbmlElement("SEEK_POSITION", INTEGER)); + ELEMENTS.put(ElementIDs.MUXING_APP, new EbmlElement("MUXING_APP", UTF8)); + ELEMENTS.put(ElementIDs.WRITING_APP, new EbmlElement("WRITING_APP", UTF8)); + ELEMENTS.put(ElementIDs.CODEC_ID, new EbmlElement("CODEC_ID", STRING)); + ELEMENTS.put(ElementIDs.VOID_ELEMENT, new EbmlElement("VOID", VOID)); + ELEMENTS.put(ElementIDs.TIMESTAMP_SCALE, new EbmlElement("TIMESTAMP_SCALE", INTEGER)); + ELEMENTS.put(ElementIDs.DURATION, new EbmlElement("DURATION", FLOAT)); + ELEMENTS.put(ElementIDs.CLUSTER, new EbmlElement("CLUSTER", VOID)); + ELEMENTS.put(ElementIDs.SEGMENT_UUID, new EbmlElement("SEGMENT_UUID", BINARY)); + ELEMENTS.put(ElementIDs.TRACKS, new EbmlElement("TRACKS", MASTER)); + ELEMENTS.put(ElementIDs.TRACK_ENTRY, new EbmlElement("TRACK_ENTRY", MASTER)); + ELEMENTS.put(ElementIDs.TRACK_NUMBER, new EbmlElement("TRACK_NUMBER", INTEGER)); + ELEMENTS.put(ElementIDs.TRACK_UID, new EbmlElement("TRACK_UID", INTEGER)); + ELEMENTS.put(ElementIDs.TRACK_TYPE, new EbmlElement("TRACK_TYPE", INTEGER)); + ELEMENTS.put(ElementIDs.TAG_LACING, new EbmlElement("TAG_LACING", INTEGER)); + ELEMENTS.put(ElementIDs.AUDIO, new EbmlElement("AUDIO", MASTER)); + ELEMENTS.put(ElementIDs.CHANNELS, new EbmlElement("CHANNELS", INTEGER)); + ELEMENTS.put(ElementIDs.SAMPLING_FREQUENCY, new EbmlElement("SAMPLING_FREQUENCY", FLOAT)); + ELEMENTS.put(ElementIDs.BIT_DEPTH, new EbmlElement("BIT_DEPTH", INTEGER)); + ELEMENTS.put(ElementIDs.CODEC_PRIVATE, new EbmlElement("CODEC_PRIVATE", VOID)); + ELEMENTS.put(ElementIDs.CUES, new EbmlElement("CUES", VOID)); + ELEMENTS.put(ElementIDs.LANGUAGE, new EbmlElement("LANGUAGE", STRING)); + ELEMENTS.put(ElementIDs.LANGUAGE_BCP47, new EbmlElement("LANGUAGE_BCP47", STRING)); + ELEMENTS.put(ElementIDs.DEFAULT_DURATION, new EbmlElement("DEFAULT_DURATION", INTEGER)); + ELEMENTS.put(ElementIDs.VIDEO, new EbmlElement("VIDEO", MASTER)); + ELEMENTS.put(ElementIDs.DISPLAY_WIDTH, new EbmlElement("DISPLAY_WIDTH", INTEGER)); + ELEMENTS.put(ElementIDs.DISPLAY_HEIGHT, new EbmlElement("DISPLAY_HEIGHT", INTEGER)); + ELEMENTS.put(ElementIDs.DISPLAY_UNIT, new EbmlElement("DISPLAY_UNIT", INTEGER)); + ELEMENTS.put(ElementIDs.PIXEL_WIDTH, new EbmlElement("PIXEL_WIDTH", INTEGER)); + ELEMENTS.put(ElementIDs.PIXEL_HEIGHT, new EbmlElement("PIXEL_HEIGHT", INTEGER)); + ELEMENTS.put(ElementIDs.FLAG_INTERLACED, new EbmlElement("FLAG_INTERLACED", INTEGER)); + ELEMENTS.put(ElementIDs.COLOR, new EbmlElement("COLOR", MASTER)); + ELEMENTS.put(ElementIDs.TRANSFER_CHARACTERISTICS, new EbmlElement("TRANSFER_CHARACTERISTICS", INTEGER)); + ELEMENTS.put(ElementIDs.MATRIX_COEFFICIENTS, new EbmlElement("MATRIX_COEFFICIENTS", INTEGER)); + ELEMENTS.put(ElementIDs.PRIMARIES, new EbmlElement("PRIMARIES", INTEGER)); + ELEMENTS.put(ElementIDs.RANGE, new EbmlElement("RANGE", INTEGER)); + ELEMENTS.put(ElementIDs.CHROMA_SITING_HORZ, new EbmlElement("CHROMA_SITING_HORZ", INTEGER)); + ELEMENTS.put(ElementIDs.CHROMA_SITING_VERT, new EbmlElement("CHROMA_SITING_VERT", INTEGER)); + ELEMENTS.put(ElementIDs.CODEC_DELAY, new EbmlElement("CODEC_DELAY", INTEGER)); + ELEMENTS.put(ElementIDs.SEEK_PRE_ROLL, new EbmlElement("SEEK_PRE_ROLL", INTEGER)); + ELEMENTS.put(ElementIDs.TAGS, new EbmlElement("TAGS", MASTER)); + ELEMENTS.put(ElementIDs.TAG, new EbmlElement("TAG", MASTER)); + ELEMENTS.put(ElementIDs.TARGETS, new EbmlElement("TARGETS", MASTER)); + ELEMENTS.put(ElementIDs.SIMPLE_TAG, new EbmlElement("SIMPLE_TAG", MASTER)); + ELEMENTS.put(ElementIDs.TAG_NAME, new EbmlElement("TAG_NAME", UTF8)); + ELEMENTS.put(ElementIDs.TAG_LANGUAGE, new EbmlElement("TAG_LANGUAGE", STRING)); + ELEMENTS.put(ElementIDs.TAG_STRING, new EbmlElement("TAG_STRING", UTF8)); + ELEMENTS.put(ElementIDs.TAG_LANGUAGE_BCP47, new EbmlElement("TAG_LANGUAGE_BCP47", STRING)); + ELEMENTS.put(ElementIDs.TAG_TRACK_UID, new EbmlElement("TAG_TRACK_UID", INTEGER)); + } + + public void extract(final SequentialReader reader, Metadata metadata) throws IOException { + reader.setMotorolaByteOrder(true); + Map data = new HashMap<>(); + while (reader.available() > 0) { + extractSubContext(reader, data); + } + getMetadata(data, metadata); + } + + + private void getMetadata(Map data, Metadata metadata) throws IOException { + createDirectories(data, metadata); + for (Map.Entry entry: data.entrySet()){ + if (entry.getValue() instanceof List){ + for (Object member: (List) entry.getValue()){ + doCreateDirectory(member, metadata); + } + } else { + doCreateDirectory(entry.getValue(), metadata); + } + } + } + + @SuppressWarnings("unchecked") + private void doCreateDirectory(Object data, Metadata metadata) throws IOException { + if (data instanceof Map){ + createDirectories((Map) data, metadata); + } + } + + @SuppressWarnings("unchecked") + private void createDirectories(Map data, Metadata metadata) { + Directory dir = null; + if (data.containsKey(ElementIDs.TRACK_TYPE)) { + switch (((Long)data.get(ElementIDs.TRACK_TYPE)).intValue()) { + case 1: + dir = new VideoDirectory(); + break; + case 2: + dir = new AudioDirectory(); + break; + } + if (dir != null) { + mapToDirectory(dir, data); + metadata.addDirectory(dir); + } + } + if (data.containsKey(ElementIDs.SEGMENT_INFO)) { + dir = new SegmentInfoDirectory(); + createDirectories((Map) data.get(ElementIDs.SEGMENT_INFO), metadata); + mapToDirectory(dir, (Map) data.get(ElementIDs.SEGMENT_INFO)); + metadata.addDirectory(dir); + } + if (data.containsKey(ElementIDs.EBML_HEADER_ELEMENT)) { + dir = new EbmlDirectory(); + mapToDirectory(dir, (Map) data.get(ElementIDs.EBML_HEADER_ELEMENT)); + metadata.addDirectory(dir); + } + if (data.containsKey(ElementIDs.TRACKS)){ + createDirectories((Map) data.get(ElementIDs.TRACKS), metadata); + } + if (data.containsKey(ElementIDs.TRACK_ENTRY)){ + if (data.get(ElementIDs.TRACK_ENTRY) instanceof List){ + for (Object entry: (List) data.get(ElementIDs.TRACK_ENTRY)){ + if (entry instanceof Map){ + createDirectories((Map) entry, metadata); + } + } + } + } + + } + + private void mapToDirectory(Directory directory, Map data) { + for (Map.Entry values : data.entrySet()) { + put(directory, values.getKey(), values.getValue()); + } + } + + private void put(Directory directory, int id, Object value) { + EbmlElement element = ELEMENTS.get(id); + switch (element.getType()) { + case INTEGER: + case SIGNED_INTEGER: + directory.setLong(id, (long) value); + break; + case FLOAT: + directory.setDouble(id, (double) value); + break; + case STRING: + case UTF8: + directory.setString(id, (String) value); + break; + case BINARY: + directory.setByteArray(id, (byte[]) value); + break; + } + } + + private void extractSubContext(final SequentialReader reader, Map data) throws IOException { + reader.setMotorolaByteOrder(true); + int eid = DataParser.getElementId(reader); + long size = decodeInteger(reader); + Object value = null; + EbmlElement element = ELEMENTS.get(eid); + if (element == null) { + element = new EbmlElement(String.format("0x%02X [ unknown ]", eid), UNKNOWN); + } + switch (element.getType()) { + case STRING: + value = DataParser.getString(reader, size); + break; + case INTEGER: + value = DataParser.getLong(reader, size); + break; + case BINARY: + value = DataParser.getByteArray(reader, size); + break; + case MASTER: + StreamReader sc = new StreamReader(new ByteArrayInputStream(reader.getBytes((int) size))); + Map subData = new HashMap<>(); + while (sc.available() > 0) + extractSubContext(sc, subData); + value = subData; + break; + case UTF8: + value = new String(reader.getBytes((int) size), StandardCharsets.UTF_8); + break; + case VOID: + reader.skip(size); + break; + case FLOAT: + value = size == 4 ? reader.getFloat32() : reader.getDouble64(); + break; + case UNKNOWN: + reader.skip(size); + return; + } + if (ELEMENTS.containsKey(eid) && value != null) { + if (data.containsKey(eid)){ + Object previous = data.get(eid); + List list = new ArrayList<>(); + list.add(previous); + list.add(value); + value = list; + } + data.put(eid, value); + } + } +} diff --git a/Source/com/drew/metadata/mkv/SegmentInfoDirectory.java b/Source/com/drew/metadata/mkv/SegmentInfoDirectory.java new file mode 100644 index 000000000..4a7ef095b --- /dev/null +++ b/Source/com/drew/metadata/mkv/SegmentInfoDirectory.java @@ -0,0 +1,33 @@ +package com.drew.metadata.mkv; + +import com.drew.metadata.Directory; +import com.drew.metadata.TagDescriptor; + +import java.util.HashMap; + +import static com.drew.metadata.mkv.ElementIDs.*; + +public class SegmentInfoDirectory extends Directory { + private static final HashMap _tagNameMap = new HashMap<>(); + + public SegmentInfoDirectory() { + this.setDescriptor(new TagDescriptor<>(this)); + } + + static { + _tagNameMap.put(TIMESTAMP_SCALE, "Timestamp scale"); + _tagNameMap.put(MUXING_APP, "Muxing app"); + _tagNameMap.put(WRITING_APP, "Writing app"); + _tagNameMap.put(DURATION, "Duration"); + } + + @Override + public String getName() { + return "Segment"; + } + + @Override + protected HashMap getTagNameMap() { + return _tagNameMap; + } +} diff --git a/Source/com/drew/metadata/mkv/VideoDirectory.java b/Source/com/drew/metadata/mkv/VideoDirectory.java new file mode 100644 index 000000000..1aba19fa9 --- /dev/null +++ b/Source/com/drew/metadata/mkv/VideoDirectory.java @@ -0,0 +1,50 @@ +package com.drew.metadata.mkv; + +import com.drew.metadata.Directory; + +import java.util.HashMap; + +import com.drew.metadata.TagDescriptor; + +import static com.drew.metadata.mkv.ElementIDs.*; + +public class VideoDirectory extends Directory { + private static final HashMap _tagNameMap = new HashMap<>(); + + public VideoDirectory() { + this.setDescriptor(new TagDescriptor<>(this)); + } + + static { + + _tagNameMap.put(TRACK_NUMBER, "Track number"); + _tagNameMap.put(TRACK_UID, "Track UID"); + _tagNameMap.put(TRACK_TYPE, "Track type"); + _tagNameMap.put(TAG_LACING, "Tag lacing"); + _tagNameMap.put(CODEC_ID, "Codec ID"); + _tagNameMap.put(LANGUAGE, "Language"); + _tagNameMap.put(LANGUAGE_BCP47, "Language BCP47"); + _tagNameMap.put(DEFAULT_DURATION, "Default duration"); + _tagNameMap.put(PIXEL_HEIGHT, "Pixel height"); + _tagNameMap.put(PIXEL_WIDTH, "Pixel width"); + _tagNameMap.put(FLAG_INTERLACED, "Interalced"); + _tagNameMap.put(CHROMA_SITING_HORZ, "Chroma siting horizontal"); + _tagNameMap.put(CHROMA_SITING_VERT, "Chroma siting vertical"); + _tagNameMap.put(TRANSFER_CHARACTERISTICS, "Color transfer characteristics"); + _tagNameMap.put(MATRIX_COEFFICIENTS, "Color matrix coefficients"); + _tagNameMap.put(PRIMARIES, "Color primaries"); + _tagNameMap.put(RANGE, "Color range"); + _tagNameMap.put(DISPLAY_WIDTH, "Display width"); + _tagNameMap.put(DISPLAY_HEIGHT, "Display height"); + } + + @Override + public String getName() { + return "Video"; + } + + @Override + protected HashMap getTagNameMap() { + return _tagNameMap; + } +} diff --git a/Source/com/drew/metadata/mkv/package-info.java b/Source/com/drew/metadata/mkv/package-info.java new file mode 100644 index 000000000..dfb50d61d --- /dev/null +++ b/Source/com/drew/metadata/mkv/package-info.java @@ -0,0 +1 @@ +package com.drew.metadata.mkv; From 53855ba67b2e711fa083cd17ff4393e55767d88f Mon Sep 17 00:00:00 2001 From: njovic Date: Sat, 31 Aug 2024 19:11:59 +0200 Subject: [PATCH 2/4] Code style --- .../com/drew/metadata/mkv/AudioDirectory.java | 20 ++-- Source/com/drew/metadata/mkv/DataParser.java | 50 ++++++--- .../com/drew/metadata/mkv/EbmlDirectory.java | 17 ++- Source/com/drew/metadata/mkv/EbmlElement.java | 51 +++++---- Source/com/drew/metadata/mkv/ElementIDs.java | 3 +- Source/com/drew/metadata/mkv/MkvReader.java | 103 +++++++++++------- .../metadata/mkv/SegmentInfoDirectory.java | 21 ++-- .../com/drew/metadata/mkv/VideoDirectory.java | 25 +++-- 8 files changed, 182 insertions(+), 108 deletions(-) diff --git a/Source/com/drew/metadata/mkv/AudioDirectory.java b/Source/com/drew/metadata/mkv/AudioDirectory.java index c9063b8a4..8bb220034 100644 --- a/Source/com/drew/metadata/mkv/AudioDirectory.java +++ b/Source/com/drew/metadata/mkv/AudioDirectory.java @@ -7,14 +7,12 @@ import static com.drew.metadata.mkv.ElementIDs.*; -public class AudioDirectory extends Directory { +public class AudioDirectory extends Directory +{ private static final HashMap _tagNameMap = new HashMap<>(); - public AudioDirectory(){ - this.setDescriptor(new TagDescriptor(this)); - } - static { - + static + { _tagNameMap.put(TRACK_NUMBER, "Track number"); _tagNameMap.put(TRACK_UID, "Track UID"); _tagNameMap.put(TRACK_TYPE, "Track type"); @@ -26,16 +24,22 @@ public AudioDirectory(){ _tagNameMap.put(CHANNELS, "Channels"); _tagNameMap.put(SAMPLING_FREQUENCY, "Sampling frequency"); _tagNameMap.put(BIT_DEPTH, "Bit depth"); + } + public AudioDirectory() + { + this.setDescriptor(new TagDescriptor(this)); } @Override - public String getName() { + public String getName() + { return "Audio"; } @Override - protected HashMap getTagNameMap() { + protected HashMap getTagNameMap() + { return _tagNameMap; } } diff --git a/Source/com/drew/metadata/mkv/DataParser.java b/Source/com/drew/metadata/mkv/DataParser.java index 8c1497df0..dba9ad45f 100644 --- a/Source/com/drew/metadata/mkv/DataParser.java +++ b/Source/com/drew/metadata/mkv/DataParser.java @@ -4,65 +4,79 @@ import java.io.IOException; -public class DataParser { - private static final long[] VSINT_SUBTR = { 0x3F, 0x1FFF, 0x0FFFFF, 0x07FFFFFF, - 0x03FFFFFFFFL, 0x01FFFFFFFFFFL, - 0x00FFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL }; +public class DataParser +{ + private static final long[] VSINT_SUBTR = {0x3F, 0x1FFF, 0x0FFFFF, 0x07FFFFFF, 0x03FFFFFFFFL, 0x01FFFFFFFFFFL, 0x00FFFFFFFFFFFFL, 0x007FFFFFFFFFFFFFL}; - static long doDecodeInteger(final SequentialReader reader, boolean signed) throws IOException { + static long doDecodeInteger(final SequentialReader reader, boolean signed) throws IOException + { byte firstByte = reader.getBytes(1)[0]; int position = 7; - for (; position >= 0; position--) { - if ((firstByte & (1 << position)) != 0) { + for (; position >= 0; position--) + { + if ((firstByte & (1 << position)) != 0) + { break; } } int length = 7 - position; byte[] values = reader.getBytes(length); long result = (firstByte & ((1L << position) - 1)) << (length * 8); - for (int i = 1; i <= length; i++) { + for (int i = 1; i <= length; i++) + { result |= ((long) (values[i - 1] & 0xFF) << ((length - i) * 8)); } return signed ? result - VSINT_SUBTR[length] : result; } - static long decodeInteger(final SequentialReader reader) throws IOException { + static long decodeInteger(final SequentialReader reader) throws IOException + { return doDecodeInteger(reader, false); } - static long decodeSignedInteger(final SequentialReader reader) throws IOException { + static long decodeSignedInteger(final SequentialReader reader) throws IOException + { return doDecodeInteger(reader, true); } - static int getElementId(final SequentialReader reader) throws IOException { + static int getElementId(final SequentialReader reader) throws IOException + { byte firstByte = reader.getBytes(1)[0]; int position = 7; - for (; position >= 0; position--) { - if ((firstByte & (1 << position)) != 0) { + for (; position >= 0; position--) + { + if ((firstByte & (1 << position)) != 0) + { break; } } int length = 7 - position; byte[] values = reader.getBytes(length); int result = ((int) (firstByte & 0xFF)) << (length * 8); - for (int i = 1; i <= length; i++) { + for (int i = 1; i <= length; i++) + { result |= (((int) values[i - 1] & 0xFF) << ((length - i) * 8)); } return result; } - static long getLong(final SequentialReader reader, long size) throws IOException { + static long getLong(final SequentialReader reader, long size) throws IOException + { long result = 0L; - for (long i = size - 1; i >= 0; i--){ + for (long i = size - 1; i >= 0; i--) + { result |= (long) (reader.getByte() & 0xFF) << (i * 8); } return result; } - static byte[] getByteArray(final SequentialReader reader, long size) throws IOException { + static byte[] getByteArray(final SequentialReader reader, long size) throws IOException + { return reader.getBytes((int) size); } - static String getString(final SequentialReader reader, long size) throws IOException{ + + static String getString(final SequentialReader reader, long size) throws IOException + { return reader.getString((int) size); } } diff --git a/Source/com/drew/metadata/mkv/EbmlDirectory.java b/Source/com/drew/metadata/mkv/EbmlDirectory.java index d9db9f46e..9e2b6428c 100644 --- a/Source/com/drew/metadata/mkv/EbmlDirectory.java +++ b/Source/com/drew/metadata/mkv/EbmlDirectory.java @@ -4,12 +4,16 @@ import com.drew.metadata.TagDescriptor; import java.util.HashMap; + import static com.drew.metadata.mkv.ElementIDs.*; -public class EbmlDirectory extends Directory { +public class EbmlDirectory extends Directory +{ private static final HashMap _tagNameMap = new HashMap<>(); - static { + + static + { _tagNameMap.put(EBML_VERSION, "Version"); _tagNameMap.put(EBML_READ_VERSION, "Read version"); _tagNameMap.put(EBML_MAX_ID_LENGTH, "Maximum ID length"); @@ -19,17 +23,20 @@ public class EbmlDirectory extends Directory { _tagNameMap.put(DOCTYPE_READ_VERSION, "Doctype read version"); } - public EbmlDirectory() { + public EbmlDirectory() + { this.setDescriptor(new TagDescriptor(this)); } @Override - public String getName() { + public String getName() + { return "EBML"; } @Override - protected HashMap getTagNameMap() { + protected HashMap getTagNameMap() + { return _tagNameMap; } } diff --git a/Source/com/drew/metadata/mkv/EbmlElement.java b/Source/com/drew/metadata/mkv/EbmlElement.java index 6c1d3caad..c6faa7c08 100644 --- a/Source/com/drew/metadata/mkv/EbmlElement.java +++ b/Source/com/drew/metadata/mkv/EbmlElement.java @@ -1,39 +1,50 @@ package com.drew.metadata.mkv; -public class EbmlElement { - private final String name; - private final Type type; - private final DirectoryType directory; +public class EbmlElement +{ + private final String _name; + private final Type _type; + private final DirectoryType _directory; - public String toString(){ - return name; + public EbmlElement(String _name, Type type) + { + this(_name, type, DirectoryType.UNKNOWN); } - public String getName() { - return name; + + public EbmlElement(String name, Type type, DirectoryType directory) + { + _name = name; + _type = type; + _directory = directory; } - public Type getType() { - return type; + public String toString() + { + return _name; } - public EbmlElement(String name, Type type) { - this(name, type, DirectoryType.UNKNOWN); + public String get_name() + { + return _name; } - public EbmlElement(String name, Type type, DirectoryType directory) { - this.name = name; - this.type = type; - this.directory = directory; + + public Type get_type() + { + return _type; } - public DirectoryType getDirectory() { - return directory; + public DirectoryType getDirectory() + { + return _directory; } - public enum Type{ + public enum Type + { MASTER, STRING, INTEGER, SIGNED_INTEGER, UTF8, BINARY, VOID, UNKNOWN, FLOAT } - public enum DirectoryType{ + public enum DirectoryType + { EBML, SEGMENT, VIDEO, AUDIO, UNKNOWN } } diff --git a/Source/com/drew/metadata/mkv/ElementIDs.java b/Source/com/drew/metadata/mkv/ElementIDs.java index 4dfeb06bd..ffdc983f8 100644 --- a/Source/com/drew/metadata/mkv/ElementIDs.java +++ b/Source/com/drew/metadata/mkv/ElementIDs.java @@ -1,6 +1,7 @@ package com.drew.metadata.mkv; -public class ElementIDs { +public class ElementIDs +{ static final int EBML_HEADER_ELEMENT = 0x1A45DFA3; static final int EBML_VERSION = 0x4286; diff --git a/Source/com/drew/metadata/mkv/MkvReader.java b/Source/com/drew/metadata/mkv/MkvReader.java index e8e0075da..325d9943d 100644 --- a/Source/com/drew/metadata/mkv/MkvReader.java +++ b/Source/com/drew/metadata/mkv/MkvReader.java @@ -6,7 +6,6 @@ import com.drew.metadata.Metadata; import java.io.ByteArrayInputStream; -import java.io.FileInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -14,15 +13,17 @@ import java.util.List; import java.util.Map; +import static com.drew.metadata.mkv.DataParser.decodeInteger; import static com.drew.metadata.mkv.EbmlElement.DirectoryType.EBML; import static com.drew.metadata.mkv.EbmlElement.Type.*; -import static com.drew.metadata.mkv.DataParser.decodeInteger; -public class MkvReader { +public class MkvReader +{ private static final Map ELEMENTS = new HashMap<>(); - static { + static + { // VOID is legit element type in Matroska spec, we're abusing it here to skip parts we don't need ELEMENTS.put(ElementIDs.EBML_HEADER_ELEMENT, new EbmlElement("EBML_HEADER", MASTER)); ELEMENTS.put(ElementIDs.EBML_VERSION, new EbmlElement("EBML_VERSION", INTEGER, EBML)); @@ -88,41 +89,53 @@ public class MkvReader { ELEMENTS.put(ElementIDs.TAG_TRACK_UID, new EbmlElement("TAG_TRACK_UID", INTEGER)); } - public void extract(final SequentialReader reader, Metadata metadata) throws IOException { + public void extract(final SequentialReader reader, Metadata metadata) throws IOException + { reader.setMotorolaByteOrder(true); Map data = new HashMap<>(); - while (reader.available() > 0) { - extractSubContext(reader, data); + while (reader.available() > 0) + { + extractSubContext(reader, data); } getMetadata(data, metadata); } - private void getMetadata(Map data, Metadata metadata) throws IOException { + private void getMetadata(Map data, Metadata metadata) throws IOException + { createDirectories(data, metadata); - for (Map.Entry entry: data.entrySet()){ - if (entry.getValue() instanceof List){ - for (Object member: (List) entry.getValue()){ - doCreateDirectory(member, metadata); + for (Map.Entry entry : data.entrySet()) + { + if (entry.getValue() instanceof List) + { + for (Object member : (List) entry.getValue()) + { + doCreateDirectory(member, metadata); } - } else { + } else + { doCreateDirectory(entry.getValue(), metadata); } } } @SuppressWarnings("unchecked") - private void doCreateDirectory(Object data, Metadata metadata) throws IOException { - if (data instanceof Map){ + private void doCreateDirectory(Object data, Metadata metadata) throws IOException + { + if (data instanceof Map) + { createDirectories((Map) data, metadata); } } @SuppressWarnings("unchecked") - private void createDirectories(Map data, Metadata metadata) { + private void createDirectories(Map data, Metadata metadata) + { Directory dir = null; - if (data.containsKey(ElementIDs.TRACK_TYPE)) { - switch (((Long)data.get(ElementIDs.TRACK_TYPE)).intValue()) { + if (data.containsKey(ElementIDs.TRACK_TYPE)) + { + switch (((Long) data.get(ElementIDs.TRACK_TYPE)).intValue()) + { case 1: dir = new VideoDirectory(); break; @@ -130,29 +143,37 @@ private void createDirectories(Map data, Metadata metadata) { dir = new AudioDirectory(); break; } - if (dir != null) { + if (dir != null) + { mapToDirectory(dir, data); metadata.addDirectory(dir); } } - if (data.containsKey(ElementIDs.SEGMENT_INFO)) { + if (data.containsKey(ElementIDs.SEGMENT_INFO)) + { dir = new SegmentInfoDirectory(); createDirectories((Map) data.get(ElementIDs.SEGMENT_INFO), metadata); mapToDirectory(dir, (Map) data.get(ElementIDs.SEGMENT_INFO)); metadata.addDirectory(dir); } - if (data.containsKey(ElementIDs.EBML_HEADER_ELEMENT)) { + if (data.containsKey(ElementIDs.EBML_HEADER_ELEMENT)) + { dir = new EbmlDirectory(); mapToDirectory(dir, (Map) data.get(ElementIDs.EBML_HEADER_ELEMENT)); metadata.addDirectory(dir); } - if (data.containsKey(ElementIDs.TRACKS)){ + if (data.containsKey(ElementIDs.TRACKS)) + { createDirectories((Map) data.get(ElementIDs.TRACKS), metadata); } - if (data.containsKey(ElementIDs.TRACK_ENTRY)){ - if (data.get(ElementIDs.TRACK_ENTRY) instanceof List){ - for (Object entry: (List) data.get(ElementIDs.TRACK_ENTRY)){ - if (entry instanceof Map){ + if (data.containsKey(ElementIDs.TRACK_ENTRY)) + { + if (data.get(ElementIDs.TRACK_ENTRY) instanceof List) + { + for (Object entry : (List) data.get(ElementIDs.TRACK_ENTRY)) + { + if (entry instanceof Map) + { createDirectories((Map) entry, metadata); } } @@ -161,15 +182,19 @@ private void createDirectories(Map data, Metadata metadata) { } - private void mapToDirectory(Directory directory, Map data) { - for (Map.Entry values : data.entrySet()) { + private void mapToDirectory(Directory directory, Map data) + { + for (Map.Entry values : data.entrySet()) + { put(directory, values.getKey(), values.getValue()); } } - private void put(Directory directory, int id, Object value) { + private void put(Directory directory, int id, Object value) + { EbmlElement element = ELEMENTS.get(id); - switch (element.getType()) { + switch (element.get_type()) + { case INTEGER: case SIGNED_INTEGER: directory.setLong(id, (long) value); @@ -187,16 +212,19 @@ private void put(Directory directory, int id, Object value) { } } - private void extractSubContext(final SequentialReader reader, Map data) throws IOException { + private void extractSubContext(final SequentialReader reader, Map data) throws IOException + { reader.setMotorolaByteOrder(true); int eid = DataParser.getElementId(reader); long size = decodeInteger(reader); Object value = null; EbmlElement element = ELEMENTS.get(eid); - if (element == null) { + if (element == null) + { element = new EbmlElement(String.format("0x%02X [ unknown ]", eid), UNKNOWN); } - switch (element.getType()) { + switch (element.get_type()) + { case STRING: value = DataParser.getString(reader, size); break; @@ -209,8 +237,7 @@ private void extractSubContext(final SequentialReader reader, Map subData = new HashMap<>(); - while (sc.available() > 0) - extractSubContext(sc, subData); + while (sc.available() > 0) extractSubContext(sc, subData); value = subData; break; case UTF8: @@ -226,8 +253,10 @@ private void extractSubContext(final SequentialReader reader, Map list = new ArrayList<>(); list.add(previous); diff --git a/Source/com/drew/metadata/mkv/SegmentInfoDirectory.java b/Source/com/drew/metadata/mkv/SegmentInfoDirectory.java index 4a7ef095b..7834c7a1c 100644 --- a/Source/com/drew/metadata/mkv/SegmentInfoDirectory.java +++ b/Source/com/drew/metadata/mkv/SegmentInfoDirectory.java @@ -7,27 +7,32 @@ import static com.drew.metadata.mkv.ElementIDs.*; -public class SegmentInfoDirectory extends Directory { +public class SegmentInfoDirectory extends Directory +{ private static final HashMap _tagNameMap = new HashMap<>(); - public SegmentInfoDirectory() { - this.setDescriptor(new TagDescriptor<>(this)); - } - - static { + static + { _tagNameMap.put(TIMESTAMP_SCALE, "Timestamp scale"); _tagNameMap.put(MUXING_APP, "Muxing app"); _tagNameMap.put(WRITING_APP, "Writing app"); _tagNameMap.put(DURATION, "Duration"); } + public SegmentInfoDirectory() + { + this.setDescriptor(new TagDescriptor<>(this)); + } + @Override - public String getName() { + public String getName() + { return "Segment"; } @Override - protected HashMap getTagNameMap() { + protected HashMap getTagNameMap() + { return _tagNameMap; } } diff --git a/Source/com/drew/metadata/mkv/VideoDirectory.java b/Source/com/drew/metadata/mkv/VideoDirectory.java index 1aba19fa9..8bb8c3ea4 100644 --- a/Source/com/drew/metadata/mkv/VideoDirectory.java +++ b/Source/com/drew/metadata/mkv/VideoDirectory.java @@ -1,22 +1,18 @@ package com.drew.metadata.mkv; import com.drew.metadata.Directory; +import com.drew.metadata.TagDescriptor; import java.util.HashMap; -import com.drew.metadata.TagDescriptor; - import static com.drew.metadata.mkv.ElementIDs.*; -public class VideoDirectory extends Directory { +public class VideoDirectory extends Directory +{ private static final HashMap _tagNameMap = new HashMap<>(); - public VideoDirectory() { - this.setDescriptor(new TagDescriptor<>(this)); - } - - static { - + static + { _tagNameMap.put(TRACK_NUMBER, "Track number"); _tagNameMap.put(TRACK_UID, "Track UID"); _tagNameMap.put(TRACK_TYPE, "Track type"); @@ -38,13 +34,20 @@ public VideoDirectory() { _tagNameMap.put(DISPLAY_HEIGHT, "Display height"); } + public VideoDirectory() + { + this.setDescriptor(new TagDescriptor<>(this)); + } + @Override - public String getName() { + public String getName() + { return "Video"; } @Override - protected HashMap getTagNameMap() { + protected HashMap getTagNameMap() + { return _tagNameMap; } } From 615493e3a2d997f1e5325f0a8ccebbab661f88a5 Mon Sep 17 00:00:00 2001 From: njovic Date: Sat, 31 Aug 2024 19:13:43 +0200 Subject: [PATCH 3/4] Code style --- Source/com/drew/imaging/mkv/MkvMetadataReader.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/com/drew/imaging/mkv/MkvMetadataReader.java b/Source/com/drew/imaging/mkv/MkvMetadataReader.java index efe118ab5..5d1b4623b 100644 --- a/Source/com/drew/imaging/mkv/MkvMetadataReader.java +++ b/Source/com/drew/imaging/mkv/MkvMetadataReader.java @@ -1,18 +1,18 @@ package com.drew.imaging.mkv; -import com.drew.imaging.heif.HeifReader; import com.drew.lang.StreamReader; import com.drew.lang.annotations.NotNull; import com.drew.metadata.Metadata; -import com.drew.metadata.heif.HeifBoxHandler; import com.drew.metadata.mkv.MkvReader; import java.io.IOException; import java.io.InputStream; -public class MkvMetadataReader { +public class MkvMetadataReader +{ @NotNull - public static Metadata readMetadata(@NotNull InputStream inputStream) throws IOException { + public static Metadata readMetadata(@NotNull InputStream inputStream) throws IOException + { Metadata metadata = new Metadata(); new MkvReader().extract(new StreamReader(inputStream), metadata); return metadata; From ea42c4f51e407e004d3d7cbeb26be4ae612a0c95 Mon Sep 17 00:00:00 2001 From: njovic Date: Sat, 31 Aug 2024 19:25:28 +0200 Subject: [PATCH 4/4] Audio and Video subdirectories --- Source/com/drew/metadata/mkv/MkvReader.java | 7 +++++++ Source/com/drew/metadata/mkv/VideoDirectory.java | 1 + 2 files changed, 8 insertions(+) diff --git a/Source/com/drew/metadata/mkv/MkvReader.java b/Source/com/drew/metadata/mkv/MkvReader.java index 325d9943d..69aaca2b5 100644 --- a/Source/com/drew/metadata/mkv/MkvReader.java +++ b/Source/com/drew/metadata/mkv/MkvReader.java @@ -138,9 +138,11 @@ private void createDirectories(Map data, Metadata metadata) { case 1: dir = new VideoDirectory(); + mapToDirectory(dir, (Map) data.get(ElementIDs.VIDEO)); break; case 2: dir = new AudioDirectory(); + mapToDirectory(dir, (Map) data.get(ElementIDs.AUDIO)); break; } if (dir != null) @@ -219,6 +221,7 @@ private void extractSubContext(final SequentialReader reader, Map