static void gst_vaapidecode_init (GstVaapiDecode * decode) { GstVideoDecoder *const vdec = GST_VIDEO_DECODER (decode); gst_vaapi_plugin_base_init (GST_VAAPI_PLUGIN_BASE (decode), GST_CAT_DEFAULT); decode->decoder = NULL; decode->decoder_caps = NULL; decode->allowed_caps = NULL; g_mutex_init (&decode->surface_ready_mutex); g_cond_init (&decode->surface_ready); gst_video_decoder_set_packetized (vdec, FALSE); #if !GST_CHECK_VERSION(1,4,0) /* Pad through which data comes in to the element */ GstPad *pad = GST_VAAPI_PLUGIN_BASE_SINK_PAD (decode); gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_vaapidecode_query)); /* Pad through which data goes out of the element */ pad = GST_VAAPI_PLUGIN_BASE_SRC_PAD (decode); gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_vaapidecode_query)); #endif }
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 GstCaps * gst_vaapiencode_h265_get_caps (GstVaapiEncode * base_encode) { GstVaapiEncodeH265 *const encode = GST_VAAPIENCODE_H265_CAST (base_encode); GstCaps *caps, *allowed_caps; caps = gst_caps_from_string (GST_CODEC_CAPS); /* Check whether "stream-format" is hvcC mode */ allowed_caps = gst_pad_get_allowed_caps (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode)); if (allowed_caps) { const char *stream_format = NULL; GstStructure *structure; guint i, num_structures; num_structures = gst_caps_get_size (allowed_caps); for (i = 0; !stream_format && i < num_structures; i++) { structure = gst_caps_get_structure (allowed_caps, i); if (!gst_structure_has_field_typed (structure, "stream-format", G_TYPE_STRING)) continue; stream_format = gst_structure_get_string (structure, "stream-format"); } encode->is_hvc = stream_format && strcmp (stream_format, "hvc1") == 0; gst_caps_unref (allowed_caps); } gst_caps_set_simple (caps, "stream-format", G_TYPE_STRING, encode->is_hvc ? "hvc1" : "byte-stream", NULL); base_encode->need_codec_data = encode->is_hvc; /* XXX: update profile and level information */ return caps; }
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 void gst_vaapiencode_buffer_loop (GstVaapiEncode * encode) { GstFlowReturn ret; const gint64 timeout = 50000; /* microseconds */ ret = gst_vaapiencode_push_frame (encode, timeout); if (ret == GST_FLOW_OK || ret == GST_VAAPI_ENCODE_FLOW_TIMEOUT) return; gst_pad_pause_task (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode)); }
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_h265_set_config (GstVaapiEncode * base_encode) { GstVaapiEncoderH265 *const encoder = GST_VAAPI_ENCODER_H265 (base_encode->encoder); GstCaps *allowed_caps; GstVaapiProfile profile; /* Check for the largest profile that is supported */ allowed_caps = gst_pad_get_allowed_caps (GST_VAAPI_PLUGIN_BASE_SRC_PAD (base_encode)); if (!allowed_caps) return TRUE; profile = find_best_profile (allowed_caps); gst_caps_unref (allowed_caps); if (profile) { GST_INFO ("using %s profile as target decoder constraints", gst_vaapi_utils_h265_get_profile_string (profile)); if (!gst_vaapi_encoder_h265_set_max_profile (encoder, profile)) 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 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 gboolean ensure_allowed_sinkpad_caps (GstVaapiEncode * encode) { GstVaapiEncodeClass *klass = GST_VAAPIENCODE_GET_CLASS (encode); GstCaps *out_caps, *raw_caps = NULL; GArray *formats = NULL; gboolean ret = FALSE; GstVaapiProfile profile = GST_VAAPI_PROFILE_UNKNOWN; if (encode->allowed_sinkpad_caps) return TRUE; if (!encode->encoder) return TRUE; out_caps = gst_caps_from_string (GST_VAAPI_MAKE_SURFACE_CAPS); if (!out_caps) goto failed_create_va_caps; if (klass->get_profile) { GstCaps *allowed = gst_pad_get_allowed_caps (GST_VAAPI_PLUGIN_BASE_SRC_PAD (encode)); if (allowed) { if (!gst_caps_is_empty (allowed) && !gst_caps_is_any (allowed)) profile = klass->get_profile (allowed); gst_caps_unref (allowed); } } formats = gst_vaapi_encoder_get_surface_formats (encode->encoder, profile); if (!formats) goto failed_get_formats; raw_caps = gst_vaapi_video_format_new_template_caps_from_list (formats); if (!raw_caps) goto failed_create_raw_caps; out_caps = gst_caps_make_writable (out_caps); gst_caps_append (out_caps, gst_caps_copy (raw_caps)); gst_caps_replace (&encode->allowed_sinkpad_caps, out_caps); GST_INFO_OBJECT (encode, "Allowed sink caps %" GST_PTR_FORMAT, encode->allowed_sinkpad_caps); ret = TRUE; bail: if (out_caps) gst_caps_unref (out_caps); if (raw_caps) gst_caps_unref (raw_caps); if (formats) g_array_unref (formats); return ret; failed_create_va_caps: { GST_WARNING_OBJECT (encode, "failed to create VA/GL sink caps"); return FALSE; } failed_get_formats: { GST_WARNING_OBJECT (encode, "failed to get allowed surface formats"); goto bail; } failed_create_raw_caps: { GST_WARNING_OBJECT (encode, "failed to create raw sink caps"); goto bail; } }