#30 WIP extracted the IcecastMetadataUpdater and merged some of the icecast configuration method into a new superclass.

This commit is contained in:
Denny
2016-12-24 05:41:04 -05:00
parent 5e4cbf2a4e
commit c216f56c4f
7 changed files with 509 additions and 677 deletions

View File

@@ -0,0 +1,292 @@
/*******************************************************************************
* 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.icecast;
import audio.broadcast.BroadcastConfiguration;
import audio.broadcast.BroadcastFormat;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlTransient;
import java.util.Base64;
public abstract class IcecastConfiguration extends BroadcastConfiguration
{
protected String mUserName = "source";
private String mMountPoint = "/stream";
private String mDescription;
private String mGenre;
private boolean mPublic;
private int mBitRate = 16;
private int mChannels = 1;
private int mSampleRate = 8000;
private String mURL;
public IcecastConfiguration(BroadcastFormat format)
{
super(format);
}
public IcecastConfiguration()
{
}
/**
* Base64 encoded version of the username and password with prepended 'Basic ' tag.
*/
@XmlTransient
public String getBase64EncodedCredentials()
{
StringBuilder sb = new StringBuilder();
if(hasUserName())
{
sb.append(getUserName()).append(":").append(getPassword());
}
else
{
sb.append(getPassword());
}
String base64 = Base64.getEncoder().encodeToString(sb.toString().getBytes());
return "Basic " + base64;
}
@XmlAttribute( name="user_name" )
public String getUserName()
{
return mUserName;
}
public void setUserName(String userName)
{
mUserName = userName;
}
public boolean hasUserName()
{
return mUserName != null;
}
/**
* Mount point or path to the stream
* @return mount point
*/
@XmlAttribute( name="mount_point" )
public String getMountPoint()
{
return mMountPoint;
}
/**
* Sets the mount point (path) for the stream
* @param mountPoint
*/
public void setMountPoint(String mountPoint)
{
if(mountPoint != null && !mountPoint.isEmpty())
{
if(mountPoint.startsWith("/"))
{
mMountPoint = mountPoint;
}
else
{
mMountPoint = "/" + mountPoint;
}
}
}
public boolean hasMountPoint()
{
return mMountPoint != null;
}
@XmlAttribute( name="description" )
public String getDescription()
{
return mDescription;
}
public void setDescription(String description)
{
mDescription = description;
}
public boolean hasDescription()
{
return mDescription != null;
}
/**
* Stream genre
*/
@XmlAttribute( name="genre" )
public String getGenre()
{
return mGenre;
}
/**
* Stream genre
* @param genre
*/
public void setGenre(String genre)
{
mGenre = genre;
}
public boolean hasGenre()
{
return mGenre != null;
}
/**
* Public visibility of the broadcastAudio
*/
@XmlAttribute( name="public" )
public boolean isPublic()
{
return mPublic;
}
/**
* Sets public visibility of the broadcastAudio
* @param isPublic indicates if the broadcastAudio should be visible to the public
*/
public void setPublic(boolean isPublic)
{
mPublic = isPublic;
}
/**
* Number of audio channels in the broadcastAudio
*/
@XmlAttribute( name="channels" )
public int getChannels()
{
return mChannels;
}
/**
* Sets the number of audio channels in the broadcastAudio
*/
public void setChannels(int channels)
{
mChannels = channels;
}
public boolean hasChannels()
{
return mChannels > 0;
}
/**
* Bit rate in bits per second
*/
@XmlAttribute( name="bitrate" )
public int getBitRate()
{
return mBitRate;
}
/**
* Bit rate
* @param bitRate in samples per second
*/
public void setBitRate(int bitRate)
{
mBitRate = bitRate;
}
public boolean hasBitRate()
{
return mBitRate > 0;
}
@XmlAttribute( name="sample_rate" )
public int getSampleRate()
{
return mSampleRate;
}
public void setSampleRate(int sampleRate)
{
mSampleRate = sampleRate;
}
public boolean hasSampleRate()
{
return mSampleRate > 0;
}
/**
* URL associated with the broadcastAudio where users can find additional details.
*/
@XmlAttribute( name="url" )
public String getURL()
{
return mURL;
}
/**
* URL associated with the broadcastAudio where users can find additional details.
* @param url
*/
public void setURL(String url)
{
mURL = url;
}
public boolean hasURL()
{
return mURL != null;
}
@Override
public boolean isValid()
{
if(!super.isValid())
{
return false;
}
else if(getUserName() == null)
{
return false;
}
else if(getMountPoint() == null)
{
return false;
}
else if(getChannels() != 1)
{
return false;
}
else if(getSampleRate() <= 0)
{
return false;
}
else if(getBitRate() <= 0)
{
return false;
}
return true;
}
}

View File

@@ -24,18 +24,8 @@ import audio.broadcast.BroadcastServerType;
import java.util.Base64;
public class IcecastHTTPConfiguration extends BroadcastConfiguration
public class IcecastHTTPConfiguration extends IcecastConfiguration
{
private String mUserName = "source";
private String mMountPoint = "stream";
private String mDescription;
private String mGenre;
private boolean mPublic;
private int mBitRate = 16; //kHz
private int mChannels = 1;
private int mSampleRate = 8000;
private String mURL;
public IcecastHTTPConfiguration()
{
//no-arg JAXB constructor
@@ -84,234 +74,5 @@ public class IcecastHTTPConfiguration extends BroadcastConfiguration
return BroadcastServerType.ICECAST_HTTP;
}
/**
* Base64 encoded version of the username and password with prepended 'Basic ' tag.
*/
public String getEncodedCredentials()
{
StringBuilder sb = new StringBuilder();
if(hasUserName())
{
sb.append(getUserName()).append(":").append(getPassword());
}
else
{
sb.append(getPassword());
}
String base64 = Base64.getEncoder().encodeToString(sb.toString().getBytes());
return "Basic " + base64;
}
/**
* User name. Default is 'source'.
*/
public String getUserName()
{
return mUserName;
}
/**
* Sets the user name.
*/
public void setUserName(String userName)
{
mUserName = userName;
}
/**
* Indicates if this configuration has a user name
*/
public boolean hasUserName()
{
return mUserName != null;
}
/**
* Mount point or path to the stream
* @return mount point
*/
public String getMountPoint()
{
return mMountPoint;
}
/**
* Sets the mount point (path) for the stream
* @param mountPoint
*/
public void setMountPoint(String mountPoint)
{
mMountPoint = mountPoint;
}
/**
* Indicates if this configuration has a mount point
*/
public boolean hasMountPoint()
{
return mMountPoint != null;
}
/**
* Stream description
*/
public String getDescription()
{
return mDescription;
}
/**
* Sets the stream description
*/
public void setDescription(String description)
{
mDescription = description;
}
/**
* Indicates if this configuration contains a stream description
*/
public boolean hasDescription()
{
return mDescription != null;
}
/**
* Stream genre
*/
public String getGenre()
{
return mGenre;
}
/**
* Sets the stream genre
*/
public void setGenre(String genre)
{
mGenre = genre;
}
/**
* Indicates if this configuration contains a genre
*/
public boolean hasGenre()
{
return mGenre != null;
}
/**
* Public visibility of the broadcastAudio
*/
public boolean isPublic()
{
return mPublic;
}
/**
* Sets public visibility of the broadcastAudio
* @param isPublic indicates if the broadcastAudio should be visible to the public
*/
public void setPublic(boolean isPublic)
{
mPublic = isPublic;
}
/**
* Number of audio channels in the broadcastAudio
*/
public int getChannels()
{
return mChannels;
}
/**
* Sets the number of audio channels in the broadcastAudio
*/
public void setChannels(int channels)
{
mChannels = channels;
}
public boolean hasChannels()
{
return mChannels > 0;
}
/**
* Bit rate in bits per second
*/
public int getBitRate()
{
return mBitRate;
}
/**
* Bit rate
* @param bitRate in samples per second
*/
public void setBitRate(int bitRate)
{
mBitRate = bitRate;
}
/**
* Indicates if this configuration has a bit rate value
*/
public boolean hasBitRate()
{
return mBitRate > 0;
}
/**
* Sample rate of the input PCM audio samples. Default is 8 kHz
*/
public int getSampleRate()
{
return mSampleRate;
}
/**
* Sets the input PCM audio sample rate
*/
public void setSampleRate(int sampleRate)
{
mSampleRate = sampleRate;
}
/**
* Indicates if this configuration contains an audio sample rate
*/
public boolean hasSampleRate()
{
return mSampleRate > 0;
}
/**
* URL associated with the broadcastAudio where users can find additional details.
*/
public String getURL()
{
return mURL;
}
/**
* URL associated with the broadcastAudio where users can find additional details.
*/
public void setURL(String url)
{
mURL = url;
}
/**
* Indicates if this configuration contains a stream URL
*/
public boolean hasURL()
{
return mURL != null;
}
}

