5b624bed4591865dc2a7f27580584f476fb1f006
[platform/upstream/gst-plugins-bad.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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * SECTION:element-pnmdec
22  * @title: pnmdec
23  *
24  * Decodes pnm images.
25  *
26  * ## Example launch line
27  * |[
28  * gst-launch-1.0 filesrc location=test.pnm ! pnmdec ! videoconvert ! autovideosink
29  * ]| The above pipeline reads a pnm file and renders it to the screen.
30  *
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 #include <gst/base/gstbytereader.h>
43 #include <string.h>
44 #include <stdio.h>
45
46 static gboolean gst_pnmdec_start (GstVideoDecoder * decoder);
47 static GstFlowReturn gst_pnmdec_finish (GstVideoDecoder * decoder);
48 static gboolean gst_pnmdec_set_format (GstVideoDecoder * decoder,
49     GstVideoCodecState * state);
50 static gboolean gst_pnmdec_stop (GstVideoDecoder * decoder);
51 static GstFlowReturn gst_pnmdec_parse (GstVideoDecoder * decoder,
52     GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos);
53 static GstFlowReturn gst_pnmdec_handle_frame (GstVideoDecoder * decoder,
54     GstVideoCodecFrame * frame);
55 static GstFlowReturn
56 gst_pnmdec_parse_ascii (GstPnmdec * s, const guint8 * b, guint bs);
57
58 G_DEFINE_TYPE (GstPnmdec, gst_pnmdec, GST_TYPE_VIDEO_DECODER);
59
60 static GstStaticPadTemplate gst_pnmdec_src_pad_template =
61 GST_STATIC_PAD_TEMPLATE ("src",
62     GST_PAD_SRC,
63     GST_PAD_ALWAYS,
64     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
65         ("{ RGB, GRAY8, GRAY16_BE, GRAY16_LE }")));
66
67 static GstStaticCaps gst_pnmdec_gray16_caps =
68 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ GRAY16_BE, GRAY16_LE }"));
69
70 static GstStaticPadTemplate gst_pnmdec_sink_pad_template =
71 GST_STATIC_PAD_TEMPLATE ("sink",
72     GST_PAD_SINK,
73     GST_PAD_ALWAYS,
74     GST_STATIC_CAPS (MIME_ALL));
75
76 GST_DEBUG_CATEGORY (pnmdecoder_debug);
77 #define GST_CAT_DEFAULT pnmdecoder_debug
78
79 static void
80 gst_pnmdec_class_init (GstPnmdecClass * klass)
81 {
82   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
83   GstVideoDecoderClass *vdec_class = (GstVideoDecoderClass *) klass;
84
85   GST_DEBUG_CATEGORY_INIT (pnmdecoder_debug, "pnmdec", 0, "PNM Video Decoder");
86
87   gst_element_class_add_static_pad_template (element_class,
88       &gst_pnmdec_src_pad_template);
89   gst_element_class_add_static_pad_template (element_class,
90       &gst_pnmdec_sink_pad_template);
91
92   gst_element_class_set_static_metadata (element_class, "PNM image decoder",
93       "Codec/Decoder/Image",
94       "Decodes images in portable pixmap/graymap/bitmap/anymamp (PNM) format",
95       "Lutz Mueller <lutz@users.sourceforge.net>");
96
97   vdec_class->start = gst_pnmdec_start;
98   vdec_class->finish = gst_pnmdec_finish;
99   vdec_class->stop = gst_pnmdec_stop;
100   vdec_class->parse = gst_pnmdec_parse;
101   vdec_class->handle_frame = gst_pnmdec_handle_frame;
102   vdec_class->set_format = gst_pnmdec_set_format;
103 }
104
105 static void
106 gst_pnmdec_flush (GstPnmdec * s)
107 {
108   memset (&s->mngr, 0, sizeof (s->mngr));
109   s->size = 0;
110   s->current_size = 0;
111   if (s->buf) {
112     gst_buffer_unref (s->buf);
113     s->buf = NULL;
114   }
115 }
116
117 static void
118 gst_pnmdec_init (GstPnmdec * s)
119 {
120   /* Initialize decoder */
121   s->buf = NULL;
122   gst_pnmdec_flush (s);
123
124   gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST
125       (s), TRUE);
126   GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (s));
127 }
128
129 static GstFlowReturn
130 gst_pnmdec_negotiate (GstVideoDecoder * decoder)
131 {
132   GstPnmdec *pnmdec = (GstPnmdec *) decoder;
133   GstVideoFormat fmt = GST_VIDEO_FORMAT_UNKNOWN;
134   GstVideoCodecState *output_state;
135
136   switch (pnmdec->mngr.info.type) {
137     case GST_PNM_TYPE_BITMAP:
138       if (pnmdec->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
139         return GST_FLOW_ERROR;
140       }
141       pnmdec->size = pnmdec->mngr.info.width * pnmdec->mngr.info.height * 1;
142       fmt = GST_VIDEO_FORMAT_GRAY8;
143       break;
144     case GST_PNM_TYPE_GRAYMAP:
145       if (pnmdec->mngr.info.max > 255) {
146         GstCaps *gray16_caps = gst_static_caps_get (&gst_pnmdec_gray16_caps);
147         GstCaps *peercaps;
148         GstStructure *peerstruct;
149         const gchar *fmtstr;
150
151         pnmdec->size = pnmdec->mngr.info.width * pnmdec->mngr.info.height * 2;
152         /* perform some basic negotiation to resolve which endianess,
153          * if any, is supported by the component downstream. Query
154          * the peer caps, intersecting with our preferred caps
155          */
156         peercaps =
157             gst_pad_peer_query_caps (GST_VIDEO_DECODER_SRC_PAD (decoder),
158             gray16_caps);
159         gst_caps_unref (gray16_caps);
160
161         GST_DEBUG ("Received caps from peer: %" GST_PTR_FORMAT, peercaps);
162         if (gst_caps_is_empty (peercaps)) {
163           gst_caps_unref (peercaps);
164           return FALSE;
165         }
166
167         if (!gst_caps_is_fixed (peercaps))
168           peercaps = gst_caps_fixate (peercaps);
169
170         peerstruct = gst_caps_get_structure (peercaps, 0);
171         fmtstr = gst_structure_get_string (peerstruct, "format");
172         if (fmtstr) {
173           if (g_str_equal (fmtstr, "GRAY16_BE")) {
174             fmt = GST_VIDEO_FORMAT_GRAY16_BE;
175           } else if (g_str_equal (fmtstr, "GRAY16_LE")) {
176             fmt = GST_VIDEO_FORMAT_GRAY16_LE;
177           }
178         }
179         gst_caps_unref (peercaps);
180       } else {
181         pnmdec->size = pnmdec->mngr.info.width * pnmdec->mngr.info.height * 1;
182         fmt = GST_VIDEO_FORMAT_GRAY8;
183       }
184       break;
185     case GST_PNM_TYPE_PIXMAP:
186       pnmdec->size = pnmdec->mngr.info.width * pnmdec->mngr.info.height * 3;
187       fmt = GST_VIDEO_FORMAT_RGB;
188       break;
189   }
190
191   if (fmt == GST_VIDEO_FORMAT_UNKNOWN)
192     return GST_FLOW_NOT_NEGOTIATED;
193
194   pnmdec->out_format = fmt;
195
196   output_state =
197       gst_video_decoder_set_output_state (decoder, fmt,
198       pnmdec->mngr.info.width, pnmdec->mngr.info.height, pnmdec->input_state);
199   gst_video_codec_state_unref (output_state);
200
201   if (gst_video_decoder_negotiate (decoder) == FALSE)
202     return GST_FLOW_NOT_NEGOTIATED;
203
204   return GST_FLOW_OK;
205 }
206
207 static gboolean
208 gst_pnmdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
209 {
210   GstPnmdec *pnmdec = (GstPnmdec *) decoder;
211
212   gst_pnmdec_negotiate (decoder);
213
214   if (pnmdec->input_state)
215     gst_video_codec_state_unref (pnmdec->input_state);
216   pnmdec->input_state = gst_video_codec_state_ref (state);
217
218   return TRUE;
219 }
220
221 static gboolean
222 gst_pnmdec_stop (GstVideoDecoder * decoder)
223 {
224   GstPnmdec *pnmdec = (GstPnmdec *) decoder;
225
226   if (pnmdec->input_state) {
227     gst_video_codec_state_unref (pnmdec->input_state);
228     pnmdec->input_state = NULL;
229   }
230
231   if (pnmdec->buf) {
232     gst_buffer_unref (pnmdec->buf);
233     pnmdec->buf = NULL;
234   }
235   return TRUE;
236 }
237
238 static GstFlowReturn
239 gst_pnmdec_parse_ascii (GstPnmdec * s, const guint8 * b, guint bs)
240 {
241   GScanner *scanner;
242   guint i = 0;
243   guint target, last_val = 0;
244   GstMapInfo map;
245   guint8 *outdata;
246
247   target = s->size - s->current_size;
248
249   gst_buffer_map (s->buf, &map, GST_MAP_WRITE);
250
251   if (bs) {
252     GST_MEMDUMP_OBJECT (s, "Starting parse:", b, MIN (16, bs));
253   }
254
255   /* leave the number of bytes already parsed */
256   outdata = map.data + s->current_size;
257
258   if (s->have_last_val) {
259     while (bs && *b >= '0' && *b <= '9') {
260       s->last_val = 10 * s->last_val + *b - '0';
261       b++;
262       if (!--bs) {
263         goto drop_error;
264       }
265     }
266     if (s->last_val > s->mngr.info.max) {
267       GST_DEBUG_OBJECT (s, "Corrupt ASCII encoded PNM file.");
268       goto drop_error;
269     }
270
271     GST_LOG_OBJECT (s, "Collected partial value from previous parse - %u",
272         s->last_val);
273     if (s->mngr.info.max > 255) {
274       if (i + 1 >= target) {
275         GST_DEBUG_OBJECT (s, "PNM file contains too much data.");
276         goto drop_error;
277       }
278       if (s->out_format == GST_VIDEO_FORMAT_GRAY16_BE)
279         GST_WRITE_UINT16_BE (outdata + i, s->last_val);
280       else
281         GST_WRITE_UINT16_LE (outdata + i, s->last_val);
282       i += 2;
283     } else {
284       outdata[i++] = s->last_val;
285     }
286     last_val = s->last_val;
287     s->have_last_val = FALSE;
288   }
289
290   /* Might be no data if we're at EOS */
291   if (!bs)
292     goto done;
293
294   scanner = g_scanner_new (NULL);
295   g_scanner_input_text (scanner, (gchar *) b, bs);
296   while (!g_scanner_eof (scanner)) {
297     switch (g_scanner_get_next_token (scanner)) {
298       case G_TOKEN_INT:
299         if (s->mngr.info.max > 255) {
300           if (i + 1 >= target) {
301             GST_DEBUG_OBJECT (s,
302                 "PNM file contains too much data after line %u col %u.",
303                 scanner->line, scanner->position);
304             g_scanner_destroy (scanner);
305             goto done;          // drop_error;
306           }
307           if (s->out_format == GST_VIDEO_FORMAT_GRAY16_BE)
308             GST_WRITE_UINT16_BE (outdata + i, scanner->value.v_int);
309           else
310             GST_WRITE_UINT16_LE (outdata + i, scanner->value.v_int);
311           i += 2;
312         } else {
313           if (i == target) {
314             GST_DEBUG_OBJECT (s,
315                 "PNM file contains too much data after line %u col %u.",
316                 scanner->line, scanner->position);
317             g_scanner_destroy (scanner);
318             goto drop_error;
319           }
320           outdata[i++] = scanner->value.v_int;
321         }
322         last_val = scanner->value.v_int;
323         break;
324       default:
325         /* Should we care? */ ;
326     }
327   }
328   g_scanner_destroy (scanner);
329
330   /* If we didn't get the whole image, handle the last byte with care. */
331   if (i && i < target && b[bs - 1] >= '0' && b[bs - 1] <= '9') {
332     s->last_val = last_val;
333     s->have_last_val = TRUE;
334     if (s->mngr.info.max > 255)
335       i -= 2;
336     else
337       i--;
338     GST_LOG_OBJECT (s, "Stored last value %u for next parse cycle",
339         s->last_val);
340   }
341
342 done:
343   /* Update the number of output bytes parsed in this scan */
344   s->current_size += i;
345   GST_LOG_OBJECT (s, "Parsed %u bytes, now have %u bytes of %u output",
346       i, s->current_size, s->size);
347   gst_buffer_unmap (s->buf, &map);
348
349   return GST_FLOW_OK;
350
351 drop_error:
352   gst_buffer_unmap (s->buf, &map);
353
354   return GST_FLOW_ERROR;
355 }
356
357 static GstFlowReturn
358 gst_pnmdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
359 {
360   GstPnmdec *s = (GstPnmdec *) decoder;
361   GstMapInfo imap, omap;
362   guint i_rowstride;
363   guint o_rowstride;
364   GstFlowReturn r = GST_FLOW_OK;
365   gint bytes, i, total_bytes = 0;
366
367   r = gst_video_decoder_allocate_output_frame (decoder, frame);
368   if (r != GST_FLOW_OK) {
369     gst_video_decoder_drop_frame (GST_VIDEO_DECODER (s), frame);
370     goto out;
371   }
372
373   if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
374     /* In case of ASCII parsed data is stored in buf, so input needs to be
375        taken from here for frame processing */
376     gst_buffer_map (s->buf, &imap, GST_MAP_READ);
377   } else {
378     gst_buffer_map (frame->input_buffer, &imap, GST_MAP_READ);
379   }
380   gst_buffer_map (frame->output_buffer, &omap, GST_MAP_WRITE);
381
382   gst_buffer_copy_into (frame->output_buffer, frame->input_buffer,
383       GST_BUFFER_COPY_METADATA, 0, 0);
384
385   if (s->mngr.info.type == GST_PNM_TYPE_BITMAP) {
386     bytes = (s->mngr.info.width * s->mngr.info.height + 7) / 8;
387     for (i = 0; i < bytes; i++) {
388       omap.data[i * 8] = (imap.data[i] & 0x80) ? 0 : 255;
389       omap.data[i * 8 + 1] = (imap.data[i] & 0x40) ? 0 : 255;
390       omap.data[i * 8 + 2] = (imap.data[i] & 0x20) ? 0 : 255;
391       omap.data[i * 8 + 3] = (imap.data[i] & 0x10) ? 0 : 255;
392       omap.data[i * 8 + 4] = (imap.data[i] & 0x08) ? 0 : 255;
393       omap.data[i * 8 + 5] = (imap.data[i] & 0x04) ? 0 : 255;
394       omap.data[i * 8 + 6] = (imap.data[i] & 0x02) ? 0 : 255;
395       omap.data[i * 8 + 7] = (imap.data[i] & 0x01) ? 0 : 255;
396     }
397     total_bytes = bytes * 8;
398   } else
399     /* Need to convert from PNM rowstride to GStreamer rowstride */
400   if (s->mngr.info.width % 4 != 0) {
401     if (s->mngr.info.type == GST_PNM_TYPE_PIXMAP) {
402       i_rowstride = 3 * s->mngr.info.width;
403       o_rowstride = GST_ROUND_UP_4 (i_rowstride);
404     } else {
405       if (s->mngr.info.max > 255)
406         i_rowstride = s->mngr.info.width * 2;
407       else
408         i_rowstride = s->mngr.info.width;
409       o_rowstride = GST_ROUND_UP_4 (i_rowstride);
410     }
411
412     for (i = 0; i < s->mngr.info.height; i++)
413       memcpy (omap.data + i * o_rowstride, imap.data + i * i_rowstride,
414           i_rowstride);
415     total_bytes = o_rowstride * s->mngr.info.height;
416   } else {
417     memcpy (omap.data, imap.data, s->size);
418     total_bytes = s->size;
419   }
420
421   if (s->mngr.info.type != GST_PNM_TYPE_BITMAP) {
422     if (s->mngr.info.max > 255 && s->mngr.info.max < 65535) {
423       /* Convert the pixels from 0 - max range to 0 - 65535 range
424        * and appropriate endianness (input is always BE) */
425       guint8 *data = omap.data;
426       gint max = s->mngr.info.max;
427       if (s->out_format == GST_VIDEO_FORMAT_GRAY16_BE) {
428         for (i = 0; i < total_bytes; i += 2) {
429           /* Clamp to 65535 */
430           guint16 val = GST_READ_UINT16_BE (data + i);
431           val = (val > max) ? 65535 : 65535 * val / max;
432           GST_WRITE_UINT16_BE (data + i, val);
433         }
434       } else {
435         for (i = 0; i < total_bytes; i += 2) {
436           /* Clamp to 65535 */
437           guint16 val = GST_READ_UINT16_BE (data + i);
438           val = (val > max) ? 65535 : 65535 * val / max;
439           GST_WRITE_UINT16_LE (data + i, val);
440         }
441       }
442     } else if (s->mngr.info.max < 255) {
443       /* Convert the pixels from 0 - max range to 0 - 255 range */
444       gint max = s->mngr.info.max;
445       for (i = 0; i < total_bytes; i++) {
446         if (omap.data[i] <= max) {
447           omap.data[i] = 255 * omap.data[i] / max;
448         } else {
449           /* This is an error case, wherein value in the data stream is
450              more than max. Clamp such values to 255 */
451           omap.data[i] = 255;
452         }
453       }
454     }
455   }
456
457   if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
458     gst_buffer_unmap (s->buf, &imap);
459   } else {
460     gst_buffer_unmap (frame->input_buffer, &imap);
461   }
462   gst_buffer_unmap (frame->output_buffer, &omap);
463
464   s->current_size = 0;
465
466   r = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (s), frame);
467
468 out:
469   gst_pnmdec_flush (s);
470
471   return r;
472 }
473
474 static GstFlowReturn
475 gst_pnmdec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
476     GstAdapter * adapter, gboolean at_eos)
477 {
478   gsize size;
479   GstPnmdec *s = GST_PNMDEC (decoder);
480   GstFlowReturn r = GST_FLOW_OK;
481   guint offset = 0;
482   const guint8 *raw_data = NULL;
483
484   GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
485
486   size = gst_adapter_available (adapter);
487   if (size > 0)
488     raw_data = gst_adapter_map (adapter, size);
489
490   GST_LOG_OBJECT (s, "Entering parse with %" G_GSIZE_FORMAT " bytes. at_eos %d",
491       size, at_eos);
492
493   if (s->mngr.info.fields != GST_PNM_INFO_FIELDS_ALL) {
494     GstPnmInfoMngrResult res;
495
496     if (size < 8)
497       goto need_more_data;
498
499     res = gst_pnm_info_mngr_scan (&s->mngr, raw_data, size);
500
501     switch (res) {
502       case GST_PNM_INFO_MNGR_RESULT_FAILED:
503         r = GST_FLOW_ERROR;
504         goto out;
505       case GST_PNM_INFO_MNGR_RESULT_READING:
506         r = GST_FLOW_OK;
507         goto out;
508       case GST_PNM_INFO_MNGR_RESULT_FINISHED:
509
510         r = gst_pnmdec_negotiate (decoder);
511         if (r != GST_FLOW_OK)
512           goto out;
513
514         if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
515           /* It is not possible to know the size of input ascii data to parse.
516              So we have to parse and know the number of pixels parsed and
517              then finally decide when we have full frame */
518           GST_DEBUG_OBJECT (s, "Allocating output frame of size %u", s->size);
519           s->buf = gst_buffer_new_and_alloc (s->size);
520         }
521         offset = s->mngr.data_offset;
522         gst_adapter_flush (adapter, offset);
523         size = size - offset;
524     }
525   }
526
527   if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
528     /* Parse ASCII data and populate s->current_size with the number of
529        bytes actually parsed from the input data */
530     GST_DEBUG_OBJECT (s, "Parsing %u bytes at offset %u", (guint) size, offset);
531     r = gst_pnmdec_parse_ascii (s, raw_data + offset, size);
532   } else {
533     /* Bitmap Contains 8 pixels in a byte */
534     if (s->mngr.info.type == GST_PNM_TYPE_BITMAP)
535       s->current_size += (size * 8);
536     else
537       s->current_size += size;
538   }
539
540   gst_video_decoder_add_to_frame (decoder, size);
541   if (s->size <= s->current_size) {
542     goto have_full_frame;
543   }
544
545 need_more_data:
546   return GST_VIDEO_DECODER_FLOW_NEED_DATA;
547
548 have_full_frame:
549   return gst_video_decoder_have_frame (decoder);
550
551 out:
552   return r;
553 }
554
555 static gboolean
556 gst_pnmdec_start (GstVideoDecoder * decoder)
557 {
558   GstPnmdec *pnmdec = (GstPnmdec *) decoder;
559   gst_video_decoder_set_packetized (GST_VIDEO_DECODER (pnmdec), FALSE);
560   gst_pnmdec_flush (pnmdec);
561   return TRUE;
562 }
563
564 static GstFlowReturn
565 gst_pnmdec_finish (GstVideoDecoder * decoder)
566 {
567   GstPnmdec *s = (GstPnmdec *) decoder;
568
569   GST_LOG_OBJECT (s, "finishing");
570
571   if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
572     /* One last go at outputting any final value */
573     gst_pnmdec_parse_ascii (s, 0, 0);
574     if (s->size <= s->current_size) {
575       return gst_video_decoder_have_frame (decoder);
576     }
577   }
578
579   return GST_FLOW_OK;
580 }