1 /* GStreamer PNM decoder
2 * Copyright (C) 2009 Lutz Mueller <lutz@users.sourceforge.net>
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.
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.
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.
21 * SECTION:element-pnmdec
26 * ## Example launch line
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.
37 #include "gstpnmdec.h"
38 #include "gstpnmutils.h"
40 #include <gst/gstutils.h>
41 #include <gst/video/video.h>
42 #include <gst/base/gstbytereader.h>
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);
56 gst_pnmdec_parse_ascii (GstPnmdec * s, const guint8 * b, guint bs);
58 G_DEFINE_TYPE (GstPnmdec, gst_pnmdec, GST_TYPE_VIDEO_DECODER);
60 static GstStaticPadTemplate gst_pnmdec_src_pad_template =
61 GST_STATIC_PAD_TEMPLATE ("src",
64 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE
65 ("{ RGB, GRAY8, GRAY16_BE, GRAY16_LE }")));
67 static GstStaticCaps gst_pnmdec_gray16_caps =
68 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ GRAY16_BE, GRAY16_LE }"));
70 static GstStaticPadTemplate gst_pnmdec_sink_pad_template =
71 GST_STATIC_PAD_TEMPLATE ("sink",
74 GST_STATIC_CAPS (MIME_ALL));
76 GST_DEBUG_CATEGORY (pnmdecoder_debug);
77 #define GST_CAT_DEFAULT pnmdecoder_debug
80 gst_pnmdec_class_init (GstPnmdecClass * klass)
82 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
83 GstVideoDecoderClass *vdec_class = (GstVideoDecoderClass *) klass;
85 GST_DEBUG_CATEGORY_INIT (pnmdecoder_debug, "pnmdec", 0, "PNM Video Decoder");
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);
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>");
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;
106 gst_pnmdec_flush (GstPnmdec * s)
108 memset (&s->mngr, 0, sizeof (s->mngr));
112 gst_buffer_unref (s->buf);
118 gst_pnmdec_init (GstPnmdec * s)
120 /* Initialize decoder */
122 gst_pnmdec_flush (s);
124 gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST
126 GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (s));
130 gst_pnmdec_negotiate (GstVideoDecoder * decoder)
132 GstPnmdec *pnmdec = (GstPnmdec *) decoder;
133 GstVideoFormat fmt = GST_VIDEO_FORMAT_UNKNOWN;
134 GstVideoCodecState *output_state;
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;
141 pnmdec->size = pnmdec->mngr.info.width * pnmdec->mngr.info.height * 1;
142 fmt = GST_VIDEO_FORMAT_GRAY8;
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);
148 GstStructure *peerstruct;
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
157 gst_pad_peer_query_caps (GST_VIDEO_DECODER_SRC_PAD (decoder),
159 gst_caps_unref (gray16_caps);
161 GST_DEBUG ("Received caps from peer: %" GST_PTR_FORMAT, peercaps);
162 if (gst_caps_is_empty (peercaps)) {
163 gst_caps_unref (peercaps);
167 if (!gst_caps_is_fixed (peercaps))
168 peercaps = gst_caps_fixate (peercaps);
170 peerstruct = gst_caps_get_structure (peercaps, 0);
171 fmtstr = gst_structure_get_string (peerstruct, "format");
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;
179 gst_caps_unref (peercaps);
181 pnmdec->size = pnmdec->mngr.info.width * pnmdec->mngr.info.height * 1;
182 fmt = GST_VIDEO_FORMAT_GRAY8;
185 case GST_PNM_TYPE_PIXMAP:
186 pnmdec->size = pnmdec->mngr.info.width * pnmdec->mngr.info.height * 3;
187 fmt = GST_VIDEO_FORMAT_RGB;
191 if (fmt == GST_VIDEO_FORMAT_UNKNOWN)
192 return GST_FLOW_NOT_NEGOTIATED;
194 pnmdec->out_format = fmt;
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);
201 if (gst_video_decoder_negotiate (decoder) == FALSE)
202 return GST_FLOW_NOT_NEGOTIATED;
208 gst_pnmdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
210 GstPnmdec *pnmdec = (GstPnmdec *) decoder;
212 gst_pnmdec_negotiate (decoder);
214 if (pnmdec->input_state)
215 gst_video_codec_state_unref (pnmdec->input_state);
216 pnmdec->input_state = gst_video_codec_state_ref (state);
222 gst_pnmdec_stop (GstVideoDecoder * decoder)
224 GstPnmdec *pnmdec = (GstPnmdec *) decoder;
226 if (pnmdec->input_state) {
227 gst_video_codec_state_unref (pnmdec->input_state);
228 pnmdec->input_state = NULL;
232 gst_buffer_unref (pnmdec->buf);
239 gst_pnmdec_parse_ascii (GstPnmdec * s, const guint8 * b, guint bs)
243 guint target, last_val = 0;
250 target = s->size - s->current_size;
252 gst_buffer_map (s->buf, &map, GST_MAP_WRITE);
255 GST_MEMDUMP_OBJECT (s, "Starting parse:", b, MIN (16, bs));
258 /* leave the number of bytes already parsed */
259 outdata = map.data + s->current_size;
261 if (s->have_last_val) {
262 while (bs && *b >= '0' && *b <= '9') {
263 s->last_val = 10 * s->last_val + *b - '0';
269 if (s->last_val > s->mngr.info.max) {
270 GST_DEBUG_OBJECT (s, "Corrupt ASCII encoded PNM file.");
274 GST_LOG_OBJECT (s, "Collected partial value from previous parse - %u",
276 if (s->mngr.info.max > 255) {
277 if (i + 1 >= target) {
278 GST_DEBUG_OBJECT (s, "PNM file contains too much data.");
281 if (s->out_format == GST_VIDEO_FORMAT_GRAY16_BE)
282 GST_WRITE_UINT16_BE (outdata + i, s->last_val);
284 GST_WRITE_UINT16_LE (outdata + i, s->last_val);
287 outdata[i++] = s->last_val;
289 last_val = s->last_val;
290 s->have_last_val = FALSE;
293 /* Might be no data if we're at EOS */
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)) {
302 if (s->mngr.info.max > 255) {
303 if (i + 1 >= target) {
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;
310 if (s->out_format == GST_VIDEO_FORMAT_GRAY16_BE)
311 GST_WRITE_UINT16_BE (outdata + i, scanner->value.v_int);
313 GST_WRITE_UINT16_LE (outdata + i, scanner->value.v_int);
318 "PNM file contains too much data after line %u col %u.",
319 scanner->line, scanner->position);
320 g_scanner_destroy (scanner);
323 outdata[i++] = scanner->value.v_int;
325 last_val = scanner->value.v_int;
328 /* Should we care? */ ;
331 g_scanner_destroy (scanner);
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)
341 GST_LOG_OBJECT (s, "Stored last value %u for next parse cycle",
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);
355 gst_buffer_unmap (s->buf, &map);
357 return GST_FLOW_ERROR;
361 gst_pnmdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
363 GstPnmdec *s = (GstPnmdec *) decoder;
364 GstMapInfo imap, omap;
367 GstFlowReturn r = GST_FLOW_OK;
368 gint bytes, i, total_bytes = 0;
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);
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);
381 gst_buffer_map (frame->input_buffer, &imap, GST_MAP_READ);
383 gst_buffer_map (frame->output_buffer, &omap, GST_MAP_WRITE);
385 gst_buffer_copy_into (frame->output_buffer, frame->input_buffer,
386 GST_BUFFER_COPY_METADATA, 0, 0);
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;
400 total_bytes = bytes * 8;
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);
408 if (s->mngr.info.max > 255)
409 i_rowstride = s->mngr.info.width * 2;
411 i_rowstride = s->mngr.info.width;
412 o_rowstride = GST_ROUND_UP_4 (i_rowstride);
415 for (i = 0; i < s->mngr.info.height; i++)
416 memcpy (omap.data + i * o_rowstride, imap.data + i * i_rowstride,
418 total_bytes = o_rowstride * s->mngr.info.height;
420 memcpy (omap.data, imap.data, s->size);
421 total_bytes = s->size;
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) {
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);
438 for (i = 0; i < total_bytes; i += 2) {
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);
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;
452 /* This is an error case, wherein value in the data stream is
453 more than max. Clamp such values to 255 */
460 if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
461 gst_buffer_unmap (s->buf, &imap);
463 gst_buffer_unmap (frame->input_buffer, &imap);
465 gst_buffer_unmap (frame->output_buffer, &omap);
469 r = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (s), frame);
472 gst_pnmdec_flush (s);
478 gst_pnmdec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
479 GstAdapter * adapter, gboolean at_eos)
482 GstPnmdec *s = GST_PNMDEC (decoder);
483 GstFlowReturn r = GST_FLOW_OK;
485 const guint8 *raw_data = NULL;
487 GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
489 size = gst_adapter_available (adapter);
491 raw_data = gst_adapter_map (adapter, size);
493 GST_LOG_OBJECT (s, "Entering parse with %" G_GSIZE_FORMAT " bytes. at_eos %d",
496 if (s->mngr.info.fields != GST_PNM_INFO_FIELDS_ALL) {
497 GstPnmInfoMngrResult res;
502 res = gst_pnm_info_mngr_scan (&s->mngr, raw_data, size);
505 case GST_PNM_INFO_MNGR_RESULT_FAILED:
508 case GST_PNM_INFO_MNGR_RESULT_READING:
511 case GST_PNM_INFO_MNGR_RESULT_FINISHED:
513 r = gst_pnmdec_negotiate (decoder);
514 if (r != GST_FLOW_OK)
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);
524 offset = s->mngr.data_offset;
525 gst_adapter_flush (adapter, offset);
526 size = size - offset;
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);
536 /* Bitmap Contains 8 pixels in a byte */
537 if (s->mngr.info.type == GST_PNM_TYPE_BITMAP)
538 s->current_size += (size * 8);
540 s->current_size += size;
543 gst_video_decoder_add_to_frame (decoder, size);
544 if (s->size <= s->current_size) {
545 goto have_full_frame;
549 return GST_VIDEO_DECODER_FLOW_NEED_DATA;
552 return gst_video_decoder_have_frame (decoder);
559 gst_pnmdec_start (GstVideoDecoder * decoder)
561 GstPnmdec *pnmdec = (GstPnmdec *) decoder;
562 gst_video_decoder_set_packetized (GST_VIDEO_DECODER (pnmdec), FALSE);
563 gst_pnmdec_flush (pnmdec);
568 gst_pnmdec_finish (GstVideoDecoder * decoder)
570 GstPnmdec *s = (GstPnmdec *) decoder;
572 GST_LOG_OBJECT (s, "finishing");
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);