e_comp_wl_capture: Support video capture 85/217385/9
authorSeunghun Lee <shiin.lee@samsung.com>
Thu, 7 Nov 2019 04:40:16 +0000 (13:40 +0900)
committerSeunghun Lee <shiin.lee@samsung.com>
Tue, 12 Nov 2019 08:24:06 +0000 (17:24 +0900)
A video client will be captured with its parent window if given E_Client
has video client as subsurface when e_comp_wl_capture_client_image_save
is called.

Note that it only supports NV12 and RGB colorspace of format of video
buffer and capturing video client by itself isn't supported since its
usage is not defined yet.

Change-Id: Ibaef3c4c0c4459ce7cdc9448800de89e1282d047

src/bin/e_comp_wl_capture.c

index 3789c4367a22a41693c4565deb2dd1ab2145428a..7a662d020d85a1008ac14c4991cbb1ff290571c2 100644 (file)
@@ -5,6 +5,12 @@
 #include <wayland-tbm-server.h>
 #include <pixman.h>
 
+#define ENABLE_VIDEO_CAPTURE
+
+#ifdef ENABLE_VIDEO_CAPTURE
+#include "e_util_video.h"  /* For converting colorspace */
+#endif
+
 #define CAPINF(f, ec, str, obj, x...)                                \
    do                                                                \
      {                                                               \
@@ -53,6 +59,16 @@ struct _E_Capture_Client
 
 typedef struct
 {
+#ifdef ENABLE_VIDEO_CAPTURE
+   E_Comp_Wl_Buffer_Ref buffer_ref;
+
+   struct
+     {
+        Eina_Rectangle buffer;
+        Eina_Rectangle output;
+     } viewport;
+#endif
+
    void *shm_buffer_ptr;
    int shm_buffer_stride;
    int shm_buffer_h;
@@ -88,8 +104,578 @@ typedef struct {
 
      E_Capture_Client_Save_End_Cb func_end;
      void *data;
+
+#ifdef ENABLE_VIDEO_CAPTURE
+     Capture_Data *video_capture_data;
+#endif
 } Thread_Data;
 
+#ifdef ENABLE_VIDEO_CAPTURE
+static pixman_format_code_t _e_capture_image_data_pixman_format_get_from_tbm_surface(tbm_format format);
+static pixman_format_code_t _e_capture_image_data_pixman_format_get_from_shm_buffer(uint32_t format);
+
+static void
+_e_capture_client_image_viewport_apply(pixman_image_t *image, Eina_Rectangle *src, Eina_Rectangle *dst, uint32_t transform)
+{
+   struct pixman_f_transform ft;
+   pixman_transform_t t;
+   double scale_x, scale_y;
+   int c, s;
+   int tx, ty;
+
+   tx = src->x;
+   ty = src->y;
+
+   switch (transform)
+     {
+      case WL_OUTPUT_TRANSFORM_90:
+         c = 0, s = 1;
+         ty = -(src->x + src->w);
+         break;
+      case WL_OUTPUT_TRANSFORM_180:
+         c = -1, s = 0;
+         tx = -(src->y + src->h);
+         ty = -(src->x + src->w);
+         break;
+      case WL_OUTPUT_TRANSFORM_270:
+         c = 0, s = -1;
+         tx = -(src->y + src->h);
+         break;
+      default:
+         c = 1, s = 0;
+         break;
+     }
+
+   switch (transform)
+     {
+      default:
+      case WL_OUTPUT_TRANSFORM_NORMAL:
+      case WL_OUTPUT_TRANSFORM_180:
+         scale_x = (double)dst->w / (double)src->w;
+         scale_y = (double)dst->h / (double)src->h;
+         break;
+      case WL_OUTPUT_TRANSFORM_90:
+      case WL_OUTPUT_TRANSFORM_270:
+         scale_x = (double)dst->w / (double)src->h;
+         scale_y = (double)dst->h / (double)src->w;
+         break;
+     }
+
+   pixman_f_transform_init_identity(&ft);
+   pixman_f_transform_translate(&ft, NULL, tx, ty);
+   pixman_f_transform_rotate(&ft, NULL, c, s);
+   pixman_f_transform_scale(NULL, &ft, scale_x, scale_y);
+
+   pixman_transform_from_pixman_f_transform(&t, &ft);
+   pixman_image_set_transform(image, &t);
+}
+
+static pixman_image_t *
+_pixman_image_create_with_transform(unsigned char *ptr, int bw, int bh, int stride, Eina_Rectangle *src, Eina_Rectangle *dst, uint32_t transform)
+{
+   pixman_image_t *image;
+   int bpp;
+
+   EINA_SAFETY_ON_FALSE_RETURN_VAL((src->x >= 0), NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL((src->y >= 0), NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL((src->w > 0), NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL((src->h > 0), NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(((src->x + src->w) <= bw), NULL);
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(((src->y + src->h) <= bh), NULL);
+
+   if ((src->x > 0) || (src->y > 0))
+     {
+        bpp = stride / bw;
+        ptr += (src->y * stride) + (src->x * bpp);
+     }
+
+   image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
+                                        src->w, src->h,
+                                        (uint32_t *)ptr,
+                                        stride);
+   if (!image)
+     {
+        ERR("Failed to create pixman_image_t");
+        return NULL;
+     }
+
+   _e_capture_client_image_viewport_apply(image, src, dst, transform);
+
+   return image;
+}
+
+static void
+_e_capture_client_video_composite(Thread_Data *td, pixman_image_t *target_image)
+{
+   Capture_Data *video_capture_data;
+   tbm_surface_h video_surface;
+   tbm_surface_info_s info;
+   tbm_format format;
+   pixman_image_t *video_image;
+   Eina_Bool need_destroy = EINA_FALSE;
+   int res;
+
+   video_capture_data = td->video_capture_data;
+   video_surface = video_capture_data->tbm_surface;
+
+   format = tbm_surface_get_format(video_surface);
+   switch (format)
+     {
+      case TBM_FORMAT_ARGB8888:
+      case TBM_FORMAT_XRGB8888:
+         break;
+      case TBM_FORMAT_NV12:
+         video_surface = e_util_video_tbm_surface_convert_to_rgb(video_surface);
+         need_destroy = EINA_TRUE;
+         break;
+      default:
+         ERR("Unsupported format(%c%c%c%c)", FOURCC_STR(format));
+         return;
+     }
+
+   res = tbm_surface_map(video_surface, TBM_SURF_OPTION_READ, &info);
+   if (res != TBM_SURFACE_ERROR_NONE)
+     {
+        ERR("Failed to map tbm_surface");
+        goto end;
+     }
+
+   video_image =
+      _pixman_image_create_with_transform(info.planes[0].ptr,
+                                          tbm_surface_get_width(video_surface),
+                                          tbm_surface_get_height(video_surface),
+                                          info.planes[0].stride,
+                                          &video_capture_data->viewport.buffer,
+                                          &video_capture_data->viewport.output,
+                                          video_capture_data->transform);
+   if (!video_image)
+     {
+        ERR("Failed to create pixman_image for video buffer");
+        goto end;
+     }
+
+   pixman_image_composite(PIXMAN_OP_OVER, video_image, NULL, target_image,
+                          0, 0,
+                          0, 0,
+                          video_capture_data->viewport.output.x,
+                          video_capture_data->viewport.output.y,
+                          video_capture_data->viewport.output.w,
+                          video_capture_data->viewport.output.h);
+
+   pixman_image_unref(video_image);
+
+end:
+   if (need_destroy)
+     tbm_surface_destroy(video_surface);
+}
+
+static void
+_e_capture_client_main_composite(Thread_Data *td, pixman_image_t *target_image)
+{
+   // for base
+   tbm_surface_info_s info;
+   int tw = 0, th = 0;
+   pixman_image_t *src_img = NULL;
+   pixman_format_code_t src_format;
+   pixman_transform_t t;
+   struct pixman_f_transform ft;
+   unsigned char *src_ptr = NULL;
+   int c = 0, s = 0, tx = 0, ty = 0;
+   int w, h;
+
+   // for child
+   tbm_surface_h c_dst_surface = NULL;
+   tbm_surface_info_s c_info;
+   pixman_image_t *c_src_img = NULL, *c_dst_img = NULL;
+   pixman_format_code_t c_src_format, c_dst_format;
+   pixman_transform_t c_t;
+   struct pixman_f_transform c_ft;
+   unsigned char *c_src_ptr = NULL, *c_dst_ptr = NULL;
+   int c_x, c_y, c_w, c_h;
+   int c_tx, c_ty, c_tw, c_th;
+
+   EINA_SAFETY_ON_NULL_RETURN(td);
+
+   if (td->transform > WL_OUTPUT_TRANSFORM_270) return;
+
+   if (td->tbm_surface)
+     {
+        src_format = _e_capture_image_data_pixman_format_get_from_tbm_surface(tbm_surface_get_format(td->tbm_surface));
+
+        tbm_surface_map(td->tbm_surface, TBM_SURF_OPTION_READ, &info);
+        src_ptr = info.planes[0].ptr;
+
+        w = tbm_surface_get_width(td->tbm_surface);
+        h = tbm_surface_get_height(td->tbm_surface);
+        src_img = pixman_image_create_bits(src_format, w, h, (uint32_t*)src_ptr, info.planes[0].stride);
+        EINA_SAFETY_ON_NULL_GOTO(src_img, clean_up);
+     }
+   else if (td->shm_buffer_ptr)
+     {
+        src_format = _e_capture_image_data_pixman_format_get_from_shm_buffer(td->shm_buffer_format);
+
+        src_ptr = td->shm_buffer_ptr;
+        w = td->shm_buffer_stride;
+        h = td->shm_buffer_h;
+        src_img = pixman_image_create_bits(src_format, w, h, (uint32_t*)src_ptr, w * 4);
+        EINA_SAFETY_ON_NULL_GOTO(src_img, clean_up);
+     }
+   else
+     {
+        ERR("invalid buffer");
+        return;
+     }
+
+   if (td->transform == WL_OUTPUT_TRANSFORM_90)
+     {
+        c = 0, s = -1, tx = -h;
+        tw = h, th = w;
+     }
+   else if (td->transform == WL_OUTPUT_TRANSFORM_180)
+     {
+        c = -1, s = 0, tx = -w, ty = -h;
+        tw = w, th = h;
+     }
+   else if (td->transform == WL_OUTPUT_TRANSFORM_270)
+     {
+        c = 0, s = 1, ty = -w;
+        tw = h, th = w;
+     }
+   else
+     {
+        c = 1, s = 0;
+        tw = w, th = h;
+     }
+
+   pixman_f_transform_init_identity(&ft);
+   pixman_f_transform_translate(&ft, NULL, tx, ty);
+   pixman_f_transform_rotate(&ft, NULL, c, s);
+
+   pixman_transform_from_pixman_f_transform(&t, &ft);
+   pixman_image_set_transform(src_img, &t);
+
+   pixman_image_composite(PIXMAN_OP_OVER, src_img, NULL, target_image, 0, 0, 0, 0, 0, 0, tw, th);
+
+   // for child data
+   if (td->child_data)
+     {
+        c_x = td->child_data->x;
+        c_y = td->child_data->y;
+        c_w = td->child_data->w;
+        c_h = td->child_data->h;
+
+        c_tx = 0;
+        c_ty = 0;
+        c_tw = c_w;
+        c_th = c_h;
+
+        if (td->child_data->tbm_surface)
+          {
+             c_src_format = _e_capture_image_data_pixman_format_get_from_tbm_surface(tbm_surface_get_format(td->child_data->tbm_surface));
+             c_dst_format = c_src_format;
+
+             tbm_surface_map(td->child_data->tbm_surface, TBM_SURF_OPTION_READ, &c_info);
+             c_src_ptr = c_info.planes[0].ptr;
+
+             c_src_img = pixman_image_create_bits(c_src_format, c_w, c_h, (uint32_t*)c_src_ptr, c_info.planes[0].stride);
+             EINA_SAFETY_ON_NULL_GOTO(c_src_img, clean_up);
+          }
+        else if (td->child_data->shm_buffer_ptr)
+          {
+             c_src_format = _e_capture_image_data_pixman_format_get_from_shm_buffer(td->child_data->shm_buffer_format);
+             c_dst_format = c_src_format;
+
+             c_src_ptr = td->child_data->shm_buffer_ptr;
+             c_src_img = pixman_image_create_bits(c_src_format, c_w, c_h, (uint32_t*)c_src_ptr, c_w * 4);
+             EINA_SAFETY_ON_NULL_GOTO(c_src_img, clean_up);
+          }
+        else
+          {
+             ERR("invalid buffer");
+             goto clean_up;
+          }
+
+        if (td->child_data->transform == WL_OUTPUT_TRANSFORM_90)
+          {
+             c = 0, s = -1, c_tx = -c_h;
+             c_tw = c_h, c_th = c_w;
+          }
+        else if (td->child_data->transform == WL_OUTPUT_TRANSFORM_180)
+          {
+             c = -1, s = 0, c_tx = -c_w, c_ty = -c_h;
+             c_tw = c_w, c_th = c_h;
+          }
+        else if (td->child_data->transform == WL_OUTPUT_TRANSFORM_270)
+          {
+             c = 0, s = 1, c_ty = -c_w;
+             c_tw = c_h, c_th = c_w;
+          }
+        else
+          {
+             c = 1, s = 0;
+             c_tw = c_w, c_th = c_h;
+          }
+
+        c_dst_surface = tbm_surface_create(c_tw, c_th, tbm_surface_get_format(td->child_data->tbm_surface));
+        EINA_SAFETY_ON_NULL_GOTO(c_dst_surface, clean_up);
+
+        tbm_surface_map(c_dst_surface, TBM_SURF_OPTION_WRITE, &c_info);
+        c_dst_ptr = c_info.planes[0].ptr;
+
+        c_dst_img = pixman_image_create_bits(c_dst_format, c_tw, c_th, (uint32_t*)c_dst_ptr, c_info.planes[0].stride);
+        EINA_SAFETY_ON_NULL_GOTO(c_dst_img, clean_up);
+
+        pixman_f_transform_init_identity(&c_ft);
+        pixman_f_transform_translate(&c_ft, NULL, c_tx, c_ty);
+        pixman_f_transform_rotate(&c_ft, NULL, c, s);
+
+        pixman_transform_from_pixman_f_transform(&c_t, &c_ft);
+        pixman_image_set_transform(c_src_img, &c_t);
+
+        pixman_image_composite(PIXMAN_OP_SRC, c_src_img, NULL, c_dst_img, 0, 0, 0, 0, 0, 0, c_tw, c_th);
+
+        CAPDBG("image composite with child. child(win:%zx, ec:%p)",
+               td->ec, "ECC", NULL,
+               e_client_util_win_get(td->child_data->ec), td->child_data->ec);
+
+        pixman_image_composite(PIXMAN_OP_OVER, c_dst_img, NULL, target_image, 0, 0, 0, 0, c_x, c_y, tw, th);
+     }
+
+clean_up:
+   if (td->child_data)
+     {
+        if (c_src_ptr && td->child_data->tbm_surface) tbm_surface_unmap(td->child_data->tbm_surface);
+        if (c_dst_ptr) tbm_surface_unmap(c_dst_surface);
+
+        if (c_dst_surface)
+          {
+             tbm_surface_destroy(c_dst_surface);
+             c_dst_surface = NULL;
+          }
+
+        if (c_src_img) pixman_image_unref(c_src_img);
+        if (c_dst_img) pixman_image_unref(c_dst_img);
+     }
+
+   if (src_ptr && td->tbm_surface) tbm_surface_unmap(td->tbm_surface);
+   if (src_img) pixman_image_unref(src_img);
+}
+
+static void
+_pixman_image_fill_black(pixman_image_t *image)
+{
+   pixman_image_t *black_image;
+   pixman_color_t color;
+   int w, h;
+
+   w = pixman_image_get_width(image);
+   h = pixman_image_get_height(image);
+
+   color.red = 0;
+   color.green = 0;
+   color.blue = 0;
+   color.alpha = 0xffff;
+
+   black_image = pixman_image_create_solid_fill(&color);
+
+   pixman_image_composite(PIXMAN_OP_SRC,
+                          black_image, /* src */
+                          NULL, /* mask */
+                          image, /* dst */
+                          0, 0, /* src x,y */
+                          0, 0, /* mask x,y */
+                          0, 0, /* dst x,y */
+                          w, h);
+   pixman_image_unref(black_image);
+}
+
+static Eina_Bool
+_e_capture_client_capture_with_video(Thread_Data *td)
+{
+   tbm_surface_h main_surface, capture_surface;
+   tbm_surface_info_s info;
+   pixman_image_t *capture_image;
+   unsigned char *capture_data;
+   int w, h;
+   int res;
+   Eina_Bool ret = EINA_TRUE;
+
+   main_surface = td->tbm_surface;
+   w = tbm_surface_get_width(main_surface);
+   h = tbm_surface_get_height(main_surface);
+   if ((w <= 0) || (h <= 0))
+     {
+        ERR("Abnormal buffer size of main surface %dx%d", w, h);
+        return EINA_FALSE;
+     }
+
+   capture_surface = tbm_surface_create(w, h, TBM_FORMAT_ARGB8888);
+   if (!capture_surface)
+     {
+        ERR("Failed to create tbm_surface_h");
+        return EINA_FALSE;
+     }
+
+   res = tbm_surface_map(capture_surface, TBM_SURF_OPTION_READ | TBM_SURF_OPTION_WRITE, &info);
+   if (res != TBM_SURFACE_ERROR_NONE)
+     {
+        ERR("Failed to map tbm_surface_h");
+        ret = EINA_FALSE;
+        goto err_map;
+     }
+
+   capture_data = info.planes[0].ptr;
+   capture_image = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h,
+                                            (uint32_t *)capture_data,
+                                            info.planes[0].stride);
+   if (!capture_image)
+     {
+        ERR("Failed to create pixman_image for capture");
+        ret = EINA_FALSE;
+        goto err_image;
+     }
+
+   _pixman_image_fill_black(capture_image);
+
+   _e_capture_client_video_composite(td, capture_image);
+   _e_capture_client_main_composite(td, capture_image);
+
+   res = tbm_surface_internal_capture_buffer(capture_surface, td->image_dir, td->image_name, "png");
+   if (res != TBM_SURFACE_ERROR_NONE)
+     {
+        ERR("Failed to capture buffer");
+        ret = EINA_FALSE;
+     }
+
+   pixman_image_unref(capture_image);
+err_image:
+   tbm_surface_unmap(capture_surface);
+err_map:
+   tbm_surface_destroy(capture_surface);
+
+   return ret;
+}
+
+static Capture_Data *
+_e_capture_client_video_data_create(E_Client *ec)
+{
+   Capture_Data *capture_data;
+   E_Comp_Wl_Buffer *buffer = NULL;
+   Eina_Bool res;
+
+   buffer = e_pixmap_resource_get(ec->pixmap);
+   if (!buffer)
+     {
+        ERR("Failed to get resource of E_Pixmap");
+        return NULL;
+     }
+
+   capture_data = E_NEW(Capture_Data, 1);
+   if (!capture_data)
+     return NULL;
+
+   CAPDBG("Create video capture data", ec, "Capture_Data", capture_data);
+
+   /* Is referencing E_Client necessary here?
+    * Is it to prevent E_Client from being destoyed?
+    * It seems not necessary E_Client anywhere during capture process. */
+   e_object_ref(E_OBJECT(ec));
+   capture_data->ec = ec;
+
+   /* Referencing buffer here to prevent compositor from signaling release
+    * event for wl_buffer. */
+   e_comp_wl_buffer_reference(&capture_data->buffer_ref, buffer);
+   switch (buffer->type)
+     {
+      case E_COMP_WL_BUFFER_TYPE_VIDEO:
+         /* capture_data->buffer_ref.buffer can indicate NULL pointer
+          * if wl_buffer is destroyed later. */
+         capture_data->tbm_surface = buffer->tbm_surface;
+         if (!capture_data->tbm_surface) goto err;
+
+         /* Referencing tbm_surface_h to prevent it from being freed. */
+         tbm_surface_internal_ref(capture_data->tbm_surface);
+         break;
+      default:
+         ERR("Unsupported buffer type(%d) win(0x%08zx) name(%s)",
+             buffer->type, e_client_util_win_get(ec), ec->icccm.name?:"");
+         goto err;
+     }
+
+   res = e_comp_wl_surface_viewport_get(ec,
+                                        &capture_data->viewport.buffer,
+                                        &capture_data->viewport.output,
+                                        &capture_data->transform);
+   if (!res)
+     {
+        ERR("Failed to get viewport");
+        goto err_vp;
+     }
+
+   CAPDBG("Buffer Size(%dx%d) Viewport(buffer %d,%d %dx%d output %d,%d %dx%d transform %d)",
+          ec, "Capture_Data", capture_data,
+          buffer->w, buffer->h,
+          EINA_RECTANGLE_ARGS(&capture_data->viewport.buffer),
+          EINA_RECTANGLE_ARGS(&capture_data->viewport.output),
+          capture_data->transform);
+
+   return capture_data;
+err_vp:
+   tbm_surface_internal_unref(capture_data->tbm_surface);
+err:
+   e_object_unref(E_OBJECT(ec));
+   e_comp_wl_buffer_reference(&capture_data->buffer_ref, NULL);
+   free(capture_data);
+   return NULL;
+}
+
+static void
+_e_capture_client_video_data_destroy(Capture_Data *capture_data)
+{
+   CAPDBG("Destroy video capture data", capture_data->ec,
+          "Capture_Data", capture_data);
+
+   if (capture_data->shm_pool)
+       wl_shm_pool_unref(capture_data->shm_pool);
+
+   if (capture_data->tbm_surface)
+       tbm_surface_internal_unref(capture_data->tbm_surface);
+
+   e_comp_wl_buffer_reference(&capture_data->buffer_ref, NULL);
+
+   e_object_unref(E_OBJECT(capture_data->ec));
+
+   free(capture_data);
+}
+
+static Eina_Bool
+_e_capture_client_surface_tree_foreach_cb(void *data, E_Client *ec)
+{
+   Thread_Data *td;
+
+   td = data;
+
+   if (e_object_is_del(E_OBJECT(ec)))
+     return EINA_TRUE;
+
+   /* NOTE: Here only handle video client for minimum modification for now.
+    * The other surfaces won't be handled here. However, it must be
+    * handled later. */
+   if (!e_client_is_video(ec))
+     return EINA_TRUE;
+
+   td->video_capture_data = _e_capture_client_video_data_create(ec);
+   if (!td->video_capture_data)
+     ERR("Failed to create video capture data w:0x%08zx ec:%8p",
+         e_client_util_win_get(ec), ec);
+
+   /* NOTE: Returning EINA_FALSE here means that it doesn't want to iterate
+    * surface tree anymore. Because capturing the video surface is supported
+    * for only one surface now.*/
+   return EINA_FALSE;
+}
+#endif
+
 static pixman_format_code_t
 _e_capture_image_data_pixman_format_get_from_tbm_surface(tbm_format format)
 {
@@ -420,7 +1006,21 @@ _e_capture_client_image_data_save(Thread_Data *td)
    shm_buffer_ptr = td->shm_buffer_ptr;
    tbm_surface = td->tbm_surface;
 
+#ifdef ENABLE_VIDEO_CAPTURE
+   if (td->video_capture_data)
+     {
+        ret = _e_capture_client_capture_with_video(td);
+        if (!ret)
+          {
+             CAPDBG("IMG save fail: %s/%s.png",
+                    td->ec, "ECC", NULL, td->image_dir, td->image_name);
+             return EINA_FALSE;
+          }
+     }
+   else if (shm_buffer_ptr)
+#else
    if (shm_buffer_ptr)
+#endif
      {
          stride = td->shm_buffer_stride;
          w = stride / 4;
@@ -689,6 +1289,11 @@ _e_capture_client_child_data_check(Thread_Data *td)
  void
 _e_capture_client_save_thread_data_free(Thread_Data *td)
 {
+#ifdef ENABLE_VIDEO_CAPTURE
+   if (td->video_capture_data)
+     _e_capture_client_video_data_destroy(td->video_capture_data);
+#endif
+
    if (td->child_data)
       {
          _e_capture_client_child_data_release(td);
@@ -920,6 +1525,14 @@ _e_capture_client_save(E_Capture_Client *ecc,
          goto end;
      }
 
+#ifdef ENABLE_VIDEO_CAPTURE
+   /* Don't iterate surface tree if it's video client. */
+   if (!e_client_is_video(td->ec))
+     e_client_surface_tree_foreach(ec,
+                                   _e_capture_client_surface_tree_foreach_cb,
+                                   td);
+#endif
+
    if (!skip_child)
       _e_capture_client_child_data_check(td);