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

Add support for MP4 UUID and MOV XMP boxes #455

Merged
merged 11 commits into from
Jan 15, 2020
6 changes: 5 additions & 1 deletion Source/com/drew/imaging/mp4/Mp4Reader.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ private static void processBoxes(StreamReader reader, long atomEnd, Mp4Handler h
} else if (box.usertype != null) {
reader.skip(box.size - 24);
} else if (box.size > 1) {
reader.skip(box.size - 8);
if (box.isLargeSize) {
reader.skip(box.size - 16);
} else {
reader.skip(box.size - 8);
}
} else if (box.size == -1) {
break;
}
Expand Down
6 changes: 5 additions & 1 deletion Source/com/drew/metadata/mov/QuickTimeAtomHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.drew.metadata.Metadata;
import com.drew.metadata.mov.atoms.*;
import com.drew.metadata.mov.atoms.canon.CanonThumbnailAtom;
import com.drew.metadata.xmp.XmpReader;

import java.io.IOException;

Expand Down Expand Up @@ -57,7 +58,8 @@ public boolean shouldAcceptAtom(@NotNull Atom atom)
|| atom.type.equals(QuickTimeAtomTypes.ATOM_MOVIE_HEADER)
|| atom.type.equals(QuickTimeAtomTypes.ATOM_HANDLER)
|| atom.type.equals(QuickTimeAtomTypes.ATOM_MEDIA_HEADER)
|| atom.type.equals(QuickTimeAtomTypes.ATOM_CANON_THUMBNAIL);
|| atom.type.equals(QuickTimeAtomTypes.ATOM_CANON_THUMBNAIL)
|| atom.type.equals(QuickTimeAtomTypes.ATOM_ADOBE_XMP);
}

@Override
Expand Down Expand Up @@ -90,6 +92,8 @@ public QuickTimeHandler processAtom(@NotNull Atom atom, @Nullable byte[] payload
} else if (atom.type.equals(QuickTimeAtomTypes.ATOM_CANON_THUMBNAIL)) {
CanonThumbnailAtom canonThumbnailAtom = new CanonThumbnailAtom(reader);
canonThumbnailAtom.addMetadata(directory);
} else if (atom.type.equals(QuickTimeAtomTypes.ATOM_ADOBE_XMP)) {
new XmpReader().extract(payload, metadata, directory);
}
} else {
if (atom.type.equals(QuickTimeContainerTypes.ATOM_COMPRESSED_MOVIE)) {
Expand Down
2 changes: 2 additions & 0 deletions Source/com/drew/metadata/mov/QuickTimeAtomTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class QuickTimeAtomTypes
public static final String ATOM_TIME_TO_SAMPLE = "stts";
public static final String ATOM_MEDIA_HEADER = "mdhd";
public static final String ATOM_CANON_THUMBNAIL = "CNTH";
public static final String ATOM_ADOBE_XMP = "XMP_";

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

Expand All @@ -57,5 +58,6 @@ public class QuickTimeAtomTypes
_atomList.add(ATOM_TIME_TO_SAMPLE);
_atomList.add(ATOM_MEDIA_HEADER);
_atomList.add(ATOM_CANON_THUMBNAIL);
_atomList.add(ATOM_ADOBE_XMP);
}
}
4 changes: 4 additions & 0 deletions Source/com/drew/metadata/mov/QuickTimeDirectory.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public class QuickTimeDirectory extends Directory {

public static final int TAG_CANON_THUMBNAIL_DT = 0x2000;

public static final int TAG_ADOBE_XMP = 0x3000;

@NotNull
private static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();

Expand All @@ -80,6 +82,8 @@ public class QuickTimeDirectory extends Directory {
_tagNameMap.put(TAG_MEDIA_TIME_SCALE, "Media Time Scale");

_tagNameMap.put(TAG_CANON_THUMBNAIL_DT, "Canon Thumbnail DateTime");

_tagNameMap.put(TAG_ADOBE_XMP, "Adobe Bridge XMP");
}

public QuickTimeDirectory()
Expand Down
7 changes: 6 additions & 1 deletion Source/com/drew/metadata/mp4/Mp4BoxHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.drew.lang.annotations.Nullable;
import com.drew.metadata.Metadata;
import com.drew.metadata.mp4.boxes.*;
import com.drew.metadata.mp4.media.Mp4UuidBoxHandler;

import java.io.IOException;

Expand Down Expand Up @@ -56,7 +57,8 @@ public boolean shouldAcceptBox(@NotNull Box box)
|| box.type.equals(Mp4BoxTypes.BOX_MOVIE_HEADER)
|| box.type.equals(Mp4BoxTypes.BOX_HANDLER)
|| box.type.equals(Mp4BoxTypes.BOX_MEDIA_HEADER)
|| box.type.equals(Mp4BoxTypes.BOX_TRACK_HEADER);
|| box.type.equals(Mp4BoxTypes.BOX_TRACK_HEADER)
|| box.type.equals(Mp4BoxTypes.BOX_USER_DEFINED);
}

