All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.antmedia.settings.ServerSettings Maven / Gradle / Ivy

Go to download

Ant Media Server supports RTMP, RTSP, MP4, HLS, WebRTC, Adaptive Streaming, etc.

There is a newer version: 2.11.3
Show newest version
package io.antmedia.settings;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.bytedeco.ffmpeg.global.avutil;
import org.red5.server.tomcat.TomcatConnector;
import org.red5.server.tomcat.TomcatLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.PropertySource;
import org.apache.catalina.util.NetMask;
import org.webrtc.Logging;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import io.antmedia.licence.ILicenceService;

@PropertySource("/conf/red5.properties")
@JsonIgnoreProperties(ignoreUnknown=true)
public class ServerSettings implements ApplicationContextAware, Serializable {

	private static final long serialVersionUID = 1L;

	public static final String BEAN_NAME = "ant.media.server.settings";

	private static final String SETTINGS_HEART_BEAT_ENABLED = "server.heartbeatEnabled";

	private static final String SETTINGS_USE_GLOBAL_IP = "useGlobalIp";

	private static final String SETTINGS_PROXY_ADDRESS = "proxy.address";

	private static final String SETTINGS_NODE_GROUP = "nodeGroup";

	public static final String LOG_LEVEL_ALL = "ALL";
	public static final String LOG_LEVEL_TRACE = "TRACE";
	public static final String LOG_LEVEL_DEBUG = "DEBUG";
	public static final String LOG_LEVEL_INFO = "INFO";
	public static final String LOG_LEVEL_WARN = "WARN";
	public static final String LOG_LEVEL_ERROR = "ERROR";
	public static final String LOG_LEVEL_OFF = "OFF";

	public static final String DEFAULT_NODE_GROUP = "default";


	private static final String SETTINGS_CPU_MEASUREMENT_PERIOD_MS = "server.cpu_measurement_period_ms";

	private static final String SETTINGS_CPU_MEASUREMENT_WINDOW_SIZE = "server.cpu_measurement_window_size";

	private static final String SETTINGS_SERVER_DEFAULT_HTTP_PORT = "http.port";

	private static final String SETTINGS_ORIGIN_PORT = "server.origin_port";

	private static final String SETTINGS_SRT_PORT = "server.srt_port";

	private static final String SETTINGS_RTMP_PORT = "rtmp.port";

	private static final String ALLOWED_DASH_BOARD_CIDR = "server.allowed_dashboard_CIDR";

	private static final String SETTINGS_NATIVE_LOG_LEVEL = "nativeLogLevel";

	private static final String SETTINGS_LOG_LEVEL = "logLevel";

	private static final String SETTINGS_LICENSE_KEY = "server.licence_key";

	private static final String SETTINGS_SERVER_NAME = "server.name";

	private static final String SETTINGS_MARKET_PLACE_NAME = "server.marketplace";

	public static final String SETTINGS_JWT_SERVER_SECRET_KEY = "server.jwtServerSecretKey";

	/** jwt server filter control*/
	public static final String SETTINGS_JWT_SERVER_CONTROL_ENABLED = "server.jwtServerControlEnabled";

	public static final String SETTINGS_JWKS_URL = "server.jwksURL";

	private static final String SETTINGS_SERVER_STATUS_WEBHOOK_URL = "server.statusWebHookURL";

	/**
	 * The IP filter that is allowed to access the web panel of Ant Media Server
	 */
	@Value( "${"+ALLOWED_DASH_BOARD_CIDR+":'0.0.0.0/0'}" )
	private String allowedDashboardCIDR;

	@JsonIgnore
	private transient Queue allowedCIDRList = new ConcurrentLinkedQueue<>();


	private static Logger logger = LoggerFactory.getLogger(ServerSettings.class);

	private static String localHostAddress;

	private static String globalHostAddress;

	private String hostAddress;

	/**
	 * Fully Qualified Domain Name of the Server. 
	 * It's used in SSL as well.
	 */
	@Value( "${"+SETTINGS_SERVER_NAME+":#{null}}" )
	private String serverName;

