Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / tests / examples / encoding / encoding.c
1 /* Example application for using GstProfile and encodebin
2  * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <stdlib.h>
25 #include <glib.h>
26 #include <glib/gprintf.h>
27 #include <gst/gst.h>
28 #include <gst/pbutils/pbutils.h>
29 #include <gst/pbutils/encoding-profile.h>
30 #include "gstcapslist.h"
31
32 static gboolean silent = FALSE;
33
34 static void
35 list_codecs (void)
36 {
37   GstCaps *l;
38   GstCaps *caps;
39   guint i, len;
40
41   caps = gst_caps_new_empty ();
42
43   g_print ("Available container formats:\n");
44   l = gst_caps_list_container_formats (GST_RANK_NONE);
45   len = gst_caps_get_size (l);
46   for (i = 0; i < len; i++) {
47     GstStructure *st = gst_caps_steal_structure (l, 0);
48     gchar *tmpstr, *desc;
49
50     gst_caps_append_structure (caps, st);
51
52     tmpstr = gst_caps_to_string (caps);
53     desc = gst_pb_utils_get_codec_description (caps);
54     g_print ("  %s - %s\n", desc, tmpstr);
55     g_free (tmpstr);
56     if (desc)
57       g_free (desc);
58     gst_caps_remove_structure (caps, 0);
59   }
60   g_print ("\n");
61   gst_caps_unref (l);
62
63   g_print ("Available video codecs:\n");
64   l = gst_caps_list_video_encoding_formats (GST_RANK_NONE);
65   len = gst_caps_get_size (l);
66   for (i = 0; i < len; i++) {
67     GstStructure *st = gst_caps_steal_structure (l, 0);
68     gchar *tmpstr, *desc;
69
70     gst_caps_append_structure (caps, st);
71
72     tmpstr = gst_caps_to_string (caps);
73     desc = gst_pb_utils_get_codec_description (caps);
74     g_print ("  %s - %s\n", desc, tmpstr);
75     g_free (tmpstr);
76     if (desc)
77       g_free (desc);
78     gst_caps_remove_structure (caps, 0);
79   }
80   g_print ("\n");
81   gst_caps_unref (l);
82
83   g_print ("Available audio codecs:\n");
84   l = gst_caps_list_audio_encoding_formats (GST_RANK_NONE);
85   len = gst_caps_get_size (l);
86   for (i = 0; i < len; i++) {
87     GstStructure *st = gst_caps_steal_structure (l, 0);
88     gchar *tmpstr, *desc;
89
90     gst_caps_append_structure (caps, st);
91
92     tmpstr = gst_caps_to_string (caps);
93     desc = gst_pb_utils_get_codec_description (caps);
94     g_print ("  %s - %s\n", desc, tmpstr);
95     g_free (tmpstr);
96     if (desc)
97       g_free (desc);
98     gst_caps_remove_structure (caps, 0);
99   }
100   g_print ("\n");
101   gst_caps_unref (l);
102
103   gst_caps_unref (caps);
104 }
105
106 static gchar *
107 generate_filename (const GstCaps * container, const GstCaps * vcodec,
108     const GstCaps * acodec)
109 {
110   gchar *a, *b, *c;
111   gchar *res = NULL;
112   guint i;
113
114   a = gst_pb_utils_get_codec_description (container);
115   b = gst_pb_utils_get_codec_description (vcodec);
116   c = gst_pb_utils_get_codec_description (acodec);
117
118   if (!a)
119     a = g_strdup_printf ("%.10s",
120         g_uri_escape_string (gst_caps_to_string (container), NULL, FALSE));
121   if (!b)
122     b = g_strdup_printf ("%.10s",
123         g_uri_escape_string (gst_caps_to_string (vcodec), NULL, FALSE));
124   if (!c)
125     c = g_strdup_printf ("%.10s",
126         g_uri_escape_string (gst_caps_to_string (acodec), NULL, FALSE));
127
128   for (i = 0; i < 256 && res == NULL; i++) {
129     res = g_strdup_printf ("%s-%s-%s-%d.file", a, b, c, i);
130     if (g_file_test (res, G_FILE_TEST_EXISTS)) {
131       g_free (res);
132       res = NULL;
133     }
134   }
135   /* Make sure file doesn't already exist */
136
137   g_free (a);
138   g_free (b);
139   g_free (c);
140
141   return res;
142 }
143
144 static GstEncodingProfile *
145 create_profile (GstCaps * cf, GstCaps * vf, GstCaps * af)
146 {
147   GstEncodingContainerProfile *cprof = NULL;
148
149   cprof =
150       gst_encoding_container_profile_new ((gchar *) "test-application-profile",
151       NULL, cf, NULL);
152
153   if (vf)
154     gst_encoding_container_profile_add_profile (cprof,
155         (GstEncodingProfile *) gst_encoding_video_profile_new (vf,
156             NULL, NULL, 0));
157   if (af)
158     gst_encoding_container_profile_add_profile (cprof, (GstEncodingProfile *)
159         gst_encoding_audio_profile_new (af, NULL, NULL, 0));
160
161   /* Let's print out some info */
162   if (!silent) {
163     gchar *desc = gst_pb_utils_get_codec_description (cf);
164     gchar *cd = gst_caps_to_string (cf);
165     g_print ("Encoding parameters\n");
166     g_print ("  Container format : %s (%s)\n", desc, cd);
167     g_free (desc);
168     g_free (cd);
169     if (vf) {
170       desc = gst_pb_utils_get_codec_description (vf);
171       cd = gst_caps_to_string (vf);
172       g_print ("  Video format : %s (%s)\n", desc, cd);
173       g_free (desc);
174       g_free (cd);
175     }
176     if (af) {
177       desc = gst_pb_utils_get_codec_description (af);
178       cd = gst_caps_to_string (af);
179       g_print ("  Audio format : %s (%s)\n", desc, cd);
180       g_free (desc);
181       g_free (cd);
182     }
183   }
184
185   return (GstEncodingProfile *) cprof;
186 }
187
188 static GstEncodingProfile *
189 create_profile_from_string (gchar * format, gchar * vformat, gchar * aformat)
190 {
191   GstEncodingProfile *prof = NULL;
192   GstCaps *cf = NULL, *vf = NULL, *af = NULL;
193
194   if (format)
195     cf = gst_caps_from_string (format);
196   if (vformat)
197     vf = gst_caps_from_string (vformat);
198   if (aformat)
199     af = gst_caps_from_string (aformat);
200
201   if (G_UNLIKELY ((vformat && (vf == NULL)) || (aformat && (af == NULL))))
202     goto beach;
203
204   prof = create_profile (cf, vf, af);
205
206 beach:
207   if (cf)
208     gst_caps_unref (cf);
209   if (vf)
210     gst_caps_unref (vf);
211   if (af)
212     gst_caps_unref (af);
213
214   return prof;
215 }
216
217 static void
218 pad_added_cb (GstElement * uridecodebin, GstPad * pad, GstElement * encodebin)
219 {
220   GstPad *sinkpad;
221
222   sinkpad = gst_element_get_compatible_pad (encodebin, pad, NULL);
223
224   if (sinkpad == NULL) {
225     GstCaps *caps;
226
227     /* Ask encodebin for a compatible pad */
228     caps = gst_pad_get_caps (pad, NULL);
229     g_signal_emit_by_name (encodebin, "request-pad", caps, &sinkpad);
230     if (caps)
231       gst_caps_unref (caps);
232   }
233   if (sinkpad == NULL) {
234     g_print ("Couldn't get an encoding channel for pad %s:%s\n",
235         GST_DEBUG_PAD_NAME (pad));
236     return;
237   }
238
239   if (G_UNLIKELY (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)) {
240     g_print ("Couldn't link pads\n");
241   }
242
243   return;
244 }
245
246 static gboolean
247 autoplug_continue_cb (GstElement * uridecodebin, GstPad * somepad,
248     GstCaps * caps, GstElement * encodebin)
249 {
250   GstPad *sinkpad;
251
252   g_signal_emit_by_name (encodebin, "request-pad", caps, &sinkpad);
253
254   if (sinkpad == NULL)
255     return TRUE;
256
257   return FALSE;
258 }
259
260 static void
261 bus_message_cb (GstBus * bus, GstMessage * message, GMainLoop * mainloop)
262 {
263   switch (GST_MESSAGE_TYPE (message)) {
264     case GST_MESSAGE_ERROR:
265       g_print ("ERROR\n");
266       gst_bus_set_flushing (bus, TRUE);
267       g_main_loop_quit (mainloop);
268       break;
269     case GST_MESSAGE_EOS:
270       g_print ("Done\n");
271       g_main_loop_quit (mainloop);
272       break;
273     default:
274       break;
275   }
276 }
277
278 static void
279 transcode_file (gchar * uri, gchar * outputuri, GstEncodingProfile * prof)
280 {
281   GstElement *pipeline;
282   GstElement *src;
283   GstElement *ebin;
284   GstElement *sink;
285   GstBus *bus;
286   GstCaps *profilecaps, *rescaps;
287   GMainLoop *mainloop;
288
289   g_print (" Input URI  : %s\n", uri);
290   g_print (" Output URI : %s\n", outputuri);
291
292   sink = gst_element_make_from_uri (GST_URI_SINK, outputuri, "sink");
293   if (G_UNLIKELY (sink == NULL)) {
294     g_print ("Can't create output sink, most likely invalid output URI !\n");
295     return;
296   }
297
298   src = gst_element_factory_make ("uridecodebin", NULL);
299   if (G_UNLIKELY (src == NULL)) {
300     g_print ("Can't create uridecodebin for input URI, aborting!\n");
301     return;
302   }
303
304   /* Figure out the streams that can be passed as-is to encodebin */
305   g_object_get (src, "caps", &rescaps, NULL);
306   rescaps = gst_caps_copy (rescaps);
307   profilecaps = gst_encoding_profile_get_input_caps (prof);
308   gst_caps_append (rescaps, profilecaps);
309
310   /* Set properties */
311   g_object_set (src, "uri", uri, "caps", rescaps, NULL);
312
313   ebin = gst_element_factory_make ("encodebin", NULL);
314   g_object_set (ebin, "profile", prof, NULL);
315
316   g_signal_connect (src, "autoplug-continue", G_CALLBACK (autoplug_continue_cb),
317       ebin);
318   g_signal_connect (src, "pad-added", G_CALLBACK (pad_added_cb), ebin);
319
320   pipeline = gst_pipeline_new ("encoding-pipeline");
321
322   gst_bin_add_many (GST_BIN (pipeline), src, ebin, sink, NULL);
323
324   gst_element_link (ebin, sink);
325
326   mainloop = g_main_loop_new (NULL, FALSE);
327
328   bus = gst_pipeline_get_bus ((GstPipeline *) pipeline);
329   gst_bus_add_signal_watch (bus);
330   g_signal_connect (bus, "message", G_CALLBACK (bus_message_cb), mainloop);
331
332   if (gst_element_set_state (pipeline,
333           GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
334     g_print ("Failed to start the encoding\n");
335     return;
336   }
337
338   g_main_loop_run (mainloop);
339
340   gst_element_set_state (pipeline, GST_STATE_NULL);
341   gst_object_unref (pipeline);
342 }
343
344 static gchar *
345 ensure_uri (gchar * location)
346 {
347   gchar *res;
348   gchar *path;
349
350   if (gst_uri_is_valid (location))
351     return g_strdup (location);
352
353   if (!g_path_is_absolute (location)) {
354     gchar *cur_dir;
355     cur_dir = g_get_current_dir ();
356     path = g_build_filename (cur_dir, location, NULL);
357     g_free (cur_dir);
358   } else
359     path = g_strdup (location);
360
361   res = g_filename_to_uri (path, NULL, NULL);
362   g_free (path);
363
364   return res;
365 }
366
367 int
368 main (int argc, char **argv)
369 {
370   GError *err = NULL;
371   gchar *outputuri = NULL;
372   gchar *format = NULL;
373   gchar *aformat = NULL;
374   gchar *vformat = NULL;
375   gboolean allmissing = FALSE;
376   gboolean listcodecs = FALSE;
377   GOptionEntry options[] = {
378     {"silent", 's', 0, G_OPTION_ARG_NONE, &silent,
379         "Don't output the information structure", NULL},
380     {"outputuri", 'o', 0, G_OPTION_ARG_STRING, &outputuri,
381         "URI to encode to", "URI (<protocol>://<location>)"},
382     {"format", 'f', 0, G_OPTION_ARG_STRING, &format,
383         "Container format", "<GstCaps>"},
384     {"vformat", 'v', 0, G_OPTION_ARG_STRING, &vformat,
385         "Video format", "<GstCaps>"},
386     {"aformat", 'a', 0, G_OPTION_ARG_STRING, &aformat,
387         "Audio format", "<GstCaps>"},
388     {"allmissing", 'm', 0, G_OPTION_ARG_NONE, &allmissing,
389         "encode to all matching format/codec that aren't specified", NULL},
390     {"list-codecs", 'l', 0, G_OPTION_ARG_NONE, &listcodecs,
391         "list all available codecs and container formats", NULL},
392     {NULL}
393   };
394   GOptionContext *ctx;
395   GstEncodingProfile *prof;
396   gchar *inputuri;
397
398   if (!g_thread_supported ())
399     g_thread_init (NULL);
400
401   ctx = g_option_context_new ("- encode URIs with GstProfile and encodebin");
402   g_option_context_add_main_entries (ctx, options, NULL);
403   g_option_context_add_group (ctx, gst_init_get_option_group ());
404
405   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
406     g_print ("Error initializing: %s\n", err->message);
407     exit (1);
408   }
409
410   if (listcodecs) {
411     list_codecs ();
412     g_option_context_free (ctx);
413     exit (0);
414   }
415
416   if (outputuri == NULL || argc != 2) {
417     g_print ("%s", g_option_context_get_help (ctx, TRUE, NULL));
418     g_option_context_free (ctx);
419     exit (-1);
420   }
421
422   g_option_context_free (ctx);
423
424   /* Fixup outputuri to be a URI */
425   inputuri = ensure_uri (argv[1]);
426   outputuri = ensure_uri (outputuri);
427
428   if (allmissing) {
429     GList *muxers;
430     GstCaps *formats = NULL;
431     GstCaps *vformats = NULL;
432     GstCaps *aformats = NULL;
433     guint f, v, a, flen, vlen, alen;
434
435     if (!format)
436       formats = gst_caps_list_container_formats (GST_RANK_NONE);
437     else
438       formats = gst_caps_from_string (format);
439
440     if (!vformat)
441       vformats = gst_caps_list_video_encoding_formats (GST_RANK_NONE);
442     else
443       vformats = gst_caps_from_string (vformat);
444
445     if (!aformat)
446       aformats = gst_caps_list_audio_encoding_formats (GST_RANK_NONE);
447     else
448       aformats = gst_caps_from_string (aformat);
449     muxers =
450         gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_MUXER,
451         GST_RANK_NONE);
452
453     flen = gst_caps_get_size (formats);
454
455     for (f = 0; f < flen; f++) {
456       GstCaps *container =
457           gst_caps_new_full (gst_caps_steal_structure (formats, 0), NULL);
458       GstCaps *compatv =
459           gst_caps_list_compatible_codecs (container, vformats, muxers);
460       GstCaps *compata =
461           gst_caps_list_compatible_codecs (container, aformats, muxers);
462
463       vlen = gst_caps_get_size (compatv);
464       alen = gst_caps_get_size (compata);
465
466
467       for (v = 0; v < vlen; v++) {
468         GstCaps *vcodec =
469             gst_caps_new_full (gst_structure_copy (gst_caps_get_structure
470                 (compatv, v)), NULL);
471         for (a = 0; a < alen; a++) {
472           GstCaps *acodec =
473               gst_caps_new_full (gst_structure_copy (gst_caps_get_structure
474                   (compata, a)), NULL);
475
476           prof =
477               create_profile ((GstCaps *) container, (GstCaps *) vcodec,
478               (GstCaps *) acodec);
479           if (G_UNLIKELY (prof == NULL)) {
480             g_print ("Wrong arguments\n");
481             break;
482           }
483           outputuri =
484               ensure_uri (generate_filename (container, vcodec, acodec));
485           transcode_file (inputuri, outputuri, prof);
486           gst_encoding_profile_unref (prof);
487
488           gst_caps_unref (acodec);
489         }
490         gst_caps_unref (vcodec);
491       }
492       gst_caps_unref (container);
493     }
494
495   } else {
496
497     /* Create the profile */
498     prof = create_profile_from_string (format, vformat, aformat);
499     if (G_UNLIKELY (prof == NULL)) {
500       g_print ("Encoding arguments are not valid !\n");
501       return 1;
502     }
503
504     /* Trancode file */
505     transcode_file (inputuri, outputuri, prof);
506
507     /* cleanup */
508     gst_encoding_profile_unref (prof);
509
510   }
511   return 0;
512 }