1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.media.cts; 17 18 import android.media.AudioFormat; 19 import android.media.AudioManager; 20 import android.media.AudioTrack; 21 import android.media.AudioAttributes; 22 import android.util.Log; 23 24 import java.nio.ByteBuffer; 25 import java.util.LinkedList; 26 27 /** 28 * Class for playing audio by using audio track. 29 * {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods will 30 * block until all data has been written to system. In order to avoid blocking, this class 31 * caculates available buffer size first then writes to audio sink. 32 */ 33 public class NonBlockingAudioTrack { 34 private static final String TAG = NonBlockingAudioTrack.class.getSimpleName(); 35 36 class QueueElement { 37 ByteBuffer data; 38 int size; 39 long pts; 40 } 41 42 private AudioTrack mAudioTrack; 43 private int mSampleRate; 44 private int mNumBytesQueued = 0; 45 private LinkedList<QueueElement> mQueue = new LinkedList<QueueElement>(); 46 private boolean mStopped; 47 NonBlockingAudioTrack(int sampleRate, int channelCount, boolean hwAvSync, int audioSessionId)48 public NonBlockingAudioTrack(int sampleRate, int channelCount, boolean hwAvSync, 49 int audioSessionId) { 50 int channelConfig; 51 switch (channelCount) { 52 case 1: 53 channelConfig = AudioFormat.CHANNEL_OUT_MONO; 54 break; 55 case 2: 56 channelConfig = AudioFormat.CHANNEL_OUT_STEREO; 57 break; 58 case 6: 59 channelConfig = AudioFormat.CHANNEL_OUT_5POINT1; 60 break; 61 default: 62 throw new IllegalArgumentException(); 63 } 64 65 int minBufferSize = 66 AudioTrack.getMinBufferSize( 67 sampleRate, 68 channelConfig, 69 AudioFormat.ENCODING_PCM_16BIT); 70 71 int bufferSize = 2 * minBufferSize; 72 73 if (!hwAvSync) { 74 mAudioTrack = new AudioTrack( 75 AudioManager.STREAM_MUSIC, 76 sampleRate, 77 channelConfig, 78 AudioFormat.ENCODING_PCM_16BIT, 79 bufferSize, 80 AudioTrack.MODE_STREAM); 81 } 82 else { 83 // build AudioTrack using Audio Attributes and FLAG_HW_AV_SYNC 84 AudioAttributes audioAttributes = (new AudioAttributes.Builder()) 85 .setLegacyStreamType(AudioManager.STREAM_MUSIC) 86 .setFlags(AudioAttributes.FLAG_HW_AV_SYNC) 87 .build(); 88 AudioFormat audioFormat = (new AudioFormat.Builder()) 89 .setChannelMask(channelConfig) 90 .setEncoding(AudioFormat.ENCODING_PCM_16BIT) 91 .setSampleRate(sampleRate) 92 .build(); 93 mAudioTrack = new AudioTrack(audioAttributes, audioFormat, bufferSize, 94 AudioTrack.MODE_STREAM, audioSessionId); 95 } 96 97 mSampleRate = sampleRate; 98 } 99 getAudioTimeUs()100 public long getAudioTimeUs() { 101 int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition(); 102 103 return (numFramesPlayed * 1000000L) / mSampleRate; 104 } 105 getNumBytesQueued()106 public int getNumBytesQueued() { 107 return mNumBytesQueued; 108 } 109 play()110 public void play() { 111 mStopped = false; 112 mAudioTrack.play(); 113 } 114 stop()115 public void stop() { 116 if (mQueue.isEmpty()) { 117 mAudioTrack.stop(); 118 mNumBytesQueued = 0; 119 } else { 120 mStopped = true; 121 } 122 } 123 pause()124 public void pause() { 125 mAudioTrack.pause(); 126 } 127 flush()128 public void flush() { 129 if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { 130 return; 131 } 132 mAudioTrack.flush(); 133 mQueue.clear(); 134 mNumBytesQueued = 0; 135 mStopped = false; 136 } 137 release()138 public void release() { 139 mQueue.clear(); 140 mNumBytesQueued = 0; 141 mAudioTrack.release(); 142 mAudioTrack = null; 143 mStopped = false; 144 } 145 process()146 public void process() { 147 while (!mQueue.isEmpty()) { 148 QueueElement element = mQueue.peekFirst(); 149 int written = mAudioTrack.write(element.data, element.size, 150 AudioTrack.WRITE_NON_BLOCKING, element.pts); 151 if (written < 0) { 152 throw new RuntimeException("Audiotrack.write() failed."); 153 } 154 155 mNumBytesQueued -= written; 156 element.size -= written; 157 if (element.size != 0) { 158 break; 159 } 160 mQueue.removeFirst(); 161 } 162 if (mStopped) { 163 mAudioTrack.stop(); 164 mNumBytesQueued = 0; 165 mStopped = false; 166 } 167 } 168 getPlayState()169 public int getPlayState() { 170 return mAudioTrack.getPlayState(); 171 } 172 write(ByteBuffer data, int size, long pts)173 public void write(ByteBuffer data, int size, long pts) { 174 QueueElement element = new QueueElement(); 175 element.data = data; 176 element.size = size; 177 element.pts = pts; 178 179 // accumulate size written to queue 180 mNumBytesQueued += size; 181 mQueue.add(element); 182 } 183 } 184 185