mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-15 01:57:39 -05:00
#30 - adds streaming status color indicators to the streaming panel table.
Resolves issue with temporary streaming file name collisions by adding a 1-up number to each generated temporary recording file.
This commit is contained in:
@@ -223,7 +223,8 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
}
|
}
|
||||||
catch(IOException ioe)
|
catch(IOException ioe)
|
||||||
{
|
{
|
||||||
mLog.error("Error deleting temporary internet recording file: " + recording.getPath().toString());
|
mLog.error("Stream [" + getBroadcastConfiguration().getName() + "] - error deleting temporary internet " +
|
||||||
|
"recording file: " + recording.getPath().toString() + " - " + ioe.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,44 +355,51 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
{
|
{
|
||||||
if(mProcessing.compareAndSet(false, true))
|
if(mProcessing.compareAndSet(false, true))
|
||||||
{
|
{
|
||||||
if(mInputStream == null || mInputStream.available() <= 0)
|
try
|
||||||
{
|
{
|
||||||
if(mFinalSilencePadding > 0)
|
if(mInputStream == null || mInputStream.available() <= 0)
|
||||||
{
|
{
|
||||||
broadcastAudio(mSilenceGenerator.generate(mFinalSilencePadding));
|
if(mFinalSilencePadding > 0)
|
||||||
mFinalSilencePadding = 0;
|
{
|
||||||
|
broadcastAudio(mSilenceGenerator.generate(mFinalSilencePadding));
|
||||||
|
mFinalSilencePadding = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
nextRecording();
|
if(mInputStream != null)
|
||||||
}
|
|
||||||
|
|
||||||
if(mInputStream != null)
|
|
||||||
{
|
|
||||||
//We need to stream at 13.888 fps (144 byte frame) to achieve 2000 Bps or 16 kbps
|
|
||||||
mBytesStreamedRequired += 2000; //2000 bytes per second for 16 kbps data rate
|
|
||||||
int bytesToStream = mBytesStreamedRequired - mBytesStreamedActual;
|
|
||||||
|
|
||||||
//Trim length to whole-frame intervals (144 byte frame)
|
|
||||||
bytesToStream -= (bytesToStream % 144);
|
|
||||||
|
|
||||||
int length = Math.min(bytesToStream, mInputStream.available());
|
|
||||||
|
|
||||||
byte[] audio = new byte[length];
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
mBytesStreamedActual += mInputStream.read(audio);
|
//We need to stream at 13.888 fps (144 byte frame) to achieve 2000 Bps or 16 kbps
|
||||||
|
mBytesStreamedRequired += 2000; //2000 bytes per second for 16 kbps data rate
|
||||||
|
int bytesToStream = mBytesStreamedRequired - mBytesStreamedActual;
|
||||||
|
|
||||||
broadcastAudio(audio);
|
//Trim length to whole-frame intervals (144 byte frame)
|
||||||
|
bytesToStream -= (bytesToStream % 144);
|
||||||
|
|
||||||
|
int length = Math.min(bytesToStream, mInputStream.available());
|
||||||
|
|
||||||
|
byte[] audio = new byte[length];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mBytesStreamedActual += mInputStream.read(audio);
|
||||||
|
|
||||||
|
broadcastAudio(audio);
|
||||||
|
}
|
||||||
|
catch(IOException ioe)
|
||||||
|
{
|
||||||
|
mLog.error("Error reading from in-memory audio recording input stream", ioe);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(IOException ioe)
|
else
|
||||||
{
|
{
|
||||||
mLog.error("Error reading from in-memory audio recording input stream", ioe);
|
broadcastAudio(mSilenceGenerator.generate(PROCESSOR_RUN_INTERVAL_MS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
broadcastAudio(mSilenceGenerator.generate(PROCESSOR_RUN_INTERVAL_MS));
|
mLog.error("Error while processing audio streaming queue", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
mProcessing.set(false);
|
mProcessing.set(false);
|
||||||
@@ -447,17 +455,18 @@ public abstract class AudioBroadcaster implements IAudioPacketListener
|
|||||||
|
|
||||||
metadataUpdateRequired = false;
|
metadataUpdateRequired = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeRecording(recording);
|
||||||
}
|
}
|
||||||
catch(IOException ioe)
|
catch(IOException ioe)
|
||||||
{
|
{
|
||||||
mLog.error("Error reading temporary audio stream recording [" + recording.getPath().toString() +
|
mLog.error("Stream [" + getBroadcastConfiguration().getName() + "] error reading temporary audio " +
|
||||||
"] - skipping recording");
|
"stream recording [" + recording.getPath().toString() + "] - skipping recording - ", ioe);
|
||||||
|
|
||||||
mInputStream = null;
|
mInputStream = null;
|
||||||
|
metadataUpdateRequired = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRecording(recording);
|
|
||||||
|
|
||||||
broadcast(new BroadcastEvent(AudioBroadcaster.this, BroadcastEvent.Event.BROADCASTER_QUEUE_CHANGE));
|
broadcast(new BroadcastEvent(AudioBroadcaster.this, BroadcastEvent.Event.BROADCASTER_QUEUE_CHANGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,11 +53,11 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
|
|
||||||
private static final String UNIQUE_NAME_REGEX = "(.*)\\((\\d*)\\)";
|
private static final String UNIQUE_NAME_REGEX = "(.*)\\((\\d*)\\)";
|
||||||
|
|
||||||
private static final int COLUMN_SERVER_ICON = 0;
|
public static final int COLUMN_SERVER_ICON = 0;
|
||||||
private static final int COLUMN_STREAM_NAME = 1;
|
public static final int COLUMN_STREAM_NAME = 1;
|
||||||
private static final int COLUMN_BROADCASTER_STATUS = 2;
|
public static final int COLUMN_BROADCASTER_STATUS = 2;
|
||||||
private static final int COLUMN_BROADCASTER_QUEUE_SIZE = 3;
|
public static final int COLUMN_BROADCASTER_QUEUE_SIZE = 3;
|
||||||
private 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<>();
|
||||||
|
|
||||||
@@ -513,15 +513,15 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
|
|
||||||
if(audioBroadcasterA != null)
|
if(audioBroadcasterA != null)
|
||||||
{
|
{
|
||||||
return audioBroadcasterA.getBroadcastState().toString();
|
return audioBroadcasterA.getBroadcastState();
|
||||||
}
|
}
|
||||||
else if(!configuration.isEnabled())
|
else if(!configuration.isEnabled())
|
||||||
{
|
{
|
||||||
return "Disabled";
|
return BroadcastState.DISABLED;
|
||||||
}
|
}
|
||||||
else if(!configuration.isValid())
|
else if(!configuration.isValid())
|
||||||
{
|
{
|
||||||
return "Invalid Settings";
|
return BroadcastState.INVALID_SETTINGS;
|
||||||
}
|
}
|
||||||
case COLUMN_BROADCASTER_QUEUE_SIZE:
|
case COLUMN_BROADCASTER_QUEUE_SIZE:
|
||||||
AudioBroadcaster audioBroadcasterB = mBroadcasterMap.get(configuration.getName());
|
AudioBroadcaster audioBroadcasterB = mBroadcasterMap.get(configuration.getName());
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ import javax.swing.event.ListSelectionEvent;
|
|||||||
import javax.swing.event.ListSelectionListener;
|
import javax.swing.event.ListSelectionListener;
|
||||||
import javax.swing.event.TableColumnModelEvent;
|
import javax.swing.event.TableColumnModelEvent;
|
||||||
import javax.swing.event.TableColumnModelListener;
|
import javax.swing.event.TableColumnModelListener;
|
||||||
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
@@ -77,8 +80,10 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
|
|||||||
mBroadcastConfigurationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
mBroadcastConfigurationTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
mBroadcastConfigurationTable.getSelectionModel().addListSelectionListener(BroadcastPanel.this);
|
mBroadcastConfigurationTable.getSelectionModel().addListSelectionListener(BroadcastPanel.this);
|
||||||
|
|
||||||
mBroadcastConfigurationTable.getColumnModel().getColumn(0).setPreferredWidth(1);
|
mBroadcastConfigurationTable.getColumnModel().getColumn(BroadcastModel.COLUMN_SERVER_ICON).setPreferredWidth(1);
|
||||||
mBroadcastConfigurationTable.getColumnModel().getColumn(1).setPreferredWidth(25);
|
mBroadcastConfigurationTable.getColumnModel().getColumn(BroadcastModel.COLUMN_STREAM_NAME).setPreferredWidth(25);
|
||||||
|
mBroadcastConfigurationTable.getColumnModel().getColumn(BroadcastModel.COLUMN_BROADCASTER_STATUS)
|
||||||
|
.setCellRenderer(new StatusCellRenderer());
|
||||||
|
|
||||||
JScrollPane scroller = new JScrollPane(mBroadcastConfigurationTable);
|
JScrollPane scroller = new JScrollPane(mBroadcastConfigurationTable);
|
||||||
|
|
||||||
@@ -261,4 +266,67 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom cell renderer for the broadcast state column.
|
||||||
|
*/
|
||||||
|
public class StatusCellRenderer extends DefaultTableCellRenderer
|
||||||
|
{
|
||||||
|
public StatusCellRenderer()
|
||||||
|
{
|
||||||
|
setOpaque(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
||||||
|
boolean hasFocus, int row, int column)
|
||||||
|
{
|
||||||
|
JLabel component = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
|
||||||
|
if(isSelected)
|
||||||
|
{
|
||||||
|
setBackground(table.getSelectionBackground());
|
||||||
|
setForeground(table.getSelectionForeground());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(value instanceof BroadcastState)
|
||||||
|
{
|
||||||
|
BroadcastState state = (BroadcastState)value;
|
||||||
|
|
||||||
|
if(state == BroadcastState.CONNECTED)
|
||||||
|
{
|
||||||
|
setBackground(Color.GREEN);
|
||||||
|
setForeground(table.getForeground());
|
||||||
|
}
|
||||||
|
else if(state == BroadcastState.DISABLED)
|
||||||
|
{
|
||||||
|
setBackground(table.getBackground());
|
||||||
|
setForeground(Color.LIGHT_GRAY);
|
||||||
|
}
|
||||||
|
else if(state == BroadcastState.INVALID_SETTINGS)
|
||||||
|
{
|
||||||
|
setBackground(Color.YELLOW);
|
||||||
|
setForeground(table.getForeground());
|
||||||
|
}
|
||||||
|
else if(state.isErrorState())
|
||||||
|
{
|
||||||
|
setBackground(Color.RED);
|
||||||
|
setForeground(table.getForeground());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setBackground(table.getBackground());
|
||||||
|
setForeground(table.getForeground());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setForeground(table.getForeground());
|
||||||
|
setBackground(table.getBackground());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ public enum BroadcastState
|
|||||||
*/
|
*/
|
||||||
CONNECTING("Connecting", false),
|
CONNECTING("Connecting", false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the configuration is disabled
|
||||||
|
*/
|
||||||
|
DISABLED("Disabled", true),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnected from the broadcastAudio server
|
* Disconnected from the broadcastAudio server
|
||||||
*/
|
*/
|
||||||
@@ -50,6 +55,11 @@ public enum BroadcastState
|
|||||||
*/
|
*/
|
||||||
INVALID_MOUNT_POINT("Invalid Mount/Stream ID", true),
|
INVALID_MOUNT_POINT("Invalid Mount/Stream ID", true),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalid configuration settings
|
||||||
|
*/
|
||||||
|
INVALID_SETTINGS("Invalid Settings", true),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote server max sources has been exceeded
|
* Remote server max sources has been exceeded
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -30,18 +30,20 @@ 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.ScheduledFuture;
|
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;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class StreamManager implements Listener<AudioPacket>
|
public class StreamManager implements Listener<AudioPacket>
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(StreamManager.class);
|
private final static Logger mLog = LoggerFactory.getLogger(StreamManager.class);
|
||||||
private static final long MAXIMUM_RECORDER_LIFESPAN_MILLIS = 30000; //30 seconds
|
private static final long MAXIMUM_RECORDER_LIFESPAN_MILLIS = 30000; //30 seconds
|
||||||
|
|
||||||
|
private static AtomicInteger sNextRecordingNumber = new AtomicInteger();
|
||||||
|
|
||||||
private ThreadPoolManager mThreadPoolManager;
|
private ThreadPoolManager mThreadPoolManager;
|
||||||
private AudioBroadcaster mAudioBroadcaster;
|
private AudioBroadcaster mAudioBroadcaster;
|
||||||
private Path mTempDirectory;
|
private Path mTempDirectory;
|
||||||
@@ -197,6 +199,7 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(BroadcastModel.TEMPORARY_STREAM_FILE_SUFFIX);
|
sb.append(BroadcastModel.TEMPORARY_STREAM_FILE_SUFFIX);
|
||||||
|
sb.append(sNextRecordingNumber.incrementAndGet()).append("_");
|
||||||
sb.append( TimeStamp.getLongTimeStamp( "_" ) );
|
sb.append( TimeStamp.getLongTimeStamp( "_" ) );
|
||||||
sb.append( mAudioBroadcaster.getBroadcastConfiguration().getBroadcastFormat().getFileExtension() );
|
sb.append( mAudioBroadcaster.getBroadcastConfiguration().getBroadcastFormat().getFileExtension() );
|
||||||
|
|
||||||
@@ -221,6 +224,9 @@ public class StreamManager implements Listener<AudioPacket>
|
|||||||
.filter(entry -> entry.getValue().getTimeRecordingStart() + MAXIMUM_RECORDER_LIFESPAN_MILLIS < now)
|
.filter(entry -> entry.getValue().getTimeRecordingStart() + MAXIMUM_RECORDER_LIFESPAN_MILLIS < now)
|
||||||
.forEach(entry ->
|
.forEach(entry ->
|
||||||
{
|
{
|
||||||
|
mLog.debug("Maximum recording time limit reached - removing recorder: " +
|
||||||
|
entry.getValue().getPath().toString());
|
||||||
|
|
||||||
removeRecorder(entry.getKey());
|
removeRecorder(entry.getKey());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* 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/>
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
package audio.broadcast.shoutcast.v1;
|
|
||||||
|
|
||||||
import audio.broadcast.AudioBroadcaster;
|
|
||||||
import audio.broadcast.BroadcastConfiguration;
|
|
||||||
import audio.broadcast.shoutcast.ShoutcastMetadataUpdater;
|
|
||||||
import audio.metadata.AudioMetadata;
|
|
||||||
import controller.ThreadPoolManager;
|
|
||||||
|
|
||||||
public abstract class ShoutcastAudioBroadcaster extends AudioBroadcaster
|
|
||||||
{
|
|
||||||
private ShoutcastMetadataUpdater mMetadataUpdater;
|
|
||||||
|
|
||||||
public ShoutcastAudioBroadcaster(ThreadPoolManager threadPoolManager, BroadcastConfiguration broadcastConfiguration)
|
|
||||||
{
|
|
||||||
super(threadPoolManager, broadcastConfiguration);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void broadcastMetadata(AudioMetadata metadata)
|
|
||||||
{
|
|
||||||
if(mMetadataUpdater == null)
|
|
||||||
{
|
|
||||||
mMetadataUpdater = new ShoutcastMetadataUpdater(getThreadPoolManager(),
|
|
||||||
(ShoutcastV1Configuration)getBroadcastConfiguration());
|
|
||||||
}
|
|
||||||
|
|
||||||
mMetadataUpdater.update(metadata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,9 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v1;
|
package audio.broadcast.shoutcast.v1;
|
||||||
|
|
||||||
|
import audio.broadcast.AudioBroadcaster;
|
||||||
import audio.broadcast.BroadcastState;
|
import audio.broadcast.BroadcastState;
|
||||||
|
import audio.broadcast.IBroadcastMetadataUpdater;
|
||||||
import controller.ThreadPoolManager;
|
import controller.ThreadPoolManager;
|
||||||
import org.apache.mina.core.RuntimeIoException;
|
import org.apache.mina.core.RuntimeIoException;
|
||||||
import org.apache.mina.core.buffer.IoBuffer;
|
import org.apache.mina.core.buffer.IoBuffer;
|
||||||
@@ -37,13 +39,14 @@ import java.net.InetSocketAddress;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public class ShoutcastV1AudioBroadcaster extends ShoutcastAudioBroadcaster
|
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 static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
|
||||||
|
|
||||||
private NioSocketConnector mSocketConnector;
|
private NioSocketConnector mSocketConnector;
|
||||||
private IoSession mStreamingSession = null;
|
private IoSession mStreamingSession = null;
|
||||||
|
private IBroadcastMetadataUpdater mMetadataUpdater;
|
||||||
|
|
||||||
private long mLastConnectionAttempt = 0;
|
private long mLastConnectionAttempt = 0;
|
||||||
private AtomicBoolean mConnecting = new AtomicBoolean();
|
private AtomicBoolean mConnecting = new AtomicBoolean();
|
||||||
@@ -71,6 +74,16 @@ public class ShoutcastV1AudioBroadcaster extends ShoutcastAudioBroadcaster
|
|||||||
return (ShoutcastV1Configuration)getBroadcastConfiguration();
|
return (ShoutcastV1Configuration)getBroadcastConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IBroadcastMetadataUpdater getMetadataUpdater()
|
||||||
|
{
|
||||||
|
if(mMetadataUpdater == null)
|
||||||
|
{
|
||||||
|
mMetadataUpdater = new ShoutcastV1BroadcastMetadataUpdater(getThreadPoolManager(), getConfiguration());
|
||||||
|
}
|
||||||
|
|
||||||
|
return mMetadataUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcasts the audio frame or sequence
|
* Broadcasts the audio frame or sequence
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,12 +16,9 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast;
|
package audio.broadcast.shoutcast.v1;
|
||||||
|
|
||||||
import audio.broadcast.icecast.IcecastConfiguration;
|
import audio.broadcast.IBroadcastMetadataUpdater;
|
||||||
import audio.broadcast.icecast.IcecastHeader;
|
|
||||||
import audio.broadcast.shoutcast.v1.ShoutcastMetadata;
|
|
||||||
import audio.broadcast.shoutcast.v1.ShoutcastV1Configuration;
|
|
||||||
import audio.metadata.AudioMetadata;
|
import audio.metadata.AudioMetadata;
|
||||||
import audio.metadata.Metadata;
|
import audio.metadata.Metadata;
|
||||||
import audio.metadata.MetadataType;
|
import audio.metadata.MetadataType;
|
||||||
@@ -29,9 +26,6 @@ import controller.ThreadPoolManager;
|
|||||||
import org.apache.mina.core.future.ConnectFuture;
|
import org.apache.mina.core.future.ConnectFuture;
|
||||||
import org.apache.mina.core.service.IoHandlerAdapter;
|
import org.apache.mina.core.service.IoHandlerAdapter;
|
||||||
import org.apache.mina.core.session.IoSession;
|
import org.apache.mina.core.session.IoSession;
|
||||||
import org.apache.mina.filter.codec.ProtocolCodecFilter;
|
|
||||||
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
|
|
||||||
import org.apache.mina.filter.logging.LoggingFilter;
|
|
||||||
import org.apache.mina.http.HttpClientCodec;
|
import org.apache.mina.http.HttpClientCodec;
|
||||||
import org.apache.mina.http.HttpRequestImpl;
|
import org.apache.mina.http.HttpRequestImpl;
|
||||||
import org.apache.mina.http.api.HttpMethod;
|
import org.apache.mina.http.api.HttpMethod;
|
||||||
@@ -40,7 +34,6 @@ import org.apache.mina.http.api.HttpVersion;
|
|||||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import properties.SystemProperties;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
@@ -53,9 +46,9 @@ import java.util.concurrent.LinkedTransferQueue;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public class ShoutcastMetadataUpdater
|
public class ShoutcastV1BroadcastMetadataUpdater implements IBroadcastMetadataUpdater
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(ShoutcastMetadataUpdater.class);
|
private final static Logger mLog = LoggerFactory.getLogger(ShoutcastV1BroadcastMetadataUpdater.class);
|
||||||
private final static String UTF8 = "UTF-8";
|
private final static String UTF8 = "UTF-8";
|
||||||
|
|
||||||
private ThreadPoolManager mThreadPoolManager;
|
private ThreadPoolManager mThreadPoolManager;
|
||||||
@@ -73,7 +66,7 @@ public class ShoutcastMetadataUpdater
|
|||||||
* broadcaster. When multiple metadata updates are received prior to completion of the current ongoing update
|
* 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.
|
* sequence, those updates will be queued and processed in the order received.
|
||||||
*/
|
*/
|
||||||
public ShoutcastMetadataUpdater(ThreadPoolManager threadPoolManager, ShoutcastV1Configuration shoutcastV1Configuration)
|
public ShoutcastV1BroadcastMetadataUpdater(ThreadPoolManager threadPoolManager, ShoutcastV1Configuration shoutcastV1Configuration)
|
||||||
{
|
{
|
||||||
mThreadPoolManager = threadPoolManager;
|
mThreadPoolManager = threadPoolManager;
|
||||||
mShoutcastV1Configuration = shoutcastV1Configuration;
|
mShoutcastV1Configuration = shoutcastV1Configuration;
|
||||||
@@ -89,7 +82,7 @@ public class ShoutcastMetadataUpdater
|
|||||||
mSocketConnector = new NioSocketConnector();
|
mSocketConnector = new NioSocketConnector();
|
||||||
|
|
||||||
// mSocketConnector.getFilterChain().addLast("logger",
|
// mSocketConnector.getFilterChain().addLast("logger",
|
||||||
// new LoggingFilter(ShoutcastMetadataUpdater.class));
|
// new LoggingFilter(ShoutcastV1BroadcastMetadataUpdater.class));
|
||||||
|
|
||||||
mSocketConnector.getFilterChain().addLast("http_client_codec", new HttpClientCodec());
|
mSocketConnector.getFilterChain().addLast("http_client_codec", new HttpClientCodec());
|
||||||
|
|
||||||
@@ -257,7 +250,6 @@ public class ShoutcastMetadataUpdater
|
|||||||
sb.append("&song=").append(URLEncoder.encode(song, UTF8));
|
sb.append("&song=").append(URLEncoder.encode(song, UTF8));
|
||||||
|
|
||||||
Map<String,String> headers = new HashMap<>();
|
Map<String,String> headers = new HashMap<>();
|
||||||
// headers.put("User-Agent", SystemProperties.getInstance().getApplicationName() + " (Mozilla compatible)");
|
|
||||||
|
|
||||||
HttpRequestImpl request = new HttpRequestImpl(HttpVersion.HTTP_1_0, HttpMethod.GET, "/admin.cgi",
|
HttpRequestImpl request = new HttpRequestImpl(HttpVersion.HTTP_1_0, HttpMethod.GET, "/admin.cgi",
|
||||||
sb.toString(), headers);
|
sb.toString(), headers);
|
||||||
@@ -239,7 +239,6 @@ public class ShoutcastV1ConfigurationEditor extends BroadcastConfigurationEditor
|
|||||||
updateConfigurationName(config, mName.getText());
|
updateConfigurationName(config, mName.getText());
|
||||||
config.setHost(mServer.getText());
|
config.setHost(mServer.getText());
|
||||||
config.setPort(getPort());
|
config.setPort(getPort());
|
||||||
config.setDescription(mDescription.getText());
|
|
||||||
config.setPassword(mPassword.getText());
|
config.setPassword(mPassword.getText());
|
||||||
config.setDescription(mDescription.getText());
|
config.setDescription(mDescription.getText());
|
||||||
config.setGenre(mGenre.getText());
|
config.setGenre(mGenre.getText());
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
* Copyright (C) 2014-2017 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,120 +18,199 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2;
|
package audio.broadcast.shoutcast.v2;
|
||||||
|
|
||||||
import audio.AudioPacket;
|
|
||||||
import audio.broadcast.AudioBroadcaster;
|
import audio.broadcast.AudioBroadcaster;
|
||||||
import audio.broadcast.BroadcastFactory;
|
|
||||||
import audio.broadcast.BroadcastFormat;
|
|
||||||
import audio.broadcast.BroadcastState;
|
import audio.broadcast.BroadcastState;
|
||||||
import audio.broadcast.shoutcast.v2.message.AuthenticateBroadcast;
|
import audio.broadcast.IBroadcastMetadataUpdater;
|
||||||
import audio.broadcast.shoutcast.v2.message.ConfigureIcyGenre;
|
import audio.broadcast.shoutcast.v2.ultravox.AuthenticateBroadcast;
|
||||||
import audio.broadcast.shoutcast.v2.message.ConfigureIcyName;
|
import audio.broadcast.shoutcast.v2.ultravox.CacheableXMLMetadata;
|
||||||
import audio.broadcast.shoutcast.v2.message.ConfigureIcyPublic;
|
import audio.broadcast.shoutcast.v2.ultravox.ConfigureIcyName;
|
||||||
import audio.broadcast.shoutcast.v2.message.ConfigureIcyURL;
|
import audio.broadcast.shoutcast.v2.ultravox.ConfigureIcyPublic;
|
||||||
import audio.broadcast.shoutcast.v2.message.MP3Audio;
|
import audio.broadcast.shoutcast.v2.ultravox.MP3Audio;
|
||||||
import audio.broadcast.shoutcast.v2.message.NegotiateMaxPayloadSize;
|
import audio.broadcast.shoutcast.v2.ultravox.NegotiateMaxPayloadSize;
|
||||||
import audio.broadcast.shoutcast.v2.message.RequestCipher;
|
import audio.broadcast.shoutcast.v2.ultravox.RequestCipher;
|
||||||
import audio.broadcast.shoutcast.v2.message.SetupBroadcast;
|
import audio.broadcast.shoutcast.v2.ultravox.SetupBroadcast;
|
||||||
import audio.broadcast.shoutcast.v2.message.Standby;
|
import audio.broadcast.shoutcast.v2.ultravox.Standby;
|
||||||
import audio.broadcast.shoutcast.v2.message.UltravoxMessage;
|
import audio.broadcast.shoutcast.v2.ultravox.StreamMimeType;
|
||||||
import audio.broadcast.shoutcast.v2.message.UltravoxMessageFactory;
|
import audio.broadcast.shoutcast.v2.ultravox.TerminateBroadcast;
|
||||||
import audio.broadcast.shoutcast.v2.message.UltravoxMessageType;
|
import audio.broadcast.shoutcast.v2.ultravox.UltravoxMessage;
|
||||||
import audio.broadcast.shoutcast.v2.message.UltravoxMetadata;
|
import audio.broadcast.shoutcast.v2.ultravox.UltravoxMessageFactory;
|
||||||
import audio.broadcast.shoutcast.v2.message.XMLMetadata;
|
import audio.broadcast.shoutcast.v2.ultravox.UltravoxMessageType;
|
||||||
|
import audio.broadcast.shoutcast.v2.ultravox.UltravoxMetadata;
|
||||||
|
import audio.broadcast.shoutcast.v2.ultravox.UltravoxProtocolFactory;
|
||||||
import audio.metadata.AudioMetadata;
|
import audio.metadata.AudioMetadata;
|
||||||
|
import audio.metadata.Metadata;
|
||||||
|
import audio.metadata.MetadataType;
|
||||||
import controller.ThreadPoolManager;
|
import controller.ThreadPoolManager;
|
||||||
|
import org.apache.mina.core.RuntimeIoException;
|
||||||
|
import org.apache.mina.core.future.ConnectFuture;
|
||||||
|
import org.apache.mina.core.service.IoHandlerAdapter;
|
||||||
|
import org.apache.mina.core.session.IoSession;
|
||||||
|
import org.apache.mina.filter.codec.ProtocolCodecFilter;
|
||||||
|
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import properties.SystemProperties;
|
import properties.SystemProperties;
|
||||||
import record.wave.AudioPacketMonoWaveReader;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.ConnectException;
|
||||||
import java.net.SocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.LinkedTransferQueue;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster
|
public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster implements IBroadcastMetadataUpdater
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( ShoutcastV2AudioBroadcaster.class );
|
private final static Logger mLog = LoggerFactory.getLogger( ShoutcastV2AudioBroadcaster.class );
|
||||||
|
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
|
||||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 15000; //15 seconds
|
|
||||||
private static final boolean GET_RESPONSE = true;
|
|
||||||
private static final boolean NO_RESPONSE_EXPECTED = false;
|
|
||||||
private long mLastConnectionAttempt = 0;
|
|
||||||
|
|
||||||
private Socket mSocket;
|
|
||||||
private DataOutputStream mOutputStream;
|
|
||||||
private DataInputStream mInputStream;
|
|
||||||
private int mMaxPayloadSize = 16377;
|
private int mMaxPayloadSize = 16377;
|
||||||
private List<AudioPacket> mPacketsToBroadcast = new ArrayList<>();
|
|
||||||
|
private NioSocketConnector mSocketConnector;
|
||||||
|
private IoSession mStreamingSession = null;
|
||||||
|
|
||||||
|
private long mLastConnectionAttempt = 0;
|
||||||
|
private AtomicBoolean mConnecting = new AtomicBoolean();
|
||||||
|
private LinkedTransferQueue<UltravoxMessage> mMetadataMessageQueue = new LinkedTransferQueue<>();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Shoutcast Version 2 broadcaster.
|
* Shoutcast 2.x (Ultravox 2.1) broadcaster. This broadcaster is compatible with Shoutcast 2.x and newer versions
|
||||||
* @param configuration details for shoutcast version 2
|
* of the server software.
|
||||||
|
*
|
||||||
|
* Note: use @see ShoutcastV1AudioBroadcaster for Shoutcast version 1.x and older.
|
||||||
|
*
|
||||||
|
* 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(ThreadPoolManager threadPoolManager, ShoutcastV2Configuration configuration)
|
||||||
{
|
{
|
||||||
super(threadPoolManager, configuration);
|
super(threadPoolManager, configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public ShoutcastV2Configuration getConfiguration()
|
||||||
* Shoutcast V2 Configuration information
|
|
||||||
*/
|
|
||||||
private ShoutcastV2Configuration getShoutcastConfiguration()
|
|
||||||
{
|
{
|
||||||
return (ShoutcastV2Configuration)getBroadcastConfiguration();
|
return (ShoutcastV2Configuration)getBroadcastConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void broadcastMetadata(AudioMetadata metadata)
|
|
||||||
{
|
|
||||||
mLog.debug("Request to broadcast audio metadata to shoutcast v2 server - needs coding");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcast audio
|
* Broadcasts the audio frame or sequence
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void broadcastAudio(byte[] audio)
|
protected void broadcastAudio(byte[] audio)
|
||||||
{
|
{
|
||||||
//If we're connected, send the audio, otherwise discard it
|
//Dispatch any queued metadata messages
|
||||||
if(connect())
|
UltravoxMessage metadataMessage = mMetadataMessageQueue.poll();
|
||||||
{
|
|
||||||
List<UltravoxMessage> audioMessages = getAudioMessages(audio);
|
|
||||||
|
|
||||||
try
|
while(metadataMessage != null)
|
||||||
{
|
{
|
||||||
for(UltravoxMessage audioMessage: audioMessages)
|
mStreamingSession.write(metadataMessage);
|
||||||
{
|
metadataMessage = mMetadataMessageQueue.poll();
|
||||||
send(audioMessage, NO_RESPONSE_EXPECTED);
|
}
|
||||||
}
|
|
||||||
}
|
//Dispatch audio message
|
||||||
catch(IOException e)
|
if(audio != null && audio.length > 0 && connect() && mStreamingSession != null && mStreamingSession.isConnected())
|
||||||
{
|
{
|
||||||
mLog.error("Error while dispatching audio", e);
|
MP3Audio mp3Audio = new MP3Audio();
|
||||||
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
|
mp3Audio.setPayload(audio);
|
||||||
return;
|
|
||||||
}
|
mStreamingSession.write(mp3Audio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(AudioMetadata metadata)
|
||||||
|
{
|
||||||
|
List<UltravoxMessage> metadataMessages = getMetadataMessages(metadata);
|
||||||
|
|
||||||
|
if(!metadataMessages.isEmpty())
|
||||||
|
{
|
||||||
|
mMetadataMessageQueue.addAll(metadataMessages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IBroadcastMetadataUpdater getMetadataUpdater()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the audio handler is currently connected to the remote server and capable of streaming audio.
|
* (Re)Connects the broadcaster to the remote server if it currently is disconnected and indicates if the broadcaster
|
||||||
|
* is currently connected to the remote server following any connection attempts.
|
||||||
|
*
|
||||||
|
* Attempts to connect via this method when the broadcast state indicates an error condition will be ignored.
|
||||||
*
|
*
|
||||||
* This method will attempt to establish a connection or reconnect to the streaming server.
|
|
||||||
* @return true if the audio handler can stream audio
|
* @return true if the audio handler can stream audio
|
||||||
*/
|
*/
|
||||||
private boolean connect()
|
private boolean connect()
|
||||||
{
|
{
|
||||||
if(!connected())
|
if(!connected() && canConnect() &&
|
||||||
|
(mLastConnectionAttempt + RECONNECT_INTERVAL_MILLISECONDS < System.currentTimeMillis()) &&
|
||||||
|
mConnecting.compareAndSet(false, true))
|
||||||
{
|
{
|
||||||
createConnection();
|
mLastConnectionAttempt = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if(mSocketConnector == null)
|
||||||
|
{
|
||||||
|
mSocketConnector = new NioSocketConnector();
|
||||||
|
mSocketConnector.setConnectTimeoutCheckInterval(10000);
|
||||||
|
|
||||||
|
// mSocketConnector.getFilterChain().addLast("logger",
|
||||||
|
// new LoggingFilter(ShoutcastV2AudioBroadcaster.class));
|
||||||
|
|
||||||
|
mSocketConnector.getFilterChain().addLast("codec",
|
||||||
|
new ProtocolCodecFilter(new UltravoxProtocolFactory()));
|
||||||
|
|
||||||
|
mSocketConnector.setHandler(new ShoutcastV2IOHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
mStreamingSession = null;
|
||||||
|
|
||||||
|
Runnable runnable = new Runnable()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.CONNECTING);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ConnectFuture future = mSocketConnector
|
||||||
|
.connect(new InetSocketAddress(getBroadcastConfiguration().getHost(),
|
||||||
|
getBroadcastConfiguration().getPort()));
|
||||||
|
future.awaitUninterruptibly();
|
||||||
|
mStreamingSession = future.getSession();
|
||||||
|
}
|
||||||
|
catch(RuntimeIoException rie)
|
||||||
|
{
|
||||||
|
Throwable throwableCause = rie.getCause();
|
||||||
|
|
||||||
|
if(throwableCause instanceof ConnectException)
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.NO_SERVER);
|
||||||
|
}
|
||||||
|
else if(throwableCause != null)
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
|
mLog.error("Failed to connect", rie);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
|
mLog.error("Failed to connect - no exception is available");
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getThreadPoolManager().scheduleOnce(runnable, 0l, TimeUnit.SECONDS);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return connected();
|
return connected();
|
||||||
@@ -139,112 +218,85 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnect from the remote server
|
* Disconnect from the remote broadcast server and cleanup input/output streams and socket connection
|
||||||
*/
|
*/
|
||||||
public void disconnect()
|
public void disconnect()
|
||||||
{
|
{
|
||||||
// TerminateBroadcast terminate = (TerminateBroadcast)UltravoxMessageFactory
|
if(connected())
|
||||||
// .getMessage(UltravoxMessageType.TERMINATE_BROADCAST);
|
|
||||||
// response = send(terminate, out, in);
|
|
||||||
// mLog.debug("Terminate Response:" + (response.getPayload() != null ? response.getPayload() : ""));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the message. When get response is true, reads from the input stream and converts the response to a
|
|
||||||
* message that is returned.
|
|
||||||
*
|
|
||||||
* @param message to send to the shoutcast/ultravox server
|
|
||||||
* @param getResponse true if the sent message will generate a response
|
|
||||||
* @return response message when getResponse is set to true
|
|
||||||
* @throws IOException if there is an issue while communicating with the server
|
|
||||||
*/
|
|
||||||
private UltravoxMessage send(UltravoxMessage message, boolean getResponse) throws IOException
|
|
||||||
{
|
|
||||||
byte[] bytes = message.getMessage();
|
|
||||||
|
|
||||||
mOutputStream.write(bytes);
|
|
||||||
|
|
||||||
if(getResponse)
|
|
||||||
{
|
{
|
||||||
try
|
if(mStreamingSession != null)
|
||||||
{
|
{
|
||||||
Thread.sleep(50);
|
mStreamingSession.write(new TerminateBroadcast());
|
||||||
}
|
mStreamingSession.closeNow();
|
||||||
catch (InterruptedException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
int available = mInputStream.available();
|
|
||||||
|
|
||||||
if(available > 0)
|
|
||||||
{
|
|
||||||
byte[] buffer = new byte[available];
|
|
||||||
int read = mInputStream.read(buffer);
|
|
||||||
|
|
||||||
return UltravoxMessageFactory.getMessage(buffer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the encoded audio byte array into a list of ultravox messages ready for transmission
|
* Encodes the list of metadata in one or more Ultravox CacheableXMLMetadata messages according to the maximum negotiated
|
||||||
* @param audio to embed as message payload
|
|
||||||
* @return list of messages containing the audio payload
|
|
||||||
*/
|
|
||||||
private List<UltravoxMessage> getAudioMessages(byte[] audio)
|
|
||||||
{
|
|
||||||
List<UltravoxMessage> messages = new ArrayList<>();
|
|
||||||
|
|
||||||
int pointer = 0;
|
|
||||||
|
|
||||||
while(pointer < audio.length)
|
|
||||||
{
|
|
||||||
int payloadSize = Math.min(mMaxPayloadSize, audio.length - pointer);
|
|
||||||
|
|
||||||
byte[] payload = new byte[payloadSize];
|
|
||||||
|
|
||||||
System.arraycopy(audio, pointer, payload, 0, payloadSize);
|
|
||||||
|
|
||||||
MP3Audio mp3Audio = (MP3Audio)UltravoxMessageFactory.getMessage(UltravoxMessageType.MP3_DATA);
|
|
||||||
mp3Audio.setPayload(payload);
|
|
||||||
messages.add(mp3Audio);
|
|
||||||
|
|
||||||
pointer += payloadSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encodes the list of metadata in one or more Ultravox XMLMetadata messages according to the maximum negotiated
|
|
||||||
* payload size for the current connection. Each entry in the list of metadata strings should be an xml encoded
|
* payload size for the current connection. Each entry in the list of metadata strings should be an xml encoded
|
||||||
* value: <tag>value</tag>
|
* value: <tag>value</tag>
|
||||||
*
|
*
|
||||||
* See UltravoxMetadata.TAG.asXML(String value)
|
* See UltravoxMetadata.TAG.asXML(String value)
|
||||||
*
|
*
|
||||||
* @param xmlMetadata list of xml encoded values
|
* @param metadata containing audio metadata tags and attributes
|
||||||
* @return a sequence of XMLMetadata messages sufficient to carry the complete set of metadata
|
* @return a sequence of CacheableXMLMetadata messages sufficient to carry the complete set of metadata
|
||||||
*/
|
*/
|
||||||
private List<UltravoxMessage> getMetadataMessages(List<String> xmlMetadata)
|
private List<UltravoxMessage> getMetadataMessages(AudioMetadata metadata)
|
||||||
{
|
{
|
||||||
List<UltravoxMessage> messages = new ArrayList<>();
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<metadata>\n");
|
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?><metadata>");
|
||||||
|
|
||||||
for(String metadata: xmlMetadata)
|
sb.append(UltravoxMetadata.ALBUM_TITLE.asXML(getConfiguration().getName()));
|
||||||
|
|
||||||
|
sb.append(UltravoxMetadata.BROADCAST_CLIENT_APPLICATION.asXML(SystemProperties.getInstance().getApplicationName()));
|
||||||
|
|
||||||
|
if(getConfiguration().hasGenre())
|
||||||
{
|
{
|
||||||
sb.append(metadata);
|
sb.append(UltravoxMetadata.GENRE.asXML(getConfiguration().getGenre()));
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("\n</metadata>");
|
if(getConfiguration().hasURL())
|
||||||
|
{
|
||||||
|
sb.append(UltravoxMetadata.URL.asXML(getConfiguration().getURL()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(metadata != null)
|
||||||
|
{
|
||||||
|
Metadata to = metadata.getMetadata(MetadataType.TO);
|
||||||
|
if(to != null)
|
||||||
|
{
|
||||||
|
if(to.hasAlias())
|
||||||
|
{
|
||||||
|
sb.append(UltravoxMetadata.TITLE_2.asXML(to.getAlias().toString()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.append(UltravoxMetadata.TITLE_2.asXML(to.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Metadata from = metadata.getMetadata(MetadataType.FROM);
|
||||||
|
if(from != null)
|
||||||
|
{
|
||||||
|
if(from.hasAlias())
|
||||||
|
{
|
||||||
|
sb.append(UltravoxMetadata.TITLE_3.asXML("From: " + from.getAlias().toString()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.append(UltravoxMetadata.TITLE_3.asXML("From: " + from.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.append(UltravoxMetadata.TITLE_2.asXML("Scanning ..."));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("</metadata>");
|
||||||
|
|
||||||
mLog.debug("Metadata:" + sb.toString());
|
|
||||||
byte[] xml = sb.toString().getBytes();
|
byte[] xml = sb.toString().getBytes();
|
||||||
|
|
||||||
int pointer = 0;
|
int pointer = 0;
|
||||||
@@ -257,6 +309,8 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List<UltravoxMessage> messages = new ArrayList<>();
|
||||||
|
|
||||||
while(pointer < xml.length && messageCounter <= messageCount)
|
while(pointer < xml.length && messageCounter <= messageCount)
|
||||||
{
|
{
|
||||||
int payloadSize = Math.min(mMaxPayloadSize - 6, xml.length - pointer);
|
int payloadSize = Math.min(mMaxPayloadSize - 6, xml.length - pointer);
|
||||||
@@ -269,7 +323,7 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster
|
|||||||
|
|
||||||
System.arraycopy(xml, pointer, payload, 6, payloadSize);
|
System.arraycopy(xml, pointer, payload, 6, payloadSize);
|
||||||
|
|
||||||
XMLMetadata message = (XMLMetadata)UltravoxMessageFactory.getMessage(UltravoxMessageType.XML_METADATA);
|
CacheableXMLMetadata message = new CacheableXMLMetadata();
|
||||||
message.setPayload(payload);
|
message.setPayload(payload);
|
||||||
messages.add(message);
|
messages.add(message);
|
||||||
|
|
||||||
@@ -281,235 +335,194 @@ public class ShoutcastV2AudioBroadcaster extends AudioBroadcaster
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a connnection to the remote server using the shoutcast configuration information. Once disconnected
|
* IO Handler for managing Shoutcast V2 connection and credentials
|
||||||
* following a successful connection, attempts to reestablish a connection on a set interval
|
|
||||||
*/
|
*/
|
||||||
private void createConnection()
|
public class ShoutcastV2IOHandler extends IoHandlerAdapter
|
||||||
{
|
{
|
||||||
if(!connected() && System.currentTimeMillis() - mLastConnectionAttempt >= RECONNECT_INTERVAL_MILLISECONDS)
|
/**
|
||||||
|
* Sends stream configuration and user credentials upon connecting to remote server
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void sessionOpened(IoSession session) throws Exception
|
||||||
{
|
{
|
||||||
mLastConnectionAttempt = System.currentTimeMillis();
|
session.write(UltravoxMessageFactory.getMessage(UltravoxMessageType.REQUEST_CIPHER));
|
||||||
|
}
|
||||||
|
|
||||||
createSocket();
|
@Override
|
||||||
|
public void sessionClosed(IoSession session) throws Exception
|
||||||
if (mSocket.isConnected())
|
{
|
||||||
|
//If there is already an error state, don't override it. Otherwise, set state to disconnected
|
||||||
|
if(!getBroadcastState().isErrorState())
|
||||||
{
|
{
|
||||||
RequestCipher requestCipher = (RequestCipher) UltravoxMessageFactory
|
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||||
.getMessage(UltravoxMessageType.REQUEST_CIPHER);
|
}
|
||||||
|
|
||||||
try
|
mSocketConnector.dispose();
|
||||||
|
mStreamingSession = null;
|
||||||
|
mSocketConnector = null;
|
||||||
|
|
||||||
|
mConnecting.set(false);
|
||||||
|
super.sessionClosed(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
|
||||||
|
{
|
||||||
|
if(cause instanceof IOException && ((IOException)cause).getMessage().startsWith("Connection reset"))
|
||||||
|
{
|
||||||
|
mLog.info("Streaming connection reset by remote server - reestablishing connection");
|
||||||
|
disconnect();
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLog.error("Broadcast error", cause);
|
||||||
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
mConnecting.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void messageReceived(IoSession session, Object object) throws Exception
|
||||||
|
{
|
||||||
|
if(object instanceof UltravoxMessage)
|
||||||
|
{
|
||||||
|
UltravoxMessage ultravoxMessage = (UltravoxMessage)object;
|
||||||
|
|
||||||
|
switch (ultravoxMessage.getMessageType())
|
||||||
{
|
{
|
||||||
UltravoxMessage response = send(requestCipher, GET_RESPONSE);
|
case REQUEST_CIPHER:
|
||||||
|
String cipherKey = ((RequestCipher)ultravoxMessage).getCipher();
|
||||||
|
|
||||||
if (response instanceof RequestCipher)
|
AuthenticateBroadcast authenticateBroadcast = new AuthenticateBroadcast();
|
||||||
{
|
|
||||||
RequestCipher cipherResponse = (RequestCipher) response;
|
|
||||||
|
|
||||||
AuthenticateBroadcast authenticateBroadcast = (AuthenticateBroadcast) UltravoxMessageFactory
|
authenticateBroadcast.setCredentials(cipherKey, getConfiguration().getStreamID(),
|
||||||
.getMessage(UltravoxMessageType.AUTHENTICATE_BROADCAST);
|
getConfiguration().getUserID(), getConfiguration().getPassword());
|
||||||
authenticateBroadcast.setCredentials(cipherResponse.getCipher(),
|
|
||||||
getShoutcastConfiguration().getStreamID(), getShoutcastConfiguration().getUserID(),
|
|
||||||
getShoutcastConfiguration().getPassword());
|
|
||||||
response = send(authenticateBroadcast, GET_RESPONSE);
|
|
||||||
|
|
||||||
if (response.isErrorResponse())
|
session.write(authenticateBroadcast);
|
||||||
|
break;
|
||||||
|
case AUTHENTICATE_BROADCAST:
|
||||||
|
if(ultravoxMessage.isErrorResponse())
|
||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.INVALID_CREDENTIALS);
|
String errorMessage = ultravoxMessage.getErrorMessage();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UltravoxMessage streamMimeType = UltravoxMessageFactory
|
if(errorMessage.startsWith(AuthenticateBroadcast.STREAM_ID_ERROR))
|
||||||
.getMessage(UltravoxMessageType.STREAM_MIME_TYPE);
|
{
|
||||||
streamMimeType.setPayload(getShoutcastConfiguration().getBroadcastFormat().getValue());
|
setBroadcastState(BroadcastState.INVALID_MOUNT_POINT);
|
||||||
response = send(streamMimeType, GET_RESPONSE);
|
}
|
||||||
|
else
|
||||||
if (response.isErrorResponse())
|
{
|
||||||
{
|
mLog.error("Invalid Credentials - response: " + ultravoxMessage.getPayload());
|
||||||
setBroadcastState(BroadcastState.UNSUPPORTED_AUDIO_FORMAT);
|
setBroadcastState(BroadcastState.INVALID_CREDENTIALS);
|
||||||
mLog.error("Audio format [" + getShoutcastConfiguration().getBroadcastFormat() + "] not supported");
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetupBroadcast setupBroadcast = (SetupBroadcast) UltravoxMessageFactory
|
|
||||||
.getMessage(UltravoxMessageType.SETUP_BROADCAST);
|
|
||||||
setupBroadcast.setBitRate(getShoutcastConfiguration().getBitRate(),
|
|
||||||
getShoutcastConfiguration().getBitRate());
|
|
||||||
response = send(setupBroadcast, GET_RESPONSE);
|
|
||||||
|
|
||||||
if (response.isErrorResponse())
|
|
||||||
{
|
|
||||||
setBroadcastState(BroadcastState.UNSUPPORTED_AUDIO_FORMAT);
|
|
||||||
mLog.error("Audio bit rate [" + getShoutcastConfiguration().getBitRate() + "] not supported");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NegotiateMaxPayloadSize negotiateMaxPayloadSize = (NegotiateMaxPayloadSize) UltravoxMessageFactory
|
|
||||||
.getMessage(UltravoxMessageType.NEGOTIATE_MAX_PAYLOAD_SIZE);
|
|
||||||
negotiateMaxPayloadSize.setMaximumPayloadSize(mMaxPayloadSize, 14000);
|
|
||||||
response = send(negotiateMaxPayloadSize, GET_RESPONSE);
|
|
||||||
mMaxPayloadSize = ((NegotiateMaxPayloadSize) response).getMaximumPayloadSize();
|
|
||||||
|
|
||||||
List<String> metadata = new ArrayList<>();
|
|
||||||
|
|
||||||
if (getShoutcastConfiguration().getStreamName() != null)
|
|
||||||
{
|
|
||||||
metadata.add(UltravoxMetadata.TITLE_1.asXML(getShoutcastConfiguration().getStreamName()));
|
|
||||||
|
|
||||||
ConfigureIcyName icyName = (ConfigureIcyName) UltravoxMessageFactory
|
|
||||||
.getMessage(UltravoxMessageType.CONFIGURE_ICY_NAME);
|
|
||||||
icyName.setName(getShoutcastConfiguration().getStreamName());
|
|
||||||
response = send(icyName, NO_RESPONSE_EXPECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getShoutcastConfiguration().getGenre() != null)
|
|
||||||
{
|
|
||||||
metadata.add(UltravoxMetadata.GENRE.asXML(getShoutcastConfiguration().getGenre()));
|
|
||||||
|
|
||||||
ConfigureIcyGenre icyGenre = (ConfigureIcyGenre) UltravoxMessageFactory
|
|
||||||
.getMessage(UltravoxMessageType.CONFIGURE_ICY_GENRE);
|
|
||||||
icyGenre.setGenre(getShoutcastConfiguration().getGenre());
|
|
||||||
response = send(icyGenre, NO_RESPONSE_EXPECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getShoutcastConfiguration().getURL() != null)
|
|
||||||
{
|
|
||||||
ConfigureIcyURL icyURL = (ConfigureIcyURL) UltravoxMessageFactory
|
|
||||||
.getMessage(UltravoxMessageType.CONFIGURE_ICY_URL);
|
|
||||||
icyURL.setURL(getShoutcastConfiguration().getURL());
|
|
||||||
response = send(icyURL, NO_RESPONSE_EXPECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigureIcyPublic icyPublic = (ConfigureIcyPublic) UltravoxMessageFactory
|
|
||||||
.getMessage(UltravoxMessageType.CONFIGURE_ICY_PUBLIC);
|
|
||||||
icyPublic.setPublic(getShoutcastConfiguration().isPublic());
|
|
||||||
response = send(icyPublic, NO_RESPONSE_EXPECTED);
|
|
||||||
|
|
||||||
Standby standby = (Standby) UltravoxMessageFactory.getMessage(UltravoxMessageType.STANDBY);
|
|
||||||
response = send(standby, GET_RESPONSE);
|
|
||||||
|
|
||||||
if (response.isValidResponse())
|
|
||||||
{
|
|
||||||
setBroadcastState(BroadcastState.CONNECTED);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
StreamMimeType streamMimeType = new StreamMimeType();
|
||||||
mLog.error("Error message after configuration and sending standby:" +
|
streamMimeType.setFormat(getConfiguration().getBroadcastFormat());
|
||||||
(response.getError() != null ? response.getError() : "no error message"));
|
session.write(streamMimeType);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
metadata.add(UltravoxMetadata.BROADCAST_CLIENT_APPLICATION
|
case STREAM_MIME_TYPE:
|
||||||
.asXML(SystemProperties.getInstance().getApplicationName()));
|
if(ultravoxMessage.isErrorResponse())
|
||||||
|
|
||||||
mLog.debug("Fetching metadata messages from metadata...");
|
|
||||||
List<UltravoxMessage> messages = getMetadataMessages(metadata);
|
|
||||||
|
|
||||||
for (UltravoxMessage message : messages)
|
|
||||||
{
|
{
|
||||||
mLog.debug("Sending metadata message");
|
mLog.error("Unsupported Audio Format:" + getConfiguration().getBroadcastFormat().toString() +
|
||||||
send(message, GET_RESPONSE);
|
" - " + ultravoxMessage.getErrorMessage());
|
||||||
|
setBroadcastState(BroadcastState.UNSUPPORTED_AUDIO_FORMAT);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetupBroadcast setupBroadcast = new SetupBroadcast();
|
||||||
|
|
||||||
mLog.debug("We're connected!!!");
|
//Use the same value for average and minimum bit rates
|
||||||
}
|
setupBroadcast.setBitRate(getConfiguration().getBitRate(), getConfiguration().getBitRate());
|
||||||
}
|
|
||||||
catch (IOException e)
|
session.write(setupBroadcast);
|
||||||
{
|
}
|
||||||
setBroadcastState(BroadcastState.ERROR);
|
break;
|
||||||
mLog.error("Error while creating connection to server", e);
|
case SETUP_BROADCAST:
|
||||||
|
if(ultravoxMessage.isErrorResponse())
|
||||||
|
{
|
||||||
|
mLog.error("Unsupported Audio Bit Rate:" + getConfiguration().getBitRate() +
|
||||||
|
" - " + ultravoxMessage.getErrorMessage());
|
||||||
|
setBroadcastState(BroadcastState.UNSUPPORTED_AUDIO_FORMAT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NegotiateMaxPayloadSize negotiateMaxPayloadSize = new NegotiateMaxPayloadSize();
|
||||||
|
|
||||||
|
negotiateMaxPayloadSize.setMaximumPayloadSize(16377, 4192);
|
||||||
|
|
||||||
|
session.write(negotiateMaxPayloadSize);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NEGOTIATE_MAX_PAYLOAD_SIZE:
|
||||||
|
if(ultravoxMessage.isErrorResponse())
|
||||||
|
{
|
||||||
|
mLog.error("Unsupported maximum payload size (18 min - 36 kbps max) - " +
|
||||||
|
ultravoxMessage.getErrorMessage());
|
||||||
|
setBroadcastState(BroadcastState.UNSUPPORTED_AUDIO_FORMAT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mMaxPayloadSize = ((NegotiateMaxPayloadSize)ultravoxMessage).getMaximumPayloadSize();
|
||||||
|
|
||||||
|
ConfigureIcyPublic configureIcyPublic = new ConfigureIcyPublic();
|
||||||
|
configureIcyPublic.setPublic(getConfiguration().isPublic());
|
||||||
|
session.write(configureIcyPublic);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONFIGURE_ICY_PUBLIC:
|
||||||
|
if(ultravoxMessage.isErrorResponse())
|
||||||
|
{
|
||||||
|
mLog.error("Error setting shoutcast stream as public - " + ultravoxMessage.getErrorMessage());
|
||||||
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConfigureIcyName configureIcyName = new ConfigureIcyName();
|
||||||
|
configureIcyName.setName(getConfiguration().getName());
|
||||||
|
session.write(configureIcyName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONFIGURE_ICY_NAME:
|
||||||
|
if(ultravoxMessage.isErrorResponse())
|
||||||
|
{
|
||||||
|
mLog.error("Error setting shoutcast stream name - " + ultravoxMessage.getErrorMessage());
|
||||||
|
setBroadcastState(BroadcastState.CONFIGURATION_ERROR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
session.write(new Standby());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STANDBY:
|
||||||
|
if(ultravoxMessage.isErrorResponse())
|
||||||
|
{
|
||||||
|
mLog.error("Error following stream configuration and standby - " +
|
||||||
|
ultravoxMessage.getErrorMessage());
|
||||||
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.CONNECTED);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mLog.error("Unrecognized ultravox message:" + ultravoxMessage.getMessageType() +
|
||||||
|
" payload:" + ultravoxMessage.getPayload());
|
||||||
|
|
||||||
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
}
|
{
|
||||||
|
mLog.debug("Unrecognized message received from shoutcast v2 server:" + object.toString());
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a socket connection to the remote server and sets the state to CONNECTING.
|
|
||||||
*/
|
|
||||||
private void createSocket()
|
|
||||||
{
|
|
||||||
if(mSocket == null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mSocket = new Socket(getShoutcastConfiguration().getHost(),
|
|
||||||
getShoutcastConfiguration().getPort());
|
|
||||||
mOutputStream = new DataOutputStream(mSocket.getOutputStream());
|
|
||||||
mInputStream = new DataInputStream(mSocket.getInputStream());
|
|
||||||
}
|
|
||||||
catch(UnknownHostException uhe)
|
|
||||||
{
|
|
||||||
setBroadcastState(BroadcastState.NO_SERVER);
|
|
||||||
mLog.error("Unknown host or port. Unable to create connection to streaming server host[" +
|
|
||||||
getShoutcastConfiguration().getHost() + "] and port[" +
|
|
||||||
getShoutcastConfiguration().getPort() + "] - will reattempt connection periodically");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch(IOException ioe)
|
|
||||||
{
|
|
||||||
setBroadcastState(BroadcastState.ERROR);
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
|
|
||||||
mLog.error("Error connecting to streaming server host[" +
|
|
||||||
getShoutcastConfiguration().getHost() + "] and port[" +
|
|
||||||
getShoutcastConfiguration().getPort() + "]", ioe);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mSocket.isConnected())
|
|
||||||
{
|
|
||||||
setBroadcastState(BroadcastState.CONNECTING);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SocketAddress address = getShoutcastConfiguration().getAddress();
|
|
||||||
mSocket.connect(address);
|
|
||||||
setBroadcastState(BroadcastState.CONNECTING);
|
|
||||||
}
|
|
||||||
catch(UnknownHostException uhe)
|
|
||||||
{
|
|
||||||
setBroadcastState(BroadcastState.NO_SERVER);
|
|
||||||
}
|
|
||||||
catch(IOException e)
|
|
||||||
{
|
|
||||||
setBroadcastState(BroadcastState.ERROR);
|
|
||||||
|
|
||||||
mLog.error("Error connecting to streaming server host[" +
|
|
||||||
getShoutcastConfiguration().getHost() + "] and port[" +
|
|
||||||
getShoutcastConfiguration().getPort() + "]", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UltravoxMessage send(UltravoxMessage message,
|
|
||||||
DataOutputStream out,
|
|
||||||
DataInputStream in,
|
|
||||||
int maxPayloadSize) throws IOException
|
|
||||||
{
|
|
||||||
byte[] bytes = message.getMessage();
|
|
||||||
|
|
||||||
out.write(bytes);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.sleep(50);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
int available = in.available();
|
|
||||||
|
|
||||||
if(available > 0)
|
|
||||||
{
|
|
||||||
byte[] buffer = new byte[available];
|
|
||||||
int read = in.read(buffer);
|
|
||||||
|
|
||||||
return UltravoxMessageFactory.getMessage(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,13 @@ import audio.broadcast.BroadcastConfiguration;
|
|||||||
import audio.broadcast.BroadcastFormat;
|
import audio.broadcast.BroadcastFormat;
|
||||||
import audio.broadcast.BroadcastServerType;
|
import audio.broadcast.BroadcastServerType;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAttribute;
|
||||||
|
|
||||||
public class ShoutcastV2Configuration extends BroadcastConfiguration
|
public class ShoutcastV2Configuration extends BroadcastConfiguration
|
||||||
{
|
{
|
||||||
private int mStreamID;
|
private int mStreamID;
|
||||||
private String mUserID;
|
private String mUserID;
|
||||||
private int mBitRate = 16;
|
private int mBitRate = 16;
|
||||||
private String mStreamName;
|
|
||||||
private String mGenre;
|
private String mGenre;
|
||||||
private String mURL;
|
private String mURL;
|
||||||
private boolean mPublic = true;
|
private boolean mPublic = true;
|
||||||
@@ -59,7 +60,6 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
|||||||
copy.setStreamID(getStreamID());
|
copy.setStreamID(getStreamID());
|
||||||
copy.setUserID(getUserID());
|
copy.setUserID(getUserID());
|
||||||
copy.setBitRate(getBitRate());
|
copy.setBitRate(getBitRate());
|
||||||
copy.setStreamName(getStreamName());
|
|
||||||
copy.setGenre(getGenre());
|
copy.setGenre(getGenre());
|
||||||
copy.setURL(getURL());
|
copy.setURL(getURL());
|
||||||
copy.setPublic(isPublic());
|
copy.setPublic(isPublic());
|
||||||
@@ -77,6 +77,7 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
|||||||
/**
|
/**
|
||||||
* Stream ID
|
* Stream ID
|
||||||
*/
|
*/
|
||||||
|
@XmlAttribute(name = "stream_id")
|
||||||
public int getStreamID()
|
public int getStreamID()
|
||||||
{
|
{
|
||||||
return mStreamID;
|
return mStreamID;
|
||||||
@@ -96,6 +97,7 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
|||||||
/**
|
/**
|
||||||
* User ID
|
* User ID
|
||||||
*/
|
*/
|
||||||
|
@XmlAttribute(name = "user_id")
|
||||||
public String getUserID()
|
public String getUserID()
|
||||||
{
|
{
|
||||||
return mUserID;
|
return mUserID;
|
||||||
@@ -113,6 +115,7 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
|||||||
/**
|
/**
|
||||||
* Bit rate in kilobits per second
|
* Bit rate in kilobits per second
|
||||||
*/
|
*/
|
||||||
|
@XmlAttribute(name = "bitrate")
|
||||||
public int getBitRate()
|
public int getBitRate()
|
||||||
{
|
{
|
||||||
return mBitRate;
|
return mBitRate;
|
||||||
@@ -127,26 +130,10 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
|||||||
mBitRate = bitRate;
|
mBitRate = bitRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Stream name
|
|
||||||
*/
|
|
||||||
public String getStreamName()
|
|
||||||
{
|
|
||||||
return mStreamName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the stream name
|
|
||||||
* @param name
|
|
||||||
*/
|
|
||||||
public void setStreamName(String name)
|
|
||||||
{
|
|
||||||
mStreamName = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream genre
|
* Stream genre
|
||||||
*/
|
*/
|
||||||
|
@XmlAttribute(name = "genre")
|
||||||
public String getGenre()
|
public String getGenre()
|
||||||
{
|
{
|
||||||
return mGenre;
|
return mGenre;
|
||||||
@@ -160,9 +147,16 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
|||||||
mGenre = genre;
|
mGenre = genre;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean hasGenre()
|
||||||
|
{
|
||||||
|
return mGenre != null && !mGenre.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public visibility of the broadcastAudio
|
* Public visibility of the broadcastAudio
|
||||||
*/
|
*/
|
||||||
|
@XmlAttribute(name = "public")
|
||||||
public boolean isPublic()
|
public boolean isPublic()
|
||||||
{
|
{
|
||||||
return mPublic;
|
return mPublic;
|
||||||
@@ -180,6 +174,7 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
|||||||
/**
|
/**
|
||||||
* URL associated with the broadcastAudio where users can find additional details.
|
* URL associated with the broadcastAudio where users can find additional details.
|
||||||
*/
|
*/
|
||||||
|
@XmlAttribute(name = "url")
|
||||||
public String getURL()
|
public String getURL()
|
||||||
{
|
{
|
||||||
return mURL;
|
return mURL;
|
||||||
@@ -193,4 +188,18 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
|||||||
{
|
{
|
||||||
mURL = url;
|
mURL = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasURL()
|
||||||
|
{
|
||||||
|
return mURL != null && !mURL.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid()
|
||||||
|
{
|
||||||
|
return super.isValid() && hasName() &&
|
||||||
|
getUserID() != null && !getUserID().isEmpty() &&
|
||||||
|
1 <= getStreamID() &&
|
||||||
|
getPassword() != null && !getPassword().isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,363 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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.broadcast.shoutcast.v2;
|
||||||
|
|
||||||
|
import alias.AliasModel;
|
||||||
|
import audio.broadcast.BroadcastConfiguration;
|
||||||
|
import audio.broadcast.BroadcastConfigurationEditor;
|
||||||
|
import audio.broadcast.BroadcastEvent;
|
||||||
|
import audio.broadcast.BroadcastModel;
|
||||||
|
import audio.broadcast.BroadcastServerType;
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import settings.SettingsManager;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
|
public class ShoutcastV2ConfigurationEditor extends BroadcastConfigurationEditor
|
||||||
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger( ShoutcastV2ConfigurationEditor.class );
|
||||||
|
|
||||||
|
private static final int ONE_MINUTE_MS = 60000;
|
||||||
|
|
||||||
|
private JTextField mName;
|
||||||
|
private JTextField mServer;
|
||||||
|
private JTextField mPort;
|
||||||
|
private JTextField mStreamID;
|
||||||
|
private JTextField mUserID;
|
||||||
|
private JTextField mPassword;
|
||||||
|
private JTextField mGenre;
|
||||||
|
private JCheckBox mPublic;
|
||||||
|
private JSlider mDelay;
|
||||||
|
private JLabel mDelayValue;
|
||||||
|
private JCheckBox mEnabled;
|
||||||
|
private JButton mSaveButton;
|
||||||
|
private JButton mResetButton;
|
||||||
|
|
||||||
|
public ShoutcastV2ConfigurationEditor(BroadcastModel broadcastModel, AliasModel aliasModel,
|
||||||
|
SettingsManager settingsManager)
|
||||||
|
{
|
||||||
|
super(broadcastModel, aliasModel, settingsManager);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
setLayout( new MigLayout( "fill,wrap 2", "[align right][grow,fill]",
|
||||||
|
"[][][][][][][][][][][][][][grow,fill]" ) );
|
||||||
|
setPreferredSize(new Dimension(150,400));
|
||||||
|
|
||||||
|
JLabel channelLabel = new JLabel("Shoutcast (v2) Stream");
|
||||||
|
|
||||||
|
ImageIcon icon = mSettingsManager.getImageIcon(BroadcastServerType.SHOUTCAST_V2.getIconName(), 25);
|
||||||
|
channelLabel.setIcon(icon);
|
||||||
|
|
||||||
|
add(channelLabel, "span, align center");
|
||||||
|
|
||||||
|
add(new JLabel("Name:"));
|
||||||
|
mName = new JTextField();
|
||||||
|
mName.getDocument().addDocumentListener(this);
|
||||||
|
mName.setToolTipText("Unique name for this stream");
|
||||||
|
add(mName);
|
||||||
|
|
||||||
|
add(new JLabel("Server:"));
|
||||||
|
mServer = new JTextField();
|
||||||
|
mServer.getDocument().addDocumentListener(this);
|
||||||
|
mServer.setToolTipText("Server address (e.g. audio1.shoutcast.com or localhost)");
|
||||||
|
add(mServer);
|
||||||
|
|
||||||
|
add(new JLabel("Port:"));
|
||||||
|
mPort = new JTextField();
|
||||||
|
mPort.getDocument().addDocumentListener(this);
|
||||||
|
mPort.setToolTipText("Server port number (e.g. 80)");
|
||||||
|
add(mPort);
|
||||||
|
|
||||||
|
add(new JLabel("Stream ID:"));
|
||||||
|
mStreamID = new JTextField();
|
||||||
|
mStreamID.getDocument().addDocumentListener(this);
|
||||||
|
mStreamID.setToolTipText("Stream Identifier)");
|
||||||
|
add(mStreamID);
|
||||||
|
|
||||||
|
add(new JLabel("User ID:"));
|
||||||
|
mUserID = new JTextField();
|
||||||
|
mUserID.getDocument().addDocumentListener(this);
|
||||||
|
mUserID.setToolTipText("User ID/Name");
|
||||||
|
add(mUserID);
|
||||||
|
|
||||||
|
add(new JLabel("Password:"));
|
||||||
|
mPassword = new JTextField();
|
||||||
|
mPassword.getDocument().addDocumentListener(this);
|
||||||
|
mPassword.setToolTipText("Password for the stream");
|
||||||
|
add(mPassword);
|
||||||
|
|
||||||
|
add(new JLabel("Genre:"));
|
||||||
|
mGenre = new JTextField();
|
||||||
|
mGenre.getDocument().addDocumentListener(this);
|
||||||
|
mGenre.setToolTipText("Genre (e.g. public safety)");
|
||||||
|
add(mGenre);
|
||||||
|
|
||||||
|
add(new JLabel("Public:"));
|
||||||
|
mPublic = new JCheckBox();
|
||||||
|
mPublic.addActionListener(new ActionListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mPublic.setToolTipText("Designate stream as public (checked) or private");
|
||||||
|
add(mPublic);
|
||||||
|
|
||||||
|
add(new JLabel());
|
||||||
|
mDelayValue = new JLabel("0 Minutes");
|
||||||
|
add(mDelayValue);
|
||||||
|
|
||||||
|
add(new JLabel("Delay:"));
|
||||||
|
mDelay = new JSlider(0,60,0);
|
||||||
|
mDelay.setMajorTickSpacing(10);
|
||||||
|
mDelay.setMinorTickSpacing(5);
|
||||||
|
mDelay.addChangeListener(new ChangeListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e)
|
||||||
|
{
|
||||||
|
mDelayValue.setText(mDelay.getValue() + " Minutes");
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mDelay.setToolTipText("Audio broadcast delay in minutes");
|
||||||
|
add(mDelay);
|
||||||
|
|
||||||
|
add(new JLabel("Enabled:")); //Empty
|
||||||
|
mEnabled = new JCheckBox();
|
||||||
|
mEnabled.setToolTipText("Enable (checked) or disable this stream");
|
||||||
|
mEnabled.addActionListener(new ActionListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
add(mEnabled);
|
||||||
|
|
||||||
|
mSaveButton = new JButton("Save");
|
||||||
|
mSaveButton.setEnabled(false);
|
||||||
|
mSaveButton.setToolTipText("Click to save configuration information");
|
||||||
|
mSaveButton.addActionListener(new ActionListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
add(mSaveButton);
|
||||||
|
|
||||||
|
mResetButton = new JButton("Reset");
|
||||||
|
mResetButton.setEnabled(false);
|
||||||
|
mResetButton.setToolTipText("Click to reset any changes you've made since the last save");
|
||||||
|
mResetButton.addActionListener(new ActionListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
setModified(false);
|
||||||
|
setItem(getItem());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
add(mResetButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setModified(boolean modified)
|
||||||
|
{
|
||||||
|
super.setModified(modified);
|
||||||
|
|
||||||
|
mSaveButton.setEnabled(modified);
|
||||||
|
mResetButton.setEnabled(modified);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItem(BroadcastConfiguration item)
|
||||||
|
{
|
||||||
|
super.setItem(item);
|
||||||
|
|
||||||
|
if(hasItem())
|
||||||
|
{
|
||||||
|
ShoutcastV2Configuration config = (ShoutcastV2Configuration)getItem();
|
||||||
|
|
||||||
|
mName.setText(config.getName());
|
||||||
|
mServer.setText(config.getHost());
|
||||||
|
mPort.setText(config.getPort() != 0 ? String.valueOf(config.getPort()) : null);
|
||||||
|
mStreamID.setText(String.valueOf(config.getStreamID()));
|
||||||
|
mUserID.setText(config.getUserID());
|
||||||
|
mPassword.setText(config.getPassword());
|
||||||
|
mGenre.setText(config.getGenre());
|
||||||
|
mPublic.setSelected(config.isPublic());
|
||||||
|
mDelay.setValue((int)(config.getDelay() / ONE_MINUTE_MS));
|
||||||
|
mEnabled.setSelected(config.isEnabled());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mName.setText(null);
|
||||||
|
mServer.setText(null);
|
||||||
|
mPort.setText(null);
|
||||||
|
mStreamID.setText(null);
|
||||||
|
mUserID.setText(null);
|
||||||
|
mPassword.setText(null);
|
||||||
|
mGenre.setText(null);
|
||||||
|
mPublic.setSelected(true);
|
||||||
|
mDelay.setValue(0);
|
||||||
|
mEnabled.setSelected(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
setModified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save()
|
||||||
|
{
|
||||||
|
ShoutcastV2Configuration config = (ShoutcastV2Configuration)getItem();
|
||||||
|
|
||||||
|
if(validateConfiguration())
|
||||||
|
{
|
||||||
|
updateConfigurationName(config, mName.getText());
|
||||||
|
config.setHost(mServer.getText());
|
||||||
|
config.setPort(getPort());
|
||||||
|
config.setUserID(mUserID.getText());
|
||||||
|
config.setPassword(mPassword.getText());
|
||||||
|
config.setStreamID(getStreamID());
|
||||||
|
config.setGenre(mGenre.getText());
|
||||||
|
config.setPublic(mPublic.isSelected());
|
||||||
|
config.setDelay(mDelay.getValue() * ONE_MINUTE_MS);
|
||||||
|
config.setEnabled(mEnabled.isSelected());
|
||||||
|
|
||||||
|
setModified(false);
|
||||||
|
|
||||||
|
mBroadcastModel.process(new BroadcastEvent(getItem(), BroadcastEvent.Event.CONFIGURATION_CHANGE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the user specified values in the controls prior to saving them to the configuration
|
||||||
|
*/
|
||||||
|
private boolean validateConfiguration()
|
||||||
|
{
|
||||||
|
String name = mName.getText();
|
||||||
|
|
||||||
|
//A unique stream name is required
|
||||||
|
if(!mBroadcastModel.isUniqueName(name, getItem()))
|
||||||
|
{
|
||||||
|
String message = (name == null || name.isEmpty()) ?
|
||||||
|
"Please specify a stream name." : "Stream name " + name +
|
||||||
|
"is already in use.\nPlease choose another name.";
|
||||||
|
|
||||||
|
JOptionPane.showMessageDialog(ShoutcastV2ConfigurationEditor.this,
|
||||||
|
message, "Invalid Stream Name", JOptionPane.ERROR_MESSAGE);
|
||||||
|
|
||||||
|
mName.requestFocus();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Server address is required
|
||||||
|
if(!validateTextField(mServer, "Invalid Server Address", "Please specify a server address."))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Port is required
|
||||||
|
if(!validateIntegerTextField(mPort, "Invalid Port Number", "Please specify a port number (1 <> 65535)", 1, 65535))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Stream ID is required
|
||||||
|
if(!validateIntegerTextField(mStreamID, "Invalid Stream ID", "Please specify a stream ID of 1 or higher", 1, Integer.MAX_VALUE))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//User ID is required
|
||||||
|
if(!validateTextField(mUserID, "Invalid User ID", "Please specify a user ID/name."))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Password is required
|
||||||
|
if(!validateTextField(mPassword, "Invalid Password", "Please specify a password."))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an Integer from the port text control or returns 0 if the control doesn't contain an integer value.
|
||||||
|
*/
|
||||||
|
private int getPort()
|
||||||
|
{
|
||||||
|
String port = mPort.getText();
|
||||||
|
|
||||||
|
if(port != null && !port.isEmpty())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Integer.parseInt(port);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
//Do nothing, we couldn't parse the number value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an Integer from the port text control or returns 0 if the control doesn't contain an integer value.
|
||||||
|
*/
|
||||||
|
private int getStreamID()
|
||||||
|
{
|
||||||
|
String streamID = mStreamID.getText();
|
||||||
|
|
||||||
|
if(streamID != null && !streamID.isEmpty())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Integer.parseInt(streamID);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
//Do nothing, we couldn't parse the number value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* 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/>
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
|
||||||
|
|
||||||
public class XMLMetadata extends UltravoxMessage
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Client request to server to send cacheable XML metadata
|
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
|
||||||
XMLMetadata()
|
|
||||||
{
|
|
||||||
super(UltravoxMessageType.XML_METADATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Server response to client request
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*
|
|
||||||
* @param data bytes received from the server
|
|
||||||
*/
|
|
||||||
XMLMetadata(byte[] data)
|
|
||||||
{
|
|
||||||
super(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * Adds custom metadata (TXXX) tagged value to the metadata set. The metadata value is identified by the
|
|
||||||
// * id parameter.
|
|
||||||
// *
|
|
||||||
// * @param id tag to identify the type of metadata
|
|
||||||
// * @param value for the id tag
|
|
||||||
// */
|
|
||||||
// public void addCustomMetadata(String id, String value)
|
|
||||||
// {
|
|
||||||
// StringBuilder sb = new StringBuilder();
|
|
||||||
// sb.append("<").append(UltravoxMetadata.CUSTOM_TEXT_FIELD.getXMLTag());
|
|
||||||
// sb.append(" id=\"").append(id).append("\">");
|
|
||||||
// sb.append(value);
|
|
||||||
// sb.append("</").append(UltravoxMetadata.CUSTOM_TEXT_FIELD.getXMLTag()).append(">");
|
|
||||||
//
|
|
||||||
// mMetadata.add(sb.toString());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Adds the binary object data to the metadata set.
|
|
||||||
// * @param id tag to identify the binary object
|
|
||||||
// * @param mime type
|
|
||||||
// * @param filename associated with the data
|
|
||||||
// * @param base64Data base-64 encoded binary data to embed in the metadata tag
|
|
||||||
// */
|
|
||||||
// public void addBinaryObject(String id, String mime, String filename, String base64Data)
|
|
||||||
// {
|
|
||||||
// StringBuilder sb = new StringBuilder();
|
|
||||||
// sb.append("<").append(UltravoxMetadata.GENERAL_BINARY_OBJECT.getXMLTag());
|
|
||||||
// sb.append(" mime=\"").append(mime).append("\"");
|
|
||||||
// sb.append(" filename=\"").append(filename).append("\"");
|
|
||||||
// sb.append(" id=\"").append(id).append("\"");
|
|
||||||
// sb.append(">");
|
|
||||||
// sb.append(base64Data);
|
|
||||||
// sb.append("</").append(UltravoxMetadata.GENERAL_BINARY_OBJECT.getXMLTag()).append(">");
|
|
||||||
//
|
|
||||||
// mMetadata.add(sb.toString());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Adds a recording time (TDRC) tagged metadata timestamp
|
|
||||||
// *
|
|
||||||
// * @param timestamp for the recording time
|
|
||||||
// */
|
|
||||||
// public void addRecordingTime(long timestamp)
|
|
||||||
// {
|
|
||||||
// Calendar calendar = new GregorianCalendar();
|
|
||||||
// calendar.setTimeInMillis(timestamp);
|
|
||||||
//
|
|
||||||
// StringBuilder sb = new StringBuilder();
|
|
||||||
// sb.append("<TDRC>\n");
|
|
||||||
// sb.append("<YEAR>").append(calendar.get(Calendar.YEAR)).append("</YEAR>\n");
|
|
||||||
// sb.append("<MONTH>").append(calendar.get(Calendar.MONTH)).append("</MONTH>\n");
|
|
||||||
// sb.append("<DAY>").append(calendar.get(Calendar.DAY_OF_MONTH)).append("</DAY>\n");
|
|
||||||
// sb.append("<HOUR>").append(calendar.get(Calendar.HOUR_OF_DAY)).append("</HOUR>\n");
|
|
||||||
// sb.append("<MINUTE>").append(calendar.get(Calendar.MINUTE)).append("</MINUTE>\n");
|
|
||||||
// sb.append("<ZONE>").append(calendar.get(Calendar.ZONE_OFFSET)).append("</ZONE>\n");
|
|
||||||
// sb.append("</TDRC>\n");
|
|
||||||
//
|
|
||||||
// mMetadata.add(sb.toString());
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
import util.XTEA;
|
import util.XTEA;
|
||||||
|
|
||||||
@@ -24,12 +24,12 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
public class AuthenticateBroadcast extends UltravoxMessage
|
public class AuthenticateBroadcast extends UltravoxMessage
|
||||||
{
|
{
|
||||||
|
public static final String STREAM_ID_ERROR = "Stream ID Error";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client request to server to send authentication credentials
|
* Client request to server to send authentication credentials
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
AuthenticateBroadcast()
|
public AuthenticateBroadcast()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.AUTHENTICATE_BROADCAST);
|
super(UltravoxMessageType.AUTHENTICATE_BROADCAST);
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ public class AuthenticateBroadcast extends UltravoxMessage
|
|||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
sb.append(SHOUTCAST_VERSION);
|
sb.append(ULTRAVOX_VERSION);
|
||||||
sb.append(":");
|
sb.append(":");
|
||||||
sb.append(streamID);
|
sb.append(streamID);
|
||||||
sb.append(":");
|
sb.append(":");
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* 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/>
|
||||||
|
*
|
||||||
|
******************************************************************************/
|
||||||
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
|
public class CacheableXMLMetadata extends UltravoxMessage
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Client request to server to send cacheable XML metadata
|
||||||
|
*/
|
||||||
|
public CacheableXMLMetadata()
|
||||||
|
{
|
||||||
|
super(UltravoxMessageType.CACHEABLE_XML_METADATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server response to client request
|
||||||
|
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||||
|
*
|
||||||
|
* @param data bytes received from the server
|
||||||
|
*/
|
||||||
|
CacheableXMLMetadata(byte[] data)
|
||||||
|
{
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class ConfigureIcyGenre extends UltravoxMessage
|
public class ConfigureIcyGenre extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to configure the ICY-GENRE metadata
|
* Client request to server to configure the ICY-GENRE metadata
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
ConfigureIcyGenre()
|
public ConfigureIcyGenre()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.CONFIGURE_ICY_GENRE);
|
super(UltravoxMessageType.CONFIGURE_ICY_GENRE);
|
||||||
}
|
}
|
||||||
@@ -16,20 +16,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
import util.XTEA;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class ConfigureIcyName extends UltravoxMessage
|
public class ConfigureIcyName extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to configure the ICY-NAME metadata
|
* Client request to server to configure the ICY-NAME metadata
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
ConfigureIcyName()
|
public ConfigureIcyName()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.CONFIGURE_ICY_NAME);
|
super(UltravoxMessageType.CONFIGURE_ICY_NAME);
|
||||||
}
|
}
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class ConfigureIcyPublic extends UltravoxMessage
|
public class ConfigureIcyPublic extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to configure the ICY-PUBLIC metadata
|
* Client request to server to configure the ICY-PUBLIC metadata
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
ConfigureIcyPublic()
|
public ConfigureIcyPublic()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.CONFIGURE_ICY_PUBLIC);
|
super(UltravoxMessageType.CONFIGURE_ICY_PUBLIC);
|
||||||
}
|
}
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class ConfigureIcyURL extends UltravoxMessage
|
public class ConfigureIcyURL extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to configure the ICY-URL metadata
|
* Client request to server to configure the ICY-URL metadata
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
ConfigureIcyURL()
|
public ConfigureIcyURL()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.CONFIGURE_ICY_URL);
|
super(UltravoxMessageType.CONFIGURE_ICY_URL);
|
||||||
}
|
}
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class FlushCachedMetadata extends UltravoxMessage
|
public class FlushCachedMetadata extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to flush any currently cached stream metadata
|
* Client request to server to flush any currently cached stream metadata
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
FlushCachedMetadata()
|
public FlushCachedMetadata()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.FLUSH_CACHED_METADATA);
|
super(UltravoxMessageType.FLUSH_CACHED_METADATA);
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class MP3Audio extends UltravoxMessage
|
public class MP3Audio extends UltravoxMessage
|
||||||
{
|
{
|
||||||
@@ -25,7 +25,7 @@ public class MP3Audio extends UltravoxMessage
|
|||||||
*
|
*
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||||
*/
|
*/
|
||||||
MP3Audio()
|
public MP3Audio()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.MP3_DATA);
|
super(UltravoxMessageType.MP3_DATA);
|
||||||
}
|
}
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class NegotiateBufferSize extends UltravoxMessage
|
public class NegotiateBufferSize extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to negotiate the server's buffer size
|
* Client request to server to negotiate the server's buffer size
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
NegotiateBufferSize()
|
public NegotiateBufferSize()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.NEGOTIATE_BUFFER_SIZE);
|
super(UltravoxMessageType.NEGOTIATE_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
@@ -55,10 +53,10 @@ public class NegotiateBufferSize extends UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Negotiated buffer size from a server response message.
|
* Negotiated buffer size from a server response ultravox.
|
||||||
*
|
*
|
||||||
* @return server negotiated buffer size, or zero if this is not a server response message, or this is an error
|
* @return server negotiated buffer size, or zero if this is not a server response ultravox, or this is an error
|
||||||
* server response message, or the server response message buffer size cannot be determined from the message payload.
|
* server response ultravox, or the server response ultravox buffer size cannot be determined from the ultravox payload.
|
||||||
*/
|
*/
|
||||||
public int getBufferSize()
|
public int getBufferSize()
|
||||||
{
|
{
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class NegotiateMaxPayloadSize extends UltravoxMessage
|
public class NegotiateMaxPayloadSize extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to negotiate maximum payload size
|
* Client request to server to negotiate maximum payload size
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
NegotiateMaxPayloadSize()
|
public NegotiateMaxPayloadSize()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.NEGOTIATE_MAX_PAYLOAD_SIZE);
|
super(UltravoxMessageType.NEGOTIATE_MAX_PAYLOAD_SIZE);
|
||||||
}
|
}
|
||||||
@@ -42,7 +40,7 @@ public class NegotiateMaxPayloadSize extends UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the maximum payload size.
|
* Sets the maximum payload size in bytes
|
||||||
*
|
*
|
||||||
* @param desiredMaximum payload size (no larger than 16377)
|
* @param desiredMaximum payload size (no larger than 16377)
|
||||||
* @param minimum acceptable payload size (no larger than 16377)
|
* @param minimum acceptable payload size (no larger than 16377)
|
||||||
@@ -59,11 +57,11 @@ public class NegotiateMaxPayloadSize extends UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Negotiated maximum payload size from a server response message.
|
* Negotiated maximum payload size from a server response ultravox.
|
||||||
*
|
*
|
||||||
* @return server negotiated maximum payload size, or zero if this is not a server response message, or this is an
|
* @return server negotiated maximum payload size, or zero if this is not a server response ultravox, or this is an
|
||||||
* error server response message, or the server response message maximum payload size cannot be determined from the
|
* error server response ultravox, or the server response ultravox maximum payload size cannot be determined from the
|
||||||
* message payload.
|
* ultravox payload.
|
||||||
*/
|
*/
|
||||||
public int getMaximumPayloadSize()
|
public int getMaximumPayloadSize()
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
|
public class PassThroughXMLMetadata extends UltravoxMessage
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Client request to server to send pass-through XML metadata
|
||||||
|
*/
|
||||||
|
public PassThroughXMLMetadata()
|
||||||
|
{
|
||||||
|
super(UltravoxMessageType.PASS_THROUGH_XML_METADATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server response to client request
|
||||||
|
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||||
|
*
|
||||||
|
* @param data bytes received from the server
|
||||||
|
*/
|
||||||
|
PassThroughXMLMetadata(byte[] data)
|
||||||
|
{
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,19 +16,17 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class RequestCipher extends UltravoxMessage
|
public class RequestCipher extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server for encryption key
|
* Client request to server for encryption key
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
RequestCipher()
|
public RequestCipher()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.REQUEST_CIPHER);
|
super(UltravoxMessageType.REQUEST_CIPHER);
|
||||||
setPayload(SHOUTCAST_VERSION);
|
setPayload(ULTRAVOX_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class SetupBroadcast extends UltravoxMessage
|
public class SetupBroadcast extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to specify the stream bit rate
|
* Client request to server to specify the stream bit rate
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
SetupBroadcast()
|
public SetupBroadcast()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.SETUP_BROADCAST);
|
super(UltravoxMessageType.SETUP_BROADCAST);
|
||||||
}
|
}
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class Standby extends UltravoxMessage
|
public class Standby extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to notify that configuration is complete, prepare for streaming
|
* Client request to server to notify that configuration is complete, prepare for streaming
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
Standby()
|
public Standby()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.STANDBY);
|
super(UltravoxMessageType.STANDBY);
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
import audio.broadcast.BroadcastFormat;
|
import audio.broadcast.BroadcastFormat;
|
||||||
|
|
||||||
@@ -24,10 +24,8 @@ public class StreamMimeType extends UltravoxMessage
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to set stream MIME type
|
* Client request to server to set stream MIME type
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
StreamMimeType()
|
public StreamMimeType()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.STREAM_MIME_TYPE);
|
super(UltravoxMessageType.STREAM_MIME_TYPE);
|
||||||
}
|
}
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class TerminateBroadcast extends UltravoxMessage
|
public class TerminateBroadcast extends UltravoxMessage
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Client request to server to terminate the broadcastAudio
|
* Client request to server to terminate the broadcastAudio
|
||||||
*
|
|
||||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
|
||||||
*/
|
*/
|
||||||
TerminateBroadcast()
|
public TerminateBroadcast()
|
||||||
{
|
{
|
||||||
super(UltravoxMessageType.TERMINATE_BROADCAST);
|
super(UltravoxMessageType.TERMINATE_BROADCAST);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
|
import org.apache.mina.core.buffer.IoBuffer;
|
||||||
|
import org.apache.mina.core.session.IoSession;
|
||||||
|
import org.apache.mina.filter.codec.ProtocolDecoderAdapter;
|
||||||
|
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class UltravoxDecoder extends ProtocolDecoderAdapter
|
||||||
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(UltravoxDecoder.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(IoSession ioSession, IoBuffer ioBuffer, ProtocolDecoderOutput out) throws Exception
|
||||||
|
{
|
||||||
|
byte[] message = new byte[ioBuffer.limit()];
|
||||||
|
|
||||||
|
ioBuffer.get(message);
|
||||||
|
|
||||||
|
UltravoxMessage ultravoxMessage = UltravoxMessageFactory.getMessage(message);
|
||||||
|
|
||||||
|
if(ultravoxMessage != null)
|
||||||
|
{
|
||||||
|
out.write(ultravoxMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.write(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
|
import org.apache.mina.core.buffer.IoBuffer;
|
||||||
|
import org.apache.mina.core.session.IoSession;
|
||||||
|
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
|
||||||
|
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||||
|
|
||||||
|
public class UltravoxEncoder extends ProtocolEncoderAdapter
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void encode(IoSession ioSession, Object message, ProtocolEncoderOutput out) throws Exception
|
||||||
|
{
|
||||||
|
if(message instanceof UltravoxMessage)
|
||||||
|
{
|
||||||
|
byte[] data = ((UltravoxMessage)message).getMessage();
|
||||||
|
|
||||||
|
IoBuffer buf = IoBuffer.allocate(data.length).setAutoExpand(false);
|
||||||
|
buf.put(data);
|
||||||
|
buf.flip();
|
||||||
|
out.write(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
import bits.BinaryMessage;
|
import bits.BinaryMessage;
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
public abstract class UltravoxMessage
|
public abstract class UltravoxMessage
|
||||||
{
|
{
|
||||||
public static final String SHOUTCAST_VERSION = "2.1";
|
public static final String ULTRAVOX_VERSION = "2.1";
|
||||||
public static final String VALID_RESPONSE_PREFIX = "ACK";
|
public static final String VALID_RESPONSE_PREFIX = "ACK";
|
||||||
public static final String VALID_RESPONSE_PAYLOAD_PREFIX = "ACK:";
|
public static final String VALID_RESPONSE_PAYLOAD_PREFIX = "ACK:";
|
||||||
public static final String ERROR_RESPONSE_PREFIX = "NAK:";
|
public static final String ERROR_RESPONSE_PREFIX = "NAK:";
|
||||||
@@ -40,7 +40,7 @@ public abstract class UltravoxMessage
|
|||||||
private BinaryMessage mMessage;
|
private BinaryMessage mMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a message from the byte array
|
* Constructs a ultravox from the byte array
|
||||||
*/
|
*/
|
||||||
public UltravoxMessage(byte[] data)
|
public UltravoxMessage(byte[] data)
|
||||||
{
|
{
|
||||||
@@ -56,9 +56,9 @@ public abstract class UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an empty message of the specified length
|
* Constructs an empty ultravox of the specified length
|
||||||
*
|
*
|
||||||
* @param ultravoxMessageType type of message
|
* @param ultravoxMessageType type of ultravox
|
||||||
*/
|
*/
|
||||||
public UltravoxMessage(UltravoxMessageType ultravoxMessageType)
|
public UltravoxMessage(UltravoxMessageType ultravoxMessageType)
|
||||||
{
|
{
|
||||||
@@ -71,7 +71,7 @@ public abstract class UltravoxMessage
|
|||||||
/**
|
/**
|
||||||
* Message byte array
|
* Message byte array
|
||||||
*
|
*
|
||||||
* @return message bytes
|
* @return ultravox bytes
|
||||||
*/
|
*/
|
||||||
public byte[] getMessage()
|
public byte[] getMessage()
|
||||||
{
|
{
|
||||||
@@ -80,11 +80,11 @@ public abstract class UltravoxMessage
|
|||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return mMessage.toString();
|
return getMessageType().name();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the message sync bits to the predefined sync pattern: 01011010 (0x5A)
|
* Sets the ultravox sync bits to the predefined sync pattern: 01011010 (0x5A)
|
||||||
*/
|
*/
|
||||||
private void setSync()
|
private void setSync()
|
||||||
{
|
{
|
||||||
@@ -92,7 +92,7 @@ public abstract class UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ultravox message class
|
* Ultravox ultravox class
|
||||||
*/
|
*/
|
||||||
public UltravoxMessageClass getMessageClass()
|
public UltravoxMessageClass getMessageClass()
|
||||||
{
|
{
|
||||||
@@ -100,7 +100,7 @@ public abstract class UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ultravox message type
|
* Ultravox ultravox type
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public UltravoxMessageType getMessageType()
|
public UltravoxMessageType getMessageType()
|
||||||
@@ -109,8 +109,8 @@ public abstract class UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the message bits according to the message class and type
|
* Sets the ultravox bits according to the ultravox class and type
|
||||||
* @param messageType of ultravox message
|
* @param messageType of ultravox ultravox
|
||||||
*/
|
*/
|
||||||
private void setMessageType(UltravoxMessageType messageType)
|
private void setMessageType(UltravoxMessageType messageType)
|
||||||
{
|
{
|
||||||
@@ -118,7 +118,7 @@ public abstract class UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this message has the required delivery flag set
|
* Indicates if this ultravox has the required delivery flag set
|
||||||
*/
|
*/
|
||||||
public boolean isRequiredDelivery()
|
public boolean isRequiredDelivery()
|
||||||
{
|
{
|
||||||
@@ -141,7 +141,7 @@ public abstract class UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send queue priority (0 - 7) for this message
|
* Send queue priority (0 - 7) for this ultravox
|
||||||
*/
|
*/
|
||||||
public int getSendQueuePriority()
|
public int getSendQueuePriority()
|
||||||
{
|
{
|
||||||
@@ -149,7 +149,7 @@ public abstract class UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets send queue priority for this message
|
* Sets send queue priority for this ultravox
|
||||||
* @param priority 0 - 7
|
* @param priority 0 - 7
|
||||||
*/
|
*/
|
||||||
public void setSendQueuePriority(int priority)
|
public void setSendQueuePriority(int priority)
|
||||||
@@ -186,7 +186,7 @@ public abstract class UltravoxMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the message payload, updates the message length field, and appends a trailing 0x00 byte after the payload.
|
* Sets the ultravox payload, updates the ultravox length field, and appends a trailing 0x00 byte after the payload.
|
||||||
*/
|
*/
|
||||||
public void setPayload(String payload)
|
public void setPayload(String payload)
|
||||||
{
|
{
|
||||||
@@ -211,6 +211,9 @@ public abstract class UltravoxMessage
|
|||||||
mMessage.setByte(payloadPointer, (byte)0x00);
|
mMessage.setByte(payloadPointer, (byte)0x00);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the response is a valid (ie non-error) response
|
||||||
|
*/
|
||||||
public boolean isValidResponse()
|
public boolean isValidResponse()
|
||||||
{
|
{
|
||||||
String payload = getPayload();
|
String payload = getPayload();
|
||||||
@@ -218,6 +221,9 @@ public abstract class UltravoxMessage
|
|||||||
return payload != null && payload.startsWith(VALID_RESPONSE_PREFIX);
|
return payload != null && payload.startsWith(VALID_RESPONSE_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the response message contains an error message.
|
||||||
|
*/
|
||||||
public boolean isErrorResponse()
|
public boolean isErrorResponse()
|
||||||
{
|
{
|
||||||
String payload = getPayload();
|
String payload = getPayload();
|
||||||
@@ -225,7 +231,10 @@ public abstract class UltravoxMessage
|
|||||||
return payload != null && payload.startsWith(ERROR_RESPONSE_PREFIX);
|
return payload != null && payload.startsWith(ERROR_RESPONSE_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getError()
|
/**
|
||||||
|
* Textual message available when the error response flag is set.
|
||||||
|
*/
|
||||||
|
public String getErrorMessage()
|
||||||
{
|
{
|
||||||
String payload = getPayload();
|
String payload = getPayload();
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public enum UltravoxMessageClass
|
public enum UltravoxMessageClass
|
||||||
{
|
{
|
||||||
@@ -16,12 +16,12 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public class UltravoxMessageFactory
|
public class UltravoxMessageFactory
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Creates an empty message of the specified message type for sending to the server
|
* Creates an empty ultravox of the specified ultravox type for sending to the server
|
||||||
*/
|
*/
|
||||||
public static UltravoxMessage getMessage(UltravoxMessageType type)
|
public static UltravoxMessage getMessage(UltravoxMessageType type)
|
||||||
{
|
{
|
||||||
@@ -29,6 +29,8 @@ public class UltravoxMessageFactory
|
|||||||
{
|
{
|
||||||
case AUTHENTICATE_BROADCAST:
|
case AUTHENTICATE_BROADCAST:
|
||||||
return new AuthenticateBroadcast();
|
return new AuthenticateBroadcast();
|
||||||
|
case CACHEABLE_XML_METADATA:
|
||||||
|
return new CacheableXMLMetadata();
|
||||||
case CONFIGURE_ICY_GENRE:
|
case CONFIGURE_ICY_GENRE:
|
||||||
return new ConfigureIcyGenre();
|
return new ConfigureIcyGenre();
|
||||||
case CONFIGURE_ICY_NAME:
|
case CONFIGURE_ICY_NAME:
|
||||||
@@ -45,6 +47,8 @@ public class UltravoxMessageFactory
|
|||||||
return new NegotiateBufferSize();
|
return new NegotiateBufferSize();
|
||||||
case NEGOTIATE_MAX_PAYLOAD_SIZE:
|
case NEGOTIATE_MAX_PAYLOAD_SIZE:
|
||||||
return new NegotiateMaxPayloadSize();
|
return new NegotiateMaxPayloadSize();
|
||||||
|
case PASS_THROUGH_XML_METADATA:
|
||||||
|
return new PassThroughXMLMetadata();
|
||||||
case REQUEST_CIPHER:
|
case REQUEST_CIPHER:
|
||||||
return new RequestCipher();
|
return new RequestCipher();
|
||||||
case SETUP_BROADCAST:
|
case SETUP_BROADCAST:
|
||||||
@@ -55,19 +59,17 @@ public class UltravoxMessageFactory
|
|||||||
return new StreamMimeType();
|
return new StreamMimeType();
|
||||||
case TERMINATE_BROADCAST:
|
case TERMINATE_BROADCAST:
|
||||||
return new TerminateBroadcast();
|
return new TerminateBroadcast();
|
||||||
case XML_METADATA:
|
|
||||||
return new XMLMetadata();
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an ultravox message from the server response bytes.
|
* Creates an ultravox ultravox from the server response bytes.
|
||||||
*
|
*
|
||||||
* @param bytes in network order (big-endian).
|
* @param bytes in network order (big-endian).
|
||||||
*
|
*
|
||||||
* @return constructed message or null if the message type cannot be determined or is unrecognized
|
* @return constructed ultravox or null if the ultravox type cannot be determined or is unrecognized
|
||||||
*/
|
*/
|
||||||
public static UltravoxMessage getMessage(byte[] bytes)
|
public static UltravoxMessage getMessage(byte[] bytes)
|
||||||
{
|
{
|
||||||
@@ -77,6 +79,8 @@ public class UltravoxMessageFactory
|
|||||||
{
|
{
|
||||||
case AUTHENTICATE_BROADCAST:
|
case AUTHENTICATE_BROADCAST:
|
||||||
return new AuthenticateBroadcast(bytes);
|
return new AuthenticateBroadcast(bytes);
|
||||||
|
case CACHEABLE_XML_METADATA:
|
||||||
|
return new CacheableXMLMetadata(bytes);
|
||||||
case CONFIGURE_ICY_GENRE:
|
case CONFIGURE_ICY_GENRE:
|
||||||
return new ConfigureIcyGenre(bytes);
|
return new ConfigureIcyGenre(bytes);
|
||||||
case CONFIGURE_ICY_NAME:
|
case CONFIGURE_ICY_NAME:
|
||||||
@@ -93,6 +97,8 @@ public class UltravoxMessageFactory
|
|||||||
return new NegotiateBufferSize(bytes);
|
return new NegotiateBufferSize(bytes);
|
||||||
case NEGOTIATE_MAX_PAYLOAD_SIZE:
|
case NEGOTIATE_MAX_PAYLOAD_SIZE:
|
||||||
return new NegotiateMaxPayloadSize(bytes);
|
return new NegotiateMaxPayloadSize(bytes);
|
||||||
|
case PASS_THROUGH_XML_METADATA:
|
||||||
|
return new PassThroughXMLMetadata(bytes);
|
||||||
case REQUEST_CIPHER:
|
case REQUEST_CIPHER:
|
||||||
return new RequestCipher(bytes);
|
return new RequestCipher(bytes);
|
||||||
case SETUP_BROADCAST:
|
case SETUP_BROADCAST:
|
||||||
@@ -103,8 +109,6 @@ public class UltravoxMessageFactory
|
|||||||
return new StreamMimeType(bytes);
|
return new StreamMimeType(bytes);
|
||||||
case TERMINATE_BROADCAST:
|
case TERMINATE_BROADCAST:
|
||||||
return new TerminateBroadcast(bytes);
|
return new TerminateBroadcast(bytes);
|
||||||
case XML_METADATA:
|
|
||||||
return new XMLMetadata(bytes);
|
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
@@ -112,10 +116,10 @@ public class UltravoxMessageFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifies the message type from the server response byte array
|
* Identifies the ultravox type from the server response byte array
|
||||||
*
|
*
|
||||||
* @param data in little-endian format
|
* @param data in little-endian format
|
||||||
* @return message type or UNKNOWN if the message type cannot be determined.
|
* @return ultravox type or UNKNOWN if the ultravox type cannot be determined.
|
||||||
*/
|
*/
|
||||||
private static UltravoxMessageType getMessageType(byte[] data)
|
private static UltravoxMessageType getMessageType(byte[] data)
|
||||||
{
|
{
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public enum UltravoxMessageType
|
public enum UltravoxMessageType
|
||||||
{
|
{
|
||||||
@@ -33,7 +33,8 @@ public enum UltravoxMessageType
|
|||||||
CONFIGURE_ICY_GENRE(0x1101),
|
CONFIGURE_ICY_GENRE(0x1101),
|
||||||
CONFIGURE_ICY_URL(0x1102),
|
CONFIGURE_ICY_URL(0x1102),
|
||||||
CONFIGURE_ICY_PUBLIC(0x1103),
|
CONFIGURE_ICY_PUBLIC(0x1103),
|
||||||
XML_METADATA(0x3902),
|
CACHEABLE_XML_METADATA(0x3902),
|
||||||
|
PASS_THROUGH_XML_METADATA(0x5902),
|
||||||
MP3_DATA(0x7000),
|
MP3_DATA(0x7000),
|
||||||
UNKNOWN(0x0000);
|
UNKNOWN(0x0000);
|
||||||
|
|
||||||
@@ -80,7 +81,9 @@ public enum UltravoxMessageType
|
|||||||
case 0x1103:
|
case 0x1103:
|
||||||
return CONFIGURE_ICY_PUBLIC;
|
return CONFIGURE_ICY_PUBLIC;
|
||||||
case 0x3902:
|
case 0x3902:
|
||||||
return XML_METADATA;
|
return CACHEABLE_XML_METADATA;
|
||||||
|
case 0x5902:
|
||||||
|
return PASS_THROUGH_XML_METADATA;
|
||||||
case 0x7000:
|
case 0x7000:
|
||||||
return MP3_DATA;
|
return MP3_DATA;
|
||||||
default:
|
default:
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
*
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package audio.broadcast.shoutcast.v2.message;
|
package audio.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
public enum UltravoxMetadata
|
public enum UltravoxMetadata
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* 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.broadcast.shoutcast.v2.ultravox;
|
||||||
|
|
||||||
|
import org.apache.mina.core.session.IoSession;
|
||||||
|
import org.apache.mina.filter.codec.ProtocolCodecFactory;
|
||||||
|
import org.apache.mina.filter.codec.ProtocolDecoder;
|
||||||
|
import org.apache.mina.filter.codec.ProtocolEncoder;
|
||||||
|
|
||||||
|
public class UltravoxProtocolFactory implements ProtocolCodecFactory
|
||||||
|
{
|
||||||
|
private ProtocolEncoder mEncoder;
|
||||||
|
private ProtocolDecoder mDecoder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProtocolEncoder getEncoder(IoSession ioSession) throws Exception
|
||||||
|
{
|
||||||
|
if(mEncoder == null)
|
||||||
|
{
|
||||||
|
mEncoder = new UltravoxEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProtocolDecoder getDecoder(IoSession ioSession) throws Exception
|
||||||
|
{
|
||||||
|
if(mDecoder == null)
|
||||||
|
{
|
||||||
|
mDecoder = new UltravoxDecoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mDecoder;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user