	/**
	 * Customer License Key
	 */
	@Value( "${"+SETTINGS_LICENSE_KEY+":#{null}}" )
	private String licenceKey;

	/**
	 * The setting for customized marketplace build.
	 * It's initialized by getting the value from the LicenceBean
	 */
	private boolean buildForMarket = false;

	/**
	 * Name of the marketplace
	 */
	@Value( "${"+SETTINGS_MARKET_PLACE_NAME+":#{null}}" )
	private String marketplace;


	@Value( "${"+SETTINGS_LOG_LEVEL+":'INFO'}" )
	private String logLevel = null;

	/**
	 * if the license is offline. It checks license key against hardware
	 * So license key should be provided by Ant Media specifically.
	 * It's initialized by getting the value from the LicenceBean
	 */
	private boolean offlineLicense = false;

	/**
	 * Native Log Level is used for ffmpeg and WebRTC logs
	 */
	@Value( "${"+SETTINGS_NATIVE_LOG_LEVEL+":'ERROR'}" )
	private String nativeLogLevel = LOG_LEVEL_ERROR;

	/**
	 * Enable heart beat for Ant Media Server
	 */
	@Value( "${"+SETTINGS_HEART_BEAT_ENABLED+":true}" )
	private boolean heartbeatEnabled; 

	/**
	 * Use global IP address for especially in cluster communication
	 */
	@Value( "${"+SETTINGS_USE_GLOBAL_IP+":false}" )
	private boolean useGlobalIp;

	/**
	 * The proxy IP address and port for license checking. 
	 * If there is a proxy in front of Ant Media Server(reverse proxy) please enter its IP and port
	 * The format will be : for example:
	 * 					 192.168.0.1:3012
	 */
	@Value( "${"+SETTINGS_PROXY_ADDRESS+":#{null}}" )
	private String proxyAddress;


	@Value( "${"+SETTINGS_NODE_GROUP+":"+DEFAULT_NODE_GROUP+"}" )
	private String nodeGroup = DEFAULT_NODE_GROUP;

	private Logging.Severity webrtcLogLevel = Logging.Severity.LS_WARNING;

	/**
	 * CPU load is measured for every period and this measurement is used to understand 
	 * if server has enough CPU to handle new requests
	 */
	@Value( "${"+SETTINGS_CPU_MEASUREMENT_PERIOD_MS+":1000}" )
	private int cpuMeasurementPeriodMs;


	/**
	 * Measured CPU load are added to a list with this size and average of the measure CPU loads
	 * are calculated. It's used to check CPU has enough CPU resource
	 */
	@Value( "${"+SETTINGS_CPU_MEASUREMENT_WINDOW_SIZE+":5}" )
	private int cpuMeasurementWindowSize;

	/**
	 * Server default HTTP port
	 * It's 5080 by default
	 */
	@Value( "${"+SETTINGS_SERVER_DEFAULT_HTTP_PORT+":5080}" )
	private int defaultHttpPort;

	/**
	 * Server JWT Control Enabled to access the REST API of the web panel
	 */
	@Value( "${"+SETTINGS_JWT_SERVER_CONTROL_ENABLED+":false}" )
	private boolean jwtServerControlEnabled;

	/**
	 * Server JWT secret key
	 * "afw7Zz9MqvLiheA5X3GFEKvLWb1JTKC2"
	 *
	 */
	@Value( "${"+SETTINGS_JWT_SERVER_SECRET_KEY+":#{null}}" )
	private String jwtServerSecretKey;

	/*
	 * JWKS URL - it's effective if {@link#jwtControlEnabled} is true
	 *
	 * It's null by default. If it's not null, JWKS is used to filter.
	 * Otherwise it uses JWT
	 */

	@Value( "${" + SETTINGS_JWKS_URL +":#{null}}")
	private String jwksURL;

	/**
	 * The port that is opened by origin in cluster mode.
	 * Edges are connected to the origin through this port.
	 */
	@Value( "${"+SETTINGS_ORIGIN_PORT+":5000}" )
	private int originServerPort;

