/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/pbutils/encoding-profile.c
Line | Count | Source |
1 | | /* GStreamer encoding profiles library |
2 | | * Copyright (C) 2009-2010 Edward Hervey <edward.hervey@collabora.co.uk> |
3 | | * (C) 2009-2010 Nokia Corporation |
4 | | * |
5 | | * This library is free software; you can redistribute it and/or |
6 | | * modify it under the terms of the GNU Library General Public |
7 | | * License as published by the Free Software Foundation; either |
8 | | * version 2 of the License, or (at your option) any later version. |
9 | | * |
10 | | * This library is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | | * Library General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU Library General Public |
16 | | * License along with this library; if not, write to the |
17 | | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
18 | | * Boston, MA 02110-1301, USA. |
19 | | */ |
20 | | |
21 | | /** |
22 | | * SECTION:encoding-profile |
23 | | * @title: GstEncodingProfile |
24 | | * @short_description: Encoding profile library |
25 | | * @symbols: |
26 | | * - GstEncodingProfile |
27 | | * - GstEncodingContainerProfile |
28 | | * - GstEncodingVideoProfile |
29 | | * - GstEncodingAudioProfile |
30 | | * |
31 | | * Functions to create and handle encoding profiles. |
32 | | * |
33 | | * Encoding profiles describe the media types and settings one wishes to use for |
34 | | * an encoding process. The top-level profiles are commonly |
35 | | * #GstEncodingContainerProfile(s) (which contains a user-readable name and |
36 | | * description along with which container format to use). These, in turn, |
37 | | * reference one or more #GstEncodingProfile(s) which indicate which encoding |
38 | | * format should be used on each individual streams. |
39 | | * |
40 | | * #GstEncodingProfile(s) can be provided to the 'encodebin' element, which will |
41 | | * take care of selecting and setting up the required elements to produce an |
42 | | * output stream conforming to the specifications of the profile. |
43 | | * |
44 | | * The encoding profiles do not necessarily specify which #GstElement to use for |
45 | | * the various encoding and muxing steps, as they allow to specifying the format |
46 | | * one wishes to use. |
47 | | * |
48 | | * Encoding profiles can be created at runtime by the application or loaded from |
49 | | * (and saved to) file using the #GstEncodingTarget API. |
50 | | * |
51 | | * ## The encoding profile serialization format |
52 | | * |
53 | | * Encoding profiles can be serialized to be used in the command line tools or |
54 | | * to set it on other other #GObject-s using #gst_util_set_object_arg for |
55 | | * example. |
56 | | * |
57 | | * The serialization format aims at being simple to understand although flexible |
58 | | * enough to describe any possible encoding profile. There are several ways to |
59 | | * describe the profile depending on the context but the general idea is that it |
60 | | * is a colon separated list of EncodingProfiles descriptions, the first one |
61 | | * needs to describe a #GstEncodingContainerProfile and the following ones |
62 | | * describe elementary streams. |
63 | | * |
64 | | * ### Using encoders and muxer element factory name |
65 | | * |
66 | | * ``` |
67 | | * muxer_factory_name:video_encoder_factory_name:audio_encoder_factory_name |
68 | | * ``` |
69 | | * |
70 | | * For example to encode a stream into a WebM container, with an OGG audio |
71 | | * stream and a VP8 video stream, the serialized #GstEncodingProfile looks like: |
72 | | * |
73 | | * ``` |
74 | | * webmmux:vp8enc:vorbisenc |
75 | | * ``` |
76 | | * |
77 | | * ### Define the encoding profile in a generic way using caps: |
78 | | * |
79 | | * ``` |
80 | | * muxer_source_caps:video_encoder_source_caps:audio_encoder_source_caps |
81 | | * ``` |
82 | | * |
83 | | * For example to encode a stream into a WebM container, with an OGG audio |
84 | | * stream and a VP8 video stream, the serialized #GstEncodingProfile looks like: |
85 | | * |
86 | | * ``` |
87 | | * video/webm:video/x-vp8:audio/x-vorbis |
88 | | * ``` |
89 | | * |
90 | | * It is possible to mix caps and element type names so you can specify a |
91 | | * specific video encoder while using caps for other encoders/muxer. |
92 | | * |
93 | | * ### Using preset |
94 | | * |
95 | | * You can also set the preset name of the encoding profile using the |
96 | | * caps+preset_name syntax as in: |
97 | | * |
98 | | * ``` |
99 | | * video/webm:video/x-vp8+youtube-preset:audio/x-vorbis |
100 | | * ``` |
101 | | * |
102 | | * ### Setting properties on muxers or on the encoding profile itself |
103 | | * |
104 | | * Moreover, you can set the extra properties: |
105 | | * |
106 | | * * `|element-properties,property1=true` (See |
107 | | * #gst_encoding_profile_set_element_properties) |
108 | | * * `|presence=true` (See See #gst_encoding_profile_get_presence) |
109 | | * * `|single-segment=true` (See #gst_encoding_profile_set_single_segment) |
110 | | * * `|single-segment=true` (See |
111 | | * #gst_encoding_video_profile_set_variableframerate) |
112 | | * * `|factory-name=element-factory-name` (See #gst_encoding_profile_set_preset_name) |
113 | | * |
114 | | * for example: |
115 | | * |
116 | | * ``` |
117 | | * video/webm:video/x-vp8|presence=1|element-properties,target-bitrate=500000:audio/x-vorbis |
118 | | * ``` |
119 | | * |
120 | | * ### Enforcing properties to the stream itself (video size, number of audio channels, etc..) |
121 | | * |
122 | | * You can also use the `restriction_caps->encoded_format_caps` syntax to |
123 | | * specify the restriction caps to be set on a #GstEncodingProfile |
124 | | * |
125 | | * It corresponds to the restriction #GstCaps to apply before the encoder that |
126 | | * will be used in the profile (See #gst_encoding_profile_get_restriction). The |
127 | | * fields present in restriction caps are properties of the raw stream (that is, |
128 | | * before encoding), such as height and width for video and depth and sampling |
129 | | * rate for audio. This property does not make sense for muxers. See |
130 | | * #gst_encoding_profile_get_restriction for more details. |
131 | | * |
132 | | * To force a video stream to be encoded with a Full HD resolution (using WebM |
133 | | * as the container format, VP8 as the video codec and Vorbis as the audio |
134 | | * codec), you should use: |
135 | | * |
136 | | * ``` |
137 | | * "video/webm:video/x-raw,width=1920,height=1080->video/x-vp8:audio/x-vorbis" |
138 | | * ``` |
139 | | * |
140 | | * > NOTE: Make sure to enclose into quotes to avoid '>' to be reinterpreted by |
141 | | * > the shell. |
142 | | * |
143 | | * In the case you are specifying encoders directly, the following is also |
144 | | * possible: |
145 | | * |
146 | | * ``` |
147 | | * matroskamux:x264enc,width=1920,height=1080:audio/x-vorbis |
148 | | * ``` |
149 | | * |
150 | | * ## Some serialized encoding formats examples |
151 | | * |
152 | | * ### MP3 audio and H264 in MP4** |
153 | | * |
154 | | * ``` |
155 | | * video/quicktime,variant=iso:video/x-h264:audio/mpeg,mpegversion=1,layer=3 |
156 | | * ``` |
157 | | * |
158 | | * ### Vorbis and theora in OGG |
159 | | * |
160 | | * ``` |
161 | | * application/ogg:video/x-theora:audio/x-vorbis |
162 | | * ``` |
163 | | * |
164 | | * ### AC3 and H264 in MPEG-TS |
165 | | * |
166 | | * ``` |
167 | | * video/mpegts:video/x-h264:audio/x-ac3 |
168 | | * ``` |
169 | | * |
170 | | * ## Loading a profile from encoding targets |
171 | | * |
172 | | * Anywhere you have to use a string to define a #GstEncodingProfile, you |
173 | | * can use load it from a #GstEncodingTarget using the following syntaxes: |
174 | | * |
175 | | * ``` |
176 | | * target_name[/profilename/category] |
177 | | * ``` |
178 | | * |
179 | | * or |
180 | | * |
181 | | * ``` |
182 | | * /path/to/target.gep:profilename |
183 | | * ``` |
184 | | * |
185 | | * ## Examples |
186 | | * |
187 | | * ### Creating a profile |
188 | | * |
189 | | * ``` c |
190 | | * #include <gst/pbutils/encoding-profile.h> |
191 | | * ... |
192 | | * GstEncodingProfile * |
193 | | * create_ogg_theora_profile(void) |
194 | | *{ |
195 | | * GstEncodingContainerProfile *prof; |
196 | | * GstCaps *caps; |
197 | | * |
198 | | * caps = gst_caps_from_string("application/ogg"); |
199 | | * prof = gst_encoding_container_profile_new("Ogg audio/video", |
200 | | * "Standard OGG/THEORA/VORBIS", |
201 | | * caps, NULL); |
202 | | * gst_caps_unref (caps); |
203 | | * |
204 | | * caps = gst_caps_from_string("video/x-theora"); |
205 | | * gst_encoding_container_profile_add_profile(prof, |
206 | | * (GstEncodingProfile*) gst_encoding_video_profile_new(caps, NULL, NULL, 0)); |
207 | | * gst_caps_unref (caps); |
208 | | * |
209 | | * caps = gst_caps_from_string("audio/x-vorbis"); |
210 | | * gst_encoding_container_profile_add_profile(prof, |
211 | | * (GstEncodingProfile*) gst_encoding_audio_profile_new(caps, NULL, NULL, 0)); |
212 | | * gst_caps_unref (caps); |
213 | | * |
214 | | * return (GstEncodingProfile*) prof; |
215 | | *} |
216 | | * |
217 | | * ``` |
218 | | * |
219 | | * ### Example: Using an encoder preset with a profile |
220 | | * |
221 | | * ``` c |
222 | | * #include <gst/pbutils/encoding-profile.h> |
223 | | * ... |
224 | | * GstEncodingProfile * |
225 | | * create_ogg_theora_profile(void) |
226 | | *{ |
227 | | * GstEncodingVideoProfile *v; |
228 | | * GstEncodingAudioProfile *a; |
229 | | * GstEncodingContainerProfile *prof; |
230 | | * GstCaps *caps; |
231 | | * GstPreset *preset; |
232 | | * |
233 | | * caps = gst_caps_from_string ("application/ogg"); |
234 | | * prof = gst_encoding_container_profile_new ("Ogg audio/video", |
235 | | * "Standard OGG/THEORA/VORBIS", |
236 | | * caps, NULL); |
237 | | * gst_caps_unref (caps); |
238 | | * |
239 | | * preset = GST_PRESET (gst_element_factory_make ("theoraenc", "theorapreset")); |
240 | | * g_object_set (preset, "bitrate", 1000, NULL); |
241 | | * // The preset will be saved on the filesystem, |
242 | | * // so try to use a descriptive name |
243 | | * gst_preset_save_preset (preset, "theora_bitrate_preset"); |
244 | | * gst_object_unref (preset); |
245 | | * |
246 | | * caps = gst_caps_from_string ("video/x-theora"); |
247 | | * v = gst_encoding_video_profile_new (caps, "theora_bitrate_preset", NULL, 0); |
248 | | * gst_encoding_container_profile_add_profile (prof, (GstEncodingProfile*) v); |
249 | | * gst_caps_unref (caps); |
250 | | * |
251 | | * caps = gst_caps_from_string ("audio/x-vorbis"); |
252 | | * a = gst_encoding_audio_profile_new (caps, NULL, NULL, 0); |
253 | | * gst_encoding_container_profile_add_profile (prof, (GstEncodingProfile*) a); |
254 | | * gst_caps_unref (caps); |
255 | | * |
256 | | * return (GstEncodingProfile*) prof; |
257 | | *} |
258 | | * |
259 | | * ``` |
260 | | * |
261 | | * ### Listing categories, targets and profiles |
262 | | * |
263 | | * ``` c |
264 | | * #include <gst/pbutils/encoding-profile.h> |
265 | | * ... |
266 | | * GstEncodingProfile *prof; |
267 | | * GList *categories, *tmpc; |
268 | | * GList *targets, *tmpt; |
269 | | * ... |
270 | | * categories = gst_encoding_list_available_categories (); |
271 | | * |
272 | | * ... Show available categories to user ... |
273 | | * |
274 | | * for (tmpc = categories; tmpc; tmpc = tmpc->next) { |
275 | | * gchar *category = (gchar *) tmpc->data; |
276 | | * |
277 | | * ... and we can list all targets within that category ... |
278 | | * |
279 | | * targets = gst_encoding_list_all_targets (category); |
280 | | * |
281 | | * ... and show a list to our users ... |
282 | | * |
283 | | * g_list_foreach (targets, (GFunc) gst_encoding_target_unref, NULL); |
284 | | * g_list_free (targets); |
285 | | * } |
286 | | * |
287 | | * g_list_foreach (categories, (GFunc) g_free, NULL); |
288 | | * g_list_free (categories); |
289 | | * |
290 | | * ... |
291 | | * ``` |
292 | | */ |
293 | | |
294 | | #ifdef HAVE_CONFIG_H |
295 | | # include "config.h" |
296 | | #endif |
297 | | |
298 | | #include "encoding-profile.h" |
299 | | #include "encoding-target.h" |
300 | | |
301 | | #include <string.h> |
302 | | |
303 | | #ifndef GST_DISABLE_GST_DEBUG |
304 | | #define GST_CAT_DEFAULT gst_pb_utils_encoding_profile_ensure_debug_category() |
305 | | |
306 | | static GstDebugCategory * |
307 | | gst_pb_utils_encoding_profile_ensure_debug_category (void) |
308 | 0 | { |
309 | 0 | static gsize cat_gonce = 0; |
310 | |
|
311 | 0 | if (g_once_init_enter (&cat_gonce)) { |
312 | 0 | GstDebugCategory *cat = NULL; |
313 | |
|
314 | 0 | GST_DEBUG_CATEGORY_INIT (cat, "encoding-profile", 0, |
315 | 0 | "GstPbUtils encoding profile"); |
316 | |
|
317 | 0 | g_once_init_leave (&cat_gonce, (gsize) cat); |
318 | 0 | } |
319 | |
|
320 | 0 | return (GstDebugCategory *) cat_gonce; |
321 | 0 | } |
322 | | #endif /* GST_DISABLE_GST_DEBUG */ |
323 | | |
324 | | /* GstEncodingProfile API */ |
325 | 0 | #define PROFILE_LOCK(profile) (g_mutex_lock(&((GstEncodingProfile*)profile)->lock)) |
326 | 0 | #define PROFILE_UNLOCK(profile) (g_mutex_unlock(&((GstEncodingProfile*)profile)->lock)) |
327 | | |
328 | | struct _GstEncodingProfile |
329 | | { |
330 | | GObject parent; |
331 | | |
332 | | /*< public > */ |
333 | | gchar *name; |
334 | | gchar *description; |
335 | | GstCaps *format; |
336 | | gchar *preset; |
337 | | gchar *preset_name; |
338 | | guint presence; |
339 | | gboolean allow_dynamic_output; |
340 | | gboolean enabled; |
341 | | gboolean single_segment; |
342 | | |
343 | | GMutex lock; // { |
344 | | GstCaps *restriction; |
345 | | GstStructure *element_properties; |
346 | | // } |
347 | | }; |
348 | | |
349 | | struct _GstEncodingProfileClass |
350 | | { |
351 | | GObjectClass parent_class; |
352 | | |
353 | | void (*copy) (GstEncodingProfile * self, GstEncodingProfile * copy); |
354 | | }; |
355 | | |
356 | | enum |
357 | | { |
358 | | FIRST_PROPERTY, |
359 | | PROP_RESTRICTION_CAPS, |
360 | | PROP_ELEMENT_PROPERTIES, |
361 | | LAST_PROPERTY |
362 | | }; |
363 | | |
364 | | static GParamSpec *_properties[LAST_PROPERTY]; |
365 | | |
366 | | static void string_to_profile_transform (const GValue * src_value, |
367 | | GValue * dest_value); |
368 | | static gboolean gst_encoding_profile_deserialize_valfunc (GValue * value, |
369 | | const gchar * s); |
370 | | static gchar *gst_encoding_profile_serialize_valfunc (GValue * value); |
371 | | |
372 | | static void gst_encoding_profile_class_init (GstEncodingProfileClass * klass); |
373 | | static gpointer gst_encoding_profile_parent_class = NULL; |
374 | | |
375 | | static void |
376 | | gst_encoding_profile_class_intern_init (gpointer klass) |
377 | 0 | { |
378 | 0 | gst_encoding_profile_parent_class = g_type_class_peek_parent (klass); |
379 | 0 | gst_encoding_profile_class_init ((GstEncodingProfileClass *) klass); |
380 | 0 | } |
381 | | |
382 | | GType |
383 | | gst_encoding_profile_get_type (void) |
384 | 45 | { |
385 | 45 | static gsize g_define_type_id_init = 0; |
386 | | |
387 | 45 | if (g_once_init_enter (&g_define_type_id_init)) { |
388 | 9 | GType g_define_type_id = g_type_register_static_simple (G_TYPE_OBJECT, |
389 | 9 | g_intern_static_string ("GstEncodingProfile"), |
390 | 9 | sizeof (GstEncodingProfileClass), |
391 | 9 | (GClassInitFunc) gst_encoding_profile_class_intern_init, |
392 | 9 | sizeof (GstEncodingProfile), |
393 | 9 | NULL, |
394 | 9 | (GTypeFlags) 0); |
395 | 9 | static GstValueTable gstvtable = { |
396 | 9 | G_TYPE_NONE, |
397 | 9 | (GstValueCompareFunc) NULL, |
398 | 9 | (GstValueSerializeFunc) gst_encoding_profile_serialize_valfunc, |
399 | 9 | (GstValueDeserializeFunc) gst_encoding_profile_deserialize_valfunc |
400 | 9 | }; |
401 | | |
402 | 9 | gstvtable.type = g_define_type_id; |
403 | | |
404 | | /* Register a STRING=>PROFILE GValueTransformFunc */ |
405 | 9 | g_value_register_transform_func (G_TYPE_STRING, g_define_type_id, |
406 | 9 | string_to_profile_transform); |
407 | | /* Register gst-specific GValue functions */ |
408 | 9 | gst_value_register (&gstvtable); |
409 | | |
410 | 9 | g_once_init_leave (&g_define_type_id_init, g_define_type_id); |
411 | 9 | } |
412 | 45 | return g_define_type_id_init; |
413 | 45 | } |
414 | | |
415 | | |
416 | | static void |
417 | | _encoding_profile_get_property (GObject * object, guint prop_id, |
418 | | GValue * value, GParamSpec * pspec) |
419 | 0 | { |
420 | 0 | GstEncodingProfile *prof = (GstEncodingProfile *) object; |
421 | |
|
422 | 0 | switch (prop_id) { |
423 | 0 | case PROP_RESTRICTION_CAPS: |
424 | 0 | gst_value_set_caps (value, prof->restriction); |
425 | 0 | break; |
426 | 0 | case PROP_ELEMENT_PROPERTIES: |
427 | 0 | PROFILE_LOCK (prof); |
428 | 0 | gst_value_set_structure (value, prof->element_properties); |
429 | 0 | PROFILE_UNLOCK (prof); |
430 | 0 | break; |
431 | 0 | default: |
432 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
433 | 0 | break; |
434 | 0 | } |
435 | 0 | } |
436 | | |
437 | | static void |
438 | | _encoding_profile_set_property (GObject * object, guint prop_id, |
439 | | const GValue * value, GParamSpec * pspec) |
440 | 0 | { |
441 | 0 | GstEncodingProfile *prof = (GstEncodingProfile *) object; |
442 | |
|
443 | 0 | switch (prop_id) { |
444 | 0 | case PROP_RESTRICTION_CAPS: |
445 | 0 | gst_encoding_profile_set_restriction (prof, gst_caps_copy |
446 | 0 | (gst_value_get_caps (value))); |
447 | 0 | break; |
448 | 0 | case PROP_ELEMENT_PROPERTIES: |
449 | 0 | { |
450 | 0 | const GstStructure *structure = gst_value_get_structure (value); |
451 | |
|
452 | 0 | gst_encoding_profile_set_element_properties (prof, |
453 | 0 | structure ? gst_structure_copy (structure) : NULL); |
454 | 0 | break; |
455 | 0 | } |
456 | 0 | default: |
457 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
458 | 0 | break; |
459 | 0 | } |
460 | 0 | } |
461 | | |
462 | | static void |
463 | | gst_encoding_profile_finalize (GObject * object) |
464 | 0 | { |
465 | 0 | GstEncodingProfile *prof = (GstEncodingProfile *) object; |
466 | 0 | g_free (prof->name); |
467 | 0 | if (prof->format) |
468 | 0 | gst_caps_unref (prof->format); |
469 | 0 | g_free (prof->preset); |
470 | 0 | g_free (prof->description); |
471 | 0 | if (prof->restriction) |
472 | 0 | gst_caps_unref (prof->restriction); |
473 | 0 | g_free (prof->preset_name); |
474 | |
|
475 | 0 | G_OBJECT_CLASS (gst_encoding_profile_parent_class)->finalize (object); |
476 | 0 | } |
477 | | |
478 | | static void |
479 | | gst_encoding_profile_class_init (GstEncodingProfileClass * klass) |
480 | 0 | { |
481 | 0 | GObjectClass *gobject_class = (GObjectClass *) klass; |
482 | |
|
483 | 0 | gobject_class->finalize = gst_encoding_profile_finalize; |
484 | |
|
485 | 0 | gobject_class->set_property = _encoding_profile_set_property; |
486 | 0 | gobject_class->get_property = _encoding_profile_get_property; |
487 | |
|
488 | 0 | _properties[PROP_RESTRICTION_CAPS] = |
489 | 0 | g_param_spec_boxed ("restriction-caps", "Restriction caps", |
490 | 0 | "The restriction caps to use", GST_TYPE_CAPS, |
491 | 0 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); |
492 | | |
493 | | /** |
494 | | * GstEncodingProfile:element-properties: |
495 | | * |
496 | | * A #GstStructure defining the properties to be set to the element |
497 | | * the profile represents. |
498 | | * |
499 | | * For example for `av1enc`: |
500 | | * |
501 | | * ``` |
502 | | * element-properties,row-mt=true, end-usage=vbr |
503 | | * ``` |
504 | | * |
505 | | * Since: 1.20 |
506 | | */ |
507 | 0 | _properties[PROP_ELEMENT_PROPERTIES] = |
508 | 0 | g_param_spec_boxed ("element-properties", "Element properties", |
509 | 0 | "The element properties to use. " |
510 | 0 | "Example: {properties,boolean-prop=true,string-prop=\"hi\"}.", |
511 | 0 | GST_TYPE_STRUCTURE, |
512 | 0 | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); |
513 | |
|
514 | 0 | g_object_class_install_properties (gobject_class, LAST_PROPERTY, _properties); |
515 | 0 | } |
516 | | |
517 | | /** |
518 | | * gst_encoding_profile_get_name: |
519 | | * @profile: a #GstEncodingProfile |
520 | | * |
521 | | * Returns: (nullable): the name of the profile, can be %NULL. |
522 | | */ |
523 | | const gchar * |
524 | | gst_encoding_profile_get_name (GstEncodingProfile * profile) |
525 | 0 | { |
526 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
527 | | |
528 | 0 | return profile->name; |
529 | 0 | } |
530 | | |
531 | | /** |
532 | | * gst_encoding_profile_get_description: |
533 | | * @profile: a #GstEncodingProfile |
534 | | * |
535 | | * Returns: (nullable): the description of the profile, can be %NULL. |
536 | | */ |
537 | | const gchar * |
538 | | gst_encoding_profile_get_description (GstEncodingProfile * profile) |
539 | 0 | { |
540 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
541 | | |
542 | 0 | return profile->description; |
543 | 0 | } |
544 | | |
545 | | /** |
546 | | * gst_encoding_profile_get_format: |
547 | | * @profile: a #GstEncodingProfile |
548 | | * |
549 | | * Returns: (transfer full): (nullable): the #GstCaps corresponding to the media format used |
550 | | * in the profile. Unref after usage. |
551 | | */ |
552 | | GstCaps * |
553 | | gst_encoding_profile_get_format (GstEncodingProfile * profile) |
554 | 0 | { |
555 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
556 | | |
557 | 0 | return (profile->format ? gst_caps_ref (profile->format) : NULL); |
558 | 0 | } |
559 | | |
560 | | /** |
561 | | * gst_encoding_profile_get_preset: |
562 | | * @profile: a #GstEncodingProfile |
563 | | * |
564 | | * Returns: (nullable): the name of the #GstPreset to be used in the profile. |
565 | | * This is the name that has been set when saving the preset. |
566 | | */ |
567 | | const gchar * |
568 | | gst_encoding_profile_get_preset (GstEncodingProfile * profile) |
569 | 0 | { |
570 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
571 | | |
572 | 0 | return profile->preset; |
573 | 0 | } |
574 | | |
575 | | /** |
576 | | * gst_encoding_profile_get_preset_name: |
577 | | * @profile: a #GstEncodingProfile |
578 | | * |
579 | | * Returns: (nullable): the name of the #GstPreset factory to be used in the profile. |
580 | | */ |
581 | | const gchar * |
582 | | gst_encoding_profile_get_preset_name (GstEncodingProfile * profile) |
583 | 0 | { |
584 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
585 | | |
586 | 0 | return profile->preset_name; |
587 | 0 | } |
588 | | |
589 | | /** |
590 | | * gst_encoding_profile_get_presence: |
591 | | * @profile: a #GstEncodingProfile |
592 | | * |
593 | | * Returns: The number of times the profile is used in its parent |
594 | | * container profile. If 0, it is not a mandatory stream. |
595 | | */ |
596 | | guint |
597 | | gst_encoding_profile_get_presence (GstEncodingProfile * profile) |
598 | 0 | { |
599 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), 0); |
600 | | |
601 | 0 | return profile->presence; |
602 | 0 | } |
603 | | |
604 | | /** |
605 | | * gst_encoding_profile_get_enabled: |
606 | | * @profile: a #GstEncodingProfile |
607 | | * |
608 | | * Returns: Whether @profile is enabled or not |
609 | | * |
610 | | * Since: 1.6 |
611 | | */ |
612 | | gboolean |
613 | | gst_encoding_profile_is_enabled (GstEncodingProfile * profile) |
614 | 0 | { |
615 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
616 | | |
617 | 0 | return profile->enabled; |
618 | 0 | } |
619 | | |
620 | | /** |
621 | | * gst_encoding_profile_get_restriction: |
622 | | * @profile: a #GstEncodingProfile |
623 | | * |
624 | | * Returns: (transfer full) (nullable): The restriction #GstCaps to apply before the encoder |
625 | | * that will be used in the profile. The fields present in restriction caps are |
626 | | * properties of the raw stream (that is before encoding), such as height and |
627 | | * width for video and depth and sampling rate for audio. Does not apply to |
628 | | * #GstEncodingContainerProfile (since there is no corresponding raw stream). |
629 | | * Can be %NULL. Unref after usage. |
630 | | */ |
631 | | GstCaps * |
632 | | gst_encoding_profile_get_restriction (GstEncodingProfile * profile) |
633 | 0 | { |
634 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
635 | | |
636 | | |
637 | 0 | return (profile->restriction ? gst_caps_ref (profile->restriction) : NULL); |
638 | 0 | } |
639 | | |
640 | | /** |
641 | | * gst_encoding_profile_set_name: |
642 | | * @profile: a #GstEncodingProfile |
643 | | * @name: (nullable): the name to set on the profile |
644 | | * |
645 | | * Set @name as the given name for the @profile. A copy of @name will be made |
646 | | * internally. |
647 | | */ |
648 | | void |
649 | | gst_encoding_profile_set_name (GstEncodingProfile * profile, const gchar * name) |
650 | 0 | { |
651 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
652 | | |
653 | 0 | g_free (profile->name); |
654 | 0 | profile->name = g_strdup (name); |
655 | 0 | } |
656 | | |
657 | | /** |
658 | | * gst_encoding_profile_set_description: |
659 | | * @profile: a #GstEncodingProfile |
660 | | * @description: (nullable): the description to set on the profile |
661 | | * |
662 | | * Set @description as the given description for the @profile. A copy of |
663 | | * @description will be made internally. |
664 | | */ |
665 | | void |
666 | | gst_encoding_profile_set_description (GstEncodingProfile * profile, |
667 | | const gchar * description) |
668 | 0 | { |
669 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
670 | | |
671 | 0 | g_free (profile->description); |
672 | 0 | profile->description = g_strdup (description); |
673 | 0 | } |
674 | | |
675 | | /** |
676 | | * gst_encoding_profile_set_format: |
677 | | * @profile: a #GstEncodingProfile |
678 | | * @format: (transfer none): the media format to use in the profile. |
679 | | * |
680 | | * Sets the media format used in the profile. |
681 | | */ |
682 | | void |
683 | | gst_encoding_profile_set_format (GstEncodingProfile * profile, GstCaps * format) |
684 | 0 | { |
685 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
686 | | |
687 | 0 | if (profile->format) |
688 | 0 | gst_caps_unref (profile->format); |
689 | 0 | profile->format = gst_caps_ref (format); |
690 | 0 | } |
691 | | |
692 | | /** |
693 | | * gst_encoding_profile_get_allow_dynamic_output: |
694 | | * @profile: a #GstEncodingProfile |
695 | | * |
696 | | * Get whether the format that has been negotiated in at some point can be renegotiated |
697 | | * later during the encoding. |
698 | | */ |
699 | | gboolean |
700 | | gst_encoding_profile_get_allow_dynamic_output (GstEncodingProfile * profile) |
701 | 0 | { |
702 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
703 | | |
704 | 0 | return profile->allow_dynamic_output; |
705 | 0 | } |
706 | | |
707 | | /** |
708 | | * gst_encoding_profile_set_allow_dynamic_output: |
709 | | * @profile: a #GstEncodingProfile |
710 | | * @allow_dynamic_output: Whether the format that has been negotiated first can be renegotiated |
711 | | * during the encoding |
712 | | * |
713 | | * Sets whether the format that has been negotiated in at some point can be renegotiated |
714 | | * later during the encoding. |
715 | | */ |
716 | | void |
717 | | gst_encoding_profile_set_allow_dynamic_output (GstEncodingProfile * profile, |
718 | | gboolean allow_dynamic_output) |
719 | 0 | { |
720 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
721 | | |
722 | 0 | profile->allow_dynamic_output = allow_dynamic_output; |
723 | 0 | } |
724 | | |
725 | | /** |
726 | | * gst_encoding_profile_get_single_segment: |
727 | | * @profile: a #GstEncodingProfile |
728 | | * |
729 | | * Returns: #TRUE if the stream represented by @profile should use a single |
730 | | * segment before the encoder, #FALSE otherwise. This means that buffers will be retimestamped |
731 | | * and segments will be eat so as to appear as one segment. |
732 | | * |
733 | | * Since: 1.18 |
734 | | */ |
735 | | gboolean |
736 | | gst_encoding_profile_get_single_segment (GstEncodingProfile * profile) |
737 | 0 | { |
738 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
739 | | |
740 | 0 | return profile->single_segment; |
741 | 0 | } |
742 | | |
743 | | /** |
744 | | * gst_encoding_profile_set_single_segment: |
745 | | * @profile: a #GstEncodingProfile |
746 | | * @single_segment: #TRUE if the stream represented by @profile should use a |
747 | | * single segment before the encoder, #FALSE otherwise. |
748 | | * |
749 | | * If using a single segment, buffers will be retimestamped and segments will be |
750 | | * eat so as to appear as one segment. |
751 | | * |
752 | | * > *NOTE*: Single segment is not property supported when using |
753 | | * > #encodebin:avoid-reencoding |
754 | | * |
755 | | * Since: 1.18 |
756 | | */ |
757 | | void |
758 | | gst_encoding_profile_set_single_segment (GstEncodingProfile * profile, |
759 | | gboolean single_segment) |
760 | 0 | { |
761 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
762 | | |
763 | 0 | profile->single_segment = single_segment; |
764 | 0 | } |
765 | | |
766 | | /** |
767 | | * gst_encoding_profile_set_preset: |
768 | | * @profile: a #GstEncodingProfile |
769 | | * @preset: (nullable): the element preset to use |
770 | | * |
771 | | * Sets the name of the preset to be used in the profile. |
772 | | * This is the name that has been set when saving the preset. |
773 | | * You can list the available presets for a specific element factory |
774 | | * using `$ gst-inspect-1.0 element-factory-name`, for example for |
775 | | * `x264enc`: |
776 | | * |
777 | | * ``` bash |
778 | | * $ gst-inspect-1.0 x264enc |
779 | | * ... |
780 | | * Presets: |
781 | | * "Profile Baseline": Baseline Profile |
782 | | * "Profile High": High Profile |
783 | | * "Profile Main": Main Profile |
784 | | * "Profile YouTube": YouTube recommended settings (https://support.google.com/youtube/answer/1722171) |
785 | | * "Quality High": High quality |
786 | | * "Quality Low": Low quality |
787 | | * "Quality Normal": Normal quality |
788 | | * "Zero Latency" |
789 | | * ``` |
790 | | } |
791 | | */ |
792 | | void |
793 | | gst_encoding_profile_set_preset (GstEncodingProfile * profile, |
794 | | const gchar * preset) |
795 | 0 | { |
796 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
797 | | |
798 | 0 | g_free (profile->preset); |
799 | 0 | profile->preset = g_strdup (preset); |
800 | 0 | } |
801 | | |
802 | | /** |
803 | | * gst_encoding_profile_set_preset_name: |
804 | | * @profile: a #GstEncodingProfile |
805 | | * @preset_name: (nullable): The name of the element factory to use in this @profile. |
806 | | * |
807 | | * Sets the name of the #GstPreset's factory to be used in the profile. This |
808 | | * is the name of the **element factory** that implements the #GstPreset interface not |
809 | | * the name of the preset itself (see #gst_encoding_profile_set_preset). |
810 | | */ |
811 | | void |
812 | | gst_encoding_profile_set_preset_name (GstEncodingProfile * profile, |
813 | | const gchar * preset_name) |
814 | 0 | { |
815 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
816 | | |
817 | 0 | g_free (profile->preset_name); |
818 | 0 | profile->preset_name = g_strdup (preset_name); |
819 | 0 | } |
820 | | |
821 | | /** |
822 | | * gst_encoding_profile_set_presence: |
823 | | * @profile: a #GstEncodingProfile |
824 | | * @presence: the number of time the profile can be used |
825 | | * |
826 | | * Set the number of time the profile is used in its parent |
827 | | * container profile. If 0, it is not a mandatory stream |
828 | | */ |
829 | | void |
830 | | gst_encoding_profile_set_presence (GstEncodingProfile * profile, guint presence) |
831 | 0 | { |
832 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
833 | | |
834 | 0 | profile->presence = presence; |
835 | 0 | } |
836 | | |
837 | | /** |
838 | | * gst_encoding_profile_set_enabled: |
839 | | * @profile: a #GstEncodingProfile |
840 | | * @enabled: %FALSE to disable @profile, %TRUE to enable it |
841 | | * |
842 | | * Set whether the profile should be used or not. |
843 | | * |
844 | | * Since: 1.6 |
845 | | */ |
846 | | void |
847 | | gst_encoding_profile_set_enabled (GstEncodingProfile * profile, |
848 | | gboolean enabled) |
849 | 0 | { |
850 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
851 | | |
852 | 0 | profile->enabled = enabled; |
853 | 0 | } |
854 | | |
855 | | /** |
856 | | * gst_encoding_profile_set_restriction: |
857 | | * @profile: a #GstEncodingProfile |
858 | | * @restriction: (nullable) (transfer full): the restriction to apply |
859 | | * |
860 | | * Set the restriction #GstCaps to apply before the encoder |
861 | | * that will be used in the profile. See gst_encoding_profile_get_restriction() |
862 | | * for more about restrictions. Does not apply to #GstEncodingContainerProfile. |
863 | | */ |
864 | | void |
865 | | gst_encoding_profile_set_restriction (GstEncodingProfile * profile, |
866 | | GstCaps * restriction) |
867 | 0 | { |
868 | 0 | g_return_if_fail (restriction == NULL || GST_IS_CAPS (restriction)); |
869 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (profile)); |
870 | | |
871 | 0 | if (profile->restriction) |
872 | 0 | gst_caps_unref (profile->restriction); |
873 | 0 | profile->restriction = restriction; |
874 | |
|
875 | 0 | g_object_notify_by_pspec (G_OBJECT (profile), |
876 | 0 | _properties[PROP_RESTRICTION_CAPS]); |
877 | 0 | } |
878 | | |
879 | | /** |
880 | | * gst_encoding_profile_set_element_properties: |
881 | | * @self: a #GstEncodingProfile |
882 | | * @element_properties: (transfer full): A #GstStructure defining the properties |
883 | | * to be set to the element the profile represents. |
884 | | * |
885 | | * This allows setting the muxing/encoding element properties. |
886 | | * |
887 | | * **Set properties generically** |
888 | | * |
889 | | * ``` properties |
890 | | * [element-properties, boolean-prop=true, string-prop="hi"] |
891 | | * ``` |
892 | | * |
893 | | * **Mapping properties with well known element factories** |
894 | | * |
895 | | * ``` properties |
896 | | * element-properties-map, map = { |
897 | | * [openh264enc, gop-size=32, ], |
898 | | * [x264enc, key-int-max=32, tune=zerolatency], |
899 | | * } |
900 | | * ``` |
901 | | * |
902 | | * Since: 1.20 |
903 | | */ |
904 | | void |
905 | | gst_encoding_profile_set_element_properties (GstEncodingProfile * self, |
906 | | GstStructure * element_properties) |
907 | 0 | { |
908 | 0 | g_return_if_fail (GST_IS_ENCODING_PROFILE (self)); |
909 | 0 | g_return_if_fail (!element_properties |
910 | 0 | || GST_IS_STRUCTURE (element_properties)); |
911 | | |
912 | 0 | #ifndef G_DISABLE_CHECKS |
913 | 0 | if (element_properties && |
914 | 0 | (gst_structure_has_name (element_properties, "element-properties-map") |
915 | 0 | || gst_structure_has_name (element_properties, "properties-map") |
916 | 0 | || gst_structure_has_name (element_properties, "map"))) |
917 | 0 | g_return_if_fail (gst_structure_has_field_typed (element_properties, "map", |
918 | 0 | GST_TYPE_LIST)); |
919 | 0 | #endif |
920 | | |
921 | 0 | PROFILE_LOCK (self); |
922 | 0 | if (self->element_properties) |
923 | 0 | gst_structure_free (self->element_properties); |
924 | 0 | if (element_properties) |
925 | 0 | self->element_properties = element_properties; |
926 | 0 | else |
927 | 0 | self->element_properties = NULL; |
928 | 0 | PROFILE_UNLOCK (self); |
929 | |
|
930 | 0 | g_object_notify_by_pspec (G_OBJECT (self), |
931 | 0 | _properties[PROP_ELEMENT_PROPERTIES]); |
932 | 0 | } |
933 | | |
934 | | /** |
935 | | * gst_encoding_profile_get_element_properties: |
936 | | * @self: a #GstEncodingProfile |
937 | | * |
938 | | * Returns: (transfer full) (nullable): The properties that are going to be set on the underlying element |
939 | | * |
940 | | * Since: 1.20 |
941 | | */ |
942 | | GstStructure * |
943 | | gst_encoding_profile_get_element_properties (GstEncodingProfile * self) |
944 | 0 | { |
945 | 0 | GstStructure *res = NULL; |
946 | |
|
947 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (self), NULL); |
948 | | |
949 | 0 | PROFILE_LOCK (self); |
950 | 0 | if (self->element_properties) |
951 | 0 | res = gst_structure_copy (self->element_properties); |
952 | 0 | PROFILE_UNLOCK (self); |
953 | |
|
954 | 0 | return res; |
955 | 0 | } |
956 | | |
957 | | /* Container profiles */ |
958 | | |
959 | | struct _GstEncodingContainerProfile |
960 | | { |
961 | | GstEncodingProfile parent; |
962 | | |
963 | | GList *encodingprofiles; |
964 | | }; |
965 | | |
966 | | struct _GstEncodingContainerProfileClass |
967 | | { |
968 | | GstEncodingProfileClass parent; |
969 | | }; |
970 | | |
971 | 0 | G_DEFINE_TYPE (GstEncodingContainerProfile, gst_encoding_container_profile, |
972 | 0 | GST_TYPE_ENCODING_PROFILE); |
973 | 0 |
|
974 | 0 | static void |
975 | 0 | gst_encoding_container_profile_init (GstEncodingContainerProfile * prof) |
976 | 0 | { |
977 | | /* Nothing to initialize */ |
978 | 0 | } |
979 | | |
980 | | static void |
981 | | gst_encoding_container_profile_finalize (GObject * object) |
982 | 0 | { |
983 | 0 | GstEncodingContainerProfile *prof = (GstEncodingContainerProfile *) object; |
984 | |
|
985 | 0 | g_list_foreach (prof->encodingprofiles, (GFunc) g_object_unref, NULL); |
986 | 0 | g_list_free (prof->encodingprofiles); |
987 | |
|
988 | 0 | G_OBJECT_CLASS (gst_encoding_container_profile_parent_class)->finalize |
989 | 0 | ((GObject *) prof); |
990 | 0 | } |
991 | | |
992 | | static void |
993 | | gst_encoding_container_profile_copy (GstEncodingProfile * profile, |
994 | | GstEncodingProfile * copy_profile) |
995 | 0 | { |
996 | 0 | GstEncodingContainerProfile *self = GST_ENCODING_CONTAINER_PROFILE (profile), |
997 | 0 | *copy = GST_ENCODING_CONTAINER_PROFILE (copy_profile); |
998 | 0 | GList *tmp; |
999 | |
|
1000 | 0 | for (tmp = self->encodingprofiles; tmp; tmp = tmp->next) { |
1001 | 0 | gst_encoding_container_profile_add_profile (copy, |
1002 | 0 | gst_encoding_profile_copy (tmp->data)); |
1003 | 0 | } |
1004 | 0 | } |
1005 | | |
1006 | | static void |
1007 | | gst_encoding_container_profile_class_init (GstEncodingContainerProfileClass * k) |
1008 | 0 | { |
1009 | 0 | GObjectClass *gobject_class = (GObjectClass *) k; |
1010 | |
|
1011 | 0 | gobject_class->finalize = gst_encoding_container_profile_finalize; |
1012 | |
|
1013 | 0 | ((GstEncodingProfileClass *) k)->copy = gst_encoding_container_profile_copy; |
1014 | 0 | } |
1015 | | |
1016 | | /** |
1017 | | * gst_encoding_container_profile_get_profiles: |
1018 | | * @profile: a #GstEncodingContainerProfile |
1019 | | * |
1020 | | * Returns: (element-type GstPbutils.EncodingProfile) (transfer none): |
1021 | | * the list of contained #GstEncodingProfile. |
1022 | | */ |
1023 | | const GList * |
1024 | | gst_encoding_container_profile_get_profiles (GstEncodingContainerProfile * |
1025 | | profile) |
1026 | 0 | { |
1027 | 0 | g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (profile), NULL); |
1028 | | |
1029 | 0 | return profile->encodingprofiles; |
1030 | 0 | } |
1031 | | |
1032 | | /* Video profiles */ |
1033 | | |
1034 | | struct _GstEncodingVideoProfile |
1035 | | { |
1036 | | GstEncodingProfile parent; |
1037 | | |
1038 | | guint pass; |
1039 | | gboolean variableframerate; |
1040 | | }; |
1041 | | |
1042 | | struct _GstEncodingVideoProfileClass |
1043 | | { |
1044 | | GstEncodingProfileClass parent; |
1045 | | }; |
1046 | | |
1047 | 0 | G_DEFINE_TYPE (GstEncodingVideoProfile, gst_encoding_video_profile, |
1048 | 0 | GST_TYPE_ENCODING_PROFILE); |
1049 | 0 |
|
1050 | 0 | static void |
1051 | 0 | gst_encoding_video_profile_copy (GstEncodingProfile * profile, |
1052 | 0 | GstEncodingProfile * copy_profile) |
1053 | 0 | { |
1054 | 0 | GstEncodingVideoProfile *self = GST_ENCODING_VIDEO_PROFILE (profile), |
1055 | 0 | *copy = GST_ENCODING_VIDEO_PROFILE (copy_profile); |
1056 | |
|
1057 | 0 | copy->pass = self->pass; |
1058 | 0 | copy->variableframerate = self->variableframerate; |
1059 | 0 | } |
1060 | | |
1061 | | static void |
1062 | | gst_encoding_video_profile_init (GstEncodingVideoProfile * prof) |
1063 | 0 | { |
1064 | | /* Nothing to initialize */ |
1065 | 0 | } |
1066 | | |
1067 | | static void |
1068 | | gst_encoding_video_profile_class_init (GstEncodingVideoProfileClass * klass) |
1069 | 0 | { |
1070 | 0 | ((GstEncodingProfileClass *) klass)->copy = gst_encoding_video_profile_copy; |
1071 | 0 | } |
1072 | | |
1073 | | /** |
1074 | | * gst_encoding_video_profile_get_pass: |
1075 | | * @prof: a #GstEncodingVideoProfile |
1076 | | * |
1077 | | * Get the pass number if this is part of a multi-pass profile. |
1078 | | * |
1079 | | * Returns: The pass number. Starts at 1 for multi-pass. 0 if this is |
1080 | | * not a multi-pass profile |
1081 | | */ |
1082 | | guint |
1083 | | gst_encoding_video_profile_get_pass (GstEncodingVideoProfile * prof) |
1084 | 0 | { |
1085 | 0 | g_return_val_if_fail (GST_IS_ENCODING_VIDEO_PROFILE (prof), 0); |
1086 | | |
1087 | 0 | return prof->pass; |
1088 | 0 | } |
1089 | | |
1090 | | /** |
1091 | | * gst_encoding_video_profile_get_variableframerate: |
1092 | | * @prof: a #GstEncodingVideoProfile |
1093 | | * |
1094 | | * > *NOTE*: Fixed framerate won't be enforced when #encodebin:avoid-reencoding |
1095 | | * > is set. |
1096 | | * |
1097 | | * Returns: Whether non-constant video framerate is allowed for encoding. |
1098 | | */ |
1099 | | gboolean |
1100 | | gst_encoding_video_profile_get_variableframerate (GstEncodingVideoProfile * |
1101 | | prof) |
1102 | 0 | { |
1103 | 0 | g_return_val_if_fail (GST_IS_ENCODING_VIDEO_PROFILE (prof), FALSE); |
1104 | | |
1105 | 0 | return prof->variableframerate; |
1106 | 0 | } |
1107 | | |
1108 | | /** |
1109 | | * gst_encoding_video_profile_set_pass: |
1110 | | * @prof: a #GstEncodingVideoProfile |
1111 | | * @pass: the pass number for this profile |
1112 | | * |
1113 | | * Sets the pass number of this video profile. The first pass profile should have |
1114 | | * this value set to 1. If this video profile isn't part of a multi-pass profile, |
1115 | | * you may set it to 0 (the default value). |
1116 | | */ |
1117 | | void |
1118 | | gst_encoding_video_profile_set_pass (GstEncodingVideoProfile * prof, guint pass) |
1119 | 0 | { |
1120 | 0 | g_return_if_fail (GST_IS_ENCODING_VIDEO_PROFILE (prof)); |
1121 | | |
1122 | 0 | prof->pass = pass; |
1123 | 0 | } |
1124 | | |
1125 | | /** |
1126 | | * gst_encoding_video_profile_set_variableframerate: |
1127 | | * @prof: a #GstEncodingVideoProfile |
1128 | | * @variableframerate: a boolean |
1129 | | * |
1130 | | * If set to %TRUE, then the incoming stream will be allowed to have non-constant |
1131 | | * framerate. If set to %FALSE (default value), then the incoming stream will |
1132 | | * be normalized by dropping/duplicating frames in order to produce a |
1133 | | * constance framerate. |
1134 | | */ |
1135 | | void |
1136 | | gst_encoding_video_profile_set_variableframerate (GstEncodingVideoProfile * |
1137 | | prof, gboolean variableframerate) |
1138 | 0 | { |
1139 | 0 | g_return_if_fail (GST_IS_ENCODING_VIDEO_PROFILE (prof)); |
1140 | | |
1141 | 0 | prof->variableframerate = variableframerate; |
1142 | 0 | } |
1143 | | |
1144 | | /* Audio profiles */ |
1145 | | |
1146 | | struct _GstEncodingAudioProfile |
1147 | | { |
1148 | | GstEncodingProfile parent; |
1149 | | }; |
1150 | | |
1151 | | struct _GstEncodingAudioProfileClass |
1152 | | { |
1153 | | GstEncodingProfileClass parent; |
1154 | | }; |
1155 | | |
1156 | 0 | G_DEFINE_TYPE (GstEncodingAudioProfile, gst_encoding_audio_profile, |
1157 | 0 | GST_TYPE_ENCODING_PROFILE); |
1158 | 0 |
|
1159 | 0 | static void |
1160 | 0 | gst_encoding_audio_profile_init (GstEncodingAudioProfile * prof) |
1161 | 0 | { |
1162 | | /* Nothing to initialize */ |
1163 | 0 | } |
1164 | | |
1165 | | static void |
1166 | | gst_encoding_audio_profile_class_init (GstEncodingAudioProfileClass * klass) |
1167 | 0 | { |
1168 | 0 | } |
1169 | | |
1170 | | static inline gboolean |
1171 | | _gst_caps_is_equal_safe (GstCaps * a, GstCaps * b) |
1172 | 0 | { |
1173 | 0 | if (a == b) |
1174 | 0 | return TRUE; |
1175 | 0 | if ((a == NULL) || (b == NULL)) |
1176 | 0 | return FALSE; |
1177 | 0 | return gst_caps_is_equal (a, b); |
1178 | 0 | } |
1179 | | |
1180 | | static gint |
1181 | | _compare_container_encoding_profiles (GstEncodingContainerProfile * ca, |
1182 | | GstEncodingContainerProfile * cb) |
1183 | 0 | { |
1184 | 0 | GList *tmp; |
1185 | |
|
1186 | 0 | if (g_list_length (ca->encodingprofiles) != |
1187 | 0 | g_list_length (cb->encodingprofiles)) |
1188 | 0 | return -1; |
1189 | | |
1190 | 0 | for (tmp = ca->encodingprofiles; tmp; tmp = tmp->next) { |
1191 | 0 | GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data; |
1192 | 0 | if (!gst_encoding_container_profile_contains_profile (ca, prof)) |
1193 | 0 | return -1; |
1194 | 0 | } |
1195 | | |
1196 | 0 | return 0; |
1197 | 0 | } |
1198 | | |
1199 | | static gint |
1200 | | _compare_encoding_profiles (const GstEncodingProfile * a, |
1201 | | const GstEncodingProfile * b) |
1202 | 0 | { |
1203 | 0 | if ((G_TYPE_FROM_INSTANCE (a) != G_TYPE_FROM_INSTANCE (b)) || |
1204 | 0 | !_gst_caps_is_equal_safe (a->format, b->format) || |
1205 | 0 | (g_strcmp0 (a->preset, b->preset) != 0) || |
1206 | 0 | (g_strcmp0 (a->preset_name, b->preset_name) != 0) || |
1207 | 0 | (g_strcmp0 (a->name, b->name) != 0) || |
1208 | 0 | (g_strcmp0 (a->description, b->description) != 0)) |
1209 | 0 | return -1; |
1210 | | |
1211 | 0 | if (GST_IS_ENCODING_CONTAINER_PROFILE (a)) |
1212 | 0 | return |
1213 | 0 | _compare_container_encoding_profiles (GST_ENCODING_CONTAINER_PROFILE |
1214 | 0 | (a), GST_ENCODING_CONTAINER_PROFILE (b)); |
1215 | | |
1216 | 0 | if (GST_IS_ENCODING_VIDEO_PROFILE (a)) { |
1217 | 0 | GstEncodingVideoProfile *va = (GstEncodingVideoProfile *) a; |
1218 | 0 | GstEncodingVideoProfile *vb = (GstEncodingVideoProfile *) b; |
1219 | |
|
1220 | 0 | if ((va->pass != vb->pass) |
1221 | 0 | || (va->variableframerate != vb->variableframerate)) |
1222 | 0 | return -1; |
1223 | 0 | } |
1224 | | |
1225 | 0 | return 0; |
1226 | 0 | } |
1227 | | |
1228 | | /** |
1229 | | * gst_encoding_container_profile_contains_profile: |
1230 | | * @container: a #GstEncodingContainerProfile |
1231 | | * @profile: a #GstEncodingProfile |
1232 | | * |
1233 | | * Checks if @container contains a #GstEncodingProfile identical to |
1234 | | * @profile. |
1235 | | * |
1236 | | * Returns: %TRUE if @container contains a #GstEncodingProfile identical |
1237 | | * to @profile, else %FALSE. |
1238 | | */ |
1239 | | gboolean |
1240 | | gst_encoding_container_profile_contains_profile (GstEncodingContainerProfile * |
1241 | | container, GstEncodingProfile * profile) |
1242 | 0 | { |
1243 | 0 | g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (container), FALSE); |
1244 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
1245 | | |
1246 | 0 | return (g_list_find_custom (container->encodingprofiles, profile, |
1247 | 0 | (GCompareFunc) _compare_encoding_profiles) != NULL); |
1248 | 0 | } |
1249 | | |
1250 | | /** |
1251 | | * gst_encoding_container_profile_add_profile: |
1252 | | * @container: the #GstEncodingContainerProfile to use |
1253 | | * @profile: (transfer full): the #GstEncodingProfile to add. |
1254 | | * |
1255 | | * Add a #GstEncodingProfile to the list of profiles handled by @container. |
1256 | | * |
1257 | | * No copy of @profile will be made, if you wish to use it elsewhere after this |
1258 | | * method you should increment its reference count. |
1259 | | * |
1260 | | * Returns: %TRUE if the @stream was properly added, else %FALSE. |
1261 | | */ |
1262 | | gboolean |
1263 | | gst_encoding_container_profile_add_profile (GstEncodingContainerProfile * |
1264 | | container, GstEncodingProfile * profile) |
1265 | 0 | { |
1266 | 0 | g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (container), FALSE); |
1267 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
1268 | | |
1269 | 0 | container->encodingprofiles = |
1270 | 0 | g_list_append (container->encodingprofiles, profile); |
1271 | |
|
1272 | 0 | return TRUE; |
1273 | 0 | } |
1274 | | |
1275 | | static GstEncodingProfile * |
1276 | | common_creation (GType objtype, GstCaps * format, const gchar * preset, |
1277 | | const gchar * name, const gchar * description, GstCaps * restriction, |
1278 | | guint presence) |
1279 | 0 | { |
1280 | 0 | GstEncodingProfile *prof; |
1281 | |
|
1282 | 0 | prof = (GstEncodingProfile *) g_object_new (objtype, NULL); |
1283 | |
|
1284 | 0 | if (name) |
1285 | 0 | prof->name = g_strdup (name); |
1286 | 0 | if (description) |
1287 | 0 | prof->description = g_strdup (description); |
1288 | 0 | if (preset) |
1289 | 0 | prof->preset = g_strdup (preset); |
1290 | 0 | if (format) |
1291 | 0 | prof->format = gst_caps_ref (format); |
1292 | 0 | if (restriction) |
1293 | 0 | prof->restriction = gst_caps_ref (restriction); |
1294 | 0 | prof->presence = presence; |
1295 | 0 | prof->preset_name = NULL; |
1296 | 0 | prof->allow_dynamic_output = TRUE; |
1297 | 0 | prof->enabled = TRUE; |
1298 | |
|
1299 | 0 | return prof; |
1300 | 0 | } |
1301 | | |
1302 | | /** |
1303 | | * gst_encoding_container_profile_new: |
1304 | | * @name: (nullable): The name of the container profile, can be %NULL |
1305 | | * @description: (nullable): The description of the container profile, |
1306 | | * can be %NULL |
1307 | | * @format: (transfer none): The format to use for this profile |
1308 | | * @preset: (nullable): The preset to use for this profile. |
1309 | | * |
1310 | | * Creates a new #GstEncodingContainerProfile. |
1311 | | * |
1312 | | * Returns: The newly created #GstEncodingContainerProfile. |
1313 | | */ |
1314 | | GstEncodingContainerProfile * |
1315 | | gst_encoding_container_profile_new (const gchar * name, |
1316 | | const gchar * description, GstCaps * format, const gchar * preset) |
1317 | 0 | { |
1318 | 0 | g_return_val_if_fail (GST_IS_CAPS (format), NULL); |
1319 | | |
1320 | 0 | return (GstEncodingContainerProfile *) |
1321 | 0 | common_creation (GST_TYPE_ENCODING_CONTAINER_PROFILE, format, preset, |
1322 | 0 | name, description, NULL, 0); |
1323 | 0 | } |
1324 | | |
1325 | | /** |
1326 | | * gst_encoding_video_profile_new: |
1327 | | * @format: (transfer none): the #GstCaps |
1328 | | * @preset: (nullable): the preset(s) to use on the encoder, can be %NULL |
1329 | | * @restriction: (nullable): the #GstCaps used to restrict the input to the encoder, can be |
1330 | | * NULL. See gst_encoding_profile_get_restriction() for more details. |
1331 | | * @presence: the number of time this stream must be used. 0 means any number of |
1332 | | * times (including never) |
1333 | | * |
1334 | | * Creates a new #GstEncodingVideoProfile |
1335 | | * |
1336 | | * All provided allocatable arguments will be internally copied, so can be |
1337 | | * safely freed/unreferenced after calling this method. |
1338 | | * |
1339 | | * If you wish to control the pass number (in case of multi-pass scenarios), |
1340 | | * please refer to the gst_encoding_video_profile_set_pass() documentation. |
1341 | | * |
1342 | | * If you wish to use/force a constant framerate please refer to the |
1343 | | * gst_encoding_video_profile_set_variableframerate() documentation. |
1344 | | * |
1345 | | * Returns: the newly created #GstEncodingVideoProfile. |
1346 | | */ |
1347 | | GstEncodingVideoProfile * |
1348 | | gst_encoding_video_profile_new (GstCaps * format, const gchar * preset, |
1349 | | GstCaps * restriction, guint presence) |
1350 | 0 | { |
1351 | 0 | return (GstEncodingVideoProfile *) |
1352 | 0 | common_creation (GST_TYPE_ENCODING_VIDEO_PROFILE, format, preset, NULL, |
1353 | 0 | NULL, restriction, presence); |
1354 | 0 | } |
1355 | | |
1356 | | /** |
1357 | | * gst_encoding_audio_profile_new: |
1358 | | * @format: (transfer none): the #GstCaps |
1359 | | * @preset: (nullable): the preset(s) to use on the encoder, can be %NULL |
1360 | | * @restriction: (nullable): the #GstCaps used to restrict the input to the encoder, can be |
1361 | | * NULL. See gst_encoding_profile_get_restriction() for more details. |
1362 | | * @presence: the number of time this stream must be used. 0 means any number of |
1363 | | * times (including never) |
1364 | | * |
1365 | | * Creates a new #GstEncodingAudioProfile |
1366 | | * |
1367 | | * All provided allocatable arguments will be internally copied, so can be |
1368 | | * safely freed/unreferenced after calling this method. |
1369 | | * |
1370 | | * Returns: the newly created #GstEncodingAudioProfile. |
1371 | | */ |
1372 | | GstEncodingAudioProfile * |
1373 | | gst_encoding_audio_profile_new (GstCaps * format, const gchar * preset, |
1374 | | GstCaps * restriction, guint presence) |
1375 | 0 | { |
1376 | 0 | return (GstEncodingAudioProfile *) |
1377 | 0 | common_creation (GST_TYPE_ENCODING_AUDIO_PROFILE, format, preset, NULL, |
1378 | 0 | NULL, restriction, presence); |
1379 | 0 | } |
1380 | | |
1381 | | |
1382 | | /** |
1383 | | * gst_encoding_profile_is_equal: |
1384 | | * @a: a #GstEncodingProfile |
1385 | | * @b: a #GstEncodingProfile |
1386 | | * |
1387 | | * Checks whether the two #GstEncodingProfile are equal |
1388 | | * |
1389 | | * Returns: %TRUE if @a and @b are equal, else %FALSE. |
1390 | | */ |
1391 | | gboolean |
1392 | | gst_encoding_profile_is_equal (GstEncodingProfile * a, GstEncodingProfile * b) |
1393 | 0 | { |
1394 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (a), FALSE); |
1395 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (b), FALSE); |
1396 | | |
1397 | 0 | return (_compare_encoding_profiles (a, b) == 0); |
1398 | 0 | } |
1399 | | |
1400 | | |
1401 | | /** |
1402 | | * gst_encoding_profile_get_input_caps: |
1403 | | * @profile: a #GstEncodingProfile |
1404 | | * |
1405 | | * Computes the full output caps that this @profile will be able to consume. |
1406 | | * |
1407 | | * Returns: (transfer full): The full caps the given @profile can consume. Call |
1408 | | * gst_caps_unref() when you are done with the caps. |
1409 | | */ |
1410 | | GstCaps * |
1411 | | gst_encoding_profile_get_input_caps (GstEncodingProfile * profile) |
1412 | 0 | { |
1413 | 0 | GstCaps *out, *tmp; |
1414 | 0 | GList *ltmp; |
1415 | 0 | GstStructure *st, *outst; |
1416 | 0 | const gchar *out_name; |
1417 | 0 | guint i, len; |
1418 | 0 | GstCaps *fcaps; |
1419 | |
|
1420 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
1421 | | |
1422 | 0 | if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) { |
1423 | 0 | GstCaps *res = gst_caps_new_empty (); |
1424 | |
|
1425 | 0 | for (ltmp = GST_ENCODING_CONTAINER_PROFILE (profile)->encodingprofiles; |
1426 | 0 | ltmp; ltmp = ltmp->next) { |
1427 | 0 | GstEncodingProfile *sprof = (GstEncodingProfile *) ltmp->data; |
1428 | 0 | res = gst_caps_merge (res, gst_encoding_profile_get_input_caps (sprof)); |
1429 | 0 | } |
1430 | 0 | return res; |
1431 | 0 | } |
1432 | | |
1433 | 0 | fcaps = profile->format; |
1434 | | |
1435 | | /* fast-path */ |
1436 | 0 | if ((profile->restriction == NULL) || gst_caps_is_any (profile->restriction)) |
1437 | 0 | return gst_caps_ref (fcaps); |
1438 | | |
1439 | | /* Combine the format with the restriction caps */ |
1440 | 0 | outst = gst_caps_get_structure (fcaps, 0); |
1441 | 0 | out_name = gst_structure_get_name (outst); |
1442 | 0 | tmp = gst_caps_new_empty (); |
1443 | 0 | len = gst_caps_get_size (profile->restriction); |
1444 | |
|
1445 | 0 | for (i = 0; i < len; i++) { |
1446 | 0 | st = gst_structure_copy (gst_caps_get_structure (profile->restriction, i)); |
1447 | 0 | gst_structure_set_name (st, out_name); |
1448 | 0 | gst_caps_append_structure (tmp, st); |
1449 | 0 | } |
1450 | |
|
1451 | 0 | out = gst_caps_intersect (tmp, fcaps); |
1452 | 0 | gst_caps_unref (tmp); |
1453 | |
|
1454 | 0 | return out; |
1455 | 0 | } |
1456 | | |
1457 | | /** |
1458 | | * gst_encoding_profile_get_type_nick: |
1459 | | * @profile: a #GstEncodingProfile |
1460 | | * |
1461 | | * Returns: the human-readable name of the type of @profile. |
1462 | | */ |
1463 | | const gchar * |
1464 | | gst_encoding_profile_get_type_nick (GstEncodingProfile * profile) |
1465 | 0 | { |
1466 | 0 | if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) |
1467 | 0 | return "container"; |
1468 | 0 | if (GST_IS_ENCODING_VIDEO_PROFILE (profile)) |
1469 | 0 | return "video"; |
1470 | 0 | if (GST_IS_ENCODING_AUDIO_PROFILE (profile)) |
1471 | 0 | return "audio"; |
1472 | | |
1473 | 0 | g_assert_not_reached (); |
1474 | 0 | return NULL; |
1475 | 0 | } |
1476 | | |
1477 | | extern const gchar *pb_utils_get_file_extension_from_caps (const GstCaps * |
1478 | | caps); |
1479 | | gboolean pb_utils_is_tag (const GstCaps * caps); |
1480 | | |
1481 | | static gboolean |
1482 | | gst_encoding_profile_has_format (GstEncodingProfile * profile, |
1483 | | const gchar * media_type) |
1484 | 0 | { |
1485 | 0 | GstCaps *caps; |
1486 | 0 | gboolean ret; |
1487 | |
|
1488 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE); |
1489 | | |
1490 | 0 | caps = gst_encoding_profile_get_format (profile); |
1491 | 0 | ret = gst_structure_has_name (gst_caps_get_structure (caps, 0), media_type); |
1492 | 0 | gst_caps_unref (caps); |
1493 | |
|
1494 | 0 | return ret; |
1495 | 0 | } |
1496 | | |
1497 | | static gboolean |
1498 | | gst_encoding_container_profile_has_video (GstEncodingContainerProfile * profile) |
1499 | 0 | { |
1500 | 0 | const GList *l; |
1501 | |
|
1502 | 0 | g_return_val_if_fail (GST_IS_ENCODING_CONTAINER_PROFILE (profile), FALSE); |
1503 | | |
1504 | 0 | for (l = profile->encodingprofiles; l != NULL; l = l->next) { |
1505 | 0 | if (GST_IS_ENCODING_VIDEO_PROFILE (l->data)) |
1506 | 0 | return TRUE; |
1507 | 0 | if (GST_IS_ENCODING_CONTAINER_PROFILE (l->data) && |
1508 | 0 | gst_encoding_container_profile_has_video (l->data)) |
1509 | 0 | return TRUE; |
1510 | 0 | } |
1511 | | |
1512 | 0 | return FALSE; |
1513 | 0 | } |
1514 | | |
1515 | | /** |
1516 | | * gst_encoding_profile_get_file_extension: |
1517 | | * @profile: a #GstEncodingProfile |
1518 | | * |
1519 | | * Returns: (nullable): a suitable file extension for @profile, or NULL. |
1520 | | */ |
1521 | | const gchar * |
1522 | | gst_encoding_profile_get_file_extension (GstEncodingProfile * profile) |
1523 | 0 | { |
1524 | 0 | GstEncodingContainerProfile *cprofile; |
1525 | 0 | const gchar *ext = NULL; |
1526 | 0 | gboolean has_video; |
1527 | 0 | GstCaps *caps; |
1528 | 0 | guint num_children; |
1529 | |
|
1530 | 0 | g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), NULL); |
1531 | | |
1532 | 0 | caps = gst_encoding_profile_get_format (profile); |
1533 | 0 | ext = pb_utils_get_file_extension_from_caps (caps); |
1534 | |
|
1535 | 0 | if (!GST_IS_ENCODING_CONTAINER_PROFILE (profile)) |
1536 | 0 | goto done; |
1537 | | |
1538 | 0 | cprofile = GST_ENCODING_CONTAINER_PROFILE (profile); |
1539 | |
|
1540 | 0 | num_children = g_list_length (cprofile->encodingprofiles); |
1541 | | |
1542 | | /* if it's a tag container profile (e.g. id3mux/apemux), we need |
1543 | | * to look at what's inside it */ |
1544 | 0 | if (pb_utils_is_tag (caps)) { |
1545 | 0 | GST_DEBUG ("tag container profile"); |
1546 | 0 | if (num_children == 1) { |
1547 | 0 | GstEncodingProfile *child_profile = cprofile->encodingprofiles->data; |
1548 | |
|
1549 | 0 | ext = gst_encoding_profile_get_file_extension (child_profile); |
1550 | 0 | } else { |
1551 | 0 | GST_WARNING ("expected exactly one child profile with tag profile"); |
1552 | 0 | } |
1553 | 0 | goto done; |
1554 | 0 | } |
1555 | | |
1556 | 0 | if (num_children == 0) |
1557 | 0 | goto done; |
1558 | | |
1559 | | /* special cases */ |
1560 | 0 | has_video = gst_encoding_container_profile_has_video (cprofile); |
1561 | | |
1562 | | /* Ogg */ |
1563 | 0 | if (g_strcmp0 (ext, "ogg") == 0) { |
1564 | | /* ogg with video => .ogv */ |
1565 | 0 | if (has_video) { |
1566 | 0 | ext = "ogv"; |
1567 | 0 | goto done; |
1568 | 0 | } |
1569 | | /* ogg with just speex audio => .spx */ |
1570 | 0 | if (num_children == 1) { |
1571 | 0 | GstEncodingProfile *child_profile = cprofile->encodingprofiles->data; |
1572 | |
|
1573 | 0 | if (GST_IS_ENCODING_AUDIO_PROFILE (child_profile) && |
1574 | 0 | gst_encoding_profile_has_format (child_profile, "audio/x-speex")) { |
1575 | 0 | ext = "spx"; |
1576 | 0 | goto done; |
1577 | 0 | } |
1578 | 0 | } |
1579 | | /* does anyone actually use .oga for ogg audio files? */ |
1580 | 0 | goto done; |
1581 | 0 | } |
1582 | | |
1583 | | /* Matroska */ |
1584 | 0 | if (has_video && g_strcmp0 (ext, "mka") == 0) { |
1585 | 0 | ext = "mkv"; |
1586 | 0 | goto done; |
1587 | 0 | } |
1588 | | |
1589 | | /* Windows Media / ASF */ |
1590 | 0 | if (gst_encoding_profile_has_format (profile, "video/x-ms-asf")) { |
1591 | 0 | const GList *l; |
1592 | 0 | guint num_wmv = 0, num_wma = 0, num_other = 0; |
1593 | |
|
1594 | 0 | for (l = cprofile->encodingprofiles; l != NULL; l = l->next) { |
1595 | 0 | if (gst_encoding_profile_has_format (l->data, "video/x-wmv")) |
1596 | 0 | ++num_wmv; |
1597 | 0 | else if (gst_encoding_profile_has_format (l->data, "audio/x-wma")) |
1598 | 0 | ++num_wma; |
1599 | 0 | else |
1600 | 0 | ++num_other; |
1601 | 0 | } |
1602 | |
|
1603 | 0 | if (num_other > 0) |
1604 | 0 | ext = "asf"; |
1605 | 0 | else if (num_wmv > 0) |
1606 | 0 | ext = "wmv"; |
1607 | 0 | else if (num_wma > 0) |
1608 | 0 | ext = "wma"; |
1609 | |
|
1610 | 0 | goto done; |
1611 | 0 | } |
1612 | | |
1613 | 0 | done: |
1614 | |
|
1615 | 0 | GST_INFO ("caps %" GST_PTR_FORMAT ", ext: %s", caps, GST_STR_NULL (ext)); |
1616 | 0 | gst_caps_unref (caps); |
1617 | 0 | return ext; |
1618 | 0 | } |
1619 | | |
1620 | | /** |
1621 | | * gst_encoding_profile_find: |
1622 | | * @targetname: (transfer none): The name of the target |
1623 | | * @profilename: (transfer none) (nullable): The name of the profile, if %NULL |
1624 | | * provided, it will default to the encoding profile called `default`. |
1625 | | * @category: (transfer none) (nullable): The target category. Can be %NULL |
1626 | | * |
1627 | | * Find the #GstEncodingProfile with the specified name and category. |
1628 | | * |
1629 | | * Returns: (transfer full) (nullable): The matching #GstEncodingProfile or %NULL. |
1630 | | */ |
1631 | | GstEncodingProfile * |
1632 | | gst_encoding_profile_find (const gchar * targetname, const gchar * profilename, |
1633 | | const gchar * category) |
1634 | 0 | { |
1635 | 0 | GstEncodingProfile *res = NULL; |
1636 | 0 | GstEncodingTarget *target; |
1637 | |
|
1638 | 0 | g_return_val_if_fail (targetname != NULL, NULL); |
1639 | | |
1640 | 0 | target = gst_encoding_target_load (targetname, category, NULL); |
1641 | 0 | if (target) { |
1642 | 0 | res = |
1643 | 0 | gst_encoding_target_get_profile (target, |
1644 | 0 | profilename ? profilename : "default"); |
1645 | 0 | gst_encoding_target_unref (target); |
1646 | 0 | } |
1647 | |
|
1648 | 0 | return res; |
1649 | 0 | } |
1650 | | |
1651 | | static GstEncodingProfile * |
1652 | | combo_search (const gchar * pname) |
1653 | 0 | { |
1654 | 0 | GstEncodingProfile *res = NULL; |
1655 | 0 | gchar **split; |
1656 | 0 | gint split_length; |
1657 | | |
1658 | | /* Splitup */ |
1659 | 0 | split = g_strsplit (pname, "/", 3); |
1660 | 0 | split_length = g_strv_length (split); |
1661 | 0 | if (split_length > 3 || split_length <= 0) |
1662 | 0 | goto done; |
1663 | | |
1664 | 0 | res = gst_encoding_profile_find (split[0], |
1665 | 0 | split_length == 2 ? split[1] : NULL, split_length == 3 ? split[2] : NULL); |
1666 | | |
1667 | |
|
1668 | 0 | done: |
1669 | 0 | g_strfreev (split); |
1670 | |
|
1671 | 0 | return res; |
1672 | 0 | } |
1673 | | |
1674 | | static GstCaps * |
1675 | | get_profile_format_from_possible_factory_name (const gchar * factory_desc, |
1676 | | gchar ** new_factory_name, GstCaps ** restrictions, |
1677 | | gboolean * is_rendering_muxer) |
1678 | 0 | { |
1679 | 0 | GList *tmp; |
1680 | 0 | GstCaps *caps = NULL, *tmpcaps = gst_caps_from_string (factory_desc); |
1681 | 0 | GstStructure *tmpstruct; |
1682 | 0 | GstElementFactory *fact = NULL; |
1683 | |
|
1684 | 0 | if (is_rendering_muxer) |
1685 | 0 | *is_rendering_muxer = FALSE; |
1686 | 0 | *new_factory_name = NULL; |
1687 | 0 | if (!tmpcaps || gst_caps_get_size (tmpcaps) != 1) |
1688 | 0 | goto done; |
1689 | | |
1690 | 0 | tmpstruct = gst_caps_get_structure (tmpcaps, 0); |
1691 | 0 | fact = gst_element_factory_find (gst_structure_get_name (tmpstruct)); |
1692 | 0 | if (!fact) |
1693 | 0 | goto done; |
1694 | | |
1695 | 0 | if (!gst_element_factory_list_is_type (fact, |
1696 | 0 | GST_ELEMENT_FACTORY_TYPE_ENCODER | GST_ELEMENT_FACTORY_TYPE_MUXER)) { |
1697 | 0 | GST_ERROR_OBJECT (fact, |
1698 | 0 | "is not an encoder or muxer, it can't be" |
1699 | 0 | " used in an encoding profile."); |
1700 | 0 | goto done; |
1701 | 0 | } |
1702 | | |
1703 | 0 | for (tmp = (GList *) gst_element_factory_get_static_pad_templates (fact); |
1704 | 0 | tmp; tmp = tmp->next) { |
1705 | 0 | GstStaticPadTemplate *templ = ((GstStaticPadTemplate *) tmp->data); |
1706 | |
|
1707 | 0 | if (templ->direction == GST_PAD_SRC) { |
1708 | 0 | GstCaps *tmpcaps = gst_static_caps_get (&templ->static_caps); |
1709 | |
|
1710 | 0 | if (tmpcaps) { |
1711 | 0 | if (gst_caps_get_size (tmpcaps) > 0) |
1712 | 0 | caps = |
1713 | 0 | gst_caps_new_empty_simple (gst_structure_get_name |
1714 | 0 | (gst_caps_get_structure (tmpcaps, 0))); |
1715 | |
|
1716 | 0 | gst_caps_unref (tmpcaps); |
1717 | 0 | } |
1718 | 0 | if (caps) |
1719 | 0 | break; |
1720 | 0 | } |
1721 | 0 | } |
1722 | |
|
1723 | 0 | if (caps) { |
1724 | 0 | *new_factory_name = g_strdup (gst_structure_get_name (tmpstruct)); |
1725 | |
|
1726 | 0 | if (gst_structure_n_fields (tmpstruct) && restrictions) { |
1727 | 0 | const gchar *sname = |
1728 | 0 | gst_structure_get_name (gst_caps_get_structure (caps, 0)); |
1729 | |
|
1730 | 0 | if (g_str_has_prefix (sname, "audio/")) |
1731 | 0 | gst_structure_set_name (tmpstruct, "audio/x-raw"); |
1732 | 0 | else if (g_str_has_prefix (sname, "video/") || |
1733 | 0 | g_str_has_prefix (sname, "image/")) |
1734 | 0 | gst_structure_set_name (tmpstruct, "video/x-raw"); |
1735 | |
|
1736 | 0 | *restrictions = tmpcaps; |
1737 | 0 | tmpcaps = NULL; |
1738 | 0 | } |
1739 | 0 | } else if (gst_element_factory_list_is_type (fact, |
1740 | 0 | GST_ELEMENT_FACTORY_TYPE_MUXER)) { |
1741 | 0 | *new_factory_name = g_strdup (gst_structure_get_name (tmpstruct)); |
1742 | |
|
1743 | 0 | caps = gst_caps_ref (gst_caps_new_empty ()); |
1744 | 0 | if (is_rendering_muxer) |
1745 | 0 | *is_rendering_muxer = TRUE; |
1746 | 0 | } |
1747 | | |
1748 | |
|
1749 | 0 | done: |
1750 | 0 | if (fact) |
1751 | 0 | gst_object_unref (fact); |
1752 | |
|
1753 | 0 | if (tmpcaps) |
1754 | 0 | gst_caps_unref (tmpcaps); |
1755 | |
|
1756 | 0 | return caps; |
1757 | 0 | } |
1758 | | |
1759 | | static GstEncodingProfile * |
1760 | | create_encoding_profile_from_caps (GstCaps * caps, gchar * preset_name, |
1761 | | GstCaps * restrictioncaps, gint presence, gboolean single_segment, |
1762 | | gchar * factory_name, GList * muxers_and_encoders, GstCaps * raw_audio_caps, |
1763 | | GstCaps * raw_video_caps, gboolean is_rendering_muxer) |
1764 | 0 | { |
1765 | 0 | GstEncodingProfile *profile = NULL; |
1766 | 0 | GList *factories = NULL; |
1767 | 0 | gboolean is_raw_audio = FALSE, is_raw_video = FALSE; |
1768 | |
|
1769 | 0 | if (is_rendering_muxer) { |
1770 | 0 | profile = |
1771 | 0 | GST_ENCODING_PROFILE (gst_encoding_container_profile_new |
1772 | 0 | ("User profile", "User profile", caps, NULL)); |
1773 | 0 | goto done; |
1774 | 0 | } |
1775 | | |
1776 | 0 | if (gst_caps_can_intersect (raw_audio_caps, caps)) { |
1777 | 0 | is_raw_audio = TRUE; |
1778 | 0 | } else if (gst_caps_can_intersect (raw_video_caps, caps)) { |
1779 | 0 | is_raw_video = TRUE; |
1780 | 0 | } else { |
1781 | 0 | factories = gst_element_factory_list_filter (muxers_and_encoders, caps, |
1782 | 0 | GST_PAD_SRC, FALSE); |
1783 | |
|
1784 | 0 | if (!factories) { |
1785 | 0 | GST_INFO ("Could not find factory for %" GST_PTR_FORMAT, caps); |
1786 | 0 | return NULL; |
1787 | 0 | } |
1788 | 0 | } |
1789 | | |
1790 | 0 | if (is_raw_audio || (factories |
1791 | 0 | && gst_element_factory_list_is_type (factories->data, |
1792 | 0 | GST_ELEMENT_FACTORY_TYPE_AUDIO_ENCODER))) |
1793 | 0 | profile = |
1794 | 0 | GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps, preset_name, |
1795 | 0 | restrictioncaps, presence)); |
1796 | 0 | else if (is_raw_video || (factories |
1797 | 0 | && gst_element_factory_list_is_type (factories->data, |
1798 | 0 | GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER))) |
1799 | 0 | profile = |
1800 | 0 | GST_ENCODING_PROFILE (gst_encoding_video_profile_new (caps, preset_name, |
1801 | 0 | restrictioncaps, presence)); |
1802 | 0 | else if (gst_element_factory_list_is_type (factories->data, |
1803 | 0 | GST_ELEMENT_FACTORY_TYPE_MUXER)) |
1804 | 0 | profile = |
1805 | 0 | GST_ENCODING_PROFILE (gst_encoding_container_profile_new |
1806 | 0 | ("User profile", "User profile", caps, NULL)); |
1807 | |
|
1808 | 0 | if (factories) |
1809 | 0 | gst_plugin_feature_list_free (factories); |
1810 | |
|
1811 | 0 | done: |
1812 | 0 | if (factory_name && profile) |
1813 | 0 | gst_encoding_profile_set_preset_name (profile, factory_name); |
1814 | 0 | gst_encoding_profile_set_single_segment (profile, single_segment); |
1815 | |
|
1816 | 0 | g_free (factory_name); |
1817 | |
|
1818 | 0 | return profile; |
1819 | 0 | } |
1820 | | |
1821 | | static gboolean |
1822 | | gst_structure_validate_name (const gchar * name) |
1823 | 0 | { |
1824 | 0 | const gchar *s; |
1825 | |
|
1826 | 0 | g_return_val_if_fail (name != NULL, FALSE); |
1827 | | |
1828 | 0 | if (G_UNLIKELY (!g_ascii_isalpha (*name))) |
1829 | 0 | return FALSE; |
1830 | | |
1831 | | /* FIXME: test name string more */ |
1832 | 0 | s = &name[1]; |
1833 | 0 | while (*s && (g_ascii_isalnum (*s) || strchr ("/-_.:+", *s) != NULL)) |
1834 | 0 | s++; |
1835 | |
|
1836 | 0 | if (*s == ',') |
1837 | 0 | return TRUE; |
1838 | | |
1839 | 0 | if (G_UNLIKELY (*s != '\0')) |
1840 | 0 | return FALSE; |
1841 | | |
1842 | 0 | return TRUE; |
1843 | 0 | } |
1844 | | |
1845 | | static GstEncodingProfile * |
1846 | | create_encoding_stream_profile (gchar * serialized_profile, |
1847 | | GList * muxers_and_encoders, GstCaps * raw_audio_caps, |
1848 | | GstCaps * raw_video_caps) |
1849 | 0 | { |
1850 | 0 | GstCaps *caps; |
1851 | 0 | guint presence = 0; |
1852 | 0 | gboolean single_segment = FALSE; |
1853 | 0 | gchar *strcaps = NULL, *strpresence, **strprops_v = |
1854 | 0 | NULL, **restriction_format, **preset_v = NULL, *preset_name = |
1855 | 0 | NULL, *factory_name = NULL, *variable_framerate = NULL; |
1856 | 0 | GstStructure *element_properties = NULL; |
1857 | 0 | GstCaps *restrictioncaps = NULL; |
1858 | 0 | GstEncodingProfile *profile = NULL; |
1859 | |
|
1860 | 0 | restriction_format = g_strsplit (serialized_profile, "->", 0); |
1861 | 0 | if (restriction_format[1]) { |
1862 | 0 | restrictioncaps = gst_caps_from_string (restriction_format[0]); |
1863 | 0 | strcaps = g_strdup (restriction_format[1]); |
1864 | 0 | } else { |
1865 | 0 | restrictioncaps = NULL; |
1866 | 0 | strcaps = g_strdup (restriction_format[0]); |
1867 | 0 | } |
1868 | 0 | g_strfreev (restriction_format); |
1869 | |
|
1870 | 0 | preset_v = g_strsplit (strcaps, "+", 0); |
1871 | 0 | if (preset_v[1]) { |
1872 | 0 | strpresence = preset_v[1]; |
1873 | 0 | g_free (strcaps); |
1874 | 0 | strcaps = g_strdup (preset_v[0]); |
1875 | 0 | } else { |
1876 | 0 | strpresence = preset_v[0]; |
1877 | 0 | } |
1878 | |
|
1879 | 0 | strprops_v = g_strsplit (strpresence, "|", 0); |
1880 | 0 | if (strprops_v[1]) { /* We have a properties */ |
1881 | 0 | gchar *endptr; |
1882 | 0 | guint propi; |
1883 | |
|
1884 | 0 | if (preset_v[1]) { /* We have preset and properties */ |
1885 | 0 | preset_name = g_strdup (strprops_v[0]); |
1886 | 0 | } else { /* We have a properties but no preset */ |
1887 | 0 | g_free (strcaps); |
1888 | 0 | strcaps = g_strdup (strprops_v[0]); |
1889 | 0 | } |
1890 | |
|
1891 | 0 | for (propi = 1; strprops_v[propi]; propi++) { |
1892 | 0 | gchar **propv; |
1893 | 0 | gchar *presence_str = NULL; |
1894 | 0 | gchar *prop = strprops_v[propi]; |
1895 | 0 | GstStructure *tmpstruct = NULL; |
1896 | |
|
1897 | 0 | if (gst_structure_validate_name (prop)) |
1898 | 0 | tmpstruct = gst_structure_new_from_string (prop); |
1899 | 0 | if (tmpstruct) { |
1900 | 0 | if (element_properties) |
1901 | 0 | gst_structure_free (element_properties); |
1902 | |
|
1903 | 0 | element_properties = tmpstruct; |
1904 | |
|
1905 | 0 | continue; |
1906 | 0 | } |
1907 | | |
1908 | 0 | propv = g_strsplit (prop, "=", -1); |
1909 | 0 | if (propv[1] && propv[2]) { |
1910 | 0 | g_warning ("Wrong format for property: %s, only 1 `=` is expected", |
1911 | 0 | prop); |
1912 | 0 | g_strfreev (propv); |
1913 | 0 | goto cleanup; |
1914 | 0 | } |
1915 | | |
1916 | 0 | if (!propv[1]) { |
1917 | 0 | presence_str = propv[0]; |
1918 | 0 | } else if (!g_strcmp0 (propv[0], "presence")) { |
1919 | 0 | presence_str = propv[1]; |
1920 | 0 | } else if (!g_strcmp0 (propv[0], "variable-framerate")) { |
1921 | 0 | variable_framerate = g_strdup (propv[1]); |
1922 | 0 | } else if (!g_strcmp0 (propv[0], "single-segment")) { |
1923 | 0 | GValue v = G_VALUE_INIT; |
1924 | |
|
1925 | 0 | g_value_init (&v, G_TYPE_BOOLEAN); |
1926 | 0 | if (!gst_value_deserialize (&v, propv[1])) { |
1927 | 0 | g_warning ("Invalid value for property 'single-segment': %s", |
1928 | 0 | propv[1]); |
1929 | 0 | g_strfreev (propv); |
1930 | 0 | goto cleanup; |
1931 | 0 | } |
1932 | | |
1933 | 0 | single_segment = g_value_get_boolean (&v); |
1934 | 0 | g_value_reset (&v); |
1935 | 0 | } else if (!g_strcmp0 (propv[0], "factory-name")) { |
1936 | 0 | if (factory_name) { |
1937 | 0 | g_warning ("Multiple factory names specified"); |
1938 | 0 | g_strfreev (propv); |
1939 | 0 | goto cleanup; |
1940 | 0 | } |
1941 | | |
1942 | 0 | factory_name = g_strdup (propv[1]); |
1943 | 0 | } else { |
1944 | 0 | g_warning ("Unsupported property: %s", propv[0]); |
1945 | 0 | g_strfreev (propv); |
1946 | 0 | goto cleanup; |
1947 | 0 | } |
1948 | | |
1949 | 0 | if (presence_str) { |
1950 | 0 | presence = g_ascii_strtoll (presence_str, &endptr, 10); |
1951 | |
|
1952 | 0 | if (endptr == strprops_v[1]) { |
1953 | 0 | g_warning ("Wrong presence %s", presence_str); |
1954 | 0 | g_strfreev (propv); |
1955 | 0 | goto cleanup; |
1956 | 0 | } |
1957 | 0 | } |
1958 | 0 | g_strfreev (propv); |
1959 | 0 | } |
1960 | 0 | } else { /* We have no presence */ |
1961 | 0 | if (preset_v[1]) { /* Not presence but preset */ |
1962 | 0 | preset_name = g_strdup (preset_v[1]); |
1963 | 0 | g_free (strcaps); |
1964 | 0 | strcaps = g_strdup (preset_v[0]); |
1965 | 0 | } /* Else we have no presence nor preset */ |
1966 | 0 | } |
1967 | | |
1968 | 0 | GST_DEBUG ("Creating preset with restrictions: %" GST_PTR_FORMAT |
1969 | 0 | ", caps: %s, preset %s, presence %d", restrictioncaps, strcaps, |
1970 | 0 | preset_name ? preset_name : "none", presence); |
1971 | |
|
1972 | 0 | caps = gst_caps_from_string (strcaps); |
1973 | 0 | if (caps) { |
1974 | 0 | profile = create_encoding_profile_from_caps (caps, preset_name, |
1975 | 0 | restrictioncaps, presence, single_segment, NULL, muxers_and_encoders, |
1976 | 0 | raw_audio_caps, raw_video_caps, FALSE); |
1977 | 0 | gst_caps_unref (caps); |
1978 | 0 | } |
1979 | |
|
1980 | 0 | if (!profile) { |
1981 | 0 | gboolean is_rendering_muxer; |
1982 | |
|
1983 | 0 | caps = get_profile_format_from_possible_factory_name (strcaps, |
1984 | 0 | &factory_name, restrictioncaps ? NULL : &restrictioncaps, |
1985 | 0 | &is_rendering_muxer); |
1986 | 0 | if (caps) { |
1987 | 0 | profile = create_encoding_profile_from_caps (caps, preset_name, |
1988 | 0 | restrictioncaps, presence, single_segment, factory_name, |
1989 | 0 | muxers_and_encoders, raw_audio_caps, raw_video_caps, |
1990 | 0 | is_rendering_muxer); |
1991 | 0 | gst_caps_unref (caps); |
1992 | 0 | } |
1993 | 0 | } |
1994 | |
|
1995 | 0 | if (variable_framerate) { |
1996 | 0 | if (GST_IS_ENCODING_VIDEO_PROFILE (profile)) { |
1997 | 0 | GValue v = { |
1998 | 0 | 0, |
1999 | 0 | }; |
2000 | 0 | g_value_init (&v, G_TYPE_BOOLEAN); |
2001 | 0 | if (gst_value_deserialize (&v, variable_framerate)) { |
2002 | 0 | gst_encoding_video_profile_set_variableframerate |
2003 | 0 | (GST_ENCODING_VIDEO_PROFILE (profile), g_value_get_boolean (&v)); |
2004 | 0 | } else { |
2005 | 0 | GST_WARNING ("Invalid value for variable_framerate: %s", |
2006 | 0 | variable_framerate); |
2007 | |
|
2008 | 0 | } |
2009 | 0 | g_value_reset (&v); |
2010 | 0 | } else { |
2011 | 0 | GST_WARNING |
2012 | 0 | ("Variable framerate specified on a non video encoding profile"); |
2013 | 0 | } |
2014 | 0 | } |
2015 | |
|
2016 | 0 | if (profile == NULL) { |
2017 | 0 | GST_ERROR ("No way to create a profile for description: %s", |
2018 | 0 | serialized_profile); |
2019 | 0 | } else if (element_properties) { |
2020 | 0 | gst_encoding_profile_set_element_properties (profile, |
2021 | 0 | g_steal_pointer (&element_properties)); |
2022 | 0 | } |
2023 | |
|
2024 | 0 | cleanup: |
2025 | 0 | g_free (strcaps); |
2026 | 0 | g_free (variable_framerate); |
2027 | 0 | g_strfreev (strprops_v); |
2028 | 0 | g_strfreev (preset_v); |
2029 | 0 | g_free (preset_name); |
2030 | 0 | if (element_properties) |
2031 | 0 | gst_structure_free (element_properties); |
2032 | 0 | if (restrictioncaps) |
2033 | 0 | gst_caps_unref (restrictioncaps); |
2034 | |
|
2035 | 0 | return profile; |
2036 | 0 | } |
2037 | | |
2038 | | static GstEncodingProfile * |
2039 | | parse_encoding_profile (const gchar * value) |
2040 | 0 | { |
2041 | 0 | GstEncodingProfile *res = NULL; |
2042 | 0 | gchar *caps_str = NULL; |
2043 | 0 | gchar **strcaps_v = |
2044 | 0 | g_regex_split_simple ("(?<!\\\\)(?:\\\\\\\\)*:", value, 0, 0); |
2045 | 0 | guint i; |
2046 | 0 | GList *muxers_and_encoders = |
2047 | 0 | gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_ENCODER | |
2048 | 0 | GST_ELEMENT_FACTORY_TYPE_MUXER, |
2049 | 0 | GST_RANK_MARGINAL); |
2050 | 0 | GstCaps *raw_video_caps = gst_caps_new_empty_simple ("video/x-raw"); |
2051 | 0 | GstCaps *raw_audio_caps = gst_caps_new_empty_simple ("audio/x-raw"); |
2052 | | |
2053 | | /* The regex returns NULL if no ":" found, handle that case. */ |
2054 | 0 | if (strcaps_v == NULL) |
2055 | 0 | strcaps_v = g_strsplit (value, ":", 0); |
2056 | |
|
2057 | 0 | for (i = 0; strcaps_v[i] && *strcaps_v[i]; i++) { |
2058 | 0 | GstEncodingProfile *profile; |
2059 | 0 | caps_str = g_strcompress (strcaps_v[i]); |
2060 | 0 | profile = |
2061 | 0 | create_encoding_stream_profile (caps_str, muxers_and_encoders, |
2062 | 0 | raw_audio_caps, raw_video_caps); |
2063 | |
|
2064 | 0 | if (!profile) { |
2065 | 0 | GST_ERROR ("Could not create profile for caps: %s", caps_str); |
2066 | 0 | goto error; |
2067 | 0 | } |
2068 | | |
2069 | 0 | if (res) { |
2070 | 0 | if (!GST_IS_ENCODING_CONTAINER_PROFILE (res)) { |
2071 | 0 | GST_ERROR ("The first described encoding profile was not a container" |
2072 | 0 | " but you are trying to add more profiles to it. This is not possible"); |
2073 | 0 | goto error; |
2074 | 0 | } |
2075 | | |
2076 | 0 | if (!gst_encoding_container_profile_add_profile |
2077 | 0 | (GST_ENCODING_CONTAINER_PROFILE (res), profile)) { |
2078 | 0 | GST_ERROR ("Can not add profile for caps: %s", caps_str); |
2079 | 0 | goto error; |
2080 | 0 | } |
2081 | 0 | } else { |
2082 | 0 | res = profile; |
2083 | 0 | } |
2084 | | |
2085 | 0 | g_clear_pointer (&caps_str, g_free); |
2086 | 0 | } |
2087 | | |
2088 | 0 | done: |
2089 | 0 | g_free (caps_str); |
2090 | 0 | g_strfreev (strcaps_v); |
2091 | 0 | gst_caps_unref (raw_audio_caps); |
2092 | 0 | gst_caps_unref (raw_video_caps); |
2093 | 0 | gst_plugin_feature_list_free (muxers_and_encoders); |
2094 | |
|
2095 | 0 | return res; |
2096 | | |
2097 | 0 | error: |
2098 | 0 | g_clear_object (&res); |
2099 | |
|
2100 | 0 | goto done; |
2101 | 0 | } |
2102 | | |
2103 | | /** |
2104 | | * gst_encoding_profile_from_string: |
2105 | | * @string: The string to convert into a GstEncodingProfile. |
2106 | | * |
2107 | | * Converts a string in the "encoding profile serialization format" into a |
2108 | | * GstEncodingProfile. Refer to the encoding-profile documentation for details |
2109 | | * on the format. |
2110 | | * |
2111 | | * Since: 1.26 |
2112 | | * |
2113 | | * Returns: (transfer full) (nullable): A newly created GstEncodingProfile or NULL if the |
2114 | | * input string is not a valid encoding profile serialization format. |
2115 | | */ |
2116 | | GstEncodingProfile * |
2117 | | gst_encoding_profile_from_string (const gchar * string) |
2118 | 0 | { |
2119 | 0 | GstEncodingProfile *profile; |
2120 | 0 | gchar *filename_end; |
2121 | |
|
2122 | 0 | profile = combo_search (string); |
2123 | |
|
2124 | 0 | if (profile) |
2125 | 0 | return profile; |
2126 | | |
2127 | 0 | filename_end = g_strrstr (string, ".gep"); |
2128 | 0 | if (filename_end) { |
2129 | 0 | GstEncodingTarget *target; |
2130 | 0 | gchar *profilename = NULL, *filename; |
2131 | |
|
2132 | 0 | if (filename_end[4] == ':') |
2133 | 0 | profilename = g_strdup (&filename_end[5]); |
2134 | |
|
2135 | 0 | if (filename_end[4] == '\0' || profilename) { |
2136 | 0 | filename = g_strndup (string, filename_end - string + strlen (".gep")); |
2137 | |
|
2138 | 0 | target = gst_encoding_target_load_from_file (filename, NULL); |
2139 | 0 | if (target) { |
2140 | 0 | profile = gst_encoding_target_get_profile (target, |
2141 | 0 | profilename ? profilename : "default"); |
2142 | 0 | gst_encoding_target_unref (target); |
2143 | 0 | } |
2144 | |
|
2145 | 0 | g_free (profilename); |
2146 | 0 | g_free (filename); |
2147 | 0 | } |
2148 | 0 | } |
2149 | |
|
2150 | 0 | if (!profile) |
2151 | 0 | profile = parse_encoding_profile (string); |
2152 | |
|
2153 | 0 | return profile; |
2154 | 0 | } |
2155 | | |
2156 | | /* GValue transform function */ |
2157 | | static void |
2158 | | string_to_profile_transform (const GValue * src_value, GValue * dest_value) |
2159 | 0 | { |
2160 | 0 | const gchar *profilename; |
2161 | 0 | GstEncodingProfile *profile; |
2162 | |
|
2163 | 0 | profilename = g_value_get_string (src_value); |
2164 | |
|
2165 | 0 | profile = gst_encoding_profile_from_string (profilename); |
2166 | |
|
2167 | 0 | if (profile) |
2168 | 0 | g_value_take_object (dest_value, (GObject *) profile); |
2169 | 0 | } |
2170 | | |
2171 | | static void |
2172 | | serialize_profile (GString * res, GstEncodingProfile * profile) |
2173 | 0 | { |
2174 | 0 | gchar *tmp; |
2175 | |
|
2176 | 0 | if (res->len) |
2177 | 0 | g_string_append_c (res, ':'); |
2178 | |
|
2179 | 0 | if (profile->restriction) { |
2180 | 0 | tmp = gst_caps_to_string (profile->restriction); |
2181 | 0 | g_string_append_printf (res, "%s->", tmp); |
2182 | 0 | g_free (tmp); |
2183 | 0 | } |
2184 | |
|
2185 | 0 | tmp = gst_caps_to_string (profile->format); |
2186 | 0 | g_string_append (res, tmp); |
2187 | |
|
2188 | 0 | if (profile->presence) |
2189 | 0 | g_string_append_printf (res, "|presence=%d", profile->presence); |
2190 | |
|
2191 | 0 | if (profile->single_segment) |
2192 | 0 | g_string_append_printf (res, "%ssingle-segment=true", |
2193 | 0 | profile->presence ? "" : "|"); |
2194 | |
|
2195 | 0 | if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) { |
2196 | 0 | GList *tmp; |
2197 | |
|
2198 | 0 | for (tmp = GST_ENCODING_CONTAINER_PROFILE (profile)->encodingprofiles; tmp; |
2199 | 0 | tmp = tmp->next) |
2200 | 0 | serialize_profile (res, tmp->data); |
2201 | 0 | } |
2202 | 0 | } |
2203 | | |
2204 | | /** |
2205 | | * gst_encoding_profile_to_string: |
2206 | | * @profile: (transfer none): The GstEncodingProfile to convert. |
2207 | | * |
2208 | | * Converts a GstEncodingProfile to a string in the "Encoding Profile |
2209 | | * serialization format". |
2210 | | * |
2211 | | * Since: 1.26 |
2212 | | * |
2213 | | * Returns: (transfer full): A string representation of the GstEncodingProfile. |
2214 | | */ |
2215 | | gchar * |
2216 | | gst_encoding_profile_to_string (GstEncodingProfile * profile) |
2217 | 0 | { |
2218 | 0 | GString *res; |
2219 | |
|
2220 | 0 | g_return_val_if_fail (profile != NULL, NULL); |
2221 | | |
2222 | 0 | res = g_string_new (NULL); |
2223 | |
|
2224 | 0 | serialize_profile (res, profile); |
2225 | |
|
2226 | 0 | return g_string_free (res, FALSE); |
2227 | 0 | } |
2228 | | |
2229 | | static gchar * |
2230 | | gst_encoding_profile_serialize_valfunc (GValue * value) |
2231 | 0 | { |
2232 | 0 | GstEncodingProfile *profile = g_value_get_object (value); |
2233 | 0 | return gst_encoding_profile_to_string (profile); |
2234 | 0 | } |
2235 | | |
2236 | | static gboolean |
2237 | | gst_encoding_profile_deserialize_valfunc (GValue * value, const gchar * s) |
2238 | 0 | { |
2239 | 0 | GstEncodingProfile *profile; |
2240 | |
|
2241 | 0 | profile = gst_encoding_profile_from_string (s); |
2242 | |
|
2243 | 0 | if (profile) { |
2244 | 0 | g_value_take_object (value, (GObject *) profile); |
2245 | 0 | return TRUE; |
2246 | 0 | } |
2247 | | |
2248 | 0 | return FALSE; |
2249 | 0 | } |
2250 | | |
2251 | | static GstEncodingProfile * |
2252 | | create_stream_profile_recurse (GstEncodingProfile * toplevel, |
2253 | | GstDiscovererStreamInfo * sinfo) |
2254 | 0 | { |
2255 | 0 | GstEncodingProfile *profile = NULL; |
2256 | 0 | GstStructure *s; |
2257 | 0 | GstCaps *caps; |
2258 | |
|
2259 | 0 | caps = gst_discoverer_stream_info_get_caps (sinfo); |
2260 | | |
2261 | | /* Should unify this with copy_and_clean_caps() */ |
2262 | 0 | caps = gst_caps_make_writable (caps); |
2263 | 0 | s = gst_caps_get_structure (caps, 0); |
2264 | |
|
2265 | 0 | gst_structure_remove_fields (s, "codec_data", "streamheader", "parsed", |
2266 | 0 | "colorimetry", "framed", "stream-format", "alignment", "tier", "level", |
2267 | 0 | "profile", "chroma-format", "bit-depth-luma", "bit-depth-chroma", NULL); |
2268 | |
|
2269 | 0 | GST_LOG ("Stream: %" GST_PTR_FORMAT, caps); |
2270 | 0 | if (GST_IS_DISCOVERER_AUDIO_INFO (sinfo)) { |
2271 | 0 | profile = |
2272 | 0 | (GstEncodingProfile *) gst_encoding_audio_profile_new (caps, NULL, |
2273 | 0 | NULL, 0); |
2274 | 0 | } else if (GST_IS_DISCOVERER_VIDEO_INFO (sinfo)) { |
2275 | 0 | profile = |
2276 | 0 | (GstEncodingProfile *) gst_encoding_video_profile_new (caps, NULL, |
2277 | 0 | NULL, 0); |
2278 | 0 | } else if (GST_IS_DISCOVERER_CONTAINER_INFO (sinfo)) { |
2279 | 0 | GList *streams, *stream; |
2280 | |
|
2281 | 0 | streams = |
2282 | 0 | gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO |
2283 | 0 | (sinfo)); |
2284 | |
|
2285 | 0 | if (!toplevel || !GST_IS_ENCODING_CONTAINER_PROFILE (toplevel)) { |
2286 | 0 | GstEncodingProfile *prev_toplevel = toplevel; |
2287 | |
|
2288 | 0 | toplevel = (GstEncodingProfile *) |
2289 | 0 | gst_encoding_container_profile_new ("auto-generated", |
2290 | 0 | "Automatically generated from GstDiscovererInfo", caps, NULL); |
2291 | 0 | if (prev_toplevel) |
2292 | 0 | gst_encoding_container_profile_add_profile |
2293 | 0 | (GST_ENCODING_CONTAINER_PROFILE (toplevel), prev_toplevel); |
2294 | 0 | } |
2295 | |
|
2296 | 0 | for (stream = streams; stream; stream = stream->next) |
2297 | 0 | create_stream_profile_recurse (toplevel, |
2298 | 0 | (GstDiscovererStreamInfo *) stream->data); |
2299 | 0 | gst_discoverer_stream_info_list_free (streams); |
2300 | 0 | } else { |
2301 | 0 | GST_FIXME ("Ignoring stream of type '%s'", |
2302 | 0 | g_type_name (G_OBJECT_TYPE (sinfo))); |
2303 | | /* subtitles or other ? ignore for now */ |
2304 | 0 | } |
2305 | 0 | gst_caps_unref (caps); |
2306 | |
|
2307 | 0 | if (profile) { |
2308 | 0 | const gchar *stream_id = gst_discoverer_stream_info_get_stream_id (sinfo); |
2309 | |
|
2310 | 0 | if (stream_id) { |
2311 | 0 | const gchar *subid = strchr (stream_id, '/'); |
2312 | |
|
2313 | 0 | gst_encoding_profile_set_name (profile, subid ? subid : stream_id); |
2314 | 0 | } |
2315 | |
|
2316 | 0 | if (GST_IS_ENCODING_CONTAINER_PROFILE (toplevel)) |
2317 | 0 | gst_encoding_container_profile_add_profile ((GstEncodingContainerProfile |
2318 | 0 | *) |
2319 | 0 | toplevel, profile); |
2320 | 0 | } |
2321 | |
|
2322 | 0 | if (!toplevel && profile) |
2323 | 0 | toplevel = profile; |
2324 | |
|
2325 | 0 | sinfo = gst_discoverer_stream_info_get_next (sinfo); |
2326 | 0 | if (sinfo) |
2327 | 0 | return create_stream_profile_recurse (toplevel, sinfo); |
2328 | | |
2329 | 0 | return toplevel; |
2330 | 0 | } |
2331 | | |
2332 | | /** |
2333 | | * gst_encoding_profile_from_discoverer: |
2334 | | * @info: (transfer none): The #GstDiscovererInfo to read from |
2335 | | * |
2336 | | * Creates a #GstEncodingProfile matching the formats from the given |
2337 | | * #GstDiscovererInfo. Streams other than audio or video (eg, |
2338 | | * subtitles), are currently ignored. |
2339 | | * |
2340 | | * Returns: (transfer full) (nullable): The new #GstEncodingProfile or %NULL. |
2341 | | */ |
2342 | | GstEncodingProfile * |
2343 | | gst_encoding_profile_from_discoverer (GstDiscovererInfo * info) |
2344 | 0 | { |
2345 | 0 | GstEncodingProfile *profile; |
2346 | 0 | GstDiscovererStreamInfo *sinfo; |
2347 | |
|
2348 | 0 | if (!info || gst_discoverer_info_get_result (info) != GST_DISCOVERER_OK) |
2349 | 0 | return NULL; |
2350 | | |
2351 | 0 | sinfo = gst_discoverer_info_get_stream_info (info); |
2352 | 0 | if (!sinfo) |
2353 | 0 | return NULL; |
2354 | | |
2355 | 0 | profile = create_stream_profile_recurse (NULL, sinfo); |
2356 | 0 | if (GST_IS_ENCODING_CONTAINER_PROFILE (profile)) { |
2357 | 0 | if (!gst_encoding_container_profile_get_profiles |
2358 | 0 | (GST_ENCODING_CONTAINER_PROFILE (profile))) { |
2359 | 0 | GST_ERROR ("Failed to add any streams"); |
2360 | 0 | g_object_unref (profile); |
2361 | 0 | return NULL; |
2362 | 0 | } |
2363 | 0 | } |
2364 | | |
2365 | 0 | return (GstEncodingProfile *) profile; |
2366 | 0 | } |
2367 | | |
2368 | | /** |
2369 | | * gst_encoding_profile_copy: |
2370 | | * @self: The #GstEncodingProfile to copy |
2371 | | * |
2372 | | * Makes a deep copy of @self |
2373 | | * |
2374 | | * Returns: (transfer full): The copy of @self |
2375 | | * |
2376 | | * Since: 1.12 |
2377 | | */ |
2378 | | GstEncodingProfile * |
2379 | | gst_encoding_profile_copy (GstEncodingProfile * self) |
2380 | 0 | { |
2381 | 0 | GstEncodingProfileClass *klass = |
2382 | 0 | (GstEncodingProfileClass *) G_OBJECT_GET_CLASS (self); |
2383 | 0 | GstEncodingProfile *copy = |
2384 | 0 | common_creation (G_OBJECT_TYPE (self), self->format, self->preset, |
2385 | 0 | self->name, self->description, self->restriction, self->presence); |
2386 | |
|
2387 | 0 | copy->enabled = self->enabled; |
2388 | 0 | copy->allow_dynamic_output = self->allow_dynamic_output; |
2389 | 0 | gst_encoding_profile_set_preset_name (copy, self->preset_name); |
2390 | 0 | gst_encoding_profile_set_description (copy, self->description); |
2391 | |
|
2392 | 0 | if (klass->copy) |
2393 | 0 | klass->copy (self, copy); |
2394 | |
|
2395 | 0 | return copy; |
2396 | 0 | } |