It is currently 25 May 2017, 15:29



OSC and Livecode; memory leak and decoding

CasparCG Server, Client and development

Moderators: Macey, Jonas Hummelstrand, didikunz

OSC and Livecode; memory leak and decoding

Postby jondezwaan » 24 Jun 2016, 10:23

Warning: my current findings on implementing CasparCG OSC readings in Livecode resulted in a memory leak, which caused the RAM usage to increase from about 20 MB to 2GB in about 2,5 hours.

Browsing these forums, I discovered that many people are trying to get OSC readings in Livecode. The problem was mainly not how to get them, but how to decode them. I'm not creating a step-by-step guide, I expect basic knowledge on how to get the readings, so I won't be covering that in detail, but I'll share the decoding part of it and hope to solve, together with you, the memory leak caused by the huge amount of data, which apparently Livecode can't handle directly.

First the bad news: as soon as you excecute the following command:

Code: Select all
open socket "localhost:5250"
accept datagram connections on port 6250 with message "OSC.Connected"


With the "OSC.Connected" part handling the readings, memory usage starts to increase, or at least on my machine. So I'm looking for a solution to that.

Probably the good part of this post is the decoding of the data. Many of you have been struggling with this kind of received data:
Code: Select all
#bundle    (/channel/2/mixer/audio/1   /dBFS     ,f  Ã@"Â

While the first part is considered readable, the last part of it isn't. These are the bytes and the valuable part of the data. In order to decode it, I set the item delimiter to forward slash "/", so I could use Livecodes method of cutting sentences into pieces.

Code: Select all
set the itemdelimiter to "/"


With delimiting the data, I'm able to sort the data by name, like this:

Code: Select all
if item 5 of tData = "audio" then
put tData into tDataAudio
(...)


Once you've delimited the data and put every needed piece of data in the preferred variable, the decoding can begin.

I'm someone who's programming skills are based on 'trial and error', therefor, my code can look messy and can contain unnecessary pieces, but as long as it works, I'm happy and I'm sure you will find better solutions.

Decoding Audio values

The audio values are decoded like this:

Code: Select all
 if char 1 to 4 in item 7 of tDataAudio = "dBFS" then -- dBFS
         put item 7 of tDataAudio into tDBFSraw
         delete char 1 to 11 in tDBFSraw
         delete char -1 of tDBFSraw
         put binarydecode("H8", tDBFSraw, tDBFSHex) into tDBFSConversion
         if tDBFSHex <> "tDBFSHex" then
           
            Global HexToDecDBFS
            local bytenumberDBFS
           
            put empty into tOutput1
            put empty into tOutput2
            put empty into HexToDecDBFS
           
            repeat with bytenumberDBFS = 1 to 8
               put byte bytenumberDBFS of tDBFSHex&tOutput1 into tOutput1
            end repeat
           
            put binaryencode("h*",tOutput1) into tOutput2
            put binarydecode("f", tOutput2, HexToDecDBFS) into convertQty
           
            put  HexToDecDBFS into field "mixerLevel_1"
         end if
         put empty into tDBFSraw
      end if -- end dBFS

Basically you decode the bytes as Hexadecimal digits (binarydecode("H8", (...)), then you've to reorder the bytes, re-encode them and finally decode them again, or at least, that's my understanding of this process. The readable data is now available in the 'HexToDecDBFS' variable, just make sure you've separated the audio channels first.

The same goes for the pFS data.

Decoding producer info

In order to sort the producer data I've delimited the data based upon the word "file";
Code: Select all
if item 7 of tData = "file" then
put item 8 of tData into tDataFile(...)


After that, you can use a 'switch' structure to sort the data.
Code: Select all
case "s"
leads to the filename of the file being played. The filename itself doesn't need to be decoded, it's directly readable.

Next up is current/total frames of the producer. Which can be obtained with the "h" in the switch structure:

Code: Select all
case "h"


Code: Select all
put binarydecode("H*", tDataFile, tFrameHex) into tFrameConversion
put char 1 to 16 in tFrameHex into currentFrameHex
put char 17 to 32 in tFrameHex into totalFrameHex
put baseConvert(currentFrameHex, 16, 10) into currentFramePlay
put currentFramePlay into field "currentFrameField"
put baseConvert(totalFrameHex, 16, 10) into totalFramePlay
if totalFramePlay <> 0 and totalFramePlay <> 1 then -- oscError correction
  put totalFramePlay into field "totalFrameField"
end if
put empty into tDataFile
break


A simple 'baseConvert' can do the trick for both current and total frames.

The time in seconds can be obtained via the "f" value in the data:

Code: Select all
case "f"


Code: Select all
put char 1 to 4 in tDataFile into currentTimeSec
 put char 5 to 8 in tDataFile into totalTimeSec
               
put binarydecode("H8", currentTimeSec, tTimeHex) into tTimeConversion
put binarydecode("H8", totalTimeSec, tTimeHexTotal) into tTimeTotalConversion

if tTimeHex <> "tTimeHex" then
                  Global HexToDec
                  local bytenumber
                  put empty into tOutput1
                  put empty into tOutput2
                  put empty into HexToDec

                  repeat with bytenumber = 1 to 8
                     put byte bytenumber of tTimeHex&tOutput1 into tOutput1
                  end repeat
                  put binaryencode("h*",tOutput1) into tOutput2
                  put binarydecode("f", tOutput2, HexToDec) into convertQty
                 
                  put  HexToDec into field "timeFile"

end if

 if tTimeHexTotal <> "tTimeHexTotal" then
                  Global HexToDec
                  local bytenumberTotalTime
                  put empty into tOutput1
                  put empty into tOutput2
                  put empty into HexToDecTimeSec
                   
                  repeat with bytenumberTotalTime = 1 to 8
                     put byte bytenumberTotalTime of tTimeHexTotal&tOutput1 into tOutput1
                  end repeat
                  put binaryencode("h*",tOutput1) into tOutput2
                  put binarydecode("f", tOutput2, HexToDecTimeSec) into convertQty
                 
                  put  HexToDecTimeSec into field "totalTimeFile"


It's basically the same decoding structure as the audio part, same goes for the the FPS data decoding.

A last note, which I've mentioned before; I'm not a 'real programmer', I mainly use a trial and error method in order to get my results, so if you've better solutions or know the solution to the memory leak, please share.
jondezwaan
 
Posts: 15
Joined: 04 Sep 2015, 08:16
Location: The Netherlands

Return to Tech and Development

Who is online

Users browsing this forum: Google [Bot] and 14 guests