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