| /* GStreamer | |
| * Copyright (C) 2022 Seungha Yang <seungha@centricular.com> | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Library General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Library General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Library General Public | |
| * License along with this library; if not, write to the | |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, | |
| * Boston, MA 02110-1301, USA. | |
| */ | |
| /** | |
| * SECTION:element-amfh265enc | |
| * @title: amfh265enc | |
| * @short_description: An AMD AMF API based H.265 video encoder | |
| * | |
| * amfh265enc element encodes raw video stream into compressed H.265 bitstream | |
| * via AMD AMF API. | |
| * | |
| * ## Example launch line | |
| * ``` | |
| * gst-launch-1.0 videotestsrc num-buffers=100 ! amfh265enc ! h265parse ! mp4mux ! filesink location=encoded.mp4 | |
| * ``` | |
| * | |
| * Since: 1.22 | |
| * | |
| */ | |
| #ifdef HAVE_CONFIG_H | |
| #include "config.h" | |
| #endif | |
| #include "gstamfh265enc.h" | |
| #include <components/Component.h> | |
| #include <components/VideoEncoderHEVC.h> | |
| #include <core/Factory.h> | |
| #include <set> | |
| #include <string> | |
| #include <vector> | |
| #include <string.h> | |
| using namespace amf; | |
| GST_DEBUG_CATEGORY_STATIC (gst_amf_h265_enc_debug); | |
| #define GST_CAT_DEFAULT gst_amf_h265_enc_debug | |
| static GTypeClass *parent_class = nullptr; | |
| typedef struct | |
| { | |
| amf_int64 max_bitrate; | |
| amf_int64 num_of_streams; | |
| amf_int64 max_profile; | |
| amf_int64 max_tier; | |
| amf_int64 max_level; | |
| amf_int64 min_ref_frames; | |
| amf_int64 max_ref_frames; | |
| amf_int64 num_of_hw_instances; | |
| amf_int64 color_conversion; | |
| amf_int64 pre_analysis; | |
| amf_int64 roi_map; | |
| amf_int64 max_throughput; | |
| amf_int64 query_timeout_support; | |
| amf_int64 default_qp_i; | |
| amf_int64 default_qp_p; | |
| amf_int64 min_gop_size; | |
| amf_int64 max_gop_size; | |
| amf_int64 default_gop_size; | |
| guint valign; | |
| gboolean pre_encode_supported; | |
| gboolean smart_access_supported; | |
| GstAmfEncoderPASupportedOptions pa_supported; | |
| } GstAmfH265EncDeviceCaps; | |
| /** | |
| * GstAmfH265EncUsage: | |
| * | |
| * Encoder usages | |
| * | |
| * Since: 1.22 | |
| */ | |
| #define GST_TYPE_AMF_H265_ENC_USAGE (gst_amf_h265_enc_usage_get_type ()) | |
| static GType | |
| gst_amf_h265_enc_usage_get_type (void) | |
| { | |
| static GType usage_type = 0; | |
| static const GEnumValue usages[] = { | |
| /** | |
| * GstAmfH265EncUsage::transcoding: | |
| * | |
| * Transcoding usage | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING, "Transcoding", "transcoding"}, | |
| /** | |
| * GstAmfH265EncUsage::ultra-low-latency: | |
| * | |
| * Ultra Low Latency usage | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY, "Ultra Low Latency", | |
| "ultra-low-latency"}, | |
| /** | |
| * GstAmfH265EncUsage::low-latency: | |
| * | |
| * Low Latency usage | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY, "Low Latency", "low-latency"}, | |
| /** | |
| * GstAmfH265EncUsage::webcam: | |
| * | |
| * Webcam usage | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM, "Webcam", "webcam"}, | |
| {0, nullptr, nullptr} | |
| }; | |
| if (g_once_init_enter (&usage_type)) { | |
| GType type = g_enum_register_static ("GstAmfH265EncUsage", usages); | |
| g_once_init_leave (&usage_type, type); | |
| } | |
| return usage_type; | |
| } | |
| /** | |
| * GstAmfH265EncRateControl: | |
| * | |
| * Rate control methods | |
| * | |
| * Since: 1.22 | |
| */ | |
| #define GST_TYPE_AMF_H265_ENC_RATE_CONTROL (gst_amf_h265_enc_rate_control_get_type ()) | |
| static GType | |
| gst_amf_h265_enc_rate_control_get_type (void) | |
| { | |
| static GType rate_control_type = 0; | |
| static const GEnumValue rate_controls[] = { | |
| /** | |
| * GstAmfH265EncRateControl::default: | |
| * | |
| * Default rate control method depending on usage | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN, | |
| "Default, depends on Usage", "default"}, | |
| /** | |
| * GstAmfH265EncRateControl::cqp: | |
| * | |
| * Constant QP | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP, "Constant QP", | |
| "cqp"}, | |
| /** | |
| * GstAmfH265EncRateControl::lcvbr: | |
| * | |
| * Latency Constrained Variable Bitrate | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, | |
| "Latency Constrained VBR", "lcvbr"}, | |
| /** | |
| * GstAmfH265EncRateControl::vbr: | |
| * | |
| * Peak Constrained Variable Bitrate | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR, | |
| "Peak Constrained VBR", "vbr"}, | |
| /** | |
| * GstAmfH265EncRateControl::cbr: | |
| * | |
| * Constant Bitrate | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR, "Constant Bitrate", "cbr"}, | |
| {0, nullptr, nullptr} | |
| }; | |
| if (g_once_init_enter (&rate_control_type)) { | |
| GType type = | |
| g_enum_register_static ("GstAmfH265EncRateControl", rate_controls); | |
| g_once_init_leave (&rate_control_type, type); | |
| } | |
| return rate_control_type; | |
| } | |
| /** | |
| * GstAmfH265EncPreset: | |
| * | |
| * Encoding quality presets | |
| * | |
| * Since: 1.22 | |
| */ | |
| #define AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_UNKNOWN -1 | |
| #define GST_TYPE_AMF_H265_ENC_PRESET (gst_amf_h265_enc_preset_get_type ()) | |
| static GType | |
| gst_amf_h265_enc_preset_get_type (void) | |
| { | |
| static GType preset_type = 0; | |
| static const GEnumValue presets[] = { | |
| /** | |
| * GstAmfH265EncRateControl::default: | |
| * | |
| * Default preset depends on usage | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_UNKNOWN, "Default, depends on USAGE", | |
| "default"}, | |
| /** | |
| * GstAmfH265EncRateControl::quality: | |
| * | |
| * Quality oriented preset | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY, "Quality", "quality"}, | |
| /** | |
| * | |
| * GstAmfH265EncRateControl::balanced: | |
| * | |
| * Balanced preset | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_BALANCED, "Balanced", "balanced"}, | |
| /** | |
| * GstAmfH265EncRateControl::speed: | |
| * | |
| * Speed oriented preset | |
| */ | |
| {AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED, "Speed", "speed"}, | |
| {0, nullptr, nullptr} | |
| }; | |
| if (g_once_init_enter (&preset_type)) { | |
| GType type = g_enum_register_static ("GstAmfH265EncPreset", presets); | |
| g_once_init_leave (&preset_type, type); | |
| } | |
| return preset_type; | |
| } | |
| typedef struct | |
| { | |
| GstCaps *sink_caps; | |
| GstCaps *src_caps; | |
| gint64 adapter_luid; | |
| GstAmfH265EncDeviceCaps dev_caps; | |
| } GstAmfH265EncClassData; | |
| enum | |
| { | |
| PROP_0, | |
| PROP_ADAPTER_LUID, | |
| PROP_USAGE, | |
| PROP_RATE_CONTROL, | |
| PROP_PRESET, | |
| PROP_BITRATE, | |
| PROP_MAX_BITRATE, | |
| PROP_GOP_SIZE, | |
| PROP_MIN_QP_I, | |
| PROP_MAX_QP_I, | |
| PROP_MIN_QP_P, | |
| PROP_MAX_QP_P, | |
| PROP_QP_I, | |
| PROP_QP_P, | |
| PROP_REF_FRAMES, | |
| PROP_AUD, | |
| PROP_SMART_ACCESS, | |
| PROP_PRE_ENCODE, | |
| PROP_PRE_ANALYSIS, | |
| PROP_PA_ACTIVITY_TYPE, | |
| PROP_PA_SCENE_CHANGE_DETECTION, | |
| PROP_PA_SCENE_CHANGE_DETECTION_SENSITIVITY, | |
| PROP_PA_STATIC_SCENE_DETECTION, | |
| PROP_PA_STATIC_SCENE_DETECTION_SENSITIVITY, | |
| PROP_PA_INITIAL_QP, | |
| PROP_PA_MAX_QP, | |
| PROP_PA_CAQ_STRENGTH, | |
| PROP_PA_FRAME_SAD, | |
| PROP_PA_LTR, | |
| PROP_PA_LOOKAHEAD_BUFFER_DEPTH, | |
| PROP_PA_PAQ_MODE, | |
| PROP_PA_TAQ_MODE, | |
| PROP_PA_HQMB_MODE, | |
| }; | |
| #define DEFAULT_USAGE AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING | |
| #define DEFAULT_RATE_CONTROL AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN | |
| #define DEFAULT_PRESET AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_UNKNOWN | |
| #define DEFAULT_BITRATE 0 | |
| #define DEFAULT_MAX_BITRATE 0 | |
| #define DEFAULT_MIN_MAX_QP -1 | |
| #define DEFAULT_AUD TRUE | |
| #define DEFAULT_SMART_ACCESS FALSE | |
| #define DEFAULT_PRE_ENCODE FALSE | |
| #define DOC_SINK_CAPS_COMM \ | |
| "format = (string) {NV12, P010_10LE}, " \ | |
| "width = (int) [ 128, 4096 ], height = (int) [ 128, 4096 ]" | |
| #define DOC_SINK_CAPS \ | |
| "video/x-raw(memory:D3D11Memory), " DOC_SINK_CAPS_COMM "; " \ | |
| "video/x-raw, " DOC_SINK_CAPS_COMM | |
| #define DOC_SRC_CAPS \ | |
| "video/x-h265, width = (int) [ 128, 4096 ], height = (int) [ 128, 4096 ], " \ | |
| "profile = (string) {main, main-10}, stream-format = (string) byte-stream, " \ | |
| "alignment = (string) au" | |
| typedef struct _GstAmfH265Enc | |
| { | |
| GstAmfEncoder parent; | |
| GMutex prop_lock; | |
| gboolean property_updated; | |
| gint usage; | |
| gint rate_control; | |
| gint preset; | |
| guint bitrate; | |
| guint max_bitrate; | |
| guint gop_size; | |
| gint min_qp_i; | |
| gint max_qp_i; | |
| gint min_qp_p; | |
| gint max_qp_p; | |
| guint qp_i; | |
| guint qp_p; | |
| guint ref_frames; | |
| gboolean aud; | |
| gboolean smart_access; | |
| gboolean pre_encode; | |
| GstAmfEncoderPreAnalysis pa; | |
| } GstAmfH265Enc; | |
| typedef struct _GstAmfH265EncClass | |
| { | |
| GstAmfEncoderClass parent_class; | |
| GstAmfH265EncDeviceCaps dev_caps; | |
| gint64 adapter_luid; | |
| } GstAmfH265EncClass; | |
| #define GST_AMF_H265_ENC(object) ((GstAmfH265Enc *) (object)) | |
| #define GST_AMF_H265_ENC_GET_CLASS(object) \ | |
| (G_TYPE_INSTANCE_GET_CLASS ((object),G_TYPE_FROM_INSTANCE (object),GstAmfH265EncClass)) | |
| static void gst_amf_h265_enc_finalize (GObject * object); | |
| static void gst_amf_h265_enc_set_property (GObject * object, guint prop_id, | |
| const GValue * value, GParamSpec * pspec); | |
| static void gst_amf_h265_enc_get_property (GObject * object, guint prop_id, | |
| GValue * value, GParamSpec * pspec); | |
| static GstCaps *gst_amf_h265_enc_getcaps (GstVideoEncoder * encoder, | |
| GstCaps * filter); | |
| static gboolean gst_amf_h265_enc_set_format (GstAmfEncoder * encoder, | |
| GstVideoCodecState * state, gpointer component, guint * num_reorder_frames); | |
| static gboolean gst_amf_h265_enc_set_output_state (GstAmfEncoder * encoder, | |
| GstVideoCodecState * state, gpointer component); | |
| static gboolean gst_amf_h265_enc_set_surface_prop (GstAmfEncoder * encoder, | |
| GstVideoCodecFrame * frame, gpointer surface); | |
| static GstBuffer *gst_amf_h265_enc_create_output_buffer (GstAmfEncoder * | |
| encoder, gpointer data, gboolean * sync_point); | |
| static gboolean gst_amf_h265_enc_check_reconfigure (GstAmfEncoder * encoder); | |
| static void | |
| gst_amf_h265_enc_class_init (GstAmfH265EncClass * klass, gpointer data) | |
| { | |
| GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
| GstElementClass *element_class = GST_ELEMENT_CLASS (klass); | |
| GstVideoEncoderClass *videoenc_class = GST_VIDEO_ENCODER_CLASS (klass); | |
| GstAmfEncoderClass *amf_class = GST_AMF_ENCODER_CLASS (klass); | |
| GstAmfH265EncClassData *cdata = (GstAmfH265EncClassData *) data; | |
| GstAmfH265EncDeviceCaps *dev_caps = &cdata->dev_caps; | |
| GstAmfEncoderPASupportedOptions *pa_supported = &dev_caps->pa_supported; | |
| GParamFlags param_flags = (GParamFlags) (G_PARAM_READWRITE | | |
| GST_PARAM_MUTABLE_PLAYING | G_PARAM_STATIC_STRINGS); | |
| GParamFlags pa_param_flags = (GParamFlags) (G_PARAM_READWRITE | | |
| G_PARAM_STATIC_STRINGS | GST_PARAM_CONDITIONALLY_AVAILABLE); | |
| GstPadTemplate *pad_templ; | |
| GstCaps *doc_caps; | |
| parent_class = (GTypeClass *) g_type_class_peek_parent (klass); | |
| object_class->finalize = gst_amf_h265_enc_finalize; | |
| object_class->set_property = gst_amf_h265_enc_set_property; | |
| object_class->get_property = gst_amf_h265_enc_get_property; | |
| g_object_class_install_property (object_class, PROP_ADAPTER_LUID, | |
| g_param_spec_int64 ("adapter-luid", "Adapter LUID", | |
| "DXGI Adapter LUID (Locally Unique Identifier) of associated GPU", | |
| G_MININT64, G_MAXINT64, 0, (GParamFlags) (GST_PARAM_DOC_SHOW_DEFAULT | | |
| G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); | |
| g_object_class_install_property (object_class, PROP_USAGE, | |
| g_param_spec_enum ("usage", "Usage", | |
| "Target usage", GST_TYPE_AMF_H265_ENC_USAGE, | |
| DEFAULT_USAGE, param_flags)); | |
| g_object_class_install_property (object_class, PROP_RATE_CONTROL, | |
| g_param_spec_enum ("rate-control", "Rate Control", | |
| "Rate Control Method", GST_TYPE_AMF_H265_ENC_RATE_CONTROL, | |
| DEFAULT_RATE_CONTROL, param_flags)); | |
| g_object_class_install_property (object_class, PROP_PRESET, | |
| g_param_spec_enum ("preset", "Preset", | |
| "Preset", GST_TYPE_AMF_H265_ENC_PRESET, DEFAULT_PRESET, param_flags)); | |
| g_object_class_install_property (object_class, PROP_BITRATE, | |
| g_param_spec_uint ("bitrate", "Bitrate", | |
| "Target bitrate in kbit/sec (0: USAGE default)", | |
| 0, G_MAXINT / 1000, DEFAULT_BITRATE, param_flags)); | |
| g_object_class_install_property (object_class, PROP_MAX_BITRATE, | |
| g_param_spec_uint ("max-bitrate", "Max Bitrate", | |
| "Maximum bitrate in kbit/sec (0: USAGE default)", | |
| 0, G_MAXINT / 1000, DEFAULT_MAX_BITRATE, param_flags)); | |
| g_object_class_install_property (object_class, PROP_GOP_SIZE, | |
| g_param_spec_uint ("gop-size", "GOP Size", | |
| "Number of pictures within a GOP", | |
| (guint) dev_caps->min_gop_size, (guint) dev_caps->max_gop_size, | |
| (guint) dev_caps->default_gop_size, param_flags)); | |
| g_object_class_install_property (object_class, PROP_MIN_QP_I, | |
| g_param_spec_int ("min-qp-i", "Min QP I", | |
| "Minimum allowed QP value for I frames (-1: USAGE default)", | |
| -1, 51, DEFAULT_MIN_MAX_QP, param_flags)); | |
| g_object_class_install_property (object_class, PROP_MAX_QP_I, | |
| g_param_spec_int ("max-qp-i", "Max QP I", | |
| "Maximum allowed QP value for I frames (-1: USAGE default)", | |
| -1, 51, DEFAULT_MIN_MAX_QP, param_flags)); | |
| g_object_class_install_property (object_class, PROP_MIN_QP_P, | |
| g_param_spec_int ("min-qp-p", "Min QP P", | |
| "Minimum allowed QP value for P frames (-1: USAGE default)", | |
| -1, 51, DEFAULT_MIN_MAX_QP, param_flags)); | |
| g_object_class_install_property (object_class, PROP_MAX_QP_P, | |
| g_param_spec_int ("max-qp-p", "Max QP P", | |
| "Maximum allowed QP value for P frames (-1: USAGE default)", | |
| -1, 51, DEFAULT_MIN_MAX_QP, param_flags)); | |
| g_object_class_install_property (object_class, PROP_QP_I, | |
| g_param_spec_uint ("qp-i", "QP I", | |
| "Constant QP for I frames", 0, 51, | |
| (guint) dev_caps->default_qp_i, param_flags)); | |
| g_object_class_install_property (object_class, PROP_QP_P, | |
| g_param_spec_uint ("qp-p", "QP P", | |
| "Constant QP for P frames", 0, 51, | |
| (guint) dev_caps->default_qp_p, param_flags)); | |
| g_object_class_install_property (object_class, PROP_REF_FRAMES, | |
| g_param_spec_uint ("ref-frames", "Reference Frames", | |
| "Number of reference frames", (guint) dev_caps->min_ref_frames, | |
| (guint) dev_caps->max_ref_frames, | |
| (guint) dev_caps->min_ref_frames, param_flags)); | |
| g_object_class_install_property (object_class, PROP_AUD, | |
| g_param_spec_boolean ("aud", "AUD", | |
| "Use AU (Access Unit) delimiter", DEFAULT_AUD, param_flags)); | |
| if (cdata->dev_caps.pre_encode_supported) { | |
| /** | |
| * GstAmfH265Enc:pre-encode: | |
| * | |
| * Enable pre-encode assisted rate control | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PRE_ENCODE, | |
| g_param_spec_boolean ("pre-encode", "Pre-encode", | |
| "Enable pre-encode", DEFAULT_PRE_ENCODE, | |
| (GParamFlags) (param_flags | GST_PARAM_CONDITIONALLY_AVAILABLE))); | |
| } | |
| if (cdata->dev_caps.smart_access_supported) { | |
| /** | |
| * GstAmfH265Enc:smart-access-video: | |
| * | |
| * Enable AMF SmartAccess Video feature | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_SMART_ACCESS, | |
| g_param_spec_boolean ("smart-access-video", "Smart Access Video", | |
| "Enable AMF SmartAccess Video feature for optimal distribution" | |
| " between multiple AMD hardware instances", DEFAULT_SMART_ACCESS, | |
| (GParamFlags) (G_PARAM_READWRITE | | |
| GST_PARAM_CONDITIONALLY_AVAILABLE | | |
| GST_PARAM_MUTABLE_PLAYING | G_PARAM_STATIC_STRINGS))); | |
| } | |
| if (dev_caps->pre_analysis) { | |
| /** | |
| * GstAmfH265Enc:pre-analysis: | |
| * | |
| * Enable the pre-analysis module | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PRE_ANALYSIS, | |
| g_param_spec_boolean ("pre-analysis", "Pre-analysis", | |
| "Enable pre-analysis", DEFAULT_PRE_ANALYSIS, param_flags)); | |
| if (pa_supported->activity_type) { | |
| /** | |
| * GstAmfH265Enc:pa-activity-type: | |
| * | |
| * Set the type of activity analysis for pre-analysis | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PA_ACTIVITY_TYPE, | |
| g_param_spec_enum ("pa-activity-type", "Pre-analysis activity type", | |
| "Set the type of activity analysis for pre-analysis", | |
| GST_TYPE_AMF_ENC_PA_ACTIVITY_TYPE, DEFAULT_PA_ACTIVITY_TYPE, | |
| pa_param_flags)); | |
| } | |
| if (pa_supported->scene_change_detection) { | |
| /** | |
| * GstAmfH265Enc:pa-scene-change-detection: | |
| * | |
| * Enable scene change detection for pre-analysis | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, | |
| PROP_PA_SCENE_CHANGE_DETECTION, | |
| g_param_spec_boolean ("pa-scene-change-detection", | |
| "Pre-analysis scene change detection", | |
| "Enable scene change detection for pre-analysis", | |
| DEFAULT_PA_SCENE_CHANGE_DETECTION, pa_param_flags)); | |
| } | |
| if (pa_supported->scene_change_detection_sensitivity) { | |
| /** | |
| * GstAmfH265Enc:pa-scene-change-detection-sensitivity: | |
| * | |
| * Pre-analysis scene change detection sensitivity | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, | |
| PROP_PA_SCENE_CHANGE_DETECTION_SENSITIVITY, | |
| g_param_spec_enum ("pa-scene-change-detection-sensitivity", | |
| "Pre-analysis scene change detection sensitivity", | |
| "Set the sensitivity of scene change detection for pre-analysis", | |
| GST_TYPE_AMF_ENC_PA_SCENE_CHANGE_DETECTION_SENSITIVITY, | |
| DEFAULT_PA_SCENE_CHANGE_DETECTION_SENSITIVITY, pa_param_flags)); | |
| } | |
| if (pa_supported->static_scene_detection) { | |
| /** | |
| * GstAmfH265Enc:pa-static-scene-detection: | |
| * | |
| * Enable static scene detection for pre-analysis | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, | |
| PROP_PA_STATIC_SCENE_DETECTION, | |
| g_param_spec_boolean ("pa-static-scene-detection", | |
| "Pre-analysis static scene detection", | |
| "Enable static scene detection for pre-analysis", | |
| DEFAULT_PA_STATIC_SCENE_DETECTION, pa_param_flags)); | |
| } | |
| if (pa_supported->static_scene_detection_sensitivity) { | |
| /** | |
| * GstAmfH265Enc:pa-static-scene-detection-sensitivity: | |
| * | |
| * Pre-analysis static scene detection sensitivity | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, | |
| PROP_PA_STATIC_SCENE_DETECTION_SENSITIVITY, | |
| g_param_spec_enum ("pa-static-scene-detection-sensitivity", | |
| "Pre-analysis static scene detection sensitivity", | |
| "Set the sensitivity of static scene detection for pre-analysis", | |
| GST_TYPE_AMF_ENC_PA_STATIC_SCENE_DETECTION_SENSITIVITY, | |
| DEFAULT_PA_STATIC_SCENE_DETECTION_SENSITIVITY, pa_param_flags)); | |
| } | |
| if (pa_supported->initial_qp) { | |
| /** | |
| * GstAmfH265Enc:pa-initial-qp: | |
| * | |
| * Pre-analysis initial QP | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PA_INITIAL_QP, | |
| g_param_spec_uint ("pa-initial-qp", "Pre-analysis initial QP", | |
| "The QP value that is used immediately after a scene change", 0, | |
| 51, DEFAULT_PA_INITIAL_QP, pa_param_flags)); | |
| } | |
| if (pa_supported->max_qp) { | |
| /** | |
| * GstAmfH265Enc:pa-max-qp: | |
| * | |
| * Pre-analysis max QP | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PA_MAX_QP, | |
| g_param_spec_uint ("pa-max-qp", "Pre-analysis max QP", | |
| "The QP threshold to allow a skip frame", 0, 51, | |
| DEFAULT_PA_MAX_QP, pa_param_flags)); | |
| } | |
| if (pa_supported->caq_strength) { | |
| /** | |
| * GstAmfH265Enc:pa-caq-strength: | |
| * | |
| * Content Adaptive Quantization strength for pre-analysis | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PA_CAQ_STRENGTH, | |
| g_param_spec_enum ("pa-caq-strength", "Pre-analysis CAQ strength", | |
| "Content Adaptive Quantization strength for pre-analysis", | |
| GST_TYPE_AMF_ENC_PA_CAQ_STRENGTH, DEFAULT_PA_CAQ_STRENGTH, | |
| pa_param_flags)); | |
| } | |
| if (pa_supported->frame_sad) { | |
| /** | |
| * GstAmfH265Enc:pa-frame-sad: | |
| * | |
| * Enable Frame SAD algorithm | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PA_FRAME_SAD, | |
| g_param_spec_boolean ("pa-frame-sad", "Pre-analysis SAD algorithm", | |
| "Enable Frame SAD algorithm", DEFAULT_PA_FRAME_SAD, | |
| pa_param_flags)); | |
| } | |
| if (pa_supported->ltr) { | |
| /** | |
| * GstAmfH265Enc:pa-ltr: | |
| * | |
| * Enable long term reference frame management | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PA_LTR, | |
| g_param_spec_boolean ("pa-ltr", "Pre-analysis LTR", | |
| "Enable long term reference frame management", DEFAULT_PA_LTR, | |
| pa_param_flags)); | |
| } | |
| if (pa_supported->lookahead_buffer_depth) { | |
| /** | |
| * GstAmfH265Enc:pa-lookahead-buffer-depth: | |
| * | |
| * Pre-analysis lookahead buffer depth | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, | |
| PROP_PA_LOOKAHEAD_BUFFER_DEPTH, | |
| g_param_spec_uint ("pa-lookahead-buffer-depth", | |
| "Pre-analysis lookahead buffer depth", | |
| "Set the PA lookahead buffer size", 0, 41, | |
| DEFAULT_PA_LOOKAHEAD_BUFFER_DEPTH, pa_param_flags)); | |
| } | |
| if (pa_supported->paq_mode) { | |
| /** | |
| * GstAmfH265Enc:pa-paq-mode: | |
| * | |
| * Set the perceptual adaptive quantization mode | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PA_PAQ_MODE, | |
| g_param_spec_enum ("pa-paq-mode", "Pre-analysis PAQ mode", | |
| "Set the perceptual adaptive quantization mode", | |
| GST_TYPE_AMF_ENC_PA_PAQ_MODE, DEFAULT_PA_PAQ_MODE, | |
| pa_param_flags)); | |
| } | |
| if (pa_supported->taq_mode) { | |
| /** | |
| * GstAmfH265Enc:pa-taq-mode: | |
| * | |
| * Set the temporal adaptive quantization mode | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PA_TAQ_MODE, | |
| g_param_spec_enum ("pa-taq-mode", "Pre-analysis TAQ mode", | |
| "Set the temporal adaptive quantization mode", | |
| GST_TYPE_AMF_ENC_PA_TAQ_MODE, DEFAULT_PA_TAQ_MODE, | |
| pa_param_flags)); | |
| } | |
| if (pa_supported->hmqb_mode) { | |
| /** | |
| * GstAmfH265Enc:pa-hqmb-mode: | |
| * | |
| * Set the PA high motion quality boost mode | |
| * | |
| * Since: 1.24 | |
| */ | |
| g_object_class_install_property (object_class, PROP_PA_HQMB_MODE, | |
| g_param_spec_enum ("pa-hqmb-mode", "Pre-analysis HQMB mode", | |
| "Set the PA high motion quality boost mode", | |
| GST_TYPE_AMF_ENC_PA_HQMB_MODE, DEFAULT_PA_HQMB_MODE, | |
| pa_param_flags)); | |
| } | |
| } | |
| gst_element_class_set_metadata (element_class, | |
| "AMD AMF H.265 Video Encoder", | |
| "Codec/Encoder/Video/Hardware", | |
| "Encode H.265 video streams using AMF API", | |
| "Seungha Yang <seungha@centricular.com>"); | |
| pad_templ = gst_pad_template_new ("sink", | |
| GST_PAD_SINK, GST_PAD_ALWAYS, cdata->sink_caps); | |
| doc_caps = gst_caps_from_string (DOC_SINK_CAPS); | |
| gst_pad_template_set_documentation_caps (pad_templ, doc_caps); | |
| gst_caps_unref (doc_caps); | |
| gst_element_class_add_pad_template (element_class, pad_templ); | |
| pad_templ = gst_pad_template_new ("src", | |
| GST_PAD_SRC, GST_PAD_ALWAYS, cdata->src_caps); | |
| doc_caps = gst_caps_from_string (DOC_SRC_CAPS); | |
| gst_pad_template_set_documentation_caps (pad_templ, doc_caps); | |
| gst_caps_unref (doc_caps); | |
| gst_element_class_add_pad_template (element_class, pad_templ); | |
| videoenc_class->getcaps = GST_DEBUG_FUNCPTR (gst_amf_h265_enc_getcaps); | |
| amf_class->set_format = GST_DEBUG_FUNCPTR (gst_amf_h265_enc_set_format); | |
| amf_class->set_output_state = | |
| GST_DEBUG_FUNCPTR (gst_amf_h265_enc_set_output_state); | |
| amf_class->set_surface_prop = | |
| GST_DEBUG_FUNCPTR (gst_amf_h265_enc_set_surface_prop); | |
| amf_class->create_output_buffer = | |
| GST_DEBUG_FUNCPTR (gst_amf_h265_enc_create_output_buffer); | |
| amf_class->check_reconfigure = | |
| GST_DEBUG_FUNCPTR (gst_amf_h265_enc_check_reconfigure); | |
| klass->dev_caps = cdata->dev_caps; | |
| klass->adapter_luid = cdata->adapter_luid; | |
| gst_caps_unref (cdata->sink_caps); | |
| gst_caps_unref (cdata->src_caps); | |
| g_free (cdata); | |
| gst_type_mark_as_plugin_api (GST_TYPE_AMF_H265_ENC_USAGE, | |
| (GstPluginAPIFlags) 0); | |
| gst_type_mark_as_plugin_api (GST_TYPE_AMF_H265_ENC_RATE_CONTROL, | |
| (GstPluginAPIFlags) 0); | |
| gst_type_mark_as_plugin_api (GST_TYPE_AMF_H265_ENC_PRESET, | |
| (GstPluginAPIFlags) 0); | |
| } | |
| static void | |
| gst_amf_h265_enc_init (GstAmfH265Enc * self) | |
| { | |
| GstAmfH265EncClass *klass = GST_AMF_H265_ENC_GET_CLASS (self); | |
| GstAmfH265EncDeviceCaps *dev_caps = &klass->dev_caps; | |
| gst_amf_encoder_set_subclass_data (GST_AMF_ENCODER (self), | |
| klass->adapter_luid, AMFVideoEncoder_HEVC); | |
| g_mutex_init (&self->prop_lock); | |
| self->usage = DEFAULT_USAGE; | |
| self->rate_control = DEFAULT_RATE_CONTROL; | |
| self->preset = DEFAULT_PRESET; | |
| self->bitrate = DEFAULT_BITRATE; | |
| self->max_bitrate = DEFAULT_MAX_BITRATE; | |
| self->gop_size = (guint) dev_caps->default_gop_size; | |
| self->min_qp_i = DEFAULT_MIN_MAX_QP; | |
| self->max_qp_i = DEFAULT_MIN_MAX_QP; | |
| self->min_qp_p = DEFAULT_MIN_MAX_QP; | |
| self->max_qp_p = DEFAULT_MIN_MAX_QP; | |
| self->qp_i = (guint) dev_caps->default_qp_i; | |
| self->qp_p = (guint) dev_caps->default_qp_p; | |
| self->ref_frames = (guint) dev_caps->min_ref_frames; | |
| self->aud = DEFAULT_AUD; | |
| self->smart_access = DEFAULT_SMART_ACCESS; | |
| self->pre_encode = DEFAULT_PRE_ENCODE; | |
| // Init pre-analysis options | |
| self->pa.pre_analysis = DEFAULT_PRE_ANALYSIS; | |
| self->pa.activity_type = DEFAULT_PA_ACTIVITY_TYPE; | |
| self->pa.scene_change_detection = DEFAULT_PA_SCENE_CHANGE_DETECTION; | |
| self->pa.scene_change_detection_sensitivity = | |
| DEFAULT_PA_SCENE_CHANGE_DETECTION_SENSITIVITY; | |
| self->pa.static_scene_detection = DEFAULT_PA_STATIC_SCENE_DETECTION; | |
| self->pa.static_scene_detection_sensitivity = | |
| DEFAULT_PA_STATIC_SCENE_DETECTION_SENSITIVITY; | |
| self->pa.initial_qp = DEFAULT_PA_INITIAL_QP; | |
| self->pa.max_qp = DEFAULT_PA_MAX_QP; | |
| self->pa.caq_strength = DEFAULT_PA_CAQ_STRENGTH; | |
| self->pa.frame_sad = DEFAULT_PA_FRAME_SAD; | |
| self->pa.ltr = DEFAULT_PA_LTR; | |
| self->pa.lookahead_buffer_depth = DEFAULT_PA_LOOKAHEAD_BUFFER_DEPTH; | |
| self->pa.paq_mode = DEFAULT_PA_PAQ_MODE; | |
| self->pa.taq_mode = DEFAULT_PA_TAQ_MODE; | |
| self->pa.hmqb_mode = DEFAULT_PA_HQMB_MODE; | |
| } | |
| static void | |
| gst_amf_h265_enc_finalize (GObject * object) | |
| { | |
| GstAmfH265Enc *self = GST_AMF_H265_ENC (object); | |
| g_mutex_clear (&self->prop_lock); | |
| G_OBJECT_CLASS (parent_class)->finalize (object); | |
| } | |
| static void | |
| update_int (GstAmfH265Enc * self, gint * old_val, const GValue * new_val) | |
| { | |
| gint val = g_value_get_int (new_val); | |
| if (*old_val == val) | |
| return; | |
| *old_val = val; | |
| self->property_updated = TRUE; | |
| } | |
| static void | |
| update_uint (GstAmfH265Enc * self, guint * old_val, const GValue * new_val) | |
| { | |
| guint val = g_value_get_uint (new_val); | |
| if (*old_val == val) | |
| return; | |
| *old_val = val; | |
| self->property_updated = TRUE; | |
| } | |
| static void | |
| update_enum (GstAmfH265Enc * self, gint * old_val, const GValue * new_val) | |
| { | |
| gint val = g_value_get_enum (new_val); | |
| if (*old_val == val) | |
| return; | |
| *old_val = val; | |
| self->property_updated = TRUE; | |
| } | |
| static void | |
| update_bool (GstAmfH265Enc * self, gboolean * old_val, const GValue * new_val) | |
| { | |
| gboolean val = g_value_get_boolean (new_val); | |
| if (*old_val == val) | |
| return; | |
| *old_val = val; | |
| self->property_updated = TRUE; | |
| } | |
| static void | |
| gst_amf_h265_enc_set_property (GObject * object, guint prop_id, | |
| const GValue * value, GParamSpec * pspec) | |
| { | |
| GstAmfH265Enc *self = GST_AMF_H265_ENC (object); | |
| g_mutex_lock (&self->prop_lock); | |
| switch (prop_id) { | |
| case PROP_USAGE: | |
| update_enum (self, &self->usage, value); | |
| break; | |
| case PROP_RATE_CONTROL: | |
| update_enum (self, &self->rate_control, value); | |
| break; | |
| case PROP_PRESET: | |
| update_enum (self, &self->preset, value); | |
| break; | |
| case PROP_BITRATE: | |
| update_uint (self, &self->bitrate, value); | |
| break; | |
| case PROP_MAX_BITRATE: | |
| update_uint (self, &self->max_bitrate, value); | |
| break; | |
| case PROP_GOP_SIZE: | |
| update_uint (self, &self->gop_size, value); | |
| break; | |
| case PROP_MIN_QP_I: | |
| update_int (self, &self->min_qp_i, value); | |
| break; | |
| case PROP_MAX_QP_I: | |
| update_int (self, &self->max_qp_i, value); | |
| break; | |
| case PROP_MIN_QP_P: | |
| update_int (self, &self->min_qp_p, value); | |
| break; | |
| case PROP_MAX_QP_P: | |
| update_int (self, &self->max_qp_p, value); | |
| break; | |
| case PROP_QP_I: | |
| update_uint (self, &self->qp_i, value); | |
| break; | |
| case PROP_QP_P: | |
| update_uint (self, &self->qp_p, value); | |
| break; | |
| case PROP_REF_FRAMES: | |
| update_uint (self, &self->ref_frames, value); | |
| break; | |
| case PROP_AUD: | |
| /* This is per frame property, don't need to reset encoder */ | |
| self->aud = g_value_get_boolean (value); | |
| break; | |
| case PROP_SMART_ACCESS: | |
| update_bool (self, &self->smart_access, value); | |
| break; | |
| case PROP_PRE_ENCODE: | |
| update_bool (self, &self->pre_encode, value); | |
| break; | |
| case PROP_PRE_ANALYSIS: | |
| update_bool (self, &self->pa.pre_analysis, value); | |
| break; | |
| case PROP_PA_ACTIVITY_TYPE: | |
| update_enum (self, &self->pa.activity_type, value); | |
| break; | |
| case PROP_PA_SCENE_CHANGE_DETECTION: | |
| update_bool (self, &self->pa.scene_change_detection, value); | |
| break; | |
| case PROP_PA_SCENE_CHANGE_DETECTION_SENSITIVITY: | |
| update_enum (self, &self->pa.scene_change_detection_sensitivity, value); | |
| break; | |
| case PROP_PA_STATIC_SCENE_DETECTION: | |
| update_bool (self, &self->pa.static_scene_detection, value); | |
| break; | |
| case PROP_PA_STATIC_SCENE_DETECTION_SENSITIVITY: | |
| update_enum (self, &self->pa.static_scene_detection_sensitivity, value); | |
| break; | |
| case PROP_PA_INITIAL_QP: | |
| update_uint (self, &self->pa.initial_qp, value); | |
| break; | |
| case PROP_PA_MAX_QP: | |
| update_uint (self, &self->pa.max_qp, value); | |
| break; | |
| case PROP_PA_CAQ_STRENGTH: | |
| update_enum (self, &self->pa.caq_strength, value); | |
| break; | |
| case PROP_PA_FRAME_SAD: | |
| update_bool (self, &self->pa.frame_sad, value); | |
| case PROP_PA_LTR: | |
| update_bool (self, &self->pa.ltr, value); | |
| break; | |
| case PROP_PA_LOOKAHEAD_BUFFER_DEPTH: | |
| update_uint (self, &self->pa.lookahead_buffer_depth, value); | |
| break; | |
| case PROP_PA_PAQ_MODE: | |
| update_enum (self, &self->pa.paq_mode, value); | |
| break; | |
| case PROP_PA_TAQ_MODE: | |
| update_enum (self, &self->pa.taq_mode, value); | |
| break; | |
| case PROP_PA_HQMB_MODE: | |
| update_enum (self, &self->pa.hmqb_mode, value); | |
| break; | |
| default: | |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
| break; | |
| } | |
| g_mutex_unlock (&self->prop_lock); | |
| } | |
| static void | |
| gst_amf_h265_enc_get_property (GObject * object, guint prop_id, | |
| GValue * value, GParamSpec * pspec) | |
| { | |
| GstAmfH265EncClass *klass = GST_AMF_H265_ENC_GET_CLASS (object); | |
| GstAmfH265Enc *self = GST_AMF_H265_ENC (object); | |
| switch (prop_id) { | |
| case PROP_ADAPTER_LUID: | |
| g_value_set_int64 (value, klass->adapter_luid); | |
| break; | |
| case PROP_USAGE: | |
| g_value_set_enum (value, self->usage); | |
| break; | |
| case PROP_RATE_CONTROL: | |
| g_value_set_enum (value, self->rate_control); | |
| break; | |
| case PROP_PRESET: | |
| g_value_set_enum (value, self->preset); | |
| break; | |
| case PROP_BITRATE: | |
| g_value_set_uint (value, self->bitrate); | |
| break; | |
| case PROP_MAX_BITRATE: | |
| g_value_set_uint (value, self->max_bitrate); | |
| break; | |
| case PROP_GOP_SIZE: | |
| g_value_set_uint (value, self->gop_size); | |
| break; | |
| case PROP_MIN_QP_I: | |
| g_value_set_int (value, self->min_qp_i); | |
| break; | |
| case PROP_MAX_QP_I: | |
| g_value_set_int (value, self->max_qp_i); | |
| break; | |
| case PROP_MIN_QP_P: | |
| g_value_set_int (value, self->min_qp_p); | |
| break; | |
| case PROP_MAX_QP_P: | |
| g_value_set_int (value, self->max_qp_p); | |
| break; | |
| case PROP_QP_I: | |
| g_value_set_uint (value, self->qp_i); | |
| break; | |
| case PROP_QP_P: | |
| g_value_set_uint (value, self->qp_p); | |
| break; | |
| case PROP_REF_FRAMES: | |
| g_value_set_uint (value, self->ref_frames); | |
| break; | |
| case PROP_AUD: | |
| g_value_set_boolean (value, self->aud); | |
| break; | |
| case PROP_SMART_ACCESS: | |
| g_value_set_boolean (value, self->smart_access); | |
| break; | |
| case PROP_PRE_ENCODE: | |
| g_value_set_boolean (value, self->pre_encode); | |
| break; | |
| case PROP_PRE_ANALYSIS: | |
| g_value_set_boolean (value, self->pa.pre_analysis); | |
| break; | |
| case PROP_PA_ACTIVITY_TYPE: | |
| g_value_set_enum (value, self->pa.activity_type); | |
| break; | |
| case PROP_PA_SCENE_CHANGE_DETECTION: | |
| g_value_set_boolean (value, self->pa.scene_change_detection); | |
| break; | |
| case PROP_PA_SCENE_CHANGE_DETECTION_SENSITIVITY: | |
| g_value_set_enum (value, self->pa.scene_change_detection_sensitivity); | |
| break; | |
| case PROP_PA_STATIC_SCENE_DETECTION: | |
| g_value_set_boolean (value, self->pa.static_scene_detection); | |
| break; | |
| case PROP_PA_STATIC_SCENE_DETECTION_SENSITIVITY: | |
| g_value_set_enum (value, self->pa.static_scene_detection_sensitivity); | |
| break; | |
| case PROP_PA_INITIAL_QP: | |
| g_value_set_uint (value, self->pa.initial_qp); | |
| break; | |
| case PROP_PA_MAX_QP: | |
| g_value_set_uint (value, self->pa.max_qp); | |
| break; | |
| case PROP_PA_CAQ_STRENGTH: | |
| g_value_set_enum (value, self->pa.caq_strength); | |
| break; | |
| case PROP_PA_FRAME_SAD: | |
| g_value_set_boolean (value, self->pa.frame_sad); | |
| break; | |
| case PROP_PA_LTR: | |
| g_value_set_boolean (value, self->pa.ltr); | |
| break; | |
| case PROP_PA_LOOKAHEAD_BUFFER_DEPTH: | |
| g_value_set_uint (value, self->pa.lookahead_buffer_depth); | |
| break; | |
| case PROP_PA_PAQ_MODE: | |
| g_value_set_enum (value, self->pa.paq_mode); | |
| break; | |
| case PROP_PA_TAQ_MODE: | |
| g_value_set_enum (value, self->pa.taq_mode); | |
| break; | |
| case PROP_PA_HQMB_MODE: | |
| g_value_set_enum (value, self->pa.hmqb_mode); | |
| break; | |
| default: | |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
| break; | |
| } | |
| } | |
| static void | |
| gst_amf_h265_enc_get_downstream_profiles (GstAmfH265Enc * self, | |
| std::set < std::string > &downstream_profiles) | |
| { | |
| GstCaps *allowed_caps; | |
| GstStructure *s; | |
| allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (self)); | |
| if (!allowed_caps || gst_caps_is_empty (allowed_caps) || | |
| gst_caps_is_any (allowed_caps)) { | |
| gst_clear_caps (&allowed_caps); | |
| return; | |
| } | |
| for (guint i = 0; i < gst_caps_get_size (allowed_caps); i++) { | |
| const GValue *profile_value; | |
| const gchar *profile; | |
| s = gst_caps_get_structure (allowed_caps, i); | |
| profile_value = gst_structure_get_value (s, "profile"); | |
| if (!profile_value) | |
| continue; | |
| if (GST_VALUE_HOLDS_LIST (profile_value)) { | |
| for (guint j = 0; j < gst_value_list_get_size (profile_value); j++) { | |
| const GValue *p = gst_value_list_get_value (profile_value, j); | |
| if (!G_VALUE_HOLDS_STRING (p)) | |
| continue; | |
| profile = g_value_get_string (p); | |
| if (profile) | |
| downstream_profiles.insert (profile); | |
| } | |
| } else if (G_VALUE_HOLDS_STRING (profile_value)) { | |
| profile = g_value_get_string (profile_value); | |
| if (profile) | |
| downstream_profiles.insert (profile); | |
| } | |
| } | |
| gst_clear_caps (&allowed_caps); | |
| } | |
| static GstCaps * | |
| gst_amf_h265_enc_getcaps (GstVideoEncoder * encoder, GstCaps * filter) | |
| { | |
| GstAmfH265Enc *self = GST_AMF_H265_ENC (encoder); | |
| GstCaps *template_caps; | |
| GstCaps *supported_caps; | |
| std::set < std::string > downstream_profiles; | |
| std::set < std::string > allowed_formats; | |
| gst_amf_h265_enc_get_downstream_profiles (self, downstream_profiles); | |
| GST_DEBUG_OBJECT (self, "Downstream specified %" G_GSIZE_FORMAT " profiles", | |
| downstream_profiles.size ()); | |
| if (downstream_profiles.size () == 0) | |
| return gst_video_encoder_proxy_getcaps (encoder, NULL, filter); | |
| /* *INDENT-OFF* */ | |
| for (const auto &iter : downstream_profiles) { | |
| if (iter == "main") { | |
| allowed_formats.insert("NV12"); | |
| } else if (iter == "main-10") { | |
| allowed_formats.insert("P010_10LE"); | |
| } | |
| } | |
| /* *INDENT-ON* */ | |
| template_caps = gst_pad_get_pad_template_caps (encoder->sinkpad); | |
| template_caps = gst_caps_make_writable (template_caps); | |
| GValue formats = G_VALUE_INIT; | |
| g_value_init (&formats, GST_TYPE_LIST); | |
| /* *INDENT-OFF* */ | |
| for (const auto &iter: allowed_formats) { | |
| GValue val = G_VALUE_INIT; | |
| g_value_init (&val, G_TYPE_STRING); | |
| g_value_set_string (&val, iter.c_str()); | |
| gst_value_list_append_and_take_value (&formats, &val); | |
| } | |
| /* *INDENT-ON* */ | |
| gst_caps_set_value (template_caps, "format", &formats); | |
| g_value_unset (&formats); | |
| supported_caps = gst_video_encoder_proxy_getcaps (encoder, | |
| template_caps, filter); | |
| gst_caps_unref (template_caps); | |
| GST_DEBUG_OBJECT (self, "Returning %" GST_PTR_FORMAT, supported_caps); | |
| return supported_caps; | |
| } | |
| static gboolean | |
| gst_amf_h265_enc_set_format (GstAmfEncoder * encoder, | |
| GstVideoCodecState * state, gpointer component, guint * num_reorder_frames) | |
| { | |
| GstAmfH265Enc *self = GST_AMF_H265_ENC (encoder); | |
| GstAmfH265EncClass *klass = GST_AMF_H265_ENC_GET_CLASS (self); | |
| GstAmfH265EncDeviceCaps *dev_caps = &klass->dev_caps; | |
| AMFComponent *comp = (AMFComponent *) component; | |
| GstVideoInfo *info = &state->info; | |
| const GstVideoColorimetry *cinfo = &info->colorimetry; | |
| amf_int64 color_profile; | |
| AMF_RESULT result; | |
| AMFRate framerate; | |
| AMFRatio aspect_ratio; | |
| amf_int64 int64_val; | |
| amf_int64 profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; | |
| amf_int64 color_depth; | |
| AMF_SURFACE_FORMAT surface_format; | |
| std::set < std::string > downstream_profiles; | |
| AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_ENUM rc_mode; | |
| g_mutex_lock (&self->prop_lock); | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_FRAMESIZE, | |
| AMFConstructSize (info->width, info->height)); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set frame size, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_USAGE, | |
| (amf_int64) self->usage); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set usage, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| if (self->preset > AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_UNKNOWN) { | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET, | |
| (amf_int64) self->preset); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set quality preset, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| } | |
| gst_amf_h265_enc_get_downstream_profiles (self, downstream_profiles); | |
| if (downstream_profiles.empty ()) { | |
| GST_ERROR_OBJECT (self, "Unable to get downstream profile"); | |
| goto error; | |
| } | |
| if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_P010_10LE) { | |
| if (downstream_profiles.find ("main-10") == downstream_profiles.end ()) { | |
| GST_ERROR_OBJECT (self, "Downstream does not support main-10 profile"); | |
| goto error; | |
| } else { | |
| color_depth = AMF_COLOR_BIT_DEPTH_10; | |
| surface_format = AMF_SURFACE_P010; | |
| profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10; | |
| } | |
| } else if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_NV12) { | |
| if (downstream_profiles.find ("main") == downstream_profiles.end ()) { | |
| GST_ERROR_OBJECT (self, "Downstream does not support main profile"); | |
| goto error; | |
| } else { | |
| color_depth = AMF_COLOR_BIT_DEPTH_8; | |
| surface_format = AMF_SURFACE_NV12; | |
| profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; | |
| } | |
| } else { | |
| GST_ERROR_OBJECT (self, "Unexpected format %s", | |
| gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (info))); | |
| g_assert_not_reached (); | |
| goto error; | |
| } | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_PROFILE, | |
| (amf_int64) profile); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set profile, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| result = | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH, color_depth); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set bit depth, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_MAX_NUM_REFRAMES, | |
| (amf_int64) self->ref_frames); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set ref-frames, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| aspect_ratio = AMFConstructRatio (info->par_n, info->par_d); | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_ASPECT_RATIO, | |
| aspect_ratio); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set aspect ratio, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| if (cinfo->range == GST_VIDEO_COLOR_RANGE_0_255) | |
| int64_val = AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_FULL; | |
| else | |
| int64_val = AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE_STUDIO; | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, int64_val); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set full-range-color, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| if (dev_caps->smart_access_supported) { | |
| result = | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_ENABLE_SMART_ACCESS_VIDEO, | |
| (amf_bool) self->smart_access); | |
| if (result != AMF_OK) { | |
| GST_WARNING_OBJECT (self, "Failed to set smart access video, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| } | |
| } | |
| color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN; | |
| switch (cinfo->matrix) { | |
| case GST_VIDEO_COLOR_MATRIX_BT601: | |
| if (cinfo->range == GST_VIDEO_COLOR_RANGE_0_255) { | |
| color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601; | |
| } else { | |
| color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601; | |
| } | |
| break; | |
| case GST_VIDEO_COLOR_MATRIX_BT709: | |
| if (cinfo->range == GST_VIDEO_COLOR_RANGE_0_255) { | |
| color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709; | |
| } else { | |
| color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709; | |
| } | |
| break; | |
| case GST_VIDEO_COLOR_MATRIX_BT2020: | |
| if (cinfo->range == GST_VIDEO_COLOR_RANGE_0_255) { | |
| color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020; | |
| } else { | |
| color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| result = | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE, | |
| color_profile); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set output color profile, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| result = | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC, | |
| gst_video_transfer_function_to_iso (cinfo->transfer)); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, | |
| "Failed to set output transfer characteristic, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES, | |
| gst_video_color_primaries_to_iso (cinfo->primaries)); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set output color primaries, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| if (cinfo->transfer == GST_VIDEO_TRANSFER_SMPTE2084 && | |
| state->mastering_display_info && state->content_light_level) { | |
| AMFBuffer *hdrmeta_buffer = NULL; | |
| result = | |
| comp->GetContext ()->AllocBuffer (AMF_MEMORY_HOST, | |
| sizeof (AMFHDRMetadata), &hdrmeta_buffer); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to allocate HDR metadata buffer, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| AMFHDRMetadata *hdrmeta = (AMFHDRMetadata *) hdrmeta_buffer->GetNative (); | |
| GstVideoMasteringDisplayInfo *minfo = state->mastering_display_info; | |
| GstVideoContentLightLevel *cllinfo = state->content_light_level; | |
| hdrmeta->maxMasteringLuminance = | |
| (amf_uint32) minfo->max_display_mastering_luminance; | |
| hdrmeta->minMasteringLuminance = | |
| (amf_uint32) minfo->min_display_mastering_luminance; | |
| hdrmeta->redPrimary[0] = minfo->display_primaries[0].x; | |
| hdrmeta->redPrimary[1] = minfo->display_primaries[0].y; | |
| hdrmeta->greenPrimary[0] = minfo->display_primaries[1].x; | |
| hdrmeta->greenPrimary[1] = minfo->display_primaries[1].y; | |
| hdrmeta->bluePrimary[0] = minfo->display_primaries[2].x; | |
| hdrmeta->bluePrimary[1] = minfo->display_primaries[2].y; | |
| hdrmeta->whitePoint[0] = minfo->white_point.x; | |
| hdrmeta->whitePoint[1] = minfo->white_point.y; | |
| hdrmeta->maxContentLightLevel = | |
| (amf_uint16) cllinfo->max_content_light_level; | |
| hdrmeta->maxFrameAverageLightLevel = | |
| (amf_uint16) cllinfo->max_frame_average_light_level; | |
| result = | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_INPUT_HDR_METADATA, | |
| hdrmeta_buffer); | |
| hdrmeta_buffer->Release (); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set HDR metadata, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| } | |
| if (dev_caps->pre_encode_supported) { | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_PREENCODE_ENABLE, | |
| (amf_bool) self->pre_encode); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set pre-encode, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| } | |
| if (dev_caps->pre_analysis) { | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE, | |
| (amf_bool) self->pa.pre_analysis); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set pre-analysis, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| if (self->pa.pre_analysis) { | |
| result = | |
| gst_amf_encoder_set_pre_analysis_options (encoder, comp, &self->pa, | |
| &dev_caps->pa_supported); | |
| if (result != AMF_OK) | |
| goto error; | |
| } | |
| } | |
| result = comp->Init (surface_format, info->width, info->height); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to init component, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| if (self->rate_control != AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN) { | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD, | |
| (amf_int64) self->rate_control); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set rate-control, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| } | |
| result = comp->GetProperty (AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD, | |
| &int64_val); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to get rate-control method, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| rc_mode = (AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_ENUM) int64_val; | |
| if (self->min_qp_i >= 0) { | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_MIN_QP_I, | |
| (amf_int64) self->min_qp_i); | |
| } | |
| if (self->max_qp_i >= 0) { | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_MAX_QP_I, | |
| (amf_int64) self->max_qp_i); | |
| } | |
| if (self->min_qp_p >= 0) { | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_MIN_QP_P, | |
| (amf_int64) self->min_qp_p); | |
| } | |
| if (self->max_qp_p >= 0) { | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_MAX_QP_P, | |
| (amf_int64) self->max_qp_p); | |
| } | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_QP_I, (amf_int64) self->qp_i); | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_QP_P, (amf_int64) self->qp_p); | |
| switch (rc_mode) { | |
| case AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR: | |
| if (self->bitrate > 0) { | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, | |
| (amf_int64) self->bitrate * 1000); | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_PEAK_BITRATE, | |
| (amf_int64) self->bitrate * 1000); | |
| } | |
| break; | |
| case AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR: | |
| case AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR: | |
| if (self->bitrate > 0) { | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, | |
| (amf_int64) self->bitrate * 1000); | |
| } | |
| if (self->max_bitrate > 0) { | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_PEAK_BITRATE, | |
| (amf_int64) self->max_bitrate * 1000); | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| /* Disable frame skip for now, need investigation the behavior */ | |
| result = | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_SKIP_FRAME_ENABLE, | |
| (amf_bool) false); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to disable skip frame, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| if (info->fps_n > 0 && info->fps_d) { | |
| framerate = AMFConstructRate (info->fps_n, info->fps_d); | |
| } else { | |
| framerate = AMFConstructRate (25, 1); | |
| } | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_FRAMERATE, framerate); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set frame rate, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| result = comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_GOP_SIZE, | |
| (amf_int64) self->gop_size); | |
| if (result != AMF_OK) { | |
| GST_ERROR_OBJECT (self, "Failed to set gop-size, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| goto error; | |
| } | |
| self->property_updated = FALSE; | |
| g_mutex_unlock (&self->prop_lock); | |
| return TRUE; | |
| error: | |
| g_mutex_unlock (&self->prop_lock); | |
| return FALSE; | |
| } | |
| static gboolean | |
| gst_amf_h265_enc_set_output_state (GstAmfEncoder * encoder, | |
| GstVideoCodecState * state, gpointer component) | |
| { | |
| GstAmfH265Enc *self = GST_AMF_H265_ENC (encoder); | |
| GstVideoInfo *info = &state->info; | |
| GstVideoCodecState *output_state; | |
| GstCaps *caps; | |
| GstTagList *tags; | |
| std::string caps_str = "video/x-h265, alignment = (string) au" | |
| ", stream-format = (string) byte-stream, profile = (string) "; | |
| if (GST_VIDEO_INFO_FORMAT (info) == GST_VIDEO_FORMAT_P010_10LE) { | |
| caps_str += "main-10"; | |
| } else { | |
| caps_str += "main"; | |
| } | |
| caps = gst_caps_from_string (caps_str.c_str ()); | |
| output_state = gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (self), | |
| caps, state); | |
| GST_INFO_OBJECT (self, "Output caps: %" GST_PTR_FORMAT, output_state->caps); | |
| gst_video_codec_state_unref (output_state); | |
| tags = gst_tag_list_new_empty (); | |
| gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, | |
| "amfh265enc", nullptr); | |
| gst_video_encoder_merge_tags (GST_VIDEO_ENCODER (encoder), | |
| tags, GST_TAG_MERGE_REPLACE); | |
| gst_tag_list_unref (tags); | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_amf_h265_enc_set_surface_prop (GstAmfEncoder * encoder, | |
| GstVideoCodecFrame * frame, gpointer surface) | |
| { | |
| GstAmfH265Enc *self = GST_AMF_H265_ENC (encoder); | |
| AMFSurface *surf = (AMFSurface *) surface; | |
| AMF_RESULT result; | |
| amf_bool insert_aud = self->aud ? true : false; | |
| if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) { | |
| amf_int64 type = (amf_int64) AMF_VIDEO_ENCODER_HEVC_PICTURE_TYPE_IDR; | |
| result = surf->SetProperty (AMF_VIDEO_ENCODER_HEVC_FORCE_PICTURE_TYPE, | |
| type); | |
| if (result != AMF_OK) { | |
| GST_WARNING_OBJECT (encoder, "Failed to set force idr, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| } | |
| } | |
| result = surf->SetProperty (AMF_VIDEO_ENCODER_HEVC_INSERT_AUD, &insert_aud); | |
| if (result != AMF_OK) { | |
| GST_WARNING_OBJECT (encoder, "Failed to set AUD, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| } | |
| return TRUE; | |
| } | |
| static GstBuffer * | |
| gst_amf_h265_enc_create_output_buffer (GstAmfEncoder * encoder, | |
| gpointer data, gboolean * sync_point) | |
| { | |
| GstAmfH265Enc *self = GST_AMF_H265_ENC (encoder); | |
| AMFBuffer *amf_buf = (AMFBuffer *) data; | |
| GstBuffer *buf; | |
| guint8 *data_ptr; | |
| gsize data_size; | |
| amf_int64 output_type = 0; | |
| AMF_RESULT result; | |
| data_ptr = (guint8 *) amf_buf->GetNative (); | |
| data_size = amf_buf->GetSize (); | |
| if (!data_ptr || data_size == 0) { | |
| GST_WARNING_OBJECT (self, "Empty buffer"); | |
| return nullptr; | |
| } | |
| buf = gst_buffer_new_memdup (data_ptr, data_size); | |
| result = amf_buf->GetProperty (AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE, | |
| &output_type); | |
| if (result == AMF_OK && | |
| output_type == (amf_int64) AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_IDR) { | |
| *sync_point = TRUE; | |
| } | |
| return buf; | |
| } | |
| static gboolean | |
| gst_amf_h265_enc_check_reconfigure (GstAmfEncoder * encoder) | |
| { | |
| GstAmfH265Enc *self = GST_AMF_H265_ENC (encoder); | |
| gboolean ret; | |
| g_mutex_lock (&self->prop_lock); | |
| ret = self->property_updated; | |
| g_mutex_unlock (&self->prop_lock); | |
| return ret; | |
| } | |
| static GstAmfH265EncClassData * | |
| gst_amf_h265_enc_create_class_data (GstD3D11Device * device, | |
| AMFComponent * comp) | |
| { | |
| AMF_RESULT result; | |
| GstAmfH265EncDeviceCaps dev_caps = { 0, }; | |
| std::string sink_caps_str; | |
| std::string src_caps_str; | |
| std::set < std::string > profiles; | |
| std::string profile_str; | |
| std::string resolution_str; | |
| GstAmfH265EncClassData *cdata; | |
| AMFCapsPtr amf_caps; | |
| AMFIOCapsPtr in_iocaps; | |
| AMFIOCapsPtr out_iocaps; | |
| amf_int32 in_min_width = 0, in_max_width = 0; | |
| amf_int32 in_min_height = 0, in_max_height = 0; | |
| amf_int32 out_min_width = 0, out_max_width = 0; | |
| amf_int32 out_min_height = 0, out_max_height = 0; | |
| amf_bool pre_encode_supported; | |
| amf_bool smart_access_supported; | |
| amf_int32 num_val; | |
| std::set < std::string > formats; | |
| std::string format_str; | |
| gboolean have_nv12 = FALSE; | |
| gboolean have_p010 = FALSE; | |
| gboolean d3d11_supported = FALSE; | |
| gint min_width, max_width, min_height, max_height; | |
| GstCaps *sink_caps; | |
| GstCaps *system_caps; | |
| result = comp->GetCaps (&amf_caps); | |
| if (result != AMF_OK) { | |
| GST_WARNING_OBJECT (device, "Unable to get caps"); | |
| return nullptr; | |
| } | |
| result = amf_caps->GetInputCaps (&in_iocaps); | |
| if (result != AMF_OK) { | |
| GST_WARNING_OBJECT (device, "Unable to get input io caps"); | |
| return nullptr; | |
| } | |
| in_iocaps->GetWidthRange (&in_min_width, &in_max_width); | |
| in_iocaps->GetHeightRange (&in_min_height, &in_max_height); | |
| dev_caps.valign = in_iocaps->GetVertAlign (); | |
| GST_INFO_OBJECT (device, "Input width: [%d, %d], height: [%d, %d], " | |
| "valign: %d", in_min_width, in_max_width, in_min_height, in_max_height, | |
| dev_caps.valign); | |
| num_val = in_iocaps->GetNumOfFormats (); | |
| GST_LOG_OBJECT (device, "Input format count: %d", num_val); | |
| for (amf_int32 i = 0; i < num_val; i++) { | |
| AMF_SURFACE_FORMAT format; | |
| amf_bool native = false; | |
| result = in_iocaps->GetFormatAt (i, &format, &native); | |
| if (result != AMF_OK) | |
| continue; | |
| GST_INFO_OBJECT (device, "Format %d supported, native %d", format, native); | |
| if (format == AMF_SURFACE_NV12) { | |
| have_nv12 = TRUE; | |
| formats.insert ("NV12"); | |
| } | |
| if (format == AMF_SURFACE_P010 && native) { | |
| have_p010 = TRUE; | |
| } | |
| } | |
| if (formats.empty ()) { | |
| GST_WARNING_OBJECT (device, "Empty supported input formats"); | |
| return nullptr; | |
| } | |
| if (!have_nv12) { | |
| GST_WARNING_OBJECT (device, "NV12 is not supported"); | |
| return nullptr; | |
| } | |
| num_val = in_iocaps->GetNumOfMemoryTypes (); | |
| GST_LOG_OBJECT (device, "Input memory type count: %d", num_val); | |
| for (amf_int32 i = 0; i < num_val; i++) { | |
| AMF_MEMORY_TYPE type; | |
| amf_bool native; | |
| result = in_iocaps->GetMemoryTypeAt (i, &type, &native); | |
| if (result != AMF_OK) | |
| continue; | |
| GST_INFO_OBJECT (device, | |
| "MemoryType %d supported, native %d", type, native); | |
| if (type == AMF_MEMORY_DX11) | |
| d3d11_supported = TRUE; | |
| } | |
| if (!d3d11_supported) { | |
| GST_WARNING_OBJECT (device, "D3D11 is not supported"); | |
| return nullptr; | |
| } | |
| result = amf_caps->GetOutputCaps (&out_iocaps); | |
| if (result != AMF_OK) { | |
| GST_WARNING_OBJECT (device, "Unable to get input io caps"); | |
| return nullptr; | |
| } | |
| out_iocaps->GetWidthRange (&out_min_width, &out_max_width); | |
| out_iocaps->GetHeightRange (&out_min_height, &out_max_height); | |
| GST_INFO_OBJECT (device, "Output width: [%d, %d], height: [%d, %d]", | |
| in_min_width, in_max_width, in_min_height, in_max_height); | |
| #define QUERY_CAPS_PROP(prop,val) G_STMT_START { \ | |
| amf_int64 _val = 0; \ | |
| result = amf_caps->GetProperty (prop, &_val); \ | |
| if (result == AMF_OK) { \ | |
| GST_INFO_OBJECT (device, G_STRINGIFY (val) ": %" G_GINT64_FORMAT, _val); \ | |
| dev_caps.val = _val; \ | |
| } \ | |
| } G_STMT_END | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_MAX_BITRATE, max_bitrate); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_NUM_OF_STREAMS, num_of_streams); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_MAX_PROFILE, max_profile); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_MAX_TIER, max_tier); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_MAX_LEVEL, max_level); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_MIN_REFERENCE_FRAMES, | |
| min_ref_frames); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_MAX_REFERENCE_FRAMES, | |
| max_ref_frames); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_NUM_OF_HW_INSTANCES, | |
| num_of_hw_instances); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_COLOR_CONVERSION, | |
| color_conversion); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_PRE_ANALYSIS, pre_analysis); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_ROI, roi_map); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_HEVC_CAP_MAX_THROUGHPUT, max_throughput); | |
| QUERY_CAPS_PROP (AMF_VIDEO_ENCODER_CAPS_HEVC_QUERY_TIMEOUT_SUPPORT, | |
| query_timeout_support); | |
| #undef QUERY_CAPS_PROP | |
| #define QUERY_DEFAULT_PROP(prop,val,default_val) G_STMT_START { \ | |
| const AMFPropertyInfo *pinfo = nullptr; \ | |
| result = comp->GetPropertyInfo (prop, &pinfo); \ | |
| if (result == AMF_OK && pinfo) { \ | |
| dev_caps.val = AMFVariantGetInt64 (&pinfo->defaultValue); \ | |
| GST_INFO_OBJECT (device, G_STRINGIFY (val) ": %" G_GINT64_FORMAT, \ | |
| dev_caps.val); \ | |
| } else { \ | |
| dev_caps.val = default_val; \ | |
| } \ | |
| } G_STMT_END | |
| QUERY_DEFAULT_PROP (AMF_VIDEO_ENCODER_HEVC_QP_I, default_qp_i, 26); | |
| QUERY_DEFAULT_PROP (AMF_VIDEO_ENCODER_HEVC_QP_P, default_qp_p, 26); | |
| #undef QUERY_DEFAULT_PROP | |
| result = comp->GetProperty (AMF_VIDEO_ENCODER_HEVC_PREENCODE_ENABLE, | |
| &pre_encode_supported); | |
| if (result == AMF_OK) | |
| dev_caps.pre_encode_supported = TRUE; | |
| result = comp->GetProperty (AMF_VIDEO_ENCODER_HEVC_ENABLE_SMART_ACCESS_VIDEO, | |
| &smart_access_supported); | |
| if (result == AMF_OK) | |
| dev_caps.smart_access_supported = TRUE; | |
| if (dev_caps.pre_analysis) { | |
| amf_bool pre_analysis = FALSE; | |
| // Store initial pre-analysis value | |
| result = | |
| comp->GetProperty (AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE, | |
| &pre_analysis); | |
| if (result != AMF_OK) { | |
| GST_WARNING_OBJECT (device, "Failed to get pre-analysis option"); | |
| } | |
| // We need to enable pre-analysis for checking options availability | |
| result = | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE, | |
| (amf_bool) TRUE); | |
| if (result != AMF_OK) { | |
| GST_WARNING_OBJECT (device, "Failed to set pre-analysis option"); | |
| } | |
| gst_amf_encoder_check_pa_supported_options (&dev_caps.pa_supported, comp); | |
| result = | |
| comp->SetProperty (AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE, | |
| pre_analysis); | |
| if (result != AMF_OK) { | |
| GST_WARNING_OBJECT (device, "Failed to set pre-analysis option"); | |
| } | |
| } | |
| { | |
| const AMFPropertyInfo *pinfo = nullptr; | |
| result = comp->GetPropertyInfo (AMF_VIDEO_ENCODER_HEVC_GOP_SIZE, &pinfo); | |
| if (result == AMF_OK && pinfo) { | |
| dev_caps.default_gop_size = AMFVariantGetInt64 (&pinfo->defaultValue); | |
| dev_caps.min_gop_size = AMFVariantGetInt64 (&pinfo->minValue); | |
| dev_caps.max_gop_size = AMFVariantGetInt64 (&pinfo->maxValue); | |
| GST_INFO_OBJECT (device, "gop-size: default %d, min %d, max %d", | |
| (guint) dev_caps.default_gop_size, | |
| (guint) dev_caps.min_gop_size, (guint) dev_caps.max_gop_size); | |
| } else { | |
| dev_caps.default_gop_size = 30; | |
| dev_caps.min_gop_size = 0; | |
| dev_caps.max_gop_size = G_MAXINT; | |
| } | |
| } | |
| if (formats.find ("NV12") != formats.end ()) | |
| profiles.insert ("main"); | |
| if (dev_caps.max_profile >= (amf_int64) AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10 | |
| && have_p010) { | |
| formats.insert ("P010_10LE"); | |
| profiles.insert ("main-10"); | |
| } | |
| if (profiles.empty ()) { | |
| GST_WARNING_OBJECT (device, "Failed to determine profile support"); | |
| return nullptr; | |
| } | |
| #define APPEND_STRING(dst,set,str) G_STMT_START { \ | |
| if (set.find(str) != set.end()) { \ | |
| if (!first) \ | |
| dst += ", "; \ | |
| dst += str; \ | |
| first = FALSE; \ | |
| } \ | |
| } G_STMT_END | |
| if (formats.size () == 1) { | |
| format_str = "format = (string) " + *(formats.begin ()); | |
| } else { | |
| gboolean first = TRUE; | |
| format_str = "format = (string) { "; | |
| APPEND_STRING (format_str, formats, "NV12"); | |
| APPEND_STRING (format_str, formats, "P010_10LE"); | |
| format_str += " } "; | |
| } | |
| if (profiles.size () == 1) { | |
| profile_str = "profile = (string) " + *(profiles.begin ()); | |
| } else { | |
| gboolean first = TRUE; | |
| profile_str = "profile = (string) { "; | |
| APPEND_STRING (profile_str, profiles, "main"); | |
| APPEND_STRING (profile_str, profiles, "main-10"); | |
| profile_str += " } "; | |
| } | |
| #undef APPEND_STRING | |
| min_width = MAX (in_min_width, 1); | |
| max_width = in_max_width; | |
| if (max_width == 0) { | |
| GST_WARNING_OBJECT (device, "Unknown max width, assuming 4096"); | |
| max_width = 4096; | |
| } | |
| min_height = MAX (in_min_height, 1); | |
| max_height = in_max_height; | |
| if (max_height == 0) { | |
| GST_WARNING_OBJECT (device, "Unknown max height, assuming 4096"); | |
| max_height = 4096; | |
| } | |
| resolution_str = "width = (int) [ " + std::to_string (min_width) | |
| + ", " + std::to_string (max_width) + " ]"; | |
| resolution_str += ", height = (int) [ " + std::to_string (min_height) | |
| + ", " + std::to_string (max_height) + " ]"; | |
| sink_caps_str = | |
| "video/x-raw, " + format_str + ", " + resolution_str + | |
| ", interlace-mode = (string) progressive"; | |
| src_caps_str = | |
| "video/x-h265, " + resolution_str + | |
| ", " + profile_str + | |
| ", stream-format = (string) byte-stream, alignment = (string) au"; | |
| system_caps = gst_caps_from_string (sink_caps_str.c_str ()); | |
| sink_caps = gst_caps_copy (system_caps); | |
| gst_caps_set_features (sink_caps, 0, | |
| gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, nullptr)); | |
| gst_caps_append (sink_caps, system_caps); | |
| cdata = g_new0 (GstAmfH265EncClassData, 1); | |
| cdata->sink_caps = sink_caps; | |
| cdata->src_caps = gst_caps_from_string (src_caps_str.c_str ()); | |
| cdata->dev_caps = dev_caps; | |
| g_object_get (device, "adapter-luid", &cdata->adapter_luid, nullptr); | |
| GST_MINI_OBJECT_FLAG_SET (cdata->sink_caps, | |
| GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); | |
| GST_MINI_OBJECT_FLAG_SET (cdata->src_caps, | |
| GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED); | |
| GST_DEBUG_OBJECT (device, "Sink caps %" GST_PTR_FORMAT, cdata->sink_caps); | |
| GST_DEBUG_OBJECT (device, "Src caps %" GST_PTR_FORMAT, cdata->src_caps); | |
| return cdata; | |
| } | |
| void | |
| gst_amf_h265_enc_register_d3d11 (GstPlugin * plugin, GstD3D11Device * device, | |
| gpointer context, guint rank) | |
| { | |
| GstAmfH265EncClassData *cdata; | |
| AMFContext *amf_context = (AMFContext *) context; | |
| AMFFactory *factory = (AMFFactory *) gst_amf_get_factory (); | |
| AMFComponentPtr comp; | |
| AMF_RESULT result; | |
| GST_DEBUG_CATEGORY_INIT (gst_amf_h265_enc_debug, "amfh265enc", 0, | |
| "amfh265enc"); | |
| result = factory->CreateComponent (amf_context, AMFVideoEncoder_HEVC, &comp); | |
| if (result != AMF_OK) { | |
| GST_WARNING_OBJECT (device, "Failed to create component, result %" | |
| GST_AMF_RESULT_FORMAT, GST_AMF_RESULT_ARGS (result)); | |
| return; | |
| } | |
| cdata = gst_amf_h265_enc_create_class_data (device, comp.GetPtr ()); | |
| if (!cdata) | |
| return; | |
| GType type; | |
| gchar *type_name; | |
| gchar *feature_name; | |
| GTypeInfo type_info = { | |
| sizeof (GstAmfH265EncClass), | |
| nullptr, | |
| nullptr, | |
| (GClassInitFunc) gst_amf_h265_enc_class_init, | |
| nullptr, | |
| cdata, | |
| sizeof (GstAmfH265Enc), | |
| 0, | |
| (GInstanceInitFunc) gst_amf_h265_enc_init, | |
| }; | |
| type_name = g_strdup ("GstAmfH265Enc"); | |
| feature_name = g_strdup ("amfh265enc"); | |
| gint index = 0; | |
| while (g_type_from_name (type_name)) { | |
| index++; | |
| g_free (type_name); | |
| g_free (feature_name); | |
| type_name = g_strdup_printf ("GstAmfH265Device%dEnc", index); | |
| feature_name = g_strdup_printf ("amfh265device%denc", index); | |
| } | |
| type = g_type_register_static (GST_TYPE_AMF_ENCODER, type_name, | |
| &type_info, (GTypeFlags) 0); | |
| if (rank > 0 && index != 0) | |
| rank--; | |
| if (index != 0) | |
| gst_element_type_set_skip_documentation (type); | |
| if (!gst_element_register (plugin, feature_name, rank, type)) | |
| GST_WARNING ("Failed to register plugin '%s'", type_name); | |
| g_free (type_name); | |
| g_free (feature_name); | |
| } |