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

Missing basic information from the MP4 metadata #661

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
135 changes: 120 additions & 15 deletions Source/com/drew/metadata/mp4/Mp4BoxHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,37 @@
*/
package com.drew.metadata.mp4;

import static com.drew.metadata.mp4.Mp4Directory.TAG_CATEGORY;
import static com.drew.metadata.mp4.Mp4Directory.TAG_COMMENT;
import static com.drew.metadata.mp4.Mp4Directory.TAG_LATITUDE;
import static com.drew.metadata.mp4.Mp4Directory.TAG_LONGITUDE;
import static com.drew.metadata.mp4.Mp4Directory.TAG_MOOD;
import static com.drew.metadata.mp4.Mp4Directory.TAG_SUBTITLE;
import static com.drew.metadata.mp4.Mp4Directory.TAG_TITLE;
import static com.drew.metadata.mp4.Mp4Directory.TAG_USER_RATING;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.drew.imaging.mp4.Mp4Handler;
import com.drew.lang.DateUtil;
import com.drew.lang.Rational;
import com.drew.lang.SequentialByteArrayReader;
import com.drew.lang.SequentialReader;
import com.drew.lang.StringUtil;
import com.drew.lang.annotations.NotNull;
import com.drew.lang.annotations.Nullable;
import com.drew.metadata.Metadata;
import com.drew.metadata.mp4.media.*;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.drew.metadata.mp4.Mp4Directory.TAG_LATITUDE;
import static com.drew.metadata.mp4.Mp4Directory.TAG_LONGITUDE;
import com.drew.metadata.mp4.media.Mp4HintHandler;
import com.drew.metadata.mp4.media.Mp4MetaHandler;
import com.drew.metadata.mp4.media.Mp4SoundHandler;
import com.drew.metadata.mp4.media.Mp4TextHandler;
import com.drew.metadata.mp4.media.Mp4UuidBoxHandler;
import com.drew.metadata.mp4.media.Mp4VideoHandler;

