@@ -44,6 +44,11 @@ struct camera_portal_source {
4444 obs_pipewire_stream * obs_pw_stream ;
4545 char * device_id ;
4646
47+ bool restart_stream ;
48+
49+ enum spa_media_subtype subtype ;
50+ struct obs_pw_video_format format ;
51+
4752 struct {
4853 struct spa_rectangle rect ;
4954 bool set ;
@@ -242,7 +247,7 @@ static bool update_device_id(struct camera_portal_source *camera_source, const c
242247
243248static void stream_camera (struct camera_portal_source * camera_source )
244249{
245- struct obs_pipwire_connect_stream_info connect_info ;
250+ struct obs_pipewire_connect_stream_info connect_info ;
246251 const struct spa_rectangle * resolution = NULL ;
247252 const struct spa_fraction * framerate = NULL ;
248253 struct camera_device * device ;
@@ -261,11 +266,13 @@ static void stream_camera(struct camera_portal_source *camera_source)
261266 if (camera_source -> framerate .set )
262267 framerate = & camera_source -> framerate .fraction ;
263268
264- connect_info = (struct obs_pipwire_connect_stream_info ){
269+ connect_info = (struct obs_pipewire_connect_stream_info ){
265270 .stream_name = "OBS PipeWire Camera" ,
266271 .stream_properties = pw_properties_new (PW_KEY_MEDIA_TYPE , "Video" , PW_KEY_MEDIA_CATEGORY , "Capture" ,
267272 PW_KEY_MEDIA_ROLE , "Camera" , NULL ),
268273 .video = {
274+ .subtype = & camera_source -> subtype ,
275+ .format = & camera_source -> format ,
269276 .resolution = resolution ,
270277 .framerate = framerate ,
271278 }};
@@ -277,14 +284,17 @@ static void stream_camera(struct camera_portal_source *camera_source)
277284static void camera_format_list (struct camera_device * dev , obs_property_t * prop )
278285{
279286 struct param * p ;
280- enum video_format last_format = VIDEO_FORMAT_NONE ;
287+ obs_data_t * data = NULL ;
281288
282289 obs_property_list_clear (prop );
283290
284291 spa_list_for_each (p , & dev -> param_list , link )
285292 {
286- struct obs_pw_video_format obs_pw_video_format ;
287- uint32_t media_type , media_subtype , format ;
293+ struct dstr str = {};
294+ uint32_t media_type , media_subtype ;
295+ uint32_t format = 0 ;
296+ const char * format_name ;
297+ struct spa_rectangle resolution ;
288298
289299 if (p -> id != SPA_PARAM_EnumFormat || p -> param == NULL )
290300 continue ;
@@ -293,24 +303,42 @@ static void camera_format_list(struct camera_device *dev, obs_property_t *prop)
293303 continue ;
294304 if (media_type != SPA_MEDIA_TYPE_video )
295305 continue ;
306+
307+ g_clear_pointer (& data , obs_data_release );
308+
309+ data = obs_data_create ();
310+
296311 if (media_subtype == SPA_MEDIA_SUBTYPE_raw ) {
312+ struct obs_pw_video_format obs_pw_video_format ;
313+
297314 if (spa_pod_parse_object (p -> param , SPA_TYPE_OBJECT_Format , NULL , SPA_FORMAT_VIDEO_format ,
298315 SPA_POD_Id (& format )) < 0 )
299316 continue ;
300- } else {
301- format = SPA_VIDEO_FORMAT_ENCODED ;
302- }
303317
304- if (!obs_pw_video_format_from_spa_format (format , & obs_pw_video_format ))
318+ if (!obs_pw_video_format_from_spa_format (format , & obs_pw_video_format ))
319+ continue ;
320+
321+ obs_data_set_bool (data , "encoded" , false);
322+ obs_data_set_int (data , "video_format" , format );
323+
324+ format_name = obs_pw_video_format .pretty_name ;
325+ } else {
305326 continue ;
327+ }
306328
307- if (obs_pw_video_format .video_format == last_format )
329+ if (spa_pod_parse_object (p -> param , SPA_TYPE_OBJECT_Format , format ? & format : NULL ,
330+ SPA_FORMAT_VIDEO_size , SPA_POD_OPT_Rectangle (& resolution )) < 0 )
308331 continue ;
309332
310- last_format = obs_pw_video_format .video_format ;
333+ obs_data_set_int (data , "width" , resolution .width );
334+ obs_data_set_int (data , "height" , resolution .height );
311335
312- obs_property_list_add_int (prop , obs_pw_video_format .pretty_name , format );
336+ dstr_printf (& str , "%ux%u - %s" , resolution .width , resolution .height , format_name );
337+ obs_property_list_add_string (prop , str .array , obs_data_get_json (data ));
338+ dstr_free (& str );
313339 }
340+
341+ g_clear_pointer (& data , obs_data_release );
314342}
315343
316344static bool control_changed (void * data , obs_properties_t * props , obs_property_t * prop , obs_data_t * settings )
@@ -479,12 +507,11 @@ static bool device_selected(void *data, obs_properties_t *props, obs_property_t
479507 if (device == NULL )
480508 return false;
481509
482- if (update_device_id (camera_source , device_id ))
483- stream_camera (camera_source );
510+ camera_source -> restart_stream = update_device_id (camera_source , device_id );
484511
485512 blog (LOG_INFO , "[camera-portal] Updating pixel formats" );
486513
487- property = obs_properties_get (props , "pixelformat " );
514+ property = obs_properties_get (props , "format " );
488515 new_control_properties = obs_properties_create ();
489516 obs_properties_remove_by_name (props , "controls" );
490517
@@ -499,108 +526,6 @@ static bool device_selected(void *data, obs_properties_t *props, obs_property_t
499526 return true;
500527}
501528
502- static int sort_resolutions (gconstpointer a , gconstpointer b )
503- {
504- const struct spa_rectangle * resolution_a = a ;
505- const struct spa_rectangle * resolution_b = b ;
506- int64_t area_a = resolution_a -> width * resolution_a -> height ;
507- int64_t area_b = resolution_b -> width * resolution_b -> height ;
508-
509- return area_a - area_b ;
510- }
511-
512- static void resolution_list (struct camera_device * dev , uint32_t pixelformat , obs_property_t * prop )
513- {
514- struct spa_rectangle last_resolution = SPA_RECTANGLE (0 , 0 );
515- g_autoptr (GArray ) resolutions = NULL ;
516- struct param * p ;
517- obs_data_t * data ;
518-
519- resolutions = g_array_new (FALSE, FALSE, sizeof (struct spa_rectangle ));
520-
521- spa_list_for_each (p , & dev -> param_list , link )
522- {
523- struct obs_pw_video_format obs_pw_video_format ;
524- struct spa_rectangle resolution ;
525- uint32_t media_type , media_subtype , format ;
526-
527- if (p -> id != SPA_PARAM_EnumFormat || p -> param == NULL )
528- continue ;
529-
530- if (spa_format_parse (p -> param , & media_type , & media_subtype ) < 0 )
531- continue ;
532- if (media_type != SPA_MEDIA_TYPE_video )
533- continue ;
534- if (media_subtype == SPA_MEDIA_SUBTYPE_raw ) {
535- if (spa_pod_parse_object (p -> param , SPA_TYPE_OBJECT_Format , NULL , SPA_FORMAT_VIDEO_format ,
536- SPA_POD_Id (& format )) < 0 )
537- continue ;
538- } else {
539- format = SPA_VIDEO_FORMAT_ENCODED ;
540- }
541-
542- if (!obs_pw_video_format_from_spa_format (format , & obs_pw_video_format ))
543- continue ;
544-
545- if (obs_pw_video_format .video_format != pixelformat )
546- continue ;
547-
548- if (spa_pod_parse_object (p -> param , SPA_TYPE_OBJECT_Format , NULL , SPA_FORMAT_VIDEO_size ,
549- SPA_POD_OPT_Rectangle (& resolution )) < 0 )
550- continue ;
551-
552- if (resolution .width == last_resolution .width && resolution .height == last_resolution .height )
553- continue ;
554-
555- last_resolution = resolution ;
556- g_array_append_val (resolutions , resolution );
557- }
558-
559- g_array_sort (resolutions , sort_resolutions );
560-
561- obs_property_list_clear (prop );
562-
563- data = obs_data_create ();
564- for (size_t i = 0 ; i < resolutions -> len ; i ++ ) {
565- const struct spa_rectangle * resolution = & g_array_index (resolutions , struct spa_rectangle , i );
566- struct dstr str = {};
567-
568- dstr_printf (& str , "%ux%u" , resolution -> width , resolution -> height );
569-
570- obs_data_set_int (data , "width" , resolution -> width );
571- obs_data_set_int (data , "height" , resolution -> height );
572-
573- obs_property_list_add_string (prop , str .array , obs_data_get_json (data ));
574-
575- dstr_free (& str );
576- }
577- obs_data_release (data );
578- }
579-
580- /*
581- * Format selected callback
582- */
583- static bool format_selected (void * data , obs_properties_t * properties , obs_property_t * property , obs_data_t * settings )
584- {
585- UNUSED_PARAMETER (property );
586- UNUSED_PARAMETER (settings );
587-
588- struct camera_portal_source * camera_source = data ;
589- struct camera_device * device ;
590- obs_property_t * resolution ;
591-
592- blog (LOG_INFO , "[camera-portal] Selected format for '%s'" , camera_source -> device_id );
593-
594- device = g_hash_table_lookup (connection -> devices , camera_source -> device_id );
595- if (device == NULL )
596- return false;
597-
598- resolution = obs_properties_get (properties , "resolution" );
599- resolution_list (device , obs_data_get_int (settings , "pixelformat" ), resolution );
600-
601- return true;
602- }
603-
604529static int compare_framerates (gconstpointer a , gconstpointer b )
605530{
606531 const struct spa_fraction * framerate_a = a ;
@@ -760,47 +685,68 @@ static bool framerate_selected(void *data, obs_properties_t *properties, obs_pro
760685}
761686
762687/*
763- * Resolution selected callback
688+ * Format selected callback
764689 */
765-
766- static bool parse_resolution (struct spa_rectangle * dest , const char * json )
690+ static bool parse_format (struct camera_portal_source * dest , const char * json )
767691{
768692 obs_data_t * data = obs_data_create_from_json (json );
693+ bool ret = false;
769694
770695 if (!data )
771696 return false;
772697
773- dest -> width = obs_data_get_int (data , "width" );
774- dest -> height = obs_data_get_int (data , "height" );
698+ if (obs_data_has_user_value (data , "video_format" ) && obs_data_has_user_value (data , "encoded" )) {
699+ struct obs_pw_video_format format ;
700+
701+ if (obs_data_get_bool (data , "encoded" )) {
702+ dest -> subtype = obs_data_get_int (data , "video_format" );
703+ ret = true;
704+ } else if (obs_pw_video_format_from_spa_format (obs_data_get_int (data , "video_format" ), & format )) {
705+ dest -> subtype = SPA_MEDIA_SUBTYPE_raw ;
706+ dest -> format = format ;
707+ ret = true;
708+ }
709+
710+ if (obs_data_has_user_value (data , "width" ) && obs_data_has_user_value (data , "height" )) {
711+ dest -> resolution .rect .width = obs_data_get_int (data , "width" );
712+ dest -> resolution .rect .height = obs_data_get_int (data , "height" );
713+ dest -> resolution .set = true;
714+ }
715+ }
716+
775717 obs_data_release (data );
776- return true ;
718+ return ret ;
777719}
778720
779- static bool resolution_selected (void * data , obs_properties_t * properties , obs_property_t * property ,
780- obs_data_t * settings )
721+ static bool format_selected (void * data , obs_properties_t * properties , obs_property_t * property , obs_data_t * settings )
781722{
782- UNUSED_PARAMETER (properties );
783723 UNUSED_PARAMETER (property );
784724 UNUSED_PARAMETER (settings );
785725
786726 struct camera_portal_source * camera_source = data ;
787- struct spa_rectangle resolution ;
788727 struct camera_device * device ;
728+ enum spa_media_subtype last_subtype = camera_source -> subtype ;
729+ enum spa_video_format last_format = camera_source -> format .spa_format ;
789730
790- blog (LOG_INFO , "[camera-portal] Selected resolution for '%s'" , camera_source -> device_id );
731+ blog (LOG_INFO , "[camera-portal] Selected format for '%s'" , camera_source -> device_id );
791732
792733 device = g_hash_table_lookup (connection -> devices , camera_source -> device_id );
793734 if (device == NULL )
794735 return false;
795736
796- if (!parse_resolution ( & resolution , obs_data_get_string (settings , "resolution " )))
737+ if (!parse_format ( camera_source , obs_data_get_string (settings , "format " )))
797738 return false;
798739
799- if (camera_source -> obs_pw_stream )
800- obs_pipewire_stream_set_resolution (camera_source -> obs_pw_stream , & resolution );
740+ if (!camera_source -> obs_pw_stream || camera_source -> restart_stream || last_subtype != camera_source -> subtype ||
741+ last_format != camera_source -> format .spa_format ) {
742+ camera_source -> restart_stream = false;
743+ stream_camera (camera_source );
744+ } else if (camera_source -> obs_pw_stream ) {
745+ obs_pipewire_stream_set_resolution (camera_source -> obs_pw_stream , & camera_source -> resolution .rect );
746+ }
801747
802748 property = obs_properties_get (properties , "framerate" );
803- framerate_list (device , obs_data_get_int ( settings , "pixelformat" ), & resolution , property );
749+ framerate_list (device , camera_source -> format . spa_format , & camera_source -> resolution . rect , property );
804750
805751 return true;
806752}
@@ -1116,6 +1062,19 @@ static const char *pipewire_camera_get_name(void *data)
11161062 return obs_module_text ("PipeWireCamera" );
11171063}
11181064
1065+ static bool parse_resolution (struct spa_rectangle * dest , const char * json )
1066+ {
1067+ obs_data_t * data = obs_data_create_from_json (json );
1068+
1069+ if (!data )
1070+ return false;
1071+
1072+ dest -> width = obs_data_get_int (data , "width" );
1073+ dest -> height = obs_data_get_int (data , "height" );
1074+ obs_data_release (data );
1075+ return true;
1076+ }
1077+
11191078static void * pipewire_camera_create (obs_data_t * settings , obs_source_t * source )
11201079{
11211080 struct camera_portal_source * camera_source ;
@@ -1125,8 +1084,30 @@ static void *pipewire_camera_create(obs_data_t *settings, obs_source_t *source)
11251084 camera_source -> device_id = bstrdup (obs_data_get_string (settings , "device_id" ));
11261085 camera_source -> framerate .set =
11271086 parse_framerate (& camera_source -> framerate .fraction , obs_data_get_string (settings , "framerate" ));
1128- camera_source -> resolution .set =
1129- parse_resolution (& camera_source -> resolution .rect , obs_data_get_string (settings , "resolution" ));
1087+
1088+ if (obs_data_has_user_value (settings , "format" )) {
1089+ parse_format (camera_source , obs_data_get_string (settings , "format" ));
1090+ } else if (obs_pw_video_format_from_spa_format (obs_data_get_int (settings , "pixelformat" ),
1091+ & camera_source -> format )) {
1092+ camera_source -> subtype = SPA_MEDIA_SUBTYPE_raw ;
1093+
1094+ camera_source -> resolution .set =
1095+ parse_resolution (& camera_source -> resolution .rect , obs_data_get_string (settings , "resolution" ));
1096+
1097+ /* NOTE: We can convert to the new format only if resolution is available */
1098+ if (camera_source -> resolution .set ) {
1099+ obs_data_t * format = obs_data_create ();
1100+
1101+ obs_data_set_bool (format , "encoded" , false);
1102+ obs_data_set_int (format , "video_format" , camera_source -> format .spa_format );
1103+ obs_data_set_int (format , "width" , camera_source -> resolution .rect .width );
1104+ obs_data_set_int (format , "height" , camera_source -> resolution .rect .height );
1105+
1106+ obs_data_set_string (settings , "format" , obs_data_get_json (format ));
1107+
1108+ obs_data_release (format );
1109+ }
1110+ }
11301111
11311112 access_camera (camera_source );
11321113
@@ -1156,7 +1137,6 @@ static obs_properties_t *pipewire_camera_get_properties(void *data)
11561137 struct camera_portal_source * camera_source = data ;
11571138 obs_properties_t * controls_props ;
11581139 obs_properties_t * props ;
1159- obs_property_t * resolution_list ;
11601140 obs_property_t * framerate_list ;
11611141 obs_property_t * device_list ;
11621142 obs_property_t * format_list ;
@@ -1166,11 +1146,8 @@ static obs_properties_t *pipewire_camera_get_properties(void *data)
11661146 device_list = obs_properties_add_list (props , "device_id" , obs_module_text ("PipeWireCameraDevice" ),
11671147 OBS_COMBO_TYPE_LIST , OBS_COMBO_FORMAT_STRING );
11681148
1169- format_list = obs_properties_add_list (props , "pixelformat" , obs_module_text ("VideoFormat" ), OBS_COMBO_TYPE_LIST ,
1170- OBS_COMBO_FORMAT_INT );
1171-
1172- resolution_list = obs_properties_add_list (props , "resolution" , obs_module_text ("Resolution" ),
1173- OBS_COMBO_TYPE_LIST , OBS_COMBO_FORMAT_STRING );
1149+ format_list = obs_properties_add_list (props , "format" , obs_module_text ("VideoFormat" ), OBS_COMBO_TYPE_LIST ,
1150+ OBS_COMBO_FORMAT_STRING );
11741151
11751152 framerate_list = obs_properties_add_list (props , "framerate" , obs_module_text ("FrameRate" ), OBS_COMBO_TYPE_LIST ,
11761153 OBS_COMBO_FORMAT_STRING );
@@ -1184,7 +1161,6 @@ static obs_properties_t *pipewire_camera_get_properties(void *data)
11841161
11851162 obs_property_set_modified_callback2 (device_list , device_selected , camera_source );
11861163 obs_property_set_modified_callback2 (format_list , format_selected , camera_source );
1187- obs_property_set_modified_callback2 (resolution_list , resolution_selected , camera_source );
11881164 obs_property_set_modified_callback2 (framerate_list , framerate_selected , camera_source );
11891165
11901166 return props ;
0 commit comments