#108 [WIP] Created new ThreadPool class with a static ScheduledThreadExecutor that is now shared by all components. Removed ThreadPoolManager from all classes. Consolidating into 1 pool seems to have reduced CPU usage by ~15-20%. Overall memory usage is also reduced.

This commit is contained in:
Denny
2017-01-22 09:33:47 -05:00
parent 523ac0314e
commit 6e94d9ce51
39 changed files with 8822 additions and 9118 deletions

View File

@@ -22,8 +22,6 @@ import audio.AudioEvent.Type;
import audio.output.AudioOutput;
import audio.output.MonoAudioOutput;
import audio.output.StereoAudioOutput;
import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import properties.SystemProperties;
@@ -32,6 +30,7 @@ import sample.Listener;
import source.mixer.MixerChannel;
import source.mixer.MixerChannelConfiguration;
import source.mixer.MixerManager;
import util.ThreadPool;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Mixer;
@@ -70,7 +69,6 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
private Broadcaster<AudioEvent> mControllerBroadcaster = new Broadcaster<>();
private ThreadPoolManager mThreadPoolManager;
private ScheduledFuture<?> mProcessingTask;
private MixerManager mMixerManager;
private MixerChannelConfiguration mMixerChannelConfiguration;
@@ -81,9 +79,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
* packets to any combination of outputs based on any alias audio routing
* options specified by the user.
*/
public AudioManager(ThreadPoolManager manager, MixerManager mixerManager)
public AudioManager(MixerManager mixerManager)
{
mThreadPoolManager = manager;
mMixerManager = mixerManager;
loadSettings();
@@ -145,14 +142,13 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
public void dispose()
{
if(mThreadPoolManager != null && mProcessingTask != null)
if(mProcessingTask != null)
{
mThreadPoolManager.cancel(mProcessingTask);
mProcessingTask.cancel(true);
}
mAudioPacketQueue.clear();
mThreadPoolManager = null;
mProcessingTask = null;
mChannelConnectionMap.clear();
@@ -224,9 +220,9 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
{
mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_STARTED);
if(mThreadPoolManager != null && mProcessingTask != null)
if(mProcessingTask != null)
{
mThreadPoolManager.cancel(mProcessingTask);
mProcessingTask.cancel(true);
}
disposeCurrentConfiguration();
@@ -234,19 +230,18 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
switch(entry.getMixerChannel())
{
case MONO:
AudioOutput mono = new MonoAudioOutput(mThreadPoolManager,
entry.getMixer());
AudioOutput mono = new MonoAudioOutput(entry.getMixer());
mAudioOutputConnections.add(new AudioOutputConnection(mono));
mAvailableConnectionCount++;
mAudioOutputMap.put(mono.getChannelName(), mono);
break;
case STEREO:
AudioOutput left = new StereoAudioOutput(mThreadPoolManager, entry.getMixer(), MixerChannel.LEFT);
AudioOutput left = new StereoAudioOutput(entry.getMixer(), MixerChannel.LEFT);
mAudioOutputConnections.add(new AudioOutputConnection(left));
mAvailableConnectionCount++;
mAudioOutputMap.put(left.getChannelName(), left);
AudioOutput right = new StereoAudioOutput(mThreadPoolManager, entry.getMixer(), MixerChannel.RIGHT);
AudioOutput right = new StereoAudioOutput(entry.getMixer(), MixerChannel.RIGHT);
mAudioOutputConnections.add(new AudioOutputConnection(right));
mAvailableConnectionCount++;
mAudioOutputMap.put(right.getChannelName(), right);
@@ -256,9 +251,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
+ "configuration: " + entry.getMixerChannel());
}
mProcessingTask = mThreadPoolManager.scheduleFixedRate(
ThreadType.AUDIO_PROCESSING, new AudioPacketProcessor(), 15,
TimeUnit.MILLISECONDS);
mProcessingTask = ThreadPool.SCHEDULED.scheduleAtFixedRate(new AudioPacketProcessor(),
0, 15, TimeUnit.MILLISECONDS);
mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_COMPLETE);

View File

