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

feat:add crude CR3 support #624

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
4 changes: 3 additions & 1 deletion Source/com/drew/imaging/ImageMetadataReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ public static Metadata readMetadata(@NotNull final InputStream inputStream, fina
case Orf:
case Rw2:
return TiffMetadataReader.readMetadata(new RandomAccessStreamReader(inputStream, RandomAccessStreamReader.DEFAULT_CHUNK_LENGTH, streamLength));
case Crx:
return QuickTimeMetadataReader.readMetadata(inputStream, fileType);
case Psd:
return PsdMetadataReader.readMetadata(inputStream);
case Png:
Expand All @@ -172,7 +174,7 @@ public static Metadata readMetadata(@NotNull final InputStream inputStream, fina
case Wav:
return WavMetadataReader.readMetadata(inputStream);
case QuickTime:
return QuickTimeMetadataReader.readMetadata(inputStream);
return QuickTimeMetadataReader.readMetadata(inputStream, fileType);
case Mp4:
return Mp4MetadataReader.readMetadata(inputStream);
case Mp3:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
*/
package com.drew.imaging.quicktime;

import com.drew.imaging.FileType;
import com.drew.imaging.FileTypeDetector;
import com.drew.imaging.ImageProcessingException;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Metadata;
Expand All @@ -39,7 +41,8 @@ public static Metadata readMetadata(@NotNull final File file) throws ImageProces
InputStream inputStream = new FileInputStream(file);
Metadata metadata;
try {
metadata = readMetadata(inputStream);
FileType fileType = FileTypeDetector.detectFileType(inputStream);
metadata = readMetadata(inputStream, fileType);
} finally {
inputStream.close();
}
Expand All @@ -48,10 +51,10 @@ public static Metadata readMetadata(@NotNull final File file) throws ImageProces
}

@NotNull
public static Metadata readMetadata(@NotNull InputStream inputStream)
public static Metadata readMetadata(@NotNull InputStream inputStream, FileType fileType)
{
Metadata metadata = new Metadata();
QuickTimeReader.extract(inputStream, new QuickTimeAtomHandler(metadata));
QuickTimeReader.extract(inputStream, new QuickTimeAtomHandler(metadata), fileType);
return metadata;
}
}
88 changes: 81 additions & 7 deletions Source/com/drew/imaging/quicktime/QuickTimeReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,45 @@
*/
package com.drew.imaging.quicktime;

import com.drew.imaging.FileType;
import com.drew.imaging.tiff.TiffProcessingException;
import com.drew.imaging.tiff.TiffReader;
import com.drew.lang.StreamReader;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.exif.ExifSubIFDDirectory;
import com.drew.metadata.exif.GpsDirectory;
import com.drew.metadata.exif.makernotes.CanonMakernoteDirectory;
import com.drew.metadata.mov.QuickTimeContainerTypes;
import com.drew.metadata.mov.QuickTimeContext;
import com.drew.metadata.mov.atoms.Atom;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

