encoding-target.c 34.6 KB
Newer Older
Edward Hervey's avatar
Edward Hervey committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* GStreamer encoding profile registry
 * Copyright (C) 2010 Edward Hervey <edward.hervey@collabora.co.uk>
 *           (C) 2010 Nokia Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
17
18
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Edward Hervey's avatar
Edward Hervey committed
19
 */
20
/**
21
22
 * SECTION:encoding-target
 *
23
24
25
26
27
28
 * On top of the notion of profiles, we implement the notion of EncodingTarget.
 * Encoding Targets are basically a higher level of abstraction to define formats
 * for specific target types. Those can define several GstEncodingProfiles with
 * different names, for example one for transcoding in full HD, another one for
 * low res, etc.. which are defined in the same encoding target.
 *
29
 * Basically if you want to encode a stream to send it to, say, youtube you should
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
 * have a Youtube encoding target defined in the "online-service" category.
 *
 * ## Encoding target serialization format
 *
 * Encoding targets are serialized in a KeyFile like files.
 *
 * |[
 * [GStreamer Encoding Target]
 * name : <name>
 * category : <category>
 * \description : <description> #translatable
 *
 * [profile-<profile1name>]
 * name : <name>
 * \description : <description> #optional
 * format : <format>
 * preset : <preset>
 *
 * [streamprofile-<id>]
 * parent : <encodingprofile.name>[,<encodingprofile.name>..]
 * \type : <type> # "audio", "video", "text"
 * format : <format>
 * preset : <preset>
 * restriction : <restriction>
 * presence : <presence>
 * pass : <pass>
 * variableframerate : <variableframerate>
 * ]|
 *
 * ## Location of encoding target files
 *
 * $GST_DATADIR/gstreamer-GST_API_VERSION/encoding-profile
 * $HOME/gstreamer-GST_API_VERSION/encoding-profile
 *
 * There also is a GST_ENCODING_TARGET_PATH environment variable
 * defining a list of folder containing encoding target files.
 *
 * ## Naming convention
 *
 * |[
 *   $(target.category)/$(target.name).gep
 * ]|
 *
 * ## Naming restrictions:
 *
 *  * lowercase ASCII letter for the first character
 *  * Same for all other characters + numerics + hyphens
 */
Edward Hervey's avatar
Edward Hervey committed
78

79
80
81
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
82
83

#include <locale.h>
84
#include <errno.h>
85
#include <string.h>
Edward Hervey's avatar
Edward Hervey committed
86
#include "encoding-target.h"
87
#include "pbutils-private.h"
Edward Hervey's avatar
Edward Hervey committed
88

89
/* Documented in encoding-profile.c */
90

91
#define GST_ENCODING_TARGET_HEADER "GStreamer Encoding Target"
92
93
#define GST_ENCODING_TARGET_DIRECTORY "encoding-profiles"
#define GST_ENCODING_TARGET_SUFFIX ".gep"
Edward Hervey's avatar
Edward Hervey committed
94
95
96

struct _GstEncodingTarget
{
97
  GObject parent;
Edward Hervey's avatar
Edward Hervey committed
98
99
100
101

  gchar *name;
  gchar *category;
  gchar *description;
102
  gchar *path;
Edward Hervey's avatar
Edward Hervey committed
103
104
105
106
107
108
  GList *profiles;

  /*< private > */
  gchar *keyfile;
};

109
G_DEFINE_TYPE (GstEncodingTarget, gst_encoding_target, G_TYPE_OBJECT);
Edward Hervey's avatar
Edward Hervey committed
110
111
112
113
114
115
116
117

static void
gst_encoding_target_init (GstEncodingTarget * target)
{
  /* Nothing to initialize */
}

static void
118
gst_encoding_target_finalize (GObject * object)
Edward Hervey's avatar
Edward Hervey committed
119
{
120
121
  GstEncodingTarget *target = (GstEncodingTarget *) object;

Edward Hervey's avatar
Edward Hervey committed
122
123
  GST_DEBUG ("Finalizing");

124
125
126
  g_free (target->name);
  g_free (target->category);
  g_free (target->description);
127
  g_free (target->path);
Edward Hervey's avatar
Edward Hervey committed
128

Wim Taymans's avatar
Wim Taymans committed
129
  g_list_foreach (target->profiles, (GFunc) g_object_unref, NULL);
Edward Hervey's avatar
Edward Hervey committed
130
131
132
133
  g_list_free (target->profiles);
}

