| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.media; |
| |
| import static org.junit.Assert.assertArrayEquals; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| |
| import android.media.AudioFormat; |
| import android.media.AudioTrack; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.robolectric.annotation.Config; |
| |
| import org.chromium.media.AudioTrackOutputStream.AudioBufferInfo; |
| import org.chromium.testing.local.LocalRobolectricTestRunner; |
| |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Tests for AudioTrackOutputStream. |
| */ |
| @RunWith(LocalRobolectricTestRunner.class) |
| @Config(manifest = Config.NONE) |
| public class AudioTrackOutputStreamTest { |
| static class ObservableAudioTrack extends AudioTrack { |
| private List<Byte> mReceivedData = new ArrayList<Byte>(); |
| private boolean mPartialWrite = true; |
| |
| public ObservableAudioTrack(int streamType, int sampleRateInHz, int channelConfig, |
| int audioFormat, int bufferSizeInBytes, int mode) { |
| super(streamType, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, mode); |
| } |
| |
| @Override |
| public int write(ByteBuffer audioData, int sizeInBytes, int writeMode) { |
| int writternSize = mPartialWrite ? sizeInBytes / 2 : sizeInBytes; |
| mPartialWrite = !mPartialWrite; |
| |
| if (writternSize > 0) { |
| byte[] array = new byte[writternSize]; |
| audioData.get(array); |
| recordData(array, 0, writternSize); |
| } |
| |
| return writternSize; |
| } |
| |
| @Override |
| public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) { |
| int writternSize = mPartialWrite ? sizeInBytes / 2 : sizeInBytes; |
| mPartialWrite = !mPartialWrite; |
| |
| if (writternSize > 0) recordData(audioData, offsetInBytes, writternSize); |
| |
| return writternSize; |
| } |
| |
| private void recordData(byte[] audioData, int offsetInBytes, int sizeInBytes) { |
| for (; sizeInBytes > 0; --sizeInBytes) mReceivedData.add(audioData[offsetInBytes++]); |
| } |
| |
| public List<Byte> getReceivedData() { |
| return mReceivedData; |
| } |
| } |
| |
| static class DataProvider implements AudioTrackOutputStream.Callback { |
| private static final int MIN_BUFFER_SIZE = 800; |
| private List<Byte> mGeneratedData = new ArrayList<Byte>(); |
| private CountDownLatch mDoneSignal; |
| private ObservableAudioTrack mAudioTrack; |
| |
| public DataProvider(int bufferCount) { |
| assert bufferCount > 0; |
| mDoneSignal = new CountDownLatch(bufferCount + 1); |
| } |
| |
| public void updateBufferCount(int bufferCount) { |
| assert bufferCount > 0; |
| mDoneSignal = new CountDownLatch(bufferCount + 1); |
| } |
| |
| @Override |
| public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { |
| return MIN_BUFFER_SIZE; |
| } |
| |
| @Override |
| public AudioTrack createAudioTrack(int streamType, int sampleRateInHz, int channelConfig, |
| int audioFormat, int bufferSizeInBytes, int mode) { |
| mAudioTrack = new ObservableAudioTrack(streamType, sampleRateInHz, channelConfig, |
| audioFormat, bufferSizeInBytes, mode); |
| return mAudioTrack; |
| } |
| |
| @Override |
| public AudioBufferInfo onMoreData(ByteBuffer audioData, long delayInFrames) { |
| mDoneSignal.countDown(); |
| if (mDoneSignal.getCount() <= 0) { |
| try { |
| Thread.sleep(3); |
| } catch (Exception e) { |
| } |
| return null; |
| } |
| |
| final int dataSize = MIN_BUFFER_SIZE; |
| for (int i = 0; i < dataSize; ++i) { |
| byte data = (byte) i; |
| audioData.put(data); |
| mGeneratedData.add(data); |
| } |
| return new AudioBufferInfo(dataSize, dataSize); |
| } |
| |
| @Override |
| public long getAddress(ByteBuffer byteBuffer) { |
| return 0x10001L; |
| } |
| |
| @Override |
| public void onError() {} |
| |
| public void waitForOutOfData() throws InterruptedException { |
| mDoneSignal.await(300, TimeUnit.MILLISECONDS); |
| } |
| |
| public List<Byte> getGeneratedData() { |
| return mGeneratedData; |
| } |
| |
| public List<Byte> getReceivedData() { |
| return mAudioTrack.getReceivedData(); |
| } |
| |
| public int getBufferSize() { |
| return MIN_BUFFER_SIZE; |
| } |
| }; |
| |
| @Test |
| public void playSimpleBitstream() throws InterruptedException { |
| DataProvider provider = new DataProvider(3); |
| |
| AudioTrackOutputStream stream = AudioTrackOutputStream.create(provider); |
| stream.open(2, 44100, AudioFormat.ENCODING_E_AC3); |
| stream.start(0x888); |
| |
| provider.waitForOutOfData(); |
| List<Byte> generatedData = provider.getGeneratedData(); |
| List<Byte> receivedData = provider.getReceivedData(); |
| |
| assertEquals(3 * provider.getBufferSize(), generatedData.size()); |
| assertArrayEquals(generatedData.toArray(), receivedData.toArray()); |
| |
| stream.stop(); |
| stream.close(); |
| } |
| |
| @Test |
| public void playPiecewiseBitstream() throws InterruptedException { |
| DataProvider provider = new DataProvider(3); |
| |
| AudioTrackOutputStream stream = AudioTrackOutputStream.create(provider); |
| stream.open(2, 44100, AudioFormat.ENCODING_E_AC3); |
| stream.start(0x888); |
| |
| provider.waitForOutOfData(); |
| |
| provider.updateBufferCount(3); |
| provider.waitForOutOfData(); |
| |
| List<Byte> generatedData = provider.getGeneratedData(); |
| List<Byte> receivedData = provider.getReceivedData(); |
| |
| assertTrue(6 * provider.getBufferSize() <= generatedData.size()); |
| assertArrayEquals(generatedData.toArray(), receivedData.toArray()); |
| |
| stream.stop(); |
| stream.close(); |
| } |
| } |