pnm: Use correct rowstride for 8 bit grayscale too
[platform/upstream/gstreamer.git] / gst / pnm / gstpnmenc.c
1 /* GStreamer
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Library General Public
5  * License as published by the Free Software Foundation; either
6  * version 2 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Library General Public License for more details.
12  *
13  * You should have received a copy of the GNU Library General Public
14  * License along with this library; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 02111-1307, USA.
17  */
18
19 /**
20  * SECTION:element-pnmenc
21  *
22  * Encodes pnm images.
23  *
24  * <refsect">
25  * <title>Example launch line</title>
26  * |[
27  * gst-launch videotestsrc num_buffers=1 ! pnmenc ! ffmpegcolorspace ! "video/x-raw-gray" ! pnmenc ! filesink location=test.pnm
28  * ]| The above pipeline writes a test pnm file.
29  * </refsect2>
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #include "gstpnmenc.h"
37 #include "gstpnmutils.h"
38
39 #include <gst/gstutils.h>
40 #include <gst/video/video.h>
41
42 #include <string.h>
43
44 static GstElementDetails pnmenc_details =
45 GST_ELEMENT_DETAILS ("PNM converter", "Codec/Encoder/Image",
46     "Encodes in PNM format",
47     "Lutz Mueller <lutz@users.sourceforge.net>");
48
49 static GstStaticPadTemplate sink_pad_template =
50     GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
51     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB "; "
52         "video/x-raw-gray, width =" GST_VIDEO_SIZE_RANGE ", "
53         "height =" GST_VIDEO_SIZE_RANGE ", framerate =" GST_VIDEO_FPS_RANGE ", "
54         "bpp= (int) 8, depth= (int) 8"));
55
56 static GstStaticPadTemplate src_pad_template =
57 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
58     GST_STATIC_CAPS (MIME_ALL));
59
60 static GstFlowReturn
61 gst_pnmenc_chain (GstPad * pad, GstBuffer * buf)
62 {
63   GstPnmenc *s = GST_PNMENC (gst_pad_get_parent (pad));
64   GstFlowReturn r;
65   gchar *header;
66   GstBuffer *out;
67
68   if (s->info.fields != GST_PNM_INFO_FIELDS_ALL) {
69     r = GST_FLOW_NOT_NEGOTIATED;
70     goto out;
71   }
72
73   /* Assumption: One buffer, one image. That is, always first write header. */
74   header = g_strdup_printf ("P%i\n%i %i\n%i\n",
75       s->info.type, s->info.width, s->info.height, s->info.max);
76   out = gst_buffer_new ();
77   gst_buffer_set_data (out, (guchar *) header, strlen (header));
78   gst_buffer_set_caps (out, GST_PAD_CAPS (s->src));
79   if ((r = gst_pad_push (s->src, out)) != GST_FLOW_OK)
80     goto out;
81
82   /* Need to convert from GStreamer rowstride to PNM rowstride */
83   if (s->info.width % 4 != 0) {
84     guint i_rowstride;
85     guint o_rowstride;
86     GstBuffer *obuf;
87     guint i;
88
89     if (s->info.type == GST_PNM_TYPE_PIXMAP_RAW) {
90       o_rowstride = 3 * s->info.width;
91       i_rowstride = GST_ROUND_UP_4 (o_rowstride);
92     } else {
93       o_rowstride = s->info.width;
94       i_rowstride = GST_ROUND_UP_4 (o_rowstride);
95     }
96
97     obuf = gst_buffer_new_and_alloc (o_rowstride * s->info.height);
98     for (i = 0; i < s->info.height; i++)
99       memcpy (GST_BUFFER_DATA (obuf) + o_rowstride * i,
100           GST_BUFFER_DATA (buf) + i_rowstride * i, o_rowstride);
101     gst_buffer_unref (buf);
102     buf = obuf;
103   } else {
104     /* Pass through the data. */
105     buf = gst_buffer_make_metadata_writable (buf);
106   }
107   gst_buffer_set_caps (buf, GST_PAD_CAPS (s->src));
108   r = gst_pad_push (s->src, buf);
109
110 out:
111   gst_object_unref (s);
112
113   return r;
114 }
115
116 static gboolean
117 gst_pnmenc_setcaps_func_sink (GstPad * pad, GstCaps * caps)
118 {
119   GstPnmenc *s = GST_PNMENC (gst_pad_get_parent (pad));
120   GstStructure *structure = gst_caps_get_structure (caps, 0);
121   const gchar *mime = gst_structure_get_name (structure);
122   gboolean r = TRUE;
123   GstCaps *srccaps;
124
125   s->info.max = 255;
126   s->info.fields = GST_PNM_INFO_FIELDS_MAX;
127
128   /* Set caps on the source. */
129   if (!strcmp (mime, "video/x-raw-rgb")) {
130     s->info.type = GST_PNM_TYPE_PIXMAP_RAW;
131     srccaps = gst_caps_from_string (MIME_PM);
132   } else if (!strcmp (mime, "video/x-raw-gray")) {
133     s->info.type = GST_PNM_TYPE_GRAYMAP_RAW;
134     srccaps = gst_caps_from_string (MIME_GM);
135   } else {
136     r = FALSE;
137     goto out;
138   }
139   gst_pad_set_caps (s->src, srccaps);
140   gst_caps_unref (srccaps);
141   s->info.fields |= GST_PNM_INFO_FIELDS_TYPE;
142
143   /* Remember width and height of the input data. */
144   if (!gst_structure_get_int (structure, "width", (int *) &s->info.width) ||
145       !gst_structure_get_int (structure, "height", (int *) &s->info.height)) {
146     r = FALSE;
147     goto out;
148   }
149   s->info.fields |= GST_PNM_INFO_FIELDS_WIDTH | GST_PNM_INFO_FIELDS_HEIGHT;
150
151 out:
152   gst_object_unref (s);
153   return r;
154 }
155
156 static void
157 gst_pnmenc_init (GstPnmenc * s, GstPnmencClass * klass)
158 {
159   GstPad *pad;
160
161   pad =
162       gst_pad_new_from_template (gst_static_pad_template_get
163       (&sink_pad_template), "sink");
164   gst_pad_set_setcaps_function (pad, gst_pnmenc_setcaps_func_sink);
165   gst_pad_set_chain_function (pad, gst_pnmenc_chain);
166   gst_pad_use_fixed_caps (pad);
167   gst_element_add_pad (GST_ELEMENT (s), pad);
168
169   s->src =
170       gst_pad_new_from_template (gst_static_pad_template_get
171       (&src_pad_template), "src");
172   gst_element_add_pad (GST_ELEMENT (s), s->src);
173 }
174
175 static void
176 gst_pnmenc_base_init (gpointer g_class)
177 {
178   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
179
180   gst_element_class_add_pad_template (element_class,
181       gst_static_pad_template_get (&sink_pad_template));
182   gst_element_class_add_pad_template (element_class,
183       gst_static_pad_template_get (&src_pad_template));
184   gst_element_class_set_details (element_class, &pnmenc_details);
185 }
186
187 static void
188 gst_pnmenc_class_init (GstPnmencClass * klass)
189 {
190   /* Nothing to see here. Move along. */
191 }
192
193 GST_BOILERPLATE (GstPnmenc, gst_pnmenc, GstElement, GST_TYPE_ELEMENT)