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; }
/* Creates a new VA surface object proxy, backed from a pool and useful to allocate reconstructed surfaces */ GstVaapiSurfaceProxy * gst_vaapi_encoder_create_surface (GstVaapiEncoder * encoder) { GstVaapiSurfaceProxy *proxy; g_return_val_if_fail (encoder->context != NULL, NULL); g_mutex_lock (&encoder->mutex); for (;;) { proxy = gst_vaapi_context_get_surface_proxy (encoder->context); if (proxy) break; /* Wait for a free surface proxy to become available */ g_cond_wait (&encoder->surface_free, &encoder->mutex); } g_mutex_unlock (&encoder->mutex); gst_vaapi_surface_proxy_set_destroy_notify (proxy, (GDestroyNotify) _surface_proxy_released_notify, encoder); return proxy; }
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; } }
static gpointer decoder_thread (gpointer data) { App *const app = data; GError *error = NULL; GstVaapiDecoderStatus status; GstVaapiSurfaceProxy *proxy; RenderFrame *rfp; GstBuffer *buffer; GstClockTime pts; gboolean got_surface, got_eos = FALSE; gint64 end_time; guint ofs; g_print ("Decoder thread started\n"); #define SEND_ERROR(...) \ do { \ error = g_error_new(APP_ERROR, APP_ERROR_DECODER, __VA_ARGS__); \ goto send_error; \ } while (0) pts = g_get_monotonic_time (); ofs = 0; while (!app->decoder_thread_cancel) { if (G_UNLIKELY (ofs == app->file_size)) buffer = NULL; else { const gsize size = MIN (4096, app->file_size - ofs); buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, app->file_data, app->file_size, ofs, size, NULL, NULL); if (!buffer) SEND_ERROR ("failed to allocate new buffer"); ofs += size; } if (!gst_vaapi_decoder_put_buffer (app->decoder, buffer)) SEND_ERROR ("failed to push buffer to decoder"); gst_buffer_replace (&buffer, NULL); get_surface: status = gst_vaapi_decoder_get_surface (app->decoder, &proxy); switch (status) { case GST_VAAPI_DECODER_STATUS_SUCCESS: gst_vaapi_surface_proxy_set_destroy_notify (proxy, (GDestroyNotify) decoder_release, app); rfp = render_frame_new (); if (!rfp) SEND_ERROR ("failed to allocate render frame"); rfp->proxy = proxy; rfp->pts = pts; rfp->duration = app->frame_duration; pts += app->frame_duration; g_async_queue_push (app->decoder_queue, rfp); break; case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA: /* nothing to do, just continue to the next iteration */ break; case GST_VAAPI_DECODER_STATUS_END_OF_STREAM: gst_vaapi_decoder_flush (app->decoder); if (got_eos) goto send_eos; got_eos = TRUE; break; case GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE: end_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND; g_mutex_lock (&app->mutex); got_surface = g_cond_wait_until (&app->decoder_ready, &app->mutex, end_time); g_mutex_unlock (&app->mutex); if (got_surface) goto get_surface; SEND_ERROR ("failed to acquire a surface within one second"); break; default: SEND_ERROR ("%s", get_decoder_status_string (status)); break; } } return NULL; #undef SEND_ERROR send_eos: app_send_eos (app); return NULL; send_error: app_send_error (app, error); return NULL; }
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; } }