Fix double semicolons
[platform/upstream/gstreamer.git] / gst / debugutils / gstpushfilesrc.c
1 /* GStreamer Push File Source
2  * Copyright (C) <2007> Tim-Philipp Müller <tim centricular 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 /**
21  * SECTION:element-pushfilesrc
22  * @see_also: filesrc
23  *
24  * This element is only useful for debugging purposes. It implements an URI
25  * protocol handler for the 'pushfile' protocol and behaves like a file source
26  * element that cannot be activated in pull-mode. This makes it very easy to
27  * debug demuxers or decoders that can operate both pull and push-based in
28  * connection with the playbin element (which creates a source based on the
29  * URI passed).
30  *
31  * <refsect2>
32  * <title>Example launch line</title>
33  * |[
34  * gst-launch-1.0 -m playbin uri=pushfile:///home/you/some/file.ogg
35  * ]| This plays back the given file using playbin, with the demuxer operating
36  * push-based.
37  * </refsect2>
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include "gstpushfilesrc.h"
45
46 #include <gst/gst.h>
47
48 GST_DEBUG_CATEGORY_STATIC (pushfilesrc_debug);
49 #define GST_CAT_DEFAULT pushfilesrc_debug
50
51 enum
52 {
53   PROP_0,
54   PROP_LOCATION,
55   PROP_TIME_SEGMENT,
56   PROP_STREAM_TIME,
57   PROP_START_TIME,
58   PROP_INITIAL_TIMESTAMP,
59   PROP_RATE,
60   PROP_APPLIED_RATE
61 };
62
63 #define DEFAULT_TIME_SEGMENT FALSE
64 #define DEFAULT_STREAM_TIME 0
65 #define DEFAULT_START_TIME 0
66 #define DEFAULT_INITIAL_TIMESTAMP GST_CLOCK_TIME_NONE
67 #define DEFAULT_RATE 1.0
68 #define DEFAULT_APPLIED_RATE 1.0
69
70 static void gst_push_file_src_set_property (GObject * object,
71     guint prop_id, const GValue * value, GParamSpec * pspec);
72 static void gst_push_file_src_get_property (GObject * object,
73     guint prop_id, GValue * value, GParamSpec * pspec);
74
75 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
76     GST_PAD_SRC,
77     GST_PAD_ALWAYS,
78     GST_STATIC_CAPS_ANY);
79
80 static void gst_push_file_src_uri_handler_init (gpointer g_iface,
81     gpointer iface_data);
82
83 #define gst_push_file_src_parent_class parent_class
84 G_DEFINE_TYPE_WITH_CODE (GstPushFileSrc, gst_push_file_src, GST_TYPE_BIN,
85     G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
86         gst_push_file_src_uri_handler_init));
87
88 static void
89 gst_push_file_src_dispose (GObject * obj)
90 {
91   GstPushFileSrc *src = GST_PUSH_FILE_SRC (obj);
92
93   if (src->srcpad) {
94     gst_element_remove_pad (GST_ELEMENT (src), src->srcpad);
95     src->srcpad = NULL;
96   }
97   if (src->filesrc) {
98     gst_bin_remove (GST_BIN (src), src->filesrc);
99     src->filesrc = NULL;
100   }
101
102   G_OBJECT_CLASS (parent_class)->dispose (obj);
103 }
104
105 static void
106 gst_push_file_src_class_init (GstPushFileSrcClass * g_class)
107 {
108   GObjectClass *gobject_class;
109   GstElementClass *element_class;
110
111   gobject_class = G_OBJECT_CLASS (g_class);
112   element_class = GST_ELEMENT_CLASS (g_class);
113
114   GST_DEBUG_CATEGORY_INIT (pushfilesrc_debug, "pushfilesrc", 0,
115       "pushfilesrc element");
116
117   gobject_class->dispose = gst_push_file_src_dispose;
118   gobject_class->set_property = gst_push_file_src_set_property;
119   gobject_class->get_property = gst_push_file_src_get_property;
120
121   g_object_class_install_property (gobject_class, PROP_LOCATION,
122       g_param_spec_string ("location", "File Location",
123           "Location of the file to read", NULL,
124           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
125           GST_PARAM_MUTABLE_READY));
126
127   g_object_class_install_property (gobject_class, PROP_TIME_SEGMENT,
128       g_param_spec_boolean ("time-segment", "Time Segment",
129           "Emit TIME SEGMENTS", DEFAULT_TIME_SEGMENT, G_PARAM_READWRITE));
130
131   g_object_class_install_property (gobject_class, PROP_STREAM_TIME,
132       g_param_spec_int64 ("stream-time", "Stream Time",
133           "Initial Stream Time (if time-segment TRUE)", 0, G_MAXINT64,
134           DEFAULT_STREAM_TIME, G_PARAM_READWRITE));
135
136   g_object_class_install_property (gobject_class, PROP_START_TIME,
137       g_param_spec_int64 ("start-time", "Start Time",
138           "Initial Start Time (if time-segment TRUE)", 0, G_MAXINT64,
139           DEFAULT_START_TIME, G_PARAM_READWRITE));
140
141   g_object_class_install_property (gobject_class, PROP_INITIAL_TIMESTAMP,
142       g_param_spec_uint64 ("initial-timestamp", "Initial Timestamp",
143           "Initial Buffer Timestamp (if time-segment TRUE)", 0, G_MAXUINT64,
144           DEFAULT_INITIAL_TIMESTAMP, G_PARAM_READWRITE));
145
146   g_object_class_install_property (gobject_class, PROP_RATE,
147       g_param_spec_double ("rate", "Rate", "Rate to use in TIME SEGMENT",
148           G_MINDOUBLE, G_MAXDOUBLE, DEFAULT_RATE, G_PARAM_READWRITE));
149
150   g_object_class_install_property (gobject_class, PROP_APPLIED_RATE,
151       g_param_spec_double ("applied-rate", "Applied Rate",
152           "Applied rate to use in TIME SEGMENT", G_MINDOUBLE, G_MAXDOUBLE,
153           DEFAULT_APPLIED_RATE, G_PARAM_READWRITE));
154
155   gst_element_class_add_pad_template (element_class,
156       gst_static_pad_template_get (&srctemplate));
157
158   gst_element_class_set_static_metadata (element_class, "Push File Source",
159       "Testing",
160       "Implements pushfile:// URI-handler for push-based file access",
161       "Tim-Philipp Müller <tim centricular net>");
162 }
163
164 static void
165 gst_push_file_src_set_property (GObject * object, guint prop_id,
166     const GValue * value, GParamSpec * pspec)
167 {
168   GstPushFileSrc *src = (GstPushFileSrc *) object;
169
170   switch (prop_id) {
171     case PROP_LOCATION:
172       g_object_set_property (G_OBJECT (src->filesrc), "location", value);
173       break;
174     case PROP_TIME_SEGMENT:
175       src->time_segment = g_value_get_boolean (value);
176       break;
177     case PROP_STREAM_TIME:
178       src->stream_time = g_value_get_int64 (value);
179       break;
180     case PROP_START_TIME:
181       src->start_time = g_value_get_int64 (value);
182       break;
183     case PROP_INITIAL_TIMESTAMP:
184       src->initial_timestamp = g_value_get_uint64 (value);
185       break;
186     case PROP_RATE:
187       src->rate = g_value_get_double (value);
188       break;
189     case PROP_APPLIED_RATE:
190       src->applied_rate = g_value_get_double (value);
191       break;
192     default:
193       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194       break;
195   }
196 }
197
198 static void
199 gst_push_file_src_get_property (GObject * object, guint prop_id, GValue * value,
200     GParamSpec * pspec)
201 {
202   GstPushFileSrc *src = (GstPushFileSrc *) object;
203
204   switch (prop_id) {
205     case PROP_LOCATION:
206       g_object_get_property (G_OBJECT (src->filesrc), "location", value);
207       break;
208     case PROP_TIME_SEGMENT:
209       g_value_set_boolean (value, src->time_segment);
210       break;
211     case PROP_STREAM_TIME:
212       g_value_set_int64 (value, src->stream_time);
213       break;
214     case PROP_START_TIME:
215       g_value_set_int64 (value, src->start_time);
216       break;
217     case PROP_INITIAL_TIMESTAMP:
218       g_value_set_uint64 (value, src->initial_timestamp);
219       break;
220     case PROP_RATE:
221       g_value_set_double (value, src->rate);
222       break;
223     case PROP_APPLIED_RATE:
224       g_value_set_double (value, src->applied_rate);
225       break;
226     default:
227       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
228       break;
229   }
230 }
231
232 static GstPadProbeReturn
233 gst_push_file_src_ghostpad_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
234     GstPushFileSrc * src)
235 {
236   GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);
237
238   if (src->time_segment && !src->seen_first_buffer) {
239     GST_BUFFER_TIMESTAMP (buffer) = src->initial_timestamp;
240     src->seen_first_buffer = TRUE;
241   }
242   return GST_PAD_PROBE_OK;
243 }
244
245 static GstPadProbeReturn
246 gst_push_file_src_ghostpad_event_probe (GstPad * pad, GstPadProbeInfo * info,
247     GstPushFileSrc * src)
248 {
249   GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
250
251   switch (GST_EVENT_TYPE (event)) {
252     case GST_EVENT_SEGMENT:
253     {
254       if (src->time_segment) {
255         GstSegment segment;
256         GstEvent *replacement;
257         GST_DEBUG_OBJECT (src, "Replacing outgoing segment with TIME SEGMENT");
258         gst_segment_init (&segment, GST_FORMAT_TIME);
259         segment.start = src->start_time;
260         segment.time = src->stream_time;
261         segment.rate = src->rate;
262         segment.applied_rate = src->applied_rate;
263         replacement = gst_event_new_segment (&segment);
264         gst_event_unref (event);
265         GST_PAD_PROBE_INFO_DATA (info) = replacement;
266       }
267     }
268     default:
269       break;
270   }
271   return GST_PAD_PROBE_OK;
272 }
273
274 static gboolean
275 gst_push_file_src_ghostpad_event (GstPad * pad, GstObject * parent,
276     GstEvent * event)
277 {
278   GstPushFileSrc *src = (GstPushFileSrc *) parent;
279   gboolean ret;
280
281   switch (GST_EVENT_TYPE (event)) {
282     case GST_EVENT_SEEK:
283       if (src->time_segment) {
284         /* When working in time we don't allow seeks */
285         GST_DEBUG_OBJECT (src, "Refusing seek event in TIME mode");
286         gst_event_unref (event);
287         ret = FALSE;
288         break;
289       }
290       /* PASSTHROUGH */
291     default:
292       ret = gst_pad_event_default (pad, parent, event);
293       break;
294   }
295
296   return ret;
297 }
298
299 static gboolean
300 gst_push_file_src_ghostpad_query (GstPad * pad, GstObject * parent,
301     GstQuery * query)
302 {
303   GstPushFileSrc *src = (GstPushFileSrc *) parent;
304   gboolean res;
305
306   switch (GST_QUERY_TYPE (query)) {
307     case GST_QUERY_SCHEDULING:
308       /* When working in time we don't allow seeks */
309       if (src->time_segment)
310         gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEQUENTIAL, 1, -1,
311             0);
312       else
313         gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1,
314             0);
315       gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);
316       res = TRUE;
317       break;
318     default:
319       res = gst_pad_query_default (pad, parent, query);
320       break;
321   }
322   return res;
323 }
324
325 static void
326 gst_push_file_src_init (GstPushFileSrc * src)
327 {
328   src->time_segment = DEFAULT_TIME_SEGMENT;
329   src->stream_time = DEFAULT_STREAM_TIME;
330   src->start_time = DEFAULT_START_TIME;
331   src->initial_timestamp = DEFAULT_INITIAL_TIMESTAMP;
332   src->rate = DEFAULT_RATE;
333   src->applied_rate = DEFAULT_APPLIED_RATE;
334   src->seen_first_buffer = FALSE;
335
336   src->filesrc = gst_element_factory_make ("filesrc", "real-filesrc");
337   if (src->filesrc) {
338     GstPad *pad;
339
340     gst_bin_add (GST_BIN (src), src->filesrc);
341     pad = gst_element_get_static_pad (src->filesrc, "src");
342     g_assert (pad != NULL);
343     src->srcpad = gst_ghost_pad_new ("src", pad);
344     /* FIXME^H^HCORE: try pushfile:///foo/bar.ext ! typefind ! fakesink without
345      * this and watch core bugginess (some pad stays in flushing state) */
346     gst_pad_set_query_function (src->srcpad,
347         GST_DEBUG_FUNCPTR (gst_push_file_src_ghostpad_query));
348     gst_pad_set_event_function (src->srcpad,
349         GST_DEBUG_FUNCPTR (gst_push_file_src_ghostpad_event));
350     /* Add outgoing event probe to replace segment and buffer timestamp */
351     gst_pad_add_probe (src->srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
352         (GstPadProbeCallback) gst_push_file_src_ghostpad_event_probe,
353         src, NULL);
354     gst_pad_add_probe (src->srcpad, GST_PAD_PROBE_TYPE_BUFFER,
355         (GstPadProbeCallback) gst_push_file_src_ghostpad_buffer_probe,
356         src, NULL);
357     gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
358     gst_object_unref (pad);
359   }
360 }
361
362 /*** GSTURIHANDLER INTERFACE *************************************************/
363
364 static GstURIType
365 gst_push_file_src_uri_get_type (GType type)
366 {
367   return GST_URI_SRC;
368 }
369
370 static const gchar *const *
371 gst_push_file_src_uri_get_protocols (GType type)
372 {
373   static const gchar *protocols[] = { "pushfile", NULL };
374
375   return protocols;
376 }
377
378 static gchar *
379 gst_push_file_src_uri_get_uri (GstURIHandler * handler)
380 {
381   GstPushFileSrc *src = GST_PUSH_FILE_SRC (handler);
382   gchar *fileuri, *pushfileuri;
383
384   if (src->filesrc == NULL)
385     return NULL;
386
387   fileuri = gst_uri_handler_get_uri (GST_URI_HANDLER (src->filesrc));
388   if (fileuri == NULL)
389     return NULL;
390   pushfileuri = g_strconcat ("push", fileuri, NULL);
391   g_free (fileuri);
392
393   return pushfileuri;
394 }
395
396 static gboolean
397 gst_push_file_src_uri_set_uri (GstURIHandler * handler, const gchar * uri,
398     GError ** error)
399 {
400   GstPushFileSrc *src = GST_PUSH_FILE_SRC (handler);
401
402   if (src->filesrc == NULL) {
403     g_set_error_literal (error, GST_URI_ERROR, GST_URI_ERROR_BAD_STATE,
404         "Could not create file source element");
405     return FALSE;
406   }
407
408   /* skip 'push' bit */
409   return gst_uri_handler_set_uri (GST_URI_HANDLER (src->filesrc), uri + 4,
410       error);
411 }
412
413 static void
414 gst_push_file_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
415 {
416   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
417
418   iface->get_type = gst_push_file_src_uri_get_type;
419   iface->get_protocols = gst_push_file_src_uri_get_protocols;
420   iface->get_uri = gst_push_file_src_uri_get_uri;
421   iface->set_uri = gst_push_file_src_uri_set_uri;
422 }