static void
134
gst_encoding_target_class_init (GObjectClass * klass)
Edward Hervey's avatar
Edward Hervey committed
135
{
136
  klass->finalize = gst_encoding_target_finalize;
Edward Hervey's avatar
Edward Hervey committed
137
138
139
140
141
142
}

/**
 * gst_encoding_target_get_name:
 * @target: a #GstEncodingTarget
 *
143
 * Returns: (transfer none): The name of the @target.
Edward Hervey's avatar
Edward Hervey committed
144
145
146
147
148
149
150
151
152
153
154
 */
const gchar *
gst_encoding_target_get_name (GstEncodingTarget * target)
{
  return target->name;
}

/**
 * gst_encoding_target_get_category:
 * @target: a #GstEncodingTarget
 *
155
156
 * Returns: (transfer none): The category of the @target. For example:
 * #GST_ENCODING_CATEGORY_DEVICE.
Edward Hervey's avatar
Edward Hervey committed
157
158
159
160
161
162
163
164
165
166
167
 */
const gchar *
gst_encoding_target_get_category (GstEncodingTarget * target)
{
  return target->category;
}

/**
 * gst_encoding_target_get_description:
 * @target: a #GstEncodingTarget
 *
168
 * Returns: (transfer none): The description of the @target.
Edward Hervey's avatar
Edward Hervey committed
169
170
171
172
173
174
175
 */
const gchar *
gst_encoding_target_get_description (GstEncodingTarget * target)
{
  return target->description;
}

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**
 * gst_encoding_target_get_path:
 * @target: a #GstEncodingTarget
 *
 * Returns: (transfer none): The path to the @target file.
 *
 * Since: 1.18
 */
const gchar *
gst_encoding_target_get_path (GstEncodingTarget * target)
{
  g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), NULL);
  return target->path;
}

Edward Hervey's avatar
Edward Hervey committed
191
192
193
194
/**
 * gst_encoding_target_get_profiles:
 * @target: a #GstEncodingTarget
 *
195
 * Returns: (transfer none) (element-type GstPbutils.EncodingProfile): A list of
196
 * #GstEncodingProfile(s) this @target handles.
Edward Hervey's avatar
Edward Hervey committed
197
198
199
200
201
 */
const GList *
gst_encoding_target_get_profiles (GstEncodingTarget * target)
{
  return target->profiles;
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
}

/**
 * gst_encoding_target_get_profile:
 * @target: a #GstEncodingTarget
 * @name: the name of the profile to retrieve
 *
 * Returns: (transfer full): The matching #GstEncodingProfile, or %NULL.
 */
GstEncodingProfile *
gst_encoding_target_get_profile (GstEncodingTarget * target, const gchar * name)
{
  GList *tmp;

  g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), NULL);
  g_return_val_if_fail (name != NULL, NULL);

  for (tmp = target->profiles; tmp; tmp = tmp->next) {
    GstEncodingProfile *tprof = (GstEncodingProfile *) tmp->data;

    if (!g_strcmp0 (gst_encoding_profile_get_name (tprof), name)) {
      gst_encoding_profile_ref (tprof);
      return tprof;
    }
  }

  return NULL;
Edward Hervey's avatar
Edward Hervey committed
229
230
}

231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
static inline gboolean
validate_name (const gchar * name)
{
  guint i, len;

  len = strlen (name);
  if (len == 0)
    return FALSE;

  /* First character can only be a lower case ASCII character */
  if (!g_ascii_isalpha (name[0]) || !g_ascii_islower (name[0]))
    return FALSE;

  /* All following characters can only by:
   * either a lower case ASCII character
   * or an hyphen
   * or a numeric */
  for (i = 1; i < len; i++) {
    /* if uppercase ASCII letter, return */
    if (g_ascii_isupper (name[i]))
      return FALSE;
    /* if a digit, continue */
    if (g_ascii_isdigit (name[i]))
      continue;
    /* if an hyphen, continue */
    if (name[i] == '-')
      continue;
258
259
260
261
    /* if an ';', continue (list delimiter) */
    if (name[i] == ';') {
      continue;
    }
262
263
264
265
266
267
268
    /* remaining should only be ascii letters */
    if (!g_ascii_isalpha (name[i]))
      return FALSE;
  }

  return TRUE;
}
Edward Hervey's avatar
Edward Hervey committed
269
270
271
272

