mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 09:37:36 -05:00
#30 - changes internal MP3 temporary recording stream management so that the broadcast model manages the recordings and sends the completed recordings to the broadcasters to avoid 2 or more streams recording MP3 files for the same audio call.
Note: there is a bug where temporary recordings are not being consistently deleted that I'm still working on.
This commit is contained in:
@@ -36,7 +36,7 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public abstract class AudioBroadcaster implements IAudioPacketListener
|
public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( AudioBroadcaster.class );
|
private final static Logger mLog = LoggerFactory.getLogger( AudioBroadcaster.class );
|
||||||
|
|
||||||
@@ -46,14 +46,13 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
private ScheduledFuture mScheduledTask;
|
private ScheduledFuture mScheduledTask;
|
||||||
|
|
||||||
private RecordingQueueProcessor mRecordingQueueProcessor = new RecordingQueueProcessor();
|
private RecordingQueueProcessor mRecordingQueueProcessor = new RecordingQueueProcessor();
|
||||||
private LinkedTransferQueue<StreamableAudioRecording> mAudioRecordingQueue = new LinkedTransferQueue<>();
|
private LinkedTransferQueue<AudioRecording> mAudioRecordingQueue = new LinkedTransferQueue<>();
|
||||||
|
|
||||||
private ISilenceGenerator mSilenceGenerator;
|
private ISilenceGenerator mSilenceGenerator;
|
||||||
|
|
||||||
private Listener<BroadcastEvent> mBroadcastEventListener;
|
private Listener<BroadcastEvent> mBroadcastEventListener;
|
||||||
private BroadcastState mBroadcastState = BroadcastState.READY;
|
private BroadcastState mBroadcastState = BroadcastState.READY;
|
||||||
|
|
||||||
private StreamManager mStreamManager;
|
|
||||||
private int mStreamedAudioCount = 0;
|
private int mStreamedAudioCount = 0;
|
||||||
private BroadcastConfiguration mBroadcastConfiguration;
|
private BroadcastConfiguration mBroadcastConfiguration;
|
||||||
private long mDelay;
|
private long mDelay;
|
||||||
@@ -85,11 +84,7 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
mThreadPoolManager = threadPoolManager;
|
mThreadPoolManager = threadPoolManager;
|
||||||
mBroadcastConfiguration = broadcastConfiguration;
|
mBroadcastConfiguration = broadcastConfiguration;
|
||||||
mDelay = mBroadcastConfiguration.getDelay();
|
mDelay = mBroadcastConfiguration.getDelay();
|
||||||
|
|
||||||
mSilenceGenerator = BroadcastFactory.getSilenceGenerator(broadcastConfiguration.getBroadcastFormat());
|
mSilenceGenerator = BroadcastFactory.getSilenceGenerator(broadcastConfiguration.getBroadcastFormat());
|
||||||
|
|
||||||
mStreamManager = new StreamManager(threadPoolManager, this,
|
|
||||||
SystemProperties.getInstance().getApplicationFolder(BroadcastModel.TEMPORARY_STREAM_DIRECTORY));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ThreadPoolManager getThreadPoolManager()
|
protected ThreadPoolManager getThreadPoolManager()
|
||||||
@@ -133,8 +128,6 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
{
|
{
|
||||||
if(mStreaming.compareAndSet(false, true))
|
if(mStreaming.compareAndSet(false, true))
|
||||||
{
|
{
|
||||||
mStreamManager.start();
|
|
||||||
|
|
||||||
if(mScheduledTask == null)
|
if(mScheduledTask == null)
|
||||||
{
|
{
|
||||||
if(mThreadPoolManager != null)
|
if(mThreadPoolManager != null)
|
||||||
@@ -153,8 +146,6 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
{
|
{
|
||||||
if(mStreaming.compareAndSet(true, false))
|
if(mStreaming.compareAndSet(true, false))
|
||||||
{
|
{
|
||||||
mStreamManager.stop();
|
|
||||||
|
|
||||||
if(mThreadPoolManager != null && mScheduledTask != null)
|
if(mThreadPoolManager != null && mScheduledTask != null)
|
||||||
{
|
{
|
||||||
mThreadPoolManager.cancel(mScheduledTask);
|
mThreadPoolManager.cancel(mScheduledTask);
|
||||||
@@ -201,7 +192,7 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
*
|
*
|
||||||
* @param recording to queue for broadcasting
|
* @param recording to queue for broadcasting
|
||||||
*/
|
*/
|
||||||
public void receive(StreamableAudioRecording recording)
|
public void receive(AudioRecording recording)
|
||||||
{
|
{
|
||||||
if(connected())
|
if(connected())
|
||||||
{
|
{
|
||||||
@@ -210,24 +201,6 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleanup method to remove a temporary recording file from disk.
|
|
||||||
*
|
|
||||||
* @param recording to remove
|
|
||||||
*/
|
|
||||||
private void removeRecording(StreamableAudioRecording recording)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Files.delete(recording.getPath());
|
|
||||||
}
|
|
||||||
catch(IOException ioe)
|
|
||||||
{
|
|
||||||
mLog.error("Stream [" + getBroadcastConfiguration().getName() + "] - error deleting temporary internet " +
|
|
||||||
"recording file: " + recording.getPath().toString() + " - " + ioe.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcast configuration used by this broadcaster
|
* Broadcast configuration used by this broadcaster
|
||||||
*/
|
*/
|
||||||
@@ -236,15 +209,6 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
return mBroadcastConfiguration;
|
return mBroadcastConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* IAudioPacketListener interface method to gain access to the internal stream manager for enqueuing audio packets.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Listener<AudioPacket> getAudioPacketListener()
|
|
||||||
{
|
|
||||||
return mStreamManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the listener to receive broadcast events/state changes
|
* Registers the listener to receive broadcast events/state changes
|
||||||
*/
|
*/
|
||||||
@@ -291,11 +255,11 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
|
|
||||||
if(!connected())
|
if(!connected())
|
||||||
{
|
{
|
||||||
StreamableAudioRecording recording = mAudioRecordingQueue.poll();
|
//Remove all pending audio recordings
|
||||||
|
AudioRecording recording = mAudioRecordingQueue.poll();
|
||||||
|
|
||||||
while(recording != null)
|
while(recording != null)
|
||||||
{
|
{
|
||||||
removeRecording(recording);
|
|
||||||
recording = mAudioRecordingQueue.poll();
|
recording = mAudioRecordingQueue.poll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -426,37 +390,38 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
|
|
||||||
mInputStream = null;
|
mInputStream = null;
|
||||||
|
|
||||||
StreamableAudioRecording nextRecording = mAudioRecordingQueue.peek();
|
AudioRecording nextRecording = mAudioRecordingQueue.peek();
|
||||||
|
|
||||||
if(nextRecording != null && nextRecording.getStartTime() + mDelay <= System.currentTimeMillis())
|
if(nextRecording != null && nextRecording.getStartTime() + mDelay <= System.currentTimeMillis())
|
||||||
{
|
{
|
||||||
StreamableAudioRecording recording = mAudioRecordingQueue.poll();
|
AudioRecording recording = mAudioRecordingQueue.poll();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
byte[] audio = Files.readAllBytes(recording.getPath());
|
if(Files.exists(recording.getPath()))
|
||||||
|
|
||||||
if(audio != null && audio.length > 0)
|
|
||||||
{
|
{
|
||||||
mInputStream = new ByteArrayInputStream(audio);
|
byte[] audio = Files.readAllBytes(recording.getPath());
|
||||||
|
|
||||||
mFinalSilencePadding = PROCESSOR_RUN_INTERVAL_MS -
|
if(audio != null && audio.length > 0)
|
||||||
(recording.getRecordingLength() % PROCESSOR_RUN_INTERVAL_MS);
|
|
||||||
|
|
||||||
while(mFinalSilencePadding >= PROCESSOR_RUN_INTERVAL_MS)
|
|
||||||
{
|
{
|
||||||
mFinalSilencePadding -= PROCESSOR_RUN_INTERVAL_MS;
|
mInputStream = new ByteArrayInputStream(audio);
|
||||||
}
|
|
||||||
|
|
||||||
if(connected())
|
mFinalSilencePadding = PROCESSOR_RUN_INTERVAL_MS -
|
||||||
{
|
(recording.getRecordingLength() % PROCESSOR_RUN_INTERVAL_MS);
|
||||||
broadcastMetadata(recording.getAudioMetadata());
|
|
||||||
}
|
|
||||||
|
|
||||||
metadataUpdateRequired = false;
|
while(mFinalSilencePadding >= PROCESSOR_RUN_INTERVAL_MS)
|
||||||
|
{
|
||||||
|
mFinalSilencePadding -= PROCESSOR_RUN_INTERVAL_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(connected())
|
||||||
|
{
|
||||||
|
broadcastMetadata(recording.getAudioMetadata());
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataUpdateRequired = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRecording(recording);
|
|
||||||
}
|
}
|
||||||
catch(IOException ioe)
|
catch(IOException ioe)
|
||||||
{
|
{
|
||||||
@@ -467,6 +432,8 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
metadataUpdateRequired = false;
|
metadataUpdateRequired = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recording.removePendingReplay();
|
||||||
|
|
||||||
broadcast(new BroadcastEvent(AudioBroadcaster.this, BroadcastEvent.Event.BROADCASTER_QUEUE_CHANGE));
|
broadcast(new BroadcastEvent(AudioBroadcaster.this, BroadcastEvent.Event.BROADCASTER_QUEUE_CHANGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,13 +21,15 @@ package audio.broadcast;
|
|||||||
import audio.metadata.AudioMetadata;
|
import audio.metadata.AudioMetadata;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class StreamableAudioRecording implements Comparable<StreamableAudioRecording>
|
public class AudioRecording implements Comparable<AudioRecording>
|
||||||
{
|
{
|
||||||
private Path mPath;
|
private Path mPath;
|
||||||
private AudioMetadata mAudioMetadata;
|
private AudioMetadata mAudioMetadata;
|
||||||
private long mStartTime;
|
private long mStartTime;
|
||||||
private long mRecordingLength;
|
private long mRecordingLength;
|
||||||
|
private AtomicInteger mPendingReplayCount = new AtomicInteger();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Audio recording that is ready to be streamed
|
* Audio recording that is ready to be streamed
|
||||||
@@ -36,7 +38,7 @@ public class StreamableAudioRecording implements Comparable<StreamableAudioRecor
|
|||||||
* @param start time of recording in milliseconds since epoch
|
* @param start time of recording in milliseconds since epoch
|
||||||
* @param recordingLength in milliseconds
|
* @param recordingLength in milliseconds
|
||||||
*/
|
*/
|
||||||
public StreamableAudioRecording(Path path, AudioMetadata audioMetadata, long start, long recordingLength)
|
public AudioRecording(Path path, AudioMetadata audioMetadata, long start, long recordingLength)
|
||||||
{
|
{
|
||||||
mPath = path;
|
mPath = path;
|
||||||
mAudioMetadata = audioMetadata;
|
mAudioMetadata = audioMetadata;
|
||||||
@@ -81,8 +83,33 @@ public class StreamableAudioRecording implements Comparable<StreamableAudioRecor
|
|||||||
* Implements comparable for sorting recordings based on start time in ascending order
|
* Implements comparable for sorting recordings based on start time in ascending order
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(StreamableAudioRecording otherRecording)
|
public int compareTo(AudioRecording otherRecording)
|
||||||
{
|
{
|
||||||
return Long.compare(getStartTime(), otherRecording.getStartTime());
|
return Long.compare(getStartTime(), otherRecording.getStartTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the count of pending replays.
|
||||||
|
*/
|
||||||
|
public void addPendingReplay()
|
||||||
|
{
|
||||||
|
mPendingReplayCount.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements the count of pending replays.
|
||||||
|
*/
|
||||||
|
public void removePendingReplay()
|
||||||
|
{
|
||||||
|
mPendingReplayCount.decrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if there are any remaining pending replays. Once the pending replay count is less than or equal to
|
||||||
|
* zero, the recording can be deleted.
|
||||||
|
*/
|
||||||
|
public boolean hasPendingReplays()
|
||||||
|
{
|
||||||
|
return mPendingReplayCount.get() <= 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@ package audio.broadcast;
|
|||||||
|
|
||||||
import alias.id.broadcast.BroadcastChannel;
|
import alias.id.broadcast.BroadcastChannel;
|
||||||
import audio.AudioPacket;
|
import audio.AudioPacket;
|
||||||
|
import audio.metadata.AudioMetadata;
|
||||||
import controller.ThreadPoolManager;
|
import controller.ThreadPoolManager;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -36,6 +37,7 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
@@ -60,12 +62,13 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
public static final int COLUMN_BROADCASTER_STREAMED_COUNT = 4;
|
public static final int COLUMN_BROADCASTER_STREAMED_COUNT = 4;
|
||||||
|
|
||||||
private List<BroadcastConfiguration> mBroadcastConfigurations = new CopyOnWriteArrayList<>();
|
private List<BroadcastConfiguration> mBroadcastConfigurations = new CopyOnWriteArrayList<>();
|
||||||
|
private List<AudioRecording> mRecordingQueue = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
private Map<String,BroadcastConfiguration> mBroadcastConfigurationMap = new HashMap<>();
|
private Map<String,BroadcastConfiguration> mBroadcastConfigurationMap = new HashMap<>();
|
||||||
private Map<String,AudioBroadcaster> mBroadcasterMap = new HashMap<>();
|
private Map<String,AudioBroadcaster> mBroadcasterMap = new HashMap<>();
|
||||||
private ThreadPoolManager mThreadPoolManager;
|
private ThreadPoolManager mThreadPoolManager;
|
||||||
private SettingsManager mSettingsManager;
|
private SettingsManager mSettingsManager;
|
||||||
|
private StreamManager mStreamManager;
|
||||||
private Broadcaster<BroadcastEvent> mBroadcastEventBroadcaster = new Broadcaster<>();
|
private Broadcaster<BroadcastEvent> mBroadcastEventBroadcaster = new Broadcaster<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,6 +78,13 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
{
|
{
|
||||||
mThreadPoolManager = threadPoolManager;
|
mThreadPoolManager = threadPoolManager;
|
||||||
mSettingsManager = settingsManager;
|
mSettingsManager = settingsManager;
|
||||||
|
mStreamManager = new StreamManager(threadPoolManager, new CompletedRecordingListener(), BroadcastFormat.MP3,
|
||||||
|
SystemProperties.getInstance().getApplicationFolder(TEMPORARY_STREAM_DIRECTORY));
|
||||||
|
mStreamManager.start();
|
||||||
|
|
||||||
|
//Monitor to remove temporary recording files that have been streamed by all audio broadcasters
|
||||||
|
mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_RECORDING,
|
||||||
|
new RecordingDeletionMonitor(), 15l, TimeUnit.SECONDS );
|
||||||
|
|
||||||
removeOrphanedTemporaryRecordings();
|
removeOrphanedTemporaryRecordings();
|
||||||
}
|
}
|
||||||
@@ -265,22 +275,10 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
@Override
|
@Override
|
||||||
public void receive(AudioPacket audioPacket)
|
public void receive(AudioPacket audioPacket)
|
||||||
{
|
{
|
||||||
if (audioPacket.hasAudioMetadata() && audioPacket.getAudioMetadata().isStreamable())
|
if (audioPacket.hasAudioMetadata() &&
|
||||||
|
audioPacket.getAudioMetadata().isStreamable())
|
||||||
{
|
{
|
||||||
for (BroadcastChannel broadcastChannel : audioPacket.getAudioMetadata().getBroadcastChannels())
|
mStreamManager.receive(audioPacket);
|
||||||
{
|
|
||||||
String channelName = broadcastChannel.getChannelName();
|
|
||||||
|
|
||||||
if (channelName != null)
|
|
||||||
{
|
|
||||||
AudioBroadcaster audioBroadcaster = getBroadcaster(channelName);
|
|
||||||
|
|
||||||
if (audioBroadcaster != null)
|
|
||||||
{
|
|
||||||
audioBroadcaster.getAudioPacketListener().receive(audioPacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,6 +598,25 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup method to remove a temporary recording file from disk.
|
||||||
|
*
|
||||||
|
* @param recording to remove
|
||||||
|
*/
|
||||||
|
private void removeRecording(AudioRecording recording)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Files.delete(recording.getPath());
|
||||||
|
}
|
||||||
|
catch(IOException ioe)
|
||||||
|
{
|
||||||
|
mLog.error("Error deleting temporary internet recording file: " + recording.getPath().toString() + " - " +
|
||||||
|
ioe.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes any temporary stream recordings left-over from the previous application run.
|
* Removes any temporary stream recordings left-over from the previous application run.
|
||||||
*
|
*
|
||||||
@@ -659,4 +676,80 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
createBroadcaster(mBroadcastConfiguration);
|
createBroadcaster(mBroadcastConfiguration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes completed audio recordings and distributes them to the audio broadcasters. Adds the recording to
|
||||||
|
* the audio recording queue to be monitored for deletion.
|
||||||
|
*/
|
||||||
|
public class CompletedRecordingListener implements Listener<AudioRecording>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void receive(AudioRecording audioRecording)
|
||||||
|
{
|
||||||
|
AudioMetadata metadata = audioRecording.getAudioMetadata();
|
||||||
|
|
||||||
|
if(metadata != null && metadata.isStreamable())
|
||||||
|
{
|
||||||
|
for (BroadcastChannel broadcastChannel : metadata.getBroadcastChannels())
|
||||||
|
{
|
||||||
|
String channelName = broadcastChannel.getChannelName();
|
||||||
|
|
||||||
|
if (channelName != null)
|
||||||
|
{
|
||||||
|
AudioBroadcaster audioBroadcaster = getBroadcaster(channelName);
|
||||||
|
|
||||||
|
if (audioBroadcaster != null)
|
||||||
|
{
|
||||||
|
audioRecording.addPendingReplay();
|
||||||
|
audioBroadcaster.receive(audioRecording);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mRecordingQueue.add(audioRecording);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monitors the recording queue and removes any recordings that have no pending replays by audio broadcasters
|
||||||
|
*/
|
||||||
|
public class RecordingDeletionMonitor implements Runnable
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<AudioRecording> recordingsToDelete = new ArrayList<>();
|
||||||
|
|
||||||
|
Iterator<AudioRecording> it = mRecordingQueue.iterator();
|
||||||
|
|
||||||
|
AudioRecording recording;
|
||||||
|
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
recording = it.next();
|
||||||
|
|
||||||
|
if(!recording.hasPendingReplays())
|
||||||
|
{
|
||||||
|
recordingsToDelete.add(recording);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!recordingsToDelete.isEmpty())
|
||||||
|
{
|
||||||
|
for(AudioRecording recordingToDelete: recordingsToDelete)
|
||||||
|
{
|
||||||
|
mRecordingQueue.remove(recordingToDelete);
|
||||||
|
removeRecording(recordingToDelete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
mLog.error("Error while checking audio recording queue for recordings to delete", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
private static AtomicInteger sNextRecordingNumber = new AtomicInteger();
|
private static AtomicInteger sNextRecordingNumber = new AtomicInteger();
|
||||||
|
|
||||||
private ThreadPoolManager mThreadPoolManager;
|
private ThreadPoolManager mThreadPoolManager;
|
||||||
private AudioBroadcaster mAudioBroadcaster;
|
private Listener<AudioRecording> mAudioRecordingListener;
|
||||||
|
private BroadcastFormat mBroadcastFormat;
|
||||||
private Path mTempDirectory;
|
private Path mTempDirectory;
|
||||||
private Map<Integer, AudioRecorder> mStreamRecorders = new HashMap<>();
|
private Map<Integer, AudioRecorder> mStreamRecorders = new HashMap<>();
|
||||||
private Runnable mRecorderMonitor;
|
private Runnable mRecorderMonitor;
|
||||||
@@ -61,16 +62,18 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
* Completed streamable audio recordings are nominated to the output listener (for broadcast) upon completion
|
* Completed streamable audio recordings are nominated to the output listener (for broadcast) upon completion
|
||||||
*
|
*
|
||||||
* @param threadPoolManager for scheduling runnables
|
* @param threadPoolManager for scheduling runnables
|
||||||
* @param audioBroadcaster to receive completed streamable audio recordings
|
* @param listener to receive completed audio recordings
|
||||||
* @param tempDirectory where to store temporary audio recordings
|
* @param tempDirectory where to store temporary audio recordings
|
||||||
*/
|
*/
|
||||||
public StreamManager(ThreadPoolManager threadPoolManager, AudioBroadcaster audioBroadcaster, Path tempDirectory)
|
public StreamManager(ThreadPoolManager threadPoolManager, Listener<AudioRecording> listener,
|
||||||
|
BroadcastFormat broadcastFormat, Path tempDirectory)
|
||||||
{
|
{
|
||||||
assert(tempDirectory != null && Files.isDirectory(tempDirectory));
|
assert(tempDirectory != null && Files.isDirectory(tempDirectory));
|
||||||
assert(mThreadPoolManager != null);
|
assert(mThreadPoolManager != null);
|
||||||
|
|
||||||
mThreadPoolManager = threadPoolManager;
|
mThreadPoolManager = threadPoolManager;
|
||||||
mAudioBroadcaster = audioBroadcaster;
|
mAudioRecordingListener = listener;
|
||||||
|
mBroadcastFormat = broadcastFormat;
|
||||||
mTempDirectory = tempDirectory;
|
mTempDirectory = tempDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +144,7 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
|
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
|
||||||
mAudioBroadcaster.getBroadcastConfiguration().getBroadcastFormat());
|
mBroadcastFormat);
|
||||||
recorder.start(mThreadPoolManager.getScheduledExecutorService());
|
recorder.start(mThreadPoolManager.getScheduledExecutorService());
|
||||||
recorder.receive(audioPacket);
|
recorder.receive(audioPacket);
|
||||||
mStreamRecorders.put(sourceChannelID, recorder);
|
mStreamRecorders.put(sourceChannelID, recorder);
|
||||||
@@ -178,13 +181,13 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
@Override
|
@Override
|
||||||
public void receive(AudioRecorder audioRecorder)
|
public void receive(AudioRecorder audioRecorder)
|
||||||
{
|
{
|
||||||
StreamableAudioRecording streamableAudioRecording =
|
AudioRecording audioRecording =
|
||||||
new StreamableAudioRecording(audioRecorder.getPath(), audioRecorder.getMetadata(),
|
new AudioRecording(audioRecorder.getPath(), audioRecorder.getMetadata(),
|
||||||
audioRecorder.getTimeRecordingStart(), audioRecorder.getRecordingLength());
|
audioRecorder.getTimeRecordingStart(), audioRecorder.getRecordingLength());
|
||||||
|
|
||||||
if(mAudioBroadcaster != null)
|
if(mAudioRecordingListener != null)
|
||||||
{
|
{
|
||||||
mAudioBroadcaster.receive(streamableAudioRecording);
|
mAudioRecordingListener.receive(audioRecording);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -201,7 +204,7 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
sb.append(BroadcastModel.TEMPORARY_STREAM_FILE_SUFFIX);
|
sb.append(BroadcastModel.TEMPORARY_STREAM_FILE_SUFFIX);
|
||||||
sb.append(sNextRecordingNumber.incrementAndGet()).append("_");
|
sb.append(sNextRecordingNumber.incrementAndGet()).append("_");
|
||||||
sb.append( TimeStamp.getLongTimeStamp( "_" ) );
|
sb.append( TimeStamp.getLongTimeStamp( "_" ) );
|
||||||
sb.append( mAudioBroadcaster.getBroadcastConfiguration().getBroadcastFormat().getFileExtension() );
|
sb.append( mBroadcastFormat.getFileExtension() );
|
||||||
|
|
||||||
Path temporaryRecordingPath = mTempDirectory.resolve(sb.toString());
|
Path temporaryRecordingPath = mTempDirectory.resolve(sb.toString());
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,11 @@ public abstract class AudioRecorder extends Module implements Listener<AudioPack
|
|||||||
{
|
{
|
||||||
mRecordingClosedListener = listener;
|
mRecordingClosedListener = listener;
|
||||||
|
|
||||||
mRunning.set(false);
|
if(!mRunning.compareAndSet(true, false))
|
||||||
|
{
|
||||||
|
//If this recorder has already been stopped/closed, immediately inform the listener
|
||||||
|
mRecordingClosedListener.receive(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user