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;
247 target = s->size - s->current_size;
249 gst_buffer_map (s->buf, &map, GST_MAP_WRITE);
252 GST_MEMDUMP_OBJECT (s, "Starting parse:", b, MIN (16, bs));
255 /* leave the number of bytes already parsed */
256 outdata = map.data + s->current_size;
258 if (s->have_last_val) {
259 while (bs && *b >= '0' && *b <= '9') {
260 s->last_val = 10 * s->last_val + *b - '0';
266 if (s->last_val > s->mngr.info.max) {
267 GST_DEBUG_OBJECT (s, "Corrupt ASCII encoded PNM file.");
271 GST_LOG_OBJECT (s, "Collected partial value from previous parse - %u",
273 if (s->mngr.info.max > 255) {
274 if (i + 1 >= target) {
275 GST_DEBUG_OBJECT (s, "PNM file contains too much data.");
278 if (s->out_format == GST_VIDEO_FORMAT_GRAY16_BE)
279 GST_WRITE_UINT16_BE (outdata + i, s->last_val);
281 GST_WRITE_UINT16_LE (outdata + i, s->last_val);
284 outdata[i++] = s->last_val;
286 last_val = s->last_val;
287 s->have_last_val = FALSE;
290 /* Might be no data if we're at EOS */
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)) {
299 if (s->mngr.info.max > 255) {
300 if (i + 1 >= target) {
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;
307 if (s->out_format == GST_VIDEO_FORMAT_GRAY16_BE)
308 GST_WRITE_UINT16_BE (outdata + i, scanner->value.v_int);
310 GST_WRITE_UINT16_LE (outdata + i, scanner->value.v_int);
315 "PNM file contains too much data after line %u col %u.",
316 scanner->line, scanner->position);
317 g_scanner_destroy (scanner);
320 outdata[i++] = scanner->value.v_int;
322 last_val = scanner->value.v_int;
325 /* Should we care? */ ;
328 g_scanner_destroy (scanner);
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)
338 GST_LOG_OBJECT (s, "Stored last value %u for next parse cycle",
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);
352 gst_buffer_unmap (s->buf, &map);
354 return GST_FLOW_ERROR;
358 gst_pnmdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
360 GstPnmdec *s = (GstPnmdec *) decoder;
361 GstMapInfo imap, omap;
364 GstFlowReturn r = GST_FLOW_OK;
365 gint bytes, i, total_bytes = 0;
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);
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);
378 gst_buffer_map (frame->input_buffer, &imap, GST_MAP_READ);
380 gst_buffer_map (frame->output_buffer, &omap, GST_MAP_WRITE);
382 gst_buffer_copy_into (frame->output_buffer, frame->input_buffer,
383 GST_BUFFER_COPY_METADATA, 0, 0);
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;
397 total_bytes = bytes * 8;
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);
405 if (s->mngr.info.max > 255)
406 i_rowstride = s->mngr.info.width * 2;
408 i_rowstride = s->mngr.info.width;
409 o_rowstride = GST_ROUND_UP_4 (i_rowstride);
412 for (i = 0; i < s->mngr.info.height; i++)
413 memcpy (omap.data + i * o_rowstride, imap.data + i * i_rowstride,
415 total_bytes = o_rowstride * s->mngr.info.height;
417 memcpy (omap.data, imap.data, s->size);
418 total_bytes = s->size;
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) {
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);
435 for (i = 0; i < total_bytes; i += 2) {
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);
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;
449 /* This is an error case, wherein value in the data stream is
450 more than max. Clamp such values to 255 */
457 if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
458 gst_buffer_unmap (s->buf, &imap);
460 gst_buffer_unmap (frame->input_buffer, &imap);
462 gst_buffer_unmap (frame->output_buffer, &omap);
466 r = gst_video_decoder_finish_frame (GST_VIDEO_DECODER (s), frame);
469 gst_pnmdec_flush (s);
475 gst_pnmdec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
476 GstAdapter * adapter, gboolean at_eos)
479 GstPnmdec *s = GST_PNMDEC (decoder);
480 GstFlowReturn r = GST_FLOW_OK;
482 const guint8 *raw_data = NULL;
484 GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
486 size = gst_adapter_available (adapter);
488 raw_data = gst_adapter_map (adapter, size);
490 GST_LOG_OBJECT (s, "Entering parse with %" G_GSIZE_FORMAT " bytes. at_eos %d",
493 if (s->mngr.info.fields != GST_PNM_INFO_FIELDS_ALL) {
494 GstPnmInfoMngrResult res;
499 res = gst_pnm_info_mngr_scan (&s->mngr, raw_data, size);
502 case GST_PNM_INFO_MNGR_RESULT_FAILED:
505 case GST_PNM_INFO_MNGR_RESULT_READING:
508 case GST_PNM_INFO_MNGR_RESULT_FINISHED:
510 r = gst_pnmdec_negotiate (decoder);
511 if (r != GST_FLOW_OK)
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);
521 offset = s->mngr.data_offset;
522 gst_adapter_flush (adapter, offset);
523 size = size - offset;
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);
533 /* Bitmap Contains 8 pixels in a byte */
534 if (s->mngr.info.type == GST_PNM_TYPE_BITMAP)
535 s->current_size += (size * 8);
537 s->current_size += size;
540 gst_video_decoder_add_to_frame (decoder, size);
541 if (s->size <= s->current_size) {
542 goto have_full_frame;
546 return GST_VIDEO_DECODER_FLOW_NEED_DATA;
549 return gst_video_decoder_have_frame (decoder);
556 gst_pnmdec_start (GstVideoDecoder * decoder)
558 GstPnmdec *pnmdec = (GstPnmdec *) decoder;
559 gst_video_decoder_set_packetized (GST_VIDEO_DECODER (pnmdec), FALSE);
560 gst_pnmdec_flush (pnmdec);
565 gst_pnmdec_finish (GstVideoDecoder * decoder)
567 GstPnmdec *s = (GstPnmdec *) decoder;
569 GST_LOG_OBJECT (s, "finishing");
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);