Get Framerate for Live Stream

I searched the forums and server-api. All I could find was how to grab the bitrate for a stream. I’m looking for the framerate AKA “FPS” for a live stream.

I’m setting a task that logs that publisher’s FPS every 5 minutes.

Currently looked at:

StreamUtils.getStreamBitrate(stream) which gives me 0.0

stream.getReceiveVideoFPS() which gives me -1

Any help would be much appreciated.

Take a look at the IOPerformanceCounter API. You can attach this to stream or session to get much info:

Also, you can get metadata from live stream with IMediaStreamActionNotify3.onCodecInfoVideo() and IMediaStreamActionNotify3.onVideoInfoVideo()

Richard

You will usually find FPS in the metadata actually, and there is IMediaStreamActionNotify3.onMetadata() that I forgot to mention, sorry. This is just what the encoder is reporting in the stream’s metadata, but should be accurate for the source. Frame rate at the client can be variable.

Richard

I tried IMediaStream.getReceiveVideoFPS(), and it does seem to be not implemented. It sounds like it would provide what you want, but I am not sure and I don’t have a time frame for when it might be implemented

Meanwhile, I think IOPerformanceCounter.getMessagesInBytesRate() is the best metric available for your needs. You can monitor with an Application module (From the Wowza Collection) like this:

/**
 * Wowza server software and all components Copyright 2006 - 2014, Wowza Media Systems, LLC, licensed pursuant to the Wowza Media Software End User License Agreement.
 */
package com.wowza.wms.plugin.collection.module;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import com.wowza.util.IOPerformanceCounter;
import com.wowza.wms.amf.AMFPacket;
import com.wowza.wms.application.IApplicationInstance;
import com.wowza.wms.application.WMSProperties;
import com.wowza.wms.logging.WMSLogger;
import com.wowza.wms.logging.WMSLoggerFactory;
import com.wowza.wms.logging.WMSLoggerIDs;
import com.wowza.wms.module.ModuleBase;
import com.wowza.wms.stream.IMediaStream;
import com.wowza.wms.stream.IMediaStreamActionNotify;
import com.wowza.wms.stream.IMediaStreamActionNotify2;
public class ModuleLimitPublishedStreamBandwidth extends ModuleBase implements IMediaStreamActionNotify
{
	private class MonitorStream
	{
		public Timer mTimer;
		public TimerTask mTask;
		public IMediaStream stream;
	
		public MonitorStream(IMediaStream s)
		{
			stream = s;
			mTask = new TimerTask()
			{
				public void run()
				{
	
					if (stream == null)
						stop();
	
					IOPerformanceCounter perf = stream.getMediaIOPerformance();
					Double bitrate = perf.getMessagesInBytesRate() * 8 * .001;
	
					if (debugLog)
						logger.info(MODULE_NAME + ".MonitorStream.run '" + stream.getName() + "' BitRate: " + Math.round(Math.floor(bitrate)) + "kbs, MaxBitrate:" + maxBitrate, WMSLoggerIDs.CAT_application, WMSLoggerIDs.EVT_comment);
	
					if (bitrate > maxBitrate && maxBitrate > 0)
					{
						logger.info(MODULE_NAME + ".MonitorStream.run Sent NetStream.Publish.Rejected to " + stream.getClientId() + " stream name: " + stream.getName() + ", BitRate: " + Math.round(Math.floor(bitrate)) + "kbs", WMSLoggerIDs.CAT_application, WMSLoggerIDs.EVT_comment);
						sendStreamOnStatusError(stream, "NetStream.Publish.Rejected", "bitrate too high: " + Math.round(Math.floor(bitrate)) + "kbs");
	
						stream.getClient().setShutdownClient(true);
					}
				}
			};
			mTimer = new Timer();
		}
	
		public void start()
		{
			if (mTimer == null)
				mTimer = new Timer();
			mTimer.scheduleAtFixedRate(mTask, new Date(), interval);
		}
	
		public void stop()
		{
			if (mTimer != null)
			{
				mTimer.cancel();
				mTimer = null;
			}
		}
	}
	public static final String MODULE_NAME = "ModuleLimitPublishedStreamBandwidth";
	public static final String PROP_NAME_PREFIX = "limitPublishedStreamBandwidth";
	
	int maxBitrate = 800; // 0 = no limit
	int sustained = 10;
	int interval = 5000;
	boolean debugLog = false;
	
	WMSLogger logger = null;
	public void onAppStart(IApplicationInstance appInstance)
	{
		this.logger = WMSLoggerFactory.getLoggerObj(appInstance);
		
		// old prop name
		maxBitrate = appInstance.getProperties().getPropertyInt("MaxBitrate", maxBitrate);
		// new prop name
		maxBitrate = appInstance.getProperties().getPropertyInt(PROP_NAME_PREFIX + "MaxBitrate", maxBitrate);
		
		// old prop name
		debugLog = appInstance.getProperties().getPropertyBoolean("StreamMonitorLogging", debugLog);
		// new prop name
		debugLog = appInstance.getProperties().getPropertyBoolean(PROP_NAME_PREFIX + "DebugLog", debugLog);
		if(logger.isDebugEnabled())
			debugLog = true;
		
		sustained = appInstance.getProperties().getPropertyInt(PROP_NAME_PREFIX + "Sustained", sustained); // not used
		
		logger.info(MODULE_NAME + " MaxBitrate: " + maxBitrate, WMSLoggerIDs.CAT_application, WMSLoggerIDs.EVT_comment);
	}
	public void onStreamCreate(IMediaStream stream)
	{
		stream.addClientListener(this);
	}
	public void onStreamDestroy(IMediaStream stream)
	{
		stream.removeClientListener(this);
	}
	public void onUnPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend)
	{
		logger.info(MODULE_NAME + ".onUnPublish: " + stream.getName());
		WMSProperties props = stream.getProperties();
		MonitorStream monitor;
		synchronized(props)
		{
			monitor = (MonitorStream)props.get("monitor");
		}
		if (monitor != null)
			monitor.stop();
	}
	public void onPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend)
	{
		MonitorStream monitor = new MonitorStream(stream);
		WMSProperties props = stream.getProperties();
		synchronized(props)
		{
			props.put("monitor", monitor);
		}
		monitor.start();
	}
	public void onPlay(IMediaStream stream, String streamName, double playStart, double playLen, int playReset)
	{
	}
	public void onSeek(IMediaStream stream, double location)
	{
	}
	public void onStop(IMediaStream stream)
	{
	}
	public void onPause(IMediaStream stream, boolean isPause, double location)
	{
	}
}

Richard

Actually, I think there is a better way. I will get back to you on this today.

Richard

