Implement "clear frame" functionality on Filter#1276
Conversation
This will properly send an empty / clear frame when the filter is attached to a Window or Game Capture and the program is not visible anymore
|
Thanks for such quick action and initiative on this. In discord, you said:
|
As I've mentioned above, I've tried with
The main issue is making sure the source is set to 0x0 width and height, mimicing the behavior of the invalid / empty window capture on the sender side (or just window capture in general).
I can't override |
|
Okay I ran some more tests - turns out we can use the Either way - The NDI documentation mentions
So in this case we are using the string As for the empty frame width and height, I tested it and a 1x1 width and height worked. However, I think one extra frame in its last known width and height is okay and much more readable in code. |
|
The changes you made look good and the justifications make sense. Did a lot of testing and think it is ready to go. Just some minor suggestions:
|
|
As mentioned in the Discord, I could add an additional fix onto this which would kinda tie into the clear frame functionality. In the current form, if the Dedicated NDI Output filter gets disabled, it will still send data over to the receiver (not even stick to the last frame). Now, there is one more issue - on I'm waiting to hear back if implementing this extra functionality is also wanted or not before I do another push. |
Apparently NDI doesn't like 1x1 as a width and height and is thus not sending over a correct video frame. So we'll just make sure to set it to 2x2.
|
After some more trial and error I've managed to fix the disabling and removal of the filter properly. For some reason NDI doesn't like 1x1 width and height frames so we just make sure we set it to 2x2 instead. Also fixed formatting (Didn't have clang-format installed, whoops) |
There are concerns the 2x2 empty frame may cause issues in viewers or hardware decoders. Until we have confirmation, or we implement another solution, I will dismiss my review.
|
Because of concerns with the 2x2 empty frame, could you investigate destroying the NDI sender when a empty frame is received and creating the sender only when a non empty frame is received from the source? I would be happy to test out this kind of solution to closing a window which is being captured. Thanks for your effort so far! We will get a good solution here. |
|
As talked about in the Discord, I've reworked it to destroy the NDI sender instead of sending a clear frame. Additionally in the NDI source it's now checking for |
Instead of sending an empty frame we are now closing / destroying the NDI sender when we don't get data and start it again once we do. Additionally, in the NDI source we will output an empty frame locally / internally once we detect there are no active NDI senders (by checking if `ndiLib->recv_get_no_connections` is 0)
d5c9a23 to
97dd113
Compare
|
@Lordmau5 thanks for the PR and the details. Workflow for testing will run and based on latest master. Please check that the destroy/re-creation works as expected & that there is an entry in the log (should be IIRC) Note: This will need thorough testing. |
Trouffman
left a comment
There was a problem hiding this comment.
Overall seems good in my review.
Additionally in the NDI source it's now checking for ndiLib->recv_get_no_connections == 0 and will output an empty frame locally / internally
I like this, but I am not sure if any users "relies" on the fact that is kept the last frame "forever".
Good practice is to alwasy have a backup "background".
This will have to be checked, and if it's causing issues, we can look to implement a behavior settings. I am against doing it by default because it encourage non-good practice & will be confusing with the already available behavior settings.
|
I'll be pushing some new code tomorrow that has a better implementation / fix for this, which means it would obviously need another review (and some more testing) Also, this is probably incompatible with #1266, however I've noticed similar performance improvements with this new way of doing it in first tests. |
It was calling it every 100ms when there was no NDI connection and kept spamming the logs so an extra check for the source width and height fixes it
It properly checks the size of the parent (which would be 0x0 for a source that is considered invalid by OBS, such as a Window Capture that isn't captured anymore), if the Dedicated NDI Output filter is enabled and if the source is active in. This, however, means the source needs to be active and also shown in the current scene - I have seen other repositories / filters take this approach as well and it seems to be the only way to ensure we don't get the weird render issues (Crop/Pad filter giving an empty frame etc.)
|
The inherent main issue with these changes is that it is not proper off-screen rendering anymore (Say you have the source active in another scene - because it's enabled but not active it will not have a render callback and be sent via. NDI) Hence why more testing is needed by users that have different setups with this and how it would impact them. Ultimately I very much feel we're at a crossroads and have to choose which option to go for. Additionally I should mention that this is groundwork for #1279 . Without this, the other PR won't work properly due to the same double render issues. |
This is a challenge: most users (from what I have seen) use the Ouput filter as a way to "send a specific source to NDI at all time", we are only "actively destroying this" when the filter_source is effectively "removed" or became an invalid source. An output (General/Filter) should send a clear_frame whenever:
In general the use-cases with filter are: B. Filter is ACTIVE, Source is Active, Scene is NOT Active (PRV/PGM) nor Used on displays > Should render properly, clear frame only on invalid source. C. Filter is ACTIVE, Source is NOT Active, Scene is NOT Active (PRV/PGM) nor Used on displays > Should send Clear_frame + Stop render, but keep NDI sender registered (not sure if we can do that). D. Filter is NOT ACTIVE. nothing else matter > Should send Clear_frame + Stop render, stop NDI sender (NDI source dissapear from network). @Lordmau5 / @BitRate27 please flg if any of this is not possible, knowing that the part for the Global NDI output stop is not part of this PR. |
|
I've switched it back to a draft for now just like the other PR I did. I'll play around with some more ideas in the next few days as well as hope that Exeldro might have one, too (the filter chain issue with double rendering is affecting his plugin as well so I opened an issue on his repository) I'm sure we can figure out a proper solution despite the "roadblocks" in OBS (by that I mean us misusing filters haha) |
When using Window or Game Capture and the program it's focusing on closes or loses focus, OBS will set the width and height to 0.
However, when attaching a Dedicated NDI Output filter to said Window or Game Capture and closing the program, the receiving NDI end will keep the last frame active.
This felt a bit... wrong to me. NDI in my eyes is supposed to mirror what is happening on the end of the sender and in this case, since it doesn't have any video data anymore, it should also reflect that on the receiving end.
This PR implements this functionality, albeit through a bit of a "hacky" way.
Instead of being able to, for example, set the
xresandyresto 0 on the sender, we have to utilizepicture_aspect_ratio, which is seemingly always set to 0 in the filter.(Setting
xresoryresto 0 will send it as an invalid frame, so that makes sense)The following 2 videos show the before and after behavior.
Before:
Screen.Recording.2025-05-22.110609.mp4
After:
Screen.Recording.2025-05-22.104904.mp4