ringbuffer: remove old _full version
[platform/upstream/gstreamer.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   gint channels, n;
235   const GValue *pos_val_arr, *pos_val_entry;
236   gboolean res;
237   GType t;
238
239   /* get number of channels, general type checkups */
240   g_return_val_if_fail (str != NULL, NULL);
241   res = gst_structure_get_int (str, "channels", &channels);
242   g_return_val_if_fail (res, NULL);
243   g_return_val_if_fail (channels > 0, NULL);
244   pos_val_arr = gst_structure_get_value (str,
245       GST_AUDIO_CHANNEL_POSITIONS_FIELD_NAME);
246
247   /* The following checks are here to retain compatibility for plugins not
248    * implementing this field. They expect that channels=1 implies mono
249    * and channels=2 implies stereo, so we follow that. */
250   if (pos_val_arr == NULL) {
251     /* channel layouts for 1 and 2 channels are implicit, don't warn */
252     if (channels > 2) {
253       g_warning ("Failed to retrieve channel layout from caps. This usually "
254           "means there is a GStreamer element that does not implement "
255           "multichannel audio correctly. Please file a bug.");
256     }
257
258     /* just return some default channel layout if we have one */
259     if (channels >= 1 && channels <= NUM_DEF_CHANS) {
260       const GstAudioChannelPosition *p;
261
262       p = default_positions[channels - 1];
263       return g_memdup (p, channels * sizeof (GstAudioChannelPosition));
264     }
265
266     return NULL;
267   }
268
269   g_return_val_if_fail (gst_value_array_get_size (pos_val_arr) == channels,
270       NULL);
271   for (n = 0; n < channels; n++) {
272     t = G_VALUE_TYPE (gst_value_array_get_value (pos_val_arr, n));
273     g_return_val_if_fail (t == GST_TYPE_AUDIO_CHANNEL_POSITION, NULL);
274   }
275
276   /* ... and fill array */
277   pos = g_new (GstAudioChannelPosition, channels);
278   for (n = 0; n < channels; n++) {
279     pos_val_entry = gst_value_array_get_value (pos_val_arr, n);
280     pos[n] = g_value_get_enum (pos_val_entry);
281   }
282
283   if (!gst_audio_check_channel_positions (pos, channels)) {
284     g_free (pos);
285     return NULL;
286   }
287
288   return pos;
289 }
290
291 void priv_gst_audio_info_fill_default_channel_positions (GstAudioInfo * info);
292
293 void
294 priv_gst_audio_info_fill_default_channel_positions (GstAudioInfo * info)
295 {
296   guint channels, i;
297
298   g_assert (info != NULL);
299
300   channels = GST_AUDIO_INFO_CHANNELS (info);
301
302   g_assert (channels > 0);
303
304   if (channels <= NUM_DEF_CHANS) {
305     /* just return some default channel layout if we have one */
306     for (i = 0; i < channels; ++i)
307       info->position[i] = default_positions[channels - 1][i];
308   } else {
309     /* for many many channels, the positions are always NONE */
310     for (i = 0; i < G_N_ELEMENTS (info->position); i++)
311       info->position[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
312   }
313
314   info->flags |= GST_AUDIO_FLAG_DEFAULT_POSITIONS;
315 }
316
317 /**
318  * gst_audio_set_channel_positions:
319  * @str: A #GstStructure to set channel positions on.
320  * @pos: an array of channel positions. The number of members
321  *       in this array should be equal to the (fixed!) number
322  *       of the "channels" field in the given #GstStructure.
323  *
324  * Adds a "channel-positions" field to the given #GstStructure,
325  * which will represent the channel positions as given in the
326  * provided #GstAudioChannelPosition array.
327  */
328
329 void
330 gst_audio_set_channel_positions (GstStructure * str,
331     const GstAudioChannelPosition * pos)
332 {
333   GValue pos_val_arr = { 0 }, pos_val_entry = {
334   0};
335   gint channels, n;
336
337   gboolean res;
338
339   /* get number of channels, checkups */
340   g_return_if_fail (str != NULL);
341   g_return_if_fail (pos != NULL);
342   res = gst_structure_get_int (str, "channels", &channels);
343   g_return_if_fail (res);
344   g_return_if_fail (channels > 0);
345   if (!gst_audio_check_channel_positions (pos, channels))
346     return;
347
348   /* build gvaluearray from positions */
349   g_value_init (&pos_val_entry, GST_TYPE_AUDIO_CHANNEL_POSITION);
350   g_value_init (&pos_val_arr, GST_TYPE_ARRAY);
351   for (n = 0; n < channels; n++) {
352     g_value_set_enum (&pos_val_entry, pos[n]);
353     gst_value_array_append_value (&pos_val_arr, &pos_val_entry);
354   }
355   g_value_unset (&pos_val_entry);
356
357   /* add to structure */
358   gst_structure_set_value (str,
359       GST_AUDIO_CHANNEL_POSITIONS_FIELD_NAME, &pos_val_arr);
360   g_value_unset (&pos_val_arr);
361 }
362
363 /**
364  * gst_audio_set_structure_channel_positions_list:
365  * @str: #GstStructure to set the list of channel positions
366  *       on.
367  * @pos: the array containing one or more possible audio
368  *       channel positions that we should add in each value
369  *       of the array in the given structure.
370  * @num_positions: the number of values in pos.
371  *
372  * Sets a (possibly non-fixed) list of possible audio channel
373  * positions (given in pos) on the given structure. The
374  * structure, after this function has been called, will contain
375  * a "channel-positions" field with an array of the size of
376  * the "channels" field value in the given structure (note
377  * that this means that the channels field in the provided
378  * structure should be fixed!). Each value in the array will
379  * contain each of the values given in the pos array.
380  */
381
382 void
383 gst_audio_set_structure_channel_positions_list (GstStructure * str,
384     const GstAudioChannelPosition * pos, gint num_positions)
385 {
386   gint channels, n, c;
387   GValue pos_val_arr = { 0 }, pos_val_list = {
388   0}, pos_val_entry = {
389   0};
390   gboolean res;
391
392   /* get number of channels, general type checkups */
393   g_return_if_fail (str != NULL);
394   g_return_if_fail (num_positions > 0);
395   g_return_if_fail (pos != NULL);
396   res = gst_structure_get_int (str, "channels", &channels);
397   g_return_if_fail (res);
398   g_return_if_fail (channels > 0);
399
400   /* create the array of lists */
401   g_value_init (&pos_val_arr, GST_TYPE_ARRAY);
402   g_value_init (&pos_val_entry, GST_TYPE_AUDIO_CHANNEL_POSITION);
403   for (n = 0; n < channels; n++) {
404     g_value_init (&pos_val_list, GST_TYPE_LIST);
405     for (c = 0; c < num_positions; c++) {
406       g_value_set_enum (&pos_val_entry, pos[c]);
407       gst_value_list_append_value (&pos_val_list, &pos_val_entry);
408     }
409     gst_value_array_append_value (&pos_val_arr, &pos_val_list);
410     g_value_unset (&pos_val_list);
411   }
412   g_value_unset (&pos_val_entry);
413   gst_structure_set_value (str, GST_AUDIO_CHANNEL_POSITIONS_FIELD_NAME,
414       &pos_val_arr);
415   g_value_unset (&pos_val_arr);
416 }
417
418 /*
419  * Helper function for below. The structure will be conserved,
420  * but might be cut down. Any additional structures that were
421  * created will be stored in the returned caps.
422  */
423
424 static GstCaps *
425 add_list_to_struct (GstStructure * str,
426     const GstAudioChannelPosition * pos, gint num_positions)
427 {
428   GstCaps *caps = gst_caps_new_empty ();
429
430   const GValue *chan_val;
431
432   chan_val = gst_structure_get_value (str, "channels");
433   if (G_VALUE_TYPE (chan_val) == G_TYPE_INT) {
434     gst_audio_set_structure_channel_positions_list (str, pos, num_positions);
435   } else if (G_VALUE_TYPE (chan_val) == GST_TYPE_LIST) {
436     gint size;
437
438     const GValue *sub_val;
439
440     size = gst_value_list_get_size (chan_val);
441     sub_val = gst_value_list_get_value (chan_val, 0);
442     gst_structure_set_value (str, "channels", sub_val);
443     gst_caps_append (caps, add_list_to_struct (str, pos, num_positions));
444     while (--size > 0) {
445       str = gst_structure_copy (str);
446       sub_val = gst_value_list_get_value (chan_val, size);
447       gst_structure_set_value (str, "channels", sub_val);
448       gst_caps_append (caps, add_list_to_struct (str, pos, num_positions));
449       gst_caps_append_structure (caps, str);
450     }
451   } else if (G_VALUE_TYPE (chan_val) == GST_TYPE_INT_RANGE) {
452     gint min, max;
453
454     min = gst_value_get_int_range_min (chan_val);
455     max = gst_value_get_int_range_max (chan_val);
456
457     gst_structure_set (str, "channels", G_TYPE_INT, min, NULL);
458     gst_audio_set_structure_channel_positions_list (str, pos, num_positions);
459     for (++min; min < max; min++) {
460       str = gst_structure_copy (str);
461       gst_structure_set (str, "channels", G_TYPE_INT, min, NULL);
462       gst_audio_set_structure_channel_positions_list (str, pos, num_positions);
463       gst_caps_append_structure (caps, str);
464     }
465   } else {
466     g_warning ("Unexpected value type '%s' for channels field",
467         GST_STR_NULL (g_type_name (G_VALUE_TYPE (chan_val))));
468   }
469
470   return caps;
471 }
472
473 /**
474  * gst_audio_set_caps_channel_positions_list:
475  * @caps: #GstCaps to set the list of channel positions on.
476  * @pos: the array containing one or more possible audio
477  *       channel positions that we should add in each value
478  *       of the array in the given structure.
479  * @num_positions: the number of values in pos.
480  *
481  * Sets a (possibly non-fixed) list of possible audio channel
482  * positions (given in pos) on the given caps. Each of the
483  * structures of the caps, after this function has been called,
484  * will contain a "channel-positions" field with an array.
485  * Each value in the array will contain each of the values given
486  * in the pos array. Note that the size of the caps might be
487  * increased by this, since each structure with a "channel-
488  * positions" field needs to have a fixed "channels" field.
489  * The input caps is not required to have this.
490  */
491
492 void
493 gst_audio_set_caps_channel_positions_list (GstCaps * caps,
494     const GstAudioChannelPosition * pos, gint num_positions)
495 {
496   gint size, n;
497
498   /* get number of channels, general type checkups */
499   g_return_if_fail (caps != NULL);
500   g_return_if_fail (num_positions > 0);
501   g_return_if_fail (pos != NULL);
502
503   size = gst_caps_get_size (caps);
504   for (n = 0; n < size; n++) {
505     gst_caps_append (caps, add_list_to_struct (gst_caps_get_structure (caps,
506                 n), pos, num_positions));
507   }
508 }
509
510 /**
511  * gst_audio_fixate_channel_positions:
512  * @str: a #GstStructure containing a (possibly unfixed)
513  *       "channel-positions" field.
514  *
515  * Custom fixate function. Elements that implement some sort of
516  * channel conversion algorithm should use this function for
517  * fixating on GstAudioChannelPosition properties. It will take
518  * care of equal channel positioning (left/right). Caller g_free()s
519  * the return value. The input properties may be (and are supposed
520  * to be) unfixed.
521  * Note that this function is mostly a hack because we currently
522  * have no way to add default fixation functions for new GTypes.
523  *
524  * Returns: fixed values that the caller could use as a fixed
525  * set of #GstAudioChannelPosition values.
526  */
527
528 GstAudioChannelPosition *
529 gst_audio_fixate_channel_positions (GstStructure * str)
530 {
531   GstAudioChannelPosition *pos;
532
533   gint channels, n, num_unfixed = 0, i, c;
534
535   const GValue *pos_val_arr, *pos_val_entry, *pos_val;
536
537   gboolean res, is_stereo = TRUE;
538
539   GType t;
540
541   /*
542    * We're going to do this cluelessly. We'll make an array of values that
543    * conflict with each other and, for each iteration in this array, pick
544    * either one until all unknown values are filled. This might not work in
545    * corner cases but should work OK for the general case.
546    */
547   const struct
548   {
549     const GstAudioChannelPosition pos1[2];
550     const GstAudioChannelPosition pos2[1];
551   } conf[] = {
552     /* front: mono <-> stereo */
553     {
554       {
555       GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
556             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
557     GST_AUDIO_CHANNEL_POSITION_FRONT_MONO}}, { {
558     GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
559             GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
560     GST_AUDIO_CHANNEL_POSITION_INVALID}}, { {
561     GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
562     GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}}, { {
563     GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
564             GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
565     GST_AUDIO_CHANNEL_POSITION_INVALID}}, { {
566     GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
567     GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}}, { {
568     GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
569     GST_AUDIO_CHANNEL_POSITION_LFE}}, { {
570     GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
571             GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, {
572     GST_AUDIO_CHANNEL_POSITION_INVALID}}, { {
573     GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
574     GST_AUDIO_CHANNEL_POSITION_INVALID}}
575   };
576   struct
577   {
578     gint num_opt[3];
579     guint num_opts[3];
580     gboolean is_fixed[3];
581     gint choice;                /* -1 is none, 0 is the two, 1 is the one */
582   } opt;
583
584   /* get number of channels, general type checkups */
585   g_return_val_if_fail (str != NULL, NULL);
586   res = gst_structure_get_int (str, "channels", &channels);
587   g_return_val_if_fail (res, NULL);
588   g_return_val_if_fail (channels > 0, NULL);
589
590   /* 0.8.x mono/stereo checks */
591   pos_val_arr = gst_structure_get_value (str,
592       GST_AUDIO_CHANNEL_POSITIONS_FIELD_NAME);
593   if (!pos_val_arr && (channels == 1 || channels == 2)) {
594     pos = g_new (GstAudioChannelPosition, channels);
595     if (channels == 1) {
596       pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
597     } else {
598       pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
599       pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
600     }
601     return pos;
602   }
603   g_return_val_if_fail (pos_val_arr != NULL, NULL);
604   g_return_val_if_fail (gst_value_array_get_size (pos_val_arr) == channels,
605       NULL);
606   for (n = 0; n < channels; n++) {
607     t = G_VALUE_TYPE (gst_value_array_get_value (pos_val_arr, n));
608     g_return_val_if_fail (t == GST_TYPE_LIST ||
609         t == GST_TYPE_AUDIO_CHANNEL_POSITION, NULL);
610   }
611
612   /* all unknown, to start with */
613   pos = g_new (GstAudioChannelPosition, channels);
614   for (n = 0; n < channels; n++)
615     pos[n] = GST_AUDIO_CHANNEL_POSITION_INVALID;
616   num_unfixed = channels;
617
618   /* Iterate the array of conflicting values */
619   for (i = 0; conf[i].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID ||
620       conf[i].pos2[0] != GST_AUDIO_CHANNEL_POSITION_INVALID; i++) {
621     /* front/center only important if not mono (obviously) */
622     if (conf[i].pos1[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER &&
623         !is_stereo)
624       continue;
625
626     /* init values */
627     for (n = 0; n < 3; n++) {
628       opt.num_opt[n] = -1;
629       opt.num_opts[n] = -1;
630       opt.is_fixed[n] = FALSE;
631     }
632
633     /* Now, we'll see for each channel if it allows for any of the values in
634      * the set of conflicting audio channel positions and keep scores. */
635     for (n = 0; n < channels; n++) {
636       /* if the channel is already taken, don't bother */
637       if (pos[n] != GST_AUDIO_CHANNEL_POSITION_INVALID)
638         continue;
639
640       pos_val_entry = gst_value_array_get_value (pos_val_arr, n);
641       t = G_VALUE_TYPE (pos_val_entry);
642       if (t == GST_TYPE_LIST) {
643         /* This algorhythm is suboptimal. */
644         for (c = 0; c < gst_value_list_get_size (pos_val_entry); c++) {
645           pos_val = gst_value_list_get_value (pos_val_entry, c);
646           if (g_value_get_enum (pos_val) == conf[i].pos1[0] &&
647               opt.num_opts[0] > gst_value_list_get_size (pos_val_entry) &&
648               !opt.is_fixed[0]) {
649             /* Now test if the old position of num_opt[0] also allows for
650              * the other channel (which was skipped previously). If so,
651              * keep score. */
652             if (opt.num_opt[0] != -1) {
653               gint c1;
654
655               pos_val_entry = gst_value_array_get_value (pos_val_arr,
656                   opt.num_opt[0]);
657               if (G_VALUE_TYPE (pos_val_entry) == GST_TYPE_LIST) {
658                 for (c1 = 0; c1 < gst_value_list_get_size (pos_val_entry); c1++) {
659                   pos_val = gst_value_list_get_value (pos_val_entry, c1);
660                   if (g_value_get_enum (pos_val) == conf[i].pos1[1] &&
661                       opt.num_opts[1] > opt.num_opts[0] && !opt.is_fixed[1]) {
662                     opt.num_opts[1] = opt.num_opts[0];
663                     opt.num_opt[1] = opt.num_opt[0];
664                   }
665                 }
666                 pos_val = gst_value_list_get_value (pos_val_entry, c);
667               }
668               pos_val_entry = gst_value_array_get_value (pos_val_arr, n);
669             }
670
671             /* and save values */
672             opt.num_opts[0] = gst_value_list_get_size (pos_val_entry);
673             opt.num_opt[0] = n;
674           } else if (g_value_get_enum (pos_val) == conf[i].pos1[1] &&
675               opt.num_opts[1] > gst_value_list_get_size (pos_val_entry) &&
676               !opt.is_fixed[1] && n != opt.num_opt[0]) {
677             opt.num_opts[1] = gst_value_list_get_size (pos_val_entry);
678             opt.num_opt[1] = n;
679           }
680
681           /* 2 goes separately, because 0/1 vs. 2 are separate */
682           if (g_value_get_enum (pos_val) == conf[i].pos2[0] &&
683               opt.num_opts[2] > gst_value_list_get_size (pos_val_entry) &&
684               !opt.is_fixed[2]) {
685             opt.num_opts[2] = gst_value_list_get_size (pos_val_entry);
686             opt.num_opt[2] = n;
687           }
688         }
689       } else {
690         if (g_value_get_enum (pos_val_entry) == conf[i].pos1[0]) {
691           opt.num_opt[0] = n;
692           opt.is_fixed[0] = TRUE;
693         } else if (g_value_get_enum (pos_val_entry) == conf[i].pos1[1]) {
694           opt.num_opt[1] = n;
695           opt.is_fixed[1] = TRUE;
696         } else if (g_value_get_enum (pos_val_entry) == conf[i].pos2[0]) {
697           opt.num_opt[2] = n;
698           opt.is_fixed[2] = TRUE;
699         }
700       }
701     }
702
703     /* check our results and choose either one */
704     if ((opt.is_fixed[0] || opt.is_fixed[1]) && opt.is_fixed[2]) {
705       g_warning ("Pre-fixated on both %d/%d and %d - conflict!",
706           conf[i].pos1[0], conf[i].pos1[1], conf[i].pos2[0]);
707       g_free (pos);
708       return NULL;
709     } else if ((opt.is_fixed[0] && opt.num_opt[1] == -1) ||
710         (opt.is_fixed[1] && opt.num_opt[0] == -1)) {
711       g_warning ("Pre-fixated one side, but other side n/a of %d/%d",
712           conf[i].pos1[0], conf[i].pos1[1]);
713       g_free (pos);
714       return NULL;
715     } else if (opt.is_fixed[0] || opt.is_fixed[1]) {
716       opt.choice = 0;
717     } else if (opt.is_fixed[2]) {
718       opt.choice = 1;
719     } else if (opt.num_opt[0] != -1 && opt.num_opt[1] != -1) {
720       opt.choice = 0;
721     } else if (opt.num_opt[2] != -1) {
722       opt.choice = 1;
723     } else {
724       opt.choice = -1;
725     }
726
727     /* stereo? Note that we keep is_stereo to TRUE if we didn't decide on
728      * any arrangement. The mono/stereo channels might be handled elsewhere
729      * which is clearly outside the scope of this element, so we cannot
730      * know and expect the application to handle that then. */
731     if (conf[i].pos2[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_MONO &&
732         opt.choice == 1) {
733       is_stereo = FALSE;
734     }
735
736     /* now actually decide what we'll do and fixate on that */
737     if (opt.choice == 0) {
738       g_assert (conf[i].pos1[0] != GST_AUDIO_CHANNEL_POSITION_INVALID &&
739           conf[i].pos1[1] != GST_AUDIO_CHANNEL_POSITION_INVALID);
740       pos[opt.num_opt[0]] = conf[i].pos1[0];
741       pos[opt.num_opt[1]] = conf[i].pos1[1];
742       num_unfixed -= 2;
743     } else if (opt.choice == 1) {
744       g_assert (conf[i].pos2[0] != GST_AUDIO_CHANNEL_POSITION_INVALID);
745       pos[opt.num_opt[2]] = conf[i].pos2[0];
746       num_unfixed--;
747     }
748   }
749
750   /* safety check */
751   if (num_unfixed > 0) {
752     g_warning ("%d unfixed channel positions left after fixation!",
753         num_unfixed);
754     g_free (pos);
755     return NULL;
756   }
757
758   if (!gst_audio_check_channel_positions (pos, channels)) {
759     g_free (pos);
760     return NULL;
761   }
762
763   return pos;
764 }