Take a look at this example (from soon to be published article:

package test;
import com.wowza.wms.amf.AMFDataList;
import com.wowza.wms.amf.AMFPacket;
import com.wowza.wms.application.IApplicationInstance;
import com.wowza.wms.client.IClient;
import com.wowza.wms.module.ModuleBase;
import com.wowza.wms.request.RequestFunction;
import com.wowza.wms.stream.IMediaStream;
import com.wowza.wms.stream.IMediaStreamLivePacketNotify;
public class ModuleLiveFrameRate extends ModuleBase
{
	class PacketListener implements IMediaStreamLivePacketNotify
	{
		/**
		* onLivePacket is called for every packet that is received for the live stream before the packet is processed for playback.
		* It is very important that this method returns quickly and is not delayed in any way.
		*/
		@Override
		public void onLivePacket(IMediaStream stream, AMFPacket packet)
		{
			if (packet.isVideo())
			{
				// packet.getTimecode(); returns the elapsed time, in milliseconds, between this packet and the last packet of the same type.
				double fps = (double)1000 / packet.getTimecode();
				stream.getProperties().setProperty("currentFPS", new Double(fps));
				getLogger().info("FPS: " + fps); // this makes a lot of logging.
			}
		}
	}
	private PacketListener packetListener = new PacketListener();
	private IApplicationInstance appInstance;
	public void onAppStart(IApplicationInstance appInstance)
	{
		this.appInstance = appInstance;
	}
	public void onStreamCreate(IMediaStream stream)
	{
		stream.addLivePacketListener(packetListener);
	}
	public void onStreamDestroy(IMediaStream stream)
	{
		stream.removeLivePacketListener(packetListener);
	}
	public void getCurrentFPS(IClient client, RequestFunction function, AMFDataList params)
	{
		double fps = 0;
		String streamName = getParamString(params, PARAM1);
		if (streamName != null)
		{
			fps = getCurrentFPS(streamName);
		}
		sendResult(client, params, fps);
	}
	public double getCurrentFPS(String streamName)
	{
		double fps = 0;
		IMediaStream stream = appInstance.getStreams().getStream(streamName);
		if (stream != null)
		{
			fps = stream.getProperties().getPropertyDouble("currentFPS", fps);
		}
		return fps;
	}
}

Richard

Very cool! Thank you for the update

Richard

I looked at IOPerformanceCounter API and it look like at on 3.6 there isn’t a framerate property, bitrate yes but don’t see the FPS. Am I missing something?

As far as IMediaStreamActionNotify codecInfoVideo.getFrameRate() returns 0.0. Any reason this value is 0 all the time?

I guess the problem is that the client sets the metadata to a specific FPS. So I was trying to find the actual FPS on the server side and not something that the client can manipulate. That metadata framerate property looks at the camera settings. Because of possible network congestion etc the FPS actually goes lower yet the metadata still says 15. We’ve seen it go down to 3. I want to set-up a task that detects if the FPS gets below 10.

Looks like in the Server API Doc

getReceiveVideoFPS()

Set frame per seconds for video (not currently implemented)

Do you know when that might be implemented?

Thanks Richard. We currently already implement IOPerformanceCounter.getMessagesInBytesRate(). We have a very specific need for FPS. Should I enter a support ticket in so we can track when this method will be available?

I tried IMediaStream.getReceiveVideoFPS(), and it does seem to be not implemented. It sounds like it would provide what you want, but I am not sure and I don’t have a time frame for when it might be implemented

Meanwhile, I think IOPerformanceCounter.getMessagesInBytesRate() is the best metric available for your needs. You can monitor with an Application module (From the Wowza Collection) like this:

/**
 * Wowza server software and all components Copyright 2006 - 2014, Wowza Media Systems, LLC, licensed pursuant to the Wowza Media Software End User License Agreement.
 */
package com.wowza.wms.plugin.collection.module;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import com.wowza.util.IOPerformanceCounter;
import com.wowza.wms.amf.AMFPacket;
import com.wowza.wms.application.IApplicationInstance;
import com.wowza.wms.application.WMSProperties;
import com.wowza.wms.logging.WMSLogger;
import com.wowza.wms.logging.WMSLoggerFactory;
import com.wowza.wms.logging.WMSLoggerIDs;
import com.wowza.wms.module.ModuleBase;
import com.wowza.wms.stream.IMediaStream;
import com.wowza.wms.stream.IMediaStreamActionNotify;
import com.wowza.wms.stream.IMediaStreamActionNotify2;
public class ModuleLimitPublishedStreamBandwidth extends ModuleBase implements IMediaStreamActionNotify
{
	private class MonitorStream
	{
		public Timer mTimer;
		public TimerTask mTask;
		public IMediaStream stream;
	
		public MonitorStream(IMediaStream s)
		{
			stream = s;
			mTask = new TimerTask()
			{
				public void run()
				{
	
					if (stream == null)
						stop();
	
					IOPerformanceCounter perf = stream.getMediaIOPerformance();
					Double bitrate = perf.getMessagesInBytesRate() * 8 * .001;
	
					if (debugLog)
						logger.info(MODULE_NAME + ".MonitorStream.run '" + stream.getName() + "' BitRate: " + Math.round(Math.floor(bitrate)) + "kbs, MaxBitrate:" + maxBitrate, WMSLoggerIDs.CAT_application, WMSLoggerIDs.EVT_comment);
	
					if (bitrate > maxBitrate && maxBitrate > 0)
					{
						logger.info(MODULE_NAME + ".MonitorStream.run Sent NetStream.Publish.Rejected to " + stream.getClientId() + " stream name: " + stream.getName() + ", BitRate: " + Math.round(Math.floor(bitrate)) + "kbs", WMSLoggerIDs.CAT_application, WMSLoggerIDs.EVT_comment);
						sendStreamOnStatusError(stream, "NetStream.Publish.Rejected", "bitrate too high: " + Math.round(Math.floor(bitrate)) + "kbs");
	
						stream.getClient().setShutdownClient(true);
					}
				}
			};
			mTimer = new Timer();
		}
	
		public void start()
		{
			if (mTimer == null)
				mTimer = new Timer();
			mTimer.scheduleAtFixedRate(mTask, new Date(), interval);
		}
	
		public void stop()
		{
			if (mTimer != null)
			{
				mTimer.cancel();
				mTimer = null;
			}
		}
	}
	public static final String MODULE_NAME = "ModuleLimitPublishedStreamBandwidth";
	public static final String PROP_NAME_PREFIX = "limitPublishedStreamBandwidth";
	
	int maxBitrate = 800; // 0 = no limit
	int sustained = 10;
	int interval = 5000;
	boolean debugLog = false;
	
	WMSLogger logger = null;
	public void onAppStart(IApplicationInstance appInstance)
	{
		this.logger = WMSLoggerFactory.getLoggerObj(appInstance);
		
		// old prop name
		maxBitrate = appInstance.getProperties().getPropertyInt("MaxBitrate", maxBitrate);
		// new prop name
		maxBitrate = appInstance.getProperties().getPropertyInt(PROP_NAME_PREFIX + "MaxBitrate", maxBitrate);
		
		// old prop name
		debugLog = appInstance.getProperties().getPropertyBoolean("StreamMonitorLogging", debugLog);
		// new prop name
		debugLog = appInstance.getProperties().getPropertyBoolean(PROP_NAME_PREFIX + "DebugLog", debugLog);
		if(logger.isDebugEnabled())
			debugLog = true;
		
		sustained = appInstance.getProperties().getPropertyInt(PROP_NAME_PREFIX + "Sustained", sustained); // not used
		
		logger.info(MODULE_NAME + " MaxBitrate: " + maxBitrate, WMSLoggerIDs.CAT_application, WMSLoggerIDs.EVT_comment);
	}
	public void onStreamCreate(IMediaStream stream)
	{
		stream.addClientListener(this);
	}
	public void onStreamDestroy(IMediaStream stream)
	{
		stream.removeClientListener(this);
	}
	public void onUnPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend)
	{
		logger.info(MODULE_NAME + ".onUnPublish: " + stream.getName());
		WMSProperties props = stream.getProperties();
		MonitorStream monitor;
		synchronized(props)
		{
			monitor = (MonitorStream)props.get("monitor");
		}
		if (monitor != null)
			monitor.stop();
	}
	public void onPublish(IMediaStream stream, String streamName, boolean isRecord, boolean isAppend)
	{
		MonitorStream monitor = new MonitorStream(stream);
		WMSProperties props = stream.getProperties();
		synchronized(props)
		{
			props.put("monitor", monitor);
		}
		monitor.start();
	}
	public void onPlay(IMediaStream stream, String streamName, double playStart, double playLen, int playReset)
	{
	}
	public void onSeek(IMediaStream stream, double location)
	{
	}
	public void onStop(IMediaStream stream)
	{
	}
	public void onPause(IMediaStream stream, boolean isPause, double location)
	{
	}
}

