koala
Active Member
I have a bit of criticism about scripting with lua, because I failed to accomplish the task I set out to do and I am totally frustrated. I'd like to tell about this, because I feel there is room for improvement.
This was my first (and probably last) try to automate a workflow from within OBS. I'm familiar with programming and scripting, automating things with scripting is actually a major part of what I make a living for.
I thought it was a simple task I wanted to accomplish, and I was sure it was the right thing to do from within OBS.
Task:
Mark a rectangle section on the preview with some existing entity, for example with an empty group. This is easy and already there: just create an empty group, resize and position it.
When recording finished, I wanted to read the coordinates and size of that scene item, then externally call ffmpeg.exe to do something with the coordinates in the just finished recording file (I wanted to extract that part of the video and export it as animated gif)
This is an improvement for postprocessing workflows that regularly create animated gifs (or other exports) from some part of a scene. OBS doesn't support that natively, because you cannot just designate only a part of the canvas for output with with mouse as easy as it is to resize a source. Resizing the canvas isn't feasible: it's too tedious and you lose the big picture.
So it is this:
- create callback for "recording stopped"
- at callback, get marked coordinates and call a postprocessing tool with filename and coordinates
I chose Lua because of the simplicity. I assumed no more than 50, perhaps 100 lines of code, and certainly no more than 200.
I found the tools I was given tedious to use and almost undocumented. The only really documented things are the 5 generic callbacks a script is hooked into OBS.
It took hours to get me to the point where I had a tiny configuration GUI to select a scene and a source - the source I wanted to get the coordinates and size for later. Script size so far: about 5k and 150 lines of code.
Also included was the (still empty) callback that's called at recording end.
Then I searched the API for the file name of the recording, however I was unable to find it. First, I expected it somewhere in the "output" calldata that was given to the callback, but I was unable to extract the output_t item from the "output" value. The calldata_ptr() didn't work (type mismatch). There is a corresponding calldata_source() and calldata_sceneitem() to do this with source or sceneitem callbacks, or other calldata_xxx() for scalar values, but not for output_t. There is some generic calldata_cast(calldata, name, type) missing to get arbitrary types, not only void*.
Next thing was to ignore the calldata but just get the output object directly. I got the output object, but that was all. It was just an opaque object with no usable properties (for my purpose).
I googled a whole evening to find examples for how to find the properties of a file output, but nil. I scanned all the lua scripts in the resources section of the forum, but nobody solved this issue. The api documentation itself is bare. It's only listing function names but don't explain how to use it, let alone examples. The documentation doesn't tell how to use the api, it expects you know how to use it.
Next, without the filename, I wanted to just search for the newest media file in the recording output path. I found a directory scanning function, but the documented os_stat() to read the file timestamps (for sorting) simply wasn't there. Lua itself doesn't provide directory access.
In the end, I was only able to grab the coordinates and os.execute() some external program with that coordinates. Everything else has to be done externally: scan the directory for the newest file in the hope it is the last recording, then do something with it.
I was so frustrated I simply gave up at that point and deleted everything I made so far, because it would all be a big ugly hack only consisting of workarounds.
My main complaints:
There is a great example for how to provide high level access to low level functions: Autohotkey. You can even call Windows API functions directly, and you never have the feel you only mimic C with its pointers and buffer allocations.
But for what is the lua api good for? You can as well (and much more easily) just write C code and have the whole system available with it.
In essence, there is an automated handover to postprocessing tools missing. You cannot collect and save meta information about the circumstances of a recording and give this to a postprocessing tool in an automated way. Not even a filename is available.
I apologize for the rant.
Alex
This was my first (and probably last) try to automate a workflow from within OBS. I'm familiar with programming and scripting, automating things with scripting is actually a major part of what I make a living for.
I thought it was a simple task I wanted to accomplish, and I was sure it was the right thing to do from within OBS.
Task:
Mark a rectangle section on the preview with some existing entity, for example with an empty group. This is easy and already there: just create an empty group, resize and position it.
When recording finished, I wanted to read the coordinates and size of that scene item, then externally call ffmpeg.exe to do something with the coordinates in the just finished recording file (I wanted to extract that part of the video and export it as animated gif)
This is an improvement for postprocessing workflows that regularly create animated gifs (or other exports) from some part of a scene. OBS doesn't support that natively, because you cannot just designate only a part of the canvas for output with with mouse as easy as it is to resize a source. Resizing the canvas isn't feasible: it's too tedious and you lose the big picture.
So it is this:
- create callback for "recording stopped"
- at callback, get marked coordinates and call a postprocessing tool with filename and coordinates
I chose Lua because of the simplicity. I assumed no more than 50, perhaps 100 lines of code, and certainly no more than 200.
I found the tools I was given tedious to use and almost undocumented. The only really documented things are the 5 generic callbacks a script is hooked into OBS.
It took hours to get me to the point where I had a tiny configuration GUI to select a scene and a source - the source I wanted to get the coordinates and size for later. Script size so far: about 5k and 150 lines of code.
Also included was the (still empty) callback that's called at recording end.
Then I searched the API for the file name of the recording, however I was unable to find it. First, I expected it somewhere in the "output" calldata that was given to the callback, but I was unable to extract the output_t item from the "output" value. The calldata_ptr() didn't work (type mismatch). There is a corresponding calldata_source() and calldata_sceneitem() to do this with source or sceneitem callbacks, or other calldata_xxx() for scalar values, but not for output_t. There is some generic calldata_cast(calldata, name, type) missing to get arbitrary types, not only void*.
Next thing was to ignore the calldata but just get the output object directly. I got the output object, but that was all. It was just an opaque object with no usable properties (for my purpose).
I googled a whole evening to find examples for how to find the properties of a file output, but nil. I scanned all the lua scripts in the resources section of the forum, but nobody solved this issue. The api documentation itself is bare. It's only listing function names but don't explain how to use it, let alone examples. The documentation doesn't tell how to use the api, it expects you know how to use it.
Next, without the filename, I wanted to just search for the newest media file in the recording output path. I found a directory scanning function, but the documented os_stat() to read the file timestamps (for sorting) simply wasn't there. Lua itself doesn't provide directory access.
In the end, I was only able to grab the coordinates and os.execute() some external program with that coordinates. Everything else has to be done externally: scan the directory for the newest file in the hope it is the last recording, then do something with it.
I was so frustrated I simply gave up at that point and deleted everything I made so far, because it would all be a big ugly hack only consisting of workarounds.
My main complaints:
- the lua api is too lowlevel.
- the lua api isn't documented. Instead, the C api is documented, but it is different to the lua api, because you use it differently in both languages.
- there are no examples. Google shows usage examples, but no generic examples for every api function as it is common for api documentation.
- the lua api is an incomplete port of the C api
- the scripting api lacks basic essential functionality that is available to C functions but not to scripting. (or it isn't documented enough to enable me to find it)
- there are blobs of opaque data that may contain useful information, but it's not available (or not documented enough to enable me to find it).
- having to deal with types isn't appropriate for scripting languages like Lua. It's expected that types are handled by the runtime, hidden and implicit.
There is a great example for how to provide high level access to low level functions: Autohotkey. You can even call Windows API functions directly, and you never have the feel you only mimic C with its pointers and buffer allocations.
But for what is the lua api good for? You can as well (and much more easily) just write C code and have the whole system available with it.
In essence, there is an automated handover to postprocessing tools missing. You cannot collect and save meta information about the circumstances of a recording and give this to a postprocessing tool in an automated way. Not even a filename is available.
I apologize for the rant.
Alex