GstBuffer * gst_vaapi_video_buffer_new_from_buffer (GstBuffer * buffer) { GstVaapiVideoMeta *const meta = gst_buffer_get_vaapi_video_meta (buffer); return meta ? new_vbuffer (gst_vaapi_video_meta_ref (meta)) : 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 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 gboolean gst_vaapi_texture_upload (GstVideoGLTextureUploadMeta * meta, guint texture_id[4]) { GstVaapiVideoMeta *const vmeta = gst_buffer_get_vaapi_video_meta (meta->buffer); GstVaapiVideoMetaTexture *const meta_texture = meta->user_data; GstVaapiSurfaceProxy *const proxy = gst_vaapi_video_meta_get_surface_proxy (vmeta); GstVaapiSurface *const surface = gst_vaapi_surface_proxy_get_surface (proxy); GstVaapiDisplay *const dpy = GST_VAAPI_OBJECT_DISPLAY (surface); if (!gst_vaapi_display_has_opengl (dpy)) return FALSE; if (!meta_texture->texture || /* Check whether VA display changed */ GST_VAAPI_OBJECT_DISPLAY (meta_texture->texture) != dpy || /* Check whether texture id changed */ gst_vaapi_texture_get_id (meta_texture->texture) != texture_id[0]) { /* FIXME: should we assume target? */ GstVaapiTexture *const texture = gst_vaapi_texture_new_wrapped (dpy, texture_id[0], GL_TEXTURE_2D, meta_texture->gl_format, meta_texture->width, meta_texture->height); gst_vaapi_texture_replace (&meta_texture->texture, texture); if (!texture) return FALSE; gst_vaapi_texture_unref (texture); } gst_vaapi_texture_set_orientation_flags (meta_texture->texture, get_texture_orientation_flags (meta->texture_orientation)); return gst_vaapi_texture_put_surface (meta_texture->texture, surface, gst_vaapi_surface_proxy_get_crop_rect (proxy), gst_vaapi_video_meta_get_render_flags (vmeta)); }
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; } }
/** * gst_vaapi_plugin_base_get_input_buffer: * @plugin: a #GstVaapiPluginBase * @incaps: the sink pad (input) buffer * @outbuf_ptr: the pointer to location to the VA surface backed buffer * * Acquires the sink pad (input) buffer as a VA surface backed * buffer. This is mostly useful for raw YUV buffers, as source * buffers that are already backed as a VA surface are passed * verbatim. * * Returns: #GST_FLOW_OK if the buffer could be acquired */ GstFlowReturn gst_vaapi_plugin_base_get_input_buffer (GstVaapiPluginBase * plugin, GstBuffer * inbuf, GstBuffer ** outbuf_ptr) { GstVaapiVideoMeta *meta; GstBuffer *outbuf; GstVideoFrame src_frame, out_frame; gboolean success; g_return_val_if_fail (inbuf != NULL, GST_FLOW_ERROR); g_return_val_if_fail (outbuf_ptr != NULL, GST_FLOW_ERROR); meta = gst_buffer_get_vaapi_video_meta (inbuf); if (meta) { *outbuf_ptr = gst_buffer_ref (inbuf); return GST_FLOW_OK; } if (!plugin->sinkpad_caps_is_raw) goto error_invalid_buffer; if (!plugin->sinkpad_buffer_pool) goto error_no_pool; if (!gst_buffer_pool_set_active (plugin->sinkpad_buffer_pool, TRUE)) goto error_active_pool; outbuf = NULL; if (gst_buffer_pool_acquire_buffer (plugin->sinkpad_buffer_pool, &outbuf, NULL) != GST_FLOW_OK) goto error_create_buffer; if (is_dma_buffer (inbuf)) { if (!plugin_bind_dma_to_vaapi_buffer (plugin, inbuf, outbuf)) goto error_bind_dma_buffer; goto done; } if (!gst_video_frame_map (&src_frame, &plugin->sinkpad_info, inbuf, GST_MAP_READ)) goto error_map_src_buffer; if (!gst_video_frame_map (&out_frame, &plugin->sinkpad_info, outbuf, GST_MAP_WRITE)) goto error_map_dst_buffer; success = gst_video_frame_copy (&out_frame, &src_frame); gst_video_frame_unmap (&out_frame); gst_video_frame_unmap (&src_frame); if (!success) goto error_copy_buffer; done: gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1); *outbuf_ptr = outbuf; return GST_FLOW_OK; /* ERRORS */ error_no_pool: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("no buffer pool was negotiated"), ("no buffer pool was negotiated")); return GST_FLOW_ERROR; } error_active_pool: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("failed to activate buffer pool"), ("failed to activate buffer pool")); return GST_FLOW_ERROR; } error_map_dst_buffer: { gst_video_frame_unmap (&src_frame); // fall-through } error_map_src_buffer: { GST_WARNING ("failed to map buffer"); gst_buffer_unref (outbuf); return GST_FLOW_NOT_SUPPORTED; } /* ERRORS */ error_invalid_buffer: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("failed to validate source buffer"), ("failed to validate source buffer")); return GST_FLOW_ERROR; } error_create_buffer: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("Allocation failed"), ("failed to create buffer")); return GST_FLOW_ERROR; } error_bind_dma_buffer: { GST_ELEMENT_ERROR (plugin, STREAM, FAILED, ("Allocation failed"), ("failed to bind dma_buf to VA surface buffer")); gst_buffer_unref (outbuf); return GST_FLOW_ERROR; } error_copy_buffer: { GST_WARNING ("failed to upload buffer to VA surface"); gst_buffer_unref (outbuf); return GST_FLOW_NOT_SUPPORTED; } }
static GstFlowReturn gst_vaapidecode_push_decoded_frame (GstVideoDecoder * vdec, GstVideoCodecFrame * out_frame) { GstVaapiDecode *const decode = GST_VAAPIDECODE (vdec); GstVaapiSurfaceProxy *proxy; GstFlowReturn ret; const GstVaapiRectangle *crop_rect; GstVaapiVideoMeta *meta; guint flags, out_flags = 0; if (!GST_VIDEO_CODEC_FRAME_IS_DECODE_ONLY (out_frame)) { proxy = gst_video_codec_frame_get_user_data (out_frame); /* reconfigure if un-cropped surface resolution changed */ if (is_surface_resolution_changed (vdec, GST_VAAPI_SURFACE_PROXY_SURFACE (proxy))) gst_vaapidecode_negotiate (decode); gst_vaapi_surface_proxy_set_destroy_notify (proxy, (GDestroyNotify) gst_vaapidecode_release, gst_object_ref (decode)); ret = gst_video_decoder_allocate_output_frame (vdec, out_frame); if (ret != GST_FLOW_OK) goto error_create_buffer; 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 GST_CHECK_VERSION(1,5,0) /* First-in-bundle flag only appeared in 1.5 dev */ if (flags & GST_VAAPI_SURFACE_PROXY_FLAG_FFB) { GST_BUFFER_FLAG_SET (out_frame->output_buffer, GST_VIDEO_BUFFER_FLAG_FIRST_IN_BUNDLE); } #endif crop_rect = gst_vaapi_surface_proxy_get_crop_rect (proxy); if (crop_rect) { GstVideoCropMeta *const crop_meta = gst_buffer_add_video_crop_meta (out_frame->output_buffer); if (crop_meta) { crop_meta->x = crop_rect->x; crop_meta->y = crop_rect->y; crop_meta->width = crop_rect->width; crop_meta->height = crop_rect->height; } } #if (USE_GLX || USE_EGL) if (decode->has_texture_upload_meta) gst_buffer_ensure_texture_upload_meta (out_frame->output_buffer); #endif } ret = gst_video_decoder_finish_frame (vdec, out_frame); if (ret != GST_FLOW_OK) goto error_commit_buffer; gst_video_codec_frame_unref (out_frame); 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); gst_video_codec_frame_unref (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); gst_video_codec_frame_unref (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); gst_video_codec_frame_unref (out_frame); return ret; } }
static GstFlowReturn gst_vaapi_video_buffer_pool_acquire_buffer (GstBufferPool * pool, GstBuffer ** out_buffer_ptr, GstBufferPoolAcquireParams * params) { GstVaapiVideoBufferPoolPrivate *const priv = GST_VAAPI_VIDEO_BUFFER_POOL (pool)->priv; GstVaapiVideoBufferPoolAcquireParams *const priv_params = (GstVaapiVideoBufferPoolAcquireParams *) params; GstFlowReturn ret; GstBuffer *buffer; GstMemory *mem; GstVaapiVideoMeta *meta; GstVaapiSurface *surface; GstVaapiBufferProxy *dmabuf_proxy; ret = GST_BUFFER_POOL_CLASS (gst_vaapi_video_buffer_pool_parent_class)->acquire_buffer (pool, &buffer, params); if (!priv->use_dmabuf_memory || !params || !priv_params->proxy || ret != GST_FLOW_OK) { *out_buffer_ptr = buffer; return ret; } /* The point of the following dance is to attach the right GstMemory to the * current acquired buffer. Indeed this buffer can contain any of the * GstFdmemory since this buffer have been popped out from the buffer pool's * FIFO. So there is no garantee that this matches the current surface. The * va decoder driver might not even use a FIFO. So there is no way to guess * on the ordering. In short acquire_current_buffer on the va driver and on * the buffer pool return none matching data. So we have to manually attach * the right GstFdMemory to the acquired GstBuffer. The right GstMemory is * the one associated with the current surface. */ g_assert (gst_buffer_n_memory (buffer) == 1); /* Find the cached memory associated with the given surface. */ surface = GST_VAAPI_SURFACE_PROXY_SURFACE (priv_params->proxy); dmabuf_proxy = gst_vaapi_surface_peek_buffer_proxy (surface); if (dmabuf_proxy) { mem = gst_vaapi_buffer_proxy_peek_mem (dmabuf_proxy); if (mem == gst_buffer_peek_memory (buffer, 0)) mem = NULL; else mem = gst_memory_ref (mem); } else { /* The given surface has not been exported yet. */ meta = gst_buffer_get_vaapi_video_meta (buffer); if (gst_vaapi_video_meta_get_surface_proxy (meta)) gst_vaapi_video_meta_set_surface_proxy (meta, priv_params->proxy); mem = gst_vaapi_dmabuf_memory_new (priv->allocator, gst_buffer_get_vaapi_video_meta (buffer)); } /* Attach the GstFdMemory to the output buffer. */ if (mem) { GST_DEBUG_OBJECT (pool, "assigning memory %p to acquired buffer %p", mem, buffer); gst_buffer_replace_memory (buffer, 0, mem); gst_buffer_unset_flags (buffer, GST_BUFFER_FLAG_TAG_MEMORY); } *out_buffer_ptr = buffer; return GST_FLOW_OK; }
static GstFlowReturn gst_vaapiencode_handle_frame (GstVideoEncoder * venc, GstVideoCodecFrame * frame) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc); GstVaapiEncoderStatus status; GstVaapiVideoMeta *meta; GstVaapiSurfaceProxy *proxy; GstFlowReturn ret; GstBuffer *buf; buf = NULL; ret = gst_vaapi_plugin_base_get_input_buffer (GST_VAAPI_PLUGIN_BASE (encode), frame->input_buffer, &buf); if (ret != GST_FLOW_OK) goto error_buffer_invalid; gst_buffer_replace (&frame->input_buffer, buf); gst_buffer_unref (buf); meta = gst_buffer_get_vaapi_video_meta (buf); if (!meta) goto error_buffer_no_meta; proxy = gst_vaapi_video_meta_get_surface_proxy (meta); if (!proxy) goto error_buffer_no_surface_proxy; gst_video_codec_frame_set_user_data (frame, gst_vaapi_surface_proxy_ref (proxy), (GDestroyNotify) gst_vaapi_surface_proxy_unref); GST_VIDEO_ENCODER_STREAM_UNLOCK (encode); status = gst_vaapi_encoder_put_frame (encode->encoder, frame); GST_VIDEO_ENCODER_STREAM_LOCK (encode); if (status < GST_VAAPI_ENCODER_STATUS_SUCCESS) goto error_encode_frame; gst_video_codec_frame_unref (frame); return GST_FLOW_OK; /* ERRORS */ error_buffer_invalid: { if (buf) gst_buffer_unref (buf); gst_video_codec_frame_unref (frame); return ret; } error_buffer_no_meta: { GST_ERROR ("failed to get GstVaapiVideoMeta information"); gst_video_codec_frame_unref (frame); return GST_FLOW_ERROR; } error_buffer_no_surface_proxy: { GST_ERROR ("failed to get VA surface proxy"); gst_video_codec_frame_unref (frame); return GST_FLOW_ERROR; } error_encode_frame: { GST_ERROR ("failed to encode frame %d (status %d)", frame->system_frame_number, status); gst_video_codec_frame_unref (frame); return GST_FLOW_ERROR; } }