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 #451

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 7 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,8 @@
import com.drew.lang.annotations.Nullable;
import com.drew.metadata.Metadata;
import com.drew.metadata.mp4.boxes.*;
import com.drew.metadata.mp4.media.Mp4UserBoxDirectory;
import com.drew.metadata.mp4.media.Mp4UserBoxHandler;

import java.io.IOException;

Expand Down Expand Up @@ -56,7 +58,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 +87,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)) {
Mp4UserBoxHandler userBoxHandler = new Mp4UserBoxHandler(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);
}
}
3 changes: 0 additions & 3 deletions Source/com/drew/metadata/mp4/boxes/Box.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ public Box(SequentialReader reader) throws IOException
} 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
39 changes: 39 additions & 0 deletions Source/com/drew/metadata/mp4/boxes/UserBox.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.drew.metadata.mp4.boxes;

import com.drew.lang.SequentialReader;
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.Mp4UserBoxDirectory.*;

public class UserBox extends Box {

private byte[] userData;

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

if (type.equals("uuid")) {
usertype = getUuid(reader.getBytes(16));
}

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

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

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

return uuid.toString();
}
}
10 changes: 10 additions & 0 deletions Source/com/drew/metadata/mp4/media/Mp4UserBoxDescriptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.drew.metadata.mp4.media;

import com.drew.metadata.TagDescriptor;

public class Mp4UserBoxDescriptor extends TagDescriptor<Mp4UserBoxDirectory> {

public Mp4UserBoxDescriptor(Mp4UserBoxDirectory directory) {
super(directory);
}
}
37 changes: 37 additions & 0 deletions Source/com/drew/metadata/mp4/media/Mp4UserBoxDirectory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.drew.metadata.mp4.media;

import com.drew.lang.annotations.NotNull;

import java.util.HashMap;

public class Mp4UserBoxDirectory extends Mp4MediaDirectory {
public static final Integer TAG_UUID = 901;
public static final Integer TAG_LENGTH = 902;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tag is redundant, given it can be derived from the data itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Added XmpAtom for Mov format
  • Fixed issue with offset in mp4 if size mdat box is 64-bit (it's typically only the Media Data Box)
  • Added mp4 and mov files with "UUID" box and "XMP_" atom and tests

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. Sorry I wasn't more clear about the test data. We can't add test media data to this repo without it becoming huge. There's a separate repo for this:

https://github.com/drewnoakes/metadata-extractor-images

I uploaded the test data you provided (thanks very much for preparing these):

drewnoakes/metadata-extractor-images@0f1e3cd

I'm happy to edit your PR's commits to remove those files, unless you'd like to do so.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, sure, you can move these files, thanks

public static final Integer TAG_USER_DATA = 903;

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

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

public Mp4UserBoxDirectory() {
this.setDescriptor(new Mp4UserBoxDescriptor(this));
}

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

@NotNull
@Override
protected HashMap<Integer, String> getTagNameMap() {
return _tagNameMap;
}
}
44 changes: 44 additions & 0 deletions Source/com/drew/metadata/mp4/media/Mp4UserBoxHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.drew.metadata.mp4.media;

import com.drew.imaging.mp4.Mp4Handler;
import com.drew.lang.SequentialByteArrayReader;
import com.drew.lang.SequentialReader;
import com.drew.metadata.Metadata;
import com.drew.metadata.mp4.Mp4Context;
import com.drew.metadata.mp4.boxes.Box;
import com.drew.metadata.mp4.boxes.UserBox;

import java.io.IOException;

public class Mp4UserBoxHandler<T extends Mp4UserBoxDirectory> extends Mp4Handler<Mp4UserBoxDirectory> {

public Mp4UserBoxHandler(Metadata metadata) {
super(metadata);
}

@Override
protected Mp4UserBoxDirectory getDirectory() {
return new Mp4UserBoxDirectory();
}

@Override
protected boolean shouldAcceptBox(Box box) {
return box.type.equals("uuid");
}

@Override
protected boolean shouldAcceptContainer(Box box) {
return false;
}

@Override
public Mp4Handler processBox(Box box, byte[] payload, Mp4Context context) throws IOException {
if (payload != null) {
SequentialReader reader = new SequentialByteArrayReader(payload);
UserBox userBox = new UserBox(reader, box);
userBox.addMetadata(directory);
}

return this;
}
}