-
-
Notifications
You must be signed in to change notification settings - Fork 7.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft: Pipewire unified wrappers #6669
base: master
Are you sure you want to change the base?
Conversation
Moved comment from #5728 (comment)
I like the idea to build a registry wrapper with these callbacks. I'm not sure a
I think we might talk about different things here: The screencast portal creates for each stream a new pipewire_fd together with the pipewire_id which the client can use to access only this single stream. Iirc the camera portal only grants access to a single pipewire_fd, which can be used to access pipewire on the host with permissions on the connection only to show the client cameras, etc. In this case the client is responsible to set up a registry to get the pipewire_id of each camera. Can't we just use the provided pipewire_fd together with the pipewire_id, obtained from the registry, and just create a new stream (including it's own pw_core, pw_thread_loop, etc.) with those?
This way we could create one wrapper each for pipewire_stream, pipewire_registry (with the suggested callbacks) and pipewire_node (@dimtpap could you extract such a wrapper from your audio work?) |
Btw. creating |
Added registry stuff, nothing tested and just a sketch. |
Does anyone know how to do fa935cc in nice? |
Why not make it a regular struct and pass it to |
55e6ef3
to
9511a65
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this PR needs some work, but ultimately what it tries to achieve is desired for the maintainability of the plugin. Technical details aside, I think the internal API here is a bit odd in some places. What I had in mind with device registry callbacks and whatnot was something like this:
struct obs_pipewire_device_callbacks {
void (*device_added)(void *user_data, obs_pipewire_device *device);
void (*device_removed)(void *user_data, obs_pipewire_device *device);
};
void obs_pipewire_add_device_callbacks(
obs_pipewire_data *obs_pw,
const struct obs_pipewire_device_callbacks *device_callbacks);
void obs_pipewire_remove_device_callbacks(
obs_pipewire_data *obs_pw,
const struct obs_pipewire_device_callbacks *device_callbacks);
To properly do this, we would need to split the initialization of obs_pipewire
in 2 steps:
obs_pipewire_data * obs_pipewire_create(int pipewire_fd);
obs_pipewire_stream *
obs_pipewire_connect_stream(
obs_pipewire_data *obs_pw,
uint32_t stream_node,
const char *stream_name,
struct pw_properties *stream_properties);
The screencast portal code would look more or less like this:
capture->obs_pw = obs_pipewire_create(pipewire_fd);
capture->pipewire_stream = obs_pipewire_connect_stream(capture->obs_pw,
capture->pipewire_node, "OBS Studio",
pw_properties_new(PW_KEY_MEDIA_TYPE, "Video",
PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE, "Screen", NULL)
obs_pipewire_stream_set_cursor_visible(capture->pipewire_stream,
capture->cursor_visible);
The camera portal code would look more or less like this:
static const struct obs_pipewire_device_callbacks camera_device_callbacks = {
camera_portal_device_added,
camera_portal_device_remove,
};
...
camera->obs_pw = obs_pipewire_create(pipewire_fd);
obs_pipewire_add_device_callbacks(camera->obs_pw, &camera_device_callbacks);
... (every time someone selects a camera) ...
capture->pipewire_stream = obs_pipewire_connect_stream(camera->obs_pw,
device->pipewire_node, "OBS Studio",
pw_properties_new(PW_KEY_MEDIA_TYPE, ...,
PW_KEY_MEDIA_CATEGORY, ...,
PW_KEY_MEDIA_ROLE, "Screen", NULL));
7c806b3
to
cd9951e
Compare
Added the case of an output stream to pipewire.c in planing of the virtualcamera pluging. |
cd9951e
to
a1477f9
Compare
@GeorgesStavracas I'm pretty happy with your ideas! From my side it's ready for review. |
Instead of returning an opaque void* pointer, return the leaf C type obs_pipewire_data*. This commit introduces no functional changes.
When the linux-pipewire plugin is unloaded, make sure to cleanup the D-Bus proxy for the ScreenCast portal too. This effectively doesn't change anything, but it's always good to keep up with the code hygiene.
This commit makes the stuff in pipewire.c more independent from the actual plugin by providing a wrapper to create a stream with a set or properties defined by the plugin.
This separation of obs_pipewire_create() and play_pipewire_stream() was an artifact of how the original code was written, and there is no reason to keep this separation anymore. Inlining it will help future commits too.
We don't actually need to restore the node id anymore, since this is handled by the screencast-portal.c code now. Remove the pipewire_node field, and just create the stream directly from the passed now.
This commit introduces an important distinction in the initialization process that is essential to future camera and audio work: it splits creating and connecting to the PipeWire socket, from connecting to specific PipeWire nodes. Right now, for the only consumer in existance - the screencast portal code - this distinction is irrelevant, but from an API prespective it just makes sense to model it this way.
Next commits will introduce new obs_pipewire_* types, so this renaming will make it slightly easier to read the code with different types.
This will make code easier to follow after next commit.
Sorry, this is a painful commit to review :( Until now, the only consumer of the PipeWire code is the screen cast portal code. This portal code only ever has one PipeWire connection, and one PipeWire stream, every time a monitor or a window is selected. This is reflected on obs_pipewire, which only handles a single stream for any given connection. For cameras and audio, however, a single PipeWire connection can and most likely always will provide multiple streams. For example, computers with multiple webcams will have one PipeWire connection reporting multiple PipeWire nodes, one node for each camera. This commit breaks this one-stream-per-connection assumption, and makes each call to obs_pipewire_connect_stream() return a completely separate stream object. A new structure, obs_pipewire_stream, is introduced to represent a PipeWire stream. The screencast portal code continues to only ever have one connection and one stream, and that's made explicit in the code.
Make obs_pipewire hold a list of streams created by itself, and destroy these streams when closing the connection.
We'll need to peek information about the source to determine how to process frames, so store it for later usage.
So far we've been treating format info on a per-connection basis, but now that a single connection is capable of hosting multiple streams, and each stream might negotiate a different format, it is necessary to move format info to each stream individually.
And use it instead of exhaustively listing each field of the anonymous struct into the arguments of the lookup function.
The media API requires it's own format (enum video_format) and bytes per pixel (bpp).
Cameras and audio streams will use the async output routines, but not screencasting. This needs to be handled differently in the processing callback. Co-authored-by: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
This will be reused by the camera portal code.
This will allow consumers of obs_pipewire to handle the device registry themselves. This is not useful for the screencast code since there's always only one node in the registry, but it will be used by the camera and audio code to list hardware devices.
Generalize the async server roundtrip wait so that future consumers can, for example, wait for all devices to be listed before acting on the registry.
This struct tracks the related obs object. To share common code with the virtualcamera plugin we need to be able to deal with obs_source_t and obs_output_t.
This allows us to write a plugin exporting the buffers via PipeWire
a1477f9
to
9065cea
Compare
@GeorgesStavracas I'm not sure about the api design. On one hand I'd like to have a single function for Would you prefere one api for video_in, video_out, audio_in or one unified one, created with a
Thing and internally we do a lot of switch case? |
I went this weekend on a totally different direction using a struct of function pointers holding the implementation of certain features for each backend which wants to implement the @GeorgesStavracas Please take a glance at it, if we want to go with this kind of api ( @dimtpap Please look if extending the |
I rebased the branch and added some small fixes: https://github.com/wtay/obs-studio/tree/pipewire-unified-dev |
This pull request has become too chaotic and unmanageable. I'll start breaking it in smaller chunks, and will handle it. I think something went wrong with my rebase, half of the commits don't even build. I'll fix that too. |
@GeorgesStavracas please ping me when I should start rebasing this including fixes from @wtay onto #8153 Same is valid if a review from me is helpful. |
@GeorgesStavracas What's the intended plan for this PR? Is there a roadmap of changes that need to be merged first and does this PR still make sense in its current form (I guess the change itself is desired, just not the way it's implemented in the PR)? |
Description
Unify pipewire.c api in the linux-pipewire plugin
Motivation and Context
We want to create a basic pipewire wrapper to share code between multiple obs plugins. This MR should create the basis for discussions to create sth. useful for various video and audio plugins.
How Has This Been Tested?
Types of changes
Checklist: