Snap to frame boundary in ConstantBitrateSeeker
- This change snaps the seek position for constant bitrate MP3s to the nearest frame boundary, avoiding the need to skip one byte at a time to re-synchronize (this may still happen if the MP3 does not really have fixed size frames). - Tweaked both ConstantBitrateSeeker and WavHeader to ensure the returned positions are valid. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=177441798
This commit is contained in:
parent
21d55d4eba
commit
cc54d4d3e6
@ -25,5 +25,9 @@ track 0:
|
|||||||
language = null
|
language = null
|
||||||
drmInitData = -
|
drmInitData = -
|
||||||
initializationData:
|
initializationData:
|
||||||
sample count = 0
|
sample count = 1
|
||||||
|
sample 0:
|
||||||
|
time = 0
|
||||||
|
flags = 1
|
||||||
|
data = length 418, hash B819987
|
||||||
tracksEnded = true
|
tracksEnded = true
|
||||||
|
@ -25,5 +25,9 @@ track 0:
|
|||||||
language = null
|
language = null
|
||||||
drmInitData = -
|
drmInitData = -
|
||||||
initializationData:
|
initializationData:
|
||||||
sample count = 0
|
sample count = 1
|
||||||
|
sample 0:
|
||||||
|
time = 0
|
||||||
|
flags = 1
|
||||||
|
data = length 418, hash B819987
|
||||||
tracksEnded = true
|
tracksEnded = true
|
||||||
|
@ -25,5 +25,9 @@ track 0:
|
|||||||
language = null
|
language = null
|
||||||
drmInitData = -
|
drmInitData = -
|
||||||
initializationData:
|
initializationData:
|
||||||
sample count = 0
|
sample count = 1
|
||||||
|
sample 0:
|
||||||
|
time = 0
|
||||||
|
flags = 1
|
||||||
|
data = length 418, hash B819987
|
||||||
tracksEnded = true
|
tracksEnded = true
|
||||||
|
@ -26,27 +26,47 @@ import com.google.android.exoplayer2.util.Util;
|
|||||||
private static final int BITS_PER_BYTE = 8;
|
private static final int BITS_PER_BYTE = 8;
|
||||||
|
|
||||||
private final long firstFramePosition;
|
private final long firstFramePosition;
|
||||||
|
private final long dataSize;
|
||||||
|
private final int frameSize;
|
||||||
private final int bitrate;
|
private final int bitrate;
|
||||||
private final long durationUs;
|
private final long durationUs;
|
||||||
|
|
||||||
public ConstantBitrateSeeker(long firstFramePosition, int bitrate, long inputLength) {
|
/**
|
||||||
|
* @param firstFramePosition The position (byte offset) of the first frame.
|
||||||
|
* @param inputLength The length of the stream.
|
||||||
|
* @param frameSize The size of a single frame in the stream.
|
||||||
|
* @param bitrate The stream's bitrate.
|
||||||
|
*/
|
||||||
|
public ConstantBitrateSeeker(long firstFramePosition, long inputLength, int frameSize,
|
||||||
|
int bitrate) {
|
||||||
this.firstFramePosition = firstFramePosition;
|
this.firstFramePosition = firstFramePosition;
|
||||||
|
this.frameSize = frameSize;
|
||||||
this.bitrate = bitrate;
|
this.bitrate = bitrate;
|
||||||
durationUs = inputLength == C.LENGTH_UNSET ? C.TIME_UNSET : getTimeUs(inputLength);
|
if (inputLength == C.LENGTH_UNSET) {
|
||||||
|
dataSize = C.LENGTH_UNSET;
|
||||||
|
durationUs = C.TIME_UNSET;
|
||||||
|
} else {
|
||||||
|
dataSize = inputLength - firstFramePosition;
|
||||||
|
durationUs = getTimeUs(inputLength);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSeekable() {
|
public boolean isSeekable() {
|
||||||
return durationUs != C.TIME_UNSET;
|
return dataSize != C.LENGTH_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getPosition(long timeUs) {
|
public long getPosition(long timeUs) {
|
||||||
if (durationUs == C.TIME_UNSET) {
|
if (dataSize == C.LENGTH_UNSET) {
|
||||||
return firstFramePosition;
|
return firstFramePosition;
|
||||||
}
|
}
|
||||||
timeUs = Util.constrainValue(timeUs, 0, durationUs);
|
long positionOffset = (timeUs * bitrate) / (C.MICROS_PER_SECOND * BITS_PER_BYTE);
|
||||||
return firstFramePosition + (timeUs * bitrate) / (C.MICROS_PER_SECOND * BITS_PER_BYTE);
|
// Constrain to nearest preceding frame offset.
|
||||||
|
positionOffset = (positionOffset / frameSize) * frameSize;
|
||||||
|
positionOffset = Util.constrainValue(positionOffset, 0, dataSize - frameSize);
|
||||||
|
// Add data start position.
|
||||||
|
return firstFramePosition + positionOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -393,8 +393,8 @@ public final class Mp3Extractor implements Extractor {
|
|||||||
input.peekFully(scratch.data, 0, 4);
|
input.peekFully(scratch.data, 0, 4);
|
||||||
scratch.setPosition(0);
|
scratch.setPosition(0);
|
||||||
MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
|
MpegAudioHeader.populateHeader(scratch.readInt(), synchronizedHeader);
|
||||||
return new ConstantBitrateSeeker(input.getPosition(), synchronizedHeader.bitrate,
|
return new ConstantBitrateSeeker(input.getPosition(), input.getLength(),
|
||||||
input.getLength());
|
synchronizedHeader.frameSize, synchronizedHeader.bitrate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.wav;
|
|||||||
|
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.extractor.SeekMap;
|
import com.google.android.exoplayer2.extractor.SeekMap;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
||||||
/** Header for a WAV file. */
|
/** Header for a WAV file. */
|
||||||
/* package */ final class WavHeader implements SeekMap {
|
/* package */ final class WavHeader implements SeekMap {
|
||||||
@ -83,10 +84,12 @@ import com.google.android.exoplayer2.extractor.SeekMap;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getPosition(long timeUs) {
|
public long getPosition(long timeUs) {
|
||||||
long unroundedPosition = (timeUs * averageBytesPerSecond) / C.MICROS_PER_SECOND;
|
long positionOffset = (timeUs * averageBytesPerSecond) / C.MICROS_PER_SECOND;
|
||||||
// Round down to nearest frame.
|
// Constrain to nearest preceding frame offset.
|
||||||
long position = (unroundedPosition / blockAlignment) * blockAlignment;
|
positionOffset = (positionOffset / blockAlignment) * blockAlignment;
|
||||||
return Math.min(position, dataSize - blockAlignment) + dataStartPosition;
|
positionOffset = Util.constrainValue(positionOffset, 0, dataSize - blockAlignment);
|
||||||
|
// Add data start position.
|
||||||
|
return dataStartPosition + positionOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Misc getters.
|
// Misc getters.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user