mirror of
https://github.com/samsonjs/media.git
synced 2026-04-14 12:45:47 +00:00
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
This commit is contained in:
parent
d8a61aade7
commit
a75b5fb6a9
4 changed files with 114 additions and 10 deletions
|
|
@ -31,6 +31,8 @@
|
|||
([#4360](https://github.com/google/ExoPlayer/issues/4360)).
|
||||
* 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) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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];
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue