From 00b33e2068c2d4d0344452181274c2a08ac1a2be Mon Sep 17 00:00:00 2001 From: Sanjay NM Date: Tue, 22 Jul 2014 12:42:36 +0530 Subject: [PATCH] pnmenc: Port PNM Encoder to use GstVideoEncoder Class https://bugzilla.gnome.org/show_bug.cgi?id=731943 --- gst/pnm/gstpnmenc.c | 325 +++++++++++++++++++++++++++++----------------------- gst/pnm/gstpnmenc.h | 7 +- 2 files changed, 182 insertions(+), 150 deletions(-) diff --git a/gst/pnm/gstpnmenc.c b/gst/pnm/gstpnmenc.c index dc09c6f..1c059c8 100644 --- a/gst/pnm/gstpnmenc.c +++ b/gst/pnm/gstpnmenc.c @@ -32,10 +32,6 @@ * */ -/* - * FIXME: Port to GstVideoEncoder - */ - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -45,6 +41,8 @@ #include #include +#include +#include #include @@ -65,7 +63,13 @@ static GstStaticPadTemplate src_pad_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS (MIME_ALL)); -G_DEFINE_TYPE (GstPnmenc, gst_pnmenc, GST_TYPE_ELEMENT); +G_DEFINE_TYPE (GstPnmenc, gst_pnmenc, GST_TYPE_VIDEO_ENCODER); +#define parent_class gst_pnmenc_parent_class + +static GstFlowReturn +gst_pnmenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame); + +static void gst_pnmenc_finalize (GObject * object); static void gst_pnmenc_set_property (GObject * object, guint prop_id, const GValue * value, @@ -75,10 +79,11 @@ gst_pnmenc_set_property (GObject * object, guint prop_id, const GValue * value, switch (prop_id) { case GST_PNMENC_PROP_ASCII: - if (g_value_get_boolean (value)) + if (g_value_get_boolean (value)) { s->info.encoding = GST_PNM_ENCODING_ASCII; - else + } else { s->info.encoding = GST_PNM_ENCODING_RAW; + } s->info.fields |= GST_PNM_INFO_FIELDS_ENCODING; break; default: @@ -103,161 +108,181 @@ gst_pnmenc_get_property (GObject * object, guint prop_id, GValue * value, } } -static GstFlowReturn -gst_pnmenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +static void +gst_pnmenc_init (GstPnmenc * s) { - GstPnmenc *s = GST_PNMENC (parent); - GstFlowReturn r; - gchar *header; - GstBuffer *out; - - if (s->info.width == 0 || s->info.height == 0 || s->info.fields == 0) - goto not_negotiated; - - /* Assumption: One buffer, one image. That is, always first write header. */ - header = g_strdup_printf ("P%i\n%i %i\n%i\n", - s->info.type + 3 * (1 - s->info.encoding), s->info.width, s->info.height, - s->info.max); - out = gst_buffer_new_wrapped (header, strlen (header)); - if ((r = gst_pad_push (s->src, out)) != GST_FLOW_OK) - goto out; - - /* Need to convert from GStreamer rowstride to PNM rowstride */ - if (s->info.width % 4 != 0) { - guint i_rowstride; - guint o_rowstride; - GstBuffer *obuf; - guint i; - GstMapInfo imap, omap; - - if (s->info.type == GST_PNM_TYPE_PIXMAP) { - o_rowstride = 3 * s->info.width; - i_rowstride = GST_ROUND_UP_4 (o_rowstride); - } else { - o_rowstride = s->info.width; - i_rowstride = GST_ROUND_UP_4 (o_rowstride); - } - - obuf = gst_buffer_new_and_alloc (o_rowstride * s->info.height); - gst_buffer_map (obuf, &omap, GST_MAP_WRITE); - gst_buffer_map (buf, &imap, GST_MAP_READ); - for (i = 0; i < s->info.height; i++) - memcpy (omap.data + o_rowstride * i, imap.data + i_rowstride * i, - o_rowstride); - gst_buffer_unmap (buf, &imap); - gst_buffer_unmap (obuf, &omap); - gst_buffer_unref (buf); - buf = obuf; - } else { - /* Pass through the data. */ - buf = gst_buffer_make_writable (buf); - } - - /* We might need to convert to ASCII... */ - if (s->info.encoding == GST_PNM_ENCODING_ASCII) { - GstBuffer *obuf; - guint i, o; - GstMapInfo imap, omap; - - gst_buffer_map (buf, &imap, GST_MAP_READ); - obuf = gst_buffer_new_and_alloc (imap.size * (4 + 1 / 20.)); - gst_buffer_map (obuf, &omap, GST_MAP_WRITE); - for (i = o = 0; i < imap.size; i++) { - g_snprintf ((char *) omap.data + o, 4, "%3i", imap.data[i]); - o += 3; - omap.data[o++] = ' '; - if (!((i + 1) % 20)) - omap.data[o++] = '\n'; - } - gst_buffer_unmap (buf, &imap); - gst_buffer_unmap (obuf, &omap); - gst_buffer_unref (buf); - buf = obuf; - } - - r = gst_pad_push (s->src, buf); - -out: + /* Set default encoding as RAW as ASCII takes up 4 time more bytes */ + s->info.encoding = GST_PNM_ENCODING_RAW; +} - return r; +static void +gst_pnmenc_finalize (GObject * object) +{ + GstPnmenc *pnmenc = GST_PNMENC (object); + if (pnmenc->input_state) + gst_video_codec_state_unref (pnmenc->input_state); -not_negotiated: - { - gst_buffer_unref (buf); - return GST_FLOW_NOT_NEGOTIATED; - } + G_OBJECT_CLASS (parent_class)->finalize (object); } static gboolean -gst_pnmenc_setcaps (GstPnmenc * s, GstCaps * caps) +gst_pnmenc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state) { - gboolean r; - GstCaps *srccaps; - - s->info.max = 255; - s->info.fields = GST_PNM_INFO_FIELDS_MAX; + GstPnmenc *pnmenc; + gboolean ret = TRUE; + GstVideoInfo *info; + GstVideoCodecState *output_state; - if (!gst_video_info_from_caps (&s->vinfo, caps)) - return FALSE; + pnmenc = GST_PNMENC (encoder); + info = &state->info; - if (GST_VIDEO_INFO_IS_RGB (&s->vinfo)) { - s->info.type = GST_PNM_TYPE_PIXMAP; - srccaps = gst_caps_from_string (MIME_PM); - } else if (GST_VIDEO_INFO_IS_GRAY (&s->vinfo)) { - s->info.type = GST_PNM_TYPE_GRAYMAP; - srccaps = gst_caps_from_string (MIME_GM); - } else { - return FALSE; + switch (GST_VIDEO_INFO_FORMAT (info)) { + case GST_VIDEO_FORMAT_RGB: + pnmenc->info.type = GST_PNM_TYPE_PIXMAP; + break; + case GST_VIDEO_FORMAT_GRAY8: + pnmenc->info.type = GST_PNM_TYPE_GRAYMAP; + break; + default: + ret = FALSE; + goto done; } - r = gst_pad_set_caps (s->src, srccaps); - gst_caps_unref (srccaps); - s->info.fields |= GST_PNM_INFO_FIELDS_TYPE; - /* Remember width and height of the input data. */ - s->info.width = GST_VIDEO_INFO_WIDTH (&s->vinfo); - s->info.height = GST_VIDEO_INFO_HEIGHT (&s->vinfo); - s->info.fields |= GST_PNM_INFO_FIELDS_WIDTH | GST_PNM_INFO_FIELDS_HEIGHT; + pnmenc->info.width = GST_VIDEO_INFO_WIDTH (info); + pnmenc->info.height = GST_VIDEO_INFO_HEIGHT (info); + /* Supported max value is only one, that is 255 */ + pnmenc->info.max = 255; - return r; -} + if (pnmenc->input_state) + gst_video_codec_state_unref (pnmenc->input_state); + pnmenc->input_state = gst_video_codec_state_ref (state); -static gboolean -gst_pnmenc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) -{ - GstPnmenc *s = GST_PNMENC (parent); - gboolean r = FALSE; + output_state = + gst_video_encoder_set_output_state (encoder, + gst_caps_new_empty_simple ("image/pnm"), state); + gst_video_codec_state_unref (output_state); - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_CAPS: - { - GstCaps *caps; +done: + return ret; +} - gst_event_parse_caps (event, &caps); - r = gst_pnmenc_setcaps (s, caps); - gst_event_unref (event); +static GstFlowReturn +gst_pnmenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame) +{ + GstPnmenc *pnmenc; + guint size, pixels; + GstMapInfo omap, imap; + gchar *header; + GstVideoInfo *info; + GstFlowReturn ret = GST_FLOW_OK; + guint i_rowstride, o_rowstride; + guint bytes = 0, index, head_size; + guint i, j; + + pnmenc = GST_PNMENC (encoder); + info = &pnmenc->input_state->info; + + switch (GST_VIDEO_INFO_FORMAT (info)) { + case GST_VIDEO_FORMAT_RGB: + pixels = size = pnmenc->info.width * pnmenc->info.height * 3; break; - } - default: - r = gst_pad_event_default (pad, parent, event); + case GST_VIDEO_FORMAT_GRAY8: + pixels = size = pnmenc->info.width * pnmenc->info.height * 1; break; + default: + ret = FALSE; + goto done; } - return r; -} + header = g_strdup_printf ("P%i\n%i %i\n%i\n", + pnmenc->info.type + 3 * (1 - pnmenc->info.encoding), pnmenc->info.width, + pnmenc->info.height, pnmenc->info.max); + + if (pnmenc->info.encoding == GST_PNM_ENCODING_ASCII) { + /* Per component 4 bytes are used in case of ASCII encoding */ + size = size * 4; + size += strlen (header); + frame->output_buffer = + gst_video_encoder_allocate_output_buffer (encoder, (size + size / 20)); + } else { + size += strlen (header); + frame->output_buffer = + gst_video_encoder_allocate_output_buffer (encoder, size); + } -static void -gst_pnmenc_init (GstPnmenc * s) -{ - GstPad *pad; + if (gst_buffer_map (frame->output_buffer, &omap, GST_MAP_WRITE) == FALSE) { + ret = GST_FLOW_ERROR; + goto done; + } + if (gst_buffer_map (frame->input_buffer, &imap, GST_MAP_READ) == FALSE) { + /* Unmap already mapped buffer */ + gst_buffer_unmap (frame->output_buffer, &omap); + ret = GST_FLOW_ERROR; + goto done; + } + memcpy (omap.data, header, strlen (header)); + + head_size = strlen (header); + if (pnmenc->info.encoding == GST_PNM_ENCODING_ASCII) { + /* We need to convert to ASCII */ + if (pnmenc->info.width % 4 != 0) { + /* Convert from gstreamer rowstride to PNM rowstride */ + if (pnmenc->info.type == GST_PNM_TYPE_PIXMAP) { + o_rowstride = 3 * pnmenc->info.width; + } else { + o_rowstride = pnmenc->info.width; + } + i_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (pnmenc->input_state, 0); + + for (i = 0; i < pnmenc->info.height; i++) { + index = i * i_rowstride; + for (j = 0; j < o_rowstride; j++, bytes++, index++) { + g_snprintf ((char *) omap.data + head_size, 4, "%3i", + imap.data[index]); + head_size += 3; + omap.data[head_size++] = ' '; + /* Add new line so that file will not end up with sinle big line */ + if (!((bytes + 1) % 20)) + omap.data[head_size++] = '\n'; + } + } + } else { + for (i = 0; i < pixels; i++) { + g_snprintf ((char *) omap.data + head_size, 4, "%3i", imap.data[i]); + head_size += 3; + omap.data[head_size++] = ' '; + if (!((i + 1) % 20)) + omap.data[head_size++] = '\n'; + } + } + } else { + /* Need to convert from GStreamer rowstride to PNM rowstride */ + if (pnmenc->info.width % 4 != 0) { + if (pnmenc->info.type == GST_PNM_TYPE_PIXMAP) { + o_rowstride = 3 * pnmenc->info.width; + } else { + o_rowstride = pnmenc->info.width; + } + i_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (pnmenc->input_state, 0); + + for (i = 0; i < pnmenc->info.height; i++) + memcpy (omap.data + head_size + o_rowstride * i, + imap.data + i_rowstride * i, o_rowstride); + } else { + /* size contains complete image size inlcuding header size, + Exclude header size while copying data */ + memcpy (omap.data + strlen (header), imap.data, (size - head_size)); + } + } + + gst_buffer_unmap (frame->output_buffer, &omap); + gst_buffer_unmap (frame->input_buffer, &imap); - pad = gst_pad_new_from_static_template (&sink_pad_template, "sink"); - gst_pad_set_chain_function (pad, gst_pnmenc_chain); - gst_pad_set_event_function (pad, gst_pnmenc_sink_event); - gst_pad_use_fixed_caps (pad); - gst_element_add_pad (GST_ELEMENT (s), pad); + if ((ret = gst_video_encoder_finish_frame (encoder, frame)) != GST_FLOW_OK) + goto done; - s->src = gst_pad_new_from_static_template (&src_pad_template, "src"); - gst_element_add_pad (GST_ELEMENT (s), s->src); +done: + return ret; } static void @@ -265,20 +290,28 @@ gst_pnmenc_class_init (GstPnmencClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstVideoEncoderClass *venc_class = (GstVideoEncoderClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->set_property = gst_pnmenc_set_property; + gobject_class->get_property = gst_pnmenc_get_property; + + g_object_class_install_property (gobject_class, GST_PNMENC_PROP_ASCII, + g_param_spec_boolean ("ascii", "ASCII Encoding", "The output will be " + "ASCII encoded", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_pad_template)); + gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&src_pad_template)); + gst_element_class_set_static_metadata (element_class, "PNM image encoder", "Codec/Encoder/Image", "Encodes images into portable pixmap or graymap (PNM) format", "Lutz Mueller "); - gobject_class->set_property = gst_pnmenc_set_property; - gobject_class->get_property = gst_pnmenc_get_property; - - g_object_class_install_property (gobject_class, GST_PNMENC_PROP_ASCII, - g_param_spec_boolean ("ascii", "ASCII Encoding", "The output will be " - "ASCII encoded", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + venc_class->set_format = gst_pnmenc_set_format; + venc_class->handle_frame = gst_pnmenc_handle_frame; + gobject_class->finalize = gst_pnmenc_finalize; } diff --git a/gst/pnm/gstpnmenc.h b/gst/pnm/gstpnmenc.h index 625379b..cb7f45b 100644 --- a/gst/pnm/gstpnmenc.h +++ b/gst/pnm/gstpnmenc.h @@ -38,9 +38,8 @@ typedef struct _GstPnmencClass GstPnmencClass; struct _GstPnmenc { - GstElement element; - - GstVideoInfo vinfo; + GstVideoEncoder parent; + GstVideoCodecState *input_state; GstPnmInfo info; GstPad *src; @@ -48,7 +47,7 @@ struct _GstPnmenc struct _GstPnmencClass { - GstElementClass parent_class; + GstVideoEncoderClass parent; }; GType gst_pnmenc_get_type (void) G_GNUC_CONST; -- 2.7.4