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
@@ -10,6 +10,7 @@
|
||||
<w>talkgroup</w>
|
||||
<w>tgid</w>
|
||||
<w>ultravox</w>
|
||||
<w>xtea</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
@@ -65,6 +65,11 @@ public enum BroadcastState
|
||||
*/
|
||||
UNKNOWN_HOST("Unknown Host or Invalid Port", true),
|
||||
|
||||
/**
|
||||
* Unsupported audio format
|
||||
*/
|
||||
UNSUPPORTED_AUDIO_FORMAT("Unsupported Audio Type", true),
|
||||
|
||||
/**
|
||||
* Unspecified error
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,8 @@ package audio.broadcast;
|
||||
|
||||
import audio.broadcast.shoutcast.v1.ShoutcastV1Configuration;
|
||||
import audio.broadcast.shoutcast.v1.ShoutcastV1Handler;
|
||||
import audio.broadcast.shoutcast.v2.ShoutcastV2Configuration;
|
||||
import audio.broadcast.shoutcast.v2.ShoutcastV2Handler;
|
||||
import audio.convert.IAudioConverter;
|
||||
import audio.convert.MP3AudioConverter;
|
||||
import controller.ThreadPoolManager;
|
||||
@@ -51,6 +53,9 @@ public class BroadcasterFactory
|
||||
case SHOUTCAST_V1:
|
||||
return new Broadcaster(threadPoolManager,
|
||||
new ShoutcastV1Handler((ShoutcastV1Configuration)configuration, converter));
|
||||
case SHOUTCAST_V2:
|
||||
return new Broadcaster(threadPoolManager,
|
||||
new ShoutcastV2Handler((ShoutcastV2Configuration)configuration, converter));
|
||||
case UNKNOWN:
|
||||
break;
|
||||
}
|
||||
@@ -70,7 +75,6 @@ public class BroadcasterFactory
|
||||
{
|
||||
case MP3:
|
||||
return new MP3AudioConverter(MP3_MONO_16_KHZ_BITRATE, MP3_NO_VARIABLE_BITRATE);
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -133,12 +133,12 @@ public class ShoutcastV1Handler extends BroadcastHandler
|
||||
{
|
||||
if(!connected() && System.currentTimeMillis() - mLastConnectionAttempt >= RECONNECT_INTERVAL_MILLISECONDS)
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
|
||||
createSocket();
|
||||
|
||||
if(mSocket.isConnected())
|
||||
{
|
||||
mLog.debug("Sending credentials");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
//Password
|
||||
@@ -186,7 +186,6 @@ public class ShoutcastV1Handler extends BroadcastHandler
|
||||
*/
|
||||
private void send(String data) throws IOException
|
||||
{
|
||||
mLog.debug("Sending [" + data + "]");
|
||||
if(data != null && !data.isEmpty() && mOutputStream != null)
|
||||
{
|
||||
mOutputStream.writeBytes(data);
|
||||
@@ -200,7 +199,6 @@ public class ShoutcastV1Handler extends BroadcastHandler
|
||||
*/
|
||||
private void send(byte[] data) throws IOException
|
||||
{
|
||||
mLog.debug("Sending bytes:" + data.length);
|
||||
if(connected())
|
||||
{
|
||||
int sent = 0;
|
||||
@@ -344,7 +342,7 @@ public class ShoutcastV1Handler extends BroadcastHandler
|
||||
config.setAlias("Test Configuration");
|
||||
config.setHost("localhost");
|
||||
config.setPort(8000);
|
||||
config.setPassword("denny3");
|
||||
config.setPassword("denny3:#1");
|
||||
config.setStreamName("Denny's Audio Broadcast Test");
|
||||
config.setGenre("Scanner Audio");
|
||||
config.setPublic(true);
|
||||
|
||||
@@ -24,14 +24,13 @@ import audio.broadcast.BroadcastServerType;
|
||||
|
||||
public class ShoutcastV2Configuration extends BroadcastConfiguration
|
||||
{
|
||||
private String mMountPoint;
|
||||
private String mStreamName;
|
||||
private String mSongTitle;
|
||||
private String mGenre;
|
||||
private boolean mPublic;
|
||||
private int mChannels;
|
||||
private int mStreamID;
|
||||
private String mUserID;
|
||||
private int mBitRate;
|
||||
private String mStreamName;
|
||||
private String mStreamGenre;
|
||||
private String mURL;
|
||||
private boolean mPublic = true;
|
||||
|
||||
|
||||
public ShoutcastV2Configuration(BroadcastFormat format)
|
||||
@@ -42,26 +41,60 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
||||
@Override
|
||||
public BroadcastServerType getBroadcastServerType()
|
||||
{
|
||||
return BroadcastServerType.SHOUTCAST_V1;
|
||||
}
|
||||
|
||||
public String getMountPoint()
|
||||
{
|
||||
return mMountPoint;
|
||||
return BroadcastServerType.SHOUTCAST_V2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the optional mount point. Shoutcast v1.x servers don't require a mount point, but v2.x servers do require
|
||||
* the mount point.
|
||||
*
|
||||
* Note: if the supplied mount point contains a leading slash (/) it will be removed.
|
||||
*
|
||||
* @param mountPoint
|
||||
* Stream ID
|
||||
*/
|
||||
public void setMountPoint(String mountPoint)
|
||||
public int getStreamID()
|
||||
{
|
||||
//Strip the leading slash from the mount point if the user supplies it
|
||||
mMountPoint = mountPoint.replace("/", "");
|
||||
return mStreamID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stream ID
|
||||
*
|
||||
* @param streamID numeric stream identifier between 1 - 2147483647
|
||||
*/
|
||||
public void setStreamID(int streamID)
|
||||
{
|
||||
assert(1 <= streamID && streamID <= Integer.MAX_VALUE);
|
||||
mStreamID = streamID;
|
||||
}
|
||||
|
||||
/**
|
||||
* User ID
|
||||
*/
|
||||
public String getUserID()
|
||||
{
|
||||
return mUserID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user ID
|
||||
* @param userID
|
||||
*/
|
||||
public void setUserID(String userID)
|
||||
{
|
||||
mUserID = userID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bit rate in kilobits per second
|
||||
*/
|
||||
public int getBitRate()
|
||||
{
|
||||
return mBitRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bit rate
|
||||
* @param bitRate in kilobits per second
|
||||
*/
|
||||
public void setBitRate(int bitRate)
|
||||
{
|
||||
mBitRate = bitRate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,37 +114,20 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
||||
mStreamName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Song title
|
||||
*/
|
||||
public String getSongTitle()
|
||||
{
|
||||
return mSongTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the song title
|
||||
*/
|
||||
public void setSongTitle(String title)
|
||||
{
|
||||
mSongTitle = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream genre
|
||||
*/
|
||||
public String getGenre()
|
||||
public String getStreamGenre()
|
||||
{
|
||||
return mGenre;
|
||||
return mStreamGenre;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream genre
|
||||
* @param genre
|
||||
* Sets the genre tag for this stream
|
||||
*/
|
||||
public void setGenre(String genre)
|
||||
public void setStreamGenre(String genre)
|
||||
{
|
||||
mGenre = genre;
|
||||
mStreamGenre = genre;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,39 +147,6 @@ public class ShoutcastV2Configuration extends BroadcastConfiguration
|
||||
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.
|
||||
*/
|
||||
|
||||
570
src/audio/broadcast/shoutcast/v2/ShoutcastV2Handler.java
Normal file
570
src/audio/broadcast/shoutcast/v2/ShoutcastV2Handler.java
Normal file
@@ -0,0 +1,570 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2;
|
||||
|
||||
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.shoutcast.v2.message.AuthenticateBroadcast;
|
||||
import audio.broadcast.shoutcast.v2.message.ConfigureIcyGenre;
|
||||
import audio.broadcast.shoutcast.v2.message.ConfigureIcyName;
|
||||
import audio.broadcast.shoutcast.v2.message.ConfigureIcyPublic;
|
||||
import audio.broadcast.shoutcast.v2.message.ConfigureIcyURL;
|
||||
import audio.broadcast.shoutcast.v2.message.MP3Audio;
|
||||
import audio.broadcast.shoutcast.v2.message.NegotiateBufferSize;
|
||||
import audio.broadcast.shoutcast.v2.message.NegotiateMaxPayloadSize;
|
||||
import audio.broadcast.shoutcast.v2.message.RequestCipher;
|
||||
import audio.broadcast.shoutcast.v2.message.SetupBroadcast;
|
||||
import audio.broadcast.shoutcast.v2.message.Standby;
|
||||
import audio.broadcast.shoutcast.v2.message.UltravoxMessage;
|
||||
import audio.broadcast.shoutcast.v2.message.UltravoxMessageFactory;
|
||||
import audio.broadcast.shoutcast.v2.message.UltravoxMessageType;
|
||||
import audio.broadcast.shoutcast.v2.message.UltravoxMetadata;
|
||||
import audio.broadcast.shoutcast.v2.message.XMLMetadata;
|
||||
import audio.convert.IAudioConverter;
|
||||
import controller.ThreadPoolManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import record.wave.AudioPacketMonoWaveReader;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ShoutcastV2Handler extends BroadcastHandler
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( ShoutcastV2Handler.class );
|
||||
|
||||
private static final long RECONNECT_INTERVAL_MILLISECONDS = 15000; //15 seconds
|
||||
private long mLastConnectionAttempt = 0;
|
||||
|
||||
private Socket mSocket;
|
||||
private DataOutputStream mOutputStream;
|
||||
private DataInputStream mInputStream;
|
||||
private int mMaxPayloadSize = 16377;
|
||||
|
||||
/**
|
||||
* Creates a Shoutcast Version 1 broadcast handler.
|
||||
* @param configuration
|
||||
* @param audioConverter
|
||||
*/
|
||||
public ShoutcastV2Handler(ShoutcastV2Configuration configuration, IAudioConverter audioConverter)
|
||||
{
|
||||
super(configuration, audioConverter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shoutcast V2 Configuration information
|
||||
*/
|
||||
private ShoutcastV2Configuration getShoutcastConfiguration()
|
||||
{
|
||||
return (ShoutcastV2Configuration)getBroadcastConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast audio packets
|
||||
*
|
||||
* @param audioPackets
|
||||
*/
|
||||
@Override
|
||||
public void broadcast(List<AudioPacket> audioPackets)
|
||||
{
|
||||
//If we're connected, send the audio, otherwise discard it
|
||||
if(connect())
|
||||
{
|
||||
byte[] convertedAudio = mAudioConverter.convert(audioPackets);
|
||||
|
||||
if(convertedAudio != null)
|
||||
{
|
||||
List<UltravoxMessage> audioMessages = getAudioMessages(convertedAudio);
|
||||
|
||||
for(UltravoxMessage audioMessage: audioMessages)
|
||||
{
|
||||
try
|
||||
{
|
||||
send(audioMessage, false);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
mLog.error("Error while dispatching audio", e);
|
||||
setBroadcastState(BroadcastState.BROADCAST_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.debug("Not connected");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
// TerminateBroadcast terminate = (TerminateBroadcast)UltravoxMessageFactory
|
||||
// .getMessage(UltravoxMessageType.TERMINATE_BROADCAST);
|
||||
// response = send(terminate, out, in);
|
||||
// mLog.debug("Terminate Response:" + (response.getPayload() != null ? response.getPayload() : ""));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the message. When get response is true, reads from the input stream and converts the response to a
|
||||
* message that is returned.
|
||||
*
|
||||
* @param message to send to the shoutcast/ultravox server
|
||||
* @param getResponse true if the sent message will generate a response
|
||||
* @return response message when getResponse is set to true
|
||||
* @throws IOException if there is an issue while communicating with the server
|
||||
*/
|
||||
private UltravoxMessage send(UltravoxMessage message, boolean getResponse) throws IOException
|
||||
{
|
||||
byte[] bytes = message.getMessage();
|
||||
|
||||
mOutputStream.write(bytes);
|
||||
|
||||
if(getResponse)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(50);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
int available = mInputStream.available();
|
||||
|
||||
if(available > 0)
|
||||
{
|
||||
byte[] buffer = new byte[available];
|
||||
int read = mInputStream.read(buffer);
|
||||
|
||||
return UltravoxMessageFactory.getMessage(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the encoded audio byte array into a list of ultravox messages ready for transmission
|
||||
* @param audio to embed as message payload
|
||||
* @return list of messages containing the audio payload
|
||||
*/
|
||||
private List<UltravoxMessage> getAudioMessages(byte[] audio)
|
||||
{
|
||||
List<UltravoxMessage> messages = new ArrayList<>();
|
||||
|
||||
int pointer = 0;
|
||||
|
||||
while(pointer < audio.length)
|
||||
{
|
||||
int payloadSize = Math.min(mMaxPayloadSize, audio.length - pointer);
|
||||
|
||||
byte[] payload = new byte[payloadSize];
|
||||
|
||||
System.arraycopy(audio, pointer, payload, 0, payloadSize);
|
||||
|
||||
MP3Audio mp3Audio = (MP3Audio)UltravoxMessageFactory.getMessage(UltravoxMessageType.MP3_DATA);
|
||||
mp3Audio.setPayload(payload);
|
||||
messages.add(mp3Audio);
|
||||
|
||||
pointer += payloadSize;
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the list of metadata in one or more Ultravox XMLMetadata messages according to the maximum negotiated
|
||||
* payload size for the current connection. Each entry in the list of metadata strings should be an xml encoded
|
||||
* value: <tag>value</tag>
|
||||
*
|
||||
* See UltravoxMetadata.TAG.asXML(String value)
|
||||
*
|
||||
* @param xmlMetadata list of xml encoded values
|
||||
* @return a sequence of XMLMetadata messages sufficient to carry the complete set of metadata
|
||||
*/
|
||||
private List<UltravoxMessage> getMetadataMessages(List<String> xmlMetadata)
|
||||
{
|
||||
List<UltravoxMessage> messages = new ArrayList<>();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<metadata>\n");
|
||||
|
||||
for(String metadata: xmlMetadata)
|
||||
{
|
||||
sb.append(metadata);
|
||||
}
|
||||
|
||||
sb.append("\n</metadata>");
|
||||
|
||||
mLog.debug("Metadata:" + sb.toString());
|
||||
byte[] xml = sb.toString().getBytes();
|
||||
|
||||
int pointer = 0;
|
||||
int messageCounter = 1;
|
||||
int messageCount = (int)Math.ceil((double)xml.length / (double)(mMaxPayloadSize - 6));
|
||||
|
||||
if(messageCount > 32)
|
||||
{
|
||||
messageCount = 32; //Max number of metadata messages in a sequence
|
||||
}
|
||||
|
||||
|
||||
while(pointer < xml.length && messageCounter <= messageCount)
|
||||
{
|
||||
int payloadSize = Math.min(mMaxPayloadSize - 6, xml.length - pointer);
|
||||
|
||||
byte[] payload = new byte[payloadSize + 6];
|
||||
|
||||
payload[1] = (byte)(0x01);
|
||||
payload[3] = (byte)(messageCount & 0xFF);
|
||||
payload[5] = (byte)(messageCounter & 0xFF);
|
||||
|
||||
System.arraycopy(xml, pointer, payload, 6, payloadSize);
|
||||
|
||||
XMLMetadata message = (XMLMetadata)UltravoxMessageFactory.getMessage(UltravoxMessageType.XML_METADATA);
|
||||
message.setPayload(payload);
|
||||
messages.add(message);
|
||||
|
||||
pointer += payloadSize;
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
mLastConnectionAttempt = System.currentTimeMillis();
|
||||
|
||||
createSocket();
|
||||
|
||||
if (mSocket.isConnected())
|
||||
{
|
||||
RequestCipher requestCipher = (RequestCipher) UltravoxMessageFactory
|
||||
.getMessage(UltravoxMessageType.REQUEST_CIPHER);
|
||||
|
||||
try
|
||||
{
|
||||
UltravoxMessage response = send(requestCipher, true);
|
||||
|
||||
if (response instanceof RequestCipher)
|
||||
{
|
||||
RequestCipher cipherResponse = (RequestCipher) response;
|
||||
|
||||
AuthenticateBroadcast authenticateBroadcast = (AuthenticateBroadcast) UltravoxMessageFactory
|
||||
.getMessage(UltravoxMessageType.AUTHENTICATE_BROADCAST);
|
||||
authenticateBroadcast.setCredentials(cipherResponse.getCipher(),
|
||||
getShoutcastConfiguration().getStreamID(), getShoutcastConfiguration().getUserID(),
|
||||
getShoutcastConfiguration().getPassword());
|
||||
response = send(authenticateBroadcast, true);
|
||||
|
||||
if (response.isErrorResponse())
|
||||
{
|
||||
setBroadcastState(BroadcastState.INVALID_PASSWORD);
|
||||
return;
|
||||
}
|
||||
|
||||
UltravoxMessage streamMimeType = UltravoxMessageFactory
|
||||
.getMessage(UltravoxMessageType.STREAM_MIME_TYPE);
|
||||
streamMimeType.setPayload(getShoutcastConfiguration().getBroadcastFormat().getValue());
|
||||
response = send(streamMimeType, true);
|
||||
|
||||
if (response.isErrorResponse())
|
||||
{
|
||||
setBroadcastState(BroadcastState.UNSUPPORTED_AUDIO_FORMAT);
|
||||
mLog.error("Audio format [" + getShoutcastConfiguration().getBroadcastFormat() + "] not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
SetupBroadcast setupBroadcast = (SetupBroadcast) UltravoxMessageFactory
|
||||
.getMessage(UltravoxMessageType.SETUP_BROADCAST);
|
||||
setupBroadcast.setBitRate(getShoutcastConfiguration().getBitRate(),
|
||||
getShoutcastConfiguration().getBitRate());
|
||||
response = send(setupBroadcast, true);
|
||||
|
||||
if (response.isErrorResponse())
|
||||
{
|
||||
setBroadcastState(BroadcastState.UNSUPPORTED_AUDIO_FORMAT);
|
||||
mLog.error("Audio bit rate [" + getShoutcastConfiguration().getBitRate() + "] not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
NegotiateMaxPayloadSize negotiateMaxPayloadSize = (NegotiateMaxPayloadSize) UltravoxMessageFactory
|
||||
.getMessage(UltravoxMessageType.NEGOTIATE_MAX_PAYLOAD_SIZE);
|
||||
negotiateMaxPayloadSize.setMaximumPayloadSize(mMaxPayloadSize, 14000);
|
||||
response = send(negotiateMaxPayloadSize, true);
|
||||
mMaxPayloadSize = ((NegotiateMaxPayloadSize) response).getMaximumPayloadSize();
|
||||
|
||||
List<String> metadata = new ArrayList<>();
|
||||
|
||||
if (getShoutcastConfiguration().getStreamName() != null)
|
||||
{
|
||||
metadata.add(UltravoxMetadata.TITLE_1.asXML(getShoutcastConfiguration().getStreamName()));
|
||||
|
||||
ConfigureIcyName icyName = (ConfigureIcyName) UltravoxMessageFactory
|
||||
.getMessage(UltravoxMessageType.CONFIGURE_ICY_NAME);
|
||||
icyName.setName(getShoutcastConfiguration().getStreamName());
|
||||
response = send(icyName, false);
|
||||
}
|
||||
|
||||
if (getShoutcastConfiguration().getStreamGenre() != null)
|
||||
{
|
||||
metadata.add(UltravoxMetadata.GENRE.asXML(getShoutcastConfiguration().getStreamGenre()));
|
||||
|
||||
ConfigureIcyGenre icyGenre = (ConfigureIcyGenre) UltravoxMessageFactory
|
||||
.getMessage(UltravoxMessageType.CONFIGURE_ICY_GENRE);
|
||||
icyGenre.setGenre(getShoutcastConfiguration().getStreamGenre());
|
||||
response = send(icyGenre, false);
|
||||
}
|
||||
|
||||
if (getShoutcastConfiguration().getURL() != null)
|
||||
{
|
||||
ConfigureIcyURL icyURL = (ConfigureIcyURL) UltravoxMessageFactory
|
||||
.getMessage(UltravoxMessageType.CONFIGURE_ICY_URL);
|
||||
icyURL.setURL(getShoutcastConfiguration().getURL());
|
||||
response = send(icyURL, false);
|
||||
}
|
||||
|
||||
ConfigureIcyPublic icyPublic = (ConfigureIcyPublic) UltravoxMessageFactory
|
||||
.getMessage(UltravoxMessageType.CONFIGURE_ICY_PUBLIC);
|
||||
icyPublic.setPublic(getShoutcastConfiguration().isPublic());
|
||||
response = send(icyPublic, false);
|
||||
|
||||
Standby standby = (Standby) UltravoxMessageFactory.getMessage(UltravoxMessageType.STANDBY);
|
||||
response = send(standby, true);
|
||||
|
||||
if (response.isValidResponse())
|
||||
{
|
||||
setBroadcastState(BroadcastState.CONNECTED);
|
||||
}
|
||||
else
|
||||
{
|
||||
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||
mLog.error("Error message after configuration and sending standby:" +
|
||||
(response.getError() != null ? response.getError() : "no error message"));
|
||||
}
|
||||
|
||||
metadata.add(UltravoxMetadata.BROADCAST_CLIENT_APPLICATION.asXML("sdrtrunk"));
|
||||
|
||||
mLog.debug("Fetching metadata messages from metadata...");
|
||||
List<UltravoxMessage> messages = getMetadataMessages(metadata);
|
||||
|
||||
for (UltravoxMessage message : messages)
|
||||
{
|
||||
mLog.debug("Sending metadata message");
|
||||
send(message, true);
|
||||
}
|
||||
|
||||
mLog.debug("We're connected!!!");
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
mLog.error("Error while creating connection to server", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a socket connection to the remote server and sets the state to CONNECTING.
|
||||
*/
|
||||
private void createSocket()
|
||||
{
|
||||
if(mSocket == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
mSocket = new Socket(getShoutcastConfiguration().getHost(),
|
||||
getShoutcastConfiguration().getPort());
|
||||
mOutputStream = new DataOutputStream(mSocket.getOutputStream());
|
||||
mInputStream = new DataInputStream(mSocket.getInputStream());
|
||||
}
|
||||
catch(UnknownHostException uhe)
|
||||
{
|
||||
setBroadcastState(BroadcastState.UNKNOWN_HOST);
|
||||
mLog.error("Unknown host or port. Unable to create connection to streaming server host[" +
|
||||
getShoutcastConfiguration().getHost() + "] and port[" +
|
||||
getShoutcastConfiguration().getPort() + "] - will reattempt connection periodically");
|
||||
return;
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
|
||||
mLog.error("Error connecting to streaming server host[" +
|
||||
getShoutcastConfiguration().getHost() + "] and port[" +
|
||||
getShoutcastConfiguration().getPort() + "]", ioe);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(mSocket.isConnected())
|
||||
{
|
||||
setBroadcastState(BroadcastState.CONNECTING);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
SocketAddress address = getShoutcastConfiguration().getAddress();
|
||||
mSocket.connect(address);
|
||||
setBroadcastState(BroadcastState.CONNECTING);
|
||||
}
|
||||
catch(UnknownHostException uhe)
|
||||
{
|
||||
setBroadcastState(BroadcastState.UNKNOWN_HOST);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
setBroadcastState(BroadcastState.ERROR);
|
||||
|
||||
mLog.error("Error connecting to streaming server host[" +
|
||||
getShoutcastConfiguration().getHost() + "] and port[" +
|
||||
getShoutcastConfiguration().getPort() + "]", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static UltravoxMessage send(UltravoxMessage message,
|
||||
DataOutputStream out,
|
||||
DataInputStream in,
|
||||
int maxPayloadSize) throws IOException
|
||||
{
|
||||
byte[] bytes = message.getMessage();
|
||||
|
||||
out.write(bytes);
|
||||
|
||||
try
|
||||
{
|
||||
Thread.sleep(50);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
int available = in.available();
|
||||
|
||||
if(available > 0)
|
||||
{
|
||||
byte[] buffer = new byte[available];
|
||||
int read = in.read(buffer);
|
||||
|
||||
return UltravoxMessageFactory.getMessage(buffer);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
boolean test = false;
|
||||
|
||||
ShoutcastV2Configuration config = new ShoutcastV2Configuration(BroadcastFormat.MP3);
|
||||
config.setStreamID(1);
|
||||
config.setAlias("Test Configuration");
|
||||
config.setHost("localhost");
|
||||
config.setPort(8000);
|
||||
config.setPassword("denny3");
|
||||
config.setStreamName("Denny's Audio Broadcast Test");
|
||||
config.setStreamGenre("Public Safety");
|
||||
config.setPublic(true);
|
||||
config.setURL("http://localhost:8000");
|
||||
config.setBitRate(16);
|
||||
|
||||
if(test)
|
||||
{
|
||||
}
|
||||
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() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
import util.XTEA;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class AuthenticateBroadcast extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to send authentication credentials
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
AuthenticateBroadcast()
|
||||
{
|
||||
super(UltravoxMessageType.AUTHENTICATE_BROADCAST);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
AuthenticateBroadcast(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set credential to authenticate the stream with the server
|
||||
*
|
||||
* @param encryptionKey received from the server in the RequestCipher message
|
||||
* @param streamID that we're going to stream to
|
||||
* @param userID for the stream. Note: this is optional and can be null. The server currently ignores this value.
|
||||
* @param password for the stream.
|
||||
*/
|
||||
public void setCredentials(String encryptionKey, int streamID, String userID, String password)
|
||||
{
|
||||
XTEA xtea = new XTEA(encryptionKey);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(SHOUTCAST_VERSION);
|
||||
sb.append(":");
|
||||
sb.append(streamID);
|
||||
sb.append(":");
|
||||
sb.append(userID != null ? encrypt(xtea, userID) : "");
|
||||
sb.append(":");
|
||||
sb.append(password != null ? encrypt(xtea, password) : "");
|
||||
|
||||
setPayload(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the value using the XTEA block cipher algorithm and returns the encrypted value as a 16 hex value
|
||||
*
|
||||
* @param xtea instance of the XTEA algorithm with a loaded encryption key
|
||||
* @param value to encode
|
||||
* @return encrypted password as 64-bit, 16 character hexadecimal value
|
||||
*/
|
||||
private static String encrypt(XTEA xtea, String value)
|
||||
{
|
||||
byte[] valueBytes = value.getBytes();
|
||||
|
||||
if(valueBytes.length % 8 != 0 || valueBytes.length == 0)
|
||||
{
|
||||
int newLength = ((valueBytes.length / 8) + 1) * 8;
|
||||
valueBytes = Arrays.copyOf(valueBytes, newLength);
|
||||
}
|
||||
|
||||
byte[] encrypted = xtea.encrypt(valueBytes);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(byte b: encrypted)
|
||||
{
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -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.shoutcast.v2.message;
|
||||
|
||||
public class ConfigureIcyGenre extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to configure the ICY-GENRE metadata
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
ConfigureIcyGenre()
|
||||
{
|
||||
super(UltravoxMessageType.CONFIGURE_ICY_GENRE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
ConfigureIcyGenre(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
public void setGenre(String genre)
|
||||
{
|
||||
setPayload(genre);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
import util.XTEA;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ConfigureIcyName extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to configure the ICY-NAME metadata
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
ConfigureIcyName()
|
||||
{
|
||||
super(UltravoxMessageType.CONFIGURE_ICY_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
ConfigureIcyName(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
public void setName(String name)
|
||||
{
|
||||
setPayload(name);
|
||||
}
|
||||
}
|
||||
@@ -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.shoutcast.v2.message;
|
||||
|
||||
public class ConfigureIcyPublic extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to configure the ICY-PUBLIC metadata
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
ConfigureIcyPublic()
|
||||
{
|
||||
super(UltravoxMessageType.CONFIGURE_ICY_PUBLIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
ConfigureIcyPublic(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
public void setPublic(boolean publicStream)
|
||||
{
|
||||
setPayload(publicStream ? "1" : "0");
|
||||
}
|
||||
}
|
||||
@@ -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.shoutcast.v2.message;
|
||||
|
||||
public class ConfigureIcyURL extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to configure the ICY-URL metadata
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
ConfigureIcyURL()
|
||||
{
|
||||
super(UltravoxMessageType.CONFIGURE_ICY_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
ConfigureIcyURL(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
public void setURL(String url)
|
||||
{
|
||||
setPayload(url);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public class FlushCachedMetadata extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to flush any currently cached stream metadata
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
FlushCachedMetadata()
|
||||
{
|
||||
super(UltravoxMessageType.FLUSH_CACHED_METADATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
FlushCachedMetadata(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
@@ -16,34 +16,29 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2;
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public enum UltravoxMessageType
|
||||
public class MP3Audio extends UltravoxMessage
|
||||
{
|
||||
AUTHENTICATE_BROADCAST(0x1001),
|
||||
|
||||
UNKNOWN(0x0000);
|
||||
|
||||
private int mValue;
|
||||
|
||||
private UltravoxMessageType(int value)
|
||||
/**
|
||||
* Client request to server to send authentication credentials
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
MP3Audio()
|
||||
{
|
||||
mValue = value;
|
||||
super(UltravoxMessageType.MP3_DATA);
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public static UltravoxMessageType fromValue(int value)
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
MP3Audio(byte[] data)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case 0x1001:
|
||||
return AUTHENTICATE_BROADCAST;
|
||||
default:
|
||||
return UNKNOWN;
|
||||
}
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public class NegotiateBufferSize extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to negotiate the server's buffer size
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
NegotiateBufferSize()
|
||||
{
|
||||
super(UltravoxMessageType.NEGOTIATE_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
NegotiateBufferSize(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the desired and minimum requested buffer sizes to send to the server
|
||||
* @param desired buffer size in kilobytes
|
||||
* @param minimum buffer size in kilobytes
|
||||
*/
|
||||
public void setBufferSize(int desired, int minimum)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(desired).append(":").append(minimum);
|
||||
|
||||
setPayload(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Negotiated buffer size from a server response message.
|
||||
*
|
||||
* @return server negotiated buffer size, or zero if this is not a server response message, or this is an error
|
||||
* server response message, or the server response message buffer size cannot be determined from the message payload.
|
||||
*/
|
||||
public int getBufferSize()
|
||||
{
|
||||
String payload = getPayload();
|
||||
|
||||
if(payload != null && payload.startsWith("ACK:"))
|
||||
{
|
||||
try
|
||||
{
|
||||
int bufferSize = Integer.parseInt(payload.substring(4));
|
||||
return bufferSize;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//Do nothing, we couldn't parse the value
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public class NegotiateMaxPayloadSize extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to negotiate maximum payload size
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
NegotiateMaxPayloadSize()
|
||||
{
|
||||
super(UltravoxMessageType.NEGOTIATE_MAX_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
NegotiateMaxPayloadSize(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum payload size.
|
||||
*
|
||||
* @param desiredMaximum payload size (no larger than 16377)
|
||||
* @param minimum acceptable payload size (no larger than 16377)
|
||||
*/
|
||||
public void setMaximumPayloadSize(int desiredMaximum, int minimum)
|
||||
{
|
||||
assert(desiredMaximum <= 16377);
|
||||
assert(minimum <= 16377);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(desiredMaximum).append(":").append(minimum);
|
||||
|
||||
setPayload(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Negotiated maximum payload size from a server response message.
|
||||
*
|
||||
* @return server negotiated maximum payload size, or zero if this is not a server response message, or this is an
|
||||
* error server response message, or the server response message maximum payload size cannot be determined from the
|
||||
* message payload.
|
||||
*/
|
||||
public int getMaximumPayloadSize()
|
||||
{
|
||||
String payload = getPayload();
|
||||
|
||||
if(payload != null && payload.startsWith("ACK:"))
|
||||
{
|
||||
try
|
||||
{
|
||||
int maximumPayloadSize = Integer.parseInt(payload.substring(4));
|
||||
return maximumPayloadSize;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//Do nothing, we couldn't parse the value
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
59
src/audio/broadcast/shoutcast/v2/message/RequestCipher.java
Normal file
59
src/audio/broadcast/shoutcast/v2/message/RequestCipher.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public class RequestCipher extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server for encryption key
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
RequestCipher()
|
||||
{
|
||||
super(UltravoxMessageType.REQUEST_CIPHER);
|
||||
setPayload(SHOUTCAST_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
RequestCipher(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cipher key returned from the server to use for encrypting user id and password credentials
|
||||
*
|
||||
* @return cipher key or null
|
||||
*/
|
||||
public String getCipher()
|
||||
{
|
||||
if(isValidResponse())
|
||||
{
|
||||
return getPayload().replace(VALID_RESPONSE_PAYLOAD_PREFIX, "");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
57
src/audio/broadcast/shoutcast/v2/message/SetupBroadcast.java
Normal file
57
src/audio/broadcast/shoutcast/v2/message/SetupBroadcast.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public class SetupBroadcast extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to specify the stream bit rate
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
SetupBroadcast()
|
||||
{
|
||||
super(UltravoxMessageType.SETUP_BROADCAST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
SetupBroadcast(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the average and maximum bit rates. Set both to the same value for a Constant Bit Rate (CBR) stream.
|
||||
*
|
||||
* @param average bit rate of the stream in kbps
|
||||
* @param maximum bit rate of the stream in kbps
|
||||
*/
|
||||
public void setBitRate(int average, int maximum)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(average).append(":").append(maximum);
|
||||
|
||||
setPayload(sb.toString());
|
||||
}
|
||||
}
|
||||
43
src/audio/broadcast/shoutcast/v2/message/Standby.java
Normal file
43
src/audio/broadcast/shoutcast/v2/message/Standby.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public class Standby extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to notify that configuration is complete, prepare for streaming
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
Standby()
|
||||
{
|
||||
super(UltravoxMessageType.STANDBY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
Standby(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
55
src/audio/broadcast/shoutcast/v2/message/StreamMimeType.java
Normal file
55
src/audio/broadcast/shoutcast/v2/message/StreamMimeType.java
Normal file
@@ -0,0 +1,55 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
import audio.broadcast.BroadcastFormat;
|
||||
|
||||
public class StreamMimeType extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to set stream MIME type
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
StreamMimeType()
|
||||
{
|
||||
super(UltravoxMessageType.STREAM_MIME_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
StreamMimeType(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the broadcast format or MIME type
|
||||
* @param format of the stream
|
||||
*/
|
||||
public void setFormat(BroadcastFormat format)
|
||||
{
|
||||
setPayload(format.getValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public class TerminateBroadcast extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to terminate the broadcast
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
TerminateBroadcast()
|
||||
{
|
||||
super(UltravoxMessageType.TERMINATE_BROADCAST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
TerminateBroadcast(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,19 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2;
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
import bits.BinaryMessage;
|
||||
import com.sun.org.apache.regexp.internal.RE;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public abstract class UltravoxMessage
|
||||
{
|
||||
public static final String SHOUTCAST_VERSION = "2.1";
|
||||
public static final String VALID_RESPONSE_PREFIX = "ACK";
|
||||
public static final String VALID_RESPONSE_PAYLOAD_PREFIX = "ACK:";
|
||||
public static final String ERROR_RESPONSE_PREFIX = "NAK:";
|
||||
|
||||
public static final int[] SYNC = {0,1,2,3,4,5,6,7};
|
||||
public static final int[] RESERVED = {8,9,10,11};
|
||||
public static final int REQUIRED_DELIVERY = 12;
|
||||
@@ -38,25 +44,45 @@ public abstract class UltravoxMessage
|
||||
*/
|
||||
public UltravoxMessage(byte[] data)
|
||||
{
|
||||
mMessage = new BinaryMessage(data);
|
||||
mMessage = new BinaryMessage(data.length * 8);
|
||||
|
||||
int pointer = 0;
|
||||
|
||||
for(byte b: data)
|
||||
{
|
||||
mMessage.setByte(pointer, b);
|
||||
pointer += 8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an empty message of the specified length
|
||||
*
|
||||
* @param ultravoxMessageType type of message
|
||||
* @param length of message in bits (64 minimum)
|
||||
*/
|
||||
public UltravoxMessage(UltravoxMessageType ultravoxMessageType, int length)
|
||||
public UltravoxMessage(UltravoxMessageType ultravoxMessageType)
|
||||
{
|
||||
assert(length >= 64);
|
||||
|
||||
mMessage = new BinaryMessage(length);
|
||||
mMessage = new BinaryMessage(56);
|
||||
|
||||
setSync();
|
||||
setMessageType(ultravoxMessageType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Message byte array
|
||||
*
|
||||
* @return message bytes
|
||||
*/
|
||||
public byte[] getMessage()
|
||||
{
|
||||
return mMessage.toByteArray();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mMessage.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message sync bits to the predefined sync pattern: 01011010 (0x5A)
|
||||
*/
|
||||
@@ -138,8 +164,25 @@ public abstract class UltravoxMessage
|
||||
*/
|
||||
public String getPayload()
|
||||
{
|
||||
//TODO: parse payload field
|
||||
return null;
|
||||
int payloadLength = mMessage.getInt(PAYLOAD_LENGTH);
|
||||
|
||||
byte[] payload = new byte[payloadLength];
|
||||
|
||||
int payloadPointer = 48;
|
||||
|
||||
for(int x = 0; x < payloadLength; x++)
|
||||
{
|
||||
payload[x] = mMessage.getByte(payloadPointer);
|
||||
payloadPointer += 8;
|
||||
}
|
||||
|
||||
//Strip the trailing 0x00 null value from a server response
|
||||
if(payload[payload.length - 1] == 0)
|
||||
{
|
||||
return new String(Arrays.copyOf(payload, payload.length - 1));
|
||||
}
|
||||
|
||||
return new String(payload);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,11 +190,50 @@ public abstract class UltravoxMessage
|
||||
*/
|
||||
public void setPayload(String payload)
|
||||
{
|
||||
//TODO: implement payload, along with trailing 0x00 byte and update overall payload size field.
|
||||
setPayload(payload.getBytes());
|
||||
}
|
||||
|
||||
private void setPayloadLength(int length)
|
||||
public void setPayload(byte[] payload)
|
||||
{
|
||||
//TODO: correctly size the message to payload + 1 byte and set the payload length field
|
||||
int messageBitLength = (7 + payload.length) * 8;
|
||||
|
||||
mMessage.setSize(messageBitLength);
|
||||
mMessage.setInt(payload.length, PAYLOAD_LENGTH);
|
||||
|
||||
int payloadPointer = 48;
|
||||
|
||||
for(byte payloadByte: payload)
|
||||
{
|
||||
mMessage.setByte(payloadPointer, payloadByte);
|
||||
payloadPointer += 8;
|
||||
}
|
||||
|
||||
mMessage.setByte(payloadPointer, (byte)0x00);
|
||||
}
|
||||
|
||||
public boolean isValidResponse()
|
||||
{
|
||||
String payload = getPayload();
|
||||
|
||||
return payload != null && payload.startsWith(VALID_RESPONSE_PREFIX);
|
||||
}
|
||||
|
||||
public boolean isErrorResponse()
|
||||
{
|
||||
String payload = getPayload();
|
||||
|
||||
return payload != null && payload.startsWith(ERROR_RESPONSE_PREFIX);
|
||||
}
|
||||
|
||||
public String getError()
|
||||
{
|
||||
String payload = getPayload();
|
||||
|
||||
if(payload != null)
|
||||
{
|
||||
return payload.replace(ERROR_RESPONSE_PREFIX, "");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2;
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public enum UltravoxMessageClass
|
||||
{
|
||||
@@ -0,0 +1,131 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public class UltravoxMessageFactory
|
||||
{
|
||||
/**
|
||||
* Creates an empty message of the specified message type for sending to the server
|
||||
*/
|
||||
public static UltravoxMessage getMessage(UltravoxMessageType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case AUTHENTICATE_BROADCAST:
|
||||
return new AuthenticateBroadcast();
|
||||
case CONFIGURE_ICY_GENRE:
|
||||
return new ConfigureIcyGenre();
|
||||
case CONFIGURE_ICY_NAME:
|
||||
return new ConfigureIcyName();
|
||||
case CONFIGURE_ICY_PUBLIC:
|
||||
return new ConfigureIcyPublic();
|
||||
case CONFIGURE_ICY_URL:
|
||||
return new ConfigureIcyURL();
|
||||
case FLUSH_CACHED_METADATA:
|
||||
return new FlushCachedMetadata();
|
||||
case MP3_DATA:
|
||||
return new MP3Audio();
|
||||
case NEGOTIATE_BUFFER_SIZE:
|
||||
return new NegotiateBufferSize();
|
||||
case NEGOTIATE_MAX_PAYLOAD_SIZE:
|
||||
return new NegotiateMaxPayloadSize();
|
||||
case REQUEST_CIPHER:
|
||||
return new RequestCipher();
|
||||
case SETUP_BROADCAST:
|
||||
return new SetupBroadcast();
|
||||
case STANDBY:
|
||||
return new Standby();
|
||||
case STREAM_MIME_TYPE:
|
||||
return new StreamMimeType();
|
||||
case TERMINATE_BROADCAST:
|
||||
return new TerminateBroadcast();
|
||||
case XML_METADATA:
|
||||
return new XMLMetadata();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ultravox message from the server response bytes.
|
||||
*
|
||||
* @param bytes in network order (big-endian).
|
||||
*
|
||||
* @return constructed message or null if the message type cannot be determined or is unrecognized
|
||||
*/
|
||||
public static UltravoxMessage getMessage(byte[] bytes)
|
||||
{
|
||||
UltravoxMessageType type = getMessageType(bytes);
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case AUTHENTICATE_BROADCAST:
|
||||
return new AuthenticateBroadcast(bytes);
|
||||
case CONFIGURE_ICY_GENRE:
|
||||
return new ConfigureIcyGenre(bytes);
|
||||
case CONFIGURE_ICY_NAME:
|
||||
return new ConfigureIcyName(bytes);
|
||||
case CONFIGURE_ICY_PUBLIC:
|
||||
return new ConfigureIcyPublic(bytes);
|
||||
case CONFIGURE_ICY_URL:
|
||||
return new ConfigureIcyURL(bytes);
|
||||
case FLUSH_CACHED_METADATA:
|
||||
return new FlushCachedMetadata(bytes);
|
||||
case MP3_DATA:
|
||||
return new MP3Audio(bytes);
|
||||
case NEGOTIATE_BUFFER_SIZE:
|
||||
return new NegotiateBufferSize(bytes);
|
||||
case NEGOTIATE_MAX_PAYLOAD_SIZE:
|
||||
return new NegotiateMaxPayloadSize(bytes);
|
||||
case REQUEST_CIPHER:
|
||||
return new RequestCipher(bytes);
|
||||
case SETUP_BROADCAST:
|
||||
return new SetupBroadcast(bytes);
|
||||
case STANDBY:
|
||||
return new Standby(bytes);
|
||||
case STREAM_MIME_TYPE:
|
||||
return new StreamMimeType(bytes);
|
||||
case TERMINATE_BROADCAST:
|
||||
return new TerminateBroadcast(bytes);
|
||||
case XML_METADATA:
|
||||
return new XMLMetadata(bytes);
|
||||
case UNKNOWN:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the message type from the server response byte array
|
||||
*
|
||||
* @param data in little-endian format
|
||||
* @return message type or UNKNOWN if the message type cannot be determined.
|
||||
*/
|
||||
private static UltravoxMessageType getMessageType(byte[] data)
|
||||
{
|
||||
if(data != null && data.length > 4)
|
||||
{
|
||||
int value = (data[2] << 8) + data[3];
|
||||
|
||||
return UltravoxMessageType.fromValue(value);
|
||||
}
|
||||
|
||||
return UltravoxMessageType.UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public enum UltravoxMessageType
|
||||
{
|
||||
AUTHENTICATE_BROADCAST(0x1001),
|
||||
SETUP_BROADCAST(0x1002),
|
||||
NEGOTIATE_BUFFER_SIZE(0x1003),
|
||||
STANDBY(0x1004),
|
||||
TERMINATE_BROADCAST(0x1005),
|
||||
FLUSH_CACHED_METADATA(0x1006),
|
||||
NEGOTIATE_MAX_PAYLOAD_SIZE(0x1008),
|
||||
REQUEST_CIPHER(0x1009),
|
||||
STREAM_MIME_TYPE(0x1040),
|
||||
CONFIGURE_ICY_NAME(0x1100),
|
||||
CONFIGURE_ICY_GENRE(0x1101),
|
||||
CONFIGURE_ICY_URL(0x1102),
|
||||
CONFIGURE_ICY_PUBLIC(0x1103),
|
||||
XML_METADATA(0x3902),
|
||||
MP3_DATA(0x7000),
|
||||
UNKNOWN(0x0000);
|
||||
|
||||
private int mValue;
|
||||
|
||||
private UltravoxMessageType(int value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public static UltravoxMessageType fromValue(int value)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case 0x1001:
|
||||
return AUTHENTICATE_BROADCAST;
|
||||
case 0x1002:
|
||||
return SETUP_BROADCAST;
|
||||
case 0x1003:
|
||||
return NEGOTIATE_BUFFER_SIZE;
|
||||
case 0x1004:
|
||||
return STANDBY;
|
||||
case 0x1005:
|
||||
return TERMINATE_BROADCAST;
|
||||
case 0x1006:
|
||||
return FLUSH_CACHED_METADATA;
|
||||
case 0x1008:
|
||||
return NEGOTIATE_MAX_PAYLOAD_SIZE;
|
||||
case 0x1009:
|
||||
return REQUEST_CIPHER;
|
||||
case 0x1040:
|
||||
return STREAM_MIME_TYPE;
|
||||
case 0x1100:
|
||||
return CONFIGURE_ICY_NAME;
|
||||
case 0x1101:
|
||||
return CONFIGURE_ICY_GENRE;
|
||||
case 0x1102:
|
||||
return CONFIGURE_ICY_URL;
|
||||
case 0x1103:
|
||||
return CONFIGURE_ICY_PUBLIC;
|
||||
case 0x3902:
|
||||
return XML_METADATA;
|
||||
case 0x7000:
|
||||
return MP3_DATA;
|
||||
default:
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public enum UltravoxMetadata
|
||||
{
|
||||
ALBUM_TITLE("TALB"),
|
||||
ARTIST("TPE1"),
|
||||
AUDIO_SOURCE_WEBPAGE("WOAS"),
|
||||
BROADCAST_CLIENT_APPLICATION("TENC"),
|
||||
COMMENT("COMM"),
|
||||
CUSTOM_TEXT_FIELD("TXXX"),
|
||||
ENCODE_SETTINGS("TSSE"),
|
||||
GENERAL_BINARY_OBJECT("GEOB"),
|
||||
GENRE("TCON"),
|
||||
OWNER("TOWN"),
|
||||
RADIO_STATION("TRSN"),
|
||||
RADIO_STATION_OWNER("TRSO"),
|
||||
RADIO_STATION_WEBPAGE("WORS"),
|
||||
RECORDING_TIME("TDRC"),
|
||||
SOURCE_MEDIA_TYPE("TMED"),
|
||||
TITLE_1("TIT1"),
|
||||
TITLE_2("TIT2"),
|
||||
TITLE_3("TIT3"),
|
||||
URL("URL"),
|
||||
UNKNOWN("UNKNOWN");
|
||||
|
||||
private String mXMLTag;
|
||||
|
||||
private UltravoxMetadata(String xmlTag)
|
||||
{
|
||||
mXMLTag = xmlTag;
|
||||
}
|
||||
|
||||
public String getXMLTag()
|
||||
{
|
||||
return mXMLTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the value as an XML tag stream
|
||||
* @param value to enclose in XML tags
|
||||
* @return xml tag wrapped value
|
||||
*/
|
||||
public String asXML(String value)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<").append(getXMLTag()).append(">");
|
||||
sb.append(value);
|
||||
sb.append("</").append(getXMLTag()).append(">");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
106
src/audio/broadcast/shoutcast/v2/message/XMLMetadata.java
Normal file
106
src/audio/broadcast/shoutcast/v2/message/XMLMetadata.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package audio.broadcast.shoutcast.v2.message;
|
||||
|
||||
public class XMLMetadata extends UltravoxMessage
|
||||
{
|
||||
/**
|
||||
* Client request to server to send cacheable XML metadata
|
||||
*
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*/
|
||||
XMLMetadata()
|
||||
{
|
||||
super(UltravoxMessageType.XML_METADATA);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Server response to client request
|
||||
* Package private constructor. Use the UltravoxMessageFactory for this constructor.
|
||||
*
|
||||
* @param data bytes received from the server
|
||||
*/
|
||||
XMLMetadata(byte[] data)
|
||||
{
|
||||
super(data);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Adds custom metadata (TXXX) tagged value to the metadata set. The metadata value is identified by the
|
||||
// * id parameter.
|
||||
// *
|
||||
// * @param id tag to identify the type of metadata
|
||||
// * @param value for the id tag
|
||||
// */
|
||||
// public void addCustomMetadata(String id, String value)
|
||||
// {
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// sb.append("<").append(UltravoxMetadata.CUSTOM_TEXT_FIELD.getXMLTag());
|
||||
// sb.append(" id=\"").append(id).append("\">");
|
||||
// sb.append(value);
|
||||
// sb.append("</").append(UltravoxMetadata.CUSTOM_TEXT_FIELD.getXMLTag()).append(">");
|
||||
//
|
||||
// mMetadata.add(sb.toString());
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Adds the binary object data to the metadata set.
|
||||
// * @param id tag to identify the binary object
|
||||
// * @param mime type
|
||||
// * @param filename associated with the data
|
||||
// * @param base64Data base-64 encoded binary data to embed in the metadata tag
|
||||
// */
|
||||
// public void addBinaryObject(String id, String mime, String filename, String base64Data)
|
||||
// {
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// sb.append("<").append(UltravoxMetadata.GENERAL_BINARY_OBJECT.getXMLTag());
|
||||
// sb.append(" mime=\"").append(mime).append("\"");
|
||||
// sb.append(" filename=\"").append(filename).append("\"");
|
||||
// sb.append(" id=\"").append(id).append("\"");
|
||||
// sb.append(">");
|
||||
// sb.append(base64Data);
|
||||
// sb.append("</").append(UltravoxMetadata.GENERAL_BINARY_OBJECT.getXMLTag()).append(">");
|
||||
//
|
||||
// mMetadata.add(sb.toString());
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Adds a recording time (TDRC) tagged metadata timestamp
|
||||
// *
|
||||
// * @param timestamp for the recording time
|
||||
// */
|
||||
// public void addRecordingTime(long timestamp)
|
||||
// {
|
||||
// Calendar calendar = new GregorianCalendar();
|
||||
// calendar.setTimeInMillis(timestamp);
|
||||
//
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// sb.append("<TDRC>\n");
|
||||
// sb.append("<YEAR>").append(calendar.get(Calendar.YEAR)).append("</YEAR>\n");
|
||||
// sb.append("<MONTH>").append(calendar.get(Calendar.MONTH)).append("</MONTH>\n");
|
||||
// sb.append("<DAY>").append(calendar.get(Calendar.DAY_OF_MONTH)).append("</DAY>\n");
|
||||
// sb.append("<HOUR>").append(calendar.get(Calendar.HOUR_OF_DAY)).append("</HOUR>\n");
|
||||
// sb.append("<MINUTE>").append(calendar.get(Calendar.MINUTE)).append("</MINUTE>\n");
|
||||
// sb.append("<ZONE>").append(calendar.get(Calendar.ZONE_OFFSET)).append("</ZONE>\n");
|
||||
// sb.append("</TDRC>\n");
|
||||
//
|
||||
// mMetadata.add(sb.toString());
|
||||
// }
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
******************************************************************************/
|
||||
package bits;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.logging.LogManager;
|
||||
|
||||
@@ -283,6 +284,33 @@ public class BinaryMessage extends BitSet
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this message as a little endian byte array. Extra 0 bits will be padded to the end to make the overall
|
||||
* length a multiple of 8.
|
||||
* @return little endian message byte array
|
||||
*/
|
||||
public byte[] toByteArray()
|
||||
{
|
||||
int length = size() / 8;
|
||||
|
||||
if(length * 8 < size())
|
||||
{
|
||||
length ++;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[length];
|
||||
|
||||
int pointer = 0;
|
||||
|
||||
for(int x = 0; x < length; x++)
|
||||
{
|
||||
bytes[x] = getByte(pointer);
|
||||
pointer += 8;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this bitset as a reversed bit order array of integer ones and zeros
|
||||
@@ -421,6 +449,57 @@ public class BinaryMessage extends BitSet
|
||||
return (byte)( value & 0xFF );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the byte value contained between index and index + 7 bit positions
|
||||
* @param index specifying the start of the byte value
|
||||
* @return byte value contained at index <> index + 7 bit positions
|
||||
*/
|
||||
public byte getByte(int index)
|
||||
{
|
||||
assert((index + 7) <= size());
|
||||
|
||||
int value = 0;
|
||||
|
||||
for(int x = 0; x < 8; x++)
|
||||
{
|
||||
value = value << 1;
|
||||
|
||||
if(get(index + x))
|
||||
{
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
return (byte)value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the byte value at index position through index + 7 position.
|
||||
*
|
||||
* @param index bit position where to write the MSB of the byte value
|
||||
* @param value to write at the index through index + 7 bit positions
|
||||
*/
|
||||
public void setByte(int index, byte value)
|
||||
{
|
||||
assert((index + 8) <= size());
|
||||
|
||||
int mask = 0x80;
|
||||
|
||||
for(int x = 0; x < 8; x++)
|
||||
{
|
||||
if((mask & value) == mask)
|
||||
{
|
||||
set(index + x);
|
||||
}
|
||||
else
|
||||
{
|
||||
clear(index + x);
|
||||
}
|
||||
|
||||
mask = mask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the long value represented by the bit array
|
||||
* @param bits - an array of bit positions that will be treated as if they
|
||||
|
||||
184
src/util/XTEA.java
Normal file
184
src/util/XTEA.java
Normal file
@@ -0,0 +1,184 @@
|
||||
/*******************************************************************************
|
||||
* 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 util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class XTEA
|
||||
{
|
||||
//Key Schedule = (sqrt(5) 1) * 2^31
|
||||
private static final int K = 0x9E3779B9;
|
||||
|
||||
//Byte mask values
|
||||
private static final int MASK0 = 0xFF;
|
||||
private static final int MASK1 = 0xFF00;
|
||||
private static final int MASK2 = 0xFF0000;
|
||||
private static final int MASK3 = 0xFF000000;
|
||||
|
||||
//Pre-calculated sub keys derived from encryption key
|
||||
private int mSubKeys1[] = new int[32];
|
||||
private int mSubKeys2[] = new int[32];
|
||||
|
||||
/**
|
||||
* XTEA block cipher encryption using 128 bit encryption key and 64 bit plain text block size with 32 Feistel rounds.
|
||||
* Shorter keys and/or plaintext values can be zero padded to the specified lengths.
|
||||
*
|
||||
* Algorithm source code adapted from: https://people.rit.edu/rab3106/CryptoReport.pdf (as accessed Sep. 2016)
|
||||
*
|
||||
* The encryption key can be changed at any point by invoking the setKey() method.
|
||||
*
|
||||
* @param key for encryption
|
||||
*/
|
||||
public XTEA(String key)
|
||||
{
|
||||
setKey(Arrays.copyOf(key.getBytes(), 16));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the key for this block cipher. Key must be 128 bits or 16 bytes. The key value can be zero padded to
|
||||
* achieve the specified key length.
|
||||
*
|
||||
* @param key encryption key to use
|
||||
*/
|
||||
public void setKey(byte[] key)
|
||||
{
|
||||
assert(key.length == 16);
|
||||
|
||||
int keyInts[] = new int[8];
|
||||
|
||||
//Convert the key to an array of integers
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
keyInts[i / 4] = ((key[i + 3] << 0)) & MASK0 | ((key[i + 2]) << 8) & MASK1 |
|
||||
((key[i + 1]) << 16) & MASK2 | ((key[i + 0]) << 24) & MASK3;
|
||||
}
|
||||
|
||||
//Calculate subkeys for each Feistel round
|
||||
int sum, cycle;
|
||||
|
||||
for (sum = 0, cycle = 0; cycle < 32; cycle++)
|
||||
{
|
||||
//Calculate round 1 subkey
|
||||
mSubKeys1[cycle] = keyInts[sum & 3];
|
||||
|
||||
//Add K to sum
|
||||
sum += K;
|
||||
|
||||
//Calculate round 2 subkey
|
||||
mSubKeys2[cycle] = keyInts[(sum >>> 11) & 3];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the plaintext argument. Textual byte array must be 64 bits or 8 bytes in length and can be zero padded
|
||||
* to achieve the specified length. Encrypted text byte array replaces the original plaintext byte array.
|
||||
*
|
||||
* @param plainText to encrypt
|
||||
* @return cipher text
|
||||
*/
|
||||
public byte[] encrypt(byte[] plainText)
|
||||
{
|
||||
assert(plainText.length == 8);
|
||||
|
||||
//Convert the 8 bytes of plaintext to 2 integers
|
||||
int result0 = (plainText[3] << 0) & MASK0 | (plainText[2] << 8) & MASK1 |
|
||||
(plainText[1] << 16) & MASK2 | (plainText[0] << 24) & MASK3;
|
||||
int result1 = (plainText[7] << 0) & MASK0 | (plainText[6] << 8) & MASK1 |
|
||||
(plainText[5] << 16) & MASK2 | (plainText[4] << 24) & MASK3;
|
||||
|
||||
//Unrolled loop for all Feistel rounds
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 0) + mSubKeys1[0];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 1) + mSubKeys2[0];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 1) + mSubKeys1[1];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 2) + mSubKeys2[1];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 2) + mSubKeys1[2];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 3) + mSubKeys2[2];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 3) + mSubKeys1[3];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 4) + mSubKeys2[3];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 4) + mSubKeys1[4];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 5) + mSubKeys2[4];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 5) + mSubKeys1[5];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 6) + mSubKeys2[5];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 6) + mSubKeys1[6];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 7) + mSubKeys2[6];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 7) + mSubKeys1[7];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 8) + mSubKeys2[7];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 8) + mSubKeys1[8];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 9) + mSubKeys2[8];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 9) + mSubKeys1[9];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 10) + mSubKeys2[9];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 10) + mSubKeys1[10];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 11) + mSubKeys2[10];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 11) + mSubKeys1[11];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 12) + mSubKeys2[11];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 12) + mSubKeys1[12];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 13) + mSubKeys2[12];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 13) + mSubKeys1[13];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 14) + mSubKeys2[13];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 14) + mSubKeys1[14];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 15) + mSubKeys2[14];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 15) + mSubKeys1[15];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 16) + mSubKeys2[15];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 16) + mSubKeys1[16];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 17) + mSubKeys2[16];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 17) + mSubKeys1[17];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 18) + mSubKeys2[17];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 18) + mSubKeys1[18];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 19) + mSubKeys2[18];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 19) + mSubKeys1[19];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 20) + mSubKeys2[19];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 20) + mSubKeys1[20];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 21) + mSubKeys2[20];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 21) + mSubKeys1[21];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 22) + mSubKeys2[21];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 22) + mSubKeys1[22];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 23) + mSubKeys2[22];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 23) + mSubKeys1[23];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 24) + mSubKeys2[23];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 24) + mSubKeys1[24];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 25) + mSubKeys2[24];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 25) + mSubKeys1[25];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 26) + mSubKeys2[25];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 26) + mSubKeys1[26];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 27) + mSubKeys2[26];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 27) + mSubKeys1[27];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 28) + mSubKeys2[27];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 28) + mSubKeys1[28];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 29) + mSubKeys2[28];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 29) + mSubKeys1[29];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 30) + mSubKeys2[29];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 30) + mSubKeys1[30];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 31) + mSubKeys2[30];
|
||||
result0 += (result1 << 4 ^ result1 >>> 5) + result1 ^ (K * 31) + mSubKeys1[31];
|
||||
result1 += (result0 << 4 ^ result0 >>> 5) + result0 ^ (K * 32) + mSubKeys2[31];
|
||||
|
||||
byte[] cipherText = new byte[8];
|
||||
|
||||
cipherText[0] = (byte)(result0 >>> 24);
|
||||
cipherText[1] = (byte)(result0 >>> 16);
|
||||
cipherText[2] = (byte)(result0 >>> 8);
|
||||
cipherText[3] = (byte)(result0 >>> 0);
|
||||
cipherText[4] = (byte)(result1 >>> 24);
|
||||
cipherText[5] = (byte)(result1 >>> 16);
|
||||
cipherText[6] = (byte)(result1 >>> 8);
|
||||
cipherText[7] = (byte)(result1 >>> 0);
|
||||
|
||||
return cipherText;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user