/**
 * gst_encoding_target_new:
 * @name: The name of the target.
273
 * @category: (transfer none): The name of the category to which this @target
274
 * belongs. For example: #GST_ENCODING_CATEGORY_DEVICE.
275
276
 * @description: (transfer none): A description of #GstEncodingTarget in the
 * current locale.
277
 * @profiles: (transfer none) (element-type GstPbutils.EncodingProfile): A #GList of
278
 * #GstEncodingProfile.
Edward Hervey's avatar
Edward Hervey committed
279
280
281
 *
 * Creates a new #GstEncodingTarget.
 *
282
283
284
285
 * The name and category can only consist of lowercase ASCII letters for the
 * first character, followed by either lowercase ASCII letters, digits or
 * hyphens ('-').
 *
286
 * The @category *should* be one of the existing
287
 * well-defined categories, like #GST_ENCODING_CATEGORY_DEVICE, but it
288
 * *can* be a application or user specific category if
289
 * needed.
290
 *
291
292
 * Returns: (transfer full): The newly created #GstEncodingTarget or %NULL if
 * there was an error.
Edward Hervey's avatar
Edward Hervey committed
293
294
295
296
297
298
299
300
301
302
303
304
 */

GstEncodingTarget *
gst_encoding_target_new (const gchar * name, const gchar * category,
    const gchar * description, const GList * profiles)
{
  GstEncodingTarget *res;

  g_return_val_if_fail (name != NULL, NULL);
  g_return_val_if_fail (category != NULL, NULL);
  g_return_val_if_fail (description != NULL, NULL);

305
306
307
  /* Validate name */
  if (!validate_name (name))
    goto invalid_name;
308
  if (category && !validate_name (category))
309
310
    goto invalid_category;

311
  res = (GstEncodingTarget *) g_object_new (GST_TYPE_ENCODING_TARGET, NULL);
Edward Hervey's avatar
Edward Hervey committed
312
313
314
315
316
317
318
319
320
321
322
323
324
  res->name = g_strdup (name);
  res->category = g_strdup (category);
  res->description = g_strdup (description);

  while (profiles) {
    GstEncodingProfile *prof = (GstEncodingProfile *) profiles->data;

    res->profiles =
        g_list_append (res->profiles, gst_encoding_profile_ref (prof));
    profiles = profiles->next;
  }

  return res;
325
326
327

invalid_name:
  {
Edward Hervey's avatar
Edward Hervey committed
328
    GST_ERROR ("Invalid name for encoding target : '%s'", name);
329
330
331
332
333
    return NULL;
  }

invalid_category:
  {
Edward Hervey's avatar
Edward Hervey committed
334
    GST_ERROR ("Invalid name for encoding category : '%s'", category);
335
336
    return NULL;
  }
Edward Hervey's avatar
Edward Hervey committed
337
338
339
340
341
}

/**
 * gst_encoding_target_add_profile:
 * @target: the #GstEncodingTarget to add a profile to
342
 * @profile: (transfer full): the #GstEncodingProfile to add
Edward Hervey's avatar
Edward Hervey committed
343
 *
344
345
 * Adds the given @profile to the @target. Each added profile must have
 * a unique name within the profile.
Edward Hervey's avatar
Edward Hervey committed
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
 *
 * The @target will steal a reference to the @profile. If you wish to use
 * the profile after calling this method, you should increase its reference
 * count.
 *
 * Returns: %TRUE if the profile was added, else %FALSE.
 **/

gboolean
gst_encoding_target_add_profile (GstEncodingTarget * target,
    GstEncodingProfile * profile)
{
  GList *tmp;

  g_return_val_if_fail (GST_IS_ENCODING_TARGET (target), FALSE);
  g_return_val_if_fail (GST_IS_ENCODING_PROFILE (profile), FALSE);

  /* Make sure profile isn't already controlled by this target */
  for (tmp = target->profiles; tmp; tmp = tmp->next) {
    GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data;

    if (!g_strcmp0 (gst_encoding_profile_get_name (profile),
            gst_encoding_profile_get_name (prof))) {
      GST_WARNING ("Profile already present in target");
      return FALSE;
    }
  }

  target->profiles = g_list_append (target->profiles, profile);

  return TRUE;
}

