mirror of
https://github.com/androidx/media.git
synced 2025-05-07 15:40:37 +08:00
Provide access to original media timestamps in AudioSink.
* Add `setOutputStreamOffsetUs(long)` method in `AudioSink`. * Add private methods `setOutputStreamOffsetUs(long)` method in `MediaCodecRenderer` and `DecoderAudioRenderer`. * Add protected method `onOutputStreamOffsetUs(long)` method in `MediaCodecRenderer`, in which: * `MediaCodecRenderer` itself will be no-op for this method. * `MediaCodecAudioRenderer` will propagate this value to its `audioSink`. * Add logics in `DecoderAudioRenderer` to calculate `outputStreamOffsetUs`. PiperOrigin-RevId: 479265429 (cherry picked from commit 5bff8623748a1ce9e411cfa9df171143925da89f)
This commit is contained in:
parent
1cfeddc369
commit
243c29a8ad
@ -431,6 +431,14 @@ public interface AudioSink {
|
|||||||
@RequiresApi(23)
|
@RequiresApi(23)
|
||||||
default void setPreferredDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {}
|
default void setPreferredDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the offset that is added to the media timestamp before it is passed as {@code
|
||||||
|
* presentationTimeUs} in {@link #handleBuffer(ByteBuffer, long, int)}.
|
||||||
|
*
|
||||||
|
* @param outputStreamOffsetUs The output stream offset in microseconds.
|
||||||
|
*/
|
||||||
|
default void setOutputStreamOffsetUs(long outputStreamOffsetUs) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables tunneling, if possible. The sink is reset if tunneling was previously disabled.
|
* Enables tunneling, if possible. The sink is reset if tunneling was previously disabled.
|
||||||
* Enabling tunneling is only possible if the sink is based on a platform {@link AudioTrack}, and
|
* Enabling tunneling is only possible if the sink is based on a platform {@link AudioTrack}, and
|
||||||
|
@ -122,6 +122,11 @@ public abstract class DecoderAudioRenderer<
|
|||||||
* end of stream signal to indicate that it has output any remaining buffers before we release it.
|
* end of stream signal to indicate that it has output any remaining buffers before we release it.
|
||||||
*/
|
*/
|
||||||
private static final int REINITIALIZATION_STATE_WAIT_END_OF_STREAM = 2;
|
private static final int REINITIALIZATION_STATE_WAIT_END_OF_STREAM = 2;
|
||||||
|
/**
|
||||||
|
* Generally there is zero or one pending output stream offset. We track more offsets to allow for
|
||||||
|
* pending output streams that have fewer frames than the codec latency.
|
||||||
|
*/
|
||||||
|
private static final int MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT = 10;
|
||||||
|
|
||||||
private final EventDispatcher eventDispatcher;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final AudioSink audioSink;
|
private final AudioSink audioSink;
|
||||||
@ -151,6 +156,9 @@ public abstract class DecoderAudioRenderer<
|
|||||||
private boolean allowPositionDiscontinuity;
|
private boolean allowPositionDiscontinuity;
|
||||||
private boolean inputStreamEnded;
|
private boolean inputStreamEnded;
|
||||||
private boolean outputStreamEnded;
|
private boolean outputStreamEnded;
|
||||||
|
private long outputStreamOffsetUs;
|
||||||
|
private final long[] pendingOutputStreamOffsetsUs;
|
||||||
|
private int pendingOutputStreamOffsetCount;
|
||||||
|
|
||||||
public DecoderAudioRenderer() {
|
public DecoderAudioRenderer() {
|
||||||
this(/* eventHandler= */ null, /* eventListener= */ null);
|
this(/* eventHandler= */ null, /* eventListener= */ null);
|
||||||
@ -210,6 +218,8 @@ public abstract class DecoderAudioRenderer<
|
|||||||
flagsOnlyBuffer = DecoderInputBuffer.newNoDataInstance();
|
flagsOnlyBuffer = DecoderInputBuffer.newNoDataInstance();
|
||||||
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
|
decoderReinitializationState = REINITIALIZATION_STATE_NONE;
|
||||||
audioTrackNeedsConfigure = true;
|
audioTrackNeedsConfigure = true;
|
||||||
|
setOutputStreamOffsetUs(C.TIME_UNSET);
|
||||||
|
pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -394,7 +404,7 @@ public abstract class DecoderAudioRenderer<
|
|||||||
audioSink.handleDiscontinuity();
|
audioSink.handleDiscontinuity();
|
||||||
}
|
}
|
||||||
if (outputBuffer.isFirstSample()) {
|
if (outputBuffer.isFirstSample()) {
|
||||||
audioSink.handleDiscontinuity();
|
processFirstSampleOfStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,6 +450,27 @@ public abstract class DecoderAudioRenderer<
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processFirstSampleOfStream() {
|
||||||
|
audioSink.handleDiscontinuity();
|
||||||
|
if (pendingOutputStreamOffsetCount != 0) {
|
||||||
|
setOutputStreamOffsetUs(pendingOutputStreamOffsetsUs[0]);
|
||||||
|
pendingOutputStreamOffsetCount--;
|
||||||
|
System.arraycopy(
|
||||||
|
pendingOutputStreamOffsetsUs,
|
||||||
|
/* srcPos= */ 1,
|
||||||
|
pendingOutputStreamOffsetsUs,
|
||||||
|
/* destPos= */ 0,
|
||||||
|
pendingOutputStreamOffsetCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setOutputStreamOffsetUs(long outputStreamOffsetUs) {
|
||||||
|
this.outputStreamOffsetUs = outputStreamOffsetUs;
|
||||||
|
if (outputStreamOffsetUs != C.TIME_UNSET) {
|
||||||
|
audioSink.setOutputStreamOffsetUs(outputStreamOffsetUs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean feedInputBuffer() throws DecoderException, ExoPlaybackException {
|
private boolean feedInputBuffer() throws DecoderException, ExoPlaybackException {
|
||||||
if (decoder == null
|
if (decoder == null
|
||||||
|| decoderReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM
|
|| decoderReinitializationState == REINITIALIZATION_STATE_WAIT_END_OF_STREAM
|
||||||
@ -589,6 +620,7 @@ public abstract class DecoderAudioRenderer<
|
|||||||
protected void onDisabled() {
|
protected void onDisabled() {
|
||||||
inputFormat = null;
|
inputFormat = null;
|
||||||
audioTrackNeedsConfigure = true;
|
audioTrackNeedsConfigure = true;
|
||||||
|
setOutputStreamOffsetUs(C.TIME_UNSET);
|
||||||
try {
|
try {
|
||||||
setSourceDrmSession(null);
|
setSourceDrmSession(null);
|
||||||
releaseDecoder();
|
releaseDecoder();
|
||||||
@ -603,6 +635,19 @@ public abstract class DecoderAudioRenderer<
|
|||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
super.onStreamChanged(formats, startPositionUs, offsetUs);
|
super.onStreamChanged(formats, startPositionUs, offsetUs);
|
||||||
firstStreamSampleRead = false;
|
firstStreamSampleRead = false;
|
||||||
|
if (outputStreamOffsetUs == C.TIME_UNSET) {
|
||||||
|
setOutputStreamOffsetUs(offsetUs);
|
||||||
|
} else {
|
||||||
|
if (pendingOutputStreamOffsetCount == pendingOutputStreamOffsetsUs.length) {
|
||||||
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"Too many stream changes, so dropping offset: "
|
||||||
|
+ pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]);
|
||||||
|
} else {
|
||||||
|
pendingOutputStreamOffsetCount++;
|
||||||
|
}
|
||||||
|
pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1] = offsetUs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -146,6 +146,11 @@ public class ForwardingAudioSink implements AudioSink {
|
|||||||
sink.setPreferredDevice(audioDeviceInfo);
|
sink.setPreferredDevice(audioDeviceInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOutputStreamOffsetUs(long outputStreamOffsetUs) {
|
||||||
|
sink.setOutputStreamOffsetUs(outputStreamOffsetUs);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableTunnelingV21() {
|
public void enableTunnelingV21() {
|
||||||
sink.enableTunnelingV21();
|
sink.enableTunnelingV21();
|
||||||
|
@ -737,6 +737,11 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onOutputStreamOffsetUsChanged(long outputStreamOffsetUs) {
|
||||||
|
audioSink.setOutputStreamOffsetUs(outputStreamOffsetUs);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(@MessageType int messageType, @Nullable Object message)
|
public void handleMessage(@MessageType int messageType, @Nullable Object message)
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
|
@ -402,7 +402,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
|
pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
|
||||||
pendingOutputStreamSwitchTimesUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
|
pendingOutputStreamSwitchTimesUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
|
||||||
outputStreamStartPositionUs = C.TIME_UNSET;
|
outputStreamStartPositionUs = C.TIME_UNSET;
|
||||||
outputStreamOffsetUs = C.TIME_UNSET;
|
setOutputStreamOffsetUs(C.TIME_UNSET);
|
||||||
// MediaCodec outputs audio buffers in native endian:
|
// MediaCodec outputs audio buffers in native endian:
|
||||||
// https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers
|
// https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers
|
||||||
// and code called from MediaCodecAudioRenderer.processOutputBuffer expects this endianness.
|
// and code called from MediaCodecAudioRenderer.processOutputBuffer expects this endianness.
|
||||||
@ -651,7 +651,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
if (this.outputStreamOffsetUs == C.TIME_UNSET) {
|
if (this.outputStreamOffsetUs == C.TIME_UNSET) {
|
||||||
checkState(this.outputStreamStartPositionUs == C.TIME_UNSET);
|
checkState(this.outputStreamStartPositionUs == C.TIME_UNSET);
|
||||||
this.outputStreamStartPositionUs = startPositionUs;
|
this.outputStreamStartPositionUs = startPositionUs;
|
||||||
this.outputStreamOffsetUs = offsetUs;
|
setOutputStreamOffsetUs(offsetUs);
|
||||||
} else {
|
} else {
|
||||||
if (pendingOutputStreamOffsetCount == pendingOutputStreamOffsetsUs.length) {
|
if (pendingOutputStreamOffsetCount == pendingOutputStreamOffsetsUs.length) {
|
||||||
Log.w(
|
Log.w(
|
||||||
@ -688,7 +688,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
}
|
}
|
||||||
formatQueue.clear();
|
formatQueue.clear();
|
||||||
if (pendingOutputStreamOffsetCount != 0) {
|
if (pendingOutputStreamOffsetCount != 0) {
|
||||||
outputStreamOffsetUs = pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1];
|
setOutputStreamOffsetUs(pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]);
|
||||||
outputStreamStartPositionUs =
|
outputStreamStartPositionUs =
|
||||||
pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1];
|
pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1];
|
||||||
pendingOutputStreamOffsetCount = 0;
|
pendingOutputStreamOffsetCount = 0;
|
||||||
@ -707,7 +707,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
protected void onDisabled() {
|
protected void onDisabled() {
|
||||||
inputFormat = null;
|
inputFormat = null;
|
||||||
outputStreamStartPositionUs = C.TIME_UNSET;
|
outputStreamStartPositionUs = C.TIME_UNSET;
|
||||||
outputStreamOffsetUs = C.TIME_UNSET;
|
setOutputStreamOffsetUs(C.TIME_UNSET);
|
||||||
pendingOutputStreamOffsetCount = 0;
|
pendingOutputStreamOffsetCount = 0;
|
||||||
flushOrReleaseCodec();
|
flushOrReleaseCodec();
|
||||||
}
|
}
|
||||||
@ -1588,7 +1588,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
while (pendingOutputStreamOffsetCount != 0
|
while (pendingOutputStreamOffsetCount != 0
|
||||||
&& presentationTimeUs >= pendingOutputStreamSwitchTimesUs[0]) {
|
&& presentationTimeUs >= pendingOutputStreamSwitchTimesUs[0]) {
|
||||||
outputStreamStartPositionUs = pendingOutputStreamStartPositionsUs[0];
|
outputStreamStartPositionUs = pendingOutputStreamStartPositionsUs[0];
|
||||||
outputStreamOffsetUs = pendingOutputStreamOffsetsUs[0];
|
setOutputStreamOffsetUs(pendingOutputStreamOffsetsUs[0]);
|
||||||
pendingOutputStreamOffsetCount--;
|
pendingOutputStreamOffsetCount--;
|
||||||
System.arraycopy(
|
System.arraycopy(
|
||||||
pendingOutputStreamStartPositionsUs,
|
pendingOutputStreamStartPositionsUs,
|
||||||
@ -1638,6 +1638,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
DISCARD_REASON_REUSE_NOT_IMPLEMENTED);
|
DISCARD_REASON_REUSE_NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after the output stream offset changes.
|
||||||
|
*
|
||||||
|
* <p>The default implementation is a no-op.
|
||||||
|
*
|
||||||
|
* @param outputStreamOffsetUs The output stream offset in microseconds.
|
||||||
|
*/
|
||||||
|
protected void onOutputStreamOffsetUsChanged(long outputStreamOffsetUs) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnded() {
|
public boolean isEnded() {
|
||||||
return outputStreamEnded;
|
return outputStreamEnded;
|
||||||
@ -2046,6 +2057,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
|||||||
return outputStreamOffsetUs;
|
return outputStreamOffsetUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setOutputStreamOffsetUs(long outputStreamOffsetUs) {
|
||||||
|
this.outputStreamOffsetUs = outputStreamOffsetUs;
|
||||||
|
if (outputStreamOffsetUs != C.TIME_UNSET) {
|
||||||
|
onOutputStreamOffsetUsChanged(outputStreamOffsetUs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns whether this renderer supports the given {@link Format Format's} DRM scheme. */
|
/** Returns whether this renderer supports the given {@link Format Format's} DRM scheme. */
|
||||||
protected static boolean supportsFormatDrm(Format format) {
|
protected static boolean supportsFormatDrm(Format format) {
|
||||||
return format.cryptoType == C.CRYPTO_TYPE_NONE || format.cryptoType == C.CRYPTO_TYPE_FRAMEWORK;
|
return format.cryptoType == C.CRYPTO_TYPE_NONE || format.cryptoType == C.CRYPTO_TYPE_FRAMEWORK;
|
||||||
|
@ -146,7 +146,8 @@ public class DecoderAudioRendererTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void firstSampleOfStreamSignalsDiscontinuityToAudioSink() throws Exception {
|
public void firstSampleOfStreamSignalsDiscontinuityAndSetOutputStreamOffsetToAudioSink()
|
||||||
|
throws Exception {
|
||||||
when(mockAudioSink.handleBuffer(any(), anyLong(), anyInt())).thenReturn(true);
|
when(mockAudioSink.handleBuffer(any(), anyLong(), anyInt())).thenReturn(true);
|
||||||
when(mockAudioSink.isEnded()).thenReturn(true);
|
when(mockAudioSink.isEnded()).thenReturn(true);
|
||||||
InOrder inOrderAudioSink = inOrder(mockAudioSink);
|
InOrder inOrderAudioSink = inOrder(mockAudioSink);
|
||||||
@ -177,12 +178,15 @@ public class DecoderAudioRendererTest {
|
|||||||
audioRenderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
audioRenderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inOrderAudioSink.verify(mockAudioSink, times(1)).setOutputStreamOffsetUs(0);
|
||||||
inOrderAudioSink.verify(mockAudioSink, times(1)).handleDiscontinuity();
|
inOrderAudioSink.verify(mockAudioSink, times(1)).handleDiscontinuity();
|
||||||
inOrderAudioSink.verify(mockAudioSink, times(2)).handleBuffer(any(), anyLong(), anyInt());
|
inOrderAudioSink.verify(mockAudioSink, times(2)).handleBuffer(any(), anyLong(), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void firstSampleOfReplacementStreamSignalsDiscontinuityToAudioSink() throws Exception {
|
public void
|
||||||
|
firstSampleOfReplacementStreamSignalsDiscontinuityAndSetOutputStreamOffsetToAudioSink()
|
||||||
|
throws Exception {
|
||||||
when(mockAudioSink.handleBuffer(any(), anyLong(), anyInt())).thenReturn(true);
|
when(mockAudioSink.handleBuffer(any(), anyLong(), anyInt())).thenReturn(true);
|
||||||
when(mockAudioSink.isEnded()).thenReturn(true);
|
when(mockAudioSink.isEnded()).thenReturn(true);
|
||||||
InOrder inOrderAudioSink = inOrder(mockAudioSink);
|
InOrder inOrderAudioSink = inOrder(mockAudioSink);
|
||||||
@ -233,9 +237,11 @@ public class DecoderAudioRendererTest {
|
|||||||
audioRenderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
audioRenderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inOrderAudioSink.verify(mockAudioSink, times(1)).setOutputStreamOffsetUs(0);
|
||||||
inOrderAudioSink.verify(mockAudioSink, times(1)).handleDiscontinuity();
|
inOrderAudioSink.verify(mockAudioSink, times(1)).handleDiscontinuity();
|
||||||
inOrderAudioSink.verify(mockAudioSink, times(2)).handleBuffer(any(), anyLong(), anyInt());
|
inOrderAudioSink.verify(mockAudioSink, times(2)).handleBuffer(any(), anyLong(), anyInt());
|
||||||
inOrderAudioSink.verify(mockAudioSink, times(1)).handleDiscontinuity();
|
inOrderAudioSink.verify(mockAudioSink, times(1)).handleDiscontinuity();
|
||||||
|
inOrderAudioSink.verify(mockAudioSink, times(1)).setOutputStreamOffsetUs(1_000_000);
|
||||||
inOrderAudioSink.verify(mockAudioSink, times(2)).handleBuffer(any(), anyLong(), anyInt());
|
inOrderAudioSink.verify(mockAudioSink, times(2)).handleBuffer(any(), anyLong(), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any;
|
|||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.robolectric.Shadows.shadowOf;
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
@ -57,6 +58,7 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.InOrder;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
@ -324,6 +326,61 @@ public class MediaCodecAudioRendererTest {
|
|||||||
verify(audioRendererEventListener).onAudioSinkError(error);
|
verify(audioRendererEventListener).onAudioSinkError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void render_callsAudioSinkSetOutputStreamOffset_whenReplaceStream() throws Exception {
|
||||||
|
FakeSampleStream fakeSampleStream1 =
|
||||||
|
new FakeSampleStream(
|
||||||
|
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
|
||||||
|
/* mediaSourceEventDispatcher= */ null,
|
||||||
|
DrmSessionManager.DRM_UNSUPPORTED,
|
||||||
|
new DrmSessionEventListener.EventDispatcher(),
|
||||||
|
AUDIO_AAC,
|
||||||
|
ImmutableList.of(
|
||||||
|
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME),
|
||||||
|
oneByteSample(/* timeUs= */ 1_000),
|
||||||
|
END_OF_STREAM_ITEM));
|
||||||
|
fakeSampleStream1.writeData(/* startPositionUs= */ 0);
|
||||||
|
FakeSampleStream fakeSampleStream2 =
|
||||||
|
new FakeSampleStream(
|
||||||
|
new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
|
||||||
|
/* mediaSourceEventDispatcher= */ null,
|
||||||
|
DrmSessionManager.DRM_UNSUPPORTED,
|
||||||
|
new DrmSessionEventListener.EventDispatcher(),
|
||||||
|
AUDIO_AAC,
|
||||||
|
ImmutableList.of(
|
||||||
|
oneByteSample(/* timeUs= */ 1_000_000, C.BUFFER_FLAG_KEY_FRAME),
|
||||||
|
oneByteSample(/* timeUs= */ 1_001_000),
|
||||||
|
END_OF_STREAM_ITEM));
|
||||||
|
fakeSampleStream2.writeData(/* startPositionUs= */ 0);
|
||||||
|
mediaCodecAudioRenderer.enable(
|
||||||
|
RendererConfiguration.DEFAULT,
|
||||||
|
new Format[] {AUDIO_AAC},
|
||||||
|
fakeSampleStream1,
|
||||||
|
/* positionUs= */ 0,
|
||||||
|
/* joining= */ false,
|
||||||
|
/* mayRenderStartOfStream= */ true,
|
||||||
|
/* startPositionUs= */ 0,
|
||||||
|
/* offsetUs= */ 0);
|
||||||
|
|
||||||
|
mediaCodecAudioRenderer.start();
|
||||||
|
while (!mediaCodecAudioRenderer.hasReadStreamToEnd()) {
|
||||||
|
mediaCodecAudioRenderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
||||||
|
}
|
||||||
|
mediaCodecAudioRenderer.replaceStream(
|
||||||
|
new Format[] {AUDIO_AAC},
|
||||||
|
fakeSampleStream2,
|
||||||
|
/* startPositionUs= */ 1_000_000,
|
||||||
|
/* offsetUs= */ 1_000_000);
|
||||||
|
mediaCodecAudioRenderer.setCurrentStreamFinal();
|
||||||
|
while (!mediaCodecAudioRenderer.isEnded()) {
|
||||||
|
mediaCodecAudioRenderer.render(/* positionUs= */ 0, /* elapsedRealtimeUs= */ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
InOrder inOrderAudioSink = inOrder(audioSink);
|
||||||
|
inOrderAudioSink.verify(audioSink).setOutputStreamOffsetUs(0);
|
||||||
|
inOrderAudioSink.verify(audioSink).setOutputStreamOffsetUs(1_000_000);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void supportsFormat_withEac3JocMediaAndEac3Decoder_returnsTrue() throws Exception {
|
public void supportsFormat_withEac3JocMediaAndEac3Decoder_returnsTrue() throws Exception {
|
||||||
Format mediaFormat =
|
Format mediaFormat =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user