matroska: add GstToc support for muxer
[platform/upstream/gstreamer.git] / gst / multifile / gstmultifilesrc.c
1 /* GStreamer
2  * Copyright (C) 2006 David A. Schleef <ds@schleef.org>
3  *
4  * gstmultifilesrc.c:
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /**
22  * SECTION:element-multifilesrc
23  * @see_also: #GstFileSrc
24  *
25  * Reads buffers from sequentially named files. If used together with an image
26  * decoder, one needs to use the #GstMultiFileSrc:caps property or a capsfilter
27  * to force to caps containing a framerate. Otherwise image decoders send EOS
28  * after the first picture.
29  *
30  * File names are created by replacing "\%d" with the index using printf().
31  *
32  * <refsect2>
33  * <title>Example launch line</title>
34  * |[
35  * gst-launch multifilesrc location="img.%04d.png" index=0 caps="image/png,framerate=\(fraction\)12/1" ! \
36  *     pngdec ! ffmpegcolorspace ! theoraenc ! oggmux ! \
37  *     filesink location="images.ogg"
38  * ]| This pipeline creates a video file "images.ogg" by joining multiple PNG
39  * files named img.0000.png, img.0001.png, etc.
40  * </refsect2>
41 */
42
43 #ifdef HAVE_CONFIG_H
44 #  include "config.h"
45 #endif
46
47 #include "gstmultifilesrc.h"
48
49
50 static GstFlowReturn gst_multi_file_src_create (GstPushSrc * src,
51     GstBuffer ** buffer);
52
53 static void gst_multi_file_src_dispose (GObject * object);
54
55 static void gst_multi_file_src_set_property (GObject * object, guint prop_id,
56     const GValue * value, GParamSpec * pspec);
57 static void gst_multi_file_src_get_property (GObject * object, guint prop_id,
58     GValue * value, GParamSpec * pspec);
59 static GstCaps *gst_multi_file_src_getcaps (GstBaseSrc * src);
60 static gboolean gst_multi_file_src_query (GstBaseSrc * src, GstQuery * query);
61
62
63 static GstStaticPadTemplate gst_multi_file_src_pad_template =
64 GST_STATIC_PAD_TEMPLATE ("src",
65     GST_PAD_SRC,
66     GST_PAD_ALWAYS,
67     GST_STATIC_CAPS_ANY);
68
69 GST_DEBUG_CATEGORY_STATIC (gst_multi_file_src_debug);
70 #define GST_CAT_DEFAULT gst_multi_file_src_debug
71
72 enum
73 {
74   ARG_0,
75   ARG_LOCATION,
76   ARG_INDEX,
77   ARG_START_INDEX,
78   ARG_STOP_INDEX,
79   ARG_CAPS,
80   ARG_LOOP
81 };
82
83 #define DEFAULT_LOCATION "%05d"
84 #define DEFAULT_INDEX 0
85
86
87 GST_BOILERPLATE (GstMultiFileSrc, gst_multi_file_src, GstPushSrc,
88     GST_TYPE_PUSH_SRC);
89
90 static void
91 gst_multi_file_src_base_init (gpointer g_class)
92 {
93   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
94
95   GST_DEBUG_CATEGORY_INIT (gst_multi_file_src_debug, "multifilesrc", 0,
96       "multifilesrc element");
97
98   gst_element_class_add_static_pad_template (gstelement_class,
99       &gst_multi_file_src_pad_template);
100   gst_element_class_set_details_simple (gstelement_class, "Multi-File Source",
101       "Source/File",
102       "Read a sequentially named set of files into buffers",
103       "David Schleef <ds@schleef.org>");
104 }
105
106 static void
107 gst_multi_file_src_class_init (GstMultiFileSrcClass * klass)
108 {
109   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
110   GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
111   GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
112
113   gobject_class->set_property = gst_multi_file_src_set_property;
114   gobject_class->get_property = gst_multi_file_src_get_property;
115
116   g_object_class_install_property (gobject_class, ARG_LOCATION,
117       g_param_spec_string ("location", "File Location",
118           "Pattern to create file names of input files.  File names are "
119           "created by calling sprintf() with the pattern and the current "
120           "index.", DEFAULT_LOCATION,
121           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
122   g_object_class_install_property (gobject_class, ARG_INDEX,
123       g_param_spec_int ("index", "File Index",
124           "Index to use with location property to create file names.  The "
125           "index is incremented by one for each buffer read.",
126           0, INT_MAX, DEFAULT_INDEX,
127           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
128   g_object_class_install_property (gobject_class, ARG_START_INDEX,
129       g_param_spec_int ("start-index", "Start Index",
130           "Start value of index.  The initial value of index can be set "
131           "either by setting index or start-index.  When the end of the loop "
132           "is reached, the index will be set to the value start-index.",
133           0, INT_MAX, DEFAULT_INDEX,
134           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
135   g_object_class_install_property (gobject_class, ARG_STOP_INDEX,
136       g_param_spec_int ("stop-index", "Start Index",
137           "Stop value of index.  The special value -1 means no stop.",
138           -1, INT_MAX, DEFAULT_INDEX,
139           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
140   g_object_class_install_property (gobject_class, ARG_CAPS,
141       g_param_spec_boxed ("caps", "Caps",
142           "Caps describing the format of the data.",
143           GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
144   g_object_class_install_property (gobject_class, ARG_LOOP,
145       g_param_spec_boolean ("loop", "Loop",
146           "Whether to repeat from the beginning when all files have been read.",
147           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
148
149   gobject_class->dispose = gst_multi_file_src_dispose;
150
151   gstbasesrc_class->get_caps = gst_multi_file_src_getcaps;
152   gstbasesrc_class->query = gst_multi_file_src_query;
153
154   gstpushsrc_class->create = gst_multi_file_src_create;
155
156   if (sizeof (off_t) < 8) {
157     GST_LOG ("No large file support, sizeof (off_t) = %" G_GSIZE_FORMAT,
158         sizeof (off_t));
159   }
160 }
161
162 static void
163 gst_multi_file_src_init (GstMultiFileSrc * multifilesrc,
164     GstMultiFileSrcClass * g_class)
165 {
166   multifilesrc->start_index = DEFAULT_INDEX;
167   multifilesrc->index = DEFAULT_INDEX;
168   multifilesrc->stop_index = -1;
169   multifilesrc->filename = g_strdup (DEFAULT_LOCATION);
170   multifilesrc->successful_read = FALSE;
171 }
172
173 static void
174 gst_multi_file_src_dispose (GObject * object)
175 {
176   GstMultiFileSrc *src = GST_MULTI_FILE_SRC (object);
177
178   g_free (src->filename);
179   src->filename = NULL;
180   if (src->caps)
181     gst_caps_unref (src->caps);
182
183   G_OBJECT_CLASS (parent_class)->dispose (object);
184 }
185
186 static GstCaps *
187 gst_multi_file_src_getcaps (GstBaseSrc * src)
188 {
189   GstMultiFileSrc *multi_file_src = GST_MULTI_FILE_SRC (src);
190
191   GST_DEBUG_OBJECT (src, "returning %" GST_PTR_FORMAT, multi_file_src->caps);
192
193   if (multi_file_src->caps) {
194     return gst_caps_ref (multi_file_src->caps);
195   } else {
196     return gst_caps_new_any ();
197   }
198 }
199
200 static gboolean
201 gst_multi_file_src_query (GstBaseSrc * src, GstQuery * query)
202 {
203   gboolean res;
204   GstMultiFileSrc *mfsrc;
205
206   mfsrc = GST_MULTI_FILE_SRC (src);
207
208   switch (GST_QUERY_TYPE (query)) {
209     case GST_QUERY_POSITION:
210     {
211       GstFormat format;
212
213       gst_query_parse_position (query, &format, NULL);
214       switch (format) {
215         case GST_FORMAT_BUFFERS:
216         case GST_FORMAT_DEFAULT:
217           gst_query_set_position (query, GST_FORMAT_BUFFERS, mfsrc->index);
218           res = TRUE;
219           break;
220         default:
221           res = GST_BASE_SRC_CLASS (parent_class)->query (src, query);
222           break;
223       }
224       break;
225     }
226     default:
227       res = GST_BASE_SRC_CLASS (parent_class)->query (src, query);
228       break;
229   }
230   return res;
231 }
232
233 static gboolean
234 gst_multi_file_src_set_location (GstMultiFileSrc * src, const gchar * location)
235 {
236   g_free (src->filename);
237   if (location != NULL) {
238     src->filename = g_strdup (location);
239   } else {
240     src->filename = NULL;
241   }
242
243   return TRUE;
244 }
245
246 static void
247 gst_multi_file_src_set_property (GObject * object, guint prop_id,
248     const GValue * value, GParamSpec * pspec)
249 {
250   GstMultiFileSrc *src = GST_MULTI_FILE_SRC (object);
251
252   switch (prop_id) {
253     case ARG_LOCATION:
254       gst_multi_file_src_set_location (src, g_value_get_string (value));
255       break;
256     case ARG_INDEX:
257       src->index = g_value_get_int (value);
258       break;
259     case ARG_START_INDEX:
260       src->start_index = g_value_get_int (value);
261       break;
262     case ARG_STOP_INDEX:
263       src->stop_index = g_value_get_int (value);
264       break;
265     case ARG_CAPS:
266     {
267       const GstCaps *caps = gst_value_get_caps (value);
268       GstCaps *new_caps;
269
270       if (caps == NULL) {
271         new_caps = gst_caps_new_any ();
272       } else {
273         new_caps = gst_caps_copy (caps);
274       }
275       gst_caps_replace (&src->caps, new_caps);
276       gst_pad_set_caps (GST_BASE_SRC_PAD (src), new_caps);
277     }
278       break;
279     case ARG_LOOP:
280       src->loop = g_value_get_boolean (value);
281       break;
282     default:
283       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
284       break;
285   }
286 }
287
288 static void
289 gst_multi_file_src_get_property (GObject * object, guint prop_id,
290     GValue * value, GParamSpec * pspec)
291 {
292   GstMultiFileSrc *src = GST_MULTI_FILE_SRC (object);
293
294   switch (prop_id) {
295     case ARG_LOCATION:
296       g_value_set_string (value, src->filename);
297       break;
298     case ARG_INDEX:
299       g_value_set_int (value, src->index);
300       break;
301     case ARG_START_INDEX:
302       g_value_set_int (value, src->start_index);
303       break;
304     case ARG_STOP_INDEX:
305       g_value_set_int (value, src->stop_index);
306       break;
307     case ARG_CAPS:
308       gst_value_set_caps (value, src->caps);
309       break;
310     case ARG_LOOP:
311       g_value_set_boolean (value, src->loop);
312       break;
313     default:
314       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
315       break;
316   }
317 }
318
319 static gchar *
320 gst_multi_file_src_get_filename (GstMultiFileSrc * multifilesrc)
321 {
322   gchar *filename;
323
324   GST_DEBUG ("%d", multifilesrc->index);
325   filename = g_strdup_printf (multifilesrc->filename, multifilesrc->index);
326
327   return filename;
328 }
329
330 static GstFlowReturn
331 gst_multi_file_src_create (GstPushSrc * src, GstBuffer ** buffer)
332 {
333   GstMultiFileSrc *multifilesrc;
334   gsize size;
335   gchar *data;
336   gchar *filename;
337   GstBuffer *buf;
338   gboolean ret;
339   GError *error = NULL;
340
341   multifilesrc = GST_MULTI_FILE_SRC (src);
342
343   if (multifilesrc->index < multifilesrc->start_index) {
344     multifilesrc->index = multifilesrc->start_index;
345   }
346   filename = gst_multi_file_src_get_filename (multifilesrc);
347
348   GST_DEBUG_OBJECT (multifilesrc, "reading from file \"%s\".", filename);
349
350   ret = g_file_get_contents (filename, &data, &size, &error);
351   if (!ret) {
352     if (multifilesrc->successful_read) {
353       /* If we've read at least one buffer successfully, not finding the
354        * next file is EOS. */
355       g_free (filename);
356       if (error != NULL)
357         g_error_free (error);
358
359       if (multifilesrc->loop) {
360         error = NULL;
361         multifilesrc->index = multifilesrc->start_index;
362
363         filename = gst_multi_file_src_get_filename (multifilesrc);
364         ret = g_file_get_contents (filename, &data, &size, &error);
365         if (!ret) {
366           g_free (filename);
367           if (error != NULL)
368             g_error_free (error);
369
370           return GST_FLOW_UNEXPECTED;
371         }
372       } else {
373         return GST_FLOW_UNEXPECTED;
374       }
375     } else {
376       goto handle_error;
377     }
378   }
379
380   multifilesrc->successful_read = TRUE;
381   multifilesrc->index++;
382   if (multifilesrc->stop_index != -1 &&
383       multifilesrc->index >= multifilesrc->stop_index) {
384     multifilesrc->index = multifilesrc->start_index;
385   }
386
387   buf = gst_buffer_new ();
388   GST_BUFFER_DATA (buf) = (unsigned char *) data;
389   GST_BUFFER_MALLOCDATA (buf) = GST_BUFFER_DATA (buf);
390   GST_BUFFER_SIZE (buf) = size;
391   GST_BUFFER_OFFSET (buf) = multifilesrc->offset;
392   GST_BUFFER_OFFSET_END (buf) = multifilesrc->offset + size;
393   multifilesrc->offset += size;
394   gst_buffer_set_caps (buf, multifilesrc->caps);
395
396   GST_DEBUG_OBJECT (multifilesrc, "read file \"%s\".", filename);
397
398   g_free (filename);
399   *buffer = buf;
400   return GST_FLOW_OK;
401
402 handle_error:
403   {
404     if (error != NULL) {
405       GST_ELEMENT_ERROR (multifilesrc, RESOURCE, READ,
406           ("Error while reading from file \"%s\".", filename),
407           ("%s", error->message));
408       g_error_free (error);
409     } else {
410       GST_ELEMENT_ERROR (multifilesrc, RESOURCE, READ,
411           ("Error while reading from file \"%s\".", filename),
412           ("%s", g_strerror (errno)));
413     }
414     g_free (filename);
415     return GST_FLOW_ERROR;
416   }
417 }