From 783ac9a9f9ed08a4a9706d4c04039897460abf6f Mon Sep 17 00:00:00 2001 From: Nicolas Dufresne Date: Wed, 2 Aug 2017 10:39:46 -0400 Subject: [PATCH] v4l2videoenc: Move the profile/level negotation in the base class This removes duplicated code across different codec. --- sys/v4l2/gstv4l2h264enc.c | 226 ++----------------------------------- sys/v4l2/gstv4l2mpeg4enc.c | 225 ++---------------------------------- sys/v4l2/gstv4l2videoenc.c | 212 ++++++++++++++++++++++++++++++++++ sys/v4l2/gstv4l2videoenc.h | 13 ++- sys/v4l2/gstv4l2vp8enc.c | 166 ++------------------------- sys/v4l2/gstv4l2vp9enc.c | 165 ++------------------------- 6 files changed, 258 insertions(+), 749 deletions(-) diff --git a/sys/v4l2/gstv4l2h264enc.c b/sys/v4l2/gstv4l2h264enc.c index 81969b2882..d1c6036908 100644 --- a/sys/v4l2/gstv4l2h264enc.c +++ b/sys/v4l2/gstv4l2h264enc.c @@ -267,222 +267,9 @@ v4l2_level_to_string (gint v4l2_level) return NULL; } -struct ProfileLevelCtx -{ - GstV4l2H264Enc *self; - const gchar *profile; - const gchar *level; -}; - -static gboolean -get_string_list (GstStructure * s, const gchar * field, GQueue * queue) -{ - const GValue *value; - - value = gst_structure_get_value (s, field); - - if (!value) - return FALSE; - - if (GST_VALUE_HOLDS_LIST (value)) { - guint i; - - if (gst_value_list_get_size (value) == 0) - return FALSE; - - for (i = 0; i < gst_value_list_get_size (value); i++) { - const GValue *item = gst_value_list_get_value (value, i); - - if (G_VALUE_HOLDS_STRING (item)) - g_queue_push_tail (queue, g_value_dup_string (item)); - } - } else if (G_VALUE_HOLDS_STRING (value)) { - g_queue_push_tail (queue, g_value_dup_string (value)); - } - - return TRUE; -} - -static gboolean -negotiate_profile_and_level (GstCapsFeatures * features, GstStructure * s, - gpointer user_data) -{ - struct ProfileLevelCtx *ctx = user_data; - GstV4l2Object *v4l2object = GST_V4L2_VIDEO_ENC (ctx->self)->v4l2output; - GQueue profiles = G_QUEUE_INIT; - GQueue levels = G_QUEUE_INIT; - gboolean failed = FALSE; - - if (get_string_list (s, "profile", &profiles)) { - GList *l; - - for (l = profiles.head; l; l = l->next) { - struct v4l2_control control = { 0, }; - gint v4l2_profile; - const gchar *profile = l->data; - - GST_TRACE_OBJECT (ctx->self, "Trying profile %s", profile); - - control.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE; - control.value = v4l2_profile = v4l2_profile_from_string (profile); - - if (control.value < 0) - continue; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) { - GST_WARNING_OBJECT (ctx->self, "Failed to set H264 profile: '%s'", - g_strerror (errno)); - break; - } - - profile = v4l2_profile_to_string (control.value); - - if (control.value == v4l2_profile) { - ctx->profile = profile; - break; - } - - if (g_list_find_custom (l, profile, g_str_equal)) { - ctx->profile = profile; - break; - } - } - - if (profiles.length && !ctx->profile) - failed = TRUE; - - g_queue_foreach (&profiles, (GFunc) g_free, NULL); - g_queue_clear (&profiles); - } - - if (!failed && get_string_list (s, "level", &levels)) { - GList *l; - - for (l = levels.head; l; l = l->next) { - struct v4l2_control control = { 0, }; - gint v4l2_level; - const gchar *level = l->data; - - GST_TRACE_OBJECT (ctx->self, "Trying level %s", level); - - control.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL; - control.value = v4l2_level = v4l2_level_from_string (level); - - if (control.value < 0) - continue; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) { - GST_WARNING_OBJECT (ctx->self, "Failed to set H264 level: '%s'", - g_strerror (errno)); - break; - } - - level = v4l2_level_to_string (control.value); - - if (control.value == v4l2_level) { - ctx->level = level; - break; - } - - if (g_list_find_custom (l, level, g_str_equal)) { - ctx->level = level; - break; - } - } - - if (levels.length && !ctx->level) - failed = TRUE; - - g_queue_foreach (&levels, (GFunc) g_free, NULL); - g_queue_clear (&levels); - } - - /* If it failed, we continue */ - return failed; -} - -static gboolean -gst_v4l2_h264_enc_negotiate (GstVideoEncoder * encoder) -{ - GstV4l2H264Enc *self = GST_V4L2_H264_ENC (encoder); - GstV4l2VideoEnc *venc = GST_V4L2_VIDEO_ENC (encoder); - GstV4l2Object *v4l2object = venc->v4l2output; - GstCaps *allowed_caps; - struct ProfileLevelCtx ctx = { self, NULL, NULL }; - GstVideoCodecState *state; - GstStructure *s; - - GST_DEBUG_OBJECT (self, "Negotiating H264 profile and level."); - - allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); - - if (allowed_caps) { - - if (gst_caps_is_empty (allowed_caps)) - goto not_negotiated; - - allowed_caps = gst_caps_make_writable (allowed_caps); - - /* negotiate_profile_and_level() will return TRUE on failure to keep - * iterating, if gst_caps_foreach() returns TRUE it means there was no - * compatible profile and level in any of the structure */ - if (gst_caps_foreach (allowed_caps, negotiate_profile_and_level, &ctx)) { - goto no_profile_level; - } - } - - if (!ctx.profile) { - struct v4l2_control control = { 0, }; - - control.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0) - goto g_ctrl_failed; - - ctx.profile = v4l2_profile_to_string (control.value); - } - - if (!ctx.level) { - struct v4l2_control control = { 0, }; - - control.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0) - goto g_ctrl_failed; - - ctx.level = v4l2_level_to_string (control.value); - } - - GST_DEBUG_OBJECT (self, "Selected H264 profile %s at level %s", - ctx.profile, ctx.level); - - state = gst_video_encoder_get_output_state (encoder); - s = gst_caps_get_structure (state->caps, 0); - gst_structure_set (s, "profile", G_TYPE_STRING, ctx.profile, - "level", G_TYPE_STRING, ctx.level, NULL); - - return GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder); - -g_ctrl_failed: - GST_WARNING_OBJECT (self, "Failed to get H264 profile and level: '%s'", - g_strerror (errno)); - goto not_negotiated; - -no_profile_level: - GST_WARNING_OBJECT (self, "No compatible level and profiled in caps: %" - GST_PTR_FORMAT, allowed_caps); - goto not_negotiated; - -not_negotiated: - if (allowed_caps) - gst_caps_unref (allowed_caps); - return FALSE; -} - static void gst_v4l2_h264_enc_init (GstV4l2H264Enc * self) { - } static void @@ -490,13 +277,13 @@ gst_v4l2_h264_enc_class_init (GstV4l2H264EncClass * klass) { GstElementClass *element_class; GObjectClass *gobject_class; - GstVideoEncoderClass *baseclass; + GstV4l2VideoEncClass *baseclass; parent_class = g_type_class_peek_parent (klass); element_class = (GstElementClass *) klass; gobject_class = (GObjectClass *) klass; - baseclass = GST_VIDEO_ENCODER_CLASS (klass); + baseclass = (GstV4l2VideoEncClass *) (klass); GST_DEBUG_CATEGORY_INIT (gst_v4l2_h264_enc_debug, "v4l2h264enc", 0, "V4L2 H.264 Encoder"); @@ -510,7 +297,14 @@ gst_v4l2_h264_enc_class_init (GstV4l2H264EncClass * klass) GST_DEBUG_FUNCPTR (gst_v4l2_h264_enc_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_v4l2_h264_enc_get_property); - baseclass->negotiate = GST_DEBUG_FUNCPTR (gst_v4l2_h264_enc_negotiate); + + baseclass->codec_name = "H264"; + baseclass->profile_cid = V4L2_CID_MPEG_VIDEO_H264_PROFILE; + baseclass->profile_to_string = v4l2_profile_to_string; + baseclass->profile_from_string = v4l2_profile_from_string; + baseclass->level_cid = V4L2_CID_MPEG_VIDEO_H264_LEVEL; + baseclass->level_to_string = v4l2_level_to_string; + baseclass->level_from_string = v4l2_level_from_string; } /* Probing functions */ diff --git a/sys/v4l2/gstv4l2mpeg4enc.c b/sys/v4l2/gstv4l2mpeg4enc.c index 45b4956a09..7b0bb77658 100644 --- a/sys/v4l2/gstv4l2mpeg4enc.c +++ b/sys/v4l2/gstv4l2mpeg4enc.c @@ -163,218 +163,6 @@ v4l2_level_to_string (gint v4l2_level) return NULL; } -struct ProfileLevelCtx -{ - GstV4l2Mpeg4Enc *self; - const gchar *profile; - const gchar *level; -}; - -static gboolean -get_string_list (GstStructure * s, const gchar * field, GQueue * queue) -{ - const GValue *value; - - value = gst_structure_get_value (s, field); - - if (!value) - return FALSE; - - if (GST_VALUE_HOLDS_LIST (value)) { - guint i; - - if (gst_value_list_get_size (value) == 0) - return FALSE; - - for (i = 0; i < gst_value_list_get_size (value); i++) { - const GValue *item = gst_value_list_get_value (value, i); - - if (G_VALUE_HOLDS_STRING (item)) - g_queue_push_tail (queue, g_value_dup_string (item)); - } - } else if (G_VALUE_HOLDS_STRING (value)) { - g_queue_push_tail (queue, g_value_dup_string (value)); - } - - return TRUE; -} - -static gboolean -negotiate_profile_and_level (GstCapsFeatures * features, GstStructure * s, - gpointer user_data) -{ - struct ProfileLevelCtx *ctx = user_data; - GstV4l2Object *v4l2object = GST_V4L2_VIDEO_ENC (ctx->self)->v4l2output; - GQueue profiles = G_QUEUE_INIT; - GQueue levels = G_QUEUE_INIT; - gboolean failed = FALSE; - - if (get_string_list (s, "profile", &profiles)) { - GList *l; - - for (l = profiles.head; l; l = l->next) { - struct v4l2_control control = { 0, }; - gint v4l2_profile; - const gchar *profile = l->data; - - GST_TRACE_OBJECT (ctx->self, "Trying profile %s", profile); - - control.id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE; - control.value = v4l2_profile = v4l2_profile_from_string (profile); - - if (control.value < 0) - continue; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) { - GST_WARNING_OBJECT (ctx->self, "Failed to set MPEG4 profile: '%s'", - g_strerror (errno)); - break; - } - - profile = v4l2_profile_to_string (control.value); - - if (control.value == v4l2_profile) { - ctx->profile = profile; - break; - } - - if (g_list_find_custom (l, profile, g_str_equal)) { - ctx->profile = profile; - break; - } - } - - if (profiles.length && !ctx->profile) - failed = TRUE; - - g_queue_foreach (&profiles, (GFunc) g_free, NULL); - g_queue_clear (&profiles); - } - - if (!failed && get_string_list (s, "level", &levels)) { - GList *l; - - for (l = levels.head; l; l = l->next) { - struct v4l2_control control = { 0, }; - gint v4l2_level; - const gchar *level = l->data; - - GST_TRACE_OBJECT (ctx->self, "Trying level %s", level); - - control.id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL; - control.value = v4l2_level = v4l2_level_from_string (level); - - if (control.value < 0) - continue; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) { - GST_WARNING_OBJECT (ctx->self, "Failed to set MPEG4 level: '%s'", - g_strerror (errno)); - break; - } - - level = v4l2_level_to_string (control.value); - - if (control.value == v4l2_level) { - ctx->level = level; - break; - } - - if (g_list_find_custom (l, level, g_str_equal)) { - ctx->level = level; - break; - } - } - - if (levels.length && !ctx->level) - failed = TRUE; - - g_queue_foreach (&levels, (GFunc) g_free, NULL); - g_queue_clear (&levels); - } - - /* If it failed, we continue */ - return failed; -} - -static gboolean -gst_v4l2_mpeg4_enc_negotiate (GstVideoEncoder * encoder) -{ - GstV4l2Mpeg4Enc *self = GST_V4L2_MPEG4_ENC (encoder); - GstV4l2VideoEnc *venc = GST_V4L2_VIDEO_ENC (encoder); - GstV4l2Object *v4l2object = venc->v4l2output; - GstCaps *allowed_caps; - struct ProfileLevelCtx ctx = { self, NULL, NULL }; - GstVideoCodecState *state; - GstStructure *s; - - GST_DEBUG_OBJECT (self, "Negotiating MPEG4 profile and level."); - - allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); - - if (allowed_caps) { - - if (gst_caps_is_empty (allowed_caps)) - goto not_negotiated; - - allowed_caps = gst_caps_make_writable (allowed_caps); - - /* negotiate_profile_and_level() will return TRUE on failure to keep - * iterating, if gst_caps_foreach() returns TRUE it means there was no - * compatible profile and level in any of the structure */ - if (gst_caps_foreach (allowed_caps, negotiate_profile_and_level, &ctx)) { - goto no_profile_level; - } - } - - if (!ctx.profile) { - struct v4l2_control control = { 0, }; - - control.id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0) - goto g_ctrl_failed; - - ctx.profile = v4l2_profile_to_string (control.value); - } - - if (!ctx.level) { - struct v4l2_control control = { 0, }; - - control.id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0) - goto g_ctrl_failed; - - ctx.level = v4l2_level_to_string (control.value); - } - - GST_DEBUG_OBJECT (self, "Selected MPEG4 profile %s at level %s", - ctx.profile, ctx.level); - - state = gst_video_encoder_get_output_state (encoder); - s = gst_caps_get_structure (state->caps, 0); - gst_structure_set (s, "profile", G_TYPE_STRING, ctx.profile, - "level", G_TYPE_STRING, ctx.level, NULL); - - return GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder); - -g_ctrl_failed: - GST_WARNING_OBJECT (self, "Failed to get MPEG4 profile and level: '%s'", - g_strerror (errno)); - goto not_negotiated; - -no_profile_level: - GST_WARNING_OBJECT (self, "No compatible level and profiled in caps: %" - GST_PTR_FORMAT, allowed_caps); - goto not_negotiated; - -not_negotiated: - if (allowed_caps) - gst_caps_unref (allowed_caps); - return FALSE; -} - static void gst_v4l2_mpeg4_enc_init (GstV4l2Mpeg4Enc * self) { @@ -385,13 +173,13 @@ gst_v4l2_mpeg4_enc_class_init (GstV4l2Mpeg4EncClass * klass) { GstElementClass *element_class; GObjectClass *gobject_class; - GstVideoEncoderClass *baseclass; + GstV4l2VideoEncClass *baseclass; parent_class = g_type_class_peek_parent (klass); element_class = (GstElementClass *) klass; gobject_class = (GObjectClass *) klass; - baseclass = GST_VIDEO_ENCODER_CLASS (klass); + baseclass = (GstV4l2VideoEncClass *) (klass); GST_DEBUG_CATEGORY_INIT (gst_v4l2_mpeg4_enc_debug, "v4l2mpeg4enc", 0, "V4L2 MPEG4 Encoder"); @@ -406,7 +194,14 @@ gst_v4l2_mpeg4_enc_class_init (GstV4l2Mpeg4EncClass * klass) GST_DEBUG_FUNCPTR (gst_v4l2_mpeg4_enc_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_v4l2_mpeg4_enc_get_property); - baseclass->negotiate = GST_DEBUG_FUNCPTR (gst_v4l2_mpeg4_enc_negotiate); + + baseclass->codec_name = "MPEG4"; + baseclass->profile_cid = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE; + baseclass->profile_to_string = v4l2_profile_to_string; + baseclass->profile_from_string = v4l2_profile_from_string; + baseclass->level_cid = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL; + baseclass->level_to_string = v4l2_level_to_string; + baseclass->level_from_string = v4l2_level_from_string; } /* Probing functions */ diff --git a/sys/v4l2/gstv4l2videoenc.c b/sys/v4l2/gstv4l2videoenc.c index 9b14963901..ee89cec0ca 100644 --- a/sys/v4l2/gstv4l2videoenc.c +++ b/sys/v4l2/gstv4l2videoenc.c @@ -373,10 +373,205 @@ gst_v4l2_video_enc_flush (GstVideoEncoder * encoder) return TRUE; } +struct ProfileLevelCtx +{ + GstV4l2VideoEnc *self; + const gchar *profile; + const gchar *level; +}; + +static gboolean +get_string_list (GstStructure * s, const gchar * field, GQueue * queue) +{ + const GValue *value; + + value = gst_structure_get_value (s, field); + + if (!value) + return FALSE; + + if (GST_VALUE_HOLDS_LIST (value)) { + guint i; + + if (gst_value_list_get_size (value) == 0) + return FALSE; + + for (i = 0; i < gst_value_list_get_size (value); i++) { + const GValue *item = gst_value_list_get_value (value, i); + + if (G_VALUE_HOLDS_STRING (item)) + g_queue_push_tail (queue, g_value_dup_string (item)); + } + } else if (G_VALUE_HOLDS_STRING (value)) { + g_queue_push_tail (queue, g_value_dup_string (value)); + } + + return TRUE; +} + +static gboolean +negotiate_profile_and_level (GstCapsFeatures * features, GstStructure * s, + gpointer user_data) +{ + struct ProfileLevelCtx *ctx = user_data; + GstV4l2VideoEncClass *klass = GST_V4L2_VIDEO_ENC_GET_CLASS (ctx->self); + GstV4l2Object *v4l2object = GST_V4L2_VIDEO_ENC (ctx->self)->v4l2output; + GQueue profiles = G_QUEUE_INIT; + GQueue levels = G_QUEUE_INIT; + gboolean failed = FALSE; + + if (klass->profile_cid && get_string_list (s, "profile", &profiles)) { + GList *l; + + for (l = profiles.head; l; l = l->next) { + struct v4l2_control control = { 0, }; + gint v4l2_profile; + const gchar *profile = l->data; + + GST_TRACE_OBJECT (ctx->self, "Trying profile %s", profile); + + control.id = klass->profile_cid; + control.value = v4l2_profile = klass->profile_from_string (profile); + + if (control.value < 0) + continue; + + if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) { + GST_WARNING_OBJECT (ctx->self, "Failed to set %s profile: '%s'", + klass->codec_name, g_strerror (errno)); + break; + } + + profile = klass->profile_to_string (control.value); + + if (control.value == v4l2_profile) { + ctx->profile = profile; + break; + } + + if (g_list_find_custom (l, profile, g_str_equal)) { + ctx->profile = profile; + break; + } + } + + if (profiles.length && !ctx->profile) + failed = TRUE; + + g_queue_foreach (&profiles, (GFunc) g_free, NULL); + g_queue_clear (&profiles); + } + + if (!failed && klass->level_cid && get_string_list (s, "level", &levels)) { + GList *l; + + for (l = levels.head; l; l = l->next) { + struct v4l2_control control = { 0, }; + gint v4l2_level; + const gchar *level = l->data; + + GST_TRACE_OBJECT (ctx->self, "Trying level %s", level); + + control.id = klass->level_cid; + control.value = v4l2_level = klass->level_from_string (level); + + if (control.value < 0) + continue; + + if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) { + GST_WARNING_OBJECT (ctx->self, "Failed to set %s level: '%s'", + klass->codec_name, g_strerror (errno)); + break; + } + + level = klass->level_to_string (control.value); + + if (control.value == v4l2_level) { + ctx->level = level; + break; + } + + if (g_list_find_custom (l, level, g_str_equal)) { + ctx->level = level; + break; + } + } + + if (levels.length && !ctx->level) + failed = TRUE; + + g_queue_foreach (&levels, (GFunc) g_free, NULL); + g_queue_clear (&levels); + } + + /* If it failed, we continue */ + return failed; +} + static gboolean gst_v4l2_video_enc_negotiate (GstVideoEncoder * encoder) { + GstV4l2VideoEncClass *klass = GST_V4L2_VIDEO_ENC_GET_CLASS (encoder); GstV4l2VideoEnc *self = GST_V4L2_VIDEO_ENC (encoder); + GstV4l2Object *v4l2object = self->v4l2output; + GstCaps *allowed_caps; + struct ProfileLevelCtx ctx = { self, NULL, NULL }; + GstVideoCodecState *state; + GstStructure *s; + + GST_DEBUG_OBJECT (self, "Negotiating %s profile and level.", + klass->codec_name); + + allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); + + if (allowed_caps) { + + if (gst_caps_is_empty (allowed_caps)) + goto not_negotiated; + + allowed_caps = gst_caps_make_writable (allowed_caps); + + /* negotiate_profile_and_level() will return TRUE on failure to keep + * iterating, if gst_caps_foreach() returns TRUE it means there was no + * compatible profile and level in any of the structure */ + if (gst_caps_foreach (allowed_caps, negotiate_profile_and_level, &ctx)) { + goto no_profile_level; + } + } + + if (klass->profile_cid && !ctx.profile) { + struct v4l2_control control = { 0, }; + + control.id = klass->profile_cid; + + if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0) + goto g_ctrl_failed; + + ctx.profile = klass->profile_to_string (control.value); + } + + if (klass->level_cid && !ctx.level) { + struct v4l2_control control = { 0, }; + + control.id = klass->level_cid; + + if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0) + goto g_ctrl_failed; + + ctx.level = klass->level_to_string (control.value); + } + + GST_DEBUG_OBJECT (self, "Selected %s profile %s at level %s", + klass->codec_name, ctx.profile, ctx.level); + + state = gst_video_encoder_get_output_state (encoder); + s = gst_caps_get_structure (state->caps, 0); + + if (klass->profile_cid) + gst_structure_set (s, "profile", G_TYPE_STRING, ctx.profile, NULL); + + if (klass->level_cid) + gst_structure_set (s, "level", G_TYPE_STRING, ctx.level, NULL); if (!GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder)) return FALSE; @@ -388,6 +583,21 @@ gst_v4l2_video_enc_negotiate (GstVideoEncoder * encoder) } return TRUE; + +g_ctrl_failed: + GST_WARNING_OBJECT (self, "Failed to get %s profile and level: '%s'", + klass->codec_name, g_strerror (errno)); + goto not_negotiated; + +no_profile_level: + GST_WARNING_OBJECT (self, "No compatible level and profile in caps: %" + GST_PTR_FORMAT, allowed_caps); + goto not_negotiated; + +not_negotiated: + if (allowed_caps) + gst_caps_unref (allowed_caps); + return FALSE; } static GstVideoCodecFrame * @@ -783,6 +993,7 @@ gst_v4l2_video_enc_change_state (GstElement * element, return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); } + static void gst_v4l2_video_enc_dispose (GObject * object) { @@ -805,6 +1016,7 @@ gst_v4l2_video_enc_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } + static void gst_v4l2_video_enc_init (GstV4l2VideoEnc * self) { diff --git a/sys/v4l2/gstv4l2videoenc.h b/sys/v4l2/gstv4l2videoenc.h index daf1892cd1..1d5d58b054 100644 --- a/sys/v4l2/gstv4l2videoenc.h +++ b/sys/v4l2/gstv4l2videoenc.h @@ -43,6 +43,9 @@ G_BEGIN_DECLS (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2_VIDEO_ENC)) #define GST_IS_V4L2_VIDEO_ENC_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2_VIDEO_ENC)) +#define GST_V4L2_VIDEO_ENC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_V4L2_VIDEO_ENC, GstV4l2VideoEncClass)) + typedef struct _GstV4l2VideoEnc GstV4l2VideoEnc; typedef struct _GstV4l2VideoEncClass GstV4l2VideoEncClass; @@ -71,9 +74,15 @@ struct _GstV4l2VideoEncClass GstVideoEncoderClass parent_class; gchar *default_device; + const char *codec_name; - GstFlowReturn (*get_output_caps) (GstVideoEncoder * encoder, - GstCaps ** outcaps); + guint32 profile_cid; + const gchar * (*profile_to_string) (gint v4l2_profile); + gint (*profile_from_string) (const gchar * profile); + + guint32 level_cid; + const gchar * (*level_to_string) (gint v4l2_level); + gint (*level_from_string) (const gchar * level); }; GType gst_v4l2_video_enc_get_type (void); diff --git a/sys/v4l2/gstv4l2vp8enc.c b/sys/v4l2/gstv4l2vp8enc.c index 75f3586164..cf3eb06237 100644 --- a/sys/v4l2/gstv4l2vp8enc.c +++ b/sys/v4l2/gstv4l2vp8enc.c @@ -104,161 +104,6 @@ v4l2_profile_to_string (gint v4l2_profile) return NULL; } -struct ProfileCtx -{ - GstV4l2Vp8Enc *self; - const gchar *profile; -}; - -static gboolean -get_string_list (GstStructure * s, const gchar * field, GQueue * queue) -{ - const GValue *value; - - value = gst_structure_get_value (s, field); - - if (!value) - return FALSE; - - if (GST_VALUE_HOLDS_LIST (value)) { - guint i; - - if (gst_value_list_get_size (value) == 0) - return FALSE; - - for (i = 0; i < gst_value_list_get_size (value); i++) { - const GValue *item = gst_value_list_get_value (value, i); - - if (G_VALUE_HOLDS_STRING (item)) - g_queue_push_tail (queue, g_value_dup_string (item)); - } - } else if (G_VALUE_HOLDS_STRING (value)) { - g_queue_push_tail (queue, g_value_dup_string (value)); - } - - return TRUE; -} - -static gboolean -negotiate_profile (GstCapsFeatures * features, GstStructure * s, - gpointer user_data) -{ - struct ProfileCtx *ctx = user_data; - GstV4l2Object *v4l2object = GST_V4L2_VIDEO_ENC (ctx->self)->v4l2output; - GQueue profiles = G_QUEUE_INIT; - gboolean failed = FALSE; - - if (get_string_list (s, "profile", &profiles)) { - GList *l; - - for (l = profiles.head; l; l = l->next) { - struct v4l2_control control = { 0, }; - gint v4l2_profile; - const gchar *profile = l->data; - - GST_TRACE_OBJECT (ctx->self, "Trying profile %s", profile); - - control.id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE; - control.value = v4l2_profile = v4l2_profile_from_string (profile); - - if (control.value < 0) - continue; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) { - GST_WARNING_OBJECT (ctx->self, "Failed to set VP8 profile: '%s'", - g_strerror (errno)); - break; - } - - profile = v4l2_profile_to_string (control.value); - - if (control.value == v4l2_profile) { - ctx->profile = profile; - break; - } - - if (g_list_find_custom (l, profile, g_str_equal)) { - ctx->profile = profile; - break; - } - } - - if (profiles.length && !ctx->profile) - failed = TRUE; - - g_queue_foreach (&profiles, (GFunc) g_free, NULL); - g_queue_clear (&profiles); - } - - /* If it failed, we continue */ - return failed; -} - -static gboolean -gst_v4l2_vp8_enc_negotiate (GstVideoEncoder * encoder) -{ - GstV4l2Vp8Enc *self = GST_V4L2_VP8_ENC (encoder); - GstV4l2VideoEnc *venc = GST_V4L2_VIDEO_ENC (encoder); - GstV4l2Object *v4l2object = venc->v4l2output; - GstCaps *allowed_caps; - struct ProfileCtx ctx = { self, NULL }; - GstVideoCodecState *state; - GstStructure *s; - - GST_DEBUG_OBJECT (self, "Negotiating VP8 profile."); - - allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); - - if (allowed_caps) { - - if (gst_caps_is_empty (allowed_caps)) - goto not_negotiated; - - allowed_caps = gst_caps_make_writable (allowed_caps); - - /* negotiate_profile() will return TRUE on failure to keep - * iterating, if gst_caps_foreach() returns TRUE it means there was no - * compatible profile in any of the structure */ - if (gst_caps_foreach (allowed_caps, negotiate_profile, &ctx)) { - goto no_profile; - } - } - - if (!ctx.profile) { - struct v4l2_control control = { 0, }; - - control.id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0) - goto g_ctrl_failed; - - ctx.profile = v4l2_profile_to_string (control.value); - } - - GST_DEBUG_OBJECT (self, "Selected VP8 profile %s", ctx.profile); - - state = gst_video_encoder_get_output_state (encoder); - s = gst_caps_get_structure (state->caps, 0); - gst_structure_set (s, "profile", G_TYPE_STRING, ctx.profile, NULL); - - return GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder); - -g_ctrl_failed: - GST_WARNING_OBJECT (self, "Failed to get VP8 profile: '%s'", - g_strerror (errno)); - goto not_negotiated; - -no_profile: - GST_WARNING_OBJECT (self, "No compatible profile in caps: %" GST_PTR_FORMAT, - allowed_caps); - goto not_negotiated; - -not_negotiated: - if (allowed_caps) - gst_caps_unref (allowed_caps); - return FALSE; -} - static void gst_v4l2_vp8_enc_init (GstV4l2Vp8Enc * self) { @@ -269,13 +114,14 @@ gst_v4l2_vp8_enc_class_init (GstV4l2Vp8EncClass * klass) { GstElementClass *element_class; GObjectClass *gobject_class; - GstVideoEncoderClass *baseclass; + GstV4l2VideoEncClass *baseclass; parent_class = g_type_class_peek_parent (klass); element_class = (GstElementClass *) klass; gobject_class = (GObjectClass *) klass; - baseclass = GST_VIDEO_ENCODER_CLASS (klass); + baseclass = (GstV4l2VideoEncClass *) (klass); + GST_DEBUG_CATEGORY_INIT (gst_v4l2_vp8_enc_debug, "v4l2vp8enc", 0, "V4L2 VP8 Encoder"); @@ -290,7 +136,11 @@ gst_v4l2_vp8_enc_class_init (GstV4l2Vp8EncClass * klass) GST_DEBUG_FUNCPTR (gst_v4l2_vp8_enc_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_v4l2_vp8_enc_get_property); - baseclass->negotiate = GST_DEBUG_FUNCPTR (gst_v4l2_vp8_enc_negotiate); + + baseclass->codec_name = "VP8"; + baseclass->profile_cid = V4L2_CID_MPEG_VIDEO_VPX_PROFILE; + baseclass->profile_to_string = v4l2_profile_to_string; + baseclass->profile_from_string = v4l2_profile_from_string; } /* Probing functions */ diff --git a/sys/v4l2/gstv4l2vp9enc.c b/sys/v4l2/gstv4l2vp9enc.c index 139525abf6..e938d78d4c 100644 --- a/sys/v4l2/gstv4l2vp9enc.c +++ b/sys/v4l2/gstv4l2vp9enc.c @@ -104,161 +104,6 @@ v4l2_profile_to_string (gint v4l2_profile) return NULL; } -struct ProfileCtx -{ - GstV4l2Vp9Enc *self; - const gchar *profile; -}; - -static gboolean -get_string_list (GstStructure * s, const gchar * field, GQueue * queue) -{ - const GValue *value; - - value = gst_structure_get_value (s, field); - - if (!value) - return FALSE; - - if (GST_VALUE_HOLDS_LIST (value)) { - guint i; - - if (gst_value_list_get_size (value) == 0) - return FALSE; - - for (i = 0; i < gst_value_list_get_size (value); i++) { - const GValue *item = gst_value_list_get_value (value, i); - - if (G_VALUE_HOLDS_STRING (item)) - g_queue_push_tail (queue, g_value_dup_string (item)); - } - } else if (G_VALUE_HOLDS_STRING (value)) { - g_queue_push_tail (queue, g_value_dup_string (value)); - } - - return TRUE; -} - -static gboolean -negotiate_profile (GstCapsFeatures * features, GstStructure * s, - gpointer user_data) -{ - struct ProfileCtx *ctx = user_data; - GstV4l2Object *v4l2object = GST_V4L2_VIDEO_ENC (ctx->self)->v4l2output; - GQueue profiles = G_QUEUE_INIT; - gboolean failed = FALSE; - - if (get_string_list (s, "profile", &profiles)) { - GList *l; - - for (l = profiles.head; l; l = l->next) { - struct v4l2_control control = { 0, }; - gint v4l2_profile; - const gchar *profile = l->data; - - GST_TRACE_OBJECT (ctx->self, "Trying profile %s", profile); - - control.id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE; - control.value = v4l2_profile = v4l2_profile_from_string (profile); - - if (control.value < 0) - continue; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_S_CTRL, &control) < 0) { - GST_WARNING_OBJECT (ctx->self, "Failed to set VP9 profile: '%s'", - g_strerror (errno)); - break; - } - - profile = v4l2_profile_to_string (control.value); - - if (control.value == v4l2_profile) { - ctx->profile = profile; - break; - } - - if (g_list_find_custom (l, profile, g_str_equal)) { - ctx->profile = profile; - break; - } - } - - if (profiles.length && !ctx->profile) - failed = TRUE; - - g_queue_foreach (&profiles, (GFunc) g_free, NULL); - g_queue_clear (&profiles); - } - - /* If it failed, we continue */ - return failed; -} - -static gboolean -gst_v4l2_vp9_enc_negotiate (GstVideoEncoder * encoder) -{ - GstV4l2Vp9Enc *self = GST_V4L2_VP9_ENC (encoder); - GstV4l2VideoEnc *venc = GST_V4L2_VIDEO_ENC (encoder); - GstV4l2Object *v4l2object = venc->v4l2output; - GstCaps *allowed_caps; - struct ProfileCtx ctx = { self, NULL }; - GstVideoCodecState *state; - GstStructure *s; - - GST_DEBUG_OBJECT (self, "Negotiating VP8 profile."); - - allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); - - if (allowed_caps) { - - if (gst_caps_is_empty (allowed_caps)) - goto not_negotiated; - - allowed_caps = gst_caps_make_writable (allowed_caps); - - /* negotiate_profile() will return TRUE on failure to keep - * iterating, if gst_caps_foreach() returns TRUE it means there was no - * compatible profile in any of the structure */ - if (gst_caps_foreach (allowed_caps, negotiate_profile, &ctx)) { - goto no_profile; - } - } - - if (!ctx.profile) { - struct v4l2_control control = { 0, }; - - control.id = V4L2_CID_MPEG_VIDEO_VPX_PROFILE; - - if (v4l2object->ioctl (v4l2object->video_fd, VIDIOC_G_CTRL, &control) < 0) - goto g_ctrl_failed; - - ctx.profile = v4l2_profile_to_string (control.value); - } - - GST_DEBUG_OBJECT (self, "Selected VP9 profile %s", ctx.profile); - - state = gst_video_encoder_get_output_state (encoder); - s = gst_caps_get_structure (state->caps, 0); - gst_structure_set (s, "profile", G_TYPE_STRING, ctx.profile, NULL); - - return GST_VIDEO_ENCODER_CLASS (parent_class)->negotiate (encoder); - -g_ctrl_failed: - GST_WARNING_OBJECT (self, "Failed to get VP9 profile: '%s'", - g_strerror (errno)); - goto not_negotiated; - -no_profile: - GST_WARNING_OBJECT (self, "No compatible profile in caps: %" GST_PTR_FORMAT, - allowed_caps); - goto not_negotiated; - -not_negotiated: - if (allowed_caps) - gst_caps_unref (allowed_caps); - return FALSE; -} - static void gst_v4l2_vp9_enc_init (GstV4l2Vp9Enc * self) { @@ -269,13 +114,13 @@ gst_v4l2_vp9_enc_class_init (GstV4l2Vp9EncClass * klass) { GstElementClass *element_class; GObjectClass *gobject_class; - GstVideoEncoderClass *baseclass; + GstV4l2VideoEncClass *baseclass; parent_class = g_type_class_peek_parent (klass); element_class = (GstElementClass *) klass; gobject_class = (GObjectClass *) klass; - baseclass = GST_VIDEO_ENCODER_CLASS (klass); + baseclass = (GstV4l2VideoEncClass *) (klass); GST_DEBUG_CATEGORY_INIT (gst_v4l2_vp9_enc_debug, "v4l2vp9enc", 0, "V4L2 VP9 Encoder"); @@ -290,7 +135,11 @@ gst_v4l2_vp9_enc_class_init (GstV4l2Vp9EncClass * klass) GST_DEBUG_FUNCPTR (gst_v4l2_vp9_enc_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_v4l2_vp9_enc_get_property); - baseclass->negotiate = GST_DEBUG_FUNCPTR (gst_v4l2_vp9_enc_negotiate); + + baseclass->codec_name = "VP9"; + baseclass->profile_cid = V4L2_CID_MPEG_VIDEO_VPX_PROFILE; + baseclass->profile_to_string = v4l2_profile_to_string; + baseclass->profile_from_string = v4l2_profile_from_string; } /* Probing functions */