/**
* @author Payton Garland
Expand Down Expand Up @@ -140,6 +153,8 @@ public Mp4Handler<?> processBox(@NotNull String type, @Nullable byte[] payload,
private void processUserData(@NotNull SequentialReader reader, int length) throws IOException
{
final int LOCATION_CODE = 0xA978797A; // "©xyz"
final int META_TYPE = 0x6D657461; // "meta"
final int XTRA_TYPE = 0x58747261; // "Xtra"

String coordinateString = null;

Expand All @@ -152,11 +167,16 @@ private void processUserData(@NotNull SequentialReader reader, int length) throw
int xyzLength = reader.getUInt16();
reader.skip(2);
coordinateString = reader.getString(xyzLength, "UTF-8");
} else if (size >= 8) {
reader.skip(size - 8);
} else {
return;
}
} else if (kind == META_TYPE && size > 16) {
reader.skip(4);
processUserDataMeta(reader, length, size - 12);
} else if (kind == XTRA_TYPE && size > 16) {
processUserDataMetaXtra(reader, length, size - 8);
} else if (size >= 8) {
reader.skip(size - 8);
} else {
return;
}
}

if (coordinateString != null) {
Expand All @@ -170,6 +190,91 @@ private void processUserData(@NotNull SequentialReader reader, int length) throw
}
}
}

private void processUserDataMeta(@NotNull SequentialReader reader, int length, long blockSize) throws IOException {
final int HDLR_TYPE = 0x68646C72; // "hdlr"
final int ILST_TYPE = 0x696C7374; // "ilst"

long initialPosition = reader.getPosition();

while (reader.getPosition() < length && (reader.getPosition() - initialPosition) < blockSize) {
long size = reader.getUInt32();
if (size <= 4)
break;
int kind = reader.getInt32();
if (kind == HDLR_TYPE) {
// nothing
reader.skip(size - 8);
} else if (kind == ILST_TYPE && size > 16) {
processUserDataMetaIList(reader, length, size - 8);
}
}
}

private void processUserDataMetaIList(@NotNull SequentialReader reader, int length, long blockSize)
throws IOException {
final int CNAM_TYPE = 0xA96E616D; // "©nam"
final int CCMT_TYPE = 0xA9636D74; // "©cmt"
long initialPosition = reader.getPosition();

while (reader.getPosition() < length && (reader.getPosition() - initialPosition) < blockSize) {
long size = reader.getUInt32();
if (size <= 4)
break;
int kind = reader.getInt32();
if (kind == CNAM_TYPE) {
long cnamSize = reader.getUInt32();
if (cnamSize > 16) {
reader.skip(12);
directory.setString(TAG_TITLE, reader.getString((int) cnamSize - 16, "UTF-8"));
}
} else if (kind == CCMT_TYPE) {
long ccmtSize = reader.getUInt32();
if (ccmtSize > 16) {
reader.skip(12);
directory.setString(TAG_COMMENT, reader.getString((int) ccmtSize - 16, "UTF-8"));
}
} else {
// nothing
}
}
}

private void processUserDataMetaXtra(@NotNull SequentialReader reader, int length, long blockSize)
throws IOException {
long initialPosition = reader.getPosition();
while (reader.getPosition() < length && (reader.getPosition() - initialPosition) < blockSize) {
long key_size = reader.getUInt32();
long key_name_size = reader.getUInt32();
String key_name = reader.getString((int) key_name_size, "UTF-8");
long entry_count = reader.getUInt32();
if (key_name.equals("WM/SubTitle")) {
String value = getProcessUserDataMetaXtraValue(reader, entry_count);
directory.setString(TAG_SUBTITLE, value);
} else if (key_name.equals("WM/SharedUserRating")) {
long value_size = reader.getUInt32();
int value_type = reader.getUInt16();
directory.setLong(TAG_USER_RATING, reader.getInt64());
} else if (key_name.equals("WM/Category")) {
String value = getProcessUserDataMetaXtraValue(reader, entry_count);
directory.setString(TAG_CATEGORY, value);
} else if (key_name.equals("WM/Mood")) {
String value = getProcessUserDataMetaXtraValue(reader, entry_count);
directory.setString(TAG_MOOD, value);
}
}
}

private String getProcessUserDataMetaXtraValue(@NotNull SequentialReader reader, long entry_count)
throws IOException {
List<String> result = new ArrayList<>();
for (long i = 0; i < entry_count; ++i) {
long value_size = reader.getUInt32();
int val_type = reader.getUInt16();
result.add(reader.getString((int) value_size - 6,"UTF-16LE").replace("\0", ""));
}
return StringUtil.join(result, " | ");
}

private void processFileType(@NotNull SequentialReader reader, long boxSize) throws IOException
{
Expand Down
16 changes: 14 additions & 2 deletions Source/com/drew/metadata/mp4/Mp4Directory.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
*/
package com.drew.metadata.mp4;

import java.util.HashMap;

import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Directory;

import java.util.HashMap;

public class Mp4Directory extends Directory {

public static final int TAG_CREATION_TIME = 0x0100;
Expand All @@ -46,6 +46,12 @@ public class Mp4Directory extends Directory {
public static final int TAG_LATITUDE = 0x2001;
public static final int TAG_LONGITUDE = 0x2002;
public static final int TAG_MEDIA_TIME_SCALE = 0x0306;
public static final int TAG_TITLE = 0x3000;
public static final int TAG_COMMENT = 0x3001;
public static final int TAG_SUBTITLE = 0x3002;
public static final int TAG_USER_RATING = 0x3003;
public static final int TAG_CATEGORY = 0x3004;
public static final int TAG_MOOD = 0x3005;

public static final int TAG_MAJOR_BRAND = 1;
public static final int TAG_MINOR_VERSION = 2;
Expand Down Expand Up @@ -78,6 +84,12 @@ public class Mp4Directory extends Directory {
_tagNameMap.put(TAG_ROTATION, "Rotation");
_tagNameMap.put(TAG_LATITUDE, "Latitude");
_tagNameMap.put(TAG_LONGITUDE, "Longitude");
_tagNameMap.put(TAG_TITLE, "Title");
_tagNameMap.put(TAG_COMMENT, "Comment");
_tagNameMap.put(TAG_SUBTITLE, "Subtitle");
_tagNameMap.put(TAG_USER_RATING, "User rating");
_tagNameMap.put(TAG_CATEGORY, "Tags");
_tagNameMap.put(TAG_MOOD, "Mood");

_tagNameMap.put(TAG_MEDIA_TIME_SCALE, "Media Time Scale");
}
Expand Down
Loading