@Override
Expand Down Expand Up @@ -84,6 +86,9 @@ public Mp4Handler processBox(@NotNull Box box, @Nullable byte[] payload, Mp4Cont
processMediaHeader(reader, box, context);
} else if (box.type.equals(Mp4BoxTypes.BOX_TRACK_HEADER)) {
processTrackHeader(reader, box);
} else if (box.type.equals(Mp4BoxTypes.BOX_USER_DEFINED)) {
Mp4UuidBoxHandler userBoxHandler = new Mp4UuidBoxHandler(metadata);
userBoxHandler.processBox(box, payload, context);
}
} else {
if (box.type.equals(Mp4ContainerTypes.BOX_COMPRESSED_MOVIE)) {
Expand Down
2 changes: 2 additions & 0 deletions Source/com/drew/metadata/mp4/Mp4BoxTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class Mp4BoxTypes
public static final String BOX_TIME_TO_SAMPLE = "stts";
public static final String BOX_MEDIA_HEADER = "mdhd";
public static final String BOX_TRACK_HEADER = "tkhd";
public static final String BOX_USER_DEFINED = "uuid";

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

Expand All @@ -53,5 +54,6 @@ public class Mp4BoxTypes
_boxList.add(BOX_TIME_TO_SAMPLE);
_boxList.add(BOX_MEDIA_HEADER);
_boxList.add(BOX_TRACK_HEADER);
_boxList.add(BOX_USER_DEFINED);
}
}
5 changes: 2 additions & 3 deletions Source/com/drew/metadata/mp4/boxes/Box.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,18 @@ public class Box
public long size;
public String type;
public String usertype;
public boolean isLargeSize;

public Box(SequentialReader reader) throws IOException
{
this.size = reader.getUInt32();
this.type = reader.getString(4);
if (size == 1) {
size = reader.getInt64();
isLargeSize = true;
} else if (size == 0) {
size = -1;
}
if (type.equals("uuid")) {
usertype = reader.getString(16);
}
}

public Box(Box box)
Expand Down
1 change: 0 additions & 1 deletion Source/com/drew/metadata/mp4/boxes/MediaHeaderBox.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

import com.drew.lang.SequentialReader;
import com.drew.metadata.mp4.Mp4Context;
import com.drew.metadata.mp4.Mp4HandlerFactory;

import java.io.IOException;

Expand Down
60 changes: 60 additions & 0 deletions Source/com/drew/metadata/mp4/boxes/UuidBox.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2002-2019 Drew Noakes and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* More information about this project is available at:
*
* https://drewnoakes.com/code/exif/
* https://github.com/drewnoakes/metadata-extractor
*/
package com.drew.metadata.mp4.boxes;

import com.drew.lang.SequentialReader;
import com.drew.metadata.mp4.Mp4BoxTypes;
import com.drew.metadata.mp4.Mp4Directory;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.UUID;

import static com.drew.metadata.mp4.media.Mp4UuidBoxDirectory.*;

public class UuidBox extends Box
{
private byte[] userData;

public UuidBox(SequentialReader reader, Box box) throws IOException
{
super(box);

if (type.equals(Mp4BoxTypes.BOX_USER_DEFINED)) {
usertype = getUuid(reader.getBytes(16));
}

userData = reader.getBytes(reader.available());
}

public void addMetadata(Mp4Directory directory)
{
directory.setString(TAG_UUID, usertype);
directory.setByteArray(TAG_USER_DATA, userData);
}

private String getUuid(byte[] bytes) {
ByteBuffer bb = ByteBuffer.wrap(bytes);
UUID uuid = new UUID(bb.getLong(), bb.getLong());

return uuid.toString();
}
}
31 changes: 31 additions & 0 deletions Source/com/drew/metadata/mp4/media/Mp4UuidBoxDescriptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2002-2019 Drew Noakes and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* More information about this project is available at:
*
* https://drewnoakes.com/code/exif/
* https://github.com/drewnoakes/metadata-extractor
*/
package com.drew.metadata.mp4.media;

import com.drew.metadata.TagDescriptor;

public class Mp4UuidBoxDescriptor extends TagDescriptor<Mp4UuidBoxDirectory>
{
public Mp4UuidBoxDescriptor(Mp4UuidBoxDirectory directory)
{
super(directory);
}
}
60 changes: 60 additions & 0 deletions Source/com/drew/metadata/mp4/media/Mp4UuidBoxDirectory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2002-2019 Drew Noakes and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* More information about this project is available at:
*
* https://drewnoakes.com/code/exif/
* https://github.com/drewnoakes/metadata-extractor
*/
package com.drew.metadata.mp4.media;

import com.drew.lang.annotations.NotNull;

import java.util.HashMap;

public class Mp4UuidBoxDirectory extends Mp4MediaDirectory
{
public static final Integer TAG_UUID = 901;
public static final Integer TAG_USER_DATA = 902;

@NotNull
private static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>();

static
{
Mp4UuidBoxDirectory.addMp4MediaTags(_tagNameMap);
_tagNameMap.put(TAG_UUID, "uuid");
_tagNameMap.put(TAG_USER_DATA, "data");
}

public Mp4UuidBoxDirectory()
{
this.setDescriptor(new Mp4UuidBoxDescriptor(this));
}

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

@NotNull
@Override
protected HashMap<Integer, String> getTagNameMap()
{
return _tagNameMap;
}
}
Loading