Simplify logic to handle FLAC files with ID3 header.

LibFlac internally can skip ID3 tags correctly. Therefore, we don't need to keep track of the whole ID3 header section and skip through this section in Java code. We can just set the whole stream to the native library, and it will handle skipping ID3 tags correctly.

The only thing that the Java part need to do is peeking and parsing ID3 tags (if present), in order to populate the track format metadata.

GitHub: #4055.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=193327602
This commit is contained in:
hoangtc 2018-04-18 02:38:42 -07:00 committed by Oliver Woodman
parent a8e16f3cfe
commit fb5e31d3d6

View file

@ -79,7 +79,7 @@ public final class FlacExtractor implements Extractor {
private static final byte[] FLAC_SIGNATURE = {'f', 'L', 'a', 'C', 0, 0, 0, 0x22};
private final Id3Peeker id3Peeker;
private final @Flags int flags;
private final boolean isId3MetadataDisabled;
private FlacDecoderJni decoderJni;
@ -90,7 +90,6 @@ public final class FlacExtractor implements Extractor {
private ByteBuffer outputByteBuffer;
private Metadata id3Metadata;
private long id3SectionSize;
private boolean metadataParsed;
@ -105,8 +104,8 @@ public final class FlacExtractor implements Extractor {
* @param flags Flags that control the extractor's behavior.
*/
public FlacExtractor(int flags) {
this.flags = flags;
id3Peeker = new Id3Peeker();
isId3MetadataDisabled = (flags & FLAG_DISABLE_ID3_METADATA) != 0;
}
@Override
@ -125,24 +124,16 @@ public final class FlacExtractor implements Extractor {
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
if (input.getPosition() == 0) {
id3Metadata = peekId3Data(input);
id3SectionSize = input.getPeekPosition();
}
boolean isFlacFormat = peekFlacSignature(input);
if (isFlacFormat) {
// If this is FLAC format, we should skip the whole ID3 section.
skipFullyId3Section(input);
}
return isFlacFormat;
return peekFlacSignature(input);
}
@Override
public int read(final ExtractorInput input, PositionHolder seekPosition)
throws IOException, InterruptedException {
if (input.getPosition() == 0) {
if (input.getPosition() == 0 && !isId3MetadataDisabled && id3Metadata == null) {
id3Metadata = peekId3Data(input);
id3SectionSize = input.getPeekPosition();
}
skipFullyId3Section(input);
decoderJni.setData(input);
@ -155,7 +146,7 @@ public final class FlacExtractor implements Extractor {
}
} catch (IOException e) {
decoderJni.reset(0);
input.setRetryPosition(id3SectionSize, e);
input.setRetryPosition(0, e);
throw e; // never executes
}
metadataParsed = true;
@ -163,7 +154,7 @@ public final class FlacExtractor implements Extractor {
boolean isSeekable = decoderJni.getSeekPosition(0) != -1;
extractorOutput.seekMap(
isSeekable
? new FlacSeekMap(streamInfo.durationUs(), decoderJni, id3SectionSize)
? new FlacSeekMap(streamInfo.durationUs(), decoderJni)
: new SeekMap.Unseekable(streamInfo.durationUs(), 0));
Format mediaFormat =
Format.createAudioSampleFormat(
@ -181,7 +172,7 @@ public final class FlacExtractor implements Extractor {
/* drmInitData= */ null,
/* selectionFlags= */ 0,
/* language= */ null,
(flags & FLAG_DISABLE_ID3_METADATA) != 0 ? null : id3Metadata);
isId3MetadataDisabled ? null : id3Metadata);
trackOutput.format(mediaFormat);
outputBuffer = new ParsableByteArray(streamInfo.maxDecodedFrameSize());
@ -196,7 +187,7 @@ public final class FlacExtractor implements Extractor {
} catch (IOException e) {
if (lastDecodePosition >= 0) {
decoderJni.reset(lastDecodePosition);
input.setRetryPosition(id3SectionSize + lastDecodePosition, e);
input.setRetryPosition(lastDecodePosition, e);
}
throw e;
}
@ -212,12 +203,11 @@ public final class FlacExtractor implements Extractor {
@Override
public void seek(long position, long timeUs) {
if (position <= id3SectionSize) {
if (position == 0) {
metadataParsed = false;
}
long flacStreamPosition = Math.max(0, position - id3SectionSize);
if (decoderJni != null) {
decoderJni.reset(flacStreamPosition);
decoderJni.reset(position);
}
}
@ -238,9 +228,8 @@ public final class FlacExtractor implements Extractor {
@Nullable
private Metadata peekId3Data(ExtractorInput input) throws IOException, InterruptedException {
input.resetPeekPosition();
boolean disableId3Frames = (flags & FLAG_DISABLE_ID3_METADATA) != 0;
Id3Decoder.FramePredicate id3FramePredicate =
disableId3Frames ? Id3Decoder.NO_FRAMES_PREDICATE : null;
isId3MetadataDisabled ? Id3Decoder.NO_FRAMES_PREDICATE : null;
return id3Peeker.peekId3Data(input, id3FramePredicate);
}
@ -255,22 +244,14 @@ public final class FlacExtractor implements Extractor {
return Arrays.equals(header, FLAC_SIGNATURE);
}
/** Skips input until we have passed the whole Id3 section. */
private void skipFullyId3Section(ExtractorInput input) throws IOException, InterruptedException {
int bytesToSkip = Math.max(0, (int) (id3SectionSize - input.getPosition()));
input.skipFully(bytesToSkip);
}
private static final class FlacSeekMap implements SeekMap {
private final long durationUs;
private final FlacDecoderJni decoderJni;
private final long id3SectionSize;
public FlacSeekMap(long durationUs, FlacDecoderJni decoderJni, long id3SectionSize) {
public FlacSeekMap(long durationUs, FlacDecoderJni decoderJni) {
this.durationUs = durationUs;
this.decoderJni = decoderJni;
this.id3SectionSize = id3SectionSize;
}
@Override
@ -281,8 +262,7 @@ public final class FlacExtractor implements Extractor {
@Override
public SeekPoints getSeekPoints(long timeUs) {
// TODO: Access the seek table via JNI to return two seek points when appropriate.
return new SeekPoints(
new SeekPoint(timeUs, id3SectionSize + decoderJni.getSeekPosition(timeUs)));
return new SeekPoints(new SeekPoint(timeUs, decoderJni.getSeekPosition(timeUs)));
}
@Override