@@ -20,10 +20,10 @@ package audio.broadcast;
import audio.convert.ISilenceGenerator;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import util.ThreadPool;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -40,7 +40,6 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
public static final int PROCESSOR_RUN_INTERVAL_MS = 1000;
private ThreadPoolManager mThreadPoolManager;
private ScheduledFuture mScheduledTask;
private RecordingQueueProcessor mRecordingQueueProcessor = new RecordingQueueProcessor();
@@ -77,19 +76,13 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
* The last audio packet's metadata is automatically attached to the closed audio recording when it is enqueued for
* broadcast. That metadata will be updated on the remote server once the audio recording is opened for streaming.
*/
public AudioBroadcaster(ThreadPoolManager threadPoolManager, BroadcastConfiguration broadcastConfiguration)
public AudioBroadcaster(BroadcastConfiguration broadcastConfiguration)
{
mThreadPoolManager = threadPoolManager;
mBroadcastConfiguration = broadcastConfiguration;
mDelay = mBroadcastConfiguration.getDelay();
mSilenceGenerator = BroadcastFactory.getSilenceGenerator(broadcastConfiguration.getBroadcastFormat());
}
protected ThreadPoolManager getThreadPoolManager()
{
return mThreadPoolManager;
}
/**
* Broadcast binary audio data frames or sequences.
*/
@@ -129,11 +122,8 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
{
if(mScheduledTask == null)
{
if(mThreadPoolManager != null)
{
mScheduledTask = mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_PROCESSING,
mRecordingQueueProcessor, PROCESSOR_RUN_INTERVAL_MS, TimeUnit.MILLISECONDS);
}
ThreadPool.SCHEDULED.scheduleAtFixedRate(mRecordingQueueProcessor, 0,
PROCESSOR_RUN_INTERVAL_MS, TimeUnit.MILLISECONDS);
}
}
}
@@ -145,9 +135,9 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
{
if(mStreaming.compareAndSet(true, false))
{
if(mThreadPoolManager != null && mScheduledTask != null)
if(mScheduledTask != null)
{
mThreadPoolManager.cancel(mScheduledTask);
mScheduledTask.cancel(true);
}
disconnect();

View File

@@ -37,7 +37,6 @@ import audio.convert.IAudioConverter;
import audio.convert.ISilenceGenerator;
import audio.convert.MP3AudioConverter;
import audio.convert.MP3SilenceGenerator;
import controller.ThreadPoolManager;
import gui.editor.Editor;
import gui.editor.EmptyEditor;
import icon.IconManager;
@@ -45,25 +44,23 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import record.AudioRecorder;
import record.mp3.MP3Recorder;
import settings.SettingsManager;
import java.nio.file.Path;
public class BroadcastFactory
{
private final static Logger mLog = LoggerFactory.getLogger( BroadcastFactory.class );
private final static Logger mLog = LoggerFactory.getLogger(BroadcastFactory.class);
public static final int MP3_MONO_16_KHZ_BITRATE = 16;
public static final boolean MP3_CONSTANT_BITRATE = false;
/**
* Creates an audio streaming broadcaster for the configuration
* @param threadPoolManager for creating an audio broadcasting connection thread
*
* @param configuration describing the server and audio types
* @return configured broadcaster or null
*/
public static AudioBroadcaster getBroadcaster(ThreadPoolManager threadPoolManager,
BroadcastConfiguration configuration)
public static AudioBroadcaster getBroadcaster(BroadcastConfiguration configuration)
{
if(configuration != null)
{
@@ -74,20 +71,15 @@ public class BroadcastFactory
switch(configuration.getBroadcastServerType())
{
case BROADCASTIFY:
return new IcecastTCPAudioBroadcaster(threadPoolManager,
(BroadcastifyConfiguration)configuration);
return new IcecastTCPAudioBroadcaster((BroadcastifyConfiguration) configuration);
case ICECAST_TCP:
return new IcecastTCPAudioBroadcaster(threadPoolManager,
(IcecastTCPConfiguration)configuration);
return new IcecastTCPAudioBroadcaster((IcecastTCPConfiguration) configuration);
case ICECAST_HTTP:
return new IcecastHTTPAudioBroadcaster(threadPoolManager,
(IcecastHTTPConfiguration)configuration);
return new IcecastHTTPAudioBroadcaster((IcecastHTTPConfiguration) configuration);
case SHOUTCAST_V1:
return new ShoutcastV1AudioBroadcaster(threadPoolManager,
(ShoutcastV1Configuration)configuration);
return new ShoutcastV1AudioBroadcaster((ShoutcastV1Configuration) configuration);
case SHOUTCAST_V2:
return new ShoutcastV2AudioBroadcaster(threadPoolManager,
(ShoutcastV2Configuration)configuration);
return new ShoutcastV2AudioBroadcaster((ShoutcastV2Configuration) configuration);
case UNKNOWN:
default:
mLog.info("Unrecognized broadcastAudio configuration: " + configuration.getBroadcastFormat().name());
@@ -101,6 +93,7 @@ public class BroadcastFactory
/**
* Creates an audio convert to convert from 8 kHz PCM audio to the specified format
*
* @param configuration containing the requested output audio format
* @return audio convert or null
*/
@@ -149,6 +142,7 @@ public class BroadcastFactory
/**
* Constructs an editor for the specified broadcastAudio configuration
*
* @param configuration to modify or view
* @param broadcastModel model for broadcastAudio configurations
* @return an editor for the specified broadcastAudio configuration
@@ -160,10 +154,10 @@ public class BroadcastFactory
{
Editor<BroadcastConfiguration> editor;
switch (configuration.getBroadcastServerType())
switch(configuration.getBroadcastServerType())
{
case BROADCASTIFY:
editor = new BroadcastifyConfigurationEditor(broadcastModel, aliasModel, iconManager);
editor = new BroadcastifyConfigurationEditor(broadcastModel, aliasModel, iconManager);
break;
case ICECAST_TCP:
editor = new IcecastTCPConfigurationEditor(broadcastModel, aliasModel, iconManager);
@@ -192,7 +186,7 @@ public class BroadcastFactory
*/
public static AudioRecorder getAudioRecorder(Path path, BroadcastFormat broadcastFormat)
{
switch (broadcastFormat)
switch(broadcastFormat)
{
case MP3:
return new MP3Recorder(path);
@@ -204,7 +198,7 @@ public class BroadcastFactory
public static ISilenceGenerator getSilenceGenerator(BroadcastFormat format)
{
switch (format)
switch(format)
{
case MP3:
return new MP3SilenceGenerator();

View File

@@ -21,13 +21,13 @@ package audio.broadcast;
import alias.id.broadcast.BroadcastChannel;
import audio.AudioPacket;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import icon.IconManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import properties.SystemProperties;
import sample.Broadcaster;
import sample.Listener;
import util.ThreadPool;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
@@ -66,7 +66,6 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
private Map<String,BroadcastConfiguration> mBroadcastConfigurationMap = new HashMap<>();
private Map<String,AudioBroadcaster> mBroadcasterMap = new HashMap<>();
private ThreadPoolManager mThreadPoolManager;
private IconManager mIconManager;
private StreamManager mStreamManager;
private Broadcaster<BroadcastEvent> mBroadcastEventBroadcaster = new Broadcaster<>();
@@ -74,17 +73,16 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
/**
* Model for managing Broadcast configurations and any associated broadcaster instances.
*/
public BroadcastModel(ThreadPoolManager threadPoolManager, IconManager iconManager)
public BroadcastModel(IconManager iconManager)
{
mThreadPoolManager = threadPoolManager;
mIconManager = iconManager;
mStreamManager = new StreamManager(threadPoolManager, new CompletedRecordingListener(), BroadcastFormat.MP3,
mStreamManager = new StreamManager(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);
ThreadPool.SCHEDULED.scheduleAtFixedRate(new RecordingDeletionMonitor(),
15l, 15l, TimeUnit.SECONDS);
removeOrphanedTemporaryRecordings();
}
@@ -293,8 +291,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
broadcastConfiguration.isValid() &&
!mBroadcasterMap.containsKey(broadcastConfiguration.getName()))
{
AudioBroadcaster audioBroadcaster = BroadcastFactory.getBroadcaster(mThreadPoolManager,
broadcastConfiguration);
AudioBroadcaster audioBroadcaster = BroadcastFactory.getBroadcaster(broadcastConfiguration);
if(audioBroadcaster != null)
{
@@ -403,7 +400,8 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
deleteBroadcaster(previousChannelName);
//Delay restarting the broadcaster to allow remote server time to cleanup
mThreadPoolManager.scheduleOnce(new DelayedBroadcasterStartup(broadcastConfiguration), 1, TimeUnit.SECONDS);
ThreadPool.SCHEDULED.schedule(new DelayedBroadcasterStartup(broadcastConfiguration),
1, TimeUnit.SECONDS);
break;
case CONFIGURATION_DELETE:
deleteBroadcaster(broadcastEvent.getBroadcastConfiguration().getName());

View File

@@ -19,11 +19,11 @@
package audio.broadcast;
import audio.AudioPacket;
import controller.ThreadPoolManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import record.AudioRecorder;
import sample.Listener;
import util.ThreadPool;
import util.TimeStamp;
import java.nio.file.Files;
@@ -44,7 +44,6 @@ public class StreamManager implements Listener<AudioPacket>
private static AtomicInteger sNextRecordingNumber = new AtomicInteger();
private ThreadPoolManager mThreadPoolManager;
private Listener<AudioRecording> mAudioRecordingListener;
private BroadcastFormat mBroadcastFormat;
private Path mTempDirectory;
@@ -61,17 +60,13 @@ public class StreamManager implements Listener<AudioPacket>
*
* Completed streamable audio recordings are nominated to the output listener (for broadcast) upon completion
*
* @param threadPoolManager for scheduling runnables
* @param listener to receive completed audio recordings
* @param tempDirectory where to store temporary audio recordings
*/
public StreamManager(ThreadPoolManager threadPoolManager, Listener<AudioRecording> listener,
BroadcastFormat broadcastFormat, Path tempDirectory)
public StreamManager(Listener<AudioRecording> listener, BroadcastFormat broadcastFormat, Path tempDirectory)
{
assert (tempDirectory != null && Files.isDirectory(tempDirectory));
assert (mThreadPoolManager != null);
mThreadPoolManager = threadPoolManager;
mAudioRecordingListener = listener;
mBroadcastFormat = broadcastFormat;
mTempDirectory = tempDirectory;
@@ -90,8 +85,8 @@ public class StreamManager implements Listener<AudioPacket>
mRecorderMonitor = new RecorderMonitor();
}
mRecorderMonitorFuture = mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_PROCESSING,
mRecorderMonitor, 2, TimeUnit.SECONDS);
mRecorderMonitorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(mRecorderMonitor,
0,2, TimeUnit.SECONDS);
}
}
@@ -145,7 +140,7 @@ public class StreamManager implements Listener<AudioPacket>
{
AudioRecorder recorder = BroadcastFactory.getAudioRecorder(getTemporaryRecordingPath(),
mBroadcastFormat);
recorder.start(mThreadPoolManager.getScheduledExecutorService());
recorder.start(ThreadPool.SCHEDULED);
recorder.receive(audioPacket);
mStreamRecorders.put(channelMetadataID, recorder);
}

View File

@@ -21,15 +21,14 @@ package audio.broadcast.icecast;
import audio.broadcast.AudioBroadcaster;
import audio.broadcast.BroadcastConfiguration;
import audio.broadcast.IBroadcastMetadataUpdater;
import controller.ThreadPoolManager;
public abstract class IcecastAudioBroadcaster extends AudioBroadcaster
{
private IBroadcastMetadataUpdater mMetadataUpdater;
public IcecastAudioBroadcaster(ThreadPoolManager threadPoolManager, BroadcastConfiguration broadcastConfiguration)
public IcecastAudioBroadcaster(BroadcastConfiguration broadcastConfiguration)
{
super(threadPoolManager, broadcastConfiguration);
super(broadcastConfiguration);
}
/**
@@ -37,7 +36,7 @@ public abstract class IcecastAudioBroadcaster extends AudioBroadcaster
*/
protected IcecastConfiguration getConfiguration()
{
return (IcecastConfiguration)getBroadcastConfiguration();
return (IcecastConfiguration) getBroadcastConfiguration();
}
@Override
@@ -45,7 +44,7 @@ public abstract class IcecastAudioBroadcaster extends AudioBroadcaster
{
if(mMetadataUpdater == null)
{
mMetadataUpdater = new IcecastBroadcastMetadataUpdater(getThreadPoolManager(), getConfiguration());
mMetadataUpdater = new IcecastBroadcastMetadataUpdater(getConfiguration());
}
return mMetadataUpdater;

View File

@@ -20,7 +20,6 @@ package audio.broadcast.icecast;
import audio.broadcast.IBroadcastMetadataUpdater;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
@@ -33,6 +32,7 @@ import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import properties.SystemProperties;
import util.ThreadPool;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
@@ -50,7 +50,6 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
private final static Logger mLog = LoggerFactory.getLogger(IcecastBroadcastMetadataUpdater.class);
private final static String UTF8 = "UTF-8";
private ThreadPoolManager mThreadPoolManager;
private IcecastConfiguration mIcecastConfiguration;
private NioSocketConnector mSocketConnector;
@@ -65,9 +64,8 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
* broadcaster. When multiple metadata updates are received prior to completion of the current ongoing update
* sequence, those updates will be queued and processed in the order received.
*/
public IcecastBroadcastMetadataUpdater(ThreadPoolManager threadPoolManager, IcecastConfiguration icecastConfiguration)
public IcecastBroadcastMetadataUpdater(IcecastConfiguration icecastConfiguration)
{
mThreadPoolManager = threadPoolManager;
mIcecastConfiguration = icecastConfiguration;
}
@@ -128,7 +126,7 @@ public class IcecastBroadcastMetadataUpdater implements IBroadcastMetadataUpdate
if(updateRequest != null)
{
mThreadPoolManager.scheduleOnce(new Runnable()
ThreadPool.SCHEDULED.schedule(new Runnable()
{
@Override
public void run()

View File

@@ -20,7 +20,6 @@ package audio.broadcast.icecast;
import audio.broadcast.BroadcastState;
import audio.convert.MP3AudioConverter;
import controller.ThreadPoolManager;
import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
@@ -38,6 +37,7 @@ import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import properties.SystemProperties;
import util.ThreadPool;
import java.net.ConnectException;
import java.net.InetSocketAddress;
@@ -48,12 +48,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
{
private static final Logger mLog = LoggerFactory.getLogger( IcecastHTTPAudioBroadcaster.class );
private static final Logger mLog = LoggerFactory.getLogger(IcecastHTTPAudioBroadcaster.class);
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
private NioSocketConnector mSocketConnector;
private IoSession mStreamingSession = null;
private Map<String, String> mHTTPHeaders;
private Map<String,String> mHTTPHeaders;
private long mLastConnectionAttempt = 0;
private AtomicBoolean mConnecting = new AtomicBoolean();
@@ -69,9 +69,9 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
*
* @param configuration for the Icecast stream
*/
public IcecastHTTPAudioBroadcaster(ThreadPoolManager threadPoolManager, IcecastHTTPConfiguration configuration)
public IcecastHTTPAudioBroadcaster(IcecastHTTPConfiguration configuration)
{
super(threadPoolManager, configuration);
super(configuration);
}
/**
@@ -101,7 +101,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
private boolean connect()
{
if(!connected() && canConnect() &&
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
mConnecting.compareAndSet(false, true))
{
mLastConnectionAttempt = System.currentTimeMillis();
@@ -159,7 +159,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
}
};
getThreadPoolManager().scheduleOnce(runnable, 0l, TimeUnit.SECONDS);
ThreadPool.SCHEDULED.schedule(runnable, 0l, TimeUnit.SECONDS);
}
@@ -264,10 +264,10 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
{
if(throwable instanceof ProtocolDecoderException)
{
Throwable cause = ((ProtocolDecoderException)throwable).getCause();
Throwable cause = ((ProtocolDecoderException) throwable).getCause();
if(cause instanceof HttpException &&
((HttpException)cause).getStatusCode() == HttpStatus.CLIENT_ERROR_LENGTH_REQUIRED.code())
((HttpException) cause).getStatusCode() == HttpStatus.CLIENT_ERROR_LENGTH_REQUIRED.code())
{
//Ignore - Icecast 2.4 sometimes doesn't send any headers with their HTTP responses. Mina expects a
//content-length for HTTP responses.
@@ -294,7 +294,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
{
if(object instanceof DefaultHttpResponse)
{
DefaultHttpResponse response = (DefaultHttpResponse)object;
DefaultHttpResponse response = (DefaultHttpResponse) object;
switch(response.getStatus())
{
@@ -322,7 +322,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
else
{
mLog.error("Icecast HTTP broadcaster - unrecognized message [ " + object.getClass() +
"] received:" + object.toString());
"] received:" + object.toString());
}
}
}

View File

@@ -21,7 +21,6 @@ package audio.broadcast.icecast;
import audio.broadcast.BroadcastState;
import audio.broadcast.icecast.codec.IcecastCodecFactory;
import audio.convert.MP3AudioConverter;
import controller.ThreadPoolManager;
import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
@@ -31,6 +30,7 @@ import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import properties.SystemProperties;
import util.ThreadPool;
import java.io.IOException;
import java.net.ConnectException;
@@ -40,7 +40,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
{
private final static Logger mLog = LoggerFactory.getLogger( IcecastTCPAudioBroadcaster.class );
private final static Logger mLog = LoggerFactory.getLogger(IcecastTCPAudioBroadcaster.class);
private final static String TERMINATOR = "\r\n";
private final static String SEPARATOR = ":";
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
@@ -62,9 +62,9 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
*
* @param configuration for the Icecast stream
*/
public IcecastTCPAudioBroadcaster(ThreadPoolManager threadPoolManager, IcecastTCPConfiguration configuration)
public IcecastTCPAudioBroadcaster(IcecastTCPConfiguration configuration)
{
super(threadPoolManager, configuration);
super(configuration);
}
/**
@@ -90,7 +90,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
private boolean connect()
{
if(!connected() && canConnect() &&
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
mConnecting.compareAndSet(false, true))
{
mLastConnectionAttempt = System.currentTimeMillis();
@@ -149,8 +149,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
}
};
getThreadPoolManager().scheduleOnce(runnable, 0l, TimeUnit.SECONDS);
ThreadPool.SCHEDULED.schedule(runnable, 0l, TimeUnit.SECONDS);
}
return connected();
@@ -241,9 +240,9 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
{
if(cause instanceof IOException && ((IOException)cause).getMessage().startsWith("Connection reset"))
if(cause instanceof IOException && ((IOException) cause).getMessage().startsWith("Connection reset"))
{
IOException ioe = (IOException)cause;
IOException ioe = (IOException) cause;
if(ioe.getMessage() != null && ioe.getMessage().startsWith("Connection reset"))
{
@@ -271,7 +270,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
{
if(object instanceof String)
{
String message = (String)object;
String message = (String) object;
if(message != null && !message.trim().isEmpty())
{

View File

@@ -21,7 +21,6 @@ package audio.broadcast.shoutcast.v1;
import audio.broadcast.AudioBroadcaster;
import audio.broadcast.BroadcastState;
import audio.broadcast.IBroadcastMetadataUpdater;
import controller.ThreadPoolManager;
import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
@@ -32,6 +31,7 @@ import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.ThreadPool;
import java.io.IOException;
import java.net.ConnectException;
@@ -41,7 +41,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
{
private final static Logger mLog = LoggerFactory.getLogger( ShoutcastV1AudioBroadcaster.class );
private final static Logger mLog = LoggerFactory.getLogger(ShoutcastV1AudioBroadcaster.class);
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
private NioSocketConnector mSocketConnector;
@@ -61,9 +61,9 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
*
* @param configuration for the Shoutcast stream
*/
public ShoutcastV1AudioBroadcaster(ThreadPoolManager threadPoolManager, ShoutcastV1Configuration configuration)
public ShoutcastV1AudioBroadcaster(ShoutcastV1Configuration configuration)
{
super(threadPoolManager, configuration);
super(configuration);
}
/**
@@ -71,7 +71,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
*/
private ShoutcastV1Configuration getConfiguration()
{
return (ShoutcastV1Configuration)getBroadcastConfiguration();
return (ShoutcastV1Configuration) getBroadcastConfiguration();
}
@Override
@@ -79,7 +79,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
{
if(mMetadataUpdater == null)
{
mMetadataUpdater = new ShoutcastV1BroadcastMetadataUpdater(getThreadPoolManager(), getConfiguration());
mMetadataUpdater = new ShoutcastV1BroadcastMetadataUpdater(getConfiguration());
}
return mMetadataUpdater;
@@ -111,7 +111,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
private boolean connect()
{
if(!connected() && canConnect() &&
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
mConnecting.compareAndSet(false, true))
{
mLastConnectionAttempt = System.currentTimeMillis();
@@ -171,8 +171,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
}
};
getThreadPoolManager().scheduleOnce(runnable, 0l, TimeUnit.SECONDS);
ThreadPool.SCHEDULED.schedule(runnable, 0l, TimeUnit.SECONDS);
}
return connected();
@@ -242,9 +241,9 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
{
if(cause instanceof IOException && ((IOException)cause).getMessage().startsWith("Connection reset"))
if(cause instanceof IOException && ((IOException) cause).getMessage().startsWith("Connection reset"))
{
IOException ioe = (IOException)cause;
IOException ioe = (IOException) cause;
if(ioe.getMessage() != null && ioe.getMessage().startsWith("Connection reset"))
{
@@ -272,7 +271,7 @@ public class ShoutcastV1AudioBroadcaster extends AudioBroadcaster
{
if(object instanceof String)
{
String message = (String)object;
String message = (String) object;
if(message != null && !message.trim().isEmpty())
{

View File

@@ -20,7 +20,6 @@ package audio.broadcast.shoutcast.v1;
import audio.broadcast.IBroadcastMetadataUpdater;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
@@ -32,6 +31,7 @@ import org.apache.mina.http.api.HttpVersion;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.ThreadPool;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
@@ -49,7 +49,6 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
private final static Logger mLog = LoggerFactory.getLogger(ShoutcastV1BroadcastMetadataUpdater.class);
private final static String UTF8 = "UTF-8";
private ThreadPoolManager mThreadPoolManager;
private ShoutcastV1Configuration mShoutcastV1Configuration;
private NioSocketConnector mSocketConnector;
@@ -63,9 +62,8 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
* broadcaster. When multiple metadata updates are received prior to completion of the current ongoing update
* sequence, those updates will be queued and processed in the order received.
*/
public ShoutcastV1BroadcastMetadataUpdater(ThreadPoolManager threadPoolManager, ShoutcastV1Configuration shoutcastV1Configuration)
public ShoutcastV1BroadcastMetadataUpdater(ShoutcastV1Configuration shoutcastV1Configuration)
{
mThreadPoolManager = threadPoolManager;
mShoutcastV1Configuration = shoutcastV1Configuration;
}
@@ -127,7 +125,7 @@ public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUp
if(updateRequest != null)
{
mThreadPoolManager.scheduleOnce(new Runnable()
ThreadPool.SCHEDULED.schedule(new Runnable()
{
@Override
public void run()

View File

@@ -38,7 +38,6 @@ import audio.broadcast.shoutcast.v2.ultravox.UltravoxMessageType;
import audio.broadcast.shoutcast.v2.ultravox.UltravoxMetadata;
import audio.broadcast.shoutcast.v2.ultravox.UltravoxProtocolFactory;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
@@ -48,6 +47,7 @@ import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import properties.SystemProperties;
import util.ThreadPool;
import java.io.IOException;
import java.net.ConnectException;
@@ -81,12 +81,11 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
* This broadcaster uses the Apache Mina library for the streaming socket connection. The
* ShoutcastV2IOHandler manages all interaction with the Shoutcast server and manages the overall broadcast state.
*
* @param threadPoolManager for thread pool access
* @param configuration for the Shoutcast V2 stream
*/
public ShoutcastV2AudioBroadcaster(ThreadPoolManager threadPoolManager, ShoutcastV2Configuration configuration)
public ShoutcastV2AudioBroadcaster(ShoutcastV2Configuration configuration)
{
super(threadPoolManager, configuration);
super(configuration);
}
public ShoutcastV2Configuration getConfiguration()
@@ -207,8 +206,7 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBr
}
};
getThreadPoolManager().scheduleOnce(runnable, 0l, TimeUnit.SECONDS);
ThreadPool.SCHEDULED.schedule(runnable, 0l, TimeUnit.SECONDS);
}
return connected();

View File

@@ -22,13 +22,12 @@ import audio.AudioEvent;
import audio.AudioPacket;
import audio.AudioPacket.Type;
import channel.metadata.Metadata;
import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import source.mixer.MixerChannel;
import util.ThreadPool;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.BooleanControl;
@@ -59,7 +58,6 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
private Listener<Metadata> mMetadataListener;
private Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
private ThreadPoolManager mThreadPoolManager;
private ScheduledFuture<?> mProcessorTask;
private SourceDataLine mOutput;
@@ -82,21 +80,18 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
* Maintains an internal non-blocking audio packet queue and processes this
* queue 25 times a second (every 40 ms).
*
* @param threadPoolManager for assigning buffer processing schedule task
* @param mixer to obtain source data line
* @param mixerChannel either mono or left/right stereo
* @param audioFormat to use during playback
* @param lineInfo to use when obtaining the source data line
* @param requestedBufferSize of approximately 1 second of audio
*/
public AudioOutput(ThreadPoolManager threadPoolManager,
Mixer mixer,
public AudioOutput(Mixer mixer,
MixerChannel mixerChannel,
AudioFormat audioFormat,
Line.Info lineInfo,
int requestedBufferSize)
{
mThreadPoolManager = threadPoolManager;
mMixer = mixer;
mMixerChannel = mixerChannel;
@@ -141,9 +136,8 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
}
/* Run the queue processor task every 40 milliseconds or 25 times a second */
mProcessorTask = mThreadPoolManager.scheduleFixedRate(
ThreadType.AUDIO_PROCESSING, new BufferProcessor(),
40, TimeUnit.MILLISECONDS);
mProcessorTask = ThreadPool.SCHEDULED.scheduleAtFixedRate(new BufferProcessor(),
0, 40, TimeUnit.MILLISECONDS);
}
mAudioStartEvent = new AudioEvent(AudioEvent.Type.AUDIO_STARTED,
@@ -170,12 +164,11 @@ public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
{
mCanProcessAudio = false;
if(mProcessorTask != null && mThreadPoolManager != null)
if(mProcessorTask != null)
{
mThreadPoolManager.cancel(mProcessorTask);
mProcessorTask.cancel(true);
}
mThreadPoolManager = null;
mProcessorTask = null;
mBuffer.clear();

View File

@@ -1,74 +1,67 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package audio.output;
import audio.AudioFormats;
import audio.AudioPacket;
import source.mixer.MixerChannel;
import javax.sound.sampled.Mixer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import javax.sound.sampled.Mixer;
import source.mixer.MixerChannel;
import audio.AudioFormats;
import audio.AudioPacket;
import controller.ThreadPoolManager;
/**
* Mono Audio output implementation
*/
public class MonoAudioOutput extends AudioOutput
{
private final static int BUFFER_SIZE = 8000;
public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer )
{
super( threadPoolManager,
mixer,
MixerChannel.MONO,
AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO,
AudioFormats.MONO_SOURCE_DATALINE_INFO,
BUFFER_SIZE );
}
private final static int BUFFER_SIZE = 8000;
public MonoAudioOutput(Mixer mixer)
{
super(mixer, MixerChannel.MONO, AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO,
AudioFormats.MONO_SOURCE_DATALINE_INFO, BUFFER_SIZE);
}
/**
* Converts the audio packet data into mono audio frames.
*/
protected ByteBuffer convert(AudioPacket packet)
{
if(packet.hasAudioBuffer())
{
float[] samples = packet.getAudioBuffer().getSamples();
/**
* Converts the audio packet data into mono audio frames.
*/
protected ByteBuffer convert( AudioPacket packet )
{
if( packet.hasAudioBuffer() )
{
float[] samples = packet.getAudioBuffer().getSamples();
/* Little-endian byte buffer */
ByteBuffer buffer =
ByteBuffer.allocate( samples.length * 2 )
.order( ByteOrder.LITTLE_ENDIAN );
ByteBuffer buffer = ByteBuffer.allocate(samples.length * 2).order(ByteOrder.LITTLE_ENDIAN);
ShortBuffer shortBuffer = buffer.asShortBuffer();
ShortBuffer shortBuffer = buffer.asShortBuffer();
for( float sample: samples )
{
shortBuffer.put( (short)( sample * Short.MAX_VALUE ) );
}
return buffer;
}
return null;
}
for(float sample : samples)
{
shortBuffer.put((short) (sample * Short.MAX_VALUE));
}
return buffer;
}
return null;
}
}

View File

@@ -1,88 +1,82 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package audio.output;
import audio.AudioFormats;
import audio.AudioPacket;
import source.mixer.MixerChannel;
import javax.sound.sampled.Mixer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import javax.sound.sampled.Mixer;
import source.mixer.MixerChannel;
import audio.AudioFormats;
import audio.AudioPacket;
import controller.ThreadPoolManager;
/**
* Stereo audio output implementation.
* Stereo audio output implementation.
*/
public class StereoAudioOutput extends AudioOutput
{
private final static int BUFFER_SIZE = 16000;
public StereoAudioOutput( ThreadPoolManager threadPoolManager,
Mixer mixer, MixerChannel channel )
{
super( threadPoolManager,
mixer,
channel,
AudioFormats.PCM_SIGNED_8KHZ_16BITS_STEREO,
AudioFormats.STEREO_SOURCE_DATALINE_INFO,
BUFFER_SIZE );
}
private final static int BUFFER_SIZE = 16000;
public StereoAudioOutput(Mixer mixer, MixerChannel channel)
{
super(mixer, channel, AudioFormats.PCM_SIGNED_8KHZ_16BITS_STEREO, AudioFormats.STEREO_SOURCE_DATALINE_INFO,
BUFFER_SIZE);
}
/**
* Converts the audio packet data into stereo audio frames with the mixer
* channel containing the audio and the other channel containing zero
* valued (silent) samples.
*/
protected ByteBuffer convert(AudioPacket packet)
{
if(packet.hasAudioBuffer())
{
float[] samples = packet.getAudioBuffer().getSamples();
/**
* Converts the audio packet data into stereo audio frames with the mixer
* channel containing the audio and the other channel containing zero
* valued (silent) samples.
*/
protected ByteBuffer convert( AudioPacket packet )
{
if( packet.hasAudioBuffer() )
{
float[] samples = packet.getAudioBuffer().getSamples();
/* Little-endian byte buffer */
ByteBuffer buffer = ByteBuffer.allocate( samples.length * 4 )
.order( ByteOrder.LITTLE_ENDIAN );
ShortBuffer shortBuffer = buffer.asShortBuffer();
ByteBuffer buffer = ByteBuffer.allocate(samples.length * 4)
.order(ByteOrder.LITTLE_ENDIAN);
if( getMixerChannel() == MixerChannel.LEFT )
{
for( float sample: samples )
{
shortBuffer.put( (short)( sample * Short.MAX_VALUE ) );
shortBuffer.put( (short)0 );
}
}
else
{
for( float sample: samples )
{
shortBuffer.put( (short)0 );
shortBuffer.put( (short)( sample * Short.MAX_VALUE ) );
}
}
return buffer;
}
return null;
}
ShortBuffer shortBuffer = buffer.asShortBuffer();
if(getMixerChannel() == MixerChannel.LEFT)
{
for(float sample : samples)
{
shortBuffer.put((short) (sample * Short.MAX_VALUE));
shortBuffer.put((short) 0);
}
}
else
{
for(float sample : samples)
{
shortBuffer.put((short) 0);
shortBuffer.put((short) (sample * Short.MAX_VALUE));
}
}
return buffer;
}
return null;
}
}