tizen 2.3 release
[framework/multimedia/gst-plugins-base0.10.git] / gst-libs / gst / audio / multichannel.c
1 /* GStreamer Multichannel-Audio helper functions
2  * (c) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 /**
20  * SECTION:gstmultichannel
21  * @short_description: Support for multichannel audio elements
22  *
23  * This module contains some helper functions and a enum to work with
24  * multichannel audio.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "multichannel.h"
32
33 #define GST_AUDIO_CHANNEL_POSITIONS_FIELD_NAME "channel-positions"
34
35 /**
36  * gst_audio_check_channel_positions:
37  * @pos: An array of #GstAudioChannelPosition.
38  * @channels: The number of elements in @pos.
39  *
40  * This functions checks if the given channel positions are valid. Channel
41  * positions are valid if:
42  * <itemizedlist>
43  *   <listitem><para>No channel positions appears twice or all positions are %GST_AUDIO_CHANNEL_POSITION_NONE.
44  *   </para></listitem>
45  *   <listitem><para>Either all or none of the channel positions are %GST_AUDIO_CHANNEL_POSITION_NONE.
46  *   </para></listitem>
47  *   <listitem><para>%GST_AUDIO_CHANNEL_POSITION_FRONT_MONO and %GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT or %GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT don't appear together in the given positions.
48  *   </para></listitem>
49  * </itemizedlist>
50  *
51  * Since: 0.10.20
52  *
53  * Returns: %TRUE if the given channel positions are valid
54  * and %FALSE otherwise.
55  */
56 gboolean
57 gst_audio_check_channel_positions (const GstAudioChannelPosition * pos,
58     guint channels)
59 {
60   gint i, n;
61
62   const struct
63   {
64     const GstAudioChannelPosition pos1[2];
65     const GstAudioChannelPosition pos2[1];
66   } conf[] = {
67     /* front: mono <-> stereo */
68     { {
69     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
70             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
71     GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}}, { {
72     GST_AUDIO_CHANNEL_POSITION_INVALID}}
73   };
74
75   g_return_val_if_fail (pos != NULL, FALSE);
76   g_return_val_if_fail (channels > 0, FALSE);
77
78   /* check for invalid channel positions */
79   for (n = 0; n < channels; n++) {
80     if (pos[n] <= GST_AUDIO_CHANNEL_POSITION_INVALID ||
81         pos[n] >= GST_AUDIO_CHANNEL_POSITION_NUM) {
82       GST_WARNING ("Channel position %d for channel %d is invalid", pos[n], n);
83       return FALSE;
84     }
85   }
86
87   /* either all channel positions are NONE or all are defined,
88    * but having only some channel positions NONE and others not
89    * is not allowed */
90   if (pos[0] == GST_AUDIO_CHANNEL_POSITION_NONE) {
91     for (n = 1; n < channels; ++n) {
92       if (pos[n] != GST_AUDIO_CHANNEL_POSITION_NONE) {
93         GST_WARNING ("Either all channel positions must be defined, or all "
94             "be set to NONE, having only some defined is not allowed");
95         return FALSE;
96       }
97     }
98     /* all positions are NONE, we are done here */
99     return TRUE;
100   }
101
102   /* check for multiple position occurrences */
103   for (i = GST_AUDIO_CHANNEL_POSITION_INVALID + 1;
104       i < GST_AUDIO_CHANNEL_POSITION_NUM; i++) {
105     gint count = 0;
106
107     for (n = 0; n < channels; n++) {
108       if (pos[n] == i)
109         count++;
110     }
111
112     /* NONE may not occur mixed with other channel positions */
113     if (i == GST_AUDIO_CHANNEL_POSITION_NONE && count > 0) {
114       GST_WARNING ("Either all channel positions must be defined, or all "
115           "be set to NONE, having only some defined is not allowed");
116       return FALSE;
117     }
118
119     if (count > 1) {
120       GST_WARNING ("Channel position %d occurred %d times, not allowed",
121           i, count);
122       return FALSE;
123     }
124   }
125
126   /* check for position conflicts */
127   for (i = 0; conf[i].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; i++) {
128     gboolean found1 = FALSE, found2 = FALSE;
129
130     for (n = 0; n < channels; n++) {
131       if (pos[n] == conf[i].pos1[0] || pos[n] == conf[i].pos1[1])
132         found1 = TRUE;
133       else if (pos[n] == conf[i].pos2[0])
134         found2 = TRUE;
135     }
136
137     if (found1 && found2) {
138       GST_WARNING ("Found conflicting channel positions %d/%d and %d",
139           conf[i].pos1[0], conf[i].pos1[1], conf[i].pos2[0]);
140       return FALSE;
141     }
142   }
143
144   return TRUE;
145 }
146
147 /* FIXME: these default positions may or may not be correct. In any
148  * case, they are mostly just a fallback for buggy plugins, so it
149  * should not really matter too much */
150 #define NUM_DEF_CHANS  8
151 static const GstAudioChannelPosition
152     default_positions[NUM_DEF_CHANS][NUM_DEF_CHANS] = {
153   /* 1 channel */
154   {
155         GST_AUDIO_CHANNEL_POSITION_FRONT_MONO,
156       },
157   /* 2 channels */
158   {
159         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
160         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
161       },
162   /* 3 channels (2.1) */
163   {
164         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
165         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
166         GST_AUDIO_CHANNEL_POSITION_LFE, /* or FRONT_CENTER for 3.0? */
167       },
168   /* 4 channels (4.0 or 3.1?) */
169   {
170         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
171         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
172         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
173         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
174       },
175   /* 5 channels */
176   {
177         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
178         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
179         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
180         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
181         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
182       },
183   /* 6 channels */
184   {
185         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
186         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
187         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
188         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
189         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
190         GST_AUDIO_CHANNEL_POSITION_LFE,
191       },
192   /* 7 channels */
193   {
194         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
195         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
196         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
197         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
198         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
199         GST_AUDIO_CHANNEL_POSITION_LFE,
200         GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
201       },
202   /* 8 channels */
203   {
204         GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
205         GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
206         GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
207         GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
208         GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
209         GST_AUDIO_CHANNEL_POSITION_LFE,
210         GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
211         GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT,
212       }
213 };
214
215 /**
216  * gst_audio_get_channel_positions:
217  * @str: A #GstStructure to retrieve channel positions from.
218  *
219  * Retrieves a number of (fixed!) audio channel positions from
220  * the provided #GstStructure and returns it as a newly allocated
221  * array. The caller should g_free () this array. The caller
222  * should also check that the members in this #GstStructure are
223  * indeed "fixed" before calling this function.
224  *
225  * Returns: a newly allocated array containing the channel
226  * positions as provided in the given #GstStructure. Returns
227  * NULL on error.
228  */
229
230 GstAudioChannelPosition *
231 gst_audio_get_channel_positions (GstStructure * str)
232 {
233   GstAudioChannelPosition *pos;
234
235   gint channels, n;
236
237   const GValue *pos_val_arr, *pos_val_entry;
238
239   gboolean res;
240
241   GType t;
242
243   /* get number of channels, general type checkups */
244   g_return_val_if_fail (str != NULL, NULL);
245   res = gst_structure_get_int (str, "channels", &channels);
246   g_return_val_if_fail (res, NULL);
247   g_return_val_if_fail (channels > 0, NULL);
248   pos_val_arr = gst_structure_get_value (str,
249       GST_AUDIO_CHANNEL_POSITIONS_FIELD_NAME);
250
251   /* The following checks are here to retain compatibility for plugins not
252    * implementing this field. They expect that channels=1 implies mono
253    * and channels=2 implies stereo, so we follow that. */
254   if (pos_val_arr == NULL) {
255     /* channel layouts for 1 and 2 channels are implicit, don't warn */
256     if (channels > 2) {
257       g_warning ("Failed to retrieve channel layout from caps. This usually "
258           "means there is a GStreamer element that does not implement "
259           "multichannel audio correctly. Please file a bug.");
260     }
261
262     /* just return some default channel layout if we have one */
263     if (channels >= 1 && channels <= NUM_DEF_CHANS) {
264       const GstAudioChannelPosition *p;
265
266       p = default_positions[channels - 1];
267       return g_memdup (p, channels * sizeof (GstAudioChannelPosition));
268     }
269
270     return NULL;
271   }
272
273   g_return_val_if_fail (gst_value_array_get_size (pos_val_arr) == channels,
274       NULL);
275   for (n = 0; n < channels; n++) {
276     t = G_VALUE_TYPE (gst_value_array_get_value (pos_val_arr, n));
277     g_return_val_if_fail (t == GST_TYPE_AUDIO_CHANNEL_POSITION, NULL);
278   }
279
280   /* ... and fill array */
281   pos = g_new (GstAudioChannelPosition, channels);
282   for (n = 0; n < channels; n++) {
283     pos_val_entry = gst_value_array_get_value (pos_val_arr, n);
284     pos[n] = g_value_get_enum (pos_val_entry);
285   }
286
287   if (!gst_audio_check_channel_positions (pos, channels)) {
288     g_free (pos);
289     return NULL;
290   }
291
292   return pos;
293 }
294
295 void priv_gst_audio_info_fill_default_channel_positions (GstAudioInfo * info);
296
297 void
298 priv_gst_audio_info_fill_default_channel_positions (GstAudioInfo * info)
299 {
300   guint channels, i;
301
302   g_assert (info != NULL);
303
304   channels = GST_AUDIO_INFO_CHANNELS (info);
305
306   g_assert (channels > 0);
307
308   if (channels <= NUM_DEF_CHANS) {
309     /* just return some default channel layout if we have one */
310     for (i = 0; i < channels; ++i)
311       info->position[i] = default_positions[channels - 1][i];
312   } else {
313     /* for many many channels, the positions are always NONE */
314     for (i = 0; i < G_N_ELEMENTS (info->position); i++)
315       info->position[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
316   }
317
318   info->flags |= GST_AUDIO_FLAG_DEFAULT_POSITIONS;
319 }
320
321 /**
322  * gst_audio_set_channel_positions:
323  * @str: A #GstStructure to set channel positions on.
324  * @pos: an array of channel positions. The number of members
325  *       in this array should be equal to the (fixed!) number
326  *       of the "channels" field in the given #GstStructure.
327  *
328  * Adds a "channel-positions" field to the given #GstStructure,
329  * which will represent the channel positions as given in the
330  * provided #GstAudioChannelPosition array.
331  */
332
333 void
334 gst_audio_set_channel_positions (GstStructure * str,
335     const GstAudioChannelPosition * pos)
336 {
337   GValue pos_val_arr = { 0 }, pos_val_entry = {
338   0};
339   gint channels, n;
340
341   gboolean res;
342
343   /* get number of channels, checkups */
344   g_return_if_fail (str != NULL);
345   g_return_if_fail (pos != NULL);
346   res = gst_structure_get_int (str, "channels", &channels);
347   g_return_if_fail (res);
348   g_return_if_fail (channels > 0);
349   if (!gst_audio_check_channel_positions (pos, channels))
350     return;
351
352   /* build gvaluearray from positions */
353   g_value_init (&pos_val_entry, GST_TYPE_AUDIO_CHANNEL_POSITION);
354   g_value_init (&pos_val_arr, GST_TYPE_ARRAY);
355   for (n = 0; n < channels; n++) {
356     g_value_set_enum (&pos_val_entry, pos[n]);
357     gst_value_array_append_value (&pos_val_arr, &pos_val_entry);
358   }
359   g_value_unset (&pos_val_entry);
360
361   /* add to structure */
362   gst_structure_set_value (str,
363       GST_AUDIO_CHANNEL_POSITIONS_FIELD_NAME, &pos_val_arr);
364   g_value_unset (&pos_val_arr);
365 }
366
367 /**
368  * gst_audio_set_structure_channel_positions_list:
369  * @str: #GstStructure to set the list of channel positions
370  *       on.
371  * @pos: the array containing one or more possible audio
372  *       channel positions that we should add in each value
373  *       of the array in the given structure.
374  * @num_positions: the number of values in pos.
375  *
376  * Sets a (possibly non-fixed) list of possible audio channel
377  * positions (given in pos) on the given structure. The
378  * structure, after this function has been called, will contain
379  * a "channel-positions" field with an array of the size of
380  * the "channels" field value in the given structure (note
381  * that this means that the channels field in the provided
382  * structure should be fixed!). Each value in the array will
383  * contain each of the values given in the pos array.
384  */
385
386 void
387 gst_audio_set_structure_channel_positions_list (GstStructure * str,
388     const GstAudioChannelPosition * pos, gint num_positions)
389 {
390   gint channels, n, c;
391   GValue pos_val_arr = { 0 }, pos_val_list = {
392   0}, pos_val_entry = {
393   0};
394   gboolean res;
395
396   /* get number of channels, general type checkups */
397   g_return_if_fail (str != NULL);
398   g_return_if_fail (num_positions > 0);
399   g_return_if_fail (pos != NULL);
400   res = gst_structure_get_int (str, "channels", &channels);
401   g_return_if_fail (res);
402   g_return_if_fail (channels > 0);
403
404   /* create the array of lists */
405   g_value_init (&pos_val_arr, GST_TYPE_ARRAY);
406   g_value_init (&pos_val_entry, GST_TYPE_AUDIO_CHANNEL_POSITION);
407   for (n = 0; n < channels; n++) {
408     g_value_init (&pos_val_list, GST_TYPE_LIST);
409     for (c = 0; c < num_positions; c++) {
410       g_value_set_enum (&pos_val_entry, pos[c]);
411       gst_value_list_append_value (&pos_val_list, &pos_val_entry);
412     }
413     gst_value_array_append_value (&pos_val_arr, &pos_val_list);
414     g_value_unset (&pos_val_list);
415   }
416   g_value_unset (&pos_val_entry);
417   gst_structure_set_value (str, GST_AUDIO_CHANNEL_POSITIONS_FIELD_NAME,
418       &pos_val_arr);
419   g_value_unset (&pos_val_arr);
420 }
421
422 /*
423  * Helper function for below. The structure will be conserved,
424  * but might be cut down. Any additional structures that were
425  * created will be stored in the returned caps.
426  */
427
428 static GstCaps *
429 add_list_to_struct (GstStructure * str,
430     const GstAudioChannelPosition * pos, gint num_positions)
431 {
432   GstCaps *caps = gst_caps_new_empty ();
433
434   const GValue *chan_val;
435
436   chan_val = gst_structure_get_value (str, "channels");
437   if (G_VALUE_TYPE (chan_val) == G_TYPE_INT) {
438     gst_audio_set_structure_channel_positions_list (str, pos, num_positions);
439   } else if (G_VALUE_TYPE (chan_val) == GST_TYPE_LIST) {
440     gint size;
441
442     const GValue *sub_val;
443
444     size = gst_value_list_get_size (chan_val);
445     sub_val = gst_value_list_get_value (chan_val, 0);
446     gst_structure_set_value (str, "channels", sub_val);
447     gst_caps_append (caps, add_list_to_struct (str, pos, num_positions));
448     while (--size > 0) {
449       str = gst_structure_copy (str);
450       sub_val = gst_value_list_get_value (chan_val, size);
451       gst_structure_set_value (str, "channels", sub_val);
452       gst_caps_append (caps, add_list_to_struct (str, pos, num_positions));
453       gst_caps_append_structure (caps, str);
454     }
455   } else if (G_VALUE_TYPE (chan_val) == GST_TYPE_INT_RANGE) {
456     gint min, max;
457
458     min = gst_value_get_int_range_min (chan_val);
459     max = gst_value_get_int_range_max (chan_val);
460
461     gst_structure_set (str, "channels", G_TYPE_INT, min, NULL);
462     gst_audio_set_structure_channel_positions_list (str, pos, num_positions);
463     for (++min; min < max; min++) {
464       str = gst_structure_copy (str);
465       gst_structure_set (str, "channels", G_TYPE_INT, min, NULL);
466       gst_audio_set_structure_channel_positions_list (str, pos, num_positions);
467       gst_caps_append_structure (caps, str);
468     }
469   } else {
470     g_warning ("Unexpected value type '%s' for channels field",
471         GST_STR_NULL (g_type_name (G_VALUE_TYPE (chan_val))));
472   }
473
474   return caps;
475 }
476
477 /**
478  * gst_audio_set_caps_channel_positions_list:
479  * @caps: #GstCaps to set the list of channel positions on.
480  * @pos: the array containing one or more possible audio
481  *       channel positions that we should add in each value
482  *       of the array in the given structure.
483  * @num_positions: the number of values in pos.
484  *
485  * Sets a (possibly non-fixed) list of possible audio channel
486  * positions (given in pos) on the given caps. Each of the
487  * structures of the caps, after this function has been called,
488  * will contain a "channel-positions" field with an array.
489  * Each value in the array will contain each of the values given
490  * in the pos array. Note that the size of the caps might be
491  * increased by this, since each structure with a "channel-
492  * positions" field needs to have a fixed "channels" field.
493  * The input caps is not required to have this.
494  */
495
496 void
497 gst_audio_set_caps_channel_positions_list (GstCaps * caps,
498     const GstAudioChannelPosition * pos, gint num_positions)
499 {
500   gint size, n;
501
502   /* get number of channels, general type checkups */
503   g_return_if_fail (caps != NULL);
504   g_return_if_fail (num_positions > 0);
505   g_return_if_fail (pos != NULL);
506
507   size = gst_caps_get_size (caps);
508   for (n = 0; n < size; n++) {
509     gst_caps_append (caps, add_list_to_struct (gst_caps_get_structure (caps,
510                 n), pos, num_positions));
511   }
512 }
513
514 /**
515  * gst_audio_fixate_channel_positions:
516  * @str: a #GstStructure containing a (possibly unfixed)
517  *       "channel-positions" field.
518  *
519  * Custom fixate function. Elements that implement some sort of
520  * channel conversion algorithm should use this function for
521  * fixating on GstAudioChannelPosition properties. It will take
522  * care of equal channel positioning (left/right). Caller g_free()s
523  * the return value. The input properties may be (and are supposed
524  * to be) unfixed.
525  * Note that this function is mostly a hack because we currently
526  * have no way to add default fixation functions for new GTypes.
527  *
528  * Returns: fixed values that the caller could use as a fixed
529  * set of #GstAudioChannelPosition values.
530  */
531
532 GstAudioChannelPosition *
533 gst_audio_fixate_channel_positions (GstStructure * str)
534 {
535   GstAudioChannelPosition *pos;
536
537   gint channels, n, num_unfixed = 0, i, c;
538
539   const GValue *pos_val_arr, *pos_val_entry, *pos_val;
540
541   gboolean res, is_stereo = TRUE;
542
543   GType t;
544
545   /*
546    * We're going to do this cluelessly. We'll make an array of values that
547    * conflict with each other and, for each iteration in this array, pick
548    * either one until all unknown values are filled. This might not work in
549    * corner cases but should work OK for the general case.
550    */
551   const struct
552   {
553     const GstAudioChannelPosition pos1[2];
554     const GstAudioChannelPosition pos2[1];
555   } conf[] = {
556     /* front: mono <-> stereo */
557     {
558       {
559       GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
560             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
561     GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}}, { {
562     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
563             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
564     GST_AUDIO_CHANNEL_POSITION_INVALID}}, { {
565     GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
566     GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}}, { {
567     GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
568             GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
569     GST_AUDIO_CHANNEL_POSITION_INVALID}}, { {
570     GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
571     GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, { {
572     GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
573     GST_AUDIO_CHANNEL_POSITION_LFE}}, { {
574     GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
575             GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, {
576     GST_AUDIO_CHANNEL_POSITION_INVALID}}, { {
577     GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
578     GST_AUDIO_CHANNEL_POSITION_INVALID}}
579   };
580   struct
581   {
582     gint num_opt[3];
583     guint num_opts[3];
584     gboolean is_fixed[3];
585     gint choice;                /* -1 is none, 0 is the two, 1 is the one */
586   } opt;
587
588   /* get number of channels, general type checkups */
589   g_return_val_if_fail (str != NULL, NULL);
590   res = gst_structure_get_int (str, "channels", &channels);
591   g_return_val_if_fail (res, NULL);
592   g_return_val_if_fail (channels > 0, NULL);
593
594   /* 0.8.x mono/stereo checks */
595   pos_val_arr = gst_structure_get_value (str,
596       GST_AUDIO_CHANNEL_POSITIONS_FIELD_NAME);
597   if (!pos_val_arr && (channels == 1 || channels == 2)) {
598     pos = g_new (GstAudioChannelPosition, channels);
599     if (channels == 1) {
600       pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
601     } else {
602       pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
603       pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
604     }
605     return pos;
606   }
607   g_return_val_if_fail (pos_val_arr != NULL, NULL);
608   g_return_val_if_fail (gst_value_array_get_size (pos_val_arr) == channels,
609       NULL);
610   for (n = 0; n < channels; n++) {
611     t = G_VALUE_TYPE (gst_value_array_get_value (pos_val_arr, n));
612     g_return_val_if_fail (t == GST_TYPE_LIST ||
613         t == GST_TYPE_AUDIO_CHANNEL_POSITION, NULL);
614   }
615
616   /* all unknown, to start with */
617   pos = g_new (GstAudioChannelPosition, channels);
618   for (n = 0; n < channels; n++)
619     pos[n] = GST_AUDIO_CHANNEL_POSITION_INVALID;
620   num_unfixed = channels;
621
622   /* Iterate the array of conflicting values */
623   for (i = 0; conf[i].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID ||
624       conf[i].pos2[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; i++) {
625     /* front/center only important if not mono (obviously) */
626     if (conf[i].pos1[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER &&
627         !is_stereo)
628       continue;
629
630     /* init values */
631     for (n = 0; n < 3; n++) {
632       opt.num_opt[n] = -1;
633       opt.num_opts[n] = -1;
634       opt.is_fixed[n] = FALSE;
635     }
636
637     /* Now, we'll see for each channel if it allows for any of the values in
638      * the set of conflicting audio channel positions and keep scores. */
639     for (n = 0; n < channels; n++) {
640       /* if the channel is already taken, don't bother */
641       if (pos[n] != GST_AUDIO_CHANNEL_POSITION_INVALID)
642         continue;
643
644       pos_val_entry = gst_value_array_get_value (pos_val_arr, n);
645       t = G_VALUE_TYPE (pos_val_entry);
646       if (t == GST_TYPE_LIST) {
647         /* This algorhythm is suboptimal. */
648         for (c = 0; c < gst_value_list_get_size (pos_val_entry); c++) {
649           pos_val = gst_value_list_get_value (pos_val_entry, c);
650           if (g_value_get_enum (pos_val) == conf[i].pos1[0] &&
651               opt.num_opts[0] > gst_value_list_get_size (pos_val_entry) &&
652               !opt.is_fixed[0]) {
653             /* Now test if the old position of num_opt[0] also allows for
654              * the other channel (which was skipped previously). If so,
655              * keep score. */
656             if (opt.num_opt[0] != -1) {
657               gint c1;
658
659               pos_val_entry = gst_value_array_get_value (pos_val_arr,
660                   opt.num_opt[0]);
661               if (G_VALUE_TYPE (pos_val_entry) == GST_TYPE_LIST) {
662                 for (c1 = 0; c1 < gst_value_list_get_size (pos_val_entry); c1++) {
663                   pos_val = gst_value_list_get_value (pos_val_entry, c1);
664                   if (g_value_get_enum (pos_val) == conf[i].pos1[1] &&
665                       opt.num_opts[1] > opt.num_opts[0] && !opt.is_fixed[1]) {
666                     opt.num_opts[1] = opt.num_opts[0];
667                     opt.num_opt[1] = opt.num_opt[0];
668                   }
669                 }
670                 pos_val = gst_value_list_get_value (pos_val_entry, c);
671               }
672               pos_val_entry = gst_value_array_get_value (pos_val_arr, n);
673             }
674
675             /* and save values */
676             opt.num_opts[0] = gst_value_list_get_size (pos_val_entry);
677             opt.num_opt[0] = n;
678           } else if (g_value_get_enum (pos_val) == conf[i].pos1[1] &&
679               opt.num_opts[1] > gst_value_list_get_size (pos_val_entry) &&
680               !opt.is_fixed[1] && n != opt.num_opt[0]) {
681             opt.num_opts[1] = gst_value_list_get_size (pos_val_entry);
682             opt.num_opt[1] = n;
683           }
684
685           /* 2 goes separately, because 0/1 vs. 2 are separate */
686           if (g_value_get_enum (pos_val) == conf[i].pos2[0] &&
687               opt.num_opts[2] > gst_value_list_get_size (pos_val_entry) &&
688               !opt.is_fixed[2]) {
689             opt.num_opts[2] = gst_value_list_get_size (pos_val_entry);
690             opt.num_opt[2] = n;
691           }
692         }
693       } else {
694         if (g_value_get_enum (pos_val_entry) == conf[i].pos1[0]) {
695           opt.num_opt[0] = n;
696           opt.is_fixed[0] = TRUE;
697         } else if (g_value_get_enum (pos_val_entry) == conf[i].pos1[1]) {
698           opt.num_opt[1] = n;
699           opt.is_fixed[1] = TRUE;
700         } else if (g_value_get_enum (pos_val_entry) == conf[i].pos2[0]) {
701           opt.num_opt[2] = n;
702           opt.is_fixed[2] = TRUE;
703         }
704       }
705     }
706
707     /* check our results and choose either one */
708     if ((opt.is_fixed[0] || opt.is_fixed[1]) && opt.is_fixed[2]) {
709       g_warning ("Pre-fixated on both %d/%d and %d - conflict!",
710           conf[i].pos1[0], conf[i].pos1[1], conf[i].pos2[0]);
711       g_free (pos);
712       return NULL;
713     } else if ((opt.is_fixed[0] && opt.num_opt[1] == -1) ||
714         (opt.is_fixed[1] && opt.num_opt[0] == -1)) {
715       g_warning ("Pre-fixated one side, but other side n/a of %d/%d",
716           conf[i].pos1[0], conf[i].pos1[1]);
717       g_free (pos);
718       return NULL;
719     } else if (opt.is_fixed[0] || opt.is_fixed[1]) {
720       opt.choice = 0;
721     } else if (opt.is_fixed[2]) {
722       opt.choice = 1;
723     } else if (opt.num_opt[0] != -1 && opt.num_opt[1] != -1) {
724       opt.choice = 0;
725     } else if (opt.num_opt[2] != -1) {
726       opt.choice = 1;
727     } else {
728       opt.choice = -1;
729     }
730
731     /* stereo? Note that we keep is_stereo to TRUE if we didn't decide on
732      * any arrangement. The mono/stereo channels might be handled elsewhere
733      * which is clearly outside the scope of this element, so we cannot
734      * know and expect the application to handle that then. */
735     if (conf[i].pos2[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_MONO &&
736         opt.choice == 1) {
737       is_stereo = FALSE;
738     }
739
740     /* now actually decide what we'll do and fixate on that */
741     if (opt.choice == 0) {
742       g_assert (conf[i].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID &&
743           conf[i].pos1[1] != GST_AUDIO_CHANNEL_POSITION_INVALID);
744       pos[opt.num_opt[0]] = conf[i].pos1[0];
745       pos[opt.num_opt[1]] = conf[i].pos1[1];
746       num_unfixed -= 2;
747     } else if (opt.choice == 1) {
748       g_assert (conf[i].pos2[0] != GST_AUDIO_CHANNEL_POSITION_INVALID);
749       pos[opt.num_opt[2]] = conf[i].pos2[0];
750       num_unfixed--;
751     }
752   }
753
754   /* safety check */
755   if (num_unfixed > 0) {
756     g_warning ("%d unfixed channel positions left after fixation!",
757         num_unfixed);
758     g_free (pos);
759     return NULL;
760   }
761
762   if (!gst_audio_check_channel_positions (pos, channels)) {
763     g_free (pos);
764     return NULL;
765   }
766
767   return pos;
768 }