This removes duplicated code across different codec.
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
{
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");
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 */
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)
{
{
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");
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 */
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;
}
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 *
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
}
+
static void
gst_v4l2_video_enc_dispose (GObject * object)
{
G_OBJECT_CLASS (parent_class)->finalize (object);
}
+
static void
gst_v4l2_video_enc_init (GstV4l2VideoEnc * self)
{
(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;
GstVideoEncoderClass parent_class;
gchar *default_device;
+ const char *codec_name;
+
+ guint32 profile_cid;
+ const gchar * (*profile_to_string) (gint v4l2_profile);
+ gint (*profile_from_string) (const gchar * profile);
- GstFlowReturn (*get_output_caps) (GstVideoEncoder * encoder,
- GstCaps ** outcaps);
+ 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);
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)
{
{
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");
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 */
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)
{
{
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");
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 */