Is there alternate of OSC?

Hi,

I’m using OSC protocol to get realtime information from server of channel, layer, clip etc using Bespoke OSC library and my application is on DotNet for Windows.

But my application is using 4% CPU when I connect to server with OSC. So I wanna know is there a way to get realtime information from server without using OSC protocol ? Or any other solution ?

Thanks

This is because OSC is very chatty (volume updates etc). You should filter and return early for OSC data you don’t need.

I don’t think that is a lot. In the Bespoke OSC library you have the possibility to tell it to only receive the info, that you want. Probably that can help to further reduce the CPU consumption.

I’m already using filtered data and in current situation I only need when a clip is loaded in background layer and for this I was thinking that using that much system resources is not good.

Right now I’m using 500ms delay after sending load command and then continue other work that is relatable with atem switcher like cut source etc but it is not always accurate to synchronise atem and ccg so now i think osc is only option to make it perfect and accurate.

I also see this right now, that OSC is telling A LOT… Tried to combine it with Universe Show Control, but for that it´s too much messages so it starts skipping need informations. Wish there was a way in Caspar Server to enable or disable the OSC messages that one is really needing… I don´t need the diag, I don´t need tick times… etc… That would help a lot in some situations.

I’m using bespoke’s library in some clients but I’m handling the filtering myself. Playing a video in a layer and showing a timecode is only using 0.5~2% in a 10 year old i7.
Maybe it’s the library own filtering that’s causing high CPU usage?

That would be very useful. Just like log levels would be useful to have OSC verbosity levels or some toggles for each feature.

I have that mostly built for NRK, just needs some refactoring. Will PR it when I have more than 2-4 hours of sleep a day!

3 Likes

General things to keep in mind here is that there is a couple of frames of delay between when casparcg starts playing and when the signal is output over SDI, that is a constant time you will always need to add delay for. For a default setup I usually use 160ms or 4 frames in 1080i50. When you need to sync an atem, I will strongly recommend to use a loadbg command to preload the media first, because loading the media can easily delay the playback by 200+ms. That’s a variable time too, so depending on how busy the disk is it might take longer or shorter, but for sync play you really need a constant time.

The folderplayout project uses this approach without any problems.

Yes right, this Decklink Card latency in my setup is like 240ms.

Actually for this I’m going to use OSC to sync it properly and I don’t want to add preloading 200 or 500ms delay of clip I’m trying to leave it on actual required time to make it quick and sharp because after adding constant and other delay overall delayed operation is noticeable.

Yes, I think this will help to make it fast.

Ohh great, thank you for your efforts.

I’ll try this , what are you using for filter messages?

I have an event handler for the BundleReceived event that loops the messages, looking only for the addresses that I need. Inside that loop I check each layer that I subscribed to at startup (there’s a class DataLayer that stores the state values for each layer)
It’s very hardcoded but it works ok.

Code
private void LayerDataHandler(object sender, OscBundleReceivedEventArgs e)
        {
            string s;
            foreach (OscMessage m in e.Bundle.Messages)
            {
                foreach (DataLayer l in DataLayers)
                {

                    if (IsNewerVersion)
                    {
                        // NEWER OSC DATA

                        s = $"/channel/{l.Channel + 1}/mixer/audio/";
                        if (m.Address.Length > s.Length && m.Address.Substring(0, s.Length) == s)
                        {
                            switch (m.Address.Substring(s.Length))
                            {
                                case "volume":
                                    l.AudioLevels[0] = Convert.ToDouble(m.Data[0])/int.MaxValue;
                                    l.AudioLevels[1] = Convert.ToDouble(m.Data[1])/int.MaxValue;
                                    break;
                            }
                        }

                        s = $"/channel/{l.Channel + 1}/stage/layer/{l.Layer}/foreground/";
                        if (m.Address.Length > s.Length && m.Address.Substring(0, s.Length) == s)
                        {
                            switch (m.Address.Substring(s.Length))
                            {
                                case "file/streams/0/fps":
                                    l.MediaFPS = Convert.ToDouble(m.Data[0])/Convert.ToDouble(m.Data[1]);
                                    break;
                                    
                                case "file/name":
                                    if (m.Data[0].ToString().IndexOf("/") >= 0)
                                    {
                                        l.MediaFileName = m.Data[0].ToString().Substring(m.Data[0].ToString().LastIndexOf("/") + 1);
                                        l.MediaFilePath = m.Data[0].ToString().Substring(0, m.Data[0].ToString().LastIndexOf("/"));
                                    }
                                    else
                                    {
                                        l.MediaFilePath = "";
                                        l.MediaFileName = m.Data[0].ToString();
                                    }
                                    break;
                                    
                                case "file/time":
                                    if(l.MediaFPS > 0)
                                    {
                                        
                                        l.MediaElapsedTimeSpan = TimeSpan.FromSeconds(Convert.ToDouble(m.Data[0]));
                                        l.MediaDurationTimeSpan = TimeSpan.FromSeconds(Convert.ToDouble(m.Data[1]));
                                        l.MediaElapsed = Math.Round(Convert.ToDouble(m.Data[0]) * l.MediaFPS);
                                        l.MediaDuration = Math.Round(Convert.ToDouble(m.Data[1]) * l.MediaFPS);
                                    }
                                    break;
                                    
                                case "loop":
                                    l.Loop = Convert.ToBoolean(m.Data[0]);
                                    break;

                                default:
                                    break;
                            }
                        }
                        else
                        {

                        }
                    }
                    else
                    {
                        // LEGACY OSC DATA

                        s = $"/channel/{l.Channel + 1}/mixer/audio/";
                        if (m.Address.Length > s.Length && m.Address.Substring(0, s.Length) == s)
                        {

                            switch (m.Address.Substring(s.Length))
                            {
                                case "1/pFS":
                                    l.AudioLevels[0] = Convert.ToSingle(m.Data[0]);
                                    break;
                                case "2/pFS":
                                    l.AudioLevels[1] = Convert.ToSingle(m.Data[0]);
                                    break;
                            }
                        }

                        s = $"/channel/{l.Channel + 1}/stage/layer/{l.Layer}/";
                        if (m.Address.Length > s.Length && m.Address.Substring(0, s.Length) == s)
                        {
                            switch (m.Address.Substring(s.Length))
                            {
                                case "foreground/type":
                                    if (m.Data[0].ToString() == "empty")
                                    {
                                        l.MediaFPS = 0;
                                        l.MediaFileName = "";
                                        l.MediaFilePath = "";
                                        l.MediaElapsed = 0;
                                        l.MediaDuration = 0;
                                        l.Loop = false;
                                    }
                                    break;

                                case "type":
                                    break;

                                case "file/fps":
                                case "foreground/file/fps":
                                    l.MediaFPS = Convert.ToDouble(m.Data[0]);
                                    break;

                                case "file/path":
                                    if (m.Data[0].ToString().IndexOf("/") >= 0)
                                    {
                                        l.MediaFileName = m.Data[0].ToString().Substring(m.Data[0].ToString().LastIndexOf("/") + 1);
                                        l.MediaFilePath = m.Data[0].ToString().Substring(0, m.Data[0].ToString().LastIndexOf("/"));
                                    }
                                    else
                                    {
                                        l.MediaFilePath = "";
                                        l.MediaFileName = m.Data[0].ToString();
                                    }
                                    break;

                                case "file/frame":
                                    l.MediaElapsed = Convert.ToInt64(m.Data[0])-2;
                                    l.MediaDuration = Convert.ToInt64(m.Data[1]);
                                    break;

                                case "file/time":
                                    l.MediaElapsedTimeSpan = TimeSpan.FromSeconds(Convert.ToDouble(m.Data[0]));
                                    l.MediaDurationTimeSpan = TimeSpan.FromSeconds(Convert.ToDouble(m.Data[1]));
                                    break;
                                case "loop":
                                    l.Loop = Convert.ToBoolean(m.Data[0]);
                                    break;
                                default:
                                    break;
                            }
                        }
                        else
                        {

                        }

                    }


                }

            }

        }
1 Like

Thanks rrebuffo, I’ll try this :slight_smile: