Skip to content

Commit

Permalink
Merge pull request #455 from drewnoakes/uuid
Browse files Browse the repository at this point in the history
Add support for MP4 UUID and MOV XMP boxes
  • Loading branch information
drewnoakes authored Jan 15, 2020
2 parents bb78cb0 + 96b7bc1 commit 482cb69
Show file tree
Hide file tree
Showing 12 changed files with 306 additions and 7 deletions.
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

0 comments on commit 482cb69

Please sign in to comment.