static gboolean
serialize_stream_profiles (GKeyFile * out, GstEncodingProfile * sprof,
    const gchar * profilename, guint id)
{
  gchar *sprofgroupname;
  gchar *tmpc;
385
  GstCaps *format, *restriction;
Edward Hervey's avatar
Edward Hervey committed
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
  const gchar *preset, *name, *description;

  sprofgroupname = g_strdup_printf ("streamprofile-%s-%d", profilename, id);

  /* Write the parent profile */
  g_key_file_set_value (out, sprofgroupname, "parent", profilename);

  g_key_file_set_value (out, sprofgroupname, "type",
      gst_encoding_profile_get_type_nick (sprof));

  format = gst_encoding_profile_get_format (sprof);
  if (format) {
    tmpc = gst_caps_to_string (format);
    g_key_file_set_value (out, sprofgroupname, "format", tmpc);
    g_free (tmpc);
  }

  name = gst_encoding_profile_get_name (sprof);
  if (name)
    g_key_file_set_string (out, sprofgroupname, "name", name);

  description = gst_encoding_profile_get_description (sprof);
  if (description)
    g_key_file_set_string (out, sprofgroupname, "description", description);

  preset = gst_encoding_profile_get_preset (sprof);
  if (preset)
    g_key_file_set_string (out, sprofgroupname, "preset", preset);

  restriction = gst_encoding_profile_get_restriction (sprof);
  if (restriction) {
    tmpc = gst_caps_to_string (restriction);
    g_key_file_set_value (out, sprofgroupname, "restriction", tmpc);
    g_free (tmpc);
  }
  g_key_file_set_integer (out, sprofgroupname, "presence",
      gst_encoding_profile_get_presence (sprof));

  if (GST_IS_ENCODING_VIDEO_PROFILE (sprof)) {
    GstEncodingVideoProfile *vp = (GstEncodingVideoProfile *) sprof;

    g_key_file_set_integer (out, sprofgroupname, "pass",
        gst_encoding_video_profile_get_pass (vp));
    g_key_file_set_boolean (out, sprofgroupname, "variableframerate",
        gst_encoding_video_profile_get_variableframerate (vp));
  }

  g_free (sprofgroupname);
434
435
436
437
  if (format)
    gst_caps_unref (format);
  if (restriction)
    gst_caps_unref (restriction);
Edward Hervey's avatar
Edward Hervey committed
438
439
440
  return TRUE;
}

441
442
443
static gchar *
get_locale (void)
{
444
  const char *loc = NULL;
445
446
  gchar *ret;

447
448
  gst_pb_utils_init_locale_text_domain ();

449
450
451
452
453
454
455
456
457
458
459
460
461
462
#ifdef ENABLE_NLS
#if defined(LC_MESSAGES)
  loc = setlocale (LC_MESSAGES, NULL);
  GST_LOG ("LC_MESSAGES: %s", GST_STR_NULL (loc));
#elif defined(LC_ALL)
  loc = setlocale (LC_ALL, NULL);
  GST_LOG ("LC_ALL: %s", GST_STR_NULL (loc));
#else
  GST_LOG ("Neither LC_ALL nor LC_MESSAGES defined");
#endif
#else /* !ENABLE_NLS */
  GST_LOG ("i18n disabled");
#endif

463
464
465
466
467
468
469
470
471
472
  if (loc == NULL || g_ascii_strncasecmp (loc, "en", 2) == 0)
    return NULL;

  /* en_GB.UTF-8 => en */
  ret = g_ascii_strdown (loc, -1);
  ret = g_strcanon (ret, "abcdefghijklmnopqrstuvwxyz", '\0');
  GST_LOG ("using locale: %s", ret);
  return ret;
}

Edward Hervey's avatar
Edward Hervey committed
473
474
475
476
477
478
479
480
481
/* Serialize the top-level profiles
 * Note: They don't have to be containerprofiles */
static gboolean
serialize_encoding_profile (GKeyFile * out, GstEncodingProfile * prof)
{
  gchar *profgroupname;
  const GList *tmp;
  guint i;
  const gchar *profname, *profdesc, *profpreset, *proftype;
482
  GstCaps *profformat;
Edward Hervey's avatar
Edward Hervey committed
483
484
485
486
487
488
489
490
491
492
493

  profname = gst_encoding_profile_get_name (prof);
  profdesc = gst_encoding_profile_get_description (prof);
  profformat = gst_encoding_profile_get_format (prof);
  profpreset = gst_encoding_profile_get_preset (prof);
  proftype = gst_encoding_profile_get_type_nick (prof);

  profgroupname = g_strdup_printf ("profile-%s", profname);

  g_key_file_set_string (out, profgroupname, "name", profname);

494
  g_key_file_set_value (out, profgroupname, "type", proftype);
Edward Hervey's avatar
Edward Hervey committed
495

496
497
498
499
500
501
502
503
504
505
506
507
  if (profdesc) {
    gchar *locale;

    locale = get_locale ();
    if (locale != NULL) {
      g_key_file_set_locale_string (out, profgroupname, "description",
          locale, profdesc);
      g_free (locale);
    } else {
      g_key_file_set_string (out, profgroupname, "description", profdesc);
    }
  }
Edward Hervey's avatar
Edward Hervey committed
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
  if (profformat) {
    gchar *tmpc = gst_caps_to_string (profformat);
    g_key_file_set_string (out, profgroupname, "format", tmpc);
    g_free (tmpc);
  }
  if (profpreset)
    g_key_file_set_string (out, profgroupname, "preset", profpreset);

  /* stream profiles */
  if (GST_IS_ENCODING_CONTAINER_PROFILE (prof)) {
    for (tmp =
        gst_encoding_container_profile_get_profiles
        (GST_ENCODING_CONTAINER_PROFILE (prof)), i = 0; tmp;
        tmp = tmp->next, i++) {
      GstEncodingProfile *sprof = (GstEncodingProfile *) tmp->data;

      if (!serialize_stream_profiles (out, sprof, profname, i))
        return FALSE;
    }
  }
528
529
  if (profformat)
    gst_caps_unref (profformat);
Edward Hervey's avatar
Edward Hervey committed
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
  g_free (profgroupname);
  return TRUE;
}

