-
-
Notifications
You must be signed in to change notification settings - Fork 9.1k
linux-pipewire: Use dynamic pod builder #10459
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,7 @@ | |
| #include <spa/debug/format.h> | ||
| #include <spa/debug/types.h> | ||
| #include <spa/param/video/type-info.h> | ||
| #include <spa/pod/dynamic.h> | ||
| #include <spa/utils/result.h> | ||
|
|
||
| //#define DEBUG_PIPEWIRE | ||
|
|
@@ -299,10 +300,10 @@ static void swap_texture_red_blue(gs_texture_t *texture) | |
| } | ||
|
|
||
| static inline struct spa_pod *build_format(obs_pipewire_stream *obs_pw_stream, | ||
| struct spa_pod_builder *b, | ||
| uint32_t format, uint64_t *modifiers, | ||
| size_t modifier_count) | ||
| { | ||
| struct spa_pod_dynamic_builder pod_builder; | ||
| struct spa_rectangle max_resolution = SPA_RECTANGLE(8192, 4320); | ||
| struct spa_rectangle min_resolution = SPA_RECTANGLE(1, 1); | ||
| struct spa_rectangle resolution; | ||
|
|
@@ -311,6 +312,8 @@ static inline struct spa_pod *build_format(obs_pipewire_stream *obs_pw_stream, | |
| struct spa_fraction min_framerate; | ||
| struct spa_fraction framerate; | ||
|
|
||
| spa_pod_dynamic_builder_init(&pod_builder, NULL, 0, 4096); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. spa_pod_dynamic_builder_clean is referenced in other places. I cant find any docs on spa_pod_dynamic_builder (not sure why i hoped it would exist), but can you confirm that we dont need to clean up after init. |
||
|
|
||
| if (obs_pw_stream->framerate.set) { | ||
| framerate = obs_pw_stream->framerate.fraction; | ||
| min_framerate = obs_pw_stream->framerate.fraction; | ||
|
|
@@ -338,63 +341,65 @@ static inline struct spa_pod *build_format(obs_pipewire_stream *obs_pw_stream, | |
| * The object type is important because it defines the properties that are | ||
| * acceptable. The id gives more context about what the object is meant to | ||
| * contain. In this case we enumerate supported formats. */ | ||
| spa_pod_builder_push_object(b, &format_frame, SPA_TYPE_OBJECT_Format, | ||
| spa_pod_builder_push_object(&pod_builder.b, &format_frame, | ||
| SPA_TYPE_OBJECT_Format, | ||
| SPA_PARAM_EnumFormat); | ||
| /* add media type and media subtype properties */ | ||
| spa_pod_builder_add(b, SPA_FORMAT_mediaType, | ||
| spa_pod_builder_add(&pod_builder.b, SPA_FORMAT_mediaType, | ||
| SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); | ||
| spa_pod_builder_add(b, SPA_FORMAT_mediaSubtype, | ||
| spa_pod_builder_add(&pod_builder.b, SPA_FORMAT_mediaSubtype, | ||
| SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); | ||
|
|
||
| /* formats */ | ||
| spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); | ||
| spa_pod_builder_add(&pod_builder.b, SPA_FORMAT_VIDEO_format, | ||
| SPA_POD_Id(format), 0); | ||
|
|
||
| /* modifier */ | ||
| if (modifier_count > 0) { | ||
| struct spa_pod_frame modifier_frame; | ||
|
|
||
| /* build an enumeration of modifiers */ | ||
| spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_modifier, | ||
| spa_pod_builder_prop(&pod_builder.b, SPA_FORMAT_VIDEO_modifier, | ||
| SPA_POD_PROP_FLAG_MANDATORY | | ||
| SPA_POD_PROP_FLAG_DONT_FIXATE); | ||
|
|
||
| spa_pod_builder_push_choice(b, &modifier_frame, SPA_CHOICE_Enum, | ||
| 0); | ||
| spa_pod_builder_push_choice(&pod_builder.b, &modifier_frame, | ||
| SPA_CHOICE_Enum, 0); | ||
|
|
||
| /* The first element of choice pods is the preferred value. Here | ||
| * we arbitrarily pick the first modifier as the preferred one. | ||
| */ | ||
| spa_pod_builder_long(b, modifiers[0]); | ||
| spa_pod_builder_long(&pod_builder.b, modifiers[0]); | ||
|
|
||
| /* modifiers from an array */ | ||
| for (uint32_t i = 0; i < modifier_count; i++) | ||
| spa_pod_builder_long(b, modifiers[i]); | ||
| spa_pod_builder_long(&pod_builder.b, modifiers[i]); | ||
|
|
||
| spa_pod_builder_pop(b, &modifier_frame); | ||
| spa_pod_builder_pop(&pod_builder.b, &modifier_frame); | ||
| } | ||
| /* add size and framerate ranges */ | ||
| spa_pod_builder_add( | ||
| b, SPA_FORMAT_VIDEO_size, | ||
| &pod_builder.b, SPA_FORMAT_VIDEO_size, | ||
| SPA_POD_CHOICE_RANGE_Rectangle(&resolution, &min_resolution, | ||
| &max_resolution), | ||
| SPA_FORMAT_VIDEO_framerate, | ||
| SPA_POD_CHOICE_RANGE_Fraction(&framerate, &min_framerate, | ||
| &max_framerate), | ||
| 0); | ||
| return spa_pod_builder_pop(b, &format_frame); | ||
| return spa_pod_builder_pop(&pod_builder.b, &format_frame); | ||
| } | ||
|
|
||
| static bool build_format_params(obs_pipewire_stream *obs_pw_stream, | ||
| struct spa_pod_builder *pod_builder, | ||
| const struct spa_pod ***param_list, | ||
| uint32_t *n_params) | ||
| GPtrArray **out_params) | ||
| { | ||
| obs_pipewire *obs_pw = obs_pw_stream->obs_pw; | ||
| uint32_t params_count = 0; | ||
| g_autoptr(GPtrArray) params = NULL; | ||
|
|
||
| assert(out_params != NULL); | ||
|
|
||
| const struct spa_pod **params; | ||
| params = bzalloc(2 * obs_pw_stream->format_info.num * | ||
| sizeof(struct spa_pod *)); | ||
| params = g_ptr_array_new_full(2 * obs_pw_stream->format_info.num * | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we want to use a dynamic array we should use the obs DARRAY to keep consistency with the rest of obs.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DARRAY in this kind of code path (not cold) should be avoided.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First of all negotiation is cold. And second we know the size so there arnt any performance concerns to begin with. Its just a matter of using the api we all know or using the gptr api. |
||
| sizeof(struct spa_pod *), | ||
| free); | ||
|
|
||
| if (!params) { | ||
| blog(LOG_ERROR, | ||
|
|
@@ -409,22 +414,27 @@ static bool build_format_params(obs_pipewire_stream *obs_pw_stream, | |
| if (obs_pw_stream->format_info.array[i].modifiers.num == 0) { | ||
| continue; | ||
| } | ||
| params[params_count++] = build_format( | ||
| obs_pw_stream, pod_builder, | ||
| obs_pw_stream->format_info.array[i].spa_format, | ||
| obs_pw_stream->format_info.array[i].modifiers.array, | ||
| obs_pw_stream->format_info.array[i].modifiers.num); | ||
| g_ptr_array_add( | ||
| params, | ||
| build_format( | ||
| obs_pw_stream, | ||
| obs_pw_stream->format_info.array[i].spa_format, | ||
| obs_pw_stream->format_info.array[i] | ||
| .modifiers.array, | ||
| obs_pw_stream->format_info.array[i] | ||
| .modifiers.num)); | ||
| } | ||
|
|
||
| build_shm: | ||
| for (size_t i = 0; i < obs_pw_stream->format_info.num; i++) { | ||
| params[params_count++] = build_format( | ||
| obs_pw_stream, pod_builder, | ||
| obs_pw_stream->format_info.array[i].spa_format, NULL, | ||
| 0); | ||
| g_ptr_array_add( | ||
| params, | ||
| build_format( | ||
| obs_pw_stream, | ||
| obs_pw_stream->format_info.array[i].spa_format, | ||
| NULL, 0)); | ||
| } | ||
| *param_list = params; | ||
| *n_params = params_count; | ||
| *out_params = g_steal_pointer(¶ms); | ||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -568,26 +578,22 @@ static void renegotiate_format(void *data, uint64_t expirations) | |
| UNUSED_PARAMETER(expirations); | ||
| obs_pipewire_stream *obs_pw_stream = (obs_pipewire_stream *)data; | ||
| obs_pipewire *obs_pw = obs_pw_stream->obs_pw; | ||
| const struct spa_pod **params = NULL; | ||
| g_autoptr(GPtrArray) params = NULL; | ||
|
|
||
| blog(LOG_INFO, "[pipewire] Renegotiating stream"); | ||
|
|
||
| pw_thread_loop_lock(obs_pw->thread_loop); | ||
|
|
||
| uint8_t params_buffer[4096]; | ||
| struct spa_pod_builder pod_builder = | ||
| SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); | ||
| uint32_t n_params; | ||
| if (!build_format_params(obs_pw_stream, &pod_builder, ¶ms, | ||
| &n_params)) { | ||
| if (!build_format_params(obs_pw_stream, ¶ms)) { | ||
| teardown_pipewire(obs_pw); | ||
| pw_thread_loop_unlock(obs_pw->thread_loop); | ||
| return; | ||
| } | ||
|
|
||
| pw_stream_update_params(obs_pw_stream->stream, params, n_params); | ||
| pw_stream_update_params(obs_pw_stream->stream, | ||
| (const struct spa_pod **)params->pdata, | ||
| params->len); | ||
| pw_thread_loop_unlock(obs_pw->thread_loop); | ||
| bfree(params); | ||
| } | ||
|
|
||
| /* ------------------------------------------------- */ | ||
|
|
@@ -1203,11 +1209,8 @@ obs_pipewire_stream *obs_pipewire_connect_stream( | |
| obs_pipewire *obs_pw, obs_source_t *source, int pipewire_node, | ||
| const struct obs_pipwire_connect_stream_info *connect_info) | ||
| { | ||
| struct spa_pod_builder pod_builder; | ||
| const struct spa_pod **params = NULL; | ||
| g_autoptr(GPtrArray) params = NULL; | ||
| obs_pipewire_stream *obs_pw_stream; | ||
| uint32_t n_params; | ||
| uint8_t params_buffer[4096]; | ||
|
|
||
| assert(connect_info != NULL); | ||
|
|
||
|
|
@@ -1246,27 +1249,22 @@ obs_pipewire_stream *obs_pipewire_connect_stream( | |
| blog(LOG_INFO, "[pipewire] Created stream %p", obs_pw_stream->stream); | ||
|
|
||
| /* Stream parameters */ | ||
| pod_builder = | ||
| SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); | ||
|
|
||
| obs_get_video_info(&obs_pw_stream->video_info); | ||
|
|
||
| if (!build_format_params(obs_pw_stream, &pod_builder, ¶ms, | ||
| &n_params)) { | ||
| if (!build_format_params(obs_pw_stream, ¶ms)) { | ||
| pw_thread_loop_unlock(obs_pw->thread_loop); | ||
| bfree(obs_pw_stream); | ||
| return NULL; | ||
| } | ||
|
|
||
| pw_stream_connect( | ||
| obs_pw_stream->stream, PW_DIRECTION_INPUT, pipewire_node, | ||
| PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS, params, | ||
| n_params); | ||
| PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS, | ||
| (const struct spa_pod **)params->pdata, params->len); | ||
|
|
||
| blog(LOG_INFO, "[pipewire] Playing stream %p", obs_pw_stream->stream); | ||
|
|
||
| pw_thread_loop_unlock(obs_pw->thread_loop); | ||
| bfree(params); | ||
|
|
||
| g_ptr_array_add(obs_pw->streams, obs_pw_stream); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
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.
This should be a lot smaller now. instead of 50 formats with 50 modifiers it just needs to hold the 50 modifiers. I assume it grows automatically, but we can just see what the size is on amd and use that I guess.