/** * gst_vaapi_surface_proxy_get_surface: * @proxy: a #GstVaapiSurfaceProxy * * Returns the #GstVaapiSurface stored in the @proxy. * * Return value: the #GstVaapiSurface */ GstVaapiSurface * gst_vaapi_surface_proxy_get_surface (GstVaapiSurfaceProxy * proxy) { g_return_val_if_fail (proxy != NULL, NULL); return GST_VAAPI_SURFACE_PROXY_SURFACE (proxy); }
/** * gst_vaapi_video_meta_get_surface: * @meta: a #GstVaapiVideoMeta * * Retrieves the #GstVaapiSurface bound to the @meta. The @meta * owns the #GstVaapiSurface so the caller is responsible for calling * gst_vaapi_object_ref() when needed. * * Return value: the #GstVaapiSurface bound to the @meta, or %NULL if * there is none */ GstVaapiSurface * gst_vaapi_video_meta_get_surface (GstVaapiVideoMeta * meta) { g_return_val_if_fail (GST_VAAPI_IS_VIDEO_META (meta), NULL); return ensure_surface_proxy (meta) ? GST_VAAPI_SURFACE_PROXY_SURFACE (meta->proxy) : NULL; }
static gboolean set_surface_proxy (GstVaapiVideoMeta * meta, GstVaapiSurfaceProxy * proxy) { GstVaapiSurface *surface; surface = GST_VAAPI_SURFACE_PROXY_SURFACE (proxy); if (!surface) return FALSE; meta->proxy = gst_vaapi_surface_proxy_ref (proxy); set_display (meta, gst_vaapi_object_get_display (GST_VAAPI_OBJECT (surface))); return TRUE; }
static gboolean ensure_surface (GstVaapiVideoMemory * mem) { if (!mem->proxy) { gst_vaapi_surface_proxy_replace (&mem->proxy, gst_vaapi_video_meta_get_surface_proxy (mem->meta)); if (!mem->proxy) { mem->proxy = new_surface_proxy (mem); if (!mem->proxy) return FALSE; gst_vaapi_video_meta_set_surface_proxy (mem->meta, mem->proxy); } } mem->surface = GST_VAAPI_SURFACE_PROXY_SURFACE (mem->proxy); return mem->surface != NULL; }
static GstVaapiEncoderStatus gst_vaapi_encoder_mpeg2_encode (GstVaapiEncoder * base_encoder, GstVaapiEncPicture * picture, GstVaapiCodedBufferProxy * codedbuf) { GstVaapiEncoderMpeg2 *const encoder = GST_VAAPI_ENCODER_MPEG2_CAST (base_encoder); GstVaapiEncoderStatus ret = GST_VAAPI_ENCODER_STATUS_ERROR_UNKNOWN; GstVaapiSurfaceProxy *reconstruct = NULL; reconstruct = gst_vaapi_encoder_create_surface (base_encoder); g_assert (GST_VAAPI_SURFACE_PROXY_SURFACE (reconstruct)); if (!ensure_sequence (encoder, picture)) goto error; if (!ensure_picture (encoder, picture, codedbuf, reconstruct)) goto error; if (!set_misc_parameters (encoder, picture)) goto error; if (!ensure_slices (encoder, picture)) goto error; if (!gst_vaapi_enc_picture_encode (picture)) goto error; if (picture->type != GST_VAAPI_PICTURE_TYPE_B) { if (encoder->new_gop) clear_references (encoder); push_reference (encoder, reconstruct); } else if (reconstruct) gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder), reconstruct); return GST_VAAPI_ENCODER_STATUS_SUCCESS; /* ERRORS */ error: { if (reconstruct) gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder), reconstruct); return ret; } }
static GstVaapiEncoderStatus gst_vaapi_encoder_jpeg_encode (GstVaapiEncoder * base_encoder, GstVaapiEncPicture * picture, GstVaapiCodedBufferProxy * codedbuf) { GstVaapiEncoderJpeg *const encoder = GST_VAAPI_ENCODER_JPEG_CAST (base_encoder); GstVaapiEncoderStatus ret = GST_VAAPI_ENCODER_STATUS_ERROR_UNKNOWN; GstVaapiSurfaceProxy *reconstruct = NULL; reconstruct = gst_vaapi_encoder_create_surface (base_encoder); g_assert (GST_VAAPI_SURFACE_PROXY_SURFACE (reconstruct)); if (!ensure_picture (encoder, picture, codedbuf, reconstruct)) goto error; if (!ensure_quantization_table (encoder, picture)) goto error; if (!ensure_huffman_table (encoder, picture)) goto error; if (!ensure_slices (encoder, picture)) goto error; if (!ensure_packed_headers (encoder, picture)) goto error; if (!gst_vaapi_enc_picture_encode (picture)) goto error; if (reconstruct) gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder), reconstruct); return GST_VAAPI_ENCODER_STATUS_SUCCESS; /* ERRORS */ error: { if (reconstruct) gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder), reconstruct); return ret; } }
static GstVaapiEncoderStatus gst_vaapi_encoder_vp8_encode (GstVaapiEncoder * base_encoder, GstVaapiEncPicture * picture, GstVaapiCodedBufferProxy * codedbuf) { GstVaapiEncoderVP8 *const encoder = GST_VAAPI_ENCODER_VP8_CAST (base_encoder); GstVaapiEncoderStatus ret = GST_VAAPI_ENCODER_STATUS_ERROR_UNKNOWN; GstVaapiSurfaceProxy *reconstruct = NULL; reconstruct = gst_vaapi_encoder_create_surface (base_encoder); g_assert (GST_VAAPI_SURFACE_PROXY_SURFACE (reconstruct)); if (!ensure_sequence (encoder, picture)) goto error; if (!ensure_misc_params (encoder, picture)) goto error; if (!ensure_picture (encoder, picture, codedbuf, reconstruct)) goto error; if (!ensure_quantization_table (encoder, picture)) goto error; if (!gst_vaapi_enc_picture_encode (picture)) goto error; if (reconstruct) { if (picture->type == GST_VAAPI_PICTURE_TYPE_I) clear_references (encoder); push_reference (encoder, reconstruct); } return GST_VAAPI_ENCODER_STATUS_SUCCESS; /* ERRORS */ error: { if (reconstruct) gst_vaapi_encoder_release_surface (GST_VAAPI_ENCODER (encoder), reconstruct); return ret; } }
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 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_vaapidecode_step(GstVaapiDecode *decode) { GstVaapiSurfaceProxy *proxy; GstVaapiDecoderStatus status; GstBuffer *buffer; GstFlowReturn ret; GstClockTime timestamp; gint64 end_time; for (;;) { end_time = decode->render_time_base; if (!end_time) end_time = g_get_monotonic_time(); end_time += GST_TIME_AS_USECONDS(decode->last_buffer_time); end_time += G_TIME_SPAN_SECOND; proxy = gst_vaapi_decoder_get_surface(decode->decoder, &status); if (!proxy) { if (status == GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE) { gboolean was_signalled; g_mutex_lock(decode->decoder_mutex); was_signalled = g_cond_wait_until( decode->decoder_ready, decode->decoder_mutex, end_time ); g_mutex_unlock(decode->decoder_mutex); if (was_signalled) continue; goto error_decode_timeout; } if (status != GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA) goto error_decode; /* More data is needed */ break; } g_object_weak_ref( G_OBJECT(proxy), (GWeakNotify)gst_vaapidecode_release, decode ); buffer = gst_vaapi_video_buffer_new(decode->display); if (!buffer) goto error_create_buffer; timestamp = GST_VAAPI_SURFACE_PROXY_TIMESTAMP(proxy); if (!decode->render_time_base) decode->render_time_base = g_get_monotonic_time(); decode->last_buffer_time = timestamp; GST_BUFFER_TIMESTAMP(buffer) = timestamp; GST_BUFFER_DURATION(buffer) = GST_VAAPI_SURFACE_PROXY_DURATION(proxy); gst_buffer_set_caps(buffer, GST_PAD_CAPS(decode->srcpad)); if (GST_VAAPI_SURFACE_PROXY_TFF(proxy)) GST_BUFFER_FLAG_SET(buffer, GST_VIDEO_BUFFER_TFF); gst_vaapi_video_buffer_set_surface_proxy( GST_VAAPI_VIDEO_BUFFER(buffer), proxy ); ret = gst_pad_push(decode->srcpad, buffer); if (ret != GST_FLOW_OK) goto error_commit_buffer; g_object_unref(proxy); } return GST_FLOW_OK; /* ERRORS */ error_decode_timeout: { GST_DEBUG("decode timeout. Decoder required a VA surface but none " "got available within one second"); return GST_FLOW_UNEXPECTED; } error_decode: { GST_DEBUG("decode error %d", status); switch (status) { case GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CODEC: case GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE: case GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_CHROMA_FORMAT: ret = GST_FLOW_NOT_SUPPORTED; break; default: ret = GST_FLOW_UNEXPECTED; break; } return ret; } error_create_buffer: { const GstVaapiID surface_id = gst_vaapi_surface_get_id(GST_VAAPI_SURFACE_PROXY_SURFACE(proxy)); GST_DEBUG("video sink failed to create video buffer for proxy'ed " "surface %" GST_VAAPI_ID_FORMAT " (error %d)", GST_VAAPI_ID_ARGS(surface_id), ret); g_object_unref(proxy); return GST_FLOW_UNEXPECTED; } error_commit_buffer: { GST_DEBUG("video sink rejected the video buffer (error %d)", ret); g_object_unref(proxy); return GST_FLOW_UNEXPECTED; } }
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; }