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