/**
* @author Payton Garland
*/
public class QuickTimeReader
{
// 8 bytes length for an atom header
private static final long ATOM_HEADER_LENGTH = 8;

private QuickTimeReader() {}

public static void extract(@NotNull InputStream inputStream, @NotNull QuickTimeHandler<?> handler)
public static void extract(@NotNull InputStream inputStream, @NotNull QuickTimeHandler<?> handler, FileType fileType)
{
StreamReader reader = new StreamReader(inputStream);
reader.setMotorolaByteOrder(true);

QuickTimeContext context = new QuickTimeContext();

processAtoms(reader, -1, handler, context);
processAtoms(reader, -1, handler, context, fileType);
}

private static void processAtoms(StreamReader reader, long atomEnd, QuickTimeHandler<?> handler, QuickTimeContext context)
private static void processAtoms(StreamReader reader, long atomEnd, QuickTimeHandler<?> handler, QuickTimeContext context, FileType fileType)
{
try {
while (atomEnd == -1 || reader.getPosition() < atomEnd) {
Expand All @@ -65,12 +78,22 @@ private static void processAtoms(StreamReader reader, long atomEnd, QuickTimeHan
break;
}

if (handler.shouldAcceptContainer(atom)) {
processAtoms(reader, atom.size + reader.getPosition() - 8, handler.processContainer(atom, context), context);
if (FileType.Crx.equals(fileType) && atom.type.equals(QuickTimeContainerTypes.ATOM_UUID)) {
byte[] cr3 = new byte[]{(byte) 0x85, (byte) 0xc0, (byte) 0xb6, (byte) 0x87, (byte) 0x82, 0x0f, 0x11, (byte) 0xe0, (byte) 0x81, 0x11, (byte) 0xf4, (byte) 0xce, 0x46, 0x2b, 0x6a, 0x48};
try {
byte[] uuid = reader.getBytes(cr3.length);
if (Arrays.equals(cr3, uuid)) {
processUuidAtoms(reader, atom.size + reader.getPosition() - ATOM_HEADER_LENGTH, handler.processContainer(atom, context));
}
} catch (IOException ex) {
handler.addError("IOException at crx uuid header: " + ex.getMessage());
}
} else if (handler.shouldAcceptContainer(atom)) {
processAtoms(reader, atom.size + reader.getPosition() - ATOM_HEADER_LENGTH, handler.processContainer(atom, context), context, fileType);
} else if (handler.shouldAcceptAtom(atom)) {
handler = handler.processAtom(atom, reader.getBytes((int)atom.size - 8), context);
handler = handler.processAtom(atom, reader.getBytes((int) (atom.size - ATOM_HEADER_LENGTH)), context);
} else if (atom.size > 8) {
reader.skip(atom.size - 8);
reader.skip(atom.size - ATOM_HEADER_LENGTH);
} else if (atom.size == -1) {
break;
}
Expand All @@ -79,4 +102,55 @@ private static void processAtoms(StreamReader reader, long atomEnd, QuickTimeHan
handler.addError(e.getMessage());
}
}

private static void processUuidAtoms(StreamReader reader, long atomEnd, QuickTimeHandler<?> handler) {
try {
while (atomEnd == -1 || reader.getPosition() < atomEnd) {

Atom atom = new Atom(reader);
// Unknown atoms will be skipped

if (atom.size > Integer.MAX_VALUE) {
handler.addError("Atom size too large.");
continue;
}

if (atom.size < 8) {
handler.addError("Atom size too small.");
continue;
}
switch (atom.type) {
case "CMT1":
{
QuickTimeTiffHandler tiffHandler = new QuickTimeTiffHandler(ExifIFD0Directory.class, handler.metadata, handler.directory, 0);
new TiffReader().processTiff(reader.asByteArrayReader((int) (atom.size - ATOM_HEADER_LENGTH)), tiffHandler, 0);
break;
}
case "CMT2":
{
QuickTimeTiffHandler tiffHandler = new QuickTimeTiffHandler(ExifSubIFDDirectory.class, handler.metadata, handler.directory, 0);
new TiffReader().processTiff(reader.asByteArrayReader((int) (atom.size - ATOM_HEADER_LENGTH)), tiffHandler, 0);
break;
}
case "CMT3":
{
QuickTimeTiffHandler tiffHandler = new QuickTimeTiffHandler(CanonMakernoteDirectory.class, handler.metadata, handler.directory, 0);
new TiffReader().processTiff(reader.asByteArrayReader((int) (atom.size - ATOM_HEADER_LENGTH)), tiffHandler, 0);
break;
}
case "CMT4":
{
QuickTimeTiffHandler tiffHandler = new QuickTimeTiffHandler(GpsDirectory.class, handler.metadata, handler.directory, 0);
new TiffReader().processTiff(reader.asByteArrayReader((int) atom.size - 8), tiffHandler, 0);
break;
}
default:
reader.skip(atom.size - ATOM_HEADER_LENGTH);

}
}
} catch (IOException | TiffProcessingException e) {
handler.addError(e.getMessage());
}
}
}
32 changes: 32 additions & 0 deletions Source/com/drew/imaging/quicktime/QuickTimeTiffHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.drew.imaging.quicktime;

import com.drew.imaging.tiff.TiffProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.exif.ExifTiffHandler;

import java.lang.reflect.InvocationTargetException;

class QuickTimeTiffHandler extends ExifTiffHandler {

private final Class<Directory> _clazz;

public QuickTimeTiffHandler(Class clazz, Metadata metadata, Directory parentDirectory, int exifStartOffset) {
super(metadata, parentDirectory, exifStartOffset);
this._clazz = clazz;
}
@Override
public void setTiffMarker(int marker) throws TiffProcessingException {
int standardTiffMarker = 0x002A;
if (marker != standardTiffMarker)
{
throw new TiffProcessingException("Unexpected TIFF marker: 0x{marker:X}");
}
try {
pushDirectory(this._clazz.getConstructor().newInstance());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
8 changes: 8 additions & 0 deletions Source/com/drew/lang/StreamReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,12 @@ private long skipInternal(long n) throws IOException
_pos += skippedTotal;
return skippedTotal;
}

public ByteArrayReader asByteArrayReader(int size) {
try {
return new ByteArrayReader(this.getBytes(size));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
3 changes: 3 additions & 0 deletions Source/com/drew/metadata/mov/QuickTimeAtomHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ public QuickTimeHandler<?> processAtom(@NotNull Atom atom, @Nullable byte[] payl
} else if (atom.type.equals(QuickTimeAtomTypes.ATOM_TRACK_HEADER)) {
TrackHeaderAtom trackHeaderAtom = new TrackHeaderAtom(reader, atom);
trackHeaderAtom.addMetadata(directory);
} else if (atom.type.equals(QuickTimeAtomTypes.ATOM_UUID)) {
TrackHeaderAtom trackHeaderAtom = new TrackHeaderAtom(reader, atom);
trackHeaderAtom.addMetadata(directory);
}
} else {
if (atom.type.equals(QuickTimeContainerTypes.ATOM_COMPRESSED_MOVIE)) {
Expand Down
1 change: 1 addition & 0 deletions Source/com/drew/metadata/mov/QuickTimeAtomTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class QuickTimeAtomTypes
public static final String ATOM_CANON_THUMBNAIL = "CNTH";
public static final String ATOM_ADOBE_XMP = "XMP_";
public static final String ATOM_TRACK_HEADER = "tkhd";
public static final String ATOM_UUID = "uuid";

private static final ArrayList<String> _atomList = new ArrayList<String>();

Expand Down
1 change: 1 addition & 0 deletions Source/com/drew/metadata/mov/QuickTimeContainerTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class QuickTimeContainerTypes
{
public static final String ATOM_MOVIE = "moov";
public static final String ATOM_USER_DATA = "udta";
public static final String ATOM_UUID = "uuid";
public static final String ATOM_TRACK = "trak";
public static final String ATOM_MEDIA = "mdia";
public static final String ATOM_MEDIA_INFORMATION = "minf";
Expand Down