mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46: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 (cherry picked from commit d1e38abf93353af1bc3fb2d9a0dfbac01e387f3e)
This commit is contained in:
parent
57187aa899
commit
03eae4f9ac
@ -2295,10 +2295,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
// Process any batched data.
|
||||
checkState(!outputStreamEnded);
|
||||
if (bypassBatchBuffer.hasSamples()) {
|
||||
boolean isDecodeOnly =
|
||||
bypassBatchBuffer.getLastSampleTimeUs() < getLastResetPositionUs()
|
||||
&& (outputFormat == null
|
||||
|| !Objects.equals(outputFormat.sampleMimeType, MimeTypes.AUDIO_OPUS));
|
||||
if (processOutputBuffer(
|
||||
positionUs,
|
||||
elapsedRealtimeUs,
|
||||
@ -2308,7 +2304,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
/* bufferFlags= */ 0,
|
||||
bypassBatchBuffer.getSampleCount(),
|
||||
bypassBatchBuffer.getFirstSampleTimeUs(),
|
||||
isDecodeOnly,
|
||||
isDecodeOnly(getLastResetPositionUs(), bypassBatchBuffer.getLastSampleTimeUs()),
|
||||
bypassBatchBuffer.isEndOfStream(),
|
||||
checkNotNull(outputFormat))) {
|
||||
// The batch buffer has been fully processed.
|
||||
@ -2405,9 +2401,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
bypassSampleBuffer.format = outputFormat;
|
||||
handleInputBufferSupplementalData(bypassSampleBuffer);
|
||||
}
|
||||
if (OpusUtil.needToDecodeOpusFrame(
|
||||
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()
|
||||
|| !bypassBatchBuffer.append(bypassSampleBuffer)) {
|
||||
bypassSampleBufferPending = true;
|
||||
@ -2428,11 +2429,31 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
return true;
|
||||
}
|
||||
long lastResetPositionUs = getLastResetPositionUs();
|
||||
boolean batchBufferIsDecodeOnly = bypassBatchBuffer.getLastSampleTimeUs() < lastResetPositionUs;
|
||||
boolean sampleBufferIsDecodeOnly = bypassSampleBuffer.timeUs < lastResetPositionUs;
|
||||
boolean batchBufferIsDecodeOnly =
|
||||
isDecodeOnly(lastResetPositionUs, bypassBatchBuffer.getLastSampleTimeUs());
|
||||
boolean sampleBufferIsDecodeOnly = isDecodeOnly(lastResetPositionUs, bypassSampleBuffer.timeUs);
|
||||
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) {
|
||||
if (Util.SDK_INT >= 21 && isMediaCodecExceptionV21(error)) {
|
||||
return true;
|
||||
|
@ -149,6 +149,23 @@ public class OpusUtil {
|
||||
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) {
|
||||
// See RFC6716, Sections 3.1 and 3.2.
|
||||
int toc = packetByte0 & 0xFF;
|
||||
|
@ -1,8 +1,7 @@
|
||||
SinkDump (OggOpus):
|
||||
buffers.length = 6
|
||||
buffers[0] = length 207, hash D462AF66
|
||||
buffers[1] = length 3891, hash FE9EE7C1
|
||||
buffers[2] = length 3732, hash C2249BC1
|
||||
buffers[3] = length 3731, hash A9384B0F
|
||||
buffers[4] = length 4091, hash 9631FA86
|
||||
buffers[5] = length 776, hash 4BC27E65
|
||||
buffers.length = 5
|
||||
buffers[0] = length 3979, hash 63A08FAD
|
||||
buffers[1] = length 3696, hash ABE4B611
|
||||
buffers[2] = length 3770, hash 67EAAA4
|
||||
buffers[3] = length 4057, hash C53B8DF3
|
||||
buffers[4] = length 926, hash B21B9A24
|
||||
|
Loading…
x
Reference in New Issue
Block a user