1 /* GStreamer
2 * Copyright (C) 2011 David Schleef <ds@entropywave.com>
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 Street, Suite 500,
17 * Boston, MA 02110-1335, USA.
18 */
19 /**
20 * SECTION:element-gstintersubsrc
21 * @title: gstintersubsrc
22 *
23 * The intersubsrc element is a subtitle source element. It is used
24 * in connection with a intersubsink element in a different pipeline,
25 * similar to interaudiosink and interaudiosrc.
26 *
27 * ## Example launch line
28 * |[
29 * gst-launch-1.0 -v intersubsrc ! kateenc ! oggmux ! filesink location=out.ogv
30 * ]|
31 *
32 * The intersubsrc element cannot be used effectively with gst-launch-1.0,
33 * as it requires a second pipeline in the application to send subtitles.
34 * See the gstintertest.c example in the gst-plugins-bad source code for
35 * more details.
36 *
37 */
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 #include <gst/gst.h>
44 #include <gst/base/gstbasesrc.h>
45 #include "gstintersubsrc.h"
46
47 GST_DEBUG_CATEGORY_STATIC (gst_inter_sub_src_debug_category);
48 #define GST_CAT_DEFAULT gst_inter_sub_src_debug_category
49
50 /* prototypes */
51 static void gst_inter_sub_src_set_property (GObject * object,
52 guint property_id, const GValue * value, GParamSpec * pspec);
53 static void gst_inter_sub_src_get_property (GObject * object,
54 guint property_id, GValue * value, GParamSpec * pspec);
55 static void gst_inter_sub_src_finalize (GObject * object);
56
57 static gboolean gst_inter_sub_src_start (GstBaseSrc * src);
58 static gboolean gst_inter_sub_src_stop (GstBaseSrc * src);
59 static void
60 gst_inter_sub_src_get_times (GstBaseSrc * src, GstBuffer * buffer,
61 GstClockTime * start, GstClockTime * end);
62 static GstFlowReturn
63 gst_inter_sub_src_create (GstBaseSrc * src, guint64 offset, guint size,
64 GstBuffer ** buf);
65
66 enum
67 {
68 PROP_0,
69 PROP_CHANNEL
70 };
71
72 #define DEFAULT_CHANNEL ("default")
73
74 /* pad templates */
75 static GstStaticPadTemplate gst_inter_sub_src_src_template =
76 GST_STATIC_PAD_TEMPLATE ("src",
77 GST_PAD_SRC,
78 GST_PAD_ALWAYS,
79 GST_STATIC_CAPS ("application/unknown")
80 );
81
82 /* class initialization */
83 #define parent_class gst_inter_sub_src_parent_class
84 G_DEFINE_TYPE (GstInterSubSrc, gst_inter_sub_src, GST_TYPE_BASE_SRC);
85
86 static void
gst_inter_sub_src_class_init(GstInterSubSrcClass * klass)87 gst_inter_sub_src_class_init (GstInterSubSrcClass * klass)
88 {
89 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
90 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
91 GstBaseSrcClass *base_src_class = GST_BASE_SRC_CLASS (klass);
92
93 GST_DEBUG_CATEGORY_INIT (gst_inter_sub_src_debug_category, "intersubsrc", 0,
94 "debug category for intersubsrc element");
95
96 gst_element_class_add_static_pad_template (element_class,
97 &gst_inter_sub_src_src_template);
98
99 gst_element_class_set_static_metadata (element_class,
100 "Internal subtitle source",
101 "Source/Subtitle",
102 "Virtual subtitle source for internal process communication",
103 "David Schleef <ds@schleef.org>");
104
105 gobject_class->set_property = gst_inter_sub_src_set_property;
106 gobject_class->get_property = gst_inter_sub_src_get_property;
107 gobject_class->finalize = gst_inter_sub_src_finalize;
108 base_src_class->start = GST_DEBUG_FUNCPTR (gst_inter_sub_src_start);
109 base_src_class->stop = GST_DEBUG_FUNCPTR (gst_inter_sub_src_stop);
110 base_src_class->get_times = GST_DEBUG_FUNCPTR (gst_inter_sub_src_get_times);
111 base_src_class->create = GST_DEBUG_FUNCPTR (gst_inter_sub_src_create);
112
113 g_object_class_install_property (gobject_class, PROP_CHANNEL,
114 g_param_spec_string ("channel", "Channel",
115 "Channel name to match inter src and sink elements",
116 DEFAULT_CHANNEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
117 }
118
119 static void
gst_inter_sub_src_init(GstInterSubSrc * intersubsrc)120 gst_inter_sub_src_init (GstInterSubSrc * intersubsrc)
121 {
122 gst_base_src_set_format (GST_BASE_SRC (intersubsrc), GST_FORMAT_TIME);
123 gst_base_src_set_live (GST_BASE_SRC (intersubsrc), TRUE);
124
125 intersubsrc->channel = g_strdup (DEFAULT_CHANNEL);
126 }
127
128 void
gst_inter_sub_src_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)129 gst_inter_sub_src_set_property (GObject * object, guint property_id,
130 const GValue * value, GParamSpec * pspec)
131 {
132 GstInterSubSrc *intersubsrc = GST_INTER_SUB_SRC (object);
133
134 switch (property_id) {
135 case PROP_CHANNEL:
136 g_free (intersubsrc->channel);
137 intersubsrc->channel = g_value_dup_string (value);
138 break;
139 default:
140 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
141 break;
142 }
143 }
144
145 void
gst_inter_sub_src_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)146 gst_inter_sub_src_get_property (GObject * object, guint property_id,
147 GValue * value, GParamSpec * pspec)
148 {
149 GstInterSubSrc *intersubsrc = GST_INTER_SUB_SRC (object);
150
151 switch (property_id) {
152 case PROP_CHANNEL:
153 g_value_set_string (value, intersubsrc->channel);
154 break;
155 default:
156 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
157 break;
158 }
159 }
160
161 static void
gst_inter_sub_src_finalize(GObject * object)162 gst_inter_sub_src_finalize (GObject * object)
163 {
164 GstInterSubSrc *intersubsrc = GST_INTER_SUB_SRC (object);
165
166 g_free (intersubsrc->channel);
167 intersubsrc->channel = NULL;
168
169 G_OBJECT_CLASS (parent_class)->finalize (object);
170 }
171
172 static gboolean
gst_inter_sub_src_start(GstBaseSrc * src)173 gst_inter_sub_src_start (GstBaseSrc * src)
174 {
175 GstInterSubSrc *intersubsrc = GST_INTER_SUB_SRC (src);
176
177 GST_DEBUG_OBJECT (intersubsrc, "start");
178
179 intersubsrc->surface = gst_inter_surface_get (intersubsrc->channel);
180
181 return TRUE;
182 }
183
184 static gboolean
gst_inter_sub_src_stop(GstBaseSrc * src)185 gst_inter_sub_src_stop (GstBaseSrc * src)
186 {
187 GstInterSubSrc *intersubsrc = GST_INTER_SUB_SRC (src);
188
189 GST_DEBUG_OBJECT (intersubsrc, "stop");
190
191 gst_inter_surface_unref (intersubsrc->surface);
192 intersubsrc->surface = NULL;
193
194 return TRUE;
195 }
196
197 static void
gst_inter_sub_src_get_times(GstBaseSrc * src,GstBuffer * buffer,GstClockTime * start,GstClockTime * end)198 gst_inter_sub_src_get_times (GstBaseSrc * src, GstBuffer * buffer,
199 GstClockTime * start, GstClockTime * end)
200 {
201 GST_DEBUG_OBJECT (src, "get_times");
202
203 /* for live sources, sync on the timestamp of the buffer */
204 if (gst_base_src_is_live (src)) {
205 GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer);
206
207 if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
208 /* get duration to calculate end time */
209 GstClockTime duration = GST_BUFFER_DURATION (buffer);
210
211 if (GST_CLOCK_TIME_IS_VALID (duration)) {
212 *end = timestamp + duration;
213 }
214 *start = timestamp;
215 }
216 } else {
217 *start = -1;
218 *end = -1;
219 }
220 }
221
222 static GstFlowReturn
gst_inter_sub_src_create(GstBaseSrc * src,guint64 offset,guint size,GstBuffer ** buf)223 gst_inter_sub_src_create (GstBaseSrc * src, guint64 offset, guint size,
224 GstBuffer ** buf)
225 {
226 GstInterSubSrc *intersubsrc = GST_INTER_SUB_SRC (src);
227 GstBuffer *buffer;
228
229 GST_DEBUG_OBJECT (intersubsrc, "create");
230
231 buffer = NULL;
232
233 g_mutex_lock (&intersubsrc->surface->mutex);
234 if (intersubsrc->surface->sub_buffer) {
235 buffer = gst_buffer_ref (intersubsrc->surface->sub_buffer);
236 //intersubsrc->surface->sub_buffer_count++;
237 //if (intersubsrc->surface->sub_buffer_count >= 30) {
238 gst_buffer_unref (intersubsrc->surface->sub_buffer);
239 intersubsrc->surface->sub_buffer = NULL;
240 //}
241 }
242 g_mutex_unlock (&intersubsrc->surface->mutex);
243
244 if (buffer == NULL) {
245 GstMapInfo map;
246
247 buffer = gst_buffer_new_and_alloc (1);
248
249 gst_buffer_map (buffer, &map, GST_MAP_WRITE);
250 map.data[0] = 0;
251 gst_buffer_unmap (buffer, &map);
252 }
253
254 buffer = gst_buffer_make_writable (buffer);
255
256 /* FIXME: does this make sense? Rate is always 0 */
257 #if 0
258 GST_BUFFER_TIMESTAMP (buffer) =
259 gst_util_uint64_scale_int (GST_SECOND, intersubsrc->n_frames,
260 intersubsrc->rate);
261 GST_DEBUG_OBJECT (intersubsrc, "create ts %" GST_TIME_FORMAT,
262 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
263 GST_BUFFER_DURATION (buffer) =
264 gst_util_uint64_scale_int (GST_SECOND, (intersubsrc->n_frames + 1),
265 intersubsrc->rate) - GST_BUFFER_TIMESTAMP (buffer);
266 #endif
267 GST_BUFFER_OFFSET (buffer) = intersubsrc->n_frames;
268 GST_BUFFER_OFFSET_END (buffer) = -1;
269 GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DISCONT);
270 if (intersubsrc->n_frames == 0) {
271 GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
272 }
273 intersubsrc->n_frames++;
274
275 *buf = buffer;
276
277 return GST_FLOW_OK;
278 }
279