Allow disabling ID3 metadata parsing if not required
Issue: #2553 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=150184824
This commit is contained in:
parent
d077e23daa
commit
db5f81ecfd
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor;
|
||||
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 java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -26,6 +27,18 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public final class GaplessInfoHolder {
|
||||
|
||||
/**
|
||||
* A {@link FramePredicate} suitable for use when decoding {@link Metadata} that will be passed
|
||||
* to {@link #setFromMetadata(Metadata)}. Only frames that might contain gapless playback
|
||||
* information are decoded.
|
||||
*/
|
||||
public static final FramePredicate GAPLESS_INFO_ID3_FRAME_PREDICATE = new FramePredicate() {
|
||||
@Override
|
||||
public boolean evaluate(int majorVersion, int id0, int id1, int id2, int id3) {
|
||||
return id0 == 'C' && id1 == 'O' && id2 == 'M' && (id3 == 'M' || majorVersion == 2);
|
||||
}
|
||||
};
|
||||
|
||||
private static final String GAPLESS_COMMENT_ID = "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})");
|
||||
|
@ -58,13 +58,18 @@ public final class Mp3Extractor implements Extractor {
|
||||
* Flags controlling the behavior of the extractor.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(flag = true, value = {FLAG_ENABLE_CONSTANT_BITRATE_SEEKING})
|
||||
@IntDef(flag = true, value = {FLAG_ENABLE_CONSTANT_BITRATE_SEEKING, FLAG_DISABLE_ID3_METADATA})
|
||||
public @interface Flags {}
|
||||
/**
|
||||
* Flag to force enable seeking using a constant bitrate assumption in cases where seeking would
|
||||
* otherwise not be possible.
|
||||
*/
|
||||
public static final int FLAG_ENABLE_CONSTANT_BITRATE_SEEKING = 1;
|
||||
/**
|
||||
* Flag to disable parsing of ID3 metadata. Can be set to save memory if ID3 metadata is not
|
||||
* required.
|
||||
*/
|
||||
public static final int FLAG_DISABLE_ID3_METADATA = 2;
|
||||
|
||||
/**
|
||||
* The maximum number of bytes to search when synchronizing, before giving up.
|
||||
@ -178,7 +183,8 @@ public final class Mp3Extractor implements Extractor {
|
||||
trackOutput.format(Format.createAudioSampleFormat(null, synchronizedHeader.mimeType, null,
|
||||
Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, synchronizedHeader.channels,
|
||||
synchronizedHeader.sampleRate, Format.NO_VALUE, gaplessInfoHolder.encoderDelay,
|
||||
gaplessInfoHolder.encoderPadding, null, null, 0, null, metadata));
|
||||
gaplessInfoHolder.encoderPadding, null, null, 0, null,
|
||||
(flags & FLAG_DISABLE_ID3_METADATA) != 0 ? null : metadata));
|
||||
}
|
||||
return readSample(input);
|
||||
}
|
||||
@ -311,7 +317,11 @@ public final class Mp3Extractor implements Extractor {
|
||||
byte[] id3Data = new byte[tagLength];
|
||||
System.arraycopy(scratch.data, 0, id3Data, 0, Id3Decoder.ID3_HEADER_LENGTH);
|
||||
input.peekFully(id3Data, Id3Decoder.ID3_HEADER_LENGTH, framesLength);
|
||||
metadata = new Id3Decoder().decode(id3Data, tagLength);
|
||||
// We need to parse enough ID3 metadata to retrieve any gapless playback information even
|
||||
// if ID3 metadata parsing is disabled.
|
||||
Id3Decoder.FramePredicate id3FramePredicate = (flags & FLAG_DISABLE_ID3_METADATA) != 0
|
||||
? GaplessInfoHolder.GAPLESS_INFO_ID3_FRAME_PREDICATE : null;
|
||||
metadata = new Id3Decoder(id3FramePredicate).decode(id3Data, tagLength);
|
||||
if (metadata != null) {
|
||||
gaplessInfoHolder.setFromMetadata(metadata);
|
||||
}
|
||||
|
@ -34,6 +34,25 @@ import java.util.Locale;
|
||||
*/
|
||||
public final class Id3Decoder implements MetadataDecoder {
|
||||
|
||||
/**
|
||||
* A predicate for determining whether individual frames should be decoded.
|
||||
*/
|
||||
public interface FramePredicate {
|
||||
|
||||
/**
|
||||
* Returns whether a frame with the specified parameters should be decoded.
|
||||
*
|
||||
* @param majorVersion The major version of the ID3 tag.
|
||||
* @param id0 The first byte of the frame ID.
|
||||
* @param id1 The second byte of the frame ID.
|
||||
* @param id2 The third byte of the frame ID.
|
||||
* @param id3 The fourth byte of the frame ID.
|
||||
* @return Whether the frame should be decoded.
|
||||
*/
|
||||
boolean evaluate(int majorVersion, int id0, int id1, int id2, int id3);
|
||||
|
||||
}
|
||||
|
||||
private static final String TAG = "Id3Decoder";
|
||||
|
||||
/**
|
||||
@ -50,6 +69,19 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
private static final int ID3_TEXT_ENCODING_UTF_16BE = 2;
|
||||
private static final int ID3_TEXT_ENCODING_UTF_8 = 3;
|
||||
|
||||
private final FramePredicate framePredicate;
|
||||
|
||||
public Id3Decoder() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param framePredicate Determines which frames are decoded. May be null to decode all frames.
|
||||
*/
|
||||
public Id3Decoder(FramePredicate framePredicate) {
|
||||
this.framePredicate = framePredicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metadata decode(MetadataInputBuffer inputBuffer) {
|
||||
ByteBuffer buffer = inputBuffer.data;
|
||||
@ -94,7 +126,7 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
int frameHeaderSize = id3Header.majorVersion == 2 ? 6 : 10;
|
||||
while (id3Data.bytesLeft() >= frameHeaderSize) {
|
||||
Id3Frame frame = decodeFrame(id3Header.majorVersion, id3Data, unsignedIntFrameSizeHack,
|
||||
frameHeaderSize);
|
||||
frameHeaderSize, framePredicate);
|
||||
if (frame != null) {
|
||||
id3Frames.add(frame);
|
||||
}
|
||||
@ -200,7 +232,7 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
}
|
||||
|
||||
private static Id3Frame decodeFrame(int majorVersion, ParsableByteArray id3Data,
|
||||
boolean unsignedIntFrameSizeHack, int frameHeaderSize) {
|
||||
boolean unsignedIntFrameSizeHack, int frameHeaderSize, FramePredicate framePredicate) {
|
||||
int frameId0 = id3Data.readUnsignedByte();
|
||||
int frameId1 = id3Data.readUnsignedByte();
|
||||
int frameId2 = id3Data.readUnsignedByte();
|
||||
@ -234,6 +266,13 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (framePredicate != null
|
||||
&& !framePredicate.evaluate(majorVersion, frameId0, frameId1, frameId2, frameId3)) {
|
||||
// Filtered by the predicate.
|
||||
id3Data.setPosition(nextFramePosition);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Frame flags.
|
||||
boolean isCompressed = false;
|
||||
boolean isEncrypted = false;
|
||||
@ -302,10 +341,10 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
frame = decodeCommentFrame(id3Data, frameSize);
|
||||
} else if (frameId0 == 'C' && frameId1 == 'H' && frameId2 == 'A' && frameId3 == 'P') {
|
||||
frame = decodeChapterFrame(id3Data, frameSize, majorVersion, unsignedIntFrameSizeHack,
|
||||
frameHeaderSize);
|
||||
frameHeaderSize, framePredicate);
|
||||
} else if (frameId0 == 'C' && frameId1 == 'T' && frameId2 == 'O' && frameId3 == 'C') {
|
||||
frame = decodeChapterTOCFrame(id3Data, frameSize, majorVersion, unsignedIntFrameSizeHack,
|
||||
frameHeaderSize);
|
||||
frameHeaderSize, framePredicate);
|
||||
} else {
|
||||
String id = majorVersion == 2
|
||||
? String.format(Locale.US, "%c%c%c", frameId0, frameId1, frameId2)
|
||||
@ -513,8 +552,8 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
}
|
||||
|
||||
private static ChapterFrame decodeChapterFrame(ParsableByteArray id3Data, int frameSize,
|
||||
int majorVersion, boolean unsignedIntFrameSizeHack, int frameHeaderSize)
|
||||
throws UnsupportedEncodingException {
|
||||
int majorVersion, boolean unsignedIntFrameSizeHack, int frameHeaderSize,
|
||||
FramePredicate framePredicate) throws UnsupportedEncodingException {
|
||||
int framePosition = id3Data.getPosition();
|
||||
int chapterIdEndIndex = indexOfZeroByte(id3Data.data, framePosition);
|
||||
String chapterId = new String(id3Data.data, framePosition, chapterIdEndIndex - framePosition,
|
||||
@ -536,7 +575,7 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
int limit = framePosition + frameSize;
|
||||
while (id3Data.getPosition() < limit) {
|
||||
Id3Frame frame = decodeFrame(majorVersion, id3Data, unsignedIntFrameSizeHack,
|
||||
frameHeaderSize);
|
||||
frameHeaderSize, framePredicate);
|
||||
if (frame != null) {
|
||||
subFrames.add(frame);
|
||||
}
|
||||
@ -548,8 +587,8 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
}
|
||||
|
||||
private static ChapterTocFrame decodeChapterTOCFrame(ParsableByteArray id3Data, int frameSize,
|
||||
int majorVersion, boolean unsignedIntFrameSizeHack, int frameHeaderSize)
|
||||
throws UnsupportedEncodingException {
|
||||
int majorVersion, boolean unsignedIntFrameSizeHack, int frameHeaderSize,
|
||||
FramePredicate framePredicate) throws UnsupportedEncodingException {
|
||||
int framePosition = id3Data.getPosition();
|
||||
int elementIdEndIndex = indexOfZeroByte(id3Data.data, framePosition);
|
||||
String elementId = new String(id3Data.data, framePosition, elementIdEndIndex - framePosition,
|
||||
@ -573,7 +612,7 @@ public final class Id3Decoder implements MetadataDecoder {
|
||||
int limit = framePosition + frameSize;
|
||||
while (id3Data.getPosition() < limit) {
|
||||
Id3Frame frame = decodeFrame(majorVersion, id3Data, unsignedIntFrameSizeHack,
|
||||
frameHeaderSize);
|
||||
frameHeaderSize, framePredicate);
|
||||
if (frame != null) {
|
||||
subFrames.add(frame);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user