	/**
	 * The SRT port that server opens to listen incoming SRT connections
	 */
	@Value("${"+SETTINGS_SRT_PORT + ":4200}")
	private int srtPort = 4200;

	/**
	 * Nme of the application which will ingestthe SRT Streams that don't have streamid.
	 */
	@Value( "${appIngestsSrtStreamsWithoutStreamId:LiveApp}" )
	private String appIngestsSrtStreamsWithoutStreamId="LiveApp";

	private boolean sslEnabled = false;
	/**
	 * The RTMP port that server opens to listen incoming RTMP connections
	 */
	@Value("${"+SETTINGS_RTMP_PORT + ":1935}")
	private int rtmpPort = 1935;


	/**
	 * Server status webhook url. It's called for several errors such 
	 * - high resource usage
	 * - Unexpected shutdown
	 */
	@Value("${"+SETTINGS_SERVER_STATUS_WEBHOOK_URL + ":#{null}}")
	private String serverStatusWebHookURL;


	public String getJwksURL() {
		return jwksURL;
	}

	public void setJwksURL(String jwksURL) {
		this.jwksURL = jwksURL;
	}

	public boolean isBuildForMarket() {
		return buildForMarket;
	}

	public void setBuildForMarket(boolean buildForMarket) {
		this.buildForMarket = buildForMarket;
	}

	public String getLicenceKey() {
		return licenceKey;
	}

	public void setLicenceKey(String licenceKey) {
		this.licenceKey = licenceKey;
	}

	public String getServerName() {
		return serverName;
	}

	public void setServerName(String serverName) {
		this.serverName = serverName;
	}

	public String getLogLevel() {
		return logLevel;
	}
	public void setLogLevel(String logLevel) {
		this.logLevel = logLevel;
	}

	public boolean isHeartbeatEnabled() {
		return heartbeatEnabled;
	}

	public void setHeartbeatEnabled(boolean heartbeatEnabled) {
		this.heartbeatEnabled = heartbeatEnabled;
	}

	public  String getHostAddress() {
		if (hostAddress == null ) {
			//which means that context is not initialized yet so that return localhost address
			logger.warn("ServerSettings is not initialized yet so that return local host address: {}", getLocalHostAddress());
			return getLocalHostAddress();
		}
		return hostAddress;
	}

	public static String getGlobalHostAddress()
	{
		if (globalHostAddress == null) 
		{
			try (InputStream in = new URL("http://checkip.amazonaws.com").openStream()){

				globalHostAddress = IOUtils.toString(in, Charset.defaultCharset()).trim();
			} catch (IOException e) {
				logger.error(ExceptionUtils.getStackTrace(e));
			}		
		}

		return globalHostAddress;
	}

	public static String getLocalHostAddress() {

		if (localHostAddress == null) {
			long startTime = System.currentTimeMillis();
			try {
				/*
				 * InetAddress.getLocalHost().getHostAddress() takes long time(5sec in macos) to return.
				 * Let it is run once
				 */
				localHostAddress = InetAddress.getLocalHost().getHostAddress();
			} catch (UnknownHostException e) {
				logger.error(ExceptionUtils.getStackTrace(e));
			}
			long diff = System.currentTimeMillis() - startTime;
			if (diff > 1000) {
				logger.warn("Getting host adress took {}ms. it's cached now and will return immediately from now on. You can "
						+ " alternatively set serverName in conf/red5.properties file ", diff);
			}
		}


		return localHostAddress;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

		String hostAddressEnv = getHostAddressFromEnvironment();
		if (hostAddressEnv != null && !hostAddressEnv.isEmpty()) {
			logger.info("Env host address is {}", hostAddressEnv);
			hostAddress = hostAddressEnv;
		}

		else if (useGlobalIp) {
			hostAddress = getGlobalHostAddress();
			logger.info("Using global host address is {}", hostAddress);
		}
		else {
			//************************************
			//this method may sometimes takes long to return
			//delaying initialization may cause some after issues
			hostAddress = getLocalHostAddress();
			logger.info("Using local host address is {}", hostAddress);
		}

		if (applicationContext.containsBean("tomcat.server")) {
			TomcatLoader tomcatLoader = (TomcatLoader) applicationContext.getBean("tomcat.server");

			List connectors = tomcatLoader.getConnectors();
			for (TomcatConnector tomcatConnector : connectors) {
				if (tomcatConnector.isSecure()) {
					this.sslEnabled = true;
					break;
				}
			}
		}

		if (applicationContext.containsBean(ILicenceService.BeanName.LICENCE_SERVICE.toString())) 
		{

			ILicenceService licenseService = (ILicenceService) applicationContext.getBean(ILicenceService.BeanName.LICENCE_SERVICE.toString());

			if (ILicenceService.LICENCE_TYPE_MARKETPLACE.equals(licenseService.getLicenseType())) {
				buildForMarket = true;
			}
			else if (ILicenceService.LICENCE_TYPE_OFFLINE.equals(licenseService.getLicenseType())) {
				offlineLicense = true;
			}
		}
		setNativeLogLevel(nativeLogLevel);

	}

