mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
#30 - WIP on the shoutcast v2 protocol
This commit is contained in:
1
.idea/dictionaries/denny.xml
generated
1
.idea/dictionaries/denny.xml
generated
@@ -4,6 +4,7 @@
|
||||
<w>bahroo</w>
|
||||
<w>esnid</w>
|
||||
<w>fleetsync</w>
|
||||
<w>icecast</w>
|
||||
<w>ident</w>
|
||||
<w>polyphase</w>
|
||||
<w>shoutcast</w>
|
||||
|
||||
41
.idea/libraries/imports.xml
generated
41
.idea/libraries/imports.xml
generated
@@ -25,8 +25,45 @@
|
||||
<root url="jar://$PROJECT_DIR$/imports/JTransforms-3.0.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/jmbe-interface-0.3.3.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/java-lame-3.98.4.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/commons-codec-1.9.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/commons-logging-1.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/httpclient-4.5.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/httpcore-4.4.5.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/httpasyncclient-4.1.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/imports/httpcore-nio-4.4.5.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
<JAVADOC>
|
||||
<root url="https://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/index.html" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/examples" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/fluent-hc/src/examples" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/fluent-hc/src/main/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/fluent-hc/src/test/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-cache/src/main/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-cache/src/main/java-deprecated" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-cache/src/test/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-osgi/src/main/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-osgi/src/test/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-win/src/examples" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-win/src/main/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient-win/src/test/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/examples" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/main/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/main/java-deprecated" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/test/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpmime/src/examples" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpmime/src/main/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpmime/src/main/java-deprecated" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpmime/src/test/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-asyncclient-4.1.2/httpasyncclient/src/examples" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-asyncclient-4.1.2/httpasyncclient/src/main/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-asyncclient-4.1.2/httpasyncclient/src/main/java-deprecated" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-asyncclient-4.1.2/httpasyncclient/src/test/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/examples" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/main/java" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/main/java-deprecated" />
|
||||
<root url="file://$USER_HOME$/Downloads/httpcomponents-client-4.5.2/httpclient/src/test/java" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
BIN
imports/commons-codec-1.9.jar
Normal file
BIN
imports/commons-codec-1.9.jar
Normal file
Binary file not shown.
BIN
imports/commons-logging-1.2.jar
Normal file
BIN
imports/commons-logging-1.2.jar
Normal file
Binary file not shown.
BIN
imports/httpclient-4.5.2.jar
Normal file
BIN
imports/httpclient-4.5.2.jar
Normal file
Binary file not shown.
BIN
imports/httpcore-4.4.5.jar
Normal file
BIN
imports/httpcore-4.4.5.jar
Normal file
Binary file not shown.
@@ -45,6 +45,16 @@ public enum BroadcastState
|
||||
*/
|
||||
INVALID_PASSWORD("Invalid Password", true),
|
||||
|
||||
/**
|
||||
* Remote server max sources has been exceeded
|
||||
*/
|
||||
MAX_SOURCES_EXCEEDED("Max Sources Exceeded", true),
|
||||
|
||||
/**
|
||||
* Specified mount point is already in use
|
||||
*/
|
||||
MOUNT_POINT_IN_USE("Mount Point In Use", true),
|
||||
|
||||
/**
|
||||
* Server is not known or reachable
|
||||
*/
|
||||
@@ -60,6 +70,11 @@ public enum BroadcastState
|
||||
*/
|
||||
READY("Ready", false),
|
||||
|
||||
/**
|
||||
* Error on the remote server
|
||||
*/
|
||||
REMOTE_SERVER_ERROR("Remote Server Error", true),
|
||||
|
||||
/**
|
||||
* Server host name or port is invalid
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
******************************************************************************/
|
||||
package audio.broadcast;
|
||||
|
||||
import audio.broadcast.icecast.IcecastConfiguration;
|
||||
import audio.broadcast.icecast.IcecastHandler;
|
||||
import audio.broadcast.shoutcast.v1.ShoutcastV1Configuration;
|
||||
import audio.broadcast.shoutcast.v1.ShoutcastV1Handler;
|
||||
import audio.broadcast.shoutcast.v2.ShoutcastV2Configuration;
|
||||
@@ -29,7 +31,8 @@ import controller.ThreadPoolManager;
|
||||
public class BroadcasterFactory
|
||||
{
|
||||
public static final int MP3_MONO_16_KHZ_BITRATE = 16;
|
||||
public static final boolean MP3_NO_VARIABLE_BITRATE = false;
|
||||
public static final boolean MP3_CONSTANT_BITRATE = false;
|
||||
public static final boolean MP3_VARIABLE_BITRATE = true;
|
||||
|
||||
/**
|
||||
* Creates an audio streaming broadcaster for the configuration
|
||||
@@ -49,7 +52,8 @@ public class BroadcasterFactory
|
||||
case BROADCASTIFY:
|
||||
break;
|
||||
case ICECAST:
|
||||
break;
|
||||
return new Broadcaster(threadPoolManager,
|
||||
new IcecastHandler((IcecastConfiguration)configuration, converter));
|
||||
case SHOUTCAST_V1:
|
||||
return new Broadcaster(threadPoolManager,
|
||||
new ShoutcastV1Handler((ShoutcastV1Configuration)configuration, converter));
|
||||
@@ -74,7 +78,7 @@ public class BroadcasterFactory
|
||||
switch(format)
|
||||
{
|
||||
case MP3:
|
||||
return new MP3AudioConverter(MP3_MONO_16_KHZ_BITRATE, MP3_NO_VARIABLE_BITRATE);
|
||||
return new MP3AudioConverter(MP3_MONO_16_KHZ_BITRATE, MP3_CONSTANT_BITRATE);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
181
src/audio/broadcast/HttpAsyncAudioStreamer.java
Normal file
181
src/audio/broadcast/HttpAsyncAudioStreamer.java
Normal file
@@ -0,0 +1,181 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import audio.AudioPacket;
|
||||
import audio.IAudioPacketListener;
|
||||
import audio.convert.IAudioConverter;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpVersion;
|
||||
import org.apache.http.entity.AbstractHttpEntity;
|
||||
import org.apache.http.entity.BasicHttpEntity;
|
||||
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
|
||||
import org.apache.http.nio.ContentEncoder;
|
||||
import org.apache.http.nio.IOControl;
|
||||
import org.apache.http.nio.entity.HttpAsyncContentProducer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sample.Listener;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class HttpAsyncAudioStreamer extends BasicHttpEntityEnclosingRequest
|
||||
implements IAudioPacketListener, Listener<AudioPacket>, HttpAsyncContentProducer
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( HttpAsyncAudioStreamer.class );
|
||||
|
||||
private LinkedBlockingQueue<AudioPacket> mAudioQueue;
|
||||
private IAudioConverter mIAudioConverter;
|
||||
private IOControl mIOControl;
|
||||
private AtomicBoolean mSuspended = new AtomicBoolean();
|
||||
|
||||
public HttpAsyncAudioStreamer(String method, String uri, IAudioConverter audioConverter, int queueSize)
|
||||
{
|
||||
super(method, uri, HttpVersion.HTTP_1_1);
|
||||
mIAudioConverter = audioConverter;
|
||||
mAudioQueue = new LinkedBlockingQueue<>(queueSize);
|
||||
|
||||
setEntity(new MyHttpEntity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues the audio packet for conversion and streaming. Audio packets are assumed to arrive in sequence.
|
||||
*/
|
||||
@Override
|
||||
public void receive(AudioPacket audioPacket)
|
||||
{
|
||||
//Insert the packet in the queue. If the queue is currently full, remove the head element and attempt
|
||||
//to reinsert the packet.
|
||||
if(!mAudioQueue.offer(audioPacket))
|
||||
{
|
||||
mLog.debug("Streamer - making room to add an audio packet");
|
||||
mAudioQueue.poll();
|
||||
mAudioQueue.offer(audioPacket);
|
||||
}
|
||||
|
||||
if(mIOControl != null && mSuspended.compareAndSet(true, false))
|
||||
{
|
||||
mLog.debug("Streamer - content available, resuming streaming");
|
||||
mIOControl.requestOutput();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<AudioPacket> getAudioPacketListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void produceContent(ContentEncoder contentEncoder, IOControl ioControl) throws IOException
|
||||
{
|
||||
//IOControl is thread safe, so we'll keep a reference to it so that we can do flow control
|
||||
if(mIOControl == null)
|
||||
{
|
||||
mLog.debug("IOControl class is: " + ioControl.getClass());
|
||||
mIOControl = ioControl;
|
||||
}
|
||||
|
||||
AudioPacket packet = mAudioQueue.poll();
|
||||
|
||||
List<AudioPacket> packets = new ArrayList<>();
|
||||
packets.add(packet);
|
||||
byte[] converted = mIAudioConverter.convert(packets);
|
||||
mLog.debug("Produce Content - writing converted audio: " + converted.length + " to:" + contentEncoder.getClass());
|
||||
int wrote = contentEncoder.write(ByteBuffer.wrap(converted));
|
||||
|
||||
mLog.debug("We wrote [" + wrote + "] bytes to the content encoder");
|
||||
|
||||
if(mAudioQueue.isEmpty() && mSuspended.compareAndSet(false, true))
|
||||
{
|
||||
mLog.debug("Produce Content - suspending output");
|
||||
mIOControl.suspendOutput();
|
||||
}
|
||||
|
||||
// List<AudioPacket> packets = new ArrayList<>();
|
||||
// mAudioQueue.drainTo(packets);
|
||||
//
|
||||
// if(packets.isEmpty() && mSuspended.compareAndSet(false, true))
|
||||
// {
|
||||
// mLog.debug("Produce Content - no audio - suspending output");
|
||||
// mIOControl.suspendOutput();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// byte[] converted = mIAudioConverter.convert(packets);
|
||||
// mLog.debug("Produce Content - writing converted audio: " + converted.length);
|
||||
// contentEncoder.write(ByteBuffer.wrap(converted));
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRepeatable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
mLog.debug("We were closed!");
|
||||
}
|
||||
|
||||
public class MyHttpEntity extends AbstractHttpEntity
|
||||
{
|
||||
public MyHttpEntity()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRepeatable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getContent() throws IOException, UnsupportedOperationException
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream outputStream) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStreaming()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
202
src/audio/broadcast/icecast/IcecastConfiguration.java
Normal file
202
src/audio/broadcast/icecast/IcecastConfiguration.java
Normal file
@@ -0,0 +1,202 @@
|
||||
/*******************************************************************************
|
||||
* 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 audio.broadcast.BroadcastServerType;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
public class IcecastConfiguration extends BroadcastConfiguration
|
||||
{
|
||||
private String mUserName;
|
||||
private String mMountPoint;
|
||||
private String mStreamName;
|
||||
private String mDescription;
|
||||
private String mGenre;
|
||||
private boolean mPublic;
|
||||
private int mChannels;
|
||||
private int mBitRate;
|
||||
private String mURL;
|
||||
|
||||
public IcecastConfiguration(BroadcastFormat format)
|
||||
{
|
||||
super(format);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BroadcastServerType getBroadcastServerType()
|
||||
{
|
||||
return BroadcastServerType.ICECAST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 encoded version of the username and password with prepended 'Basic ' tag.
|
||||
*/
|
||||
public String getAuthorization()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(getUserName()).append(":").append(getPassword());
|
||||
|
||||
String base64 = Base64.getEncoder().encodeToString(sb.toString().getBytes());
|
||||
|
||||
return "Basic " + base64;
|
||||
}
|
||||
|
||||
public String getUserName()
|
||||
{
|
||||
return mUserName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName)
|
||||
{
|
||||
mUserName = userName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream name
|
||||
*/
|
||||
public String getStreamName()
|
||||
{
|
||||
return mStreamName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stream name
|
||||
* @param name
|
||||
*/
|
||||
public void setStreamName(String name)
|
||||
{
|
||||
mStreamName = name;
|
||||
}
|
||||
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public void setDescription(String description)
|
||||
{
|
||||
mDescription = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream genre
|
||||
*/
|
||||
public String getGenre()
|
||||
{
|
||||
return mGenre;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream genre
|
||||
* @param genre
|
||||
*/
|
||||
public void setGenre(String genre)
|
||||
{
|
||||
mGenre = genre;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public visibility of the broadcast
|
||||
*/
|
||||
public boolean isPublic()
|
||||
{
|
||||
return mPublic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets public visibility of the broadcast
|
||||
* @param isPublic indicates if the broadcast should be visible to the public
|
||||
*/
|
||||
public void setPublic(boolean isPublic)
|
||||
{
|
||||
mPublic = isPublic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of audio channels in the broadcast
|
||||
*/
|
||||
public int getChannels()
|
||||
{
|
||||
return mChannels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of audio channels in the broadcast
|
||||
*/
|
||||
public void setChannels(int channels)
|
||||
{
|
||||
mChannels = channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL associated with the broadcast where users can find additional details.
|
||||
*/
|
||||
public String getURL()
|
||||
{
|
||||
return mURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL associated with the broadcast where users can find additional details.
|
||||
* @param url
|
||||
*/
|
||||
public void setURL(String url)
|
||||
{
|
||||
mURL = url;
|
||||
}
|
||||
}
|
||||
381
src/audio/broadcast/icecast/IcecastHandler.java
Normal file
381
src/audio/broadcast/icecast/IcecastHandler.java
Normal file
@@ -0,0 +1,381 @@
|
||||
/*******************************************************************************
|
||||
* 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.AudioPacket;
|
||||
import audio.broadcast.BroadcastFormat;
|
||||
import audio.broadcast.BroadcastHandler;
|
||||
import audio.broadcast.BroadcastState;
|
||||
import audio.broadcast.Broadcaster;
|
||||
import audio.broadcast.BroadcasterFactory;
|
||||
import audio.broadcast.HttpAsyncAudioStreamer;
|
||||
import audio.convert.IAudioConverter;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.HttpVersion;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.concurrent.FutureCallback;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.InputStreamEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
|
||||
import org.apache.http.impl.nio.client.HttpAsyncClients;
|
||||
import org.apache.http.nio.IOControl;
|
||||
import org.apache.http.nio.client.methods.AsyncCharConsumer;
|
||||
import org.apache.http.nio.client.methods.HttpAsyncMethods;
|
||||
import org.apache.http.nio.protocol.BasicAsyncRequestProducer;
|
||||
import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
import record.wave.AudioPacketMonoWaveReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class IcecastHandler extends BroadcastHandler
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( IcecastHandler.class );
|
||||
|
||||
private static final String HTTP_100_CONTINUE = "100-continue";
|
||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 15000; //15 seconds
|
||||
private long mLastConnectionAttempt = 0;
|
||||
private CloseableHttpAsyncClient mHttpClient;
|
||||
private HttpAsyncAudioStreamer mAudioStreamer;
|
||||
private URI mURI;
|
||||
private int mCounter = 0;
|
||||
|
||||
/**
|
||||
* Creates an Icecast broadcast handler.
|
||||
* @param configuration
|
||||
* @param audioConverter
|
||||
*/
|
||||
public IcecastHandler(IcecastConfiguration configuration, IAudioConverter audioConverter)
|
||||
{
|
||||
super(configuration, audioConverter);
|
||||
|
||||
mAudioStreamer = new HttpAsyncAudioStreamer(HttpPut.METHOD_NAME, getConfiguration().getMountPoint(),
|
||||
getAudioConverter(), 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration information
|
||||
*/
|
||||
private IcecastConfiguration getConfiguration()
|
||||
{
|
||||
return (IcecastConfiguration)getBroadcastConfiguration();
|
||||
}
|
||||
|
||||
private URI getURI() throws URISyntaxException
|
||||
{
|
||||
if(mURI == null)
|
||||
{
|
||||
mURI = new URIBuilder()
|
||||
.setScheme("http")
|
||||
.setHost(getConfiguration().getHost())
|
||||
.setPort(getConfiguration().getPort())
|
||||
.setPath(getConfiguration().getMountPoint())
|
||||
.build();
|
||||
}
|
||||
|
||||
return mURI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process audio packets using Shoutcast V1 Protocol
|
||||
* @param audioPackets
|
||||
*/
|
||||
@Override
|
||||
public void broadcast(List<AudioPacket> audioPackets)
|
||||
{
|
||||
if(mCounter > 10)
|
||||
{
|
||||
mLog.debug("Connecting ...");
|
||||
connect();
|
||||
}
|
||||
|
||||
for(AudioPacket audioPacket: audioPackets)
|
||||
{
|
||||
mAudioStreamer.receive(audioPacket);
|
||||
|
||||
mCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the audio handler is currently connected to the remote server and capable of streaming audio.
|
||||
*
|
||||
* This method will attempt to establish a connection or reconnect to the streaming server.
|
||||
* @return true if the audio handler can stream audio
|
||||
*/
|
||||
private boolean connect()
|
||||
{
|
||||
if(!connected())
|
||||
{
|
||||
createConnection();
|
||||
}
|
||||
|
||||
return connected();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnect from the remote server
|
||||
*/
|
||||
@Override
|
||||
public void disconnect()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a connnection to the remote server using the shoutcast configuration information. Once disconnected
|
||||
* following a successful connection, attempts to reestablish a connection on a set interval
|
||||
*/
|
||||
private void createConnection()
|
||||
{
|
||||
if(!connected() && System.currentTimeMillis() - mLastConnectionAttempt >= RECONNECT_INTERVAL_MILLISECONDS)
|
||||
{
|
||||
mLog.debug("Creating a connection to icecast server");
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
|
||||
if(mHttpClient == null)
|
||||
{
|
||||
mHttpClient = HttpAsyncClients.createDefault();
|
||||
mHttpClient.start();
|
||||
}
|
||||
|
||||
// try
|
||||
// {
|
||||
HttpHost httpHost = new HttpHost(getConfiguration().getHost(), getConfiguration().getPort());
|
||||
// mAudioStreamer.addHeader(IcecastHeader.EXPECT.getValue(), HTTP_100_CONTINUE);
|
||||
|
||||
mAudioStreamer.addHeader(IcecastHeader.AUTHORIZATION.getValue(), getConfiguration().getAuthorization());
|
||||
mAudioStreamer.addHeader(HttpHeaders.ACCEPT, "*/*");
|
||||
mAudioStreamer.addHeader(HttpHeaders.CONTENT_TYPE, getConfiguration().getBroadcastFormat().getValue());
|
||||
mAudioStreamer.addHeader(IcecastHeader.PUBLIC.getValue(), getConfiguration().isPublic() ? "1" : "0");
|
||||
mAudioStreamer.addHeader(IcecastHeader.NAME.getValue(), getConfiguration().getStreamName());
|
||||
mAudioStreamer.addHeader(IcecastHeader.DESCRIPTION.getValue(), getConfiguration().getHost());
|
||||
mAudioStreamer.addHeader(IcecastHeader.URL.getValue(), getConfiguration().getURL());
|
||||
mAudioStreamer.addHeader(IcecastHeader.GENRE.getValue(), getConfiguration().getGenre());
|
||||
mAudioStreamer.addHeader(IcecastHeader.USER_AGENT.getValue(), SystemProperties.getInstance().getApplicationName());
|
||||
|
||||
// BasicAsyncRequestProducer producer = new BasicAsyncRequestProducer(httpHost, mAudioStreamer);
|
||||
BasicAsyncRequestProducer producer = new MyProducer(httpHost, mAudioStreamer, mAudioStreamer);
|
||||
AsyncCharConsumer<HttpResponse> consumer = new AsyncCharConsumer<HttpResponse>()
|
||||
{
|
||||
@Override
|
||||
protected void onCharReceived(CharBuffer charBuffer, IOControl ioControl) throws IOException
|
||||
{
|
||||
mLog.debug("Received consumer characters!");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResponseReceived(HttpResponse response) throws HttpException, IOException
|
||||
{
|
||||
mLog.debug("Received consumer response:\n" + response.toString());
|
||||
|
||||
switch(response.getStatusLine().getStatusCode())
|
||||
{
|
||||
case HttpStatus.SC_OK: //200
|
||||
case HttpStatus.SC_CONTINUE: //100
|
||||
mLog.debug("Connected to icecast server");
|
||||
setBroadcastState(BroadcastState.CONNECTED);
|
||||
break;
|
||||
case HttpStatus.SC_UNAUTHORIZED: //401
|
||||
setBroadcastState(BroadcastState.INVALID_PASSWORD);
|
||||
mLog.error("Couldn't connect to Icecast2 server using login credentials");
|
||||
break;
|
||||
case HttpStatus.SC_FORBIDDEN: //403
|
||||
String reason = response.getStatusLine().getReasonPhrase();
|
||||
|
||||
if(reason.contains("Content-Type"))
|
||||
{
|
||||
setBroadcastState(BroadcastState.UNSUPPORTED_AUDIO_FORMAT);
|
||||
mLog.error("Icecast2 server does not support audio format");
|
||||
}
|
||||
else if(reason.startsWith("internal format"))
|
||||
{
|
||||
setBroadcastState(BroadcastState.REMOTE_SERVER_ERROR);
|
||||
mLog.error("Icecast2 server has an error");
|
||||
}
|
||||
else if(reason.startsWith("too many sources"))
|
||||
{
|
||||
setBroadcastState(BroadcastState.MAX_SOURCES_EXCEEDED);
|
||||
mLog.error("Icecast2 server maximum number of sources exceeded");
|
||||
}
|
||||
else if(reason.startsWith("Mountpoint"))
|
||||
{
|
||||
setBroadcastState(BroadcastState.MOUNT_POINT_IN_USE);
|
||||
mLog.error("Icecast2 server mount point is already in use");
|
||||
}
|
||||
case HttpStatus.SC_INTERNAL_SERVER_ERROR: //500
|
||||
setBroadcastState(BroadcastState.INVALID_PASSWORD);
|
||||
mLog.error("Couldn't connect to Icecast2 server using login credentials");
|
||||
break;
|
||||
default:
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
mLog.error("Error connecting to remote server. HTTP Status Code:" +
|
||||
response.getStatusLine().getStatusCode() + " Reason:" +
|
||||
response.getStatusLine().getReasonPhrase());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpResponse buildResult(HttpContext httpContext) throws Exception
|
||||
{
|
||||
mLog.debug("Received consumer request for response!!!!!!");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
mLog.debug("Sending connection request");
|
||||
Future<HttpResponse> response = mHttpClient.execute(producer, consumer, new FutureCallback<HttpResponse>()
|
||||
{
|
||||
@Override
|
||||
public void completed(HttpResponse httpResponse)
|
||||
{
|
||||
mLog.debug("Received final response:\n" + httpResponse.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Exception e)
|
||||
{
|
||||
mLog.debug("Received final response error", e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelled()
|
||||
{
|
||||
mLog.debug("Received final response - cancelled");
|
||||
}
|
||||
});
|
||||
|
||||
mLog.debug("Create connection finished ... processing");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
boolean test = false;
|
||||
|
||||
IcecastConfiguration config = new IcecastConfiguration(BroadcastFormat.MP3);
|
||||
config.setAlias("Test Configuration");
|
||||
config.setHost("localhost");
|
||||
config.setPort(8000);
|
||||
config.setMountPoint("/dennyTestStream");
|
||||
config.setUserName("source");
|
||||
config.setPassword("denny");
|
||||
config.setStreamName("Denny's Audio Broadcast Test");
|
||||
config.setDescription("Description");
|
||||
config.setGenre("Scanner Audio");
|
||||
config.setPublic(true);
|
||||
config.setURL("http://localhost:8000");
|
||||
config.setBitRate(16);
|
||||
|
||||
if(test)
|
||||
{
|
||||
CloseableHttpClient client = HttpClients.createDefault();
|
||||
|
||||
URI uri;
|
||||
|
||||
try
|
||||
{
|
||||
uri = new URIBuilder()
|
||||
.setScheme("http")
|
||||
.setHost(config.getHost())
|
||||
.setPort(config.getPort())
|
||||
.setPath(config.getMountPoint())
|
||||
.build();
|
||||
|
||||
HttpPut httpPut = new HttpPut(uri);
|
||||
httpPut.setProtocolVersion(HttpVersion.HTTP_1_1);
|
||||
|
||||
httpPut.addHeader(IcecastHeader.AUTHORIZATION.getValue(), config.getAuthorization());
|
||||
httpPut.addHeader(IcecastHeader.HOST.getValue(), config.getHost());
|
||||
httpPut.addHeader(IcecastHeader.ACCEPT.getValue(), "*/*");
|
||||
httpPut.addHeader(IcecastHeader.CONTENT_TYPE.getValue(), config.getBroadcastFormat().getValue());
|
||||
httpPut.addHeader(IcecastHeader.PUBLIC.getValue(), config.isPublic() ? "1" : "0");
|
||||
httpPut.addHeader(IcecastHeader.NAME.getValue(), config.getStreamName());
|
||||
httpPut.addHeader(IcecastHeader.DESCRIPTION.getValue(), config.getHost());
|
||||
httpPut.addHeader(IcecastHeader.URL.getValue(), config.getURL());
|
||||
httpPut.addHeader(IcecastHeader.GENRE.getValue(), config.getGenre());
|
||||
httpPut.addHeader(IcecastHeader.USER_AGENT.getValue(), SystemProperties.getInstance().getApplicationName());
|
||||
httpPut.addHeader(IcecastHeader.EXPECT.getValue(), HTTP_100_CONTINUE);
|
||||
|
||||
CloseableHttpResponse response = client.execute(httpPut);
|
||||
|
||||
mLog.debug("Response:" + response.toString());
|
||||
|
||||
}
|
||||
catch(URISyntaxException e)
|
||||
{
|
||||
mLog.error("URI syntax exception", e);
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
mLog.error("IO Exception", ioe);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThreadPoolManager threadPoolManager = new ThreadPoolManager();
|
||||
|
||||
final Broadcaster broadcaster = BroadcasterFactory.getBroadcaster(threadPoolManager,config);
|
||||
|
||||
Path path = Paths.get("/home/denny/Music/PCM.wav");
|
||||
mLog.debug("Opening: " + path.toString());
|
||||
|
||||
mLog.debug("Registering and starting audio playback");
|
||||
|
||||
while(true)
|
||||
{
|
||||
mLog.debug("Playback started [" + path.toString() + "]");
|
||||
|
||||
try (AudioPacketMonoWaveReader reader = new AudioPacketMonoWaveReader(path, true))
|
||||
{
|
||||
reader.setListener(broadcaster);
|
||||
reader.read();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
mLog.error("Error", e);
|
||||
}
|
||||
|
||||
mLog.debug("Playback ended [" + path.toString() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/audio/broadcast/icecast/IcecastHeader.java
Normal file
48
src/audio/broadcast/icecast/IcecastHeader.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
public enum IcecastHeader
|
||||
{
|
||||
ACCEPT("Accept"),
|
||||
AUTHORIZATION("Authorization"),
|
||||
CONTENT_TYPE("Content-Type"),
|
||||
DESCRIPTION("ice-description"),
|
||||
EXPECT("Expect"),
|
||||
GENRE("ice-genre"),
|
||||
HOST("Host"),
|
||||
NAME("ice-name"),
|
||||
PUBLIC("ice-public"),
|
||||
URL("ice-url"),
|
||||
USER_AGENT("User-Agent"),
|
||||
|
||||
UNKNOWN("unknown");
|
||||
|
||||
private String mValue;
|
||||
|
||||
private IcecastHeader(String value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
}
|
||||
103
src/audio/broadcast/icecast/MyProducer.java
Normal file
103
src/audio/broadcast/icecast/MyProducer.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*******************************************************************************
|
||||
* 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 org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.nio.ContentEncoder;
|
||||
import org.apache.http.nio.IOControl;
|
||||
import org.apache.http.nio.entity.HttpAsyncContentProducer;
|
||||
import org.apache.http.nio.protocol.BasicAsyncRequestProducer;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MyProducer extends BasicAsyncRequestProducer
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( MyProducer.class );
|
||||
|
||||
public MyProducer(HttpHost target, HttpEntityEnclosingRequest request, HttpAsyncContentProducer producer)
|
||||
{
|
||||
super(target, request, producer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void produceContent(ContentEncoder encoder, IOControl ioctrl) throws IOException
|
||||
{
|
||||
// mLog.debug("############### Producing Content");
|
||||
super.produceContent(encoder, ioctrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequest generateRequest()
|
||||
{
|
||||
mLog.debug("############### Generating a request");
|
||||
|
||||
HttpRequest request = super.generateRequest();
|
||||
|
||||
mLog.debug("############### Our request class is:" + request.getClass() );
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHost getTarget()
|
||||
{
|
||||
mLog.debug("############### Getting our target");
|
||||
return super.getTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestCompleted(HttpContext context)
|
||||
{
|
||||
mLog.debug("############### requesting completed");
|
||||
super.requestCompleted(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Exception ex)
|
||||
{
|
||||
mLog.debug("############### Fail!");
|
||||
super.failed(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRepeatable()
|
||||
{
|
||||
mLog.debug("############### Indicating if we're repeatable");
|
||||
return super.isRepeatable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetRequest() throws IOException
|
||||
{
|
||||
mLog.debug("############### resetting request");
|
||||
super.resetRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
mLog.debug("############### Closing");
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ import audio.convert.IAudioConverter;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import properties.SystemProperties;
|
||||
import record.wave.AudioPacketMonoWaveReader;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
@@ -402,7 +403,8 @@ public class ShoutcastV2Handler extends BroadcastHandler
|
||||
(response.getError() != null ? response.getError() : "no error message"));
|
||||
}
|
||||
|
||||
metadata.add(UltravoxMetadata.BROADCAST_CLIENT_APPLICATION.asXML("sdrtrunk"));
|
||||
metadata.add(UltravoxMetadata.BROADCAST_CLIENT_APPLICATION
|
||||
.asXML(SystemProperties.getInstance().getApplicationName()));
|
||||
|
||||
mLog.debug("Fetching metadata messages from metadata...");
|
||||
List<UltravoxMessage> messages = getMetadataMessages(metadata);
|
||||
|
||||
@@ -229,7 +229,7 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
/**
|
||||
* Setup main JFrame window
|
||||
*/
|
||||
mTitle = getTitle();
|
||||
mTitle = SystemProperties.getInstance().getApplicationName();
|
||||
mMainGui.setTitle( mTitle );
|
||||
mMainGui.setBounds( 100, 100, 1280, 800 );
|
||||
mMainGui.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
|
||||
@@ -448,32 +448,6 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
return homePath;
|
||||
}
|
||||
|
||||
private String getTitle()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "sdrtrunk" );
|
||||
|
||||
try( BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader( this.getClass()
|
||||
.getResourceAsStream( "/sdrtrunk-version" ) ) ) )
|
||||
{
|
||||
String version = reader.readLine();
|
||||
|
||||
if( version != null )
|
||||
{
|
||||
sb.append( " V" );
|
||||
sb.append( version );
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
mLog.error( "Couldn't read sdrtrunk version from application jar file" );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( TunerEvent event )
|
||||
{
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
******************************************************************************/
|
||||
package properties;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -47,7 +49,7 @@ public class SystemProperties
|
||||
private static SystemProperties mInstance;
|
||||
private static Properties mProperties;
|
||||
private Path mPropertiesPath;
|
||||
|
||||
private String mApplicationName;
|
||||
|
||||
private SystemProperties()
|
||||
{
|
||||
@@ -67,7 +69,7 @@ public class SystemProperties
|
||||
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Saves any currently changed settings to the application properties file
|
||||
*/
|
||||
@@ -86,9 +88,6 @@ public class SystemProperties
|
||||
"SDRTrunk - SDR Trunking Decoder Application Settings";
|
||||
|
||||
mProperties.store( out, comments );
|
||||
|
||||
// mLog.info( "SystemProperties - saved current properties [" +
|
||||
// propsPath.toString() + "]" );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
@@ -216,6 +215,37 @@ public class SystemProperties
|
||||
propertiesPath.toString() + "]" );
|
||||
}
|
||||
|
||||
public String getApplicationName()
|
||||
{
|
||||
if(mApplicationName == null)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "sdrtrunk" );
|
||||
|
||||
try(BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader( this.getClass()
|
||||
.getResourceAsStream( "/sdrtrunk-version" ) ) ) )
|
||||
{
|
||||
String version = reader.readLine();
|
||||
|
||||
if( version != null )
|
||||
{
|
||||
sb.append( " V" );
|
||||
sb.append( version );
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
mLog.error( "Couldn't read sdrtrunk version from application jar file" );
|
||||
}
|
||||
|
||||
mApplicationName = sb.toString();
|
||||
}
|
||||
|
||||
return mApplicationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the property, or null if the property doesn't exist
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user