From 8cca6feecae70000396c8bdcf12803899825f2dd Mon Sep 17 00:00:00 2001 From: zx5656 <103409@ad.nexty-ele.com> Date: Fri, 17 Jul 2020 16:14:24 +0900 Subject: [PATCH 1/3] audio interrupt --- .../managers/audio/AudioDecoder.java | 48 ++- .../managers/audio/AudioDecoderCompat.java | 89 ++-- .../managers/audio/AudioDecoderListener.java | 6 +- .../managers/audio/AudioStreamManager.java | 385 ++++++++++++++++-- .../managers/audio/BaseAudioDecoder.java | 29 +- 5 files changed, 468 insertions(+), 89 deletions(-) diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoder.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoder.java index cba0d212b0..51d50a800b 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoder.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoder.java @@ -43,6 +43,7 @@ import com.smartdevicelink.util.DebugTool; import java.nio.ByteBuffer; +import java.util.ArrayList; /** * The audio decoder to decode a single audio file to PCM. @@ -73,30 +74,37 @@ public void start() { decoder.setCallback(new MediaCodec.Callback() { @Override public void onInputBufferAvailable(@NonNull MediaCodec mediaCodec, int i) { - ByteBuffer inputBuffer = mediaCodec.getInputBuffer(i); - if (inputBuffer == null) return; + try { + ByteBuffer inputBuffer = mediaCodec.getInputBuffer(i); + if (inputBuffer == null) return; - MediaCodec.BufferInfo info = AudioDecoder.super.onInputBufferAvailable(extractor, inputBuffer); - mediaCodec.queueInputBuffer(i, info.offset, info.size, info.presentationTimeUs, info.flags); + MediaCodec.BufferInfo info = AudioDecoder.super.onInputBufferAvailable(extractor, inputBuffer); + mediaCodec.queueInputBuffer(i, info.offset, info.size, info.presentationTimeUs, info.flags); + } catch (Exception e) { + e.printStackTrace(); + listener.onDecoderError(e); + } } - @Override public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec, int i, @NonNull MediaCodec.BufferInfo bufferInfo) { - ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(i); - if (outputBuffer == null) return; - - if (outputBuffer.limit() > 0) { - SampleBuffer targetSampleBuffer = AudioDecoder.super.onOutputBufferAvailable(outputBuffer); - AudioDecoder.this.listener.onAudioDataAvailable(targetSampleBuffer); - } else { - DebugTool.logWarning(TAG, "output buffer empty. Chance that silence was detected"); - } - - mediaCodec.releaseOutputBuffer(i, false); - - if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) { - listener.onDecoderFinish(true); - stop(); + try { + ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(i); + if (outputBuffer == null) return; + ArrayList targetSampleBufferList = null; + if (outputBuffer.limit() > 0) { + targetSampleBufferList = AudioDecoder.super.onOutputBufferAvailable(outputBuffer); + } else { + DebugTool.logWarning(TAG, "output buffer empty. Chance that silence was detected"); + } + AudioDecoder.this.listener.onAudioDataAvailable(targetSampleBufferList,bufferInfo.flags); + mediaCodec.releaseOutputBuffer(i, false); + if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) { + listener.onDecoderFinish(true); + stop(); + } + } catch (Exception e) { + e.printStackTrace(); + listener.onDecoderError(e); } } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompat.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompat.java index 1fdf5b2196..e38209b5ac 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompat.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompat.java @@ -44,6 +44,7 @@ import java.lang.ref.WeakReference; import java.nio.ByteBuffer; +import java.util.ArrayList; /** * The audio decoder to decode a single audio file to PCM. @@ -88,6 +89,14 @@ public void start() { } } + @Override + public void stop() { + if (mThread != null) { + mThread.interrupt(); + mThread = null; + } + super.stop(); + } /** * Runnable to decode audio data @@ -106,39 +115,65 @@ private static class DecoderRunnable implements Runnable { @Override public void run() { final AudioDecoderCompat reference = weakReference.get(); - if (reference == null) { - DebugTool.logWarning(TAG, "AudioDecoderCompat reference was null"); - return; + try { + if (reference == null) { + DebugTool.logWarning(TAG, "AudioDecoderCompat reference was null"); + return; + } + if(reference.decoder == null){ + DebugTool.logWarning(TAG, "AudioDecoderCompat decoder was null"); + return; + } + while (reference!= null && !reference.mThread.isInterrupted()) { + if( AudioDecoder(reference,reference.decoder.getInputBuffers(),reference.decoder.getOutputBuffers())){ + break; + } + } + } catch (Exception e) { + DebugTool.logWarning(TAG, "DecoderRunnable Exception:" + e); + } finally { + if (reference != null && reference.mThread != null) { + try { + reference.mThread.interrupt(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + reference.mThread = null; + } + } } - final ByteBuffer[] inputBuffersArray = reference.decoder.getInputBuffers(); - final ByteBuffer[] outputBuffersArray = reference.decoder.getOutputBuffers(); + } + + boolean AudioDecoder(final AudioDecoderCompat reference,final ByteBuffer[] inputBuffersArray,final ByteBuffer[] outputBuffersArray){ MediaCodec.BufferInfo outputBufferInfo = new MediaCodec.BufferInfo(); MediaCodec.BufferInfo inputBufferInfo; ByteBuffer inputBuffer, outputBuffer; SampleBuffer sampleBuffer; - - while (reference!= null && !reference.mThread.isInterrupted()) { - int inputBuffersArrayIndex = 0; - while (inputBuffersArrayIndex != MediaCodec.INFO_TRY_AGAIN_LATER) { + int inputBuffersArrayIndex = 0; + while (inputBuffersArrayIndex != MediaCodec.INFO_TRY_AGAIN_LATER) { + try { inputBuffersArrayIndex = reference.decoder.dequeueInputBuffer(DEQUEUE_TIMEOUT); if (inputBuffersArrayIndex >= 0) { inputBuffer = inputBuffersArray[inputBuffersArrayIndex]; inputBufferInfo = reference.onInputBufferAvailable(reference.extractor, inputBuffer); reference.decoder.queueInputBuffer(inputBuffersArrayIndex, inputBufferInfo.offset, inputBufferInfo.size, inputBufferInfo.presentationTimeUs, inputBufferInfo.flags); } + } catch (Exception e) { + return true; } - - int outputBuffersArrayIndex = 0; - while (outputBuffersArrayIndex != MediaCodec.INFO_TRY_AGAIN_LATER) { + } + int outputBuffersArrayIndex = 0; + while (outputBuffersArrayIndex != MediaCodec.INFO_TRY_AGAIN_LATER) { + try { outputBuffersArrayIndex = reference.decoder.dequeueOutputBuffer(outputBufferInfo, DEQUEUE_TIMEOUT); if (outputBuffersArrayIndex >= 0) { outputBuffer = outputBuffersArray[outputBuffersArrayIndex]; if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0 && outputBufferInfo.size != 0) { reference.decoder.releaseOutputBuffer(outputBuffersArrayIndex, false); } else if (outputBuffer.limit() > 0) { - sampleBuffer = reference.onOutputBufferAvailable(outputBuffer); - if(reference.listener!=null){ - reference.listener.onAudioDataAvailable(sampleBuffer); + ArrayList sampleBufferList = reference.onOutputBufferAvailable(outputBuffer); + if (reference.listener != null) { + reference.listener.onAudioDataAvailable(sampleBufferList, outputBufferInfo.flags); } reference.decoder.releaseOutputBuffer(outputBuffersArrayIndex, false); } @@ -146,23 +181,19 @@ public void run() { MediaFormat newFormat = reference.decoder.getOutputFormat(); reference.onOutputFormatChanged(newFormat); } + } catch (Exception e) { + return true; } - - if (outputBufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) { - if (reference.listener != null) { - reference.listener.onDecoderFinish(true); - } - reference.stop(); - try { - reference.mThread.interrupt(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - reference.mThread = null; - break; - } + } + if (outputBufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) { + reference.listener.onAudioDataAvailable(null,outputBufferInfo.flags); + if (reference.listener != null) { + reference.listener.onDecoderFinish(true); } + reference.stop(); + return true; } + return false; } } } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderListener.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderListener.java index 5962ac09ea..ab73eb4b76 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderListener.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderListener.java @@ -31,6 +31,8 @@ */ package com.smartdevicelink.managers.audio; +import java.util.ArrayList; + /** * An interface for the audio decoder classes. * The caller using the audio decoder will be @@ -40,9 +42,9 @@ public interface AudioDecoderListener { /** * Notifies that decoded audio data is available. - * @param sampleBuffer The sample buffer holding the decoded audio data. + * @param sampleBufferList The sample buffer holding the decoded audio data. */ - void onAudioDataAvailable(SampleBuffer sampleBuffer); + void onAudioDataAvailable(ArrayList sampleBufferList, int flags); /** * Notifies that the audio decoding is finished. diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java index bb78741c78..4abc3310c4 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java @@ -41,6 +41,9 @@ import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; +import android.util.Log; +import android.media.MediaCodec; +import android.os.Message; import com.smartdevicelink.SdlConnection.SdlSession; import com.smartdevicelink.managers.CompletionListener; @@ -54,8 +57,11 @@ import com.smartdevicelink.proxy.interfaces.OnSystemCapabilityListener; import com.smartdevicelink.proxy.rpc.AudioPassThruCapabilities; import com.smartdevicelink.proxy.rpc.OnHMIStatus; +import com.smartdevicelink.proxy.rpc.enums.AudioType; +import com.smartdevicelink.proxy.rpc.enums.BitsPerSample; import com.smartdevicelink.proxy.rpc.enums.HMILevel; import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows; +import com.smartdevicelink.proxy.rpc.enums.SamplingRate; import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; import com.smartdevicelink.transport.utl.TransportRecord; @@ -66,6 +72,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; @@ -83,7 +90,16 @@ public class AudioStreamManager extends BaseAudioStreamManager { private IAudioStreamListener sdlAudioStream; private int sdlSampleRate; private @SampleType int sdlSampleType; - private final Queue queue; + //Decoder queue + private final Queue queue; + //Audio streaming send thread + private SendAudioStreamThread mSendAudioStreamThread; + //Audio buffer list + private ArrayList mAudioBufferList = null; + //Data transmission end time + private long mEndTimeOfSendData = 0; + //Lock data transmission thread + private final Object LOCK_SENDTHREAD = new Object(); private final WeakReference context; private final StreamingStateMachine streamingStateMachine; private AudioPassThruCapabilities audioStreamingCapabilities; @@ -230,14 +246,18 @@ public void onCapabilityRetrieved(Object capability) { @Override public void onError(String info) { DebugTool.logError(TAG, "Error retrieving audio streaming capability: " + info); - streamingStateMachine.transitionToState(StreamingStateMachine.ERROR); - transitionToState(ERROR); + //Added to get default audio passthrough function + audioStreamingCapabilities = getDefaultAudioPassThruCapabilities(); + checkState(); + } }); } @Override public void dispose() { + mEndTimeOfSendData = 0; + cleanAudioStreamThread(); stopAudioStream(new CompletionListener() { @Override public void onComplete(boolean success) { @@ -270,6 +290,9 @@ public void startAudioStream(boolean encrypted, final CompletionListener complet AudioPassThruCapabilities capabilities = (AudioPassThruCapabilities) internalInterface.getCapability(SystemCapabilityType.PCM_STREAMING); + if(capabilities == null){ + capabilities = getDefaultAudioPassThruCapabilities(); + } if (capabilities != null) { switch (capabilities.getSamplingRate()) { case _8KHZ: @@ -325,6 +348,16 @@ private void finish(CompletionListener listener, boolean isSuccess) { if (listener != null) { listener.onComplete(isSuccess); } + synchronized (LOCK_SENDTHREAD) { + if (mSendAudioStreamThread != null) { + mSendAudioStreamThread.stopAs(); + try { + mSendAudioStreamThread.join(); + } catch (InterruptedException e) { + } + mSendAudioStreamThread = null; + } + } } /** @@ -358,7 +391,7 @@ public void stopAudioStream(final CompletionListener completionListener) { * @param resourceId The specified resource file to be played. * @param completionListener A completion listener that informs when the audio file is played. */ - public void pushResource(int resourceId, final CompletionListener completionListener) { + public void pushResource(int resourceId, final CompletionListener completionListener,boolean interrupt) { Context c = context.get(); Resources r = c.getResources(); Uri uri = new Uri.Builder() @@ -367,8 +400,7 @@ public void pushResource(int resourceId, final CompletionListener completionList .appendPath(r.getResourceTypeName(resourceId)) .appendPath(r.getResourceEntryName(resourceId)) .build(); - - this.pushAudioSource(uri, completionListener); + this.pushAudioSource(uri, completionListener,interrupt); } /** @@ -379,7 +411,7 @@ public void pushResource(int resourceId, final CompletionListener completionList * @param completionListener A completion listener that informs when the audio file is played. */ @SuppressWarnings("WeakerAccess") - public void pushAudioSource(Uri audioSource, final CompletionListener completionListener) { + public void pushAudioSource(Uri audioSource, final CompletionListener completionListener,boolean interrupt) { // streaming state must be STARTED (starting the service is ready. starting stream is started) if (streamingStateMachine.getState() != StreamingStateMachine.STARTED) { return; @@ -388,27 +420,39 @@ public void pushAudioSource(Uri audioSource, final CompletionListener completion BaseAudioDecoder decoder; AudioDecoderListener decoderListener = new AudioDecoderListener() { @Override - public void onAudioDataAvailable(SampleBuffer buffer) { - if (sdlAudioStream != null) { - sdlAudioStream.sendAudio(buffer.getByteBuffer(), buffer.getPresentationTimeUs(), null); + public void onAudioDataAvailable(ArrayList sampleBufferList,int flags) { + if(mSendAudioStreamThread != null){ + ArrayList sendBufferList = new ArrayList<>(); + if(sampleBufferList == null){ + sendBufferList.add( + new SendAudioBuffer(null, flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM? SendAudioBuffer.DECODER_FINISH_SUCCESS: SendAudioBuffer.DECODER_NOT_FINISH) + ); + } else { + for(int i = 0 ; i < sampleBufferList.size() ;i++){ + SampleBuffer buffer = sampleBufferList.get(i); + int iFlag = SendAudioBuffer.DECODER_NOT_FINISH; + if(flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM && sampleBufferList.size() == i+1){ + iFlag = SendAudioBuffer.DECODER_FINISH_SUCCESS; + } + sendBufferList.add( + new SendAudioBuffer(buffer, iFlag) + ); + } + } + mSendAudioStreamThread.addAudioData(sendBufferList); } } @Override public void onDecoderFinish(boolean success) { - finish(completionListener, true); - synchronized (queue) { - // remove throws an exception if the queue is empty. The decoder of this listener - // should still be in this queue so we should be fine by just removing it - // if the queue is empty than we have a bug somewhere in the code - // and we deserve the crash... - queue.remove(); - - // if the queue contains more items then start the first one (without removing it) - if (queue.size() > 0) { - queue.element().start(); - } + // if the queue contains more items then start the first one (without removing it) + if(mSendAudioStreamThread != null && !success){ + ArrayList sendBufferList = new ArrayList<>(); + sendBufferList.add( + new SendAudioBuffer(null, SendAudioBuffer.DECODER_FINISH_FAILED) + ); + mSendAudioStreamThread.addAudioData(sendBufferList); } } @@ -424,16 +468,307 @@ public void onDecoderError(Exception e) { // this BaseAudioDecoder subclass uses methods deprecated with api 21 decoder = new AudioDecoderCompat(audioSource, context.get(), sdlSampleRate, sdlSampleType, decoderListener); } + startAudioStreamThread(new Decoder(decoder,completionListener,interrupt)); + } + + private void startAudioStreamThread(final Decoder _decoder){ + Log.d(TAG, "startAudioStreamThread() queue.size():" + queue.size()); + if (streamingStateMachine.getState() != StreamingStateMachine.STARTED || sdlAudioStream == null) { + cleanAudioStreamThread(); + return; + } + if(_decoder != null){ + if(_decoder.isInterrupt()){ + //Stop AS data + Log.d(TAG, "Audio playback interrupted"); + finish(null,true); + synchronized (queue) { + while (queue.size() > 0){ + queue.element().getAudioDecoder().stop(); + queue.remove(); + } + } + } + } + synchronized (LOCK_SENDTHREAD) { + if(mSendAudioStreamThread == null){ + mSendAudioStreamThread = new SendAudioStreamThread(new Runnable() { + + @Override public void run() { + synchronized (queue) { + if(queue.size() > 0){ + queue.element().getAudioDecoder().start(); + } + } + } + }); + synchronized (queue) { + if(_decoder != null){ + queue.add(_decoder); + } + } + mSendAudioStreamThread.start(); + } else { + synchronized (queue) { + if(_decoder != null){ + queue.add(_decoder); + } + } + } + } + } + + + private long getDelayStartAudioTime(){ + long nowTime = System.currentTimeMillis(); + long lDelay = mEndTimeOfSendData - nowTime; + if(lDelay < 0){ + lDelay = 0; + } else { + if(lDelay < 1500){ + lDelay = 0; + } else { + lDelay -= 1500; + } + } + return lDelay; + } + + private void cleanAudioStreamThread(){ + if (mSendAudioStreamThread != null) { + mSendAudioStreamThread.stopAs(); + try { + mSendAudioStreamThread.join(); + } catch (InterruptedException e) { + } + mSendAudioStreamThread = null; + } synchronized (queue) { - queue.add(decoder); + while (queue.size() > 0){ + queue.element().getAudioDecoder().stop(); + queue.remove(); + } + } + } + private class Decoder { + private BaseAudioDecoder mAudioDecoder; + private CompletionListener mCompletionListener; + private boolean mInterrupt; + public Decoder(BaseAudioDecoder decoder,CompletionListener listener,boolean interrupt){ + mAudioDecoder = decoder; + mCompletionListener = listener; + mInterrupt = interrupt; + } + public BaseAudioDecoder getAudioDecoder(){ + return mAudioDecoder; + } - if (queue.size() == 1) { - decoder.start(); + public CompletionListener getCompletionListener(){ + return mCompletionListener; + } + + public boolean isInterrupt(){ + return mInterrupt; + } + } + private class SendAudioBuffer { + private final static int DECODER_NOT_FINISH = 0; + private final static int DECODER_FINISH_SUCCESS = 1; + private final static int DECODER_FINISH_FAILED = 2; + private ByteBuffer mByteBuffer; + private long mPresentationTimeUs; + private int mFinishFlag; + + public SendAudioBuffer(SampleBuffer _buff,int flag){ + + mFinishFlag = flag; + if(_buff != null){ + mPresentationTimeUs = _buff.getPresentationTimeUs(); + ByteBuffer buff = _buff.getByteBuffer(); + mByteBuffer = ByteBuffer.allocate(buff.remaining()); + mByteBuffer.put(buff); + mByteBuffer.flip(); } + + } + + public long getPresentationTimeUs(){ + return mPresentationTimeUs; + } + + public ByteBuffer getByteBuffer(){ + return mByteBuffer; + } + + public int getFinishFlag(){ + return mFinishFlag; } } + private final class SendAudioStreamThread extends Thread{ + private static final int MSG_TICK = 1; + private static final int MSG_ADD = 2; + private static final int MSG_TERMINATE = -1; + private boolean isFirst = true; + private Handler mHandler; + private Runnable mStartedCallback; + private boolean mIsStopRequest = false; + + public SendAudioStreamThread(Runnable onStarted) { + mStartedCallback = onStarted; + } + @Override + public void run() { + Looper.prepare(); + if(mHandler == null){ + mHandler = new Handler() { + long startTime = 0; + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_TICK: { + long delay = 0; + if(mAudioBufferList != null && mAudioBufferList.size() > 0){ + while (true){ + if (streamingStateMachine.getState() != StreamingStateMachine.STARTED || sdlAudioStream == null) { + Log.e(TAG, "Streaming Status error:" + streamingStateMachine.getState() ); + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + cleanAudioStreamThread(); + } + }); + break; + } + + if(mAudioBufferList.size() > 0){ + SendAudioBuffer sBuffer = mAudioBufferList.get(0); + if(sBuffer.getByteBuffer() != null){ + long nowTime = System.currentTimeMillis(); + long AllowableTime = (nowTime - startTime + 1000) * 1000; + if( AllowableTime > sBuffer.getPresentationTimeUs()){ + long lSendDataTime = (Long.valueOf(sBuffer.getByteBuffer().limit()) * 1000 )/ (sdlSampleRate * 2); + mEndTimeOfSendData = startTime + ( sBuffer.getPresentationTimeUs()/1000) + lSendDataTime; + sdlAudioStream.sendAudio(sBuffer.getByteBuffer(), sBuffer.getPresentationTimeUs(), null); + } else { + //Delay data transmission + Log.d(TAG, "Delay the call to sendAudio"); + delay = 500; + break; + } + } + mAudioBufferList.remove(0); + + if(sBuffer.getFinishFlag() != SendAudioBuffer.DECODER_NOT_FINISH) { + final boolean isSuccess = sBuffer.getFinishFlag() == SendAudioBuffer.DECODER_FINISH_SUCCESS; + + Handler handler = new Handler(Looper.getMainLooper()); + long lDelay = getDelayStartAudioTime(); + Log.d(TAG, "Playback end notification. lDelay:" + lDelay); + handler.postDelayed(new Runnable() { + @Override + public void run() { + synchronized (queue) { + if (queue.size() > 0) { + finish(queue.poll().getCompletionListener(),isSuccess); + } else { + Log.e(TAG, "There is no element of the queue"); + } + if (queue.size() > 0) { + startAudioStreamThread(null); + } + } + } + },lDelay); + return; + } + } else { + break; + } + } + + } + sendMessageDelayed(mHandler.obtainMessage(MSG_TICK), delay); + break; + } + case MSG_ADD: { + if(mAudioBufferList == null){ + mAudioBufferList = new ArrayList<>(); + startTime = System.currentTimeMillis(); + if(startTime < mEndTimeOfSendData){ + Log.d(TAG, "The playback end time exceeds the current time. EndTime:" + mEndTimeOfSendData); + startTime = mEndTimeOfSendData; + } + } + ArrayList sendBufferList = (ArrayList)msg.obj; + for(SendAudioBuffer buff: sendBufferList){ + mAudioBufferList.add(buff); + } + break; + } + case MSG_TERMINATE: { + removeCallbacksAndMessages(null); + if(mAudioBufferList != null){ + mAudioBufferList.clear(); + mAudioBufferList = null; + } + mHandler = null; + mIsStopRequest = false; + Looper looper = Looper.myLooper(); + if (looper != null) { + looper.quit(); + } + break; + } + default: + break; + } + } + }; + } + if(mIsStopRequest){ + Log.d(TAG, "StopRequest is valid"); + return; + } + if (mStartedCallback != null) { + mStartedCallback.run(); + } + Log.d(TAG, "Starting SendAudioStreamThread"); + Looper.loop(); + Log.d(TAG, "Stopping SendAudioStreamThread"); + } + + public void addAudioData(final ArrayList sendBufferList){ + if (mHandler != null && sendBufferList != null && sendBufferList.size() > 0) { + Message msg = Message.obtain(); + msg.what = MSG_ADD; + msg.obj = sendBufferList; + mHandler.sendMessage(msg); + if(isFirst){ + mHandler.sendMessage(mHandler.obtainMessage(MSG_TICK)); + isFirst = false; + } + } else { + Log.d(TAG, "addAudioData mHandler is null"); + } + } + public void stopAs(){ + if (mHandler != null) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_TERMINATE)); + } else { + mIsStopRequest = true; + Log.d(TAG, "The thread has not started yet"); + } + } + } + private AudioPassThruCapabilities getDefaultAudioPassThruCapabilities(){ + AudioPassThruCapabilities aptCapabilities = new AudioPassThruCapabilities(); + aptCapabilities.setAudioType(AudioType.PCM); + aptCapabilities.setBitsPerSample(BitsPerSample._16_BIT); + aptCapabilities.setSamplingRate(SamplingRate._16KHZ); + return aptCapabilities; + } /** * Pushes raw audio data to SDL Core. * The audio file will be played immediately. If another audio file is currently playing, diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/BaseAudioDecoder.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/BaseAudioDecoder.java index 14ad466204..cdb184c6bf 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/BaseAudioDecoder.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/BaseAudioDecoder.java @@ -40,6 +40,7 @@ import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; +import android.util.Log; import com.smartdevicelink.managers.audio.AudioStreamManager.SampleType; import com.smartdevicelink.proxy.rpc.AudioPassThruCapabilities; @@ -48,6 +49,7 @@ import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.ArrayList; @SuppressWarnings("WeakerAccess") @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) @@ -186,7 +188,7 @@ protected MediaCodec.BufferInfo onInputBufferAvailable(@NonNull MediaExtractor e return bufferInfo; } - protected SampleBuffer onOutputBufferAvailable(@NonNull ByteBuffer outputBuffer) { + protected ArrayList onOutputBufferAvailable(@NonNull ByteBuffer outputBuffer) { double outputPresentationTimeUs = lastOutputPresentationTimeUs; double outputDurationPerSampleUs = 1000000.0 / (double)outputSampleRate; @@ -196,30 +198,31 @@ protected SampleBuffer onOutputBufferAvailable(@NonNull ByteBuffer outputBuffer) // wrap the output buffer to make it provide audio samples SampleBuffer outputSampleBuffer = SampleBuffer.wrap(outputBuffer, outputSampleType, outputChannelCount, (long)outputPresentationTimeUs); outputSampleBuffer.position(0); - - // the buffer size is related to the output and target sample rate - // add 2 samples to round up and add an extra sample - int sampleSize = outputSampleBuffer.limit() * targetSampleRate / outputSampleRate + 2; - - SampleBuffer targetSampleBuffer = SampleBuffer.allocate(sampleSize, targetSampleType, ByteOrder.LITTLE_ENDIAN, (long)targetPresentationTimeUs); + SampleBuffer targetSampleBuffer = null; Double sample; - + ArrayList targetSampleBufferList = new ArrayList<>(); do { sample = sampleAtTargetTime(lastOutputSample, outputSampleBuffer, outputPresentationTimeUs, outputDurationPerSampleUs, targetPresentationTimeUs); + if(targetSampleBuffer == null){ + //1 second worth of data + targetSampleBuffer = SampleBuffer.allocate((targetSampleRate/1000) * 1024, targetSampleType, ByteOrder.LITTLE_ENDIAN, (long)targetPresentationTimeUs); + } if (sample != null) { targetSampleBuffer.put(sample); targetPresentationTimeUs += targetDurationPerSampleUs; } + if(sample == null || targetSampleBuffer.getByteBuffer().remaining() < 2){ + targetSampleBuffer.limit(targetSampleBuffer.position()); + targetSampleBuffer.position(0); + targetSampleBufferList.add(targetSampleBuffer); + targetSampleBuffer = null; + } } while (sample != null); lastTargetPresentationTimeUs = targetPresentationTimeUs; lastOutputPresentationTimeUs += outputSampleBuffer.limit() * outputDurationPerSampleUs; lastOutputSample = outputSampleBuffer.get(outputSampleBuffer.limit() - 1); - - targetSampleBuffer.limit(targetSampleBuffer.position()); - targetSampleBuffer.position(0); - - return targetSampleBuffer; + return targetSampleBufferList; } protected void onOutputFormatChanged(@NonNull MediaFormat mediaFormat) { From ea3f0e114c9262a8047e93604e0eca99d4565b9e Mon Sep 17 00:00:00 2001 From: zx5656 <103409@ad.nexty-ele.com> Date: Fri, 17 Jul 2020 16:38:28 +0900 Subject: [PATCH 2/3] change log API --- .../managers/audio/AudioStreamManager.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java index 4abc3310c4..0544bea8da 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java @@ -472,7 +472,7 @@ public void onDecoderError(Exception e) { } private void startAudioStreamThread(final Decoder _decoder){ - Log.d(TAG, "startAudioStreamThread() queue.size():" + queue.size()); + DebugTool.logInfo(TAG, "startAudioStreamThread() queue.size():" + queue.size()); if (streamingStateMachine.getState() != StreamingStateMachine.STARTED || sdlAudioStream == null) { cleanAudioStreamThread(); return; @@ -480,7 +480,7 @@ private void startAudioStreamThread(final Decoder _decoder){ if(_decoder != null){ if(_decoder.isInterrupt()){ //Stop AS data - Log.d(TAG, "Audio playback interrupted"); + DebugTool.logInfo(TAG, "Audio playback interrupted"); finish(null,true); synchronized (queue) { while (queue.size() > 0){ @@ -630,7 +630,7 @@ public void handleMessage(Message msg) { if(mAudioBufferList != null && mAudioBufferList.size() > 0){ while (true){ if (streamingStateMachine.getState() != StreamingStateMachine.STARTED || sdlAudioStream == null) { - Log.e(TAG, "Streaming Status error:" + streamingStateMachine.getState() ); + DebugTool.logError(TAG, "Streaming Status error:" + streamingStateMachine.getState() ); Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override @@ -652,7 +652,7 @@ public void run() { sdlAudioStream.sendAudio(sBuffer.getByteBuffer(), sBuffer.getPresentationTimeUs(), null); } else { //Delay data transmission - Log.d(TAG, "Delay the call to sendAudio"); + DebugTool.logInfo(TAG, "Delay the call to sendAudio"); delay = 500; break; } @@ -664,7 +664,7 @@ public void run() { Handler handler = new Handler(Looper.getMainLooper()); long lDelay = getDelayStartAudioTime(); - Log.d(TAG, "Playback end notification. lDelay:" + lDelay); + DebugTool.logInfo(TAG, "Playback end notification. lDelay:" + lDelay); handler.postDelayed(new Runnable() { @Override public void run() { @@ -672,7 +672,7 @@ public void run() { if (queue.size() > 0) { finish(queue.poll().getCompletionListener(),isSuccess); } else { - Log.e(TAG, "There is no element of the queue"); + DebugTool.logError(TAG, "There is no element of the queue"); } if (queue.size() > 0) { startAudioStreamThread(null); @@ -696,7 +696,7 @@ public void run() { mAudioBufferList = new ArrayList<>(); startTime = System.currentTimeMillis(); if(startTime < mEndTimeOfSendData){ - Log.d(TAG, "The playback end time exceeds the current time. EndTime:" + mEndTimeOfSendData); + DebugTool.logInfo(TAG, "The playback end time exceeds the current time. EndTime:" + mEndTimeOfSendData); startTime = mEndTimeOfSendData; } } @@ -727,15 +727,15 @@ public void run() { }; } if(mIsStopRequest){ - Log.d(TAG, "StopRequest is valid"); + DebugTool.logInfo(TAG, "StopRequest is valid"); return; } if (mStartedCallback != null) { mStartedCallback.run(); } - Log.d(TAG, "Starting SendAudioStreamThread"); + DebugTool.logInfo(TAG, "Starting SendAudioStreamThread"); Looper.loop(); - Log.d(TAG, "Stopping SendAudioStreamThread"); + DebugTool.logInfo(TAG, "Stopping SendAudioStreamThread"); } public void addAudioData(final ArrayList sendBufferList){ @@ -749,7 +749,7 @@ public void addAudioData(final ArrayList sendBufferList){ isFirst = false; } } else { - Log.d(TAG, "addAudioData mHandler is null"); + DebugTool.logInfo(TAG, "addAudioData mHandler is null"); } } @@ -758,7 +758,7 @@ public void stopAs(){ mHandler.sendMessage(mHandler.obtainMessage(MSG_TERMINATE)); } else { mIsStopRequest = true; - Log.d(TAG, "The thread has not started yet"); + DebugTool.logInfo(TAG, "The thread has not started yet"); } } } From dbf29232dde0acf5265071ce7ea137750d341d0f Mon Sep 17 00:00:00 2001 From: zx-300 Date: Tue, 3 Nov 2020 16:36:13 +0800 Subject: [PATCH 3/3] CI check error about pushResource --- .../managers/audio/AudioStreamManagerTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/audio/AudioStreamManagerTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/audio/AudioStreamManagerTest.java index ce8183b9d4..a3c6ba041e 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/audio/AudioStreamManagerTest.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/audio/AudioStreamManagerTest.java @@ -334,7 +334,7 @@ public IAudioStreamListener startAudioStream(SdlSession session) { public void onComplete(boolean success) { assertEquals(true, success); - manager.pushResource(com.smartdevicelink.test.R.raw.test_audio_square_250hz_80amp_1s, mockFileListener); + manager.pushResource(com.smartdevicelink.test.R.raw.test_audio_square_250hz_80amp_1s, mockFileListener, true); } }); @@ -567,7 +567,7 @@ public void onComplete(boolean success) { public void onComplete(boolean success) { assertEquals(true, success); - manager.pushResource(com.smartdevicelink.test.R.raw.test_audio_square_250hz_80amp_1s, mockFileListener); + manager.pushResource(com.smartdevicelink.test.R.raw.test_audio_square_250hz_80amp_1s, mockFileListener, true); } });