mirror of
https://github.com/androidx/media.git
synced 2025-05-15 19:49:50 +08:00
Limit processing Opus decode-only frames by seek-preroll in offload
As Opus decoders skip some bytes prior to playback during a seek, the renderer for bypass playback should send samples to the decoder even if they would be decode-only. However, the renderer should not send samples with time preceding that range. This change adds that constraint. #minor-release PiperOrigin-RevId: 588014983
This commit is contained in:
parent
b1541b096f
commit
d1e38abf93
@ -2307,10 +2307,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
// Process any batched data.
|
// Process any batched data.
|
||||||
checkState(!outputStreamEnded);
|
checkState(!outputStreamEnded);
|
||||||
if (bypassBatchBuffer.hasSamples()) {
|
if (bypassBatchBuffer.hasSamples()) {
|
||||||
boolean isDecodeOnly =
|
|
||||||
bypassBatchBuffer.getLastSampleTimeUs() < getLastResetPositionUs()
|
|
||||||
&& (outputFormat == null
|
|
||||||
|| !Objects.equals(outputFormat.sampleMimeType, MimeTypes.AUDIO_OPUS));
|
|
||||||
if (processOutputBuffer(
|
if (processOutputBuffer(
|
||||||
positionUs,
|
positionUs,
|
||||||
elapsedRealtimeUs,
|
elapsedRealtimeUs,
|
||||||
@ -2320,7 +2316,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
/* bufferFlags= */ 0,
|
/* bufferFlags= */ 0,
|
||||||
bypassBatchBuffer.getSampleCount(),
|
bypassBatchBuffer.getSampleCount(),
|
||||||
bypassBatchBuffer.getFirstSampleTimeUs(),
|
bypassBatchBuffer.getFirstSampleTimeUs(),
|
||||||
isDecodeOnly,
|
isDecodeOnly(getLastResetPositionUs(), bypassBatchBuffer.getLastSampleTimeUs()),
|
||||||
bypassBatchBuffer.isEndOfStream(),
|
bypassBatchBuffer.isEndOfStream(),
|
||||||
checkNotNull(outputFormat))) {
|
checkNotNull(outputFormat))) {
|
||||||
// The batch buffer has been fully processed.
|
// The batch buffer has been fully processed.
|
||||||
@ -2417,8 +2413,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
bypassSampleBuffer.format = outputFormat;
|
bypassSampleBuffer.format = outputFormat;
|
||||||
handleInputBufferSupplementalData(bypassSampleBuffer);
|
handleInputBufferSupplementalData(bypassSampleBuffer);
|
||||||
}
|
}
|
||||||
oggOpusAudioPacketizer.packetize(
|
if (OpusUtil.needToDecodeOpusFrame(
|
||||||
bypassSampleBuffer, checkNotNull(outputFormat).initializationData);
|
getLastResetPositionUs(), bypassSampleBuffer.timeUs)) {
|
||||||
|
// Packetize as long as frame does not precede the last reset position by more than
|
||||||
|
// seek-preroll.
|
||||||
|
oggOpusAudioPacketizer.packetize(
|
||||||
|
bypassSampleBuffer, checkNotNull(outputFormat).initializationData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!haveBypassBatchBufferAndNewSampleSameDecodeOnlyState()
|
if (!haveBypassBatchBufferAndNewSampleSameDecodeOnlyState()
|
||||||
|| !bypassBatchBuffer.append(bypassSampleBuffer)) {
|
|| !bypassBatchBuffer.append(bypassSampleBuffer)) {
|
||||||
@ -2440,11 +2441,31 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
long lastResetPositionUs = getLastResetPositionUs();
|
long lastResetPositionUs = getLastResetPositionUs();
|
||||||
boolean batchBufferIsDecodeOnly = bypassBatchBuffer.getLastSampleTimeUs() < lastResetPositionUs;
|
boolean batchBufferIsDecodeOnly =
|
||||||
boolean sampleBufferIsDecodeOnly = bypassSampleBuffer.timeUs < lastResetPositionUs;
|
isDecodeOnly(lastResetPositionUs, bypassBatchBuffer.getLastSampleTimeUs());
|
||||||
|
boolean sampleBufferIsDecodeOnly = isDecodeOnly(lastResetPositionUs, bypassSampleBuffer.timeUs);
|
||||||
return batchBufferIsDecodeOnly == sampleBufferIsDecodeOnly;
|
return batchBufferIsDecodeOnly == sampleBufferIsDecodeOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if based on target play time and frame time that the frame should be decode-only.
|
||||||
|
*
|
||||||
|
* <p>If the format is Opus, then the frame is decode-only if the frame time precedes the start
|
||||||
|
* time by more than seek-preroll.
|
||||||
|
*
|
||||||
|
* @param startTimeUs The time to start playing at.
|
||||||
|
* @param frameTimeUs The time of the sample.
|
||||||
|
* @return Whether the frame is decode-only.
|
||||||
|
*/
|
||||||
|
private boolean isDecodeOnly(long startTimeUs, long frameTimeUs) {
|
||||||
|
// Opus frames that precede the target position by more than seek-preroll should be skipped.
|
||||||
|
return frameTimeUs < startTimeUs
|
||||||
|
&& (outputFormat == null
|
||||||
|
|| !Objects.equals(outputFormat.sampleMimeType, MimeTypes.AUDIO_OPUS)
|
||||||
|
|| !OpusUtil.needToDecodeOpusFrame(
|
||||||
|
/* startTimeUs= */ startTimeUs, /* frameTimeUs= */ frameTimeUs));
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isMediaCodecException(IllegalStateException error) {
|
private static boolean isMediaCodecException(IllegalStateException error) {
|
||||||
if (Util.SDK_INT >= 21 && isMediaCodecExceptionV21(error)) {
|
if (Util.SDK_INT >= 21 && isMediaCodecExceptionV21(error)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -149,6 +149,23 @@ public class OpusUtil {
|
|||||||
return ((header[11] & 0xFF) << 8) | (header[10] & 0xFF);
|
return ((header[11] & 0xFF) << 8) | (header[10] & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether an Opus frame should be sent to the decoder as it is either past the start
|
||||||
|
* position or within the seek-preroll duration.
|
||||||
|
*
|
||||||
|
* <p>The measure of whether an Opus frame should not be decoded is if its time precedes the start
|
||||||
|
* position by more than the default seek-preroll value.
|
||||||
|
*
|
||||||
|
* @param startTimeUs The time to start playing at.
|
||||||
|
* @param frameTimeUs The time of the Opus sample.
|
||||||
|
* @return Whether the frame should be decoded.
|
||||||
|
*/
|
||||||
|
public static boolean needToDecodeOpusFrame(long startTimeUs, long frameTimeUs) {
|
||||||
|
// Divide by 1000 in rhs value to convert nanoseconds to microseconds.
|
||||||
|
return (startTimeUs - frameTimeUs)
|
||||||
|
<= (sampleCountToNanoseconds(DEFAULT_SEEK_PRE_ROLL_SAMPLES) / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
private static long getPacketDurationUs(byte packetByte0, byte packetByte1) {
|
private static long getPacketDurationUs(byte packetByte0, byte packetByte1) {
|
||||||
// See RFC6716, Sections 3.1 and 3.2.
|
// See RFC6716, Sections 3.1 and 3.2.
|
||||||
int toc = packetByte0 & 0xFF;
|
int toc = packetByte0 & 0xFF;
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
SinkDump (OggOpus):
|
SinkDump (OggOpus):
|
||||||
buffers.length = 6
|
buffers.length = 5
|
||||||
buffers[0] = length 207, hash D462AF66
|
buffers[0] = length 3979, hash 63A08FAD
|
||||||
buffers[1] = length 3891, hash FE9EE7C1
|
buffers[1] = length 3696, hash ABE4B611
|
||||||
buffers[2] = length 3732, hash C2249BC1
|
buffers[2] = length 3770, hash 67EAAA4
|
||||||
buffers[3] = length 3731, hash A9384B0F
|
buffers[3] = length 4057, hash C53B8DF3
|
||||||
buffers[4] = length 4091, hash 9631FA86
|
buffers[4] = length 926, hash B21B9A24
|
||||||
buffers[5] = length 776, hash 4BC27E65
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user