Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / gst / subenc / gstwebvttenc.c
1 /* GStreamer
2  * Copyright (C) <2008> Thijs Vermeir <thijsvermeir@gmail.com>
3  * Copyright (C) 2011 David Schleef <ds@schleef.org>
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., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include "string.h"
25
26 #include "gstwebvttenc.h"
27 #include <gst/controller/gstcontroller.h>
28
29 GST_DEBUG_CATEGORY_STATIC (webvttenc_debug);
30 #define GST_CAT_DEFAULT webvttenc_debug
31
32 enum
33 {
34   ARG_0,
35   ARG_TIMESTAMP,
36   ARG_DURATION
37 };
38
39 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
40     GST_PAD_SRC,
41     GST_PAD_ALWAYS,
42     GST_STATIC_CAPS ("text/webvtt"));
43
44 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
45     GST_PAD_SINK,
46     GST_PAD_ALWAYS,
47     GST_STATIC_CAPS ("text/plain; text/x-pango-markup"));
48
49 static GstFlowReturn gst_webvtt_enc_chain (GstPad * pad, GstBuffer * buf);
50 static gchar *gst_webvtt_enc_timeconvertion (GstWebvttEnc * webvttenc,
51     GstBuffer * buf);
52 static gchar *gst_webvtt_enc_timestamp_to_string (GstClockTime timestamp);
53 static void gst_webvtt_enc_get_property (GObject * object, guint prop_id,
54     GValue * value, GParamSpec * pspec);
55 static void gst_webvtt_enc_reset (GstWebvttEnc * webvttenc);
56 static void gst_webvtt_enc_set_property (GObject * object, guint prop_id,
57     const GValue * value, GParamSpec * pspec);
58
59 GST_BOILERPLATE (GstWebvttEnc, gst_webvtt_enc, GstElement, GST_TYPE_ELEMENT);
60
61 static gchar *
62 gst_webvtt_enc_timestamp_to_string (GstClockTime timestamp)
63 {
64   guint h, m, s, ms;
65
66   h = timestamp / (3600 * GST_SECOND);
67
68   timestamp -= h * 3600 * GST_SECOND;
69   m = timestamp / (60 * GST_SECOND);
70
71   timestamp -= m * 60 * GST_SECOND;
72   s = timestamp / GST_SECOND;
73
74   timestamp -= s * GST_SECOND;
75   ms = timestamp / GST_MSECOND;
76
77   return g_strdup_printf ("%02d:%02d:%02d.%03d", h, m, s, ms);
78 }
79
80 static gchar *
81 gst_webvtt_enc_timeconvertion (GstWebvttEnc * webvttenc, GstBuffer * buf)
82 {
83   gchar *start_time;
84   gchar *stop_time;
85   gchar *string;
86
87   start_time = gst_webvtt_enc_timestamp_to_string (GST_BUFFER_TIMESTAMP (buf) +
88       webvttenc->timestamp);
89   if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buf))) {
90     stop_time = gst_webvtt_enc_timestamp_to_string (GST_BUFFER_TIMESTAMP (buf) +
91         webvttenc->timestamp + GST_BUFFER_DURATION (buf) + webvttenc->duration);
92   } else {
93     stop_time = gst_webvtt_enc_timestamp_to_string (GST_BUFFER_TIMESTAMP (buf) +
94         webvttenc->timestamp + webvttenc->duration);
95   }
96   string = g_strdup_printf ("%s --> %s\n", start_time, stop_time);
97
98   g_free (start_time);
99   g_free (stop_time);
100   return string;
101 }
102
103 static GstFlowReturn
104 gst_webvtt_enc_chain (GstPad * pad, GstBuffer * buf)
105 {
106   GstWebvttEnc *webvttenc;
107   GstBuffer *new_buffer;
108   gchar *timing;
109   GstFlowReturn ret;
110
111   webvttenc = GST_WEBVTT_ENC (gst_pad_get_parent_element (pad));
112
113   if (!webvttenc->pushed_header) {
114     const char *header = "WEBVTT\n\n";
115
116     new_buffer = gst_buffer_new_and_alloc (strlen (header));
117     memcpy (GST_BUFFER_DATA (new_buffer), header, strlen (header));
118
119     GST_BUFFER_TIMESTAMP (new_buffer) = GST_CLOCK_TIME_NONE;
120     GST_BUFFER_DURATION (new_buffer) = GST_CLOCK_TIME_NONE;
121
122     ret = gst_pad_push (webvttenc->srcpad, new_buffer);
123     if (ret != GST_FLOW_OK) {
124       goto out;
125     }
126
127     webvttenc->pushed_header = TRUE;
128   }
129
130   gst_object_sync_values (G_OBJECT (webvttenc), GST_BUFFER_TIMESTAMP (buf));
131
132   timing = gst_webvtt_enc_timeconvertion (webvttenc, buf);
133   new_buffer =
134       gst_buffer_new_and_alloc (strlen (timing) + GST_BUFFER_SIZE (buf) + 1);
135   memcpy (GST_BUFFER_DATA (new_buffer), timing, strlen (timing));
136   memcpy (GST_BUFFER_DATA (new_buffer) + strlen (timing), GST_BUFFER_DATA (buf),
137       GST_BUFFER_SIZE (buf));
138   memcpy (GST_BUFFER_DATA (new_buffer) + GST_BUFFER_SIZE (new_buffer) - 1,
139       "\n", 1);
140   g_free (timing);
141
142   GST_BUFFER_TIMESTAMP (new_buffer) = GST_BUFFER_TIMESTAMP (buf);
143   GST_BUFFER_DURATION (new_buffer) = GST_BUFFER_DURATION (buf);
144
145
146   ret = gst_pad_push (webvttenc->srcpad, new_buffer);
147
148 out:
149   gst_buffer_unref (buf);
150   gst_object_unref (webvttenc);
151
152   return ret;
153 }
154
155 static void
156 gst_webvtt_enc_base_init (gpointer klass)
157 {
158   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
159
160   gst_element_class_add_static_pad_template (element_class,
161       &sink_template);
162   gst_element_class_add_static_pad_template (element_class, &src_template);
163
164   gst_element_class_set_details_simple (element_class,
165       "WebVTT encoder", "Codec/Encoder/Subtitle",
166       "WebVTT subtitle encoder", "David Schleef <ds@schleef.org>");
167 }
168
169 static void
170 gst_webvtt_enc_reset (GstWebvttEnc * webvttenc)
171 {
172   webvttenc->counter = 1;
173 }
174
175 static GstStateChangeReturn
176 gst_webvtt_enc_change_state (GstElement * element, GstStateChange transition)
177 {
178   GstStateChangeReturn ret;
179   GstWebvttEnc *webvttenc = GST_WEBVTT_ENC (element);
180
181   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
182   if (ret == GST_STATE_CHANGE_FAILURE)
183     return ret;
184
185   switch (transition) {
186     case GST_STATE_CHANGE_PAUSED_TO_READY:
187       gst_webvtt_enc_reset (webvttenc);
188       break;
189     default:
190       break;
191   }
192
193   return ret;
194 }
195
196 static void
197 gst_webvtt_enc_get_property (GObject * object,
198     guint prop_id, GValue * value, GParamSpec * pspec)
199 {
200   GstWebvttEnc *webvttenc;
201
202   webvttenc = GST_WEBVTT_ENC (object);
203
204   switch (prop_id) {
205     case ARG_TIMESTAMP:
206       g_value_set_int64 (value, webvttenc->timestamp);
207       break;
208     case ARG_DURATION:
209       g_value_set_int64 (value, webvttenc->duration);
210       break;
211     default:
212       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
213       break;
214   }
215 }
216
217 static void
218 gst_webvtt_enc_set_property (GObject * object,
219     guint prop_id, const GValue * value, GParamSpec * pspec)
220 {
221
222   GstWebvttEnc *webvttenc;
223
224   webvttenc = GST_WEBVTT_ENC (object);
225
226   switch (prop_id) {
227     case ARG_TIMESTAMP:
228       webvttenc->timestamp = g_value_get_int64 (value);
229       break;
230     case ARG_DURATION:
231       webvttenc->duration = g_value_get_int64 (value);
232       break;
233     default:
234       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
235       break;
236   }
237 }
238
239 static void
240 gst_webvtt_enc_class_init (GstWebvttEncClass * klass)
241 {
242   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
243   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
244
245   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_webvtt_enc_set_property);
246   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_webvtt_enc_get_property);
247
248   element_class->change_state = GST_DEBUG_FUNCPTR (gst_webvtt_enc_change_state);
249
250   g_object_class_install_property (gobject_class, ARG_TIMESTAMP,
251       g_param_spec_int64 ("timestamp", "Offset for the starttime",
252           "Offset for the starttime for the subtitles", G_MININT64, G_MAXINT64,
253           0,
254           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
255   g_object_class_install_property (gobject_class, ARG_DURATION,
256       g_param_spec_int64 ("duration", "Offset for the duration",
257           "Offset for the duration of the subtitles", G_MININT64, G_MAXINT64,
258           0,
259           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
260
261   GST_DEBUG_CATEGORY_INIT (webvttenc_debug, "webvttenc", 0,
262       "SubRip subtitle encoder");
263 }
264
265 static void
266 gst_webvtt_enc_init (GstWebvttEnc * webvttenc, GstWebvttEncClass * klass)
267 {
268   gst_webvtt_enc_reset (webvttenc);
269
270   webvttenc->srcpad = gst_pad_new_from_static_template (&src_template, "src");
271   gst_element_add_pad (GST_ELEMENT (webvttenc), webvttenc->srcpad);
272   webvttenc->sinkpad =
273       gst_pad_new_from_static_template (&sink_template, "sink");
274   gst_element_add_pad (GST_ELEMENT (webvttenc), webvttenc->sinkpad);
275   gst_pad_set_chain_function (webvttenc->sinkpad, gst_webvtt_enc_chain);
276 }