View File

@@ -98,7 +98,7 @@ public class IcecastHTTPConfigurationEditor extends BroadcastConfigurationEditor
add(new JLabel("Mount:"));
mMountPoint = new JTextField();
mMountPoint.getDocument().addDocumentListener(this);
mMountPoint.setToolTipText("Mount point (e.g. \\12345)");
mMountPoint.setToolTipText("Mount point (e.g. /12345)");
add(mMountPoint);
add(new JLabel("User Name:"));

View File

@@ -0,0 +1,209 @@
/*******************************************************************************
* 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.icecast;
import controller.ThreadPoolManager;
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.http.HttpClientCodec;
import org.apache.mina.http.HttpRequestImpl;
import org.apache.mina.http.api.HttpMethod;
import org.apache.mina.http.api.HttpRequest;
import org.apache.mina.http.api.HttpVersion;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import properties.SystemProperties;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class IcecastMetadataUpdater
{
private final static Logger mLog = LoggerFactory.getLogger(IcecastMetadataUpdater.class);
private final static String UTF8 = "UTF-8";
private ThreadPoolManager mThreadPoolManager;
private IcecastConfiguration mIcecastConfiguration;
private NioSocketConnector mSocketConnector;
private AtomicBoolean mUpdating = new AtomicBoolean();
private Queue<String> mMetadataQueue = new LinkedTransferQueue<>();
private Map<String, String> mHTTPHeaders;
private boolean mStackTraceLoggingSuppressed;
/**
* Icecast song metadata updater. Each metadata update is processed in the order received and the dispatch of
* the HTTP update request is processed in a separate thread (runnable) to avoid delaying streaming of this
* broadcaster. When multiple metadata updates are received prior to completion of the current ongoing update
* sequence, those updates will be queued and processed in the order received.
*/
public IcecastMetadataUpdater(ThreadPoolManager threadPoolManager, IcecastConfiguration icecastConfiguration)
{
mThreadPoolManager = threadPoolManager;
mIcecastConfiguration = icecastConfiguration;
}
/**
* Initializes the network socket and HTTP headers map
*/
private void init()
{
if (mSocketConnector == null)
{
mSocketConnector = new NioSocketConnector();
// mSocketConnector.getFilterChain().addLast("logger", new LoggingFilter(IcecastMetadataUpdater.class));
mSocketConnector.getFilterChain().addLast("http_client_codec", new HttpClientCodec());
//Each metadata update session is single-use, so we'll shut it down upon success or failure
mSocketConnector.setHandler(new IoHandlerAdapter()
{
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
{
// mLog.debug("Session " + session.getId() + " metadata update complete - ERROR received");
session.closeNow();
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception
{
// mLog.debug("Session " + session.getId() + " metadata update complete - response received");
session.closeNow();
}
});
mHTTPHeaders = new HashMap<>();
mHTTPHeaders.put(IcecastHeader.USER_AGENT.getValue(), SystemProperties.getInstance().getApplicationName());
mHTTPHeaders.put(IcecastHeader.AUTHORIZATION.getValue(), mIcecastConfiguration.getBase64EncodedCredentials());
}
}
/**
* Sends a song metadata update to the remote server. Additional metadata updates received while the updater
* is in the process of sending an existing metadata update will be queued and processed in received order.
*
* Note: due to thread timing, there is a slight chance that a concurrent update request will not be processed
* and will remain in the update queue until the next metadata update is requested. However, this is a design
* trade-off to avoid having a scheduled runnable repeatedly processing the update queue.
*/
public void update(String metadata)
{
if (metadata != null)
{
mMetadataQueue.offer(metadata);
if (mUpdating.compareAndSet(false, true))
{
String metadataUpdate = mMetadataQueue.poll();
while (metadataUpdate != null)
{
//Ensure we're setup for network communications (only happens once)
init();
HttpRequest updateRequest = createUpdateRequest(metadataUpdate);
if (updateRequest != null)
{
mThreadPoolManager.scheduleOnce(new Runnable()
{
@Override
public void run()
{
try
{
ConnectFuture connectFuture = mSocketConnector
.connect(new InetSocketAddress(mIcecastConfiguration.getHost(),
mIcecastConfiguration.getPort()));
connectFuture.awaitUninterruptibly();
IoSession session = connectFuture.getSession();
if (session != null)
{
session.write(updateRequest);
}
}
catch (Exception e)
{
Throwable throwableCause = e.getCause();
if (throwableCause instanceof ConnectException)
{
//Do nothing, the server is unavailable
}
else
{
if (!mStackTraceLoggingSuppressed)
{
mLog.error("Error sending metadata update. Future errors will " +
"be suppressed", e);
mStackTraceLoggingSuppressed = true;
}
}
}
}
}, 0l, TimeUnit.SECONDS);
}
//Fetch next metadata update to send
metadataUpdate = mMetadataQueue.poll();
}
mUpdating.set(false);
}
}
}
/**
* Creates an HTTP GET request to update the metadata on the remote server
*/
private HttpRequest createUpdateRequest(String metadata)
{
try
{
String songEncoded = URLEncoder.encode(metadata, UTF8);
StringBuilder sb = new StringBuilder();
sb.append("mode=updinfo");
sb.append("&mount=").append(mIcecastConfiguration.getMountPoint());
sb.append("&charset=UTF%2d8");
sb.append("&song=").append(songEncoded);
return new HttpRequestImpl(HttpVersion.HTTP_1_1, HttpMethod.GET, "/admin/metadata",
sb.toString(), mHTTPHeaders);
}
catch (UnsupportedEncodingException e)
{
//This should never happen
mLog.error("UTF-8 encoding is not supported - can't update song metadata");
}
return null;
}
}

