Open totaam opened 4 years ago
2020-06-08_14-37-15.mp4
(815.9 KiB)Found this excellent answer on the gstreamer-devel mailing list: measuring FPS of a source live stream (using identity, elements):
using buffer probes on specific elements:
def measure_elem_stats(self, elem: Gst.Element) -> None:
"""Measure bitrate, fps and latency of gstreamer element"""
stats = GstPipeline.GstElementStats(elem)
add_buffer_probe(elem, "src", self._calc_stats, stats)
add_buffer_probe(elem, "sink", self._probe_in, stats)
def add_buffer_probe(
elem: Gst.Element,
pad_name: str,
func,
data: Optional[object] = None,
) -> bool:
"""Add a buffer probe to pad of element
:param elem: element to probe
:param pad_name: name of the pad to probe, normally 'sink' or 'src'
:param func: callback function called when a buffer is received on the pad
:param data: optional data to pass to probe function, defaults to None
:return: True if the probe was sucessfully installed,
False otherwise.
"""
if not elem:
return False
pad = elem.get_static_pad(pad_name)
if not pad:
# print(f"Couldn't find {elem.name}::{pad_name}")
return False
pad.add_probe(Gst.PadProbeType.BUFFER, func, data)
return True
def _calc_stats(
self, pad: Gst.Pad, info: Gst.PadProbeInfo, stats: GstElementStats
) -> Gst.PadProbeReturn:
"""Callback of buffer probe for measuring bitrate, fps and latency"""
buf = info.get_buffer()
time = self.get_pipe_timestamp()
diff = time - buf.pts # total latency from src
elapsed = time - stats.perf_sink.prev_time
if not is_clock_valid(stats.perf_sink.prev_time):
stats.perf_sink.prev_time = time
elif is_clock_valid(time) and elapsed > Gst.SECOND:
# frames per second
factor_n = elapsed / Gst.MSECOND
factor_d = Gst.SECOND / Gst.MSECOND
# bytes per second
bps = stats.perf_sink.bps * factor_d / factor_n
stats.bitrate = bps / (1024 * 1024 / 8) # Mbps
stats.fps = stats.perf_sink.frame_count * factor_d / factor_n
latency = stats.perf_sink.latency / stats.perf_sink.frame_count
stats.elem_latency = latency - stats.src_latency
self.__logger.debug(
f"{stats.elem_name}: "
f"bitrate={stats.bitrate} Mbps "
f"fps={stats.fps} "
f"latency elem={timestamp_msecs(stats.elem_latency)} ms "
f"total={timestamp_msecs(latency)} ms"
)
self._plot_stats(time, stats)
stats.perf_sink.reset(time)
stats.perf_sink.frame_count += 1
stats.perf_sink.bps += buf.get_size()
stats.perf_sink.latency += diff
return Gst.PadProbeReturn.OK
def _probe_in(
self, pad: Gst.Pad, info: Gst.PadProbeInfo, stats: GstElementStats
) -> Gst.PadProbeReturn:
"""Callback of buffer probe for measuring processing time"""
buf = info.get_buffer()
time = self.get_pipe_timestamp()
diff = time - buf.pts # latency from src
elapsed = time - stats.perf_src.prev_time
if not is_clock_valid(stats.perf_src.prev_time):
stats.perf_src.prev_time = time
elif is_clock_valid(time) and elapsed > Gst.SECOND:
stats.src_latency = stats.perf_src.latency / stats.perf_src.frame_count
# self.__logger.debug(
# f"{stats.elem_name}: src_latency={latency / Gst.MSECOND} ms"
# )
stats.perf_src.reset(time)
stats.perf_src.frame_count += 1
stats.perf_src.latency += diff
return Gst.PadProbeReturn.OK
class GstElementStats:
"""Statistic values for Gst.Element
:param elem: gstreamer element to measure statistics on
"""
class PerfData:
"""Used for measuring bitrate, fps and latencies in buffer probes"""
def __init__(self):
"""Constructor"""
self.prev_time = Gst.CLOCK_TIME_NONE
self.frame_count = 0
self.bps = 0
self.latency = 0
def reset(self, time: int) -> None:
"""Reset values"""
self.frame_count = 0
self.bps = 0
self.latency = 0
self.prev_time = time
def __init__(self, elem: Gst.Element):
"""Constructor"""
self.bitrate = 0 # in Mbps
self.fps = 0
self.src_latency = 0 # in ms
self.elem_latency = 0 # in ms
self.elem_name = elem.name
self.perf_src = GstPipeline.GstElementStats.PerfData()
self.perf_sink = GstPipeline.GstElementStats.PerfData()
Issue migrated from trac ticket # 2802
component: client | priority: minor
2020-06-08 12:40:29: stdedos created the issue