/* must be called with the render lock */
static void
render_last_buffer (GstWaylandSink * sink)
{
  GstWlMeta *meta;
  struct wl_surface *surface;
  struct wl_callback *callback;

  meta = gst_buffer_get_wl_meta (sink->last_buffer);
  surface = gst_wl_window_get_wl_surface (sink->window);

  g_atomic_int_set (&sink->redraw_pending, TRUE);
  callback = wl_surface_frame (surface);
  wl_callback_add_listener (callback, &frame_callback_listener, sink);

  /* Here we essentially add a reference to the buffer. This represents
   * the fact that the compositor is using the buffer and it should
   * not return back to the pool and be reused until the compositor
   * releases it. The release is handled internally in the pool */
  gst_wayland_compositor_acquire_buffer (meta->pool, sink->last_buffer);

  wl_surface_attach (surface, meta->wbuffer, 0, 0);
  wl_surface_damage (surface, 0, 0, sink->window->surface_width,
      sink->window->surface_height);

  wl_surface_commit (surface);
  wl_display_flush (sink->display->display);
}
Beispiel #2
0
static void
unref_used_buffers (gpointer key, gpointer value, gpointer data)
{
  GstBuffer *buffer = value;
  GstWlMeta *meta = gst_buffer_get_wl_meta (buffer);
  GList **to_unref = data;

  if (meta->used_by_compositor) {
    meta->used_by_compositor = FALSE;
    *to_unref = g_list_prepend (*to_unref, buffer);
  }
}
Beispiel #3
0
void
gst_wayland_compositor_acquire_buffer (GstWaylandBufferPool * self,
    GstBuffer * buffer)
{
  GstWlMeta *meta;

  meta = gst_buffer_get_wl_meta (buffer);
  g_return_if_fail (meta != NULL);
  g_return_if_fail (meta->pool == self);
  g_return_if_fail (meta->used_by_compositor == FALSE);

  meta->used_by_compositor = TRUE;
  gst_buffer_ref (buffer);
}
Beispiel #4
0
static void
buffer_release (void *data, struct wl_buffer *wl_buffer)
{
  GstWaylandBufferPool *self = data;
  GstBuffer *buffer;
  GstWlMeta *meta;

  g_mutex_lock (&self->buffers_map_mutex);
  buffer = g_hash_table_lookup (self->buffers_map, wl_buffer);

  GST_LOG_OBJECT (self, "wl_buffer::release (GstBuffer: %p)", buffer);

  if (buffer) {
    meta = gst_buffer_get_wl_meta (buffer);
    if (meta->used_by_compositor) {
      meta->used_by_compositor = FALSE;
      /* unlock before unref because stop() may be called from here */
      g_mutex_unlock (&self->buffers_map_mutex);
      gst_buffer_unref (buffer);
      return;
    }
  }
  g_mutex_unlock (&self->buffers_map_mutex);
}
static GstFlowReturn
gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
{
  GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
  GstVideoRectangle src, dst, res;
  GstBuffer *to_render;
  GstWlMeta *meta;
  GstFlowReturn ret;
  struct window *window;
  struct display *display;

  GST_LOG_OBJECT (sink, "render buffer %p", buffer);
  if (!sink->window)
    create_window (sink, sink->display, sink->video_width, sink->video_height);

  window = sink->window;
  display = sink->display;

  meta = gst_buffer_get_wl_meta (buffer);

  if (window->redraw_pending) {
    wl_display_dispatch (display->display);
  }

  if (meta && meta->sink == sink) {
    GST_LOG_OBJECT (sink, "buffer %p from our pool, writing directly", buffer);
    to_render = buffer;
  } else {
    GstMapInfo src;
    GST_LOG_OBJECT (sink, "buffer %p not from our pool, copying", buffer);

    if (!sink->pool)
      goto no_pool;

    if (!gst_buffer_pool_set_active (sink->pool, TRUE))
      goto activate_failed;

    ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
    if (ret != GST_FLOW_OK)
      goto no_buffer;

    gst_buffer_map (buffer, &src, GST_MAP_READ);
    gst_buffer_fill (to_render, 0, src.data, src.size);
    gst_buffer_unmap (buffer, &src);

    meta = gst_buffer_get_wl_meta (to_render);
  }

  src.w = sink->video_width;
  src.h = sink->video_height;
  dst.w = sink->window->width;
  dst.h = sink->window->height;

  gst_video_sink_center_rect (src, dst, &res, FALSE);

  wl_surface_attach (sink->window->surface, meta->wbuffer, 0, 0);
  wl_surface_damage (sink->window->surface, 0, 0, res.w, res.h);
  window->redraw_pending = TRUE;
  window->callback = wl_surface_frame (window->surface);
  wl_callback_add_listener (window->callback, &frame_callback_listener, window);
  wl_surface_commit (window->surface);
  wl_display_dispatch (display->display);

  if (buffer != to_render)
    gst_buffer_unref (to_render);
  return GST_FLOW_OK;

no_buffer:
  {
    GST_WARNING_OBJECT (sink, "could not create image");
    return ret;
  }
no_pool:
  {
    GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
        ("Internal error: can't allocate images"),
        ("We don't have a bufferpool negotiated"));
    return GST_FLOW_ERROR;
  }
