1 /* GStreamer DVD subtitle parser
2  * Copyright (C) 2007 Mark Nauwelaerts <mnauw@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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <string.h>
25 #include <gst/gst.h>
26 #include "gstdvdsubparse.h"
27 
28 GST_DEBUG_CATEGORY_STATIC (dvdsubparse_debug);
29 #define GST_CAT_DEFAULT   dvdsubparse_debug
30 
31 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
32     GST_PAD_SRC,
33     GST_PAD_ALWAYS,
34     GST_STATIC_CAPS ("subpicture/x-dvd, parsed=(boolean)true")
35     );
36 
37 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
38     GST_PAD_SINK,
39     GST_PAD_ALWAYS,
40     GST_STATIC_CAPS ("subpicture/x-dvd")
41     );
42 
43 static void gst_dvd_sub_parse_finalize (GObject * object);
44 
45 static void gst_dvd_sub_parse_reset (GstDvdSubParse * parse);
46 
47 static gboolean gst_dvd_sub_parse_event (GstPad * pad, GstObject * parent,
48     GstEvent * event);
49 static GstFlowReturn gst_dvd_sub_parse_chain (GstPad * pad, GstObject * parent,
50     GstBuffer * buf);
51 
52 static GstStateChangeReturn gst_dvd_sub_parse_change_state (GstElement *
53     element, GstStateChange transition);
54 
55 #define gst_dvd_sub_parse_parent_class parent_class
56 G_DEFINE_TYPE (GstDvdSubParse, gst_dvd_sub_parse, GST_TYPE_ELEMENT);
57 
58 static void
gst_dvd_sub_parse_class_init(GstDvdSubParseClass * klass)59 gst_dvd_sub_parse_class_init (GstDvdSubParseClass * klass)
60 {
61   GObjectClass *gobject_class;
62   GstElementClass *gstelement_class;
63 
64   gobject_class = (GObjectClass *) klass;
65   gstelement_class = (GstElementClass *) klass;
66 
67   gobject_class->finalize = gst_dvd_sub_parse_finalize;
68 
69   GST_DEBUG_CATEGORY_INIT (dvdsubparse_debug, "dvdsubparse", 0,
70       "DVD subtitle parser");
71 
72   gstelement_class->change_state =
73       GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_change_state);
74 
75   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
76   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
77 
78   gst_element_class_set_static_metadata (gstelement_class,
79       "DVD subtitle parser", "Codec/Parser/Subtitle",
80       "Parses and packetizes DVD subtitle streams",
81       "Mark Nauwelaerts <mnauw@users.sourceforge.net>");
82 }
83 
84 static void
gst_dvd_sub_parse_finalize(GObject * object)85 gst_dvd_sub_parse_finalize (GObject * object)
86 {
87   GstDvdSubParse *parse = GST_DVD_SUB_PARSE (object);
88 
89   g_object_unref (parse->adapter);
90   parse->adapter = NULL;
91 
92   G_OBJECT_CLASS (parent_class)->finalize (object);
93 }
94 
95 static void
gst_dvd_sub_parse_init(GstDvdSubParse * parse)96 gst_dvd_sub_parse_init (GstDvdSubParse * parse)
97 {
98   parse->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
99   gst_pad_set_chain_function (parse->sinkpad,
100       GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_chain));
101   gst_pad_set_event_function (parse->sinkpad,
102       GST_DEBUG_FUNCPTR (gst_dvd_sub_parse_event));
103   gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
104 
105   parse->srcpad = gst_pad_new_from_static_template (&src_template, "src");
106   gst_pad_use_fixed_caps (parse->srcpad);
107   gst_pad_set_caps (parse->srcpad,
108       gst_static_pad_template_get_caps (&src_template));
109   gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
110 
111   /* remainder */
112   parse->adapter = gst_adapter_new ();
113   gst_dvd_sub_parse_reset (parse);
114 }
115 
116 static void
gst_dvd_sub_parse_reset(GstDvdSubParse * parse)117 gst_dvd_sub_parse_reset (GstDvdSubParse * parse)
118 {
119   parse->needed = 0;
120   parse->stamp = GST_CLOCK_TIME_NONE;
121   gst_adapter_clear (parse->adapter);
122 }
123 
124 static gboolean
gst_dvd_sub_parse_event(GstPad * pad,GstObject * parent,GstEvent * event)125 gst_dvd_sub_parse_event (GstPad * pad, GstObject * parent, GstEvent * event)
126 {
127   GstDvdSubParse *parse;
128   gboolean ret;
129 
130   parse = GST_DVD_SUB_PARSE (parent);
131 
132   switch (GST_EVENT_TYPE (event)) {
133     case GST_EVENT_CAPS:
134     {
135       GstCaps *caps;
136 
137       gst_event_unref (event);
138       caps = gst_static_pad_template_get_caps (&src_template);
139       gst_pad_push_event (parse->srcpad, gst_event_new_caps (caps));
140       gst_caps_unref (caps);
141       ret = TRUE;
142       break;
143     }
144     case GST_EVENT_FLUSH_STOP:
145       gst_dvd_sub_parse_reset (parse);
146       /* fall-through */
147     default:
148       ret = gst_pad_event_default (pad, parent, event);
149       break;
150   }
151 
152   return ret;
153 }
154 
155 
156 static GstFlowReturn
gst_dvd_sub_parse_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)157 gst_dvd_sub_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
158 {
159   GstDvdSubParse *parse = GST_DVD_SUB_PARSE (parent);
160   GstAdapter *adapter;
161   GstBuffer *outbuf = NULL;
162   GstFlowReturn ret = GST_FLOW_OK;
163 
164   adapter = parse->adapter;
165 
166   GST_LOG_OBJECT (parse, "%" G_GSIZE_FORMAT " bytes, ts: %" GST_TIME_FORMAT,
167       gst_buffer_get_size (buf), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
168 
169   gst_adapter_push (adapter, buf);
170 
171   if (!parse->needed) {
172     guint8 data[2];
173 
174     gst_adapter_copy (adapter, data, 0, 2);
175     parse->needed = GST_READ_UINT16_BE (data);
176   }
177 
178   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
179     if (GST_CLOCK_TIME_IS_VALID (parse->stamp))
180       /* normally, we expect only the first fragment to carry a timestamp */
181       GST_WARNING_OBJECT (parse, "Received more timestamps than expected.");
182     else
183       parse->stamp = GST_BUFFER_TIMESTAMP (buf);
184   }
185 
186   if (parse->needed) {
187     guint av;
188 
189     av = gst_adapter_available (adapter);
190     if (av >= parse->needed) {
191       if (av > parse->needed) {
192         /* normally, we expect several fragment, boundary aligned */
193         GST_WARNING_OBJECT (parse, "Unexpected: needed %d, "
194             "but more (%d) is available.", parse->needed, av);
195       }
196       outbuf = gst_adapter_take_buffer (adapter, parse->needed);
197       /* decorate buffer */
198       GST_BUFFER_TIMESTAMP (outbuf) = parse->stamp;
199       /* reset state */
200       parse->stamp = GST_CLOCK_TIME_NONE;
201       parse->needed = 0;
202       /* and send along */
203       ret = gst_pad_push (parse->srcpad, outbuf);
204     }
205   }
206 
207   return ret;
208 }
209 
210 static GstStateChangeReturn
gst_dvd_sub_parse_change_state(GstElement * element,GstStateChange transition)211 gst_dvd_sub_parse_change_state (GstElement * element, GstStateChange transition)
212 {
213   GstDvdSubParse *parse = GST_DVD_SUB_PARSE (element);
214   GstStateChangeReturn ret;
215 
216   switch (transition) {
217     case GST_STATE_CHANGE_NULL_TO_READY:
218     case GST_STATE_CHANGE_READY_TO_PAUSED:
219       break;
220     case GST_STATE_CHANGE_PAUSED_TO_READY:
221       break;
222     default:
223       break;
224   }
225 
226   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
227   if (ret != GST_STATE_CHANGE_SUCCESS)
228     return ret;
229 
230   switch (transition) {
231     case GST_STATE_CHANGE_PAUSED_TO_READY:
232       gst_dvd_sub_parse_reset (parse);
233       break;
234     default:
235       break;
236   }
237 
238   return GST_STATE_CHANGE_SUCCESS;
239 }
240