static GstFlowReturn gst_vaapiencode_finish (GstVideoEncoder * venc) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc); GstVaapiEncoderStatus status; GstFlowReturn ret = GST_FLOW_OK; /* Don't try to destroy encoder if none was created in the first place. Return "not-negotiated" error since this means we did not even reach GstVideoEncoder::set_format() state, where the encoder could have been created */ if (!encode->encoder) return GST_FLOW_NOT_NEGOTIATED; status = gst_vaapi_encoder_flush (encode->encoder); GST_VIDEO_ENCODER_STREAM_UNLOCK (encode); gst_pad_stop_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode)); GST_VIDEO_ENCODER_STREAM_LOCK (encode); while (status == GST_VAAPI_ENCODER_STATUS_SUCCESS && ret == GST_FLOW_OK) ret = gst_vaapiencode_push_frame (encode, 0); if (ret == GST_VAAPI_ENCODE_FLOW_TIMEOUT) ret = GST_FLOW_OK; return ret; }
static gboolean gst_vaapiencode_close (GstVideoEncoder * venc) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc); gst_vaapiencode_destroy (encode); gst_vaapi_plugin_base_close (GST_VAAPI_PLUGIN_BASE (encode)); return TRUE; }
static gboolean gst_vaapiencode_set_format (GstVideoEncoder * venc, GstVideoCodecState * state) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc); gboolean ret; g_return_val_if_fail (state->caps != NULL, FALSE); if (!set_codec_state (encode, state)) return FALSE; if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (encode), state->caps, NULL)) return FALSE; if (encode->input_state) gst_video_codec_state_unref (encode->input_state); encode->input_state = gst_video_codec_state_ref (state); encode->input_state_changed = TRUE; ret = gst_pad_start_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode), (GstTaskFunction) gst_vaapiencode_buffer_loop, encode, NULL); if (!ret) return FALSE; /* Store some tags */ { GstTagList *tags = gst_tag_list_new_empty (); const gchar *encoder, *codec; guint bitrate = 0; g_object_get (encode, "bitrate", &bitrate, NULL); gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_NOMINAL_BITRATE, bitrate, NULL); if ((encoder = gst_element_class_get_metadata (GST_ELEMENT_GET_CLASS (encode), GST_ELEMENT_METADATA_LONGNAME))) gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, encoder, NULL); if ((codec = gst_vaapi_codec_get_name (gst_vaapi_profile_get_codec (gst_vaapi_profile_from_caps (state->caps))))) gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_CODEC, codec, NULL); gst_video_encoder_merge_tags (venc, tags, GST_TAG_MERGE_REPLACE); gst_tag_list_unref (tags); } return TRUE; }
static GstCaps * gst_vaapiencode_get_caps (GstVideoEncoder * venc, GstCaps * filter) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc); GstCaps *result; ensure_allowed_sinkpad_caps (encode); result = gst_video_encoder_proxy_getcaps (venc, encode->allowed_sinkpad_caps, filter); GST_DEBUG_OBJECT (venc, "Negotiated sink caps %" GST_PTR_FORMAT, result); return result; }
static void gst_vaapiencode_mpeg2_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstVaapiEncodeClass *const encode_class = GST_VAAPIENCODE_GET_CLASS (object); GstVaapiEncode *const base_encode = GST_VAAPIENCODE_CAST (object); switch (prop_id) { default: if (!encode_class->set_property (base_encode, prop_id, value)) G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void gst_vaapiencode_finalize (GObject * object) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (object); gst_vaapiencode_destroy (encode); if (encode->prop_values) { g_ptr_array_unref (encode->prop_values); encode->prop_values = NULL; } gst_vaapi_plugin_base_finalize (GST_VAAPI_PLUGIN_BASE (object)); G_OBJECT_CLASS (gst_vaapiencode_parent_class)->finalize (object); }
static GstStateChangeReturn gst_vaapiencode_change_state (GstElement * element, GstStateChange transition) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (element); switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: gst_pad_stop_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode)); break; default: break; } return GST_ELEMENT_CLASS (gst_vaapiencode_parent_class)->change_state (element, transition); }
static gboolean gst_vaapiencode_open (GstVideoEncoder * venc) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc); GstVaapiDisplay *const old_display = GST_VAAPI_PLUGIN_BASE_DISPLAY (encode); gboolean success; if (!gst_vaapi_plugin_base_open (GST_VAAPI_PLUGIN_BASE (encode))) return FALSE; GST_VAAPI_PLUGIN_BASE_DISPLAY (encode) = NULL; success = ensure_display (encode); if (old_display) gst_vaapi_display_unref (old_display); return success; }
static gboolean gst_vaapiencode_flush (GstVideoEncoder * venc) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc); GstVaapiEncoderStatus status; if (!encode->encoder) return FALSE; GST_LOG_OBJECT (encode, "flushing"); status = gst_vaapi_encoder_flush (encode->encoder); if (status != GST_VAAPI_ENCODER_STATUS_SUCCESS) return FALSE; gst_vaapiencode_purge (encode); gst_vaapi_encoder_replace (&encode->encoder, NULL); if (!ensure_encoder (encode)) return FALSE; if (!set_codec_state (encode, encode->input_state)) return FALSE; return TRUE; }
static gboolean gst_vaapiencode_set_format (GstVideoEncoder * venc, GstVideoCodecState * state) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc); g_return_val_if_fail (state->caps != NULL, FALSE); if (!ensure_encoder (encode)) return FALSE; if (!set_codec_state (encode, state)) return FALSE; if (!gst_vaapi_plugin_base_set_caps (GST_VAAPI_PLUGIN_BASE (encode), state->caps, NULL)) return FALSE; if (encode->input_state) gst_video_codec_state_unref (encode->input_state); encode->input_state = gst_video_codec_state_ref (state); encode->input_state_changed = TRUE; return gst_pad_start_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode), (GstTaskFunction) gst_vaapiencode_buffer_loop, encode, NULL); }
static void gst_vaapiencode_mpeg2_init (GstVaapiEncodeMpeg2 * encode) { gst_vaapiencode_init_properties (GST_VAAPIENCODE_CAST (encode)); }
static void gst_vaapiencode_jpeg_init (GstVaapiEncodeJpeg * encode) { gst_vaapiencode_init_properties (GST_VAAPIENCODE_CAST (encode)); }
static void gst_vaapiencode_h265_init (GstVaapiEncodeH265 * encode) { gst_vaapiencode_init_properties (GST_VAAPIENCODE_CAST (encode)); }
static void gst_vaapiencode_vp9_init (GstVaapiEncodeVP9 * encode) { gst_vaapiencode_init_properties (GST_VAAPIENCODE_CAST (encode)); }
static gboolean gst_vaapiencode_sink_event (GstVideoEncoder * venc, GstEvent * event) { GstVaapiEncode *const encode = GST_VAAPIENCODE_CAST (venc); GstPad *const srcpad = GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode); gboolean ret; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CUSTOM_DOWNSTREAM:{ const GstStructure *s = gst_event_get_structure (event); if (gst_structure_has_name (s, "GstVaapiEncoderRegionOfInterest")) { GstVaapiROI roi; if (!encode->encoder) return TRUE; if (!gst_structure_get_uint (s, "roi-x", &roi.rect.x) || !gst_structure_get_uint (s, "roi-y", &roi.rect.y) || !gst_structure_get_uint (s, "roi-width", &roi.rect.width) || !gst_structure_get_uint (s, "roi-height", &roi.rect.height) || !gst_structure_get_int (s, "roi-value", &roi.roi_value)) { return TRUE; } if (roi.roi_value == 0) { ret = gst_vaapi_encoder_del_roi (encode->encoder, &roi); if (ret) { GST_INFO_OBJECT (venc, "ROI: region with %d/%d/%d/%d is removed", roi.rect.x, roi.rect.y, roi.rect.width, roi.rect.height); } } else { ret = gst_vaapi_encoder_add_roi (encode->encoder, &roi); if (ret) { GST_INFO_OBJECT (venc, "ROI: region with %d/%d/%d/%d is added", roi.rect.x, roi.rect.y, roi.rect.width, roi.rect.height); } } gst_event_unref (event); return ret; } break; } default: break; } ret = GST_VIDEO_ENCODER_CLASS (gst_vaapiencode_parent_class)->sink_event (venc, event); if (!ret) return FALSE; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: gst_pad_pause_task (srcpad); break; case GST_EVENT_FLUSH_STOP: ret = gst_pad_start_task (srcpad, (GstTaskFunction) gst_vaapiencode_buffer_loop, encode, NULL); break; default: break; } return ret; }
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; } }
static gboolean gst_vaapiencode_start (GstVideoEncoder * venc) { return ensure_encoder (GST_VAAPIENCODE_CAST (venc)); }
static gboolean gst_vaapiencode_stop (GstVideoEncoder * venc) { return gst_vaapiencode_destroy (GST_VAAPIENCODE_CAST (venc)); }