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