	public String getHostAddressFromEnvironment() {
		return System.getenv("AMS_HOST_ADDRESS");
	}

	public boolean isUseGlobalIp() {
		return useGlobalIp;
	}

	public void setUseGlobalIp(boolean useGlobalIp) {
		this.useGlobalIp = useGlobalIp;
	}

	public void setProxyAddress(String proxyAddress){
		this.proxyAddress = proxyAddress;
	}

	public String getProxyAddress(){
		return proxyAddress;
	}

	/**
	 * the getAllowedCIDRList and setAllowedCIDRList are synchronized because
	 * ArrayList may throw concurrent modification
	 * 
	 * @param allowedDashboardCIDR
	 */
	public void setAllowedDashboardCIDR(String allowedDashboardCIDR) {
		this.allowedDashboardCIDR = allowedDashboardCIDR;
		allowedCIDRList = new ConcurrentLinkedQueue<>();
		fillFromInput(allowedDashboardCIDR, allowedCIDRList);
	}

	public String getAllowedDashboardCIDR() {
		return allowedDashboardCIDR;
	}

	@JsonIgnore
	public Queue getAllowedCIDRList() {
		if (allowedCIDRList.isEmpty()) {
			fillFromInput(allowedDashboardCIDR, allowedCIDRList);
		}
		return allowedCIDRList;
	}

	/**
	 * Fill a {@link NetMask} list from a string input containing a comma-separated
	 * list of (hopefully valid) {@link NetMask}s.
	 *
	 * @param input  The input string
	 * @param target The list to fill
	 * @return a string list of processing errors (empty when no errors)
	 */
	private List fillFromInput(final String input, final Queue target) {
		target.clear();
		if (input == null || input.isEmpty()) {
			return Collections.emptyList();
		}

		final List messages = new LinkedList<>();
		NetMask nm;

		for (final String s : input.split("\\s*,\\s*")) {
			try {
				nm = new NetMask(s);
				target.add(nm);
			} catch (IllegalArgumentException e) {
				messages.add(s + ": " + e.getMessage());
			}
		}

		return Collections.unmodifiableList(messages);
	}

	public String getNativeLogLevel() {
		return nativeLogLevel;
	}

