mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 01:27:34 -05:00
#30 WIP extracted the IcecastMetadataUpdater and merged some of the icecast configuration method into a new superclass.
This commit is contained in:
292
src/audio/broadcast/icecast/IcecastConfiguration.java
Normal file
292
src/audio/broadcast/icecast/IcecastConfiguration.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,18 +24,8 @@ import audio.broadcast.BroadcastServerType;
|
|||||||
|
|
||||||
import java.util.Base64;
|
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()
|
public IcecastHTTPConfiguration()
|
||||||
{
|
{
|
||||||
//no-arg JAXB constructor
|
//no-arg JAXB constructor
|
||||||
@@ -84,234 +74,5 @@ public class IcecastHTTPConfiguration extends BroadcastConfiguration
|
|||||||
return BroadcastServerType.ICECAST_HTTP;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class IcecastHTTPConfigurationEditor extends BroadcastConfigurationEditor
|
|||||||
add(new JLabel("Mount:"));
|
add(new JLabel("Mount:"));
|
||||||
mMountPoint = new JTextField();
|
mMountPoint = new JTextField();
|
||||||
mMountPoint.getDocument().addDocumentListener(this);
|
mMountPoint.getDocument().addDocumentListener(this);
|
||||||
mMountPoint.setToolTipText("Mount point (e.g. \\12345)");
|
mMountPoint.setToolTipText("Mount point (e.g. /12345)");
|
||||||
add(mMountPoint);
|
add(mMountPoint);
|
||||||
|
|
||||||
add(new JLabel("User Name:"));
|
add(new JLabel("User Name:"));
|
||||||
|
|||||||
209
src/audio/broadcast/icecast/IcecastMetadataUpdater.java
Normal file
209
src/audio/broadcast/icecast/IcecastMetadataUpdater.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,11 +31,6 @@ import org.apache.mina.core.session.IoSession;
|
|||||||
import org.apache.mina.filter.codec.ProtocolCodecFilter;
|
import org.apache.mina.filter.codec.ProtocolCodecFilter;
|
||||||
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
|
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
|
||||||
import org.apache.mina.filter.logging.LoggingFilter;
|
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.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||||
import org.asynchttpclient.DefaultAsyncHttpClient;
|
import org.asynchttpclient.DefaultAsyncHttpClient;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -43,22 +38,15 @@ import org.slf4j.LoggerFactory;
|
|||||||
import properties.SystemProperties;
|
import properties.SystemProperties;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.Charset;
|
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.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public class IcecastTCPAudioBroadcaster extends AudioBroadcaster
|
public class IcecastTCPAudioBroadcaster extends AudioBroadcaster
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( IcecastTCPAudioBroadcaster.class );
|
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 TERMINATOR = "\r\n";
|
||||||
private final static String SEPARATOR = ":";
|
private final static String SEPARATOR = ":";
|
||||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
|
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
|
||||||
@@ -171,7 +159,7 @@ public class IcecastTCPAudioBroadcaster extends AudioBroadcaster
|
|||||||
|
|
||||||
if(mIcecastMetadataUpdater == null)
|
if(mIcecastMetadataUpdater == null)
|
||||||
{
|
{
|
||||||
mIcecastMetadataUpdater = new IcecastMetadataUpdater();
|
mIcecastMetadataUpdater = new IcecastMetadataUpdater(getThreadPoolManager(), getConfiguration());
|
||||||
}
|
}
|
||||||
|
|
||||||
mIcecastMetadataUpdater.update(sb.toString());
|
mIcecastMetadataUpdater.update(sb.toString());
|
||||||
@@ -358,7 +346,7 @@ public class IcecastTCPAudioBroadcaster extends AudioBroadcaster
|
|||||||
sb.append("SOURCE ").append(getConfiguration().getMountPoint());
|
sb.append("SOURCE ").append(getConfiguration().getMountPoint());
|
||||||
sb.append(" HTTP/1.0").append(TERMINATOR);
|
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)
|
sb.append(IcecastHeader.USER_AGENT.getValue()).append(SEPARATOR)
|
||||||
.append(SystemProperties.getInstance().getApplicationName()).append(TERMINATOR);
|
.append(SystemProperties.getInstance().getApplicationName()).append(TERMINATOR);
|
||||||
sb.append(IcecastHeader.CONTENT_TYPE.getValue()).append(SEPARATOR)
|
sb.append(IcecastHeader.CONTENT_TYPE.getValue()).append(SEPARATOR)
|
||||||
@@ -459,162 +447,4 @@ public class IcecastTCPAudioBroadcaster extends AudioBroadcaster
|
|||||||
super.inputClosed(session);
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,40 +25,25 @@ import audio.broadcast.broadcastify.BroadcastifyConfiguration;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAttribute;
|
|
||||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||||
import javax.xml.bind.annotation.XmlTransient;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
@XmlSeeAlso( {BroadcastifyConfiguration.class } )
|
@XmlSeeAlso( {BroadcastifyConfiguration.class } )
|
||||||
public class IcecastTCPConfiguration extends BroadcastConfiguration
|
public class IcecastTCPConfiguration extends IcecastConfiguration
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( IcecastTCPConfiguration.class );
|
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()
|
public IcecastTCPConfiguration()
|
||||||
{
|
{
|
||||||
setUserName("source");
|
this(BroadcastFormat.MP3);
|
||||||
//No-arg constructor for JAXB
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icecast 2.3.x and 2.4.x compatible configuration
|
* Icecast 2.3.x and 2.4.x compatible configuration
|
||||||
* @param format
|
* @param format of audio
|
||||||
*/
|
*/
|
||||||
public IcecastTCPConfiguration(BroadcastFormat format)
|
public IcecastTCPConfiguration(BroadcastFormat format)
|
||||||
{
|
{
|
||||||
super(format);
|
super(format);
|
||||||
setUserName("source");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -90,249 +75,4 @@ public class IcecastTCPConfiguration extends BroadcastConfiguration
|
|||||||
{
|
{
|
||||||
return BroadcastServerType.ICECAST_TCP;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public class IcecastTCPConfigurationEditor extends BroadcastConfigurationEditor
|
|||||||
add(new JLabel("Mount:"));
|
add(new JLabel("Mount:"));
|
||||||
mMountPoint = new JTextField();
|
mMountPoint = new JTextField();
|
||||||
mMountPoint.getDocument().addDocumentListener(this);
|
mMountPoint.getDocument().addDocumentListener(this);
|
||||||
mMountPoint.setToolTipText("Mount point (e.g. \\12345)");
|
mMountPoint.setToolTipText("Mount point (e.g. /12345)");
|
||||||
add(mMountPoint);
|
add(mMountPoint);
|
||||||
|
|
||||||
add(new JLabel("Password:"));
|
add(new JLabel("Password:"));
|
||||||
|
|||||||
Reference in New Issue
Block a user