Visualising OBS Streaming Stats

Visualising OBS Streaming Stats


In these days of home working, more and more people are streaming on Twitch, YouTube, LightCast, Mixer, etc. One of the most popular platforms for doing this is Open Broadcast Software (OBS). OBS has some real-time statistics, such as CPU usage in its taskbar, but it doesn’t have any mechanism for storing these metrics, extending them, or combining them in a way to gain insights.
Streaming services can provide a summary of statistics over the length of a stream, but those are server-side, and can’t report on local resource usage or settings. What if there was a way of visualising real-time streaming stats from OBS itself?
I use InfluxDB as my main visualization engine, so I was looking for a way to get statistics from OBS that I could send to InfluxDB. I wanted to get something like frames-per-second, Mbps transmission rate, dropped frames, etc.
OBS has no built-in API for doing this, but there is a downloadable plug-in called "OBS-websocket" https://obsproject.com/forum/resources/obs-websocket-remote-control-obs-studio-from-websockets.466/
This plug-in claims to be for controlling and managing OBS facilities via a WebSocket API, but it also outputs data.
I downloaded it and looked for some way of attaching to the WebSocket API. NodeRED came to my rescue, as it often does, and so I added a WebSocket client node to read the OBS-websocket port on my PC (port 4444 by default).
Whenever I performed an action in OBS, such as swapping scene, starting / stopping recording or streaming, altering transitions, modifying sources, etc. I saw some messages being reported. Messages such as:
{
    "duration": 300,
    "from-scene": "Scene 2",
    "name": "Fade",
    "to-scene": "Scene",
    "update-type": "TransitionBegin"
}

{
    "scene-name": "Scene",
    "sources": [
        {
            "cx": 743.0,
            "cy": 418.0,
            "id": 5,
            "locked": false,
            "name": "Snapcam",
            "render": false,
            "source_cx": 1280,
            "source_cy": 720,
            "type": "av_capture_input",
            "volume": 1.0,
            "x": 0.0,
            "y": 0.0
        },
        {
            "cx": 1122.0,
            "cy": 631.0,
            "id": 4,
            "locked": false,
            "name": "webcam",
            "render": true,
            "source_cx": 1280,
            "source_cy": 720,
            "type": "av_capture_input",
            "volume": 1.0,
            "x": 0.0,
            "y": 419.0
        },
        {
            "cx": 1695.0,
            "cy": 1129.0,
            "id": 3,
            "locked": false,
            "name": "Image",
            "render": true,
            "source_cx": 500,
            "source_cy": 333,
            "type": ...

{
    "update-type": "RecordingStarting"
}

{
    "rec-timecode": "00:00:00.000",
    "update-type": "RecordingStarted"
}

{
    "rec-timecode": "00:00:01.999",
    "update-type": "RecordingStopping"
}

{
    "update-type": "RecordingStopped"
}


The messages are all JSON. None of them have a timestamp, and none of them indicate the host against which they are monitoring. But they all include “update-type”. An update-type of “StreamStatus” would indicate real-time stream statistics.

In NodeRED, JSON messages received by the WebSocket listener node can be inserted as text into the “msg.payload” object, in which case they are readable as shown above, or they can be inserted into the complete message object, in which case all the JSON properties are exposed and can be acted upon. Therefore, I did a couple of things:
·       I added a node to add timestamp and hostname to the incoming message.
·       I swapped to “complete msg” mode, and created a switch to grab only the “StreamStatus” object.


My end-game of this is to output to InfluxDB. I use Telegraf as my main InfluxDB ingest tool, but Telegraf does not currently include a WebSocket listener. No problem – I can send my captured messages from NodeRED to somewhere that Telegraf can read them.

I want to send the message payload, so I’ll need a function node to put the JSON message in the right context.

OBS-websocket sends a message every 2 seconds during a live stream, so I can’t have a simple HTTP connection. I’ll need something that works in a more streaming mode. I already use MQTT for other message-based routines, so I’ll publish to that.


On the Telegraf end, I want to subscribe to MQTT messages on obs/StreamStatus, translate the JSON message into measurement, tags, fields & timestamp, and forward to InfluxDB. For the “StreamStatus” update-type in the OBS-websocket API It looks like this:

[[inputs.mqtt_consumer]]
  servers = ["tcp://192.168.1.17:1883"]

  ## Topics that will be subscribed to.
  topics = ["obs/StreamStatus"]
 
  data_format = "json"
  json_strict = true
  json_query = ""

  tag_keys = [
    "preview-only",
    "recording",
    "recording-paused",
    "replay-buffer-active",
    "streaming",
    "hostname"
  ]

  json_string_fields = [
    "rec-timecode",
    "stream-timecode"
  ]

  json_name_key = "update-type"
  json_time_key = "timestamp"
  json_time_format = "unix_ms"
  json_timezone = "UTC"

So this should be sending data to InfluxDB during a live stream. What does InfluxDB actually receive? Quite a lot!

Creating a quick set of visualizations, I can see all this data:


If we dive into these, what do they all mean?

Window aggregates for all cells were changed to “2s”, to match the transmission rate from OBS-websocket, so as to query raw data.
Bucket name was changed to a variable, to assist in creating a template from this if appropriate.
Hostname was changed to a variable, so I can view the streaming stats of different PCs on my LAN by a simple selection.

Frame Time is a direct query of “average-frame-time”, and it would be the time OBS takes to render a frame?

Transmission Rate is a query of “kbits-per-sec”, divided by 1024 for Mbps

CPU usage is a direct query of “cpu-usage”

FPS is a direct query of “fps”

Frames is a query of “num-total-frames” and “output-total-frames” and “render-total-frames”, put through a non-negative derivative function to change them from cumulative counters to values per second, so show a more in-depth view of frames per second.

RAM used is a direct query of “memory-usage”

Stream Problems is a query of “num-dropped-frames” and “output-skipped-frames” and “render-missed-frames” and “strain”, filtered through a non-negative derivative function to change them from cumulative counters to values per second, to indicate any problems with generating and transmitting the stream at the requested bit-rate and quality.

Conclusions:
I don’t have an immediate use for this data, although it’s interesting to see that OBS released 100MB of RAM, about an hour into the stream. It’ll be worthwhile repeating the exercise for future streams, to monitor stream quality and resource usage over time. I’m monitoring many aspects of all the machines on my LAN, so I’ll be able to add operating system, GPU, and network bandwidth stats if they’ll be insightful.

Comments

Popular Posts