pnmdec: Return early on ::finish() if we have no actual data to parse
[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   if (!s->buf)
248     return GST_FLOW_OK;
249
250   target = s->size - s->current_size;
251
252   gst_buffer_map (s->buf, &map, GST_MAP_WRITE);
253
254   if (bs) {
255     GST_MEMDUMP_OBJECT (s, "Starting parse:", b, MIN (16, bs));
256   }
257
258   /* leave the number of bytes already parsed */
259   outdata = map.data + s->current_size;
260
261   if (s->have_last_val) {
262     while (bs && *b >= '0' && *b <= '9') {
263       s->last_val = 10 * s->last_val + *b - '0';
264       b++;
265       if (!--bs) {
266         goto drop_error;
267       }
268     }
269     if (s->last_val > s->mngr.info.max) {
270       GST_DEBUG_OBJECT (s, "Corrupt ASCII encoded PNM file.");
271       goto drop_error;
272     }
273
274     GST_LOG_OBJECT (s, "Collected partial value from previous parse - %u",
275         s->last_val);
276     if (s->mngr.info.max > 255) {
277       if (i + 1 >= target) {
278         GST_DEBUG_OBJECT (s, "PNM file contains too much data.");
279         goto drop_error;
280       }
281       if (s->out_format == GST_VIDEO_FORMAT_GRAY16_BE)
282         GST_WRITE_UINT16_BE (outdata + i, s->last_val);
283       else
284         GST_WRITE_UINT16_LE (outdata + i, s->last_val);
285       i += 2;
286     } else {
287       outdata[i++] = s->last_val;
288     }
289     last_val = s->last_val;
290     s->have_last_val = FALSE;
291   }
292
293   /* Might be no data if we're at EOS */
294   if (!bs)
295     goto done;
296
297   scanner = g_scanner_new (NULL);
298   g_scanner_input_text (scanner, (gchar *) b, bs);
299   while (!g_scanner_eof (scanner)) {
300     switch (g_scanner_get_next_token (scanner)) {
301       case G_TOKEN_INT:
302         if (s->mngr.info.max > 255) {
303           if (i + 1 >= target) {
304             GST_DEBUG_OBJECT (s,
305                 "PNM file contains too much data after line %u col %u.",
306                 scanner->line, scanner->position);
307             g_scanner_destroy (scanner);
308             goto done;          // drop_error;
309           }
310           if (s->out_format == GST_VIDEO_FORMAT_GRAY16_BE)
311             GST_WRITE_UINT16_BE (outdata + i, scanner->value.v_int);
312           else
313             GST_WRITE_UINT16_LE (outdata + i, scanner->value.v_int);
314           i += 2;
315         } else {
316           if (i == target) {
317             GST_DEBUG_OBJECT (s,
318                 "PNM file contains too much data after line %u col %u.",
319                 scanner->line, scanner->position);
320             g_scanner_destroy (scanner);
321             goto drop_error;
322           }
323           outdata[i++] = scanner->value.v_int;
324         }
325         last_val = scanner->value.v_int;
326         break;
327       default:
328         /* Should we care? */ ;
329     }
330   }
331   g_scanner_destroy (scanner);
332
333   /* If we didn't get the whole image, handle the last byte with care. */
334   if (i && i < target && b[bs - 1] >= '0' && b[bs - 1] <= '9') {
335     s->last_val = last_val;
336     s->have_last_val = TRUE;
337     if (s->mngr.info.max > 255)
338       i -= 2;
339     else
340       i--;
341     GST_LOG_OBJECT (s, "Stored last value %u for next parse cycle",
342         s->last_val);
343   }
344
345 done:
346   /* Update the number of output bytes parsed in this scan */
347   s->current_size += i;
348   GST_LOG_OBJECT (s, "Parsed %u bytes, now have %u bytes of %u output",
349       i, s->current_size, s->size);
350   gst_buffer_unmap (s->buf, &map);
351
352   return GST_FLOW_OK;
353
354 drop_error:
355   gst_buffer_unmap (s->buf, &map);
356
357   return GST_FLOW_ERROR;
358 }
359
360 static GstFlowReturn
361 gst_pnmdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
362 {
363   GstPnmdec *s = (GstPnmdec *) decoder;
364   GstMapInfo imap, omap;
365   guint i_rowstride;
366   guint o_rowstride;
367   GstFlowReturn r = GST_FLOW_OK;
368   gint bytes, i, total_bytes = 0;
369
370   r = gst_video_decoder_allocate_output_frame (decoder, frame);
371   if (r != GST_FLOW_OK) {
372     gst_video_decoder_drop_frame (GST_VIDEO_DECODER (s), frame);
373     goto out;
374   }
375
376   if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
377     /* In case of ASCII parsed data is stored in buf, so input needs to be
378        taken from here for frame processing */
379     gst_buffer_map (s->buf, &imap, GST_MAP_READ);
380   } else {
381     gst_buffer_map (frame->input_buffer, &imap, GST_MAP_READ);
382   }
383   gst_buffer_map (frame->output_buffer, &omap, GST_MAP_WRITE);
384
385   gst_buffer_copy_into (frame->output_buffer, frame->input_buffer,
386       GST_BUFFER_COPY_METADATA, 0, 0);
387
388   if (s->mngr.info.type == GST_PNM_TYPE_BITMAP) {
389     bytes = (s->mngr.info.width * s->mngr.info.height + 7) / 8;
390     for (i = 0; i < bytes; i++) {
391       omap.data[i * 8] = (imap.data[i] & 0x80) ? 0 : 255;
392       omap.data[i * 8 + 1] = (imap.data[i] & 0x40) ? 0 : 255;
393       omap.data[i * 8 + 2] = (imap.data[i] & 0x20) ? 0 : 255;
394       omap.data[i * 8 + 3] = (imap.data[i] & 0x10) ? 0 : 255;
395       omap.data[i * 8 + 4] = (imap.data[i] & 0x08) ? 0 : 255;
396       omap.data[i * 8 + 5] = (imap.data[i] & 0x04) ? 0 : 255;
397       omap.data[i * 8 + 6] = (imap.data[i] & 0x02) ? 0 : 255;
398       omap.data[i * 8 + 7] = (imap.data[i] & 0x01) ? 0 : 255;
399     }
400     total_bytes = bytes * 8;
401   } else
402     /* Need to convert from PNM rowstride to GStreamer rowstride */
403   if (s->mngr.info.width % 4 != 0) {
404     if (s->mngr.info.type == GST_PNM_TYPE_PIXMAP) {
405       i_rowstride = 3 * s->mngr.info.width;
406       o_rowstride = GST_ROUND_UP_4 (i_rowstride);
407     } else {
408       if (s->mngr.info.max > 255)
409         i_rowstride = s->mngr.info.width * 2;
410       else
411         i_rowstride = s->mngr.info.width;
412       o_rowstride = GST_ROUND_UP_4 (i_rowstride);
413     }
414
415     for (i = 0; i < s->mngr.info.height; i++)
416       memcpy (omap.data + i * o_rowstride, imap.data + i * i_rowstride,
417           i_rowstride);
418     total_bytes = o_rowstride * s->mngr.info.height;
419   } else {
420     memcpy (omap.data, imap.data, s->size);
421     total_bytes = s->size;
422   }
423
424   if (s->mngr.info.type != GST_PNM_TYPE_BITMAP) {
425     if (s->mngr.info.max > 255 && s->mngr.info.max < 65535) {
426       /* Convert the pixels from 0 - max range to 0 - 65535 range
427        * and appropriate endianness (input is always BE) */
428       guint8 *data = omap.data;
429       gint max = s->mngr.info.max;
430       if (s->out_format == GST_VIDEO_FORMAT_GRAY16_BE) {
431         for (i = 0; i < total_bytes; i += 2) {
432           /* Clamp to 65535 */
433           guint16 val = GST_READ_UINT16_BE (data + i);
434           val = (val > max) ? 65535 : 65535 * val / max;
435           GST_WRITE_UINT16_BE (data + i, val);
436         }
437       } else {
438         for (i = 0; i < total_bytes; i += 2) {
439           /* Clamp to 65535 */
440           guint16 val = GST_READ_UINT16_BE (data + i);
441           val = (val > max) ? 65535 : 65535 * val / max;
442           GST_WRITE_UINT16_LE (data + i, val);
443         }
444       }
445     } else if (s->mngr.info.max < 255) {
446       /* Convert the pixels from 0 - max range to 0 - 255 range */
447       gint max = s->mngr.info.max;
448       for (i = 0; i < total_bytes; i++) {
449         if (omap.data[i] <= max) {
450           omap.data[i] = 255 * omap.data[i] / max;
451         } else {
452           /* This is an error case, wherein value in the data stream is
453              more than max. Clamp such values to 255 */
454           omap.data[i] = 255;
455         }
456       }
457     }
458   }
459
460   if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
461     gst_buffer_unmap (s->buf, &imap);
462   } else {
463     gst_buffer_unmap (frame->input_buffer, &imap);
464   }
465   gst_buffer_unmap (frame->output_buffer, &omap);
466
467   s->current_size = 0;
468
469   r = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (s), frame);
470
471 out:
472   gst_pnmdec_flush (s);
473
474   return r;
475 }
476
477 static GstFlowReturn
478 gst_pnmdec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
479     GstAdapter * adapter, gboolean at_eos)
480 {
481   gsize size;
482   GstPnmdec *s = GST_PNMDEC (decoder);
483   GstFlowReturn r = GST_FLOW_OK;
484   guint offset = 0;
485   const guint8 *raw_data = NULL;
486
487   GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
488
489   size = gst_adapter_available (adapter);
490   if (size > 0)
491     raw_data = gst_adapter_map (adapter, size);
492
493   GST_LOG_OBJECT (s, "Entering parse with %" G_GSIZE_FORMAT " bytes. at_eos %d",
494       size, at_eos);
495
496   if (s->mngr.info.fields != GST_PNM_INFO_FIELDS_ALL) {
497     GstPnmInfoMngrResult res;
498
499     if (size < 8)
500       goto need_more_data;
501
502     res = gst_pnm_info_mngr_scan (&s->mngr, raw_data, size);
503
504     switch (res) {
505       case GST_PNM_INFO_MNGR_RESULT_FAILED:
506         r = GST_FLOW_ERROR;
507         goto out;
508       case GST_PNM_INFO_MNGR_RESULT_READING:
509         r = GST_FLOW_OK;
510         goto out;
511       case GST_PNM_INFO_MNGR_RESULT_FINISHED:
512
513         r = gst_pnmdec_negotiate (decoder);
514         if (r != GST_FLOW_OK)
515           goto out;
516
517         if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
518           /* It is not possible to know the size of input ascii data to parse.
519              So we have to parse and know the number of pixels parsed and
520              then finally decide when we have full frame */
521           GST_DEBUG_OBJECT (s, "Allocating output frame of size %u", s->size);
522           s->buf = gst_buffer_new_and_alloc (s->size);
523         }
524         offset = s->mngr.data_offset;
525         gst_adapter_flush (adapter, offset);
526         size = size - offset;
527     }
528   }
529
530   if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
531     /* Parse ASCII data and populate s->current_size with the number of
532        bytes actually parsed from the input data */
533     GST_DEBUG_OBJECT (s, "Parsing %u bytes at offset %u", (guint) size, offset);
534     r = gst_pnmdec_parse_ascii (s, raw_data + offset, size);
535   } else {
536     /* Bitmap Contains 8 pixels in a byte */
537     if (s->mngr.info.type == GST_PNM_TYPE_BITMAP)
538       s->current_size += (size * 8);
539     else
540       s->current_size += size;
541   }
542
543   gst_video_decoder_add_to_frame (decoder, size);
544   if (s->size <= s->current_size) {
545     goto have_full_frame;
546   }
547
548 need_more_data:
549   return GST_VIDEO_DECODER_FLOW_NEED_DATA;
550
551 have_full_frame:
552   return gst_video_decoder_have_frame (decoder);
553
554 out:
555   return r;
556 }
557
558 static gboolean
559 gst_pnmdec_start (GstVideoDecoder * decoder)
560 {
561   GstPnmdec *pnmdec = (GstPnmdec *) decoder;
562   gst_video_decoder_set_packetized (GST_VIDEO_DECODER (pnmdec), FALSE);
563   gst_pnmdec_flush (pnmdec);
564   return TRUE;
565 }
566
567 static GstFlowReturn
568 gst_pnmdec_finish (GstVideoDecoder * decoder)
569 {
570   GstPnmdec *s = (GstPnmdec *) decoder;
571
572   GST_LOG_OBJECT (s, "finishing");
573
574   if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
575     /* One last go at outputting any final value */
576     gst_pnmdec_parse_ascii (s, 0, 0);
577     if (s->size && s->size <= s->current_size) {
578       return gst_video_decoder_have_frame (decoder);
579     }
580   }
581
582   return GST_FLOW_OK;
583 }