• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer PNG Parser
2  * Copyright (C) <2013> Collabora Ltd
3  *  @author Olivier Crete <olivier.crete@collabora.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24 
25 #include "gstvideoparserselements.h"
26 #include "gstpngparse.h"
27 
28 #include <gst/base/base.h>
29 #include <gst/pbutils/pbutils.h>
30 
31 #define PNG_SIGNATURE G_GUINT64_CONSTANT (0x89504E470D0A1A0A)
32 
33 GST_DEBUG_CATEGORY (png_parse_debug);
34 #define GST_CAT_DEFAULT png_parse_debug
35 
36 static GstStaticPadTemplate srctemplate =
37 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
38     GST_PAD_ALWAYS,
39     GST_STATIC_CAPS ("image/png, width = (int)[1, MAX], height = (int)[1, MAX],"
40         "parsed = (boolean) true")
41     );
42 
43 static GstStaticPadTemplate sinktemplate =
44 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK,
45     GST_PAD_ALWAYS,
46     GST_STATIC_CAPS ("image/png")
47     );
48 
49 #define parent_class gst_png_parse_parent_class
50 G_DEFINE_TYPE (GstPngParse, gst_png_parse, GST_TYPE_BASE_PARSE);
51 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (pngparse, "pngparse", GST_RANK_PRIMARY,
52     GST_TYPE_PNG_PARSE, videoparsers_element_init (plugin));
53 
54 static gboolean gst_png_parse_start (GstBaseParse * parse);
55 static gboolean gst_png_parse_event (GstBaseParse * parse, GstEvent * event);
56 static GstFlowReturn gst_png_parse_handle_frame (GstBaseParse * parse,
57     GstBaseParseFrame * frame, gint * skipsize);
58 static GstFlowReturn gst_png_parse_pre_push_frame (GstBaseParse * parse,
59     GstBaseParseFrame * frame);
60 
61 static void
gst_png_parse_class_init(GstPngParseClass * klass)62 gst_png_parse_class_init (GstPngParseClass * klass)
63 {
64   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
65   GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass);
66 
67   GST_DEBUG_CATEGORY_INIT (png_parse_debug, "pngparse", 0, "png parser");
68 
69   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
70   gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
71   gst_element_class_set_static_metadata (gstelement_class, "PNG parser",
72       "Codec/Parser/Video/Image",
73       "Parses PNG files", "Olivier Crete <olivier.crete@collabora.com>");
74 
75   /* Override BaseParse vfuncs */
76   parse_class->start = GST_DEBUG_FUNCPTR (gst_png_parse_start);
77   parse_class->sink_event = GST_DEBUG_FUNCPTR (gst_png_parse_event);
78   parse_class->handle_frame = GST_DEBUG_FUNCPTR (gst_png_parse_handle_frame);
79   parse_class->pre_push_frame =
80       GST_DEBUG_FUNCPTR (gst_png_parse_pre_push_frame);
81 }
82 
83 static void
gst_png_parse_init(GstPngParse * pngparse)84 gst_png_parse_init (GstPngParse * pngparse)
85 {
86   GST_PAD_SET_ACCEPT_INTERSECT (GST_BASE_PARSE_SINK_PAD (pngparse));
87   GST_PAD_SET_ACCEPT_TEMPLATE (GST_BASE_PARSE_SINK_PAD (pngparse));
88 }
89 
90 static gboolean
gst_png_parse_start(GstBaseParse * parse)91 gst_png_parse_start (GstBaseParse * parse)
92 {
93   GstPngParse *pngparse = GST_PNG_PARSE (parse);
94 
95   GST_DEBUG_OBJECT (pngparse, "start");
96 
97   /* the start code and at least 2 empty frames (IHDR and IEND) */
98   gst_base_parse_set_min_frame_size (parse, 8 + 12 + 12);
99 
100   pngparse->width = 0;
101   pngparse->height = 0;
102 
103   pngparse->sent_codec_tag = FALSE;
104 
105   return TRUE;
106 }
107 
108 static gboolean
gst_png_parse_event(GstBaseParse * parse,GstEvent * event)109 gst_png_parse_event (GstBaseParse * parse, GstEvent * event)
110 {
111   gboolean res;
112 
113   res = GST_BASE_PARSE_CLASS (parent_class)->sink_event (parse, event);
114 
115   switch (GST_EVENT_TYPE (event)) {
116     case GST_EVENT_FLUSH_STOP:
117       /* the start code and at least 2 empty frames (IHDR and IEND) */
118       gst_base_parse_set_min_frame_size (parse, 8 + 12 + 12);
119       break;
120     default:
121       break;
122   }
123 
124   return res;
125 }
126 
127 static GstFlowReturn
gst_png_parse_handle_frame(GstBaseParse * parse,GstBaseParseFrame * frame,gint * skipsize)128 gst_png_parse_handle_frame (GstBaseParse * parse,
129     GstBaseParseFrame * frame, gint * skipsize)
130 {
131   GstPngParse *pngparse = GST_PNG_PARSE (parse);
132   GstMapInfo map;
133   GstByteReader reader;
134   GstFlowReturn ret = GST_FLOW_OK;
135   guint64 signature;
136   guint width = 0, height = 0;
137 
138   gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
139   gst_byte_reader_init (&reader, map.data, map.size);
140 
141   if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
142     goto beach;
143 
144   if (signature != PNG_SIGNATURE) {
145     for (;;) {
146       guint offset;
147 
148       offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
149           0x89504E47, 0, gst_byte_reader_get_remaining (&reader));
150 
151       if (offset == -1) {
152         *skipsize = gst_byte_reader_get_remaining (&reader) - 4;
153         goto beach;
154       }
155 
156       gst_byte_reader_skip (&reader, offset);
157 
158       if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
159         goto beach;
160 
161       if (signature == PNG_SIGNATURE) {
162         /* We're skipping, go out, we'll be back */
163         *skipsize = gst_byte_reader_get_pos (&reader);
164         goto beach;
165       }
166       gst_byte_reader_skip (&reader, 4);
167     }
168   }
169 
170   gst_byte_reader_skip (&reader, 8);
171 
172   for (;;) {
173     guint32 length;
174     guint32 code;
175 
176     if (!gst_byte_reader_get_uint32_be (&reader, &length))
177       goto beach;
178     if (!gst_byte_reader_get_uint32_le (&reader, &code))
179       goto beach;
180 
181     GST_TRACE_OBJECT (parse, "%" GST_FOURCC_FORMAT " chunk, %u bytes",
182         GST_FOURCC_ARGS (code), length);
183 
184     if (code == GST_MAKE_FOURCC ('I', 'H', 'D', 'R')) {
185       if (!gst_byte_reader_get_uint32_be (&reader, &width))
186         goto beach;
187       if (!gst_byte_reader_get_uint32_be (&reader, &height))
188         goto beach;
189       length -= 8;
190     } else if (code == GST_MAKE_FOURCC ('I', 'D', 'A', 'T')) {
191       gst_base_parse_set_min_frame_size (parse,
192           gst_byte_reader_get_pos (&reader) + 4 + length + 12);
193     }
194 
195     if (!gst_byte_reader_skip (&reader, length + 4))
196       goto beach;
197 
198     if (code == GST_MAKE_FOURCC ('I', 'E', 'N', 'D')) {
199       /* the start code and at least 2 empty frames (IHDR and IEND) */
200       gst_base_parse_set_min_frame_size (parse, 8 + 12 + 12);
201 
202       if (pngparse->width != width || pngparse->height != height) {
203         GstCaps *caps, *sink_caps;
204 
205         pngparse->height = height;
206         pngparse->width = width;
207 
208         caps = gst_caps_new_simple ("image/png",
209             "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
210 
211         sink_caps =
212             gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (pngparse));
213 
214         if (sink_caps) {
215           GstStructure *st;
216           gint fr_num, fr_denom;
217 
218           st = gst_caps_get_structure (sink_caps, 0);
219           if (st
220               && gst_structure_get_fraction (st, "framerate", &fr_num,
221                   &fr_denom)) {
222             gst_caps_set_simple (caps,
223                 "framerate", GST_TYPE_FRACTION, fr_num, fr_denom, NULL);
224           } else {
225             GST_WARNING_OBJECT (pngparse, "No framerate set");
226           }
227 
228           gst_caps_unref (sink_caps);
229         }
230 
231         if (!gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps))
232           ret = GST_FLOW_NOT_NEGOTIATED;
233 
234         gst_caps_unref (caps);
235 
236         if (ret != GST_FLOW_OK)
237           goto beach;
238       }
239 
240 
241       gst_buffer_unmap (frame->buffer, &map);
242       return gst_base_parse_finish_frame (parse, frame,
243           gst_byte_reader_get_pos (&reader));
244     }
245   }
246 
247 beach:
248 
249   gst_buffer_unmap (frame->buffer, &map);
250 
251   return ret;
252 }
253 
254 static GstFlowReturn
gst_png_parse_pre_push_frame(GstBaseParse * parse,GstBaseParseFrame * frame)255 gst_png_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
256 {
257   GstPngParse *pngparse = GST_PNG_PARSE (parse);
258 
259   if (!pngparse->sent_codec_tag) {
260     GstTagList *taglist;
261     GstCaps *caps;
262 
263     /* codec tag */
264     caps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (parse));
265     if (G_UNLIKELY (caps == NULL)) {
266       if (GST_PAD_IS_FLUSHING (GST_BASE_PARSE_SRC_PAD (parse))) {
267         GST_INFO_OBJECT (parse, "Src pad is flushing");
268         return GST_FLOW_FLUSHING;
269       } else {
270         GST_INFO_OBJECT (parse, "Src pad is not negotiated!");
271         return GST_FLOW_NOT_NEGOTIATED;
272       }
273     }
274 
275     taglist = gst_tag_list_new_empty ();
276     gst_pb_utils_add_codec_description_to_tag_list (taglist,
277         GST_TAG_VIDEO_CODEC, caps);
278     gst_caps_unref (caps);
279 
280     gst_base_parse_merge_tags (parse, taglist, GST_TAG_MERGE_REPLACE);
281     gst_tag_list_unref (taglist);
282 
283     /* also signals the end of first-frame processing */
284     pngparse->sent_codec_tag = TRUE;
285   }
286 
287   return GST_FLOW_OK;
288 }
289