Commit 5aff31c0 by Oliver Woodman

Further improve ID3 parsing

parent 31602af3
......@@ -35,7 +35,6 @@ import com.google.android.exoplayer2.metadata.id3.GeobFrame;
import com.google.android.exoplayer2.metadata.id3.Id3Frame;
import com.google.android.exoplayer2.metadata.id3.PrivFrame;
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
import com.google.android.exoplayer2.metadata.id3.TxxxFrame;
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.TrackGroup;
......@@ -359,10 +358,10 @@ import java.util.Locale;
private void printMetadata(Metadata metadata, String prefix) {
for (int i = 0; i < metadata.length(); i++) {
Metadata.Entry entry = metadata.get(i);
if (entry instanceof TxxxFrame) {
TxxxFrame txxxFrame = (TxxxFrame) entry;
Log.d(TAG, prefix + String.format("%s: description=%s, value=%s", txxxFrame.id,
txxxFrame.description, txxxFrame.value));
if (entry instanceof TextInformationFrame) {
TextInformationFrame textInformationFrame = (TextInformationFrame) entry;
Log.d(TAG, prefix + String.format("%s: value=%s", textInformationFrame.id,
textInformationFrame.value));
} else if (entry instanceof PrivFrame) {
PrivFrame privFrame = (PrivFrame) entry;
Log.d(TAG, prefix + String.format("%s: owner=%s", privFrame.id, privFrame.owner));
......@@ -374,10 +373,6 @@ import java.util.Locale;
ApicFrame apicFrame = (ApicFrame) entry;
Log.d(TAG, prefix + String.format("%s: mimeType=%s, description=%s",
apicFrame.id, apicFrame.mimeType, apicFrame.description));
} else if (entry instanceof TextInformationFrame) {
TextInformationFrame textInformationFrame = (TextInformationFrame) entry;
Log.d(TAG, prefix + String.format("%s: description=%s", textInformationFrame.id,
textInformationFrame.description));
} else if (entry instanceof CommentFrame) {
CommentFrame commentFrame = (CommentFrame) entry;
Log.d(TAG, prefix + String.format("%s: language=%s description=%s", commentFrame.id,
......
......@@ -59,8 +59,8 @@ public final class FormatTest extends TestCase {
DrmInitData drmInitData = new DrmInitData(DRM_DATA_1, DRM_DATA_2);
byte[] projectionData = new byte[] {1, 2, 3};
Metadata metadata = new Metadata(
new TextInformationFrame("id1", "description1"),
new TextInformationFrame("id2", "description2"));
new TextInformationFrame("id1", "description1", "value1"),
new TextInformationFrame("id2", "description2", "value2"));
Format formatToParcel = new Format("id", MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null,
1024, 2048, 1920, 1080, 24, 90, 2, projectionData, C.STEREO_MODE_TOP_BOTTOM, 6, 44100,
......
/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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.
*/
package com.google.android.exoplayer2.metadata.id3;
import android.os.Parcel;
import junit.framework.TestCase;
/**
* Test for {@link ChapterFrame}.
*/
public final class ChapterFrameTest extends TestCase {
public void testParcelable() {
Id3Frame[] subFrames = new Id3Frame[] {
new TextInformationFrame("TIT2", null, "title"),
new UrlLinkFrame("WXXX", "description", "url")
};
ChapterFrame chapterFrameToParcel = new ChapterFrame("id", 0, 1, 2, 3, subFrames);
Parcel parcel = Parcel.obtain();
chapterFrameToParcel.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
ChapterFrame chapterFrameFromParcel = ChapterFrame.CREATOR.createFromParcel(parcel);
assertEquals(chapterFrameToParcel, chapterFrameFromParcel);
parcel.recycle();
}
}
/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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.
*/
package com.google.android.exoplayer2.metadata.id3;
import android.os.Parcel;
import junit.framework.TestCase;
/**
* Test for {@link ChapterTOCFrame}.
*/
public final class ChapterTOCFrameTest extends TestCase {
public void testParcelable() {
String[] children = new String[] {"child0", "child1"};
Id3Frame[] subFrames = new Id3Frame[] {
new TextInformationFrame("TIT2", null, "title"),
new UrlLinkFrame("WXXX", "description", "url")
};
ChapterTOCFrame chapterTOCFrameToParcel = new ChapterTOCFrame("id", false, true, children,
subFrames);
Parcel parcel = Parcel.obtain();
chapterTOCFrameToParcel.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
ChapterTOCFrame chapterTOCFrameFromParcel = ChapterTOCFrame.CREATOR.createFromParcel(parcel);
assertEquals(chapterTOCFrameToParcel, chapterTOCFrameFromParcel);
parcel.recycle();
}
}
......@@ -32,9 +32,10 @@ public final class Id3DecoderTest extends TestCase {
Id3Decoder decoder = new Id3Decoder();
Metadata metadata = decoder.decode(rawId3, rawId3.length);
assertEquals(1, metadata.length());
TxxxFrame txxxFrame = (TxxxFrame) metadata.get(0);
assertEquals("", txxxFrame.description);
assertEquals("mdialog_VINDICO1527664_start", txxxFrame.value);
TextInformationFrame textInformationFrame = (TextInformationFrame) metadata.get(0);
assertEquals("TXXX", textInformationFrame.id);
assertEquals("", textInformationFrame.description);
assertEquals("mdialog_VINDICO1527664_start", textInformationFrame.value);
}
public void testDecodeApicFrame() throws MetadataDecoderException {
......@@ -60,7 +61,8 @@ public final class Id3DecoderTest extends TestCase {
assertEquals(1, metadata.length());
TextInformationFrame textInformationFrame = (TextInformationFrame) metadata.get(0);
assertEquals("TIT2", textInformationFrame.id);
assertEquals("Hello World", textInformationFrame.description);
assertNull(textInformationFrame.description);
assertEquals("Hello World", textInformationFrame.value);
}
public void testDecodePrivFrame() throws MetadataDecoderException {
......
......@@ -188,7 +188,7 @@ import com.google.android.exoplayer2.util.Util;
if (atomType == Atom.TYPE_data) {
data.skipBytes(8); // version (1), flags (3), empty (4)
String value = data.readNullTerminatedString(atomSize - 16);
return new TextInformationFrame(id, value);
return new TextInformationFrame(id, null, value);
}
Log.w(TAG, "Failed to parse text attribute: " + Atom.getAtomTypeString(type));
return null;
......@@ -213,7 +213,7 @@ import com.google.android.exoplayer2.util.Util;
value = Math.min(1, value);
}
if (value >= 0) {
return isTextInformationFrame ? new TextInformationFrame(id, Integer.toString(value))
return isTextInformationFrame ? new TextInformationFrame(id, null, Integer.toString(value))
: new CommentFrame(LANGUAGE_UNDEFINED, id, Integer.toString(value));
}
Log.w(TAG, "Failed to parse uint8 attribute: " + Atom.getAtomTypeString(type));
......@@ -228,12 +228,12 @@ import com.google.android.exoplayer2.util.Util;
data.skipBytes(10); // version (1), flags (3), empty (4), empty (2)
int index = data.readUnsignedShort();
if (index > 0) {
String description = "" + index;
String value = "" + index;
int count = data.readUnsignedShort();
if (count > 0) {
description += "/" + count;
value += "/" + count;
}
return new TextInformationFrame(attributeName, description);
return new TextInformationFrame(attributeName, null, value);
}
}
Log.w(TAG, "Failed to parse index/count attribute: " + Atom.getAtomTypeString(type));
......@@ -245,7 +245,7 @@ import com.google.android.exoplayer2.util.Util;
String genreString = (0 < genreCode && genreCode <= STANDARD_GENRES.length)
? STANDARD_GENRES[genreCode - 1] : null;
if (genreString != null) {
return new TextInformationFrame("TCON", genreString);
return new TextInformationFrame("TCON", null, genreString);
}
Log.w(TAG, "Failed to parse standard genre code");
return null;
......
......@@ -16,13 +16,13 @@
package com.google.android.exoplayer2.metadata.id3;
import android.os.Parcel;
import com.google.android.exoplayer2.util.Util;
import java.util.Arrays;
/**
* Chapter information "CHAP" ID3 frame.
* Chapter information ID3 frame.
*/
public final class ChapFrame extends Id3Frame {
public final class ChapterFrame extends Id3Frame {
public static final String ID = "CHAP";
......@@ -31,33 +31,31 @@ public final class ChapFrame extends Id3Frame {
public final int endTime;
public final int startOffset;
public final int endOffset;
public final String title;
public final String url;
public final ApicFrame image;
private final Id3Frame[] subFrames;
public ChapFrame(String chapterId, int startTime, int endTime, int startOffset, int endOffset,
String title, String url, ApicFrame image) {
public ChapterFrame(String chapterId, int startTime, int endTime, int startOffset, int endOffset,
Id3Frame[] subFrames) {
super(ID);
this.chapterId = chapterId;
this.startTime = startTime;
this.endTime = endTime;
this.startOffset = startOffset;
this.endOffset = endOffset;
this.title = title;
this.url = url;
this.image = image;
this.subFrames = subFrames;
}
/* package */ ChapFrame(Parcel in) {
/* package */ ChapterFrame(Parcel in) {
super(ID);
this.chapterId = in.readString();
this.startTime = in.readInt();
this.endTime = in.readInt();
this.startOffset = in.readInt();
this.endOffset = in.readInt();
this.title = in.readString();
this.url = in.readString();
this.image = in.readParcelable(ApicFrame.class.getClassLoader());
int subFrameCount = in.readInt();
subFrames = new Id3Frame[subFrameCount];
for (int i = 0; i < subFrameCount; i++) {
subFrames[i] = in.readParcelable(Id3Frame.class.getClassLoader());
}
}
@Override
......@@ -68,28 +66,23 @@ public final class ChapFrame extends Id3Frame {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
ChapFrame other = (ChapFrame) obj;
ChapterFrame other = (ChapterFrame) obj;
return startTime == other.startTime
&& endTime == other.endTime
&& startOffset == other.startOffset
&& endOffset == other.endOffset
&& Util.areEqual(chapterId, other.chapterId)
&& Util.areEqual(title, other.title)
&& Util.areEqual(url, other.url)
&& Util.areEqual(image, other.image);
&& endTime == other.endTime
&& startOffset == other.startOffset
&& endOffset == other.endOffset
&& Util.areEqual(chapterId, other.chapterId)
&& Arrays.equals(subFrames, other.subFrames);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (chapterId != null ? chapterId.hashCode() : 0);
result = 31 * result + startTime;
result = 31 * result + endTime;
result = 31 * result + startOffset;
result = 31 * result + endOffset;
result = 31 * result + (title != null ? title.hashCode() : 0);
result = 31 * result + (url != null ? url.hashCode() : 0);
result = 31 * result + (image != null ? image.hashCode() : 0);
result = 31 * result + (chapterId != null ? chapterId.hashCode() : 0);
return result;
}
......@@ -100,10 +93,10 @@ public final class ChapFrame extends Id3Frame {
dest.writeInt(endTime);
dest.writeInt(startOffset);
dest.writeInt(endOffset);
dest.writeString(title);
dest.writeString(url);
dest.writeString(title);
dest.writeParcelable(image, flags);
dest.writeInt(subFrames.length);
for (int i = 0; i < subFrames.length; i++) {
dest.writeParcelable(subFrames[i], 0);
}
}
@Override
......@@ -111,15 +104,18 @@ public final class ChapFrame extends Id3Frame {
return 0;
}
public static final Creator<ChapFrame> CREATOR = new Creator<ChapFrame>() {
public static final Creator<ChapterFrame> CREATOR = new Creator<ChapterFrame>() {
@Override
public ChapFrame createFromParcel(Parcel in) {
return new ChapFrame(in);
public ChapterFrame createFromParcel(Parcel in) {
return new ChapterFrame(in);
}
@Override
public ChapFrame[] newArray(int size) {
return new ChapFrame[size];
public ChapterFrame[] newArray(int size) {
return new ChapterFrame[size];
}
};
}
......@@ -22,9 +22,9 @@ import com.google.android.exoplayer2.util.Util;
import java.util.Arrays;
/**
* Chapter table of contents information "CTOC" ID3 frame.
* Chapter table of contents ID3 frame.
*/
public final class CtocFrame extends Id3Frame {
public final class ChapterTOCFrame extends Id3Frame {
public static final String ID = "CTOC";
......@@ -32,24 +32,29 @@ public final class CtocFrame extends Id3Frame {
public final boolean isRoot;
public final boolean isOrdered;
public final String[] children;
public final String title;
public final Id3Frame[] subFrames;
public CtocFrame(String elementId, boolean isRoot, boolean isOrdered, String[] children, String title) {
public ChapterTOCFrame(String elementId, boolean isRoot, boolean isOrdered, String[] children,
Id3Frame[] subFrames) {
super(ID);
this.elementId = elementId;
this.isRoot = isRoot;
this.isOrdered = isOrdered;
this.children = children;
this.title = title;
this.subFrames = subFrames;
}
/* package */ CtocFrame(Parcel in) {
/* package */ ChapterTOCFrame(Parcel in) {
super(ID);
this.elementId = in.readString();
this.isRoot = in.readByte() != 0;
this.isOrdered = in.readByte() != 0;
this.children = in.createStringArray();
this.title = in.readString();
int subFrameCount = in.readInt();
subFrames = new Id3Frame[subFrameCount];
for (int i = 0; i < subFrameCount; i++) {
subFrames[i] = in.readParcelable(Id3Frame.class.getClassLoader());
}
}
@Override
......@@ -60,43 +65,47 @@ public final class CtocFrame extends Id3Frame {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
CtocFrame other = (CtocFrame) obj;
ChapterTOCFrame other = (ChapterTOCFrame) obj;
return isRoot == other.isRoot
&& isOrdered == other.isOrdered
&& Util.areEqual(elementId, other.elementId)
&& Util.areEqual(title, other.title)
&& Arrays.equals(children, other.children);
&& isOrdered == other.isOrdered
&& Util.areEqual(elementId, other.elementId)
&& Arrays.equals(children, other.children)
&& Arrays.equals(subFrames, other.subFrames);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (elementId != null ? elementId.hashCode() : 0);
result = 31 * result + (isRoot ? 1 : 0);
result = 31 * result + (isOrdered ? 1 : 0);
result = 31 * result + Arrays.hashCode(children);
result = 31 * result + (title != null ? title.hashCode() : 0);
result = 31 * result + (elementId != null ? elementId.hashCode() : 0);
return result;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(elementId);
dest.writeByte((byte)(isRoot ? 1 : 0));
dest.writeByte((byte)(isOrdered ? 1 : 0));
dest.writeByte((byte) (isRoot ? 1 : 0));
dest.writeByte((byte) (isOrdered ? 1 : 0));
dest.writeStringArray(children);
dest.writeString(title);
dest.writeInt(subFrames.length);
for (int i = 0; i < subFrames.length; i++) {
dest.writeParcelable(subFrames[i], 0);
}
}
public static final Creator<CtocFrame> CREATOR = new Creator<CtocFrame>() {
public static final Creator<ChapterTOCFrame> CREATOR = new Creator<ChapterTOCFrame>() {
@Override
public CtocFrame createFromParcel(Parcel in) {
return new CtocFrame(in);
public ChapterTOCFrame createFromParcel(Parcel in) {
return new ChapterTOCFrame(in);
}
@Override
public CtocFrame[] newArray(int size) {
return new CtocFrame[size];
public ChapterTOCFrame[] newArray(int size) {
return new ChapterTOCFrame[size];
}
};
}
......@@ -25,15 +25,18 @@ import com.google.android.exoplayer2.util.Util;
public final class TextInformationFrame extends Id3Frame {
public final String description;
public final String value;
public TextInformationFrame(String id, String description) {
public TextInformationFrame(String id, String description, String value) {
super(id);
this.description = description;
this.value = value;
}
/* package */ TextInformationFrame(Parcel in) {
super(in.readString());
description = in.readString();
value = in.readString();
}
@Override
......@@ -45,7 +48,8 @@ public final class TextInformationFrame extends Id3Frame {
return false;
}
TextInformationFrame other = (TextInformationFrame) obj;
return id.equals(other.id) && Util.areEqual(description, other.description);
return id.equals(other.id) && Util.areEqual(description, other.description)
&& Util.areEqual(value, other.value);
}
@Override
......@@ -53,6 +57,7 @@ public final class TextInformationFrame extends Id3Frame {
int result = 17;
result = 31 * result + id.hashCode();
result = 31 * result + (description != null ? description.hashCode() : 0);
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
......@@ -60,6 +65,7 @@ public final class TextInformationFrame extends Id3Frame {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(description);
dest.writeString(value);
}
public static final Parcelable.Creator<TextInformationFrame> CREATOR =
......
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.
*/
package com.google.android.exoplayer2.metadata.id3;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.android.exoplayer2.util.Util;
/**
* TXXX (User defined text information) ID3 frame.
*/
public final class TxxxFrame extends Id3Frame {
public static final String ID = "TXXX";
public final String description;
public final String value;
public TxxxFrame(String description, String value) {
super(ID);
this.description = description;
this.value = value;
}
/* package */ TxxxFrame(Parcel in) {
super(ID);
description = in.readString();
value = in.readString();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TxxxFrame other = (TxxxFrame) obj;
return Util.areEqual(description, other.description) && Util.areEqual(value, other.value);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (description != null ? description.hashCode() : 0);
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(description);
dest.writeString(value);
}
public static final Parcelable.Creator<TxxxFrame> CREATOR = new Parcelable.Creator<TxxxFrame>() {
@Override
public TxxxFrame createFromParcel(Parcel in) {
return new TxxxFrame(in);
}
@Override
public TxxxFrame[] newArray(int size) {
return new TxxxFrame[size];
}
};
}
......@@ -21,23 +21,21 @@ import android.os.Parcelable;
import com.google.android.exoplayer2.util.Util;
/**
* Url Frame "WXX" ID3 frame.
* WXXX (User defined URL link) ID3 frame.
*/
public final class WxxxFrame extends Id3Frame {
public static final String ID = "WXXX";
public final class UrlLinkFrame extends Id3Frame {
public final String description;
public final String url;
public WxxxFrame(String description, String url) {
super(ID);
public UrlLinkFrame(String id, String description, String url) {
super(id);
this.description = description;
this.url = url;
}
/* package */ WxxxFrame(Parcel in) {
super(ID);
/* package */ UrlLinkFrame(Parcel in) {
super(in.readString());
description = in.readString();
url = in.readString();
}
......@@ -50,14 +48,15 @@ public final class WxxxFrame extends Id3Frame {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
WxxxFrame other = (WxxxFrame) obj;
return Util.areEqual(description, other.description)
UrlLinkFrame other = (UrlLinkFrame) obj;
return id.equals(other.id) && Util.areEqual(description, other.description)
&& Util.areEqual(url, other.url);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + id.hashCode();
result = 31 * result + (description != null ? description.hashCode() : 0);
result = 31 * result + (url != null ? url.hashCode() : 0);
return result;
......@@ -65,23 +64,24 @@ public final class WxxxFrame extends Id3Frame {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(description);
dest.writeString(url);
}
public static final Parcelable.Creator<WxxxFrame> CREATOR =
new Parcelable.Creator<WxxxFrame>() {
public static final Parcelable.Creator<UrlLinkFrame> CREATOR =
new Parcelable.Creator<UrlLinkFrame>() {
@Override
public WxxxFrame createFromParcel(Parcel in) {
return new WxxxFrame(in);
}
@Override
public UrlLinkFrame createFromParcel(Parcel in) {
return new UrlLinkFrame(in);
}
@Override
public WxxxFrame[] newArray(int size) {
return new WxxxFrame[size];
}
@Override
public UrlLinkFrame[] newArray(int size) {
return new UrlLinkFrame[size];
}
};
};
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment