fix build failure
[profile/ivi/gst-plugins-bad0.10.git] / gst / pnm / gstpnmdec.c
1 /* GStreamer PNM decoder
2  * Copyright (C) 2009 Lutz Mueller <lutz@users.sourceforge.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-pnmdec
22  *
23  * Decodes pnm images.
24  *
25  * <refsect2>
26  * <title>Example launch line</title>
27  * |[
28  * gst-launch filesrc location=test.pnm ! pnmdec ! ffmpegcolorspace ! autovideosink
29  * ]| The above pipeline reads a pnm file and renders it to the screen.
30  * </refsect2>
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "gstpnmdec.h"
38 #include "gstpnmutils.h"
39
40 #include <gst/gstutils.h>
41 #include <gst/video/video.h>
42
43 #include <string.h>
44
45 static GstStaticPadTemplate gst_pnmdec_src_pad_template =
46     GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
47     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB "; "
48         "video/x-raw-gray, width =" GST_VIDEO_SIZE_RANGE ", "
49         "height =" GST_VIDEO_SIZE_RANGE ", framerate =" GST_VIDEO_FPS_RANGE ", "
50         "bpp= (int) 8, depth= (int) 8"));
51
52 static GstStaticPadTemplate gst_pnmdec_sink_pad_template =
53 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
54     GST_STATIC_CAPS (MIME_ALL));
55
56 GST_BOILERPLATE (GstPnmdec, gst_pnmdec, GstElement, GST_TYPE_ELEMENT);
57
58 static GstFlowReturn
59 gst_pnmdec_push (GstPnmdec * s, GstPad * src, GstBuffer * buf)
60 {
61   /* Need to convert from PNM rowstride to GStreamer rowstride */
62   if (s->mngr.info.width % 4 != 0) {
63     guint i_rowstride;
64     guint o_rowstride;
65     GstBuffer *obuf;
66     guint i;
67
68     if (s->mngr.info.type == GST_PNM_TYPE_PIXMAP) {
69       i_rowstride = 3 * s->mngr.info.width;
70       o_rowstride = GST_ROUND_UP_4 (i_rowstride);
71     } else {
72       i_rowstride = s->mngr.info.width;
73       o_rowstride = GST_ROUND_UP_4 (i_rowstride);
74     }
75
76     obuf = gst_buffer_new_and_alloc (o_rowstride * s->mngr.info.height);
77
78     gst_buffer_copy_metadata (obuf, buf, GST_BUFFER_COPY_ALL);
79
80     for (i = 0; i < s->mngr.info.height; i++)
81       memcpy (GST_BUFFER_DATA (obuf) + i * o_rowstride,
82           GST_BUFFER_DATA (buf) + i * i_rowstride, i_rowstride);
83
84     gst_buffer_unref (buf);
85     return gst_pad_push (src, obuf);
86   } else {
87     return gst_pad_push (src, buf);
88   }
89 }
90
91 static GstFlowReturn
92 gst_pnmdec_chain_raw (GstPnmdec * s, GstPad * src, GstBuffer * buf)
93 {
94   GstFlowReturn r = GST_FLOW_OK;
95   GstBuffer *out;
96
97   /* If we got the whole image, just push the buffer. */
98   if (GST_BUFFER_SIZE (buf) == s->size) {
99     memset (&s->mngr, 0, sizeof (GstPnmInfoMngr));
100     s->size = 0;
101     gst_buffer_set_caps (buf, GST_PAD_CAPS (src));
102     return gst_pnmdec_push (s, src, buf);
103   }
104
105   /* We didn't get the whole image. */
106   if (!s->buf) {
107     s->buf = buf;
108   } else {
109     out = gst_buffer_span (s->buf, 0, buf,
110         GST_BUFFER_SIZE (s->buf) + GST_BUFFER_SIZE (buf));
111     gst_buffer_unref (buf);
112     gst_buffer_unref (s->buf);
113     s->buf = out;
114   }
115   if (!s->buf)
116     return GST_FLOW_ERROR;
117
118   /* Do we now have the full image? If yes, push. */
119   if (GST_BUFFER_SIZE (s->buf) == s->size) {
120     gst_buffer_set_caps (s->buf, GST_PAD_CAPS (src));
121     r = gst_pnmdec_push (s, src, s->buf);
122     s->buf = NULL;
123     memset (&s->mngr, 0, sizeof (GstPnmInfoMngr));
124     s->size = 0;
125   }
126
127   return r;
128 }
129
130 static GstFlowReturn
131 gst_pnmdec_chain_ascii (GstPnmdec * s, GstPad * src, GstBuffer * buf)
132 {
133   GScanner *scanner;
134   GstBuffer *out;
135   guint i = 0;
136   gchar *b = (gchar *) GST_BUFFER_DATA (buf);
137   guint bs = GST_BUFFER_SIZE (buf);
138   guint target = s->size - (s->buf ? GST_BUFFER_SIZE (s->buf) : 0);
139
140   if (!bs) {
141     gst_buffer_unref (buf);
142     return GST_FLOW_OK;
143   }
144
145   if (s->last_byte) {
146     while (*b >= '0' && *b <= '9') {
147       s->last_byte = 10 * s->last_byte + *b - '0';
148       b++;
149       if (!--bs) {
150         gst_buffer_unref (buf);
151         return GST_FLOW_OK;
152       }
153     }
154     if (s->last_byte > 255) {
155       gst_buffer_unref (buf);
156       GST_DEBUG_OBJECT (s, "Corrupt ASCII encoded PNM file.");
157       return GST_FLOW_ERROR;
158     }
159   }
160
161   out = gst_buffer_new_and_alloc (target);
162
163   if (s->last_byte) {
164     GST_BUFFER_DATA (out)[i++] = s->last_byte;
165     s->last_byte = 0;
166   }
167
168   scanner = g_scanner_new (NULL);
169   g_scanner_input_text (scanner, b, bs);
170   while (!g_scanner_eof (scanner)) {
171     switch (g_scanner_get_next_token (scanner)) {
172       case G_TOKEN_INT:
173         if (i == target) {
174           GST_DEBUG_OBJECT (s, "PNM file contains too much data.");
175           gst_buffer_unref (buf);
176           gst_buffer_unref (out);
177           return GST_FLOW_ERROR;
178         }
179         GST_BUFFER_DATA (out)[i++] = scanner->value.v_int;
180         break;
181       default:
182         /* Should we care? */ ;
183     }
184   }
185   g_scanner_destroy (scanner);
186
187   /* If we didn't get the whole image, handle the last byte with care. */
188   if (i && i < target && b[bs - 1] > '0' && b[bs - 1] <= '9')
189     s->last_byte = GST_BUFFER_DATA (out)[--i];
190
191   gst_buffer_unref (buf);
192   if (!i) {
193     gst_buffer_unref (out);
194     return GST_FLOW_OK;
195   }
196
197   GST_BUFFER_SIZE (out) = i;
198   return gst_pnmdec_chain_raw (s, src, out);
199 }
200
201 static GstFlowReturn
202 gst_pnmdec_chain (GstPad * pad, GstBuffer * data)
203 {
204   GstPnmdec *s = GST_PNMDEC (gst_pad_get_parent (pad));
205   GstPad *src = gst_element_get_static_pad (GST_ELEMENT (s), "src");
206   GstCaps *caps = NULL;
207   GstFlowReturn r = GST_FLOW_OK;
208   guint offset = 0;
209
210   if (s->mngr.info.fields != GST_PNM_INFO_FIELDS_ALL) {
211     switch (gst_pnm_info_mngr_scan (&s->mngr, GST_BUFFER_DATA (data),
212             GST_BUFFER_SIZE (data))) {
213       case GST_PNM_INFO_MNGR_RESULT_FAILED:
214         gst_buffer_unref (data);
215         r = GST_FLOW_ERROR;
216         goto out;
217       case GST_PNM_INFO_MNGR_RESULT_READING:
218         gst_buffer_unref (data);
219         r = GST_FLOW_OK;
220         goto out;
221       case GST_PNM_INFO_MNGR_RESULT_FINISHED:
222         offset = s->mngr.data_offset;
223         caps = gst_caps_copy (gst_pad_get_pad_template_caps (src));
224         switch (s->mngr.info.type) {
225           case GST_PNM_TYPE_BITMAP:
226             GST_DEBUG_OBJECT (s, "FIXME: BITMAP format not implemented!");
227             gst_caps_unref (caps);
228             gst_buffer_unref (data);
229             r = GST_FLOW_ERROR;
230             goto out;
231           case GST_PNM_TYPE_GRAYMAP:
232             gst_caps_remove_structure (caps, 0);
233             s->size = s->mngr.info.width * s->mngr.info.height * 1;
234             break;
235           case GST_PNM_TYPE_PIXMAP:
236             gst_caps_remove_structure (caps, 1);
237             s->size = s->mngr.info.width * s->mngr.info.height * 3;
238             break;
239         }
240         gst_caps_set_simple (caps,
241             "width", G_TYPE_INT, s->mngr.info.width,
242             "height", G_TYPE_INT, s->mngr.info.height, "framerate",
243             GST_TYPE_FRACTION, 0, 1, NULL);
244         if (!gst_pad_set_caps (src, caps)) {
245           gst_caps_unref (caps);
246           gst_buffer_unref (data);
247           r = GST_FLOW_ERROR;
248           goto out;
249         }
250         gst_caps_unref (caps);
251     }
252   }
253
254   if (offset == GST_BUFFER_SIZE (data)) {
255     gst_buffer_unref (data);
256     r = GST_FLOW_OK;
257     goto out;
258   }
259
260   if (offset) {
261     GstBuffer *buf = gst_buffer_create_sub (data, offset,
262         GST_BUFFER_SIZE (data) - offset);
263     gst_buffer_unref (data);
264     data = buf;
265   }
266
267   if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII)
268     r = gst_pnmdec_chain_ascii (s, src, data);
269   else
270     r = gst_pnmdec_chain_raw (s, src, data);
271
272 out:
273   gst_object_unref (src);
274   gst_object_unref (s);
275
276   return r;
277 }
278
279 static void
280 gst_pnmdec_finalize (GObject * object)
281 {
282   GstPnmdec *dec = GST_PNMDEC (object);
283
284   if (dec->buf) {
285     gst_buffer_unref (dec->buf);
286     dec->buf = NULL;
287   }
288
289   G_OBJECT_CLASS (parent_class)->finalize (object);
290 }
291
292 static void
293 gst_pnmdec_init (GstPnmdec * s, GstPnmdecClass * klass)
294 {
295   GstPad *pad;
296
297   pad =
298       gst_pad_new_from_static_template (&gst_pnmdec_sink_pad_template, "sink");
299   gst_pad_set_chain_function (pad, gst_pnmdec_chain);
300   gst_element_add_pad (GST_ELEMENT (s), pad);
301
302   pad = gst_pad_new_from_static_template (&gst_pnmdec_src_pad_template, "src");
303   gst_element_add_pad (GST_ELEMENT (s), pad);
304 }
305
306 static void
307 gst_pnmdec_base_init (gpointer g_class)
308 {
309   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
310
311   gst_element_class_add_static_pad_template (element_class,
312       &gst_pnmdec_sink_pad_template);
313   gst_element_class_add_static_pad_template (element_class,
314       &gst_pnmdec_src_pad_template);
315   gst_element_class_set_details_simple (element_class, "PNM image decoder",
316       "Codec/Decoder/Image",
317       "Decodes images in portable pixmap/graymap/bitmap/anymamp (PNM) format",
318       "Lutz Mueller <lutz@users.sourceforge.net>");
319 }
320
321 static void
322 gst_pnmdec_class_init (GstPnmdecClass * klass)
323 {
324   GObjectClass *gobject_class = (GObjectClass *) klass;
325
326   parent_class = g_type_class_peek_parent (klass);
327
328   gobject_class->finalize = gst_pnmdec_finalize;
329 }