static gboolean
serialize_target (GKeyFile * out, GstEncodingTarget * target)
{
  GList *tmp;

  g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "name", target->name);
  g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "category",
      target->category);
  g_key_file_set_string (out, GST_ENCODING_TARGET_HEADER, "description",
      target->description);

  for (tmp = target->profiles; tmp; tmp = tmp->next) {
    GstEncodingProfile *prof = (GstEncodingProfile *) tmp->data;
    if (!serialize_encoding_profile (out, prof))
      return FALSE;
  }

  return TRUE;
}

/**
 * parse_encoding_profile:
 * @in: a #GKeyFile
 * @parentprofilename: the parent profile name (including 'profile-' or 'streamprofile-' header)
 * @profilename: the profile name group to parse
 * @nbgroups: the number of top-level groups
 * @groups: the top-level groups
 */
static GstEncodingProfile *
parse_encoding_profile (GKeyFile * in, gchar * parentprofilename,
    gchar * profilename, gsize nbgroups, gchar ** groups)
{
  GstEncodingProfile *sprof = NULL;
  gchar **parent;
568
569
  gchar *proftype, *format, *preset, *restriction, *pname, *description,
      *locale;
Edward Hervey's avatar
Edward Hervey committed
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
  GstCaps *formatcaps = NULL;
  GstCaps *restrictioncaps = NULL;
  gboolean variableframerate;
  gint pass, presence;
  gsize i, nbencprofiles;

  GST_DEBUG ("parentprofilename : %s , profilename : %s",
      parentprofilename, profilename);

  if (parentprofilename) {
    gboolean found = FALSE;

    parent =
        g_key_file_get_string_list (in, profilename, "parent",
        &nbencprofiles, NULL);
    if (!parent || !nbencprofiles) {
      return NULL;
    }

    /* Check if this streamprofile is used in <profilename> */
    for (i = 0; i < nbencprofiles; i++) {
      if (!g_strcmp0 (parent[i], parentprofilename)) {
        found = TRUE;
        break;
      }
    }
    g_strfreev (parent);

    if (!found) {
      GST_DEBUG ("Stream profile '%s' isn't used in profile '%s'",
          profilename, parentprofilename);
      return NULL;
    }
  }

  pname = g_key_file_get_value (in, profilename, "name", NULL);

607
608
609
610
611
  locale = get_locale ();
  /* will try to fall back to untranslated string if no translation found */
  description = g_key_file_get_locale_string (in, profilename,
      "description", locale, NULL);
  g_free (locale);
612
613
614
615
616

  /* Note: a missing description is normal for non-container profiles */
  if (description == NULL) {
    GST_LOG ("Missing 'description' field for streamprofile %s", profilename);
  }
Edward Hervey's avatar
Edward Hervey committed
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668

  /* Parse the remaining fields */
  proftype = g_key_file_get_value (in, profilename, "type", NULL);
  if (!proftype) {
    GST_WARNING ("Missing 'type' field for streamprofile %s", profilename);
    return NULL;
  }

  format = g_key_file_get_value (in, profilename, "format", NULL);
  if (format) {
    formatcaps = gst_caps_from_string (format);
    g_free (format);
  }

  preset = g_key_file_get_value (in, profilename, "preset", NULL);

  restriction = g_key_file_get_value (in, profilename, "restriction", NULL);
  if (restriction) {
    restrictioncaps = gst_caps_from_string (restriction);
    g_free (restriction);
  }

  presence = g_key_file_get_integer (in, profilename, "presence", NULL);
  pass = g_key_file_get_integer (in, profilename, "pass", NULL);
  variableframerate =
      g_key_file_get_boolean (in, profilename, "variableframerate", NULL);

  /* Build the streamprofile ! */
  if (!g_strcmp0 (proftype, "container")) {
    GstEncodingProfile *pprof;

    sprof =
        (GstEncodingProfile *) gst_encoding_container_profile_new (pname,
        description, formatcaps, preset);
    /* Now look for the stream profiles */
    for (i = 0; i < nbgroups; i++) {
      if (!g_ascii_strncasecmp (groups[i], "streamprofile-", 13)) {
        pprof = parse_encoding_profile (in, pname, groups[i], nbgroups, groups);
        if (pprof) {
          gst_encoding_container_profile_add_profile (
              (GstEncodingContainerProfile *) sprof, pprof);
        }
      }
    }
  } else if (!g_strcmp0 (proftype, "video")) {
    sprof =
        (GstEncodingProfile *) gst_encoding_video_profile_new (formatcaps,
        preset, restrictioncaps, presence);
    gst_encoding_video_profile_set_variableframerate ((GstEncodingVideoProfile
            *) sprof, variableframerate);
    gst_encoding_video_profile_set_pass ((GstEncodingVideoProfile *) sprof,
        pass);
669
670
    gst_encoding_profile_set_name (sprof, pname);
    gst_encoding_profile_set_description (sprof, description);
Edward Hervey's avatar
Edward Hervey committed
671
672
673
674
  } else if (!g_strcmp0 (proftype, "audio")) {
    sprof =
        (GstEncodingProfile *) gst_encoding_audio_profile_new (formatcaps,
        preset, restrictioncaps, presence);
675
676
    gst_encoding_profile_set_name (sprof, pname);
    gst_encoding_profile_set_description (sprof, description);
Edward Hervey's avatar
Edward Hervey committed
677
678
679
680
681
682
683
684
  } else
    GST_ERROR ("Unknown profile format '%s'", proftype);

  if (restrictioncaps)
    gst_caps_unref (restrictioncaps);
  if (formatcaps)
    gst_caps_unref (formatcaps);

685
686
687
688
  g_free (pname);
  g_free (description);
  g_free (preset);
  g_free (proftype);
Edward Hervey's avatar
Edward Hervey committed
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715

  return sprof;
}

