/** * gst_vaapi_surface_proxy_new_from_pool: * @pool: a #GstVaapiSurfacePool * * Allocates a new surface from the supplied surface @pool and creates * the wrapped surface proxy object from it. When the last reference * to the proxy object is released, then the underlying VA surface is * pushed back to its parent pool. * * Returns: The same newly allocated @proxy object, or %NULL on error */ GstVaapiSurfaceProxy * gst_vaapi_surface_proxy_new_from_pool (GstVaapiSurfacePool * pool) { GstVaapiSurfaceProxy *proxy; g_return_val_if_fail (pool != NULL, NULL); proxy = (GstVaapiSurfaceProxy *) gst_vaapi_mini_object_new (gst_vaapi_surface_proxy_class ()); if (!proxy) return NULL; proxy->parent = NULL; proxy->destroy_func = NULL; proxy->pool = gst_vaapi_video_pool_ref (GST_VAAPI_VIDEO_POOL (pool)); proxy->surface = gst_vaapi_video_pool_get_object (proxy->pool); if (!proxy->surface) goto error; gst_vaapi_object_ref (proxy->surface); gst_vaapi_surface_proxy_init_properties (proxy); return proxy; /* ERRORS */ error: { gst_vaapi_surface_proxy_unref (proxy); return NULL; } }
/** * gst_vaapi_surface_proxy_new: * @surface: a #GstVaapiSurface * * Creates a new #GstVaapiSurfaceProxy with the specified * surface. This allows for transporting additional information that * are not to be attached to the @surface directly. * * Return value: the newly allocated #GstVaapiSurfaceProxy object */ GstVaapiSurfaceProxy * gst_vaapi_surface_proxy_new (GstVaapiSurface * surface) { GstVaapiSurfaceProxy *proxy; g_return_val_if_fail (surface != NULL, NULL); proxy = (GstVaapiSurfaceProxy *) gst_vaapi_mini_object_new (gst_vaapi_surface_proxy_class ()); if (!proxy) return NULL; proxy->parent = NULL; proxy->destroy_func = NULL; proxy->pool = NULL; proxy->surface = gst_vaapi_object_ref (surface); if (!proxy->surface) goto error; gst_vaapi_surface_proxy_init_properties (proxy); return proxy; /* ERRORS */ error: { gst_vaapi_surface_proxy_unref (proxy); return NULL; } }
static gboolean plugin_bind_dma_to_vaapi_buffer (GstVaapiPluginBase * plugin, GstBuffer * inbuf, GstBuffer * outbuf) { GstVideoInfo *const vip = &plugin->sinkpad_info; GstVaapiVideoMeta *meta; GstVaapiSurface *surface; GstVaapiSurfaceProxy *proxy; gint fd; fd = gst_dmabuf_memory_get_fd (gst_buffer_peek_memory (inbuf, 0)); if (fd < 0) return FALSE; if (!plugin_update_sinkpad_info_from_buffer (plugin, inbuf)) goto error_update_sinkpad_info; meta = gst_buffer_get_vaapi_video_meta (outbuf); g_return_val_if_fail (meta != NULL, FALSE); /* Check for a VASurface cached in the buffer */ surface = _get_cached_surface (inbuf); if (!surface) { /* otherwise create one and cache it */ surface = gst_vaapi_surface_new_with_dma_buf_handle (plugin->display, fd, vip); if (!surface) goto error_create_surface; _set_cached_surface (inbuf, surface); } proxy = gst_vaapi_surface_proxy_new (surface); if (!proxy) goto error_create_proxy; gst_vaapi_video_meta_set_surface_proxy (meta, proxy); gst_vaapi_surface_proxy_unref (proxy); gst_buffer_add_parent_buffer_meta (outbuf, inbuf); return TRUE; /* ERRORS */ error_update_sinkpad_info: { GST_ERROR_OBJECT (plugin, "failed to update sink pad video info from video meta"); return FALSE; } error_create_surface: { GST_ERROR_OBJECT (plugin, "failed to create VA surface from dma_buf handle"); return FALSE; } error_create_proxy: { GST_ERROR_OBJECT (plugin, "failed to create VA surface proxy from wrapped VA surface"); return FALSE; } }
static gboolean set_surface_proxy_from_pool (GstVaapiVideoMeta * meta, GstVaapiVideoPool * pool) { GstVaapiSurfaceProxy *proxy; gboolean success; proxy = gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL (pool)); if (!proxy) return FALSE; success = set_surface_proxy (meta, proxy); gst_vaapi_surface_proxy_unref (proxy); return success; }
static gboolean plugin_bind_dma_to_vaapi_buffer (GstVaapiPluginBase * plugin, GstBuffer * inbuf, GstBuffer * outbuf) { GstVideoInfo *const vip = &plugin->sinkpad_info; GstVaapiVideoMeta *meta; GstVaapiSurface *surface; GstVaapiSurfaceProxy *proxy; gint fd; fd = gst_dmabuf_memory_get_fd (gst_buffer_peek_memory (inbuf, 0)); if (fd < 0) return FALSE; if (!plugin_update_sinkpad_info_from_buffer (plugin, inbuf)) goto error_update_sinkpad_info; meta = gst_buffer_get_vaapi_video_meta (outbuf); g_return_val_if_fail (meta != NULL, FALSE); surface = gst_vaapi_surface_new_with_dma_buf_handle (plugin->display, fd, GST_VIDEO_INFO_SIZE (vip), GST_VIDEO_INFO_FORMAT (vip), GST_VIDEO_INFO_WIDTH (vip), GST_VIDEO_INFO_HEIGHT (vip), vip->offset, vip->stride); if (!surface) goto error_create_surface; proxy = gst_vaapi_surface_proxy_new (surface); gst_vaapi_object_unref (surface); if (!proxy) goto error_create_proxy; gst_vaapi_surface_proxy_set_destroy_notify (proxy, (GDestroyNotify) gst_buffer_unref, (gpointer) gst_buffer_ref (inbuf)); gst_vaapi_video_meta_set_surface_proxy (meta, proxy); gst_vaapi_surface_proxy_unref (proxy); return TRUE; /* ERRORS */ error_update_sinkpad_info: GST_ERROR ("failed to update sink pad video info from video meta"); return FALSE; error_create_surface: GST_ERROR ("failed to create VA surface from dma_buf handle"); return FALSE; error_create_proxy: GST_ERROR ("failed to create VA surface proxy from wrapped VA surface"); return FALSE; }
static GstFlowReturn gst_vaapidecode_push_decoded_frame (GstVideoDecoder * vdec, GstVideoCodecFrame * out_frame) { GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec); GstVaapiSurfaceProxy *proxy; GstVaapiSurface *surface; GstFlowReturn ret; const GstVaapiRectangle *crop_rect; GstVaapiVideoMeta *meta; GstBufferPoolAcquireParams *params = NULL; GstVaapiVideoBufferPoolAcquireParams vaapi_params = { {0,}, }; guint flags, out_flags = 0; gboolean alloc_renegotiate, caps_renegotiate; if (!GST_VIDEO_CODEC_FRAME_IS_DECODE_ONLY (out_frame)) { proxy = gst_video_codec_frame_get_user_data (out_frame); surface = GST_VAAPI_SURFACE_PROXY_SURFACE (proxy); crop_rect = gst_vaapi_surface_proxy_get_crop_rect (proxy); /* in theory, we are not supposed to check the surface resolution * change here since it should be advertised before from ligstvaapi. * But there are issues with it especially for some vp9 streams where * upstream element set un-cropped values in set_format() which make * everything a mess. So better doing the explicit check here irrespective * of what notification we get from upstream or libgstvaapi.Also, even if * we received notification from libgstvaapi, the frame we are going to * be pushed at this point might not have the notified resolution if there * are queued frames in decoded picture buffer. */ alloc_renegotiate = is_surface_resolution_changed (decode, surface); caps_renegotiate = is_display_resolution_changed (decode, crop_rect); if (gst_pad_needs_reconfigure (GST_VIDEO_DECODER_SRC_PAD (vdec)) || alloc_renegotiate || caps_renegotiate || decode->do_renego) { g_atomic_int_set (&decode->do_renego, FALSE); if (!gst_vaapidecode_negotiate (decode)) return GST_FLOW_ERROR; } gst_vaapi_surface_proxy_set_destroy_notify (proxy, (GDestroyNotify) gst_vaapidecode_release, gst_object_ref (decode)); if (is_src_allocator_dmabuf (decode)) { vaapi_params.proxy = gst_vaapi_surface_proxy_ref (proxy); params = (GstBufferPoolAcquireParams *) & vaapi_params; } ret = gst_video_decoder_allocate_output_frame_with_params (vdec, out_frame, params); if (params) gst_vaapi_surface_proxy_unref (vaapi_params.proxy); if (ret != GST_FLOW_OK) goto error_create_buffer; /* if not dmabuf is negotiated set the vaapi video meta in the * proxy */ if (!params) { meta = gst_buffer_get_vaapi_video_meta (out_frame->output_buffer); if (!meta) goto error_get_meta; gst_vaapi_video_meta_set_surface_proxy (meta, proxy); } flags = gst_vaapi_surface_proxy_get_flags (proxy); if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_CORRUPTED) out_flags |= GST_BUFFER_FLAG_CORRUPTED; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_INTERLACED) { out_flags |= GST_VIDEO_BUFFER_FLAG_INTERLACED; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_TFF) out_flags |= GST_VIDEO_BUFFER_FLAG_TFF; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_RFF) out_flags |= GST_VIDEO_BUFFER_FLAG_RFF; if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_ONEFIELD) out_flags |= GST_VIDEO_BUFFER_FLAG_ONEFIELD; } GST_BUFFER_FLAG_SET (out_frame->output_buffer, out_flags); if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_FFB) { GST_BUFFER_FLAG_SET (out_frame->output_buffer, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE); } #if (USE_GLX || USE_EGL) if (decode->has_texture_upload_meta) gst_buffer_ensure_texture_upload_meta (out_frame->output_buffer); #endif } if (decode->in_segment.rate < 0.0 && !GST_VIDEO_CODEC_FRAME_IS_SYNC_POINT (out_frame)) { GST_TRACE_OBJECT (decode, "drop frame in reverse playback"); gst_video_decoder_release_frame (GST_VIDEO_DECODER (decode), out_frame); return GST_FLOW_OK; } ret = gst_video_decoder_finish_frame (vdec, out_frame); if (ret != GST_FLOW_OK) goto error_commit_buffer; return GST_FLOW_OK; /* ERRORS */ error_create_buffer: { const GstVaapiID surface_id = gst_vaapi_surface_get_id (GST_VAAPI_SURFACE_PROXY_SURFACE (proxy)); GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Failed to create sink buffer"), ("video sink failed to create video buffer for proxy'ed " "surface %" GST_VAAPI_ID_FORMAT, GST_VAAPI_ID_ARGS (surface_id))); gst_video_decoder_drop_frame (vdec, out_frame); return GST_FLOW_ERROR; } error_get_meta: { GST_ELEMENT_ERROR (vdec, STREAM, FAILED, ("Failed to get vaapi video meta attached to video buffer"), ("Failed to get vaapi video meta attached to video buffer")); gst_video_decoder_drop_frame (vdec, out_frame); return GST_FLOW_ERROR; } error_commit_buffer: { GST_INFO_OBJECT (decode, "downstream element rejected the frame (%s [%d])", gst_flow_get_name (ret), ret); return ret; } }
GstMemory * gst_vaapi_dmabuf_memory_new (GstAllocator * allocator, GstVaapiVideoMeta * meta) { GstMemory *mem; GstVaapiDisplay *display; GstVaapiSurface *surface; GstVaapiSurfaceProxy *proxy; GstVaapiBufferProxy *dmabuf_proxy; gint dmabuf_fd; const GstVideoInfo *vip; guint flags; g_return_val_if_fail (allocator != NULL, NULL); g_return_val_if_fail (meta != NULL, NULL); vip = gst_allocator_get_vaapi_video_info (allocator, &flags); if (!vip) return NULL; display = gst_vaapi_video_meta_get_display (meta); if (!meta) return NULL; surface = gst_vaapi_surface_new_full (display, vip, flags); if (!surface) goto error_create_surface; proxy = gst_vaapi_surface_proxy_new (surface); if (!proxy) goto error_create_surface_proxy; dmabuf_proxy = gst_vaapi_surface_get_dma_buf_handle (surface); gst_vaapi_object_unref (surface); if (!dmabuf_proxy) goto error_create_dmabuf_proxy; gst_vaapi_video_meta_set_surface_proxy (meta, proxy); gst_vaapi_surface_proxy_unref (proxy); dmabuf_fd = gst_vaapi_buffer_proxy_get_handle (dmabuf_proxy); if (dmabuf_fd < 0 || (dmabuf_fd = dup (dmabuf_fd)) < 0) goto error_create_dmabuf_handle; mem = gst_dmabuf_allocator_alloc (allocator, dmabuf_fd, gst_vaapi_buffer_proxy_get_size (dmabuf_proxy)); if (!mem) goto error_create_dmabuf_memory; gst_mini_object_set_qdata (GST_MINI_OBJECT_CAST (mem), GST_VAAPI_BUFFER_PROXY_QUARK, dmabuf_proxy, (GDestroyNotify) gst_vaapi_buffer_proxy_unref); return mem; /* ERRORS */ error_create_surface: { GST_ERROR ("failed to create VA surface (format:%s size:%ux%u)", gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (vip)), GST_VIDEO_INFO_WIDTH (vip), GST_VIDEO_INFO_HEIGHT (vip)); return NULL; } error_create_surface_proxy: { GST_ERROR ("failed to create VA surface proxy"); gst_vaapi_object_unref (surface); return NULL; } error_create_dmabuf_proxy: { GST_ERROR ("failed to export VA surface to DMABUF"); gst_vaapi_surface_proxy_unref (proxy); return NULL; } error_create_dmabuf_handle: { GST_ERROR ("failed to duplicate DMABUF handle"); gst_vaapi_buffer_proxy_unref (dmabuf_proxy); return NULL; } error_create_dmabuf_memory: { GST_ERROR ("failed to create DMABUF memory"); gst_vaapi_buffer_proxy_unref (dmabuf_proxy); return NULL; } }
static int app_run (App * app) { GstVaapiImage *image; GstVaapiVideoPool *pool; GThread *buffer_thread; gsize id; int ret = EXIT_FAILURE; image = gst_vaapi_image_new (app->display, GST_VIDEO_FORMAT_I420, app->parser->width, app->parser->height); { GstVideoInfo vi; gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_ENCODED, app->parser->width, app->parser->height); pool = gst_vaapi_surface_pool_new_full (app->display, &vi, 0); } buffer_thread = g_thread_new ("get buffer thread", get_buffer_thread, app); while (1) { GstVaapiSurfaceProxy *proxy; GstVaapiSurface *surface; if (!load_frame (app, image)) break; if (!gst_vaapi_image_unmap (image)) break; proxy = gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL (pool)); if (!proxy) { g_warning ("Could not get surface proxy from pool."); break; } surface = gst_vaapi_surface_proxy_get_surface (proxy); if (!surface) { g_warning ("Could not get surface from proxy."); break; } if (!gst_vaapi_surface_put_image (surface, image)) { g_warning ("Could not update surface"); break; } if (!upload_frame (app->encoder, proxy)) { g_warning ("put frame failed"); break; } app->read_frames++; id = gst_vaapi_surface_get_id (surface); g_debug ("input frame %d, surface id = %" G_GSIZE_FORMAT, app->read_frames, id); gst_vaapi_surface_proxy_unref (proxy); } app->input_stopped = TRUE; g_thread_join (buffer_thread); if (!app->encode_failed && feof (app->parser->fp)) ret = EXIT_SUCCESS; gst_vaapi_video_pool_replace (&pool, NULL); gst_vaapi_object_unref (image); return ret; }