activate_failed:
  {
    GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
    ret = GST_FLOW_ERROR;
    return ret;
  }
}
static GstFlowReturn
gst_wayland_sink_render (GstBaseSink * bsink, GstBuffer * buffer)
{
  GstWaylandSink *sink = GST_WAYLAND_SINK (bsink);
  GstBuffer *to_render;
  GstWlMeta *meta;
  GstFlowReturn ret = GST_FLOW_OK;

  g_mutex_lock (&sink->render_lock);

  GST_LOG_OBJECT (sink, "render buffer %p", buffer);

  if (G_UNLIKELY (!sink->window)) {
    /* ask for window handle. Unlock render_lock while doing that because
     * set_window_handle & friends will lock it in this context */
    g_mutex_unlock (&sink->render_lock);
    gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (sink));
    g_mutex_lock (&sink->render_lock);

    if (sink->window) {
      /* inform the window about our caps */
      gst_wl_window_set_video_info (sink->window, &sink->video_info);
    } else {
      /* if we were not provided a window, create one ourselves */
      sink->window =
          gst_wl_window_new_toplevel (sink->display, &sink->video_info);
    }
    sink->video_info_changed = FALSE;
  }

  /* drop buffers until we get a frame callback */
  if (g_atomic_int_get (&sink->redraw_pending) == TRUE)
    goto done;

  if (G_UNLIKELY (sink->video_info_changed)) {
    gst_wl_window_set_video_info (sink->window, &sink->video_info);
    sink->video_info_changed = FALSE;
  }

  /* now that we have for sure set the video info on the window, it must have
   * a valid size, otherwise this means that the application has called
   * set_window_handle() without calling set_render_rectangle(), which is
   * absolutely necessary for us.
   */
  if (G_UNLIKELY (sink->window->surface_width == 0 ||
          sink->window->surface_height == 0))
    goto no_window_size;

  meta = gst_buffer_get_wl_meta (buffer);

  if (meta && meta->pool->display == sink->display) {
    GST_LOG_OBJECT (sink, "buffer %p from our pool, writing directly", buffer);
    to_render = buffer;
  } else {
    GstMapInfo src;
    GST_LOG_OBJECT (sink, "buffer %p not from our pool, copying", buffer);

    if (!sink->pool)
      goto no_pool;

    if (!gst_buffer_pool_set_active (sink->pool, TRUE))
      goto activate_failed;

    ret = gst_buffer_pool_acquire_buffer (sink->pool, &to_render, NULL);
    if (ret != GST_FLOW_OK)
      goto no_buffer;

    gst_buffer_map (buffer, &src, GST_MAP_READ);
    gst_buffer_fill (to_render, 0, src.data, src.size);
    gst_buffer_unmap (buffer, &src);
  }

  gst_buffer_replace (&sink->last_buffer, to_render);
  render_last_buffer (sink);

  if (buffer != to_render)
    gst_buffer_unref (to_render);
  goto done;

no_window_size:
  {
    GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
        ("Window has no size set"),
        ("Make sure you set the size after calling set_window_handle"));
    ret = GST_FLOW_ERROR;
    goto done;
  }
no_buffer:
  {
    GST_WARNING_OBJECT (sink, "could not create image");
    goto done;
  }
no_pool:
  {
    GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
        ("Internal error: can't allocate images"),
        ("We don't have a bufferpool negotiated"));
    ret = GST_FLOW_ERROR;
    goto done;
  }
activate_failed:
  {
    GST_ERROR_OBJECT (sink, "failed to activate bufferpool.");
    ret = GST_FLOW_ERROR;
    goto done;
  }
done:
  {
    g_mutex_unlock (&sink->render_lock);
    return ret;
  }
}