Richard

Thanks I tested this over the weekend and it works great. Thank you again!

Take a look at this example (from soon to be published article:

package test;
import com.wowza.wms.amf.AMFDataList;
import com.wowza.wms.amf.AMFPacket;
import com.wowza.wms.application.IApplicationInstance;
import com.wowza.wms.client.IClient;
import com.wowza.wms.module.ModuleBase;
import com.wowza.wms.request.RequestFunction;
import com.wowza.wms.stream.IMediaStream;
import com.wowza.wms.stream.IMediaStreamLivePacketNotify;
public class ModuleLiveFrameRate extends ModuleBase
{
	class PacketListener implements IMediaStreamLivePacketNotify
	{
		/**
		* onLivePacket is called for every packet that is received for the live stream before the packet is processed for playback.
		* It is very important that this method returns quickly and is not delayed in any way.
		*/
		@Override
		public void onLivePacket(IMediaStream stream, AMFPacket packet)
		{
			if (packet.isVideo())
			{
				// packet.getTimecode(); returns the elapsed time, in milliseconds, between this packet and the last packet of the same type.
				double fps = (double)1000 / packet.getTimecode();
				stream.getProperties().setProperty("currentFPS", new Double(fps));
				getLogger().info("FPS: " + fps); // this makes a lot of logging.
			}
		}
	}
	private PacketListener packetListener = new PacketListener();
	private IApplicationInstance appInstance;
	public void onAppStart(IApplicationInstance appInstance)
	{
		this.appInstance = appInstance;
	}
	public void onStreamCreate(IMediaStream stream)
	{
		stream.addLivePacketListener(packetListener);
	}
	public void onStreamDestroy(IMediaStream stream)
	{
		stream.removeLivePacketListener(packetListener);
	}
	public void getCurrentFPS(IClient client, RequestFunction function, AMFDataList params)
	{
		double fps = 0;
		String streamName = getParamString(params, PARAM1);
		if (streamName != null)
		{
			fps = getCurrentFPS(streamName);
		}
		sendResult(client, params, fps);
	}
	public double getCurrentFPS(String streamName)
	{
		double fps = 0;
		IMediaStream stream = appInstance.getStreams().getStream(streamName);
		if (stream != null)
		{
			fps = stream.getProperties().getPropertyDouble("currentFPS", fps);
		}
		return fps;
	}
}

Richard