mirror of
https://github.com/androidx/media.git
synced 2025-05-12 01:59:50 +08:00
Merge branch 'geekygecko-dev-v2' into dev-v2
This commit is contained in:
commit
27b8c31b85
@ -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,
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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;
|
||||
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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 com.google.android.exoplayer2.util.Util;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Chapter information ID3 frame.
|
||||
*/
|
||||
public final class ChapterFrame extends Id3Frame {
|
||||
|
||||
public static final String ID = "CHAP";
|
||||
|
||||
public final String chapterId;
|
||||
public final int startTime;
|
||||
public final int endTime;
|
||||
public final int startOffset;
|
||||
public final int endOffset;
|
||||
private final Id3Frame[] subFrames;
|
||||
|
||||
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.subFrames = subFrames;
|
||||
}
|
||||
|
||||
/* 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();
|
||||
int subFrameCount = in.readInt();
|
||||
subFrames = new Id3Frame[subFrameCount];
|
||||
for (int i = 0; i < subFrameCount; i++) {
|
||||
subFrames[i] = in.readParcelable(Id3Frame.class.getClassLoader());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ChapterFrame other = (ChapterFrame) obj;
|
||||
return startTime == other.startTime
|
||||
&& 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 + startTime;
|
||||
result = 31 * result + endTime;
|
||||
result = 31 * result + startOffset;
|
||||
result = 31 * result + endOffset;
|
||||
result = 31 * result + (chapterId != null ? chapterId.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(chapterId);
|
||||
dest.writeInt(startTime);
|
||||
dest.writeInt(endTime);
|
||||
dest.writeInt(startOffset);
|
||||
dest.writeInt(endOffset);
|
||||
dest.writeInt(subFrames.length);
|
||||
for (int i = 0; i < subFrames.length; i++) {
|
||||
dest.writeParcelable(subFrames[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final Creator<ChapterFrame> CREATOR = new Creator<ChapterFrame>() {
|
||||
|
||||
@Override
|
||||
public ChapterFrame createFromParcel(Parcel in) {
|
||||
return new ChapterFrame(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChapterFrame[] newArray(int size) {
|
||||
return new ChapterFrame[size];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Chapter table of contents ID3 frame.
|
||||
*/
|
||||
public final class ChapterTOCFrame extends Id3Frame {
|
||||
|
||||
public static final String ID = "CTOC";
|
||||
|
||||
public final String elementId;
|
||||
public final boolean isRoot;
|
||||
public final boolean isOrdered;
|
||||
public final String[] children;
|
||||
public final Id3Frame[] subFrames;
|
||||
|
||||
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.subFrames = subFrames;
|
||||
}
|
||||
|
||||
/* package */ ChapterTOCFrame(Parcel in) {
|
||||
super(ID);
|
||||
this.elementId = in.readString();
|
||||
this.isRoot = in.readByte() != 0;
|
||||
this.isOrdered = in.readByte() != 0;
|
||||
this.children = in.createStringArray();
|
||||
int subFrameCount = in.readInt();
|
||||
subFrames = new Id3Frame[subFrameCount];
|
||||
for (int i = 0; i < subFrameCount; i++) {
|
||||
subFrames[i] = in.readParcelable(Id3Frame.class.getClassLoader());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ChapterTOCFrame other = (ChapterTOCFrame) obj;
|
||||
return isRoot == other.isRoot
|
||||
&& 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 + (isRoot ? 1 : 0);
|
||||
result = 31 * result + (isOrdered ? 1 : 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.writeStringArray(children);
|
||||
dest.writeInt(subFrames.length);
|
||||
for (int i = 0; i < subFrames.length; i++) {
|
||||
dest.writeParcelable(subFrames[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Creator<ChapterTOCFrame> CREATOR = new Creator<ChapterTOCFrame>() {
|
||||
|
||||
@Override
|
||||
public ChapterTOCFrame createFromParcel(Parcel in) {
|
||||
return new ChapterTOCFrame(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChapterTOCFrame[] newArray(int size) {
|
||||
return new ChapterTOCFrame[size];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -98,7 +98,8 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
|
||||
int frameHeaderSize = id3Header.majorVersion == 2 ? 6 : 10;
|
||||
while (id3Data.bytesLeft() >= frameHeaderSize) {
|
||||
Id3Frame frame = decodeFrame(id3Header.majorVersion, id3Data, unsignedIntFrameSizeHack);
|
||||
Id3Frame frame = decodeFrame(id3Header.majorVersion, id3Data, unsignedIntFrameSizeHack,
|
||||
frameHeaderSize);
|
||||
if (frame != null) {
|
||||
id3Frames.add(frame);
|
||||
}
|
||||
@ -204,7 +205,7 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
}
|
||||
|
||||
private static Id3Frame decodeFrame(int majorVersion, ParsableByteArray id3Data,
|
||||
boolean unsignedIntFrameSizeHack) {
|
||||
boolean unsignedIntFrameSizeHack, int frameHeaderSize) {
|
||||
int frameId0 = id3Data.readUnsignedByte();
|
||||
int frameId1 = id3Data.readUnsignedByte();
|
||||
int frameId2 = id3Data.readUnsignedByte();
|
||||
@ -280,6 +281,19 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
if (frameId0 == 'T' && frameId1 == 'X' && frameId2 == 'X'
|
||||
&& (majorVersion == 2 || frameId3 == 'X')) {
|
||||
frame = decodeTxxxFrame(id3Data, frameSize);
|
||||
} else if (frameId0 == 'T') {
|
||||
String id = majorVersion == 2
|
||||
? String.format(Locale.US, "%c%c%c", frameId0, frameId1, frameId2)
|
||||
: String.format(Locale.US, "%c%c%c%c", frameId0, frameId1, frameId2, frameId3);
|
||||
frame = decodeTextInformationFrame(id3Data, frameSize, id);
|
||||
} else if (frameId0 == 'W' && frameId1 == 'X' && frameId2 == 'X'
|
||||
&& (majorVersion == 2 || frameId3 == 'X')) {
|
||||
frame = decodeWxxxFrame(id3Data, frameSize);
|
||||
} else if (frameId0 == 'W') {
|
||||
String id = majorVersion == 2
|
||||
? String.format(Locale.US, "%c%c%c", frameId0, frameId1, frameId2)
|
||||
: String.format(Locale.US, "%c%c%c%c", frameId0, frameId1, frameId2, frameId3);
|
||||
frame = decodeUrlLinkFrame(id3Data, frameSize, id);
|
||||
} else if (frameId0 == 'P' && frameId1 == 'R' && frameId2 == 'I' && frameId3 == 'V') {
|
||||
frame = decodePrivFrame(id3Data, frameSize);
|
||||
} else if (frameId0 == 'G' && frameId1 == 'E' && frameId2 == 'O'
|
||||
@ -288,14 +302,15 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
} else if (majorVersion == 2 ? (frameId0 == 'P' && frameId1 == 'I' && frameId2 == 'C')
|
||||
: (frameId0 == 'A' && frameId1 == 'P' && frameId2 == 'I' && frameId3 == 'C')) {
|
||||
frame = decodeApicFrame(id3Data, frameSize, majorVersion);
|
||||
} else if (frameId0 == 'T') {
|
||||
String id = majorVersion == 2
|
||||
? String.format(Locale.US, "%c%c%c", frameId0, frameId1, frameId2)
|
||||
: String.format(Locale.US, "%c%c%c%c", frameId0, frameId1, frameId2, frameId3);
|
||||
frame = decodeTextInformationFrame(id3Data, frameSize, id);
|
||||
} else if (frameId0 == 'C' && frameId1 == 'O' && frameId2 == 'M'
|
||||
&& (frameId3 == 'M' || majorVersion == 2)) {
|
||||
frame = decodeCommentFrame(id3Data, frameSize);
|
||||
} else if (frameId0 == 'C' && frameId1 == 'H' && frameId2 == 'A' && frameId3 == 'P') {
|
||||
frame = decodeChapterFrame(id3Data, frameSize, majorVersion, unsignedIntFrameSizeHack,
|
||||
frameHeaderSize);
|
||||
} else if (frameId0 == 'C' && frameId1 == 'T' && frameId2 == 'O' && frameId3 == 'C') {
|
||||
frame = decodeChapterTOCFrame(id3Data, frameSize, majorVersion, unsignedIntFrameSizeHack,
|
||||
frameHeaderSize);
|
||||
} else {
|
||||
String id = majorVersion == 2
|
||||
? String.format(Locale.US, "%c%c%c", frameId0, frameId1, frameId2)
|
||||
@ -311,7 +326,7 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
private static TxxxFrame decodeTxxxFrame(ParsableByteArray id3Data, int frameSize)
|
||||
private static TextInformationFrame decodeTxxxFrame(ParsableByteArray id3Data, int frameSize)
|
||||
throws UnsupportedEncodingException {
|
||||
int encoding = id3Data.readUnsignedByte();
|
||||
String charset = getCharsetName(encoding);
|
||||
@ -331,7 +346,65 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
value = "";
|
||||
}
|
||||
|
||||
return new TxxxFrame(description, value);
|
||||
return new TextInformationFrame("TXXX", description, value);
|
||||
}
|
||||
|
||||
private static TextInformationFrame decodeTextInformationFrame(ParsableByteArray id3Data,
|
||||
int frameSize, String id) throws UnsupportedEncodingException {
|
||||
if (frameSize <= 1) {
|
||||
// Frame is empty or contains only the text encoding byte.
|
||||
return new TextInformationFrame(id, null, "");
|
||||
}
|
||||
|
||||
int encoding = id3Data.readUnsignedByte();
|
||||
String charset = getCharsetName(encoding);
|
||||
|
||||
byte[] data = new byte[frameSize - 1];
|
||||
id3Data.readBytes(data, 0, frameSize - 1);
|
||||
|
||||
int valueEndIndex = indexOfEos(data, 0, encoding);
|
||||
String value = new String(data, 0, valueEndIndex, charset);
|
||||
|
||||
return new TextInformationFrame(id, null, value);
|
||||
}
|
||||
|
||||
private static UrlLinkFrame decodeWxxxFrame(ParsableByteArray id3Data,
|
||||
int frameSize) throws UnsupportedEncodingException {
|
||||
int encoding = id3Data.readUnsignedByte();
|
||||
String charset = getCharsetName(encoding);
|
||||
|
||||
byte[] data = new byte[frameSize - 1];
|
||||
id3Data.readBytes(data, 0, frameSize - 1);
|
||||
|
||||
int descriptionEndIndex = indexOfEos(data, 0, encoding);
|
||||
String description = new String(data, 0, descriptionEndIndex, charset);
|
||||
|
||||
String url;
|
||||
int urlStartIndex = descriptionEndIndex + delimiterLength(encoding);
|
||||
if (urlStartIndex < data.length) {
|
||||
int urlEndIndex = indexOfZeroByte(data, 0);
|
||||
url = new String(data, urlStartIndex, urlEndIndex - urlStartIndex, "ISO-8859-1");
|
||||
} else {
|
||||
url = "";
|
||||
}
|
||||
|
||||
return new UrlLinkFrame("WXXX", description, url);
|
||||
}
|
||||
|
||||
private static UrlLinkFrame decodeUrlLinkFrame(ParsableByteArray id3Data, int frameSize,
|
||||
String id) throws UnsupportedEncodingException {
|
||||
if (frameSize == 0) {
|
||||
// Frame is empty.
|
||||
return new UrlLinkFrame(id, null, "");
|
||||
}
|
||||
|
||||
byte[] data = new byte[frameSize];
|
||||
id3Data.readBytes(data, 0, frameSize);
|
||||
|
||||
int urlEndIndex = indexOfZeroByte(data, 0);
|
||||
String url = new String(data, 0, urlEndIndex, "ISO-8859-1");
|
||||
|
||||
return new UrlLinkFrame(id, null, url);
|
||||
}
|
||||
|
||||
private static PrivFrame decodePrivFrame(ParsableByteArray id3Data, int frameSize)
|
||||
@ -439,23 +512,69 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
return new CommentFrame(language, description, text);
|
||||
}
|
||||
|
||||
private static TextInformationFrame decodeTextInformationFrame(ParsableByteArray id3Data,
|
||||
int frameSize, String id) throws UnsupportedEncodingException {
|
||||
if (frameSize <= 1) {
|
||||
// Frame is empty or contains only the text encoding byte.
|
||||
return new TextInformationFrame(id, "");
|
||||
private static ChapterFrame decodeChapterFrame(ParsableByteArray id3Data, int frameSize,
|
||||
int majorVersion, boolean unsignedIntFrameSizeHack, int frameHeaderSize)
|
||||
throws UnsupportedEncodingException {
|
||||
int framePosition = id3Data.getPosition();
|
||||
int chapterIdEndIndex = indexOfZeroByte(id3Data.data, framePosition);
|
||||
String chapterId = new String(id3Data.data, framePosition, chapterIdEndIndex - framePosition,
|
||||
"ISO-8859-1");
|
||||
id3Data.setPosition(chapterIdEndIndex + 1);
|
||||
|
||||
int startTime = id3Data.readUnsignedByte();
|
||||
int endTime = id3Data.readUnsignedByte();
|
||||
int startOffset = id3Data.readUnsignedByte();
|
||||
int endOffset = id3Data.readUnsignedByte();
|
||||
|
||||
ArrayList<Id3Frame> subFrames = new ArrayList<>();
|
||||
int limit = framePosition + frameSize;
|
||||
while (id3Data.getPosition() < limit) {
|
||||
Id3Frame frame = decodeFrame(majorVersion, id3Data, unsignedIntFrameSizeHack,
|
||||
frameHeaderSize);
|
||||
if (frame != null) {
|
||||
subFrames.add(frame);
|
||||
}
|
||||
}
|
||||
|
||||
int encoding = id3Data.readUnsignedByte();
|
||||
String charset = getCharsetName(encoding);
|
||||
Id3Frame[] subFrameArray = new Id3Frame[subFrames.size()];
|
||||
subFrames.toArray(subFrameArray);
|
||||
return new ChapterFrame(chapterId, startTime, endTime, startOffset, endOffset, subFrameArray);
|
||||
}
|
||||
|
||||
byte[] data = new byte[frameSize - 1];
|
||||
id3Data.readBytes(data, 0, frameSize - 1);
|
||||
private static ChapterTOCFrame decodeChapterTOCFrame(ParsableByteArray id3Data, int frameSize,
|
||||
int majorVersion, boolean unsignedIntFrameSizeHack, int frameHeaderSize)
|
||||
throws UnsupportedEncodingException {
|
||||
int framePosition = id3Data.getPosition();
|
||||
int elementIdEndIndex = indexOfZeroByte(id3Data.data, framePosition);
|
||||
String elementId = new String(id3Data.data, framePosition, elementIdEndIndex - framePosition,
|
||||
"ISO-8859-1");
|
||||
id3Data.setPosition(elementIdEndIndex + 1);
|
||||
|
||||
int descriptionEndIndex = indexOfEos(data, 0, encoding);
|
||||
String description = new String(data, 0, descriptionEndIndex, charset);
|
||||
int ctocFlags = id3Data.readUnsignedByte();
|
||||
boolean isRoot = (ctocFlags & 0x0002) != 0;
|
||||
boolean isOrdered = (ctocFlags & 0x0001) != 0;
|
||||
|
||||
return new TextInformationFrame(id, description);
|
||||
int childCount = id3Data.readUnsignedByte();
|
||||
String[] children = new String[childCount];
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
int startIndex = id3Data.getPosition();
|
||||
int endIndex = indexOfZeroByte(id3Data.data, startIndex);
|
||||
children[i] = new String(id3Data.data, startIndex, endIndex - startIndex, "ISO-8859-1");
|
||||
}
|
||||
|
||||
ArrayList<Id3Frame> subFrames = new ArrayList<>();
|
||||
int limit = framePosition + frameSize;
|
||||
while (id3Data.getPosition() < limit) {
|
||||
Id3Frame frame = decodeFrame(majorVersion, id3Data, unsignedIntFrameSizeHack,
|
||||
frameHeaderSize);
|
||||
if (frame != null) {
|
||||
subFrames.add(frame);
|
||||
}
|
||||
}
|
||||
|
||||
Id3Frame[] subFrameArray = new Id3Frame[subFrames.size()];
|
||||
subFrames.toArray(subFrameArray);
|
||||
return new ChapterTOCFrame(elementId, isRoot, isOrdered, children, subFrameArray);
|
||||
}
|
||||
|
||||
private static BinaryFrame decodeBinaryFrame(ParsableByteArray id3Data, int frameSize,
|
||||
@ -467,8 +586,8 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs in-place removal of unsynchronization for {@code length} bytes starting from
|
||||
* {@link ParsableByteArray#getPosition()}
|
||||
* Performs in-place removal of unsynchronization for {@code length} bytes starting from {@link
|
||||
* ParsableByteArray#getPosition()}
|
||||
*
|
||||
* @param data Contains the data to be processed.
|
||||
* @param length The length of the data to be processed.
|
||||
@ -487,6 +606,7 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
|
||||
/**
|
||||
* Maps encoding byte from ID3v2 frame to a Charset.
|
||||
*
|
||||
* @param encodingByte The value of encoding byte from ID3v2 frame.
|
||||
* @return Charset name.
|
||||
*/
|
||||
|
@ -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 =
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* 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.
|
||||
@ -17,28 +17,27 @@ 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.
|
||||
* WXXX (User defined URL link) ID3 frame.
|
||||
*/
|
||||
public final class TxxxFrame extends Id3Frame {
|
||||
|
||||
public static final String ID = "TXXX";
|
||||
public final class UrlLinkFrame extends Id3Frame {
|
||||
|
||||
public final String description;
|
||||
public final String value;
|
||||
public final String url;
|
||||
|
||||
public TxxxFrame(String description, String value) {
|
||||
super(ID);
|
||||
public UrlLinkFrame(String id, String description, String url) {
|
||||
super(id);
|
||||
this.description = description;
|
||||
this.value = value;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/* package */ TxxxFrame(Parcel in) {
|
||||
super(ID);
|
||||
/* package */ UrlLinkFrame(Parcel in) {
|
||||
super(in.readString());
|
||||
description = in.readString();
|
||||
value = in.readString();
|
||||
url = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,36 +48,40 @@ public final class TxxxFrame extends Id3Frame {
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
TxxxFrame other = (TxxxFrame) obj;
|
||||
return Util.areEqual(description, other.description) && Util.areEqual(value, other.value);
|
||||
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 + (value != null ? value.hashCode() : 0);
|
||||
result = 31 * result + (url != null ? url.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(id);
|
||||
dest.writeString(description);
|
||||
dest.writeString(value);
|
||||
dest.writeString(url);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<TxxxFrame> CREATOR = new Parcelable.Creator<TxxxFrame>() {
|
||||
public static final Parcelable.Creator<UrlLinkFrame> CREATOR =
|
||||
new Parcelable.Creator<UrlLinkFrame>() {
|
||||
|
||||
@Override
|
||||
public TxxxFrame createFromParcel(Parcel in) {
|
||||
return new TxxxFrame(in);
|
||||
}
|
||||
@Override
|
||||
public UrlLinkFrame createFromParcel(Parcel in) {
|
||||
return new UrlLinkFrame(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TxxxFrame[] newArray(int size) {
|
||||
return new TxxxFrame[size];
|
||||
}
|
||||
@Override
|
||||
public UrlLinkFrame[] newArray(int size) {
|
||||
return new UrlLinkFrame[size];
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user