1 /* GStreamer bz2 decoder
2  * Copyright (C) 2006 Lutz Müller <lutz topfrose de>
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 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include "gstbz2dec.h"
23 
24 #include <gst/base/gsttypefindhelper.h>
25 
26 #include <bzlib.h>
27 #include <string.h>
28 
29 GST_DEBUG_CATEGORY_STATIC (bz2dec_debug);
30 #define GST_CAT_DEFAULT bz2dec_debug
31 
32 static GstStaticPadTemplate sink_template =
33 GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
34     GST_STATIC_CAPS ("application/x-bzip"));
35 static GstStaticPadTemplate src_template =
36 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
37     GST_STATIC_CAPS_ANY);
38 
39 struct _GstBz2dec
40 {
41   GstElement parent;
42 
43   GstPad *sink;
44   GstPad *src;
45 
46   /* Properties */
47   guint first_buffer_size;
48   guint buffer_size;
49 
50   gboolean ready;
51   bz_stream stream;
52   guint64 offset;
53 };
54 
55 struct _GstBz2decClass
56 {
57   GstElementClass parent_class;
58 };
59 
60 #define gst_bz2dec_parent_class parent_class
61 G_DEFINE_TYPE (GstBz2dec, gst_bz2dec, GST_TYPE_ELEMENT);
62 
63 #define DEFAULT_FIRST_BUFFER_SIZE 1024
64 #define DEFAULT_BUFFER_SIZE 1024
65 
66 enum
67 {
68   PROP_0,
69   PROP_FIRST_BUFFER_SIZE,
70   PROP_BUFFER_SIZE
71 };
72 
73 static void
gst_bz2dec_decompress_end(GstBz2dec * b)74 gst_bz2dec_decompress_end (GstBz2dec * b)
75 {
76   g_return_if_fail (GST_IS_BZ2DEC (b));
77 
78   if (b->ready) {
79     BZ2_bzDecompressEnd (&b->stream);
80     memset (&b->stream, 0, sizeof (b->stream));
81     b->ready = FALSE;
82   }
83 }
84 
85 static void
gst_bz2dec_decompress_init(GstBz2dec * b)86 gst_bz2dec_decompress_init (GstBz2dec * b)
87 {
88   g_return_if_fail (GST_IS_BZ2DEC (b));
89 
90   gst_bz2dec_decompress_end (b);
91   b->offset = 0;
92   switch (BZ2_bzDecompressInit (&b->stream, 0, 0)) {
93     case BZ_OK:
94       b->ready = TRUE;
95       return;
96     default:
97       b->ready = FALSE;
98       GST_ELEMENT_ERROR (b, CORE, FAILED, (NULL),
99           ("Failed to start decompression."));
100       return;
101   }
102 }
103 
104 static GstFlowReturn
gst_bz2dec_chain(GstPad * pad,GstObject * parent,GstBuffer * in)105 gst_bz2dec_chain (GstPad * pad, GstObject * parent, GstBuffer * in)
106 {
107   GstFlowReturn flow = GST_FLOW_OK;
108   GstBuffer *out;
109   GstBz2dec *b;
110   int r = BZ_OK;
111   GstMapInfo map = GST_MAP_INFO_INIT, omap;
112 
113   b = GST_BZ2DEC (parent);
114 
115   if (!b->ready)
116     goto not_ready;
117 
118   gst_buffer_map (in, &map, GST_MAP_READ);
119   b->stream.next_in = (char *) map.data;
120   b->stream.avail_in = map.size;
121 
122   do {
123     guint n;
124 
125     /* Create the output buffer */
126     out =
127         gst_buffer_new_and_alloc (b->offset ? b->buffer_size : b->
128         first_buffer_size);
129 
130     /* Decode */
131     gst_buffer_map (out, &omap, GST_MAP_WRITE);
132     b->stream.next_out = (char *) omap.data;
133     b->stream.avail_out = omap.size;
134     r = BZ2_bzDecompress (&b->stream);
135     gst_buffer_unmap (out, &omap);
136     if ((r != BZ_OK) && (r != BZ_STREAM_END))
137       goto decode_failed;
138 
139     if (b->stream.avail_out >= gst_buffer_get_size (out)) {
140       gst_buffer_unref (out);
141       break;
142     }
143     gst_buffer_resize (out, 0, gst_buffer_get_size (out) - b->stream.avail_out);
144     GST_BUFFER_OFFSET (out) =
145         b->stream.total_out_lo32 - gst_buffer_get_size (out);
146 
147     /* Configure source pad (if necessary) */
148     if (!b->offset) {
149       GstCaps *caps = NULL;
150 
151       caps = gst_type_find_helper_for_buffer (GST_OBJECT (b), out, NULL);
152       if (caps) {
153         gst_pad_set_caps (b->src, caps);
154         gst_pad_use_fixed_caps (b->src);
155         gst_caps_unref (caps);
156       } else {
157         /* FIXME: shouldn't we queue output buffers until we have a type? */
158       }
159     }
160 
161     /* Push data */
162     n = gst_buffer_get_size (out);
163     flow = gst_pad_push (b->src, out);
164     if (flow != GST_FLOW_OK)
165       break;
166     b->offset += n;
167   } while (r != BZ_STREAM_END);
168 
169 done:
170 
171   gst_buffer_unmap (in, &map);
172   gst_buffer_unref (in);
173   return flow;
174 
175 /* ERRORS */
176 decode_failed:
177   {
178     GST_ELEMENT_ERROR (b, STREAM, DECODE, (NULL),
179         ("Failed to decompress data (error code %i).", r));
180     gst_bz2dec_decompress_init (b);
181     gst_buffer_unref (out);
182     flow = GST_FLOW_ERROR;
183     goto done;
184   }
185 not_ready:
186   {
187     GST_ELEMENT_ERROR (b, LIBRARY, FAILED, (NULL), ("Decompressor not ready."));
188     flow = GST_FLOW_FLUSHING;
189     goto done;
190   }
191 }
192 
193 static void
gst_bz2dec_init(GstBz2dec * b)194 gst_bz2dec_init (GstBz2dec * b)
195 {
196   b->first_buffer_size = DEFAULT_FIRST_BUFFER_SIZE;
197   b->buffer_size = DEFAULT_BUFFER_SIZE;
198 
199   b->sink = gst_pad_new_from_static_template (&sink_template, "sink");
200   gst_pad_set_chain_function (b->sink, GST_DEBUG_FUNCPTR (gst_bz2dec_chain));
201   gst_element_add_pad (GST_ELEMENT (b), b->sink);
202 
203   b->src = gst_pad_new_from_static_template (&src_template, "src");
204   gst_element_add_pad (GST_ELEMENT (b), b->src);
205   gst_pad_use_fixed_caps (b->src);
206 
207   gst_bz2dec_decompress_init (b);
208 }
209 
210 static void
gst_bz2dec_finalize(GObject * object)211 gst_bz2dec_finalize (GObject * object)
212 {
213   GstBz2dec *b = GST_BZ2DEC (object);
214 
215   gst_bz2dec_decompress_end (b);
216 
217   G_OBJECT_CLASS (parent_class)->finalize (object);
218 }
219 
220 static void
gst_bz2dec_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)221 gst_bz2dec_get_property (GObject * object, guint prop_id,
222     GValue * value, GParamSpec * pspec)
223 {
224   GstBz2dec *b = GST_BZ2DEC (object);
225 
226   switch (prop_id) {
227     case PROP_BUFFER_SIZE:
228       g_value_set_uint (value, b->buffer_size);
229       break;
230     case PROP_FIRST_BUFFER_SIZE:
231       g_value_set_uint (value, b->first_buffer_size);
232       break;
233     default:
234       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
235   }
236 }
237 
238 static void
gst_bz2dec_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)239 gst_bz2dec_set_property (GObject * object, guint prop_id,
240     const GValue * value, GParamSpec * pspec)
241 {
242   GstBz2dec *b = GST_BZ2DEC (object);
243 
244   switch (prop_id) {
245     case PROP_BUFFER_SIZE:
246       b->buffer_size = g_value_get_uint (value);
247       break;
248     case PROP_FIRST_BUFFER_SIZE:
249       b->first_buffer_size = g_value_get_uint (value);
250       break;
251     default:
252       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253   }
254 }
255 
256 static GstStateChangeReturn
gst_bz2dec_change_state(GstElement * element,GstStateChange transition)257 gst_bz2dec_change_state (GstElement * element, GstStateChange transition)
258 {
259   GstBz2dec *b = GST_BZ2DEC (element);
260   GstStateChangeReturn ret;
261 
262   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
263   if (ret != GST_STATE_CHANGE_SUCCESS)
264     return ret;
265 
266   switch (transition) {
267     case GST_STATE_CHANGE_PAUSED_TO_READY:
268       gst_bz2dec_decompress_init (b);
269       break;
270     default:
271       break;
272   }
273   return ret;
274 }
275 
276 static void
gst_bz2dec_class_init(GstBz2decClass * klass)277 gst_bz2dec_class_init (GstBz2decClass * klass)
278 {
279   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
280   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
281 
282   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bz2dec_change_state);
283 
284   gobject_class->finalize = gst_bz2dec_finalize;
285   gobject_class->get_property = gst_bz2dec_get_property;
286   gobject_class->set_property = gst_bz2dec_set_property;
287 
288   g_object_class_install_property (G_OBJECT_CLASS (klass),
289       PROP_FIRST_BUFFER_SIZE, g_param_spec_uint ("first-buffer-size",
290           "Size of first buffer", "Size of first buffer (used to determine the "
291           "mime type of the uncompressed data)", 1, G_MAXUINT,
292           DEFAULT_FIRST_BUFFER_SIZE,
293           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
294   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_SIZE,
295       g_param_spec_uint ("buffer-size", "Buffer size", "Buffer size",
296           1, G_MAXUINT, DEFAULT_BUFFER_SIZE,
297           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
298 
299   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
300   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
301   gst_element_class_set_static_metadata (gstelement_class, "BZ2 decoder",
302       "Codec/Decoder", "Decodes compressed streams",
303       "Lutz Mueller <lutz@users.sourceforge.net>");
304 
305   GST_DEBUG_CATEGORY_INIT (bz2dec_debug, "bz2dec", 0, "BZ2 decompressor");
306 }
307