	public void setNativeLogLevel(String nativeLogLevel) {
		this.nativeLogLevel = nativeLogLevel;
		switch (this.nativeLogLevel) 
		{
		case LOG_LEVEL_ALL:
		case LOG_LEVEL_TRACE:
			webrtcLogLevel = Logging.Severity.LS_VERBOSE;
			avutil.av_log_set_level(avutil.AV_LOG_TRACE);			
			break;
		case LOG_LEVEL_DEBUG:
			webrtcLogLevel = Logging.Severity.LS_VERBOSE;
			avutil.av_log_set_level(avutil.AV_LOG_DEBUG);
			break;
		case LOG_LEVEL_INFO:
			webrtcLogLevel = Logging.Severity.LS_INFO;
			avutil.av_log_set_level(avutil.AV_LOG_INFO);
			break;
		case LOG_LEVEL_WARN:
			webrtcLogLevel = Logging.Severity.LS_WARNING;
			avutil.av_log_set_level(avutil.AV_LOG_WARNING);
			break;
		case LOG_LEVEL_ERROR:
			webrtcLogLevel = Logging.Severity.LS_ERROR;
			avutil.av_log_set_level(avutil.AV_LOG_ERROR);
			break;
		case LOG_LEVEL_OFF:
			webrtcLogLevel = Logging.Severity.LS_NONE;
			avutil.av_log_set_level(avutil.AV_LOG_QUIET);
			break;
		default:
			this.nativeLogLevel = LOG_LEVEL_WARN;
			webrtcLogLevel = Logging.Severity.LS_WARNING;
			avutil.av_log_set_level(avutil.AV_LOG_WARNING);
		}

	}

	public Logging.Severity getWebRTCLogLevel() {
		return webrtcLogLevel;
	}

	public String getNodeGroup() {
		return nodeGroup;
	}

	public void setNodeGroup(String nodeGroup) {
		this.nodeGroup = nodeGroup;
	}

	public int getCpuMeasurementPeriodMs() {
		return cpuMeasurementPeriodMs;
	}

	public void setCpuMeasurementPeriodMs(int cpuMeasurementPeriodMs) {
		this.cpuMeasurementPeriodMs = cpuMeasurementPeriodMs;
	}

	public int getCpuMeasurementWindowSize() {
		return cpuMeasurementWindowSize;
	}

	public void setCpuMeasurementWindowSize(int cpuMeasurementWindowSize) {
		this.cpuMeasurementWindowSize = cpuMeasurementWindowSize;
	}

	public int getDefaultHttpPort() {
		return defaultHttpPort;
	}

	public void setDefaultHttpPort(int defaultHttpPort) {
		this.defaultHttpPort = defaultHttpPort;
	}

	public int getOriginServerPort() {
		return originServerPort;
	}

	public void setOriginServerPort(int originServerPort) {
		this.originServerPort = originServerPort;
	}

	public int getSrtPort() {
		return srtPort;
	}

	public void setSrtPort(int srtPort) {
		this.srtPort = srtPort;
	}
	public int getRtmpPort() {
		return rtmpPort;
	}

	public String getMarketplace() {
		return marketplace;
	}

	public void setMarketplace(String marketplace) {
		this.marketplace = marketplace;
	}

	public String getJwtServerSecretKey() {
		return jwtServerSecretKey;
	}

	public void setJwtServerSecretKey(String jwtServerSecretKey){
		this.jwtServerSecretKey=jwtServerSecretKey;
	}

	public boolean isJwtServerControlEnabled() {
		return jwtServerControlEnabled;
	}

	public void setJwtServerControlEnabled(boolean jwtServerControlEnabled) {
		this.jwtServerControlEnabled = jwtServerControlEnabled;
	}

	public boolean isSslEnabled() {
		return sslEnabled;
	}

	public void setSslEnabled(boolean sslEnabled) {
		this.sslEnabled = sslEnabled;
	}

	public String getServerStatusWebHookURL() {
		return serverStatusWebHookURL;
	}


	public void setServerStatusWebHookURL(String serverStatusWebHookURL) {
		this.serverStatusWebHookURL = serverStatusWebHookURL;
	}

	public boolean isOfflineLicense() {
		return offlineLicense;
	}

	public void setOfflineLicense(boolean offlineLicense) {
		this.offlineLicense = offlineLicense;
	}

	public String getAppIngestsSrtStreamsWithoutStreamId() {
		return appIngestsSrtStreamsWithoutStreamId;
	}

	public void setAppIngestsSrtStreamsWithoutStreamId(String appIngestsSrtStreamsWithoutStreamId) {
		this.appIngestsSrtStreamsWithoutStreamId = appIngestsSrtStreamsWithoutStreamId;
	}




}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy