mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Resolves issue with periodic instances of audio recorders not being shutdown correctly. Adds recorder monitor as a fail-safe to automatically shutdown recorders that don't receive an end-call indication.
This commit is contained in:
@@ -29,79 +29,102 @@ import util.ThreadPool;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RecorderManager implements Listener<AudioPacket>
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(RecorderManager.class);
|
||||
|
||||
public static final int AUDIO_SAMPLE_RATE = 8000;
|
||||
public static final long IDLE_RECORDER_REMOVAL_THRESHOLD = 6000; //6 seconds
|
||||
|
||||
private Map<String,RealBufferWaveRecorder> mRecorders = new HashMap<>();
|
||||
private ScheduledFuture<?> mRecorderMonitorFuture;
|
||||
|
||||
private boolean mCanStartNewRecorders = true;
|
||||
|
||||
/**
|
||||
* Manages all audio recording for all processing channels. Reconstructs audio streams and distributes channel
|
||||
* audio to each of the recorders, starting and stopping the recorders as needed.
|
||||
* Audio recording manager. Monitors stream of audio packets produced by decoding channels and automatically starts
|
||||
* audio recorders when the channel's metadata designates a call as recordable. Routes call audio to each recorder
|
||||
* based on audio packet metadata. Recorders are shutdown when the channel sends an end-call audio packet
|
||||
* indicating that the call is complete. A separate recording monitor periodically checks for idled recorders to
|
||||
* be stopped for cases when the channel fails to send an end-call audio packet.
|
||||
*/
|
||||
public RecorderManager()
|
||||
{
|
||||
mRecorderMonitorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new RecorderMonitor(), 2,
|
||||
2, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares this class for shutdown
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
if(mRecorderMonitorFuture != null)
|
||||
{
|
||||
mRecorderMonitorFuture.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary ingest point for audio packets from all decoding channels
|
||||
* @param audioPacket to process
|
||||
*/
|
||||
@Override
|
||||
public void receive(AudioPacket audioPacket)
|
||||
{
|
||||
if(audioPacket.hasMetadata() &&
|
||||
audioPacket.getMetadata().isRecordable())
|
||||
if(audioPacket.hasMetadata() && audioPacket.getMetadata().isRecordable())
|
||||
{
|
||||
String identifier = audioPacket.getMetadata().getUniqueIdentifier();
|
||||
|
||||
if(mRecorders.containsKey(identifier))
|
||||
synchronized(mRecorders)
|
||||
{
|
||||
RealBufferWaveRecorder recorder = mRecorders.get(identifier);
|
||||
|
||||
if(audioPacket.getType() == AudioPacket.Type.AUDIO)
|
||||
if(mRecorders.containsKey(identifier))
|
||||
{
|
||||
recorder.receive(audioPacket.getAudioBuffer());
|
||||
}
|
||||
else if(audioPacket.getType() == AudioPacket.Type.END)
|
||||
{
|
||||
RealBufferWaveRecorder finished = mRecorders.remove(identifier);
|
||||
finished.stop();
|
||||
}
|
||||
}
|
||||
else if(audioPacket.getType() == AudioPacket.Type.AUDIO)
|
||||
{
|
||||
if(mCanStartNewRecorders)
|
||||
{
|
||||
String filePrefix = getFilePrefix(audioPacket);
|
||||
RealBufferWaveRecorder recorder = mRecorders.get(identifier);
|
||||
|
||||
RealBufferWaveRecorder recorder = null;
|
||||
|
||||
try
|
||||
if(audioPacket.getType() == AudioPacket.Type.AUDIO)
|
||||
{
|
||||
recorder = new RealBufferWaveRecorder(AUDIO_SAMPLE_RATE, filePrefix);
|
||||
|
||||
recorder.start(ThreadPool.SCHEDULED);
|
||||
|
||||
recorder.receive(audioPacket.getAudioBuffer());
|
||||
mRecorders.put(identifier, recorder);
|
||||
}
|
||||
catch(Exception ioe)
|
||||
else if(audioPacket.getType() == AudioPacket.Type.END)
|
||||
{
|
||||
mCanStartNewRecorders = false;
|
||||
RealBufferWaveRecorder finished = mRecorders.remove(identifier);
|
||||
finished.stop();
|
||||
}
|
||||
}
|
||||
else if(audioPacket.getType() == AudioPacket.Type.AUDIO)
|
||||
{
|
||||
if(mCanStartNewRecorders)
|
||||
{
|
||||
String filePrefix = getFilePrefix(audioPacket);
|
||||
|
||||
mLog.error("Error attempting to start new audio wave recorder. All (future) audio recording " +
|
||||
"is disabled", ioe);
|
||||
RealBufferWaveRecorder recorder = null;
|
||||
|
||||
if(recorder != null)
|
||||
try
|
||||
{
|
||||
recorder.stop();
|
||||
recorder = new RealBufferWaveRecorder(AUDIO_SAMPLE_RATE, filePrefix);
|
||||
|
||||
recorder.start(ThreadPool.SCHEDULED);
|
||||
|
||||
recorder.receive(audioPacket.getAudioBuffer());
|
||||
mRecorders.put(identifier, recorder);
|
||||
}
|
||||
catch(Exception ioe)
|
||||
{
|
||||
mCanStartNewRecorders = false;
|
||||
|
||||
mLog.error("Error attempting to start new audio wave recorder. All (future) audio recording " +
|
||||
"is disabled", ioe);
|
||||
|
||||
if(recorder != null)
|
||||
{
|
||||
recorder.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,4 +173,31 @@ public class RecorderManager implements Listener<AudioPacket>
|
||||
|
||||
return new ComplexBufferWaveRecorder(AUDIO_SAMPLE_RATE, sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors currently running recorders and removes/closes any recorders that are idle more than 6 seconds
|
||||
*/
|
||||
public class RecorderMonitor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
synchronized(mRecorders)
|
||||
{
|
||||
Iterator<Map.Entry<String,RealBufferWaveRecorder>> it = mRecorders.entrySet().iterator();
|
||||
|
||||
while(it.hasNext())
|
||||
{
|
||||
Map.Entry<String,RealBufferWaveRecorder> entry = it.next();
|
||||
|
||||
if(entry.getValue().getLastBufferReceived() + IDLE_RECORDER_REMOVAL_THRESHOLD < System.currentTimeMillis())
|
||||
{
|
||||
mLog.info("Removing idle recorder [" + entry.getKey() + "]");
|
||||
it.remove();
|
||||
entry.getValue().stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user