| /* |
| * Copyright (C) 2017 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 com.google.android.exoplayer2.audio; |
| |
| import static com.google.android.exoplayer2.RendererCapabilities.ADAPTIVE_NOT_SEAMLESS; |
| import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_HANDLED; |
| import static com.google.android.exoplayer2.RendererCapabilities.TUNNELING_NOT_SUPPORTED; |
| import static com.google.android.exoplayer2.RendererCapabilities.TUNNELING_SUPPORTED; |
| import static com.google.common.truth.Truth.assertThat; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import androidx.annotation.Nullable; |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| import com.google.android.exoplayer2.C; |
| import com.google.android.exoplayer2.Format; |
| import com.google.android.exoplayer2.RendererConfiguration; |
| import com.google.android.exoplayer2.decoder.DecoderException; |
| import com.google.android.exoplayer2.decoder.DecoderInputBuffer; |
| import com.google.android.exoplayer2.decoder.SimpleDecoder; |
| import com.google.android.exoplayer2.decoder.SimpleOutputBuffer; |
| import com.google.android.exoplayer2.drm.ExoMediaCrypto; |
| import com.google.android.exoplayer2.testutil.FakeSampleStream; |
| import com.google.android.exoplayer2.util.MimeTypes; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.robolectric.annotation.Config; |
| |
| /** Unit test for {@link DecoderAudioRenderer}. */ |
| @RunWith(AndroidJUnit4.class) |
| public class DecoderAudioRendererTest { |
| |
| private static final Format FORMAT = |
| new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_RAW).build(); |
| |
| @Mock private AudioSink mockAudioSink; |
| private DecoderAudioRenderer audioRenderer; |
| |
| @Before |
| public void setUp() throws Exception { |
| MockitoAnnotations.initMocks(this); |
| audioRenderer = |
| new DecoderAudioRenderer(null, null, mockAudioSink) { |
| @Override |
| public String getName() { |
| return "TestAudioRenderer"; |
| } |
| |
| @Override |
| @FormatSupport |
| protected int supportsFormatInternal(Format format) { |
| return FORMAT_HANDLED; |
| } |
| |
| @Override |
| protected SimpleDecoder< |
| DecoderInputBuffer, ? extends SimpleOutputBuffer, ? extends DecoderException> |
| createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) { |
| return new FakeDecoder(); |
| } |
| |
| @Override |
| protected Format getOutputFormat() { |
| return FORMAT; |
| } |
| }; |
| } |
| |
| @Config(sdk = 19) |
| @Test |
| public void supportsFormatAtApi19() { |
| assertThat(audioRenderer.supportsFormat(FORMAT)) |
| .isEqualTo(ADAPTIVE_NOT_SEAMLESS | TUNNELING_NOT_SUPPORTED | FORMAT_HANDLED); |
| } |
| |
| @Config(sdk = 21) |
| @Test |
| public void supportsFormatAtApi21() { |
| // From API 21, tunneling is supported. |
| assertThat(audioRenderer.supportsFormat(FORMAT)) |
| .isEqualTo(ADAPTIVE_NOT_SEAMLESS | TUNNELING_SUPPORTED | FORMAT_HANDLED); |
| } |
| |
| @Test |
| public void immediatelyReadEndOfStreamPlaysAudioSinkToEndOfStream() throws Exception { |
| audioRenderer.enable( |
| RendererConfiguration.DEFAULT, |
| new Format[] {FORMAT}, |
| new FakeSampleStream(FORMAT, /* eventDispatcher= */ null, /* shouldOutputSample= */ false), |
| /* positionUs= */ 0, |
| /* joining= */ false, |
| /* mayRenderStartOfStream= */ true, |
| /* offsetUs= */ 0); |
| audioRenderer.setCurrentStreamFinal(); |
| when(mockAudioSink.isEnded()).thenReturn(true); |
| while (!audioRenderer.isEnded()) { |
| audioRenderer.render(0, 0); |
| } |
| verify(mockAudioSink, times(1)).playToEndOfStream(); |
| audioRenderer.disable(); |
| audioRenderer.reset(); |
| verify(mockAudioSink, times(1)).reset(); |
| } |
| |
| private static final class FakeDecoder |
| extends SimpleDecoder<DecoderInputBuffer, SimpleOutputBuffer, DecoderException> { |
| |
| public FakeDecoder() { |
| super(new DecoderInputBuffer[1], new SimpleOutputBuffer[1]); |
| } |
| |
| @Override |
| public String getName() { |
| return "FakeDecoder"; |
| } |
| |
| @Override |
| protected DecoderInputBuffer createInputBuffer() { |
| return new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT); |
| } |
| |
| @Override |
| protected SimpleOutputBuffer createOutputBuffer() { |
| return new SimpleOutputBuffer(this::releaseOutputBuffer); |
| } |
| |
| @Override |
| protected DecoderException createUnexpectedDecodeException(Throwable error) { |
| return new DecoderException("Unexpected decode error", error); |
| } |
| |
| @Override |
| protected DecoderException decode( |
| DecoderInputBuffer inputBuffer, SimpleOutputBuffer outputBuffer, boolean reset) { |
| if (inputBuffer.isEndOfStream()) { |
| outputBuffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); |
| } |
| return null; |
| } |
| |
| } |
| |
| } |