Skip to content

Commit

Permalink
Issue #582 | Support for WebM/MKV
Browse files Browse the repository at this point in the history
  • Loading branch information
njovic committed Aug 30, 2024
1 parent 9f4ad4c commit 9e40123
Show file tree
Hide file tree
Showing 14 changed files with 600 additions and 0 deletions.
1 change: 1 addition & 0 deletions Source/com/drew/imaging/FileType.java
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
1 change: 1 addition & 0 deletions Source/com/drew/imaging/FileTypeDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions Source/com/drew/imaging/ImageMetadataReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
20 changes: 20 additions & 0 deletions Source/com/drew/imaging/mkv/MkvMetadataReader.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
1 change: 1 addition & 0 deletions Source/com/drew/imaging/mkv/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package com.drew.imaging.mkv;
41 changes: 41 additions & 0 deletions Source/com/drew/metadata/mkv/AudioDirectory.java
Original file line number Diff line number Diff line change
@@ -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<Integer, String> _tagNameMap = new HashMap<>();

public AudioDirectory(){
this.setDescriptor(new TagDescriptor<Directory>(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<Integer, String> getTagNameMap() {
return _tagNameMap;
}
}
68 changes: 68 additions & 0 deletions Source/com/drew/metadata/mkv/DataParser.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
35 changes: 35 additions & 0 deletions Source/com/drew/metadata/mkv/EbmlDirectory.java
Original file line number Diff line number Diff line change
@@ -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<Integer, String> _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<Directory>(this));
}

@Override
public String getName() {
return "EBML";
}

@Override
protected HashMap<Integer, String> getTagNameMap() {
return _tagNameMap;
}
}
39 changes: 39 additions & 0 deletions Source/com/drew/metadata/mkv/EbmlElement.java
Original file line number Diff line number Diff line change
@@ -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
}
}
67 changes: 67 additions & 0 deletions Source/com/drew/metadata/mkv/ElementIDs.java
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit 9e40123

Please sign in to comment.