diff --git a/include/screencast_common.h b/include/screencast_common.h index 5e7f34a..b8667ca 100644 --- a/include/screencast_common.h +++ b/include/screencast_common.h @@ -63,7 +63,8 @@ struct xdpw_frame { bool y_invert; uint64_t tv_sec; uint32_t tv_nsec; - struct xdpw_frame_damage damage; + struct xdpw_frame_damage damage[4]; + uint32_t damage_count; struct xdpw_buffer *xdpw_buffer; struct pw_buffer *pw_buffer; }; @@ -228,6 +229,8 @@ uint32_t xdpw_format_drm_fourcc_from_wl_shm(enum wl_shm_format format); enum spa_video_format xdpw_format_pw_from_drm_fourcc(uint32_t format); enum spa_video_format xdpw_format_pw_strip_alpha(enum spa_video_format format); +struct xdpw_frame_damage merge_damage(struct xdpw_frame_damage *damage1, struct xdpw_frame_damage *damage2); + enum xdpw_chooser_types get_chooser_type(const char *chooser_type); const char *chooser_type_str(enum xdpw_chooser_types chooser_type); #endif /* SCREENCAST_COMMON_H */ diff --git a/src/screencast/pipewire_screencast.c b/src/screencast/pipewire_screencast.c index 59e6fe4..acbab07 100644 --- a/src/screencast/pipewire_screencast.c +++ b/src/screencast/pipewire_screencast.c @@ -316,9 +316,18 @@ fixate_format: SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); - pw_stream_update_params(stream, params, 2); + params[2] = spa_pod_builder_add_object(&b[2].b, + SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, + SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoDamage), + SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int( + sizeof(struct spa_meta_region) * 4, + sizeof(struct spa_meta_region) * 1, + sizeof(struct spa_meta_region) * 4)); + + pw_stream_update_params(stream, params, 3); spa_pod_dynamic_builder_clean(&b[0]); spa_pod_dynamic_builder_clean(&b[1]); + spa_pod_dynamic_builder_clean(&b[2]); } static void pwr_handle_stream_add_buffer(void *data, struct pw_buffer *buffer) { @@ -438,6 +447,38 @@ void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) { logprint(TRACE, "pipewire: timestamp %"PRId64, h->pts); } + struct spa_meta *damage; + if ((damage = spa_buffer_find_meta(spa_buf, SPA_META_VideoDamage))) { + struct spa_region *d_region = spa_meta_first(damage); + uint32_t damage_counter = 0; + do { + if (damage_counter >= cast->current_frame.damage_count) { + *d_region = SPA_REGION(0, 0, 0, 0); + logprint(TRACE, "pipewire: end damage %u %u,%u (%ux%u)", damage_counter, + d_region->position.x, d_region->position.y, d_region->size.width, d_region->size.height); + break; + } + *d_region = SPA_REGION(cast->current_frame.damage[damage_counter].x, + cast->current_frame.damage[damage_counter].y, + cast->current_frame.damage[damage_counter].width, + cast->current_frame.damage[damage_counter].height); + logprint(TRACE, "pipewire: damage %u %u,%u (%ux%u)", damage_counter, + d_region->position.x, d_region->position.y, d_region->size.width, d_region->size.height); + damage_counter++; + } while (spa_meta_check(d_region + 1, damage) && d_region++); + + if (damage_counter < cast->current_frame.damage_count) { + struct xdpw_frame_damage damage = + {d_region->position.x, d_region->position.x, d_region->size.width, d_region->size.height}; + for (; damage_counter < cast->current_frame.damage_count; damage_counter++) { + damage = merge_damage(&damage, &cast->current_frame.damage[damage_counter]); + } + *d_region = SPA_REGION(damage.x, damage.y, damage.width, damage.height); + logprint(TRACE, "pipewire: collected damage %u %u,%u (%ux%u)", damage_counter, + d_region->position.x, d_region->position.y, d_region->size.width, d_region->size.height); + } + } + if (buffer_corrupt) { for (uint32_t plane = 0; plane < spa_buf->n_datas; plane++) { d[plane].chunk->flags = SPA_CHUNK_FLAG_CORRUPTED; diff --git a/src/screencast/screencast_common.c b/src/screencast/screencast_common.c index 58cdf97..696a225 100644 --- a/src/screencast/screencast_common.c +++ b/src/screencast/screencast_common.c @@ -410,3 +410,17 @@ const char *chooser_type_str(enum xdpw_chooser_types chooser_type) { fprintf(stderr, "Could not find chooser type %d\n", chooser_type); abort(); } + +struct xdpw_frame_damage merge_damage(struct xdpw_frame_damage *damage1, struct xdpw_frame_damage *damage2) { + struct xdpw_frame_damage damage; + uint32_t x0, y0; + damage.x = damage1->x < damage2->y ? damage1->x : damage2->x; + damage.y = damage1->y < damage2->y ? damage1->y : damage2->y; + + x0 = damage1->x + damage1->width < damage2->x + damage2->width ? damage2->x + damage2->width : damage1->x + damage1->width; + y0 = damage1->y + damage1->height < damage2->y + damage2->height ? damage2->y + damage2->height : damage1->y + damage1->height; + damage.width = x0 - damage.x; + damage.height = y0 - damage.y; + + return damage; +} diff --git a/src/screencast/wlr_screencast.c b/src/screencast/wlr_screencast.c index 0567b08..d9a95bc 100644 --- a/src/screencast/wlr_screencast.c +++ b/src/screencast/wlr_screencast.c @@ -284,6 +284,7 @@ static void wlr_frame_buffer_done(void *data, struct zwlr_screencopy_frame_v1 *f return; } + cast->current_frame.damage_count = 0; zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->current_frame.xdpw_buffer->buffer); logprint(TRACE, "wlroots: frame copied"); @@ -308,10 +309,13 @@ static void wlr_frame_damage(void *data, struct zwlr_screencopy_frame_v1 *frame, logprint(TRACE, "wlroots: damage event handler"); - cast->current_frame.damage.x = x; - cast->current_frame.damage.y = y; - cast->current_frame.damage.width = width; - cast->current_frame.damage.height = height; + logprint(TRACE, "wlroots: damage %"PRIu32": %"PRIu32",%"PRIu32"x%"PRIu32",%"PRIu32, cast->current_frame.damage_count, x, y, width, height); + struct xdpw_frame_damage damage = {x, y, width, height}; + if (cast->current_frame.damage_count < 4) { + cast->current_frame.damage[cast->current_frame.damage_count++] = damage; + } else { + cast->current_frame.damage[3] = merge_damage(&cast->current_frame.damage[3], &damage); + } } static void wlr_frame_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { @@ -463,10 +467,13 @@ static void hyprland_frame_damage(void *data, struct hyprland_toplevel_export_fr logprint(TRACE, "hyprland: damage event handler"); - cast->current_frame.damage.x = x; - cast->current_frame.damage.y = y; - cast->current_frame.damage.width = width; - cast->current_frame.damage.height = height; + logprint(TRACE, "hyprland: damage %"PRIu32": %"PRIu32",%"PRIu32"x%"PRIu32",%"PRIu32, cast->current_frame.damage_count, x, y, width, height); + struct xdpw_frame_damage damage = {x, y, width, height}; + if (cast->current_frame.damage_count < 4) { + cast->current_frame.damage[cast->current_frame.damage_count++] = damage; + } else { + cast->current_frame.damage[3] = merge_damage(&cast->current_frame.damage[3], &damage); + } } static void hyprland_frame_ready(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo,