Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #582 | Support for WebM/MKV #679

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.lang.StreamReader;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Metadata;
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;
45 changes: 45 additions & 0 deletions Source/com/drew/metadata/mkv/AudioDirectory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
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<>();

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");
}

public AudioDirectory()
{
this.setDescriptor(new TagDescriptor<Directory>(this));
}

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

@Override
protected HashMap<Integer, String> getTagNameMap()
{
return _tagNameMap;
}
}
82 changes: 82 additions & 0 deletions Source/com/drew/metadata/mkv/DataParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
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);
}
}
42 changes: 42 additions & 0 deletions Source/com/drew/metadata/mkv/EbmlDirectory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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;
}
}
50 changes: 50 additions & 0 deletions Source/com/drew/metadata/mkv/EbmlElement.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.drew.metadata.mkv;

public class EbmlElement
{
private final String _name;
private final Type _type;
private final DirectoryType _directory;

public EbmlElement(String _name, Type type)
{
this(_name, type, DirectoryType.UNKNOWN);
}

public EbmlElement(String name, Type type, DirectoryType directory)
{
_name = name;
_type = type;
_directory = directory;
}

public String toString()
{
return _name;
}

public String get_name()
{
return _name;
}

public Type get_type()
{
return _type;
}

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
}
}
68 changes: 68 additions & 0 deletions Source/com/drew/metadata/mkv/ElementIDs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
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
Loading