mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Have Sequence Player end as audio sink position is passed
The fix is to update `AudioGraphInputAudioSink.lastHandledPositionUs` when a buffer is handled, and end the `AudioGraphInputAudioSink` as the final audio sink plays out further than this position. PiperOrigin-RevId: 718901825
This commit is contained in:
parent
c1242ffef1
commit
6b54372df8
@ -70,7 +70,7 @@ import org.junit.runner.RunWith;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CompositionPlayerSeekTest {
|
||||
|
||||
private static final long TEST_TIMEOUT_MS = isRunningOnEmulator() ? 20_000 : 10_000;
|
||||
private static final long TEST_TIMEOUT_MS = isRunningOnEmulator() ? 2000_000 : 1000_000;
|
||||
|
||||
private static final MediaItem VIDEO_MEDIA_ITEM = MediaItem.fromUri(MP4_ASSET.uri);
|
||||
private static final long VIDEO_DURATION_US = MP4_ASSET.videoDurationUs;
|
||||
|
@ -527,8 +527,6 @@ public class CompositionPlayerTest {
|
||||
playerTestListener.waitUntilPlayerEnded();
|
||||
|
||||
instrumentation.runOnMainSync(compositionPlayer::release);
|
||||
|
||||
playerTestListener.waitUntilPlayerIdle();
|
||||
}
|
||||
|
||||
private static final class TestImageDecoderFactory implements ImageDecoder.Factory {
|
||||
|
@ -35,8 +35,6 @@ import java.util.Objects;
|
||||
/** Processes raw audio samples. */
|
||||
/* package */ final class AudioGraph {
|
||||
|
||||
private static final String TAG = "AudioGraph";
|
||||
|
||||
private final List<InputInfo> inputInfos;
|
||||
private final AudioMixer mixer;
|
||||
private final AudioProcessingPipeline audioProcessingPipeline;
|
||||
@ -190,7 +188,6 @@ import java.util.Objects;
|
||||
inputInfos.clear();
|
||||
mixer.reset();
|
||||
audioProcessingPipeline.reset();
|
||||
|
||||
finishedInputs = 0;
|
||||
mixerOutput = EMPTY_BUFFER;
|
||||
mixerAudioFormat = AudioFormat.NOT_SET;
|
||||
@ -280,6 +277,7 @@ import java.util.Objects;
|
||||
}
|
||||
|
||||
private boolean isMixerEnded() {
|
||||
// return !mixerOutput.hasRemaining() && activeInputCount == 0 && mixer.isEnded();
|
||||
return !mixerOutput.hasRemaining() && finishedInputs >= inputInfos.size() && mixer.isEnded();
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||
import static androidx.media3.common.util.Util.getPcmFrameSize;
|
||||
import static androidx.media3.common.util.Util.sampleCountToDurationUs;
|
||||
|
||||
import android.media.AudioTrack;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -78,18 +80,6 @@ import java.util.Objects;
|
||||
* @return The playback position relative to the start of playback, in microseconds.
|
||||
*/
|
||||
long getCurrentPositionUs(boolean sourceEnded);
|
||||
|
||||
/** Returns whether the controller is ended. */
|
||||
boolean isEnded();
|
||||
|
||||
/** See {@link #play()}. */
|
||||
default void onPlay() {}
|
||||
|
||||
/** See {@link #pause()}. */
|
||||
default void onPause() {}
|
||||
|
||||
/** See {@link #reset()}. */
|
||||
default void onReset() {}
|
||||
}
|
||||
|
||||
private final Controller controller;
|
||||
@ -100,6 +90,7 @@ import java.util.Objects;
|
||||
private boolean signalledEndOfStream;
|
||||
@Nullable private EditedMediaItemInfo currentEditedMediaItemInfo;
|
||||
private long offsetToCompositionTimeUs;
|
||||
private long inputPositionUs;
|
||||
|
||||
public AudioGraphInputAudioSink(Controller controller) {
|
||||
this.controller = controller;
|
||||
@ -144,11 +135,8 @@ import java.util.Objects;
|
||||
if (currentInputFormat == null) { // Sink not configured.
|
||||
return inputStreamEnded;
|
||||
}
|
||||
// If we are playing the last media item in the sequence, we must also check that the controller
|
||||
// is ended.
|
||||
return inputStreamEnded
|
||||
&& (!checkStateNotNull(currentEditedMediaItemInfo).isLastInSequence
|
||||
|| controller.isEnded());
|
||||
|
||||
return inputStreamEnded && getCompositionPlayerPositionUs() >= inputPositionUs;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -156,6 +144,7 @@ import java.util.Objects;
|
||||
ByteBuffer buffer, long presentationTimeUs, int encodedAccessUnitCount)
|
||||
throws InitializationException {
|
||||
checkState(!inputStreamEnded);
|
||||
|
||||
EditedMediaItem editedMediaItem = checkStateNotNull(currentEditedMediaItemInfo).editedMediaItem;
|
||||
if (outputGraphInput == null) {
|
||||
|
||||
@ -219,23 +208,17 @@ import java.util.Objects;
|
||||
|
||||
@Override
|
||||
public long getCurrentPositionUs(boolean sourceEnded) {
|
||||
long currentPositionUs = controller.getCurrentPositionUs(sourceEnded);
|
||||
if (currentPositionUs != CURRENT_POSITION_NOT_SET) {
|
||||
// Reset the position to the one expected by the player.
|
||||
currentPositionUs -= offsetToCompositionTimeUs;
|
||||
if (isEnded()) {
|
||||
return inputPositionUs;
|
||||
}
|
||||
return currentPositionUs;
|
||||
return getCompositionPlayerPositionUs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void play() {
|
||||
controller.onPlay();
|
||||
}
|
||||
public void play() {}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
controller.onPause();
|
||||
}
|
||||
public void pause() {}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
@ -248,7 +231,6 @@ import java.util.Objects;
|
||||
flush();
|
||||
currentInputFormat = null;
|
||||
currentEditedMediaItemInfo = null;
|
||||
controller.onReset();
|
||||
}
|
||||
|
||||
// Unsupported interface functionality.
|
||||
@ -301,6 +283,15 @@ import java.util.Objects;
|
||||
|
||||
// Internal methods
|
||||
|
||||
private long getCompositionPlayerPositionUs() {
|
||||
long currentPositionUs = controller.getCurrentPositionUs(/* sourceEnded= */ inputStreamEnded);
|
||||
if (currentPositionUs != CURRENT_POSITION_NOT_SET) {
|
||||
// Reset the position to the one expected by the player.
|
||||
currentPositionUs -= offsetToCompositionTimeUs;
|
||||
}
|
||||
return currentPositionUs;
|
||||
}
|
||||
|
||||
private boolean handleBufferInternal(ByteBuffer buffer, long presentationTimeUs, int flags) {
|
||||
checkStateNotNull(currentInputFormat);
|
||||
checkState(!signalledEndOfStream);
|
||||
@ -310,7 +301,8 @@ import java.util.Objects;
|
||||
if (outputBuffer == null) {
|
||||
return false;
|
||||
}
|
||||
outputBuffer.ensureSpaceForWrite(buffer.remaining());
|
||||
int bytesToWrite = buffer.remaining();
|
||||
outputBuffer.ensureSpaceForWrite(bytesToWrite);
|
||||
checkNotNull(outputBuffer.data).put(buffer).flip();
|
||||
outputBuffer.timeUs =
|
||||
presentationTimeUs == C.TIME_END_OF_SOURCE
|
||||
@ -318,7 +310,18 @@ import java.util.Objects;
|
||||
: presentationTimeUs + offsetToCompositionTimeUs;
|
||||
outputBuffer.setFlags(flags);
|
||||
|
||||
return outputGraphInput.queueInputBuffer();
|
||||
boolean bufferQueued = outputGraphInput.queueInputBuffer();
|
||||
if (bufferQueued) {
|
||||
Format currentInputFormat = checkNotNull(this.currentInputFormat);
|
||||
inputPositionUs =
|
||||
presentationTimeUs
|
||||
+ sampleCountToDurationUs(
|
||||
/* sampleCount= */ bytesToWrite
|
||||
/ getPcmFrameSize(
|
||||
currentInputFormat.pcmEncoding, currentInputFormat.channelCount),
|
||||
/* sampleRate= */ currentInputFormat.sampleRate);
|
||||
}
|
||||
return bufferQueued;
|
||||
}
|
||||
|
||||
private static final class EditedMediaItemInfo {
|
||||
|
@ -317,6 +317,7 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
||||
private LivePositionSupplier positionSupplier;
|
||||
private LivePositionSupplier bufferedPositionSupplier;
|
||||
private LivePositionSupplier totalBufferedDurationSupplier;
|
||||
private boolean isSeeking;
|
||||
|
||||
// "this" reference for position suppliers.
|
||||
@SuppressWarnings("initialization:methodref.receiver.bound.invalid")
|
||||
@ -443,20 +444,6 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
||||
|
||||
@Override
|
||||
protected State getState() {
|
||||
@Player.State int oldPlaybackState = playbackState;
|
||||
updatePlaybackState();
|
||||
if (oldPlaybackState != STATE_READY && playbackState == STATE_READY && playWhenReady) {
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
players.get(i).setPlayWhenReady(true);
|
||||
}
|
||||
} else if (oldPlaybackState == STATE_READY
|
||||
&& playWhenReady
|
||||
&& playbackState == STATE_BUFFERING) {
|
||||
// We were playing but a player got in buffering state, pause the players.
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
players.get(i).setPlayWhenReady(false);
|
||||
}
|
||||
}
|
||||
// TODO: b/328219481 - Report video size change to app.
|
||||
State.Builder state =
|
||||
new State.Builder()
|
||||
@ -501,6 +488,11 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
||||
this.playWhenReady = playWhenReady;
|
||||
playWhenReadyChangeReason = PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST;
|
||||
if (playbackState == STATE_READY) {
|
||||
if (playWhenReady) {
|
||||
finalAudioSink.play();
|
||||
} else {
|
||||
finalAudioSink.pause();
|
||||
}
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
players.get(i).setPlayWhenReady(playWhenReady);
|
||||
}
|
||||
@ -588,6 +580,7 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
||||
resetLivePositionSuppliers();
|
||||
CompositionPlayerInternal compositionPlayerInternal =
|
||||
checkStateNotNull(this.compositionPlayerInternal);
|
||||
isSeeking = true;
|
||||
compositionPlayerInternal.startSeek(positionMs);
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
players.get(i).seekTo(positionMs);
|
||||
@ -640,6 +633,8 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
||||
return;
|
||||
}
|
||||
|
||||
@Player.State int oldPlaybackState = playbackState;
|
||||
|
||||
int idleCount = 0;
|
||||
int bufferingCount = 0;
|
||||
int endedCount = 0;
|
||||
@ -666,10 +661,28 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
||||
playbackState = STATE_IDLE;
|
||||
} else if (bufferingCount > 0) {
|
||||
playbackState = STATE_BUFFERING;
|
||||
if (oldPlaybackState == STATE_READY && playWhenReady) {
|
||||
// We were playing but a player got in buffering state, pause the players.
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
players.get(i).setPlayWhenReady(false);
|
||||
}
|
||||
if (!isSeeking) {
|
||||
// The finalAudioSink cannot be paused more than once. The audio pipeline pauses it during
|
||||
// a seek, so don't pause here when seeking.
|
||||
finalAudioSink.pause();
|
||||
}
|
||||
}
|
||||
} else if (endedCount == players.size()) {
|
||||
playbackState = STATE_ENDED;
|
||||
} else {
|
||||
playbackState = STATE_READY;
|
||||
isSeeking = false;
|
||||
if (oldPlaybackState != STATE_READY && playWhenReady) {
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
players.get(i).setPlayWhenReady(true);
|
||||
}
|
||||
finalAudioSink.play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -955,6 +968,8 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
players.get(i).stop();
|
||||
}
|
||||
updatePlaybackState();
|
||||
// Invalidate the parent class state.
|
||||
invalidateState();
|
||||
} else {
|
||||
Log.w(TAG, errorMessage, cause);
|
||||
@ -1107,6 +1122,11 @@ public final class CompositionPlayer extends SimpleBasePlayer
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(int playbackState) {
|
||||
updatePlaybackState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
|
||||
playWhenReadyChangeReason = reason;
|
||||
|
@ -43,7 +43,6 @@ import java.util.Objects;
|
||||
|
||||
private int audioGraphInputsCreated;
|
||||
private int inputAudioSinksCreated;
|
||||
private int inputAudioSinksPlaying;
|
||||
private boolean hasRegisteredPrimaryFormat;
|
||||
private AudioFormat outputAudioFormat;
|
||||
private long outputFramesWritten;
|
||||
@ -74,7 +73,6 @@ import java.util.Objects;
|
||||
finalAudioSink.release();
|
||||
audioGraphInputsCreated = 0;
|
||||
inputAudioSinksCreated = 0;
|
||||
inputAudioSinksPlaying = 0;
|
||||
}
|
||||
|
||||
/** Returns an {@link AudioSink} for a single sequence of non-overlapping raw PCM audio. */
|
||||
@ -162,7 +160,6 @@ import java.util.Objects;
|
||||
|
||||
private final class SinkController implements AudioGraphInputAudioSink.Controller {
|
||||
private final boolean isSequencePrimary;
|
||||
private boolean playing;
|
||||
|
||||
public SinkController(int inputIndex) {
|
||||
this.isSequencePrimary = inputIndex == PRIMARY_SEQUENCE_INDEX;
|
||||
@ -191,41 +188,5 @@ import java.util.Objects;
|
||||
public long getCurrentPositionUs(boolean sourceEnded) {
|
||||
return finalAudioSink.getCurrentPositionUs(sourceEnded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnded() {
|
||||
return finalAudioSink.isEnded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlay() {
|
||||
if (playing) {
|
||||
return;
|
||||
}
|
||||
playing = true;
|
||||
|
||||
inputAudioSinksPlaying++;
|
||||
if (inputAudioSinksCreated == inputAudioSinksPlaying) {
|
||||
finalAudioSink.play();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (!playing) {
|
||||
return;
|
||||
}
|
||||
playing = false;
|
||||
|
||||
if (inputAudioSinksCreated == inputAudioSinksPlaying) {
|
||||
finalAudioSink.pause();
|
||||
}
|
||||
inputAudioSinksPlaying--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset() {
|
||||
onPause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -620,9 +620,10 @@ public class CompositionPlayerTest {
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||
inOrder.verify(listener).onPlaybackStateChanged(Player.STATE_ENDED);
|
||||
|
||||
player.release();
|
||||
player.stop();
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_IDLE);
|
||||
inOrder.verify(listener).onPlaybackStateChanged(Player.STATE_IDLE);
|
||||
player.release();
|
||||
|
||||
assertThat(playbackStates)
|
||||
.containsExactly(
|
||||
|
@ -1,213 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package androidx.media3.transformer;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.atMostOnce;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import androidx.media3.exoplayer.audio.AudioSink;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
/** Unit tests for {@link PlaybackAudioGraphWrapper}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PlaybackAudioGraphWrapperTest {
|
||||
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
private PlaybackAudioGraphWrapper playbackAudioGraphWrapper;
|
||||
@Mock AudioSink outputAudioSink;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
playbackAudioGraphWrapper =
|
||||
new PlaybackAudioGraphWrapper(
|
||||
new DefaultAudioMixer.Factory(), /* effects= */ ImmutableList.of(), outputAudioSink);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
playbackAudioGraphWrapper.release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processData_noAudioSinksCreated_returnsFalse() throws Exception {
|
||||
assertThat(playbackAudioGraphWrapper.processData()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processData_audioSinkHasNotConfiguredYet_returnsFalse() throws Exception {
|
||||
AudioGraphInputAudioSink unused = playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
|
||||
assertThat(playbackAudioGraphWrapper.processData()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputPlay_withOneInput_playsOutputSink() throws Exception {
|
||||
AudioGraphInputAudioSink inputAudioSink =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
|
||||
inputAudioSink.play();
|
||||
|
||||
verify(outputAudioSink).play();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputPause_withOneInput_pausesOutputSink() throws Exception {
|
||||
AudioGraphInputAudioSink inputAudioSink =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
|
||||
inputAudioSink.play();
|
||||
inputAudioSink.pause();
|
||||
|
||||
verify(outputAudioSink).pause();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputReset_withOneInput_pausesOutputSink() {
|
||||
AudioGraphInputAudioSink inputAudioSink =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
|
||||
inputAudioSink.play();
|
||||
inputAudioSink.reset();
|
||||
|
||||
verify(outputAudioSink).pause();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputPlay_whenPlaying_doesNotPlayOutputSink() throws Exception {
|
||||
AudioGraphInputAudioSink inputAudioSink =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
inputAudioSink.play();
|
||||
inputAudioSink.play();
|
||||
|
||||
verify(outputAudioSink, atMostOnce()).play();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputPause_whenNotPlaying_doesNotPauseOutputSink() throws Exception {
|
||||
AudioGraphInputAudioSink inputAudioSink =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
|
||||
inputAudioSink.pause();
|
||||
|
||||
verify(outputAudioSink, never()).pause();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void someInputPlay_withMultipleInputs_doesNotPlayOutputSink() throws Exception {
|
||||
AudioGraphInputAudioSink inputAudioSink1 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
AudioGraphInputAudioSink inputAudioSink2 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 1);
|
||||
AudioGraphInputAudioSink unused = playbackAudioGraphWrapper.createInput(/* inputIndex= */ 2);
|
||||
|
||||
inputAudioSink1.play();
|
||||
inputAudioSink2.play();
|
||||
verify(outputAudioSink, never()).play();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allInputPlay_withMultipleInputs_playsOutputSinkOnce() throws Exception {
|
||||
AudioGraphInputAudioSink inputAudioSink1 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
AudioGraphInputAudioSink inputAudioSink2 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 1);
|
||||
AudioGraphInputAudioSink inputAudioSink3 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 2);
|
||||
|
||||
inputAudioSink1.play();
|
||||
inputAudioSink2.play();
|
||||
inputAudioSink3.play();
|
||||
|
||||
verify(outputAudioSink, atMostOnce()).play();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstInputPause_withMultipleInputs_pausesOutputSink() throws Exception {
|
||||
InOrder inOrder = inOrder(outputAudioSink);
|
||||
AudioGraphInputAudioSink inputAudioSink1 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
AudioGraphInputAudioSink inputAudioSink2 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 1);
|
||||
AudioGraphInputAudioSink inputAudioSink3 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 2);
|
||||
|
||||
inputAudioSink1.play();
|
||||
inputAudioSink2.play();
|
||||
inputAudioSink3.play();
|
||||
inputAudioSink2.pause();
|
||||
|
||||
inOrder.verify(outputAudioSink).pause();
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allInputPause_withMultipleInputs_pausesOutputSinkOnce() throws Exception {
|
||||
AudioGraphInputAudioSink inputAudioSink1 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
AudioGraphInputAudioSink inputAudioSink2 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 1);
|
||||
AudioGraphInputAudioSink inputAudioSink3 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 2);
|
||||
|
||||
inputAudioSink1.play();
|
||||
inputAudioSink2.play();
|
||||
inputAudioSink3.play();
|
||||
inputAudioSink2.pause();
|
||||
inputAudioSink1.pause();
|
||||
inputAudioSink3.pause();
|
||||
|
||||
verify(outputAudioSink, atMostOnce()).pause();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inputPlayAfterPause_withMultipleInputs_playsOutputSink() throws Exception {
|
||||
InOrder inOrder = inOrder(outputAudioSink);
|
||||
AudioGraphInputAudioSink inputAudioSink1 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 0);
|
||||
AudioGraphInputAudioSink inputAudioSink2 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 1);
|
||||
AudioGraphInputAudioSink inputAudioSink3 =
|
||||
playbackAudioGraphWrapper.createInput(/* inputIndex= */ 2);
|
||||
|
||||
inputAudioSink1.play();
|
||||
inputAudioSink2.play();
|
||||
inputAudioSink3.play();
|
||||
inputAudioSink2.pause();
|
||||
inputAudioSink1.pause();
|
||||
inputAudioSink2.play();
|
||||
inputAudioSink1.play();
|
||||
|
||||
inOrder.verify(outputAudioSink).play();
|
||||
inOrder.verify(outputAudioSink).pause();
|
||||
inOrder.verify(outputAudioSink).play();
|
||||
Mockito.verifyNoMoreInteractions(outputAudioSink);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user