Commit f39d2813 by andrewlewis Committed by Oliver Woodman

Pass through all ID3 internal data from udta

Also switch from using a CommentFrame to a new InternalFrame type for ID3 data
stored with ID '----', to distinguish internal data from actual ID3 comments.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=201315254
parent 224d0c2b
......@@ -33,6 +33,8 @@
track in DASH ([#4477](https://github.com/google/ExoPlayer/issues/4477)).
* Add `PlayerView.isControllerVisible`
([#4385](https://github.com/google/ExoPlayer/issues/4385)).
* Expose all internal ID3 data stored in MP4 udta boxes, and switch from using
CommentFrame to InternalFrame for frames with gapless metadata in MP4.
### 2.8.2 ###
......
......@@ -19,6 +19,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.id3.CommentFrame;
import com.google.android.exoplayer2.metadata.id3.Id3Decoder.FramePredicate;
import com.google.android.exoplayer2.metadata.id3.InternalFrame;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
......@@ -39,7 +40,8 @@ public final class GaplessInfoHolder {
}
};
private static final String GAPLESS_COMMENT_ID = "iTunSMPB";
private static final String GAPLESS_DOMAIN = "com.apple.iTunes";
private static final String GAPLESS_DESCRIPTION = "iTunSMPB";
private static final Pattern GAPLESS_COMMENT_PATTERN =
Pattern.compile("^ [0-9a-fA-F]{8} ([0-9a-fA-F]{8}) ([0-9a-fA-F]{8})");
......@@ -91,7 +93,15 @@ public final class GaplessInfoHolder {
Metadata.Entry entry = metadata.get(i);
if (entry instanceof CommentFrame) {
CommentFrame commentFrame = (CommentFrame) entry;
if (setFromComment(commentFrame.description, commentFrame.text)) {
if (GAPLESS_DESCRIPTION.equals(commentFrame.description)
&& setFromComment(commentFrame.text)) {
return true;
}
} else if (entry instanceof InternalFrame) {
InternalFrame internalFrame = (InternalFrame) entry;
if (GAPLESS_DOMAIN.equals(internalFrame.domain)
&& GAPLESS_DESCRIPTION.equals(internalFrame.description)
&& setFromComment(internalFrame.text)) {
return true;
}
}
......@@ -103,14 +113,10 @@ public final class GaplessInfoHolder {
* Populates the holder with data parsed from a gapless playback comment (stored in an ID3 header
* or MPEG 4 user data), if valid and non-zero.
*
* @param name The comment's identifier.
* @param data The comment's payload data.
* @return Whether the holder was populated.
*/
private boolean setFromComment(String name, String data) {
if (!GAPLESS_COMMENT_ID.equals(name)) {
return false;
}
private boolean setFromComment(String data) {
Matcher matcher = GAPLESS_COMMENT_PATTERN.matcher(data);
if (matcher.find()) {
try {
......
......@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.id3.ApicFrame;
import com.google.android.exoplayer2.metadata.id3.CommentFrame;
import com.google.android.exoplayer2.metadata.id3.Id3Frame;
import com.google.android.exoplayer2.metadata.id3.InternalFrame;
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
......@@ -293,14 +294,13 @@ import com.google.android.exoplayer2.util.Util;
data.skipBytes(atomSize - 12);
}
}
if (!"com.apple.iTunes".equals(domain) || !"iTunSMPB".equals(name) || dataAtomPosition == -1) {
// We're only interested in iTunSMPB.
if (domain == null || name == null || dataAtomPosition == -1) {
return null;
}
data.setPosition(dataAtomPosition);
data.skipBytes(16); // size (4), type (4), version (1), flags (3), empty (4)
String value = data.readNullTerminatedString(dataAtomSize - 16);
return new CommentFrame(LANGUAGE_UNDEFINED, name, value);
return new InternalFrame(domain, name, value);
}
private static int parseUint8AttributeValue(ParsableByteArray data) {
......
/*
* Copyright (C) 2018 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.support.annotation.Nullable;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
/** Internal ID3 frame that is intended for use by the player. */
public final class InternalFrame extends Id3Frame {
public static final String ID = "----";
public final String domain;
public final String description;
public final String text;
public InternalFrame(String domain, String description, String text) {
super(ID);
this.domain = domain;
this.description = description;
this.text = text;
}
/* package */ InternalFrame(Parcel in) {
super(ID);
domain = Assertions.checkNotNull(in.readString());
description = Assertions.checkNotNull(in.readString());
text = Assertions.checkNotNull(in.readString());
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
InternalFrame other = (InternalFrame) obj;
return Util.areEqual(description, other.description)
&& Util.areEqual(domain, other.domain)
&& Util.areEqual(text, other.text);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (domain != null ? domain.hashCode() : 0);
result = 31 * result + (description != null ? description.hashCode() : 0);
result = 31 * result + (text != null ? text.hashCode() : 0);
return result;
}
@Override
public String toString() {
return id + ": domain=" + domain + ", description=" + description;
}
// Parcelable implementation.
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(domain);
dest.writeString(text);
}
public static final Creator<InternalFrame> CREATOR =
new Creator<InternalFrame>() {
@Override
public InternalFrame createFromParcel(Parcel in) {
return new InternalFrame(in);
}
@Override
public InternalFrame[] newArray(int size) {
return new InternalFrame[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