Home | History | Annotate | Download | only in cts
      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 
     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 
    100     public long getAudioTimeUs() {
    101         int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
    102 
    103         return (numFramesPlayed * 1000000L) / mSampleRate;
    104     }
    105 
    106     public int getNumBytesQueued() {
    107         return mNumBytesQueued;
    108     }
    109 
    110     public void play() {
    111         mStopped = false;
    112         mAudioTrack.play();
    113     }
    114 
    115     public void stop() {
    116         if (mQueue.isEmpty()) {
    117             mAudioTrack.stop();
    118             mNumBytesQueued = 0;
    119         } else {
    120             mStopped = true;
    121         }
    122     }
    123 
    124     public void pause() {
    125         mAudioTrack.pause();
    126     }
    127 
    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 
    138     public void release() {
    139         mQueue.clear();
    140         mNumBytesQueued = 0;
    141         mAudioTrack.release();
    142         mAudioTrack = null;
    143         mStopped = false;
    144     }
    145 
    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 
    169     public int getPlayState() {
    170         return mAudioTrack.getPlayState();
    171     }
    172 
    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