|
28 | 28 | import android.content.Context;
|
29 | 29 | import androidx.media3.common.MediaItem;
|
30 | 30 | import androidx.media3.common.Player;
|
| 31 | +import androidx.media3.common.audio.AudioProcessor; |
31 | 32 | import androidx.media3.exoplayer.audio.AudioSink;
|
32 | 33 | import androidx.media3.test.utils.CapturingAudioSink;
|
33 | 34 | import androidx.media3.test.utils.DumpFileAsserts;
|
|
36 | 37 | import androidx.test.core.app.ApplicationProvider;
|
37 | 38 | import androidx.test.ext.junit.runners.AndroidJUnit4;
|
38 | 39 | import java.io.IOException;
|
| 40 | +import java.nio.ByteBuffer; |
39 | 41 | import java.util.concurrent.TimeoutException;
|
| 42 | +import java.util.concurrent.atomic.AtomicLong; |
40 | 43 | import org.junit.Before;
|
41 | 44 | import org.junit.Test;
|
42 | 45 | import org.junit.runner.RunWith;
|
@@ -720,6 +723,108 @@ public void playSingleSequence_replayAfterEnd_outputCorrectSamples() throws Exce
|
720 | 723 | PREVIEW_DUMP_FILE_EXTENSION + FILE_AUDIO_RAW + "_playedTwice.dump");
|
721 | 724 | }
|
722 | 725 |
|
| 726 | + @Test |
| 727 | + public void playSingleSequence_withCustomAudioMixer_mixesTheCorrectNumberOfBytes() |
| 728 | + throws Exception { |
| 729 | + AtomicLong bytesMixed = new AtomicLong(); |
| 730 | + AudioMixer.Factory forwardingAudioMixerFactory = |
| 731 | + () -> |
| 732 | + new ForwardingAudioMixer(new DefaultAudioMixer.Factory().create()) { |
| 733 | + @Override |
| 734 | + public void queueInput(int sourceId, ByteBuffer sourceBuffer) { |
| 735 | + bytesMixed.addAndGet(sourceBuffer.remaining()); |
| 736 | + super.queueInput(sourceId, sourceBuffer); |
| 737 | + } |
| 738 | + }; |
| 739 | + CompositionPlayer player = |
| 740 | + new CompositionPlayer.Builder(context) |
| 741 | + .setClock(new FakeClock(/* isAutoAdvancing= */ true)) |
| 742 | + .setAudioMixerFactory(forwardingAudioMixerFactory) |
| 743 | + .build(); |
| 744 | + EditedMediaItem editedMediaItem = |
| 745 | + new EditedMediaItem.Builder(MediaItem.fromUri(ASSET_URI_PREFIX + FILE_AUDIO_RAW)) |
| 746 | + .setDurationUs(1_000_000L) |
| 747 | + .build(); |
| 748 | + EditedMediaItemSequence sequence = new EditedMediaItemSequence.Builder(editedMediaItem).build(); |
| 749 | + Composition composition = new Composition.Builder(sequence).build(); |
| 750 | + |
| 751 | + player.setComposition(composition); |
| 752 | + player.prepare(); |
| 753 | + player.play(); |
| 754 | + TestPlayerRunHelper.advance(player).untilState(Player.STATE_ENDED); |
| 755 | + player.release(); |
| 756 | + |
| 757 | + // Expect 1 second of single-channel, 44_100Hz, 2 bytes per sample. |
| 758 | + assertThat(bytesMixed.get()).isEqualTo(88_200); |
| 759 | + } |
| 760 | + |
| 761 | + private static class ForwardingAudioMixer implements AudioMixer { |
| 762 | + |
| 763 | + private final AudioMixer wrappedAudioMixer; |
| 764 | + |
| 765 | + public ForwardingAudioMixer(AudioMixer audioMixer) { |
| 766 | + wrappedAudioMixer = audioMixer; |
| 767 | + } |
| 768 | + |
| 769 | + @Override |
| 770 | + public void configure( |
| 771 | + AudioProcessor.AudioFormat outputAudioFormat, int bufferSizeMs, long startTimeUs) |
| 772 | + throws AudioProcessor.UnhandledAudioFormatException { |
| 773 | + wrappedAudioMixer.configure(outputAudioFormat, bufferSizeMs, startTimeUs); |
| 774 | + } |
| 775 | + |
| 776 | + @Override |
| 777 | + public void setEndTimeUs(long endTimeUs) { |
| 778 | + wrappedAudioMixer.setEndTimeUs(endTimeUs); |
| 779 | + } |
| 780 | + |
| 781 | + @Override |
| 782 | + public boolean supportsSourceAudioFormat(AudioProcessor.AudioFormat sourceFormat) { |
| 783 | + return wrappedAudioMixer.supportsSourceAudioFormat(sourceFormat); |
| 784 | + } |
| 785 | + |
| 786 | + @Override |
| 787 | + public int addSource(AudioProcessor.AudioFormat sourceFormat, long startTimeUs) |
| 788 | + throws AudioProcessor.UnhandledAudioFormatException { |
| 789 | + return wrappedAudioMixer.addSource(sourceFormat, startTimeUs); |
| 790 | + } |
| 791 | + |
| 792 | + @Override |
| 793 | + public boolean hasSource(int sourceId) { |
| 794 | + return wrappedAudioMixer.hasSource(sourceId); |
| 795 | + } |
| 796 | + |
| 797 | + @Override |
| 798 | + public void setSourceVolume(int sourceId, float volume) { |
| 799 | + wrappedAudioMixer.setSourceVolume(sourceId, volume); |
| 800 | + } |
| 801 | + |
| 802 | + @Override |
| 803 | + public void removeSource(int sourceId) { |
| 804 | + wrappedAudioMixer.removeSource(sourceId); |
| 805 | + } |
| 806 | + |
| 807 | + @Override |
| 808 | + public void queueInput(int sourceId, ByteBuffer sourceBuffer) { |
| 809 | + wrappedAudioMixer.queueInput(sourceId, sourceBuffer); |
| 810 | + } |
| 811 | + |
| 812 | + @Override |
| 813 | + public ByteBuffer getOutput() { |
| 814 | + return wrappedAudioMixer.getOutput(); |
| 815 | + } |
| 816 | + |
| 817 | + @Override |
| 818 | + public boolean isEnded() { |
| 819 | + return wrappedAudioMixer.isEnded(); |
| 820 | + } |
| 821 | + |
| 822 | + @Override |
| 823 | + public void reset() { |
| 824 | + wrappedAudioMixer.reset(); |
| 825 | + } |
| 826 | + } |
| 827 | + |
723 | 828 | private static CompositionPlayer createCompositionPlayer(Context context, AudioSink audioSink) {
|
724 | 829 | return new CompositionPlayer.Builder(context)
|
725 | 830 | .setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
|
0 commit comments