diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/broadcastify/BroadcastifyFeedConfiguration.java b/src/main/java/io/github/dsheirer/audio/broadcast/broadcastify/BroadcastifyFeedConfiguration.java
index 91f01275c..f9f9540ee 100644
--- a/src/main/java/io/github/dsheirer/audio/broadcast/broadcastify/BroadcastifyFeedConfiguration.java
+++ b/src/main/java/io/github/dsheirer/audio/broadcast/broadcastify/BroadcastifyFeedConfiguration.java
@@ -1,23 +1,20 @@
/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2024 Dennis Sheirer
*
- * * ******************************************************************************
- * * Copyright (C) 2014-2020 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
- * * *****************************************************************************
+ * 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
+ * ****************************************************************************
*/
package io.github.dsheirer.audio.broadcast.broadcastify;
@@ -35,6 +32,7 @@ public class BroadcastifyFeedConfiguration extends IcecastTCPConfiguration
private final static Logger mLog = LoggerFactory.getLogger(BroadcastifyFeedConfiguration.class);
private int mFeedID;
+ private boolean mVerboseLogging = false;
public BroadcastifyFeedConfiguration()
{
@@ -123,4 +121,15 @@ public void setFeedID(int feedID)
{
mFeedID = feedID;
}
+
+ @JacksonXmlProperty(isAttribute = true, localName = "verbose_logging")
+ public boolean isVerboseLogging()
+ {
+ return mVerboseLogging;
+ }
+
+ public void setVerboseLogging(boolean verboseLogging)
+ {
+ mVerboseLogging = verboseLogging;
+ }
}
diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java
index 851e945c6..b5e28c10d 100644
--- a/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java
+++ b/src/main/java/io/github/dsheirer/audio/broadcast/icecast/IcecastTCPAudioBroadcaster.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2022 Dennis Sheirer
+ * Copyright (C) 2014-2024 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
@@ -20,6 +20,7 @@
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.audio.broadcast.BroadcastState;
+import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyFeedConfiguration;
import io.github.dsheirer.audio.broadcast.icecast.codec.IcecastCodecFactory;
import io.github.dsheirer.audio.convert.InputAudioFormat;
import io.github.dsheirer.audio.convert.MP3AudioConverter;
@@ -39,6 +40,8 @@
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.filter.logging.LogLevel;
+import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,6 +59,7 @@ public class IcecastTCPAudioBroadcaster extends IcecastAudioBroadcaster
private IoSession mStreamingSession = null;
private long mLastConnectionAttempt = 0;
+ private boolean mVerboseLogging = false;
private AtomicBoolean mConnecting = new AtomicBoolean();
/**
@@ -73,6 +77,11 @@ public IcecastTCPAudioBroadcaster(IcecastTCPConfiguration configuration, InputAu
MP3Setting mp3Setting, AliasModel aliasModel)
{
super(configuration, inputAudioFormat, mp3Setting, aliasModel);
+
+ if(configuration instanceof BroadcastifyFeedConfiguration broadcastify)
+ {
+ mVerboseLogging = broadcastify.isVerboseLogging();
+ }
}
/**
@@ -132,9 +141,12 @@ private boolean connect()
mSocketConnector = new NioSocketConnector();
mSocketConnector.getSessionConfig().setWriteTimeout(WRITE_TIMEOUT_SECONDS);
-// LoggingFilter loggingFilter = new LoggingFilter(IcecastTCPAudioBroadcaster.class);
-// loggingFilter.setMessageSentLogLevel(LogLevel.NONE);
-// mSocketConnector.getFilterChain().addLast("logger", loggingFilter);
+ if(mVerboseLogging)
+ {
+ LoggingFilter loggingFilter = new LoggingFilter(IcecastTCPAudioBroadcaster.class);
+ loggingFilter.setMessageSentLogLevel(LogLevel.NONE);
+ mSocketConnector.getFilterChain().addLast("logger", loggingFilter);
+ }
mSocketConnector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new IcecastCodecFactory()));
@@ -148,6 +160,10 @@ private boolean connect()
@Override
public void run()
{
+ if(mVerboseLogging)
+ {
+ mLog.info("Attempting connection ...");
+ }
setBroadcastState(BroadcastState.CONNECTING);
try
@@ -156,19 +172,42 @@ public void run()
.connect(new InetSocketAddress(getBroadcastConfiguration().getHost(),
getBroadcastConfiguration().getPort()));
+ if(mVerboseLogging)
+ {
+ mLog.info("Socket created - asynchronous connect requested - entering wait period");
+ }
+
boolean connected = future.await(CONNECTION_ATTEMPT_TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS);
if(connected)
{
+ if(mVerboseLogging)
+ {
+ mLog.info("Connected.");
+ }
+
mStreamingSession = future.getSession();
mConnecting.set(false);
return;
}
+ else
+ {
+ if(mVerboseLogging)
+ {
+ mLog.info("Not Connected. Connection attempt timeout [" + CONNECTION_ATTEMPT_TIMEOUT_MILLISECONDS + "ms] exceeded");
+ }
+ }
}
catch(RuntimeIoException rioe)
{
if(rioe.getCause() instanceof SocketException)
{
+ if(mVerboseLogging)
+ {
+ mLog.info("Socket error. This usually indicates sdrtrunk can't reach the server " +
+ "address over the current network connection. Setting state to " +
+ "NETWORK UNAVAILABLE", rioe);
+ }
setBroadcastState(BroadcastState.NETWORK_UNAVAILABLE);
mConnecting.set(false);
return;
@@ -176,12 +215,22 @@ public void run()
}
catch(UnresolvedAddressException uae)
{
+ if(mVerboseLogging)
+ {
+ mLog.info("Unresolved Address error. This means the domain name services can't resolve " +
+ "the server URL to an IP address. Setting state to NETWORK UNAVAILABLE", uae);
+ }
+
setBroadcastState(BroadcastState.NETWORK_UNAVAILABLE);
mConnecting.set(false);
return;
}
catch(Exception e)
{
+ if(mVerboseLogging)
+ {
+ mLog.info("Unknown error. An error occurred while attempting to connect to the server.", e);
+ }
mLog.error("Error", e);
//Disregard ... we'll disconnect and try again
}
@@ -190,6 +239,11 @@ public void run()
mLog.error("Throwable error caught", t);
}
+ if(mVerboseLogging)
+ {
+ mLog.info("Starting disconnect sequence since an error occurred while trying to connect.");
+ }
+
disconnect();
mConnecting.set(false);
}
@@ -209,6 +263,11 @@ public void disconnect()
{
if(connected() && mStreamingSession != null)
{
+ if(mVerboseLogging)
+ {
+ mLog.info("Routine disconnect requested from a connected state with a non-null streaming session");
+ }
+
mStreamingSession.closeNow();
}
else
@@ -217,6 +276,12 @@ public void disconnect()
//want to preserve the error state that got us here, so the user can see it.
if(!getBroadcastState().isErrorState())
{
+ if(mVerboseLogging)
+ {
+ mLog.info("Disconnect requested - previous non-error state was [" + getBroadcastState() +
+ "] - changing state to DISCONNECTED");
+ }
+
setBroadcastState(BroadcastState.DISCONNECTED);
}
@@ -282,12 +347,23 @@ public void sessionOpened(IoSession session) throws Exception
mInlineActive = false;
}
+ if(mVerboseLogging)
+ {
+ mLog.info("Session opened. Sending: " + sb);
+ }
+
+
session.write(sb.toString());
}
@Override
public void sessionClosed(IoSession session) throws Exception
{
+ if(mVerboseLogging)
+ {
+ mLog.info("Session closed. Setting connecting flag to false.");
+ }
+
mLastConnectionAttempt = System.currentTimeMillis();
//If there is already an error state, don't override it. Otherwise, set state to disconnected
@@ -311,15 +387,23 @@ public void exceptionCaught(IoSession session, Throwable cause) throws Exception
mLog.error("[" + getStreamName() + "] Broadcast error", cause);
}
+ if(mVerboseLogging)
+ {
+ mLog.info("Session error caught.", cause);
+ }
+
disconnect();
}
@Override
public void messageReceived(IoSession session, Object object) throws Exception
{
- if(object instanceof String)
+ if(object instanceof String message)
{
- String message = (String) object;
+ if(mVerboseLogging)
+ {
+ mLog.info("Message Received [" + message + "]");
+ }
if(message != null && !message.trim().isEmpty())
{
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyStreamEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyStreamEditor.java
index f79228044..01477fab4 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyStreamEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyStreamEditor.java
@@ -1,23 +1,20 @@
/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2024 Dennis Sheirer
*
- * * ******************************************************************************
- * * Copyright (C) 2014-2020 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
- * * *****************************************************************************
+ * 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
+ * ****************************************************************************
*/
package io.github.dsheirer.gui.playlist.streaming;
@@ -31,7 +28,9 @@
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
+import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
+import org.controlsfx.control.ToggleSwitch;
/**
* Broadcastify streaming configuration editor
@@ -39,6 +38,7 @@
public class BroadcastifyStreamEditor extends AbstractStreamEditor
{
private GridPane mEditorPane;
+ private ToggleSwitch mVerboseLoggingToggle;
private TextField mMountPointTextField;
private IntegerTextField mFeedIdTextField;
@@ -54,16 +54,19 @@ public void setItem(BroadcastifyFeedConfiguration item)
getFeedIdTextField().setDisable(item == null);
getMountPointTextField().setDisable(item == null);
+ getVerboseLoggingToggle().setDisable(item == null);
if(item != null)
{
getFeedIdTextField().set(item.getFeedID());
getMountPointTextField().setText(item.getMountPoint());
+ getVerboseLoggingToggle().setSelected(item.isVerboseLogging());
}
else
{
getFeedIdTextField().set(0);
getMountPointTextField().setText(null);
+ getVerboseLoggingToggle().setSelected(false);
}
modifiedProperty().set(false);
@@ -76,6 +79,7 @@ public void save()
{
getItem().setFeedID(getFeedIdTextField().get());
getItem().setMountPoint(getMountPointTextField().getText());
+ getItem().setVerboseLogging(getVerboseLoggingToggle().isSelected());
}
super.save();
@@ -178,11 +182,32 @@ protected GridPane getEditorPane()
GridPane.setConstraints(getFeedIdTextField(), 1, 5);
getEditorPane().getChildren().add(getFeedIdTextField());
+
+ Label loggingLabel = new Label("Verbose Logging");
+ GridPane.setHalignment(loggingLabel, HPos.RIGHT);
+ GridPane.setConstraints(loggingLabel, 2, 5);
+ getEditorPane().getChildren().add(loggingLabel);
+
+ GridPane.setConstraints(getVerboseLoggingToggle(), 3, 5);
+ getEditorPane().getChildren().add(getVerboseLoggingToggle());
}
return mEditorPane;
}
+ private ToggleSwitch getVerboseLoggingToggle()
+ {
+ if(mVerboseLoggingToggle == null)
+ {
+ mVerboseLoggingToggle = new ToggleSwitch();
+ mVerboseLoggingToggle.setTooltip(new Tooltip("Turn on additional logging for connection troubleshooting"));
+ mVerboseLoggingToggle.setDisable(true);
+ mVerboseLoggingToggle.selectedProperty().addListener((ob, ol, ne) -> modifiedProperty().set(true));
+ }
+
+ return mVerboseLoggingToggle;
+ }
+
private TextField getMountPointTextField()
{
if(mMountPointTextField == null)