static GstEncodingTarget *
parse_keyfile (GKeyFile * in, gchar * targetname, gchar * categoryname,
    gchar * description)
{
  GstEncodingTarget *res = NULL;
  GstEncodingProfile *prof;
  gchar **groups;
  gsize i, nbgroups;

  res = gst_encoding_target_new (targetname, categoryname, description, NULL);

  /* Figure out the various profiles */
  groups = g_key_file_get_groups (in, &nbgroups);
  for (i = 0; i < nbgroups; i++) {
    if (!g_ascii_strncasecmp (groups[i], "profile-", 8)) {
      prof = parse_encoding_profile (in, NULL, groups[i], nbgroups, groups);
      if (prof)
        gst_encoding_target_add_profile (res, prof);
    }
  }

  g_strfreev (groups);

716
717
718
  g_free (targetname);
  g_free (categoryname);
  g_free (description);
Edward Hervey's avatar
Edward Hervey committed
719
720
721
722
723
724
725
726
727
728

  return res;
}

static GKeyFile *
load_file_and_read_header (const gchar * path, gchar ** targetname,
    gchar ** categoryname, gchar ** description, GError ** error)
{
  GKeyFile *in;
  gboolean res;
729
730
731
  GError *key_error = NULL;

  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
Edward Hervey's avatar
Edward Hervey committed
732
733
734
735
736
737
738

  in = g_key_file_new ();

  GST_DEBUG ("path:%s", path);

  res =
      g_key_file_load_from_file (in, path,
739
740
      G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &key_error);
  if (!res || key_error != NULL)
Edward Hervey's avatar
Edward Hervey committed
741
742
    goto load_error;

743
  key_error = NULL;
Edward Hervey's avatar
Edward Hervey committed
744
  *targetname =
745
      g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "name", &key_error);
Edward Hervey's avatar
Edward Hervey committed
746
747
748
749
750
751
752
753
754
755
756
757
758
759
  if (!*targetname)
    goto empty_name;

  *categoryname =
      g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "category", NULL);
  *description =
      g_key_file_get_value (in, GST_ENCODING_TARGET_HEADER, "description",
      NULL);

  return in;

