音频流式播放器AudioTrack配合WebSocket的完整案例
import android.annotation.SuppressLint;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
public class AudioTrackImpl implements AudioPlayerInterface {
private static final String TAG = "AudioTrackImpl";
private AudioTrack audioTrack;
private volatile boolean isPlaying = true;
/**
* 二进制数据流统计,当大于1时,通知View层可以播放数据。
*/
private int mBytdDataCount = 0;
/**
* 服务端是否全部返回数据
*/
private volatile boolean isWriteAudioFinish = false;
private final BlockingDeque<byte[]> audioDataQueue = new LinkedBlockingDeque<>();
/**
* 重新播放需要的数据
*/
private final LinkedList<byte[]> mReplayData = new LinkedList<>();
private final PlayerListener mListener;
public AudioTrackImpl(PlayerListener listener) {
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
AudioFormat audioFormat = new AudioFormat.Builder()
.setSampleRate(AudioConfig.sampleRateInHz)
.setEncoding(AudioConfig.audioFormat)
.setChannelMask(AudioConfig.channelOutConfig)
.build();
audioTrack = new AudioTrack(audioAttributes, audioFormat,AudioConfig.BUFFER_SIZE_IN_BYTES, AudioTrack.MODE_STREAM,
AudioManager.AUDIO_SESSION_ID_GENERATE);
mListener = listener;
}
@Override
public void writeAudioChunk(byte[] audioData) {
try {
if (mBytdDataCount == 0) {
ThreadDispatcher.getInstance().postToMainThread(new Runnable() {
@Override
public void run() {
mListener.onPlaybackStateChanged(PlayerListener.PLAYBACK_READY);
}
});
}
mBytdDataCount++;
audioDataQueue.put(audioData);
mReplayData.add(audioData);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void writeAudioFinish() {
isWriteAudioFinish = true;
}
@Override
public void clearAudioData() {
audioDataQueue.clear();
mReplayData.clear();
mBytdDataCount = 0;
}
@Override
public void playAudio() {
isPlaying = true;
mListener.onPlaybackStateChanged(PlayerListener.PLAYBACK_PLAYING);
ThreadDispatcher.getInstance().runInBackground(this::playServer);
}
@SuppressLint("SuspiciousIndentation")
private void playServer() {
// 检查AudioTrack是否初始化成功
if (audioTrack.getState() == AudioTrack.STATE_INITIALIZED) {
audioTrack.play();
while (isPlaying) {
try {
if (audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
byte[] audioData = audioDataQueue.take();
int writeResult = audioTrack.write(audioData, 0, audioData.length);
if (writeResult < 0) {
// 处理错误,丢掉这一块数据
continue;
}
addLostAudioData(audioData, writeResult);
}
if (audioDataQueue.isEmpty() && isWriteAudioFinish) { //注解1
isPlaying = false;
isWriteAudioFinish = false;
mListener.onPlaybackStateChanged(PlayerListener.PLAYBACK_FINISH);
}
} catch (Exception e) {
e.printStackTrace();
break;
}
}
} else {
Log.e(TAG, "Initialization failed.");
}
}
/**
* 暂停时,未写入的数据会被丢弃。这样会导致一句话可能只说了一半,后面的都丢失。这里重新存储下被丢的数据
* @param audioData 待写入AudioTrack的二进制数组,可能会由于调用了pause,导致未写完。
* @param writeResult 已经写入AudioTrack的数据长度
*/
private void addLostAudioData(byte[] audioData, int writeResult) {
int audioDataLen = audioData.length;
if (writeResult < audioDataLen) {
byte[] subArray = Arrays.copyOfRange(audioData, writeResult, audioDataLen);
audioDataQueue.addFirst(subArray);
}
}
@Override
public void replay() {
mListener.onPlaybackStateChanged(PlayerListener.PLAYBACK_REPLAY);
ThreadDispatcher.getInstance().runInBackground(this::realReplay);
}
private void realReplay() {
try {
audioDataQueue.clear();
for (byte[] mReplayDatum : mReplayData) {
audioDataQueue.put(mReplayDatum);
}
isWriteAudioFinish = true;
isPlaying = true;
playServer();
} catch (InterruptedException e) {
}
}
@Override
public void pauseAudio() {
if (audioTrack != null) {
isPlaying = false;
audioTrack.pause();
mListener.onPlaybackStateChanged(PlayerListener.PLAYBACK_PAUSE);
}
}
@Override
public void stopAudio() {
if (audioTrack != null) {
isPlaying = false;
audioTrack.stop();
audioTrack.flush(); //释放音频缓存
clearAudioData();
}
}
@Override
public boolean isPlaying() {
return isPlaying;
}
@Override
public void onDestroy() {
// 确保在Activity销毁时释放资源
if (audioTrack != null) {
isPlaying = false;
audioTrack.flush();
audioTrack.stop(); //MODE_STREAM的stop()实际上将继续播放最后写入的缓冲区的剩余部分
audioTrack.release();
audioTrack = null;
}
clearAudioData();
}
}