View File

@@ -31,11 +31,6 @@ 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.HttpRequestImpl;
import org.apache.mina.http.api.HttpMethod;
import org.apache.mina.http.api.HttpRequest;
import org.apache.mina.http.api.HttpVersion;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.asynchttpclient.DefaultAsyncHttpClient;
import org.slf4j.Logger;
@@ -43,22 +38,15 @@ import org.slf4j.LoggerFactory;
import properties.SystemProperties;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class IcecastTCPAudioBroadcaster extends AudioBroadcaster
{
private final static Logger mLog = LoggerFactory.getLogger( IcecastTCPAudioBroadcaster.class );
private final static String UTF8 = "UTF-8";
private final static String TERMINATOR = "\r\n";
private final static String SEPARATOR = ":";
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
@@ -171,7 +159,7 @@ public class IcecastTCPAudioBroadcaster extends AudioBroadcaster
if(mIcecastMetadataUpdater == null)
{
mIcecastMetadataUpdater = new IcecastMetadataUpdater();
mIcecastMetadataUpdater = new IcecastMetadataUpdater(getThreadPoolManager(), getConfiguration());
}
mIcecastMetadataUpdater.update(sb.toString());
@@ -358,7 +346,7 @@ public class IcecastTCPAudioBroadcaster extends AudioBroadcaster
sb.append("SOURCE ").append(getConfiguration().getMountPoint());
sb.append(" HTTP/1.0").append(TERMINATOR);
sb.append("Authorization: ").append(getConfiguration().getEncodedCredentials()).append(TERMINATOR);
sb.append("Authorization: ").append(getConfiguration().getBase64EncodedCredentials()).append(TERMINATOR);
sb.append(IcecastHeader.USER_AGENT.getValue()).append(SEPARATOR)
.append(SystemProperties.getInstance().getApplicationName()).append(TERMINATOR);
sb.append(IcecastHeader.CONTENT_TYPE.getValue()).append(SEPARATOR)
@@ -459,162 +447,4 @@ public class IcecastTCPAudioBroadcaster extends AudioBroadcaster
super.inputClosed(session);
}
}
public class IcecastMetadataUpdater
{
private NioSocketConnector mSocketConnector;
private AtomicBoolean mUpdating = new AtomicBoolean();
private Queue<String> mMetadataQueue = new LinkedTransferQueue<>();
private Map<String,String> mHTTPHeaders;
private boolean mStackTraceLoggingSuppressed;
/**
* Icecast song metadata updater. Each metadata update is processed in the order received and the dispatch of
* the HTTP update request is processed in a separate thread (runnable) to avoid delaying streaming of this
* broadcaster. When multiple metadata updates are received prior to completion of the current ongoing update
* sequence, those updates will be queued and processed in the order received.
*
*/
public IcecastMetadataUpdater()
{
}
/**
* Initializes the network socket and HTTP headers map
*/
private void init()
{
if(mSocketConnector == null)
{
mSocketConnector = new NioSocketConnector();
// mSocketConnector.getFilterChain().addLast("logger", new LoggingFilter(IcecastMetadataUpdater.class));
mSocketConnector.getFilterChain().addLast("http_client_codec", new HttpClientCodec());
//Each metadata update session is single-use, so we'll shut it down upon success or failure
mSocketConnector.setHandler(new IoHandlerAdapter()
{
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
{
// mLog.debug("Session " + session.getId() + " metadata update complete - ERROR received");
session.closeNow();
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception
{
// mLog.debug("Session " + session.getId() + " metadata update complete - response received");
session.closeNow();
}
});
mHTTPHeaders = new HashMap<>();
mHTTPHeaders.put(IcecastHeader.USER_AGENT.getValue(), SystemProperties.getInstance().getApplicationName());
mHTTPHeaders.put(IcecastHeader.AUTHORIZATION.getValue(), getConfiguration().getEncodedCredentials());
}
}
/**
* Sends a song metadata update to the remote server. Additional metadata updates received while the updater
* is in the process of sending an existing metadata update will be queued and processed in received order.
*
* Note: due to thread timing, there is a slight chance that a concurrent update request will not be processed
* and will remain in the update queue until the next metadata update is requested. However, this is a design
* trade-off to avoid having a scheduled runnable repeatedly processing the update queue.
*/
public void update(String metadata)
{
if(metadata != null)
{
mMetadataQueue.offer(metadata);
if(mUpdating.compareAndSet(false, true))
{
String metadataUpdate = mMetadataQueue.poll();
while(metadataUpdate != null)
{
//Ensure we're setup for network communications (only happens once)
init();
HttpRequest updateRequest = createUpdateRequest(metadataUpdate);
if(updateRequest != null)
{
getThreadPoolManager().scheduleOnce(new Runnable()
{
@Override
public void run()
{
try
{
ConnectFuture connectFuture = mSocketConnector
.connect(new InetSocketAddress(getConfiguration().getHost(),
getConfiguration().getPort()));
connectFuture.awaitUninterruptibly();
IoSession session = connectFuture.getSession();
if(session != null)
{
session.write(updateRequest);
}
}
catch(Exception e)
{
Throwable throwableCause = e.getCause();
if(throwableCause instanceof ConnectException)
{
//Do nothing, the server is unavailable
}
else
{
if(!mStackTraceLoggingSuppressed)
{
mLog.error("Error sending metadata update. Future errors will " +
"be suppressed", e);
mStackTraceLoggingSuppressed = true;
}
}
}
}
}, 0l, TimeUnit.SECONDS);
}
//Fetch next metadata update to send
metadataUpdate = mMetadataQueue.poll();
}
mUpdating.set(false);
}
}
}
/**
* Creates an HTTP GET request to update the metadata on the remote server
*/
private HttpRequest createUpdateRequest(String metadata)
{
try
{
String songEncoded = URLEncoder.encode(metadata, UTF8);
StringBuilder sb = new StringBuilder();
sb.append("mode=updinfo");
sb.append("&mount=").append(getConfiguration().getMountPoint());
sb.append("&charset=UTF%2d8");
sb.append("&song=").append(songEncoded);
return new HttpRequestImpl(HttpVersion.HTTP_1_1, HttpMethod.GET, "/admin/metadata",
sb.toString(), mHTTPHeaders);
}
catch(UnsupportedEncodingException e)
{
//This should never happen
mLog.error("UTF-8 encoding is not supported - can't update song metadata");
}
return null;
}
}
}

View File

@@ -25,40 +25,25 @@ import audio.broadcast.broadcastify.BroadcastifyConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
import java.util.Base64;
@XmlSeeAlso( {BroadcastifyConfiguration.class } )
public class IcecastTCPConfiguration extends BroadcastConfiguration
public class IcecastTCPConfiguration extends IcecastConfiguration
{
private final static Logger mLog = LoggerFactory.getLogger( IcecastTCPConfiguration.class );
private String mUserName;
private String mMountPoint;
private String mDescription;
private String mGenre;
private boolean mPublic;
private int mBitRate = 16;
private int mChannels = 1;
private int mSampleRate = 8000;
private String mURL;
public IcecastTCPConfiguration()
{
setUserName("source");
//No-arg constructor for JAXB
this(BroadcastFormat.MP3);
}
/**
* Icecast 2.3.x and 2.4.x compatible configuration
* @param format
* @param format of audio
*/
public IcecastTCPConfiguration(BroadcastFormat format)
{
super(format);
setUserName("source");
}
@Override
@@ -90,249 +75,4 @@ public class IcecastTCPConfiguration extends BroadcastConfiguration
{
return BroadcastServerType.ICECAST_TCP;
}
/**
* Base64 encoded version of the username and password with prepended 'Basic ' tag.
*/
@XmlTransient
public String getEncodedCredentials()
{
StringBuilder sb = new StringBuilder();
if(hasUserName())
{
sb.append(getUserName()).append(":").append(getPassword());
}
else
{
sb.append(getPassword());
}
String base64 = Base64.getEncoder().encodeToString(sb.toString().getBytes());
return "Basic " + base64;
}
@XmlAttribute( name="user_name" )
public String getUserName()
{
return mUserName;
}
public void setUserName(String userName)
{
mUserName = userName;
}
public boolean hasUserName()
{
return mUserName != null;
}
/**
* Mount point or path to the stream
* @return mount point
*/
@XmlAttribute( name="mount_point" )
public String getMountPoint()
{
return mMountPoint;
}
/**
* Sets the mount point (path) for the stream
* @param mountPoint
*/
public void setMountPoint(String mountPoint)
{
if(mountPoint != null && !mountPoint.isEmpty())
{
if(mountPoint.startsWith("/"))
{
mMountPoint = mountPoint;
}
else
{
mMountPoint = "/" + mountPoint;
}
}
}
public boolean hasMountPoint()
{
return mMountPoint != null;
}
@XmlAttribute( name="description" )
public String getDescription()
{
return mDescription;
}
public void setDescription(String description)
{
mDescription = description;
}
public boolean hasDescription()
{
return mDescription != null;
}
/**
* Stream genre
*/
@XmlAttribute( name="genre" )
public String getGenre()
{
return mGenre;
}
/**
* Stream genre
* @param genre
*/
public void setGenre(String genre)
{
mGenre = genre;
}
public boolean hasGenre()
{
return mGenre != null;
}
/**
* Public visibility of the broadcastAudio
*/
@XmlAttribute( name="public" )
public boolean isPublic()
{
return mPublic;
}
/**
* Sets public visibility of the broadcastAudio
* @param isPublic indicates if the broadcastAudio should be visible to the public
*/
public void setPublic(boolean isPublic)
{
mPublic = isPublic;
}
/**
* Number of audio channels in the broadcastAudio
*/
@XmlAttribute( name="channels" )
public int getChannels()
{
return mChannels;
}
/**
* Sets the number of audio channels in the broadcastAudio
*/
public void setChannels(int channels)
{
mChannels = channels;
}
public boolean hasChannels()
{
return mChannels > 0;
}
/**
* Bit rate in bits per second
*/
@XmlAttribute( name="bitrate" )
public int getBitRate()
{
return mBitRate;
}
/**
* Bit rate
* @param bitRate in samples per second
*/
public void setBitRate(int bitRate)
{
mBitRate = bitRate;
}
public boolean hasBitRate()
{
return mBitRate > 0;
}
@XmlAttribute( name="sample_rate" )
public int getSampleRate()
{
return mSampleRate;
}
public void setSampleRate(int sampleRate)
{
mSampleRate = sampleRate;
}
public boolean hasSampleRate()
{
return mSampleRate > 0;
}
/**
* URL associated with the broadcastAudio where users can find additional details.
*/
@XmlAttribute( name="url" )
public String getURL()
{
return mURL;
}
/**
* URL associated with the broadcastAudio where users can find additional details.
* @param url
*/
public void setURL(String url)
{
mURL = url;
}
public boolean hasURL()
{
return mURL != null;
}
@Override
public boolean isValid()
{
if(!super.isValid())
{
return false;
}
else if(mUserName == null)
{
return false;
}
else if(mMountPoint == null)
{
return false;
}
else if(mChannels != 1)
{
return false;
}
else if(mSampleRate <= 0)
{
return false;
}
else if(mBitRate <= 0)
{
return false;
}
return true;
}
}

View File

@@ -99,7 +99,7 @@ public class IcecastTCPConfigurationEditor extends BroadcastConfigurationEditor
add(new JLabel("Mount:"));
mMountPoint = new JTextField();
mMountPoint.getDocument().addDocumentListener(this);
mMountPoint.setToolTipText("Mount point (e.g. \\12345)");
mMountPoint.setToolTipText("Mount point (e.g. /12345)");
add(mMountPoint);
add(new JLabel("Password:"));