load_error:
  {
    GST_WARNING ("Unable to read GstEncodingTarget file %s: %s",
760
761
        path, key_error->message);
    g_propagate_error (error, key_error);
Edward Hervey's avatar
Edward Hervey committed
762
763
764
765
766
767
    g_key_file_free (in);
    return NULL;
  }

empty_name:
  {
768
769
    GST_WARNING ("Wrong header in file %s: %s", path, key_error->message);
    g_propagate_error (error, key_error);
Edward Hervey's avatar
Edward Hervey committed
770
771
772
773
774
775
    g_key_file_free (in);
    return NULL;
  }
}

/**
776
 * gst_encoding_target_load_from_file:
777
 * @filepath: (type filename): The file location to load the #GstEncodingTarget from
778
 * @error: If an error occurred, this field will be filled in.
Edward Hervey's avatar
Edward Hervey committed
779
780
781
 *
 * Opens the provided file and returns the contained #GstEncodingTarget.
 *
782
783
 * Returns: (transfer full): The #GstEncodingTarget contained in the file, else
 * %NULL
Edward Hervey's avatar
Edward Hervey committed
784
785
786
 */

GstEncodingTarget *
787
gst_encoding_target_load_from_file (const gchar * filepath, GError ** error)
Edward Hervey's avatar
Edward Hervey committed
788
789
790
791
792
{
  GKeyFile *in;
  gchar *targetname, *categoryname, *description;
  GstEncodingTarget *res = NULL;

793
  in = load_file_and_read_header (filepath, &targetname, &categoryname,
Edward Hervey's avatar
Edward Hervey committed
794
795
796
797
798
799
800
801
802
803
804
805
      &description, error);
  if (!in)
    goto beach;

  res = parse_keyfile (in, targetname, categoryname, description);

  g_key_file_free (in);

beach:
  return res;
}

806
/*
807
808
809
810
811
812
813
814
 * returned list contents must be freed
 */
static GList *
get_matching_filenames (gchar * path, gchar * filename)
{
  GList *res = NULL;
  GDir *topdir;
  const gchar *subdirname;
815
  gchar *tmp;
816
817
818
819
820

  topdir = g_dir_open (path, 0, NULL);
  if (G_UNLIKELY (topdir == NULL))
    return NULL;

821
822
823
  tmp = g_build_filename (path, filename, NULL);
  if (g_file_test (tmp, G_FILE_TEST_EXISTS))
    res = g_list_append (res, tmp);
824
825
  else
    g_free (tmp);
826

827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
  while ((subdirname = g_dir_read_name (topdir))) {
    gchar *ltmp = g_build_filename (path, subdirname, NULL);

    if (g_file_test (ltmp, G_FILE_TEST_IS_DIR)) {
      gchar *tmp = g_build_filename (path, subdirname, filename, NULL);
      /* Test to see if we have a file named like that in that directory */
      if (g_file_test (tmp, G_FILE_TEST_EXISTS))
        res = g_list_append (res, tmp);
      else
        g_free (tmp);
    }
    g_free (ltmp);
  }

  g_dir_close (topdir);

  return res;
}

static GstEncodingTarget *
gst_encoding_target_subload (gchar * path, const gchar * category,
    gchar * lfilename, GError ** error)
{
  GstEncodingTarget *target = NULL;

  if (category) {
    gchar *filename;

    filename = g_build_filename (path, category, lfilename, NULL);
856
    target = gst_encoding_target_load_from_file (filename, error);
857
858
859
860
861
862
    g_free (filename);
  } else {
    GList *tmp, *tries = get_matching_filenames (path, lfilename);

    /* Try to find a file named %s.gstprofile in any subdirectories */
    for (tmp = tries; tmp; tmp = tmp->next) {
863
      target = gst_encoding_target_load_from_file ((gchar *) tmp->data, NULL);
864
865
866
867
868
869
870
871
872
873
874
      if (target)
        break;
    }
    g_list_foreach (tries, (GFunc) g_free, NULL);
    if (tries)
      g_list_free (tries);
  }

  return target;
}

Edward Hervey's avatar
Edward Hervey committed
875
876
/**
 * gst_encoding_target_load:
877
878
879
 * @name: the name of the #GstEncodingTarget to load (automatically
 * converted to lower case internally as capital letters are not
 * valid for target names).
880
881
 * @category: (allow-none): the name of the target category, like
 * #GST_ENCODING_CATEGORY_DEVICE. Can be %NULL
882
 * @error: If an error occurred, this field will be filled in.
Edward Hervey's avatar
Edward Hervey committed
883
884
885
886
 *
 * Searches for the #GstEncodingTarget with the given name, loads it
 * and returns it.
 *
887
888
 * If the category name is specified only targets from that category will be
 * searched for.
Edward Hervey's avatar
Edward Hervey committed
889
 *
890
 * Returns: (transfer full): The #GstEncodingTarget if available, else %NULL.
Edward Hervey's avatar
Edward Hervey committed
891
892
 */
GstEncodingTarget *
893
894
gst_encoding_target_load (const gchar * name, const gchar * category,
    GError ** error)
Edward Hervey's avatar
Edward Hervey committed
895
{
896
  gint i;
897
  gchar *p, *lname, *lfilename = NULL, *tldir, **encoding_target_dirs;
898
  const gchar *envvar;
899
  GstEncodingTarget *target = NULL;
Edward Hervey's avatar
Edward Hervey committed
900

901
  g_return_val_if_fail (name != NULL, NULL);
Edward Hervey's avatar
Edward Hervey committed
902

903
904
905
906
907
  p = lname = g_str_to_ascii (name, NULL);
  for (; *p; ++p)
    *p = g_ascii_tolower (*p);

  if (!validate_name (lname))
908
909
910
911
912
    goto invalid_name;

  if (category && !validate_name (category))
    goto invalid_category;

913
  lfilename = g_strdup_printf ("%s" GST_ENCODING_TARGET_SUFFIX, lname);
914

915
916
917
918
919
920
  envvar = g_getenv ("GST_ENCODING_TARGET_PATH");
  if (envvar) {
    encoding_target_dirs = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, -1);
    for (i = 0; encoding_target_dirs[i]; i++) {
      target = gst_encoding_target_subload (encoding_target_dirs[i],
          category, lfilename, error);
921
922
923

      if (target)
        break;
924
925
926
927
928
929
    }
    g_strfreev (encoding_target_dirs);
    if (target)
      goto done;
  }

930
  /* Try from local profiles */
931

932
  tldir =
Sebastian Dröge's avatar
Sebastian Dröge committed
933
      g_build_filename (g_get_user_data_dir (), "gstreamer-" GST_API_VERSION,
934
      GST_ENCODING_TARGET_DIRECTORY, NULL);
935
936
937
938
939
940
  target = gst_encoding_target_subload (tldir, category, lfilename, error);
  g_free (tldir);

  if (target == NULL) {
    /* Try from system-wide profiles */
    tldir =
Sebastian Dröge's avatar
Sebastian Dröge committed
941
        g_build_filename (GST_DATADIR, "gstreamer-" GST_API_VERSION,
942
        GST_ENCODING_TARGET_DIRECTORY, NULL);
943
944
945
    target = gst_encoding_target_subload (tldir, category, lfilename, error);
    g_free (tldir);
  }
Edward Hervey's avatar
Edward Hervey committed
946

947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
  if (!target) {
    GList *tmp, *targets = gst_encoding_list_all_targets (NULL);

    for (tmp = targets; tmp; tmp = tmp->next) {
      gint i;
      GstEncodingTarget *tmptarget = tmp->data;
      gchar **names = g_strsplit (tmptarget->name, ";", -1);

      for (i = 0; names[i]; i++) {
        if (!g_strcmp0 (names[i], lname) && (!category ||
                !g_strcmp0 (tmptarget->category, category))) {
          target = gst_object_ref (tmptarget);

          break;
        }
      }
      g_strfreev (names);

      if (target)
        break;
    }

    g_list_free_full (targets, gst_object_unref);
  }


973
done:
974
  g_free (lfilename);
975
  g_free (lname);
976
977
978
979
980

  return target;

invalid_name:
  {
981
    GST_INFO ("Invalid name for encoding target : '%s'", name);
982
    goto done;
983
984
985
  }
invalid_category:
  {
986
    GST_INFO ("Invalid name for encoding category : '%s'", category);
987
    goto done;
988
  }
Edward Hervey's avatar
Edward Hervey committed
989
990
991
}

/**
992
 * gst_encoding_target_save_to_file:
Edward Hervey's avatar
Edward Hervey committed
993
 * @target: a #GstEncodingTarget
994
 * @filepath: (type filename): the location to store the @target at.
995
 * @error: If an error occurred, this field will be filled in.
Edward Hervey's avatar
Edward Hervey committed
996
 *
997
 * Saves the @target to the provided file location.
Edward Hervey's avatar
Edward Hervey committed
998
999
1000
 *
 * Returns: %TRUE if the target was correctly saved, else %FALSE.
 **/
For faster browsing, not all history is shown. View entire blame