22 */ |
22 */ |
23 |
23 |
24 /** |
24 /** |
25 * SECTION:element-audioconvert |
25 * SECTION:element-audioconvert |
26 * |
26 * |
27 * <refsect2> |
|
28 * <para> |
|
29 * Audioconvert converts raw audio buffers between various possible formats. |
27 * Audioconvert converts raw audio buffers between various possible formats. |
30 * It supports integer to float conversion, width/depth conversion, |
28 * It supports integer to float conversion, width/depth conversion, |
31 * signedness and endianness conversion. |
29 * signedness and endianness conversion and channel transformations. |
32 * </para> |
30 * |
33 * <para> |
31 * <refsect2> |
34 * Some format conversion are not carried out in an optimal way right now. |
|
35 * E.g. converting from double to float would cause a loss of precision. |
|
36 * </para> |
|
37 * <title>Example launch line</title> |
32 * <title>Example launch line</title> |
38 * <para> |
33 * |[ |
39 * <programlisting> |
|
40 * gst-launch -v -m audiotestsrc ! audioconvert ! audio/x-raw-int,channels=2,width=8,depth=8 ! level ! fakesink silent=TRUE |
34 * gst-launch -v -m audiotestsrc ! audioconvert ! audio/x-raw-int,channels=2,width=8,depth=8 ! level ! fakesink silent=TRUE |
41 * </programlisting> |
35 * ]| This pipeline converts audio to 8-bit. The level element shows that |
42 * This pipeline converts audio to 8-bit. The level element shows that |
|
43 * the output levels still match the one for a sine wave. |
36 * the output levels still match the one for a sine wave. |
44 * </para> |
37 * |[ |
45 * <para> |
|
46 * <programlisting> |
|
47 * gst-launch -v -m audiotestsrc ! audioconvert ! vorbisenc ! fakesink silent=TRUE |
38 * gst-launch -v -m audiotestsrc ! audioconvert ! vorbisenc ! fakesink silent=TRUE |
48 * </programlisting> |
39 * ]| The vorbis encoder takes float audio data instead of the integer data |
49 * The vorbis encoder takes float audio data instead of the integer data |
|
50 * generated by audiotestsrc. |
40 * generated by audiotestsrc. |
51 * </para> |
|
52 * </refsect2> |
41 * </refsect2> |
53 * |
42 * |
54 * Last reviewed on 2006-03-02 (0.10.4) |
43 * Last reviewed on 2006-03-02 (0.10.4) |
55 */ |
44 */ |
56 |
45 |
133 |
123 |
134 #define STATIC_CAPS \ |
124 #define STATIC_CAPS \ |
135 GST_STATIC_CAPS ( \ |
125 GST_STATIC_CAPS ( \ |
136 "audio/x-raw-float, " \ |
126 "audio/x-raw-float, " \ |
137 "rate = (int) [ 1, MAX ], " \ |
127 "rate = (int) [ 1, MAX ], " \ |
138 "channels = (int) [ 1, 8 ], " \ |
128 "channels = (int) [ 1, MAX ], " \ |
139 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
129 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
140 "width = (int) 64;" \ |
130 "width = (int) 64;" \ |
141 "audio/x-raw-float, " \ |
131 "audio/x-raw-float, " \ |
142 "rate = (int) [ 1, MAX ], " \ |
132 "rate = (int) [ 1, MAX ], " \ |
143 "channels = (int) [ 1, 8 ], " \ |
133 "channels = (int) [ 1, MAX ], " \ |
144 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
134 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
145 "width = (int) 32;" \ |
135 "width = (int) 32;" \ |
146 "audio/x-raw-int, " \ |
136 "audio/x-raw-int, " \ |
147 "rate = (int) [ 1, MAX ], " \ |
137 "rate = (int) [ 1, MAX ], " \ |
148 "channels = (int) [ 1, 8 ], " \ |
138 "channels = (int) [ 1, MAX ], " \ |
149 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
139 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
150 "width = (int) 32, " \ |
140 "width = (int) 32, " \ |
151 "depth = (int) [ 1, 32 ], " \ |
141 "depth = (int) [ 1, 32 ], " \ |
152 "signed = (boolean) { true, false }; " \ |
142 "signed = (boolean) { true, false }; " \ |
153 "audio/x-raw-int, " \ |
143 "audio/x-raw-int, " \ |
154 "rate = (int) [ 1, MAX ], " \ |
144 "rate = (int) [ 1, MAX ], " \ |
155 "channels = (int) [ 1, 8 ], " \ |
145 "channels = (int) [ 1, MAX ], " \ |
156 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
146 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
157 "width = (int) 24, " \ |
147 "width = (int) 24, " \ |
158 "depth = (int) [ 1, 24 ], " "signed = (boolean) { true, false }; " \ |
148 "depth = (int) [ 1, 24 ], " "signed = (boolean) { true, false }; " \ |
159 "audio/x-raw-int, " \ |
149 "audio/x-raw-int, " \ |
160 "rate = (int) [ 1, MAX ], " \ |
150 "rate = (int) [ 1, MAX ], " \ |
161 "channels = (int) [ 1, 8 ], " \ |
151 "channels = (int) [ 1, MAX ], " \ |
162 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
152 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
163 "width = (int) 16, " \ |
153 "width = (int) 16, " \ |
164 "depth = (int) [ 1, 16 ], " \ |
154 "depth = (int) [ 1, 16 ], " \ |
165 "signed = (boolean) { true, false }; " \ |
155 "signed = (boolean) { true, false }; " \ |
166 "audio/x-raw-int, " \ |
156 "audio/x-raw-int, " \ |
167 "rate = (int) [ 1, MAX ], " \ |
157 "rate = (int) [ 1, MAX ], " \ |
168 "channels = (int) [ 1, 8 ], " \ |
158 "channels = (int) [ 1, MAX ], " \ |
169 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
159 "endianness = (int) { LITTLE_ENDIAN, BIG_ENDIAN }, " \ |
170 "width = (int) 8, " \ |
160 "width = (int) 8, " \ |
171 "depth = (int) [ 1, 8 ], " \ |
161 "depth = (int) [ 1, 8 ], " \ |
172 "signed = (boolean) { true, false } " \ |
162 "signed = (boolean) { true, false } " \ |
173 ) |
163 ) |
261 supported_positions[i] = i; |
251 supported_positions[i] = i; |
262 |
252 |
263 g_object_class_install_property (gobject_class, ARG_DITHERING, |
253 g_object_class_install_property (gobject_class, ARG_DITHERING, |
264 g_param_spec_enum ("dithering", "Dithering", |
254 g_param_spec_enum ("dithering", "Dithering", |
265 "Selects between different dithering methods.", |
255 "Selects between different dithering methods.", |
266 GST_TYPE_AUDIO_CONVERT_DITHERING, DITHER_TPDF, G_PARAM_READWRITE)); |
256 GST_TYPE_AUDIO_CONVERT_DITHERING, DITHER_TPDF, |
|
257 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
267 |
258 |
268 g_object_class_install_property (gobject_class, ARG_NOISE_SHAPING, |
259 g_object_class_install_property (gobject_class, ARG_NOISE_SHAPING, |
269 g_param_spec_enum ("noise-shaping", "Noise shaping", |
260 g_param_spec_enum ("noise-shaping", "Noise shaping", |
270 "Selects between different noise shaping methods.", |
261 "Selects between different noise shaping methods.", |
271 GST_TYPE_AUDIO_CONVERT_NOISE_SHAPING, NOISE_SHAPING_NONE, |
262 GST_TYPE_AUDIO_CONVERT_NOISE_SHAPING, NOISE_SHAPING_NONE, |
272 G_PARAM_READWRITE)); |
263 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
273 |
264 |
274 basetransform_class->get_unit_size = |
265 basetransform_class->get_unit_size = |
275 GST_DEBUG_FUNCPTR (gst_audio_convert_get_unit_size); |
266 GST_DEBUG_FUNCPTR (gst_audio_convert_get_unit_size); |
276 basetransform_class->transform_caps = |
267 basetransform_class->transform_caps = |
277 GST_DEBUG_FUNCPTR (gst_audio_convert_transform_caps); |
268 GST_DEBUG_FUNCPTR (gst_audio_convert_transform_caps); |
525 s2 = gst_structure_copy (s); |
522 s2 = gst_structure_copy (s); |
526 gst_structure_set_name (s2, "audio/x-raw-float"); |
523 gst_structure_set_name (s2, "audio/x-raw-float"); |
527 s = make_lossless_changes (s2, TRUE); |
524 s = make_lossless_changes (s2, TRUE); |
528 gst_caps_append_structure (caps, s2); |
525 gst_caps_append_structure (caps, s2); |
529 } |
526 } |
|
527 } |
|
528 |
|
529 static gboolean |
|
530 structure_has_fixed_channel_positions (GstStructure * s, |
|
531 gboolean * unpositioned_layout) |
|
532 { |
|
533 GstAudioChannelPosition *pos; |
|
534 const GValue *val; |
|
535 gint channels = 0; |
|
536 |
|
537 if (!gst_structure_get_int (s, "channels", &channels)) |
|
538 return FALSE; /* probably a range */ |
|
539 |
|
540 val = gst_structure_get_value (s, "channel-positions"); |
|
541 if ((val == NULL || !gst_value_is_fixed (val)) && channels <= 8) { |
|
542 GST_LOG ("no or unfixed channel-positions in %" GST_PTR_FORMAT, s); |
|
543 return FALSE; |
|
544 } else if (val == NULL || !gst_value_is_fixed (val)) { |
|
545 GST_LOG ("implicit undefined channel-positions"); |
|
546 *unpositioned_layout = TRUE; |
|
547 return TRUE; |
|
548 } |
|
549 |
|
550 pos = gst_audio_get_channel_positions (s); |
|
551 if (pos && pos[0] == GST_AUDIO_CHANNEL_POSITION_NONE) { |
|
552 GST_LOG ("fixed undefined channel-positions in %" GST_PTR_FORMAT, s); |
|
553 *unpositioned_layout = TRUE; |
|
554 } else { |
|
555 GST_LOG ("fixed defined channel-positions in %" GST_PTR_FORMAT, s); |
|
556 *unpositioned_layout = FALSE; |
|
557 } |
|
558 g_free (pos); |
|
559 |
|
560 return TRUE; |
530 } |
561 } |
531 |
562 |
532 /* Audioconvert can perform all conversions on audio except for resampling. |
563 /* Audioconvert can perform all conversions on audio except for resampling. |
533 * However, there are some conversions we _prefer_ not to do. For example, it's |
564 * However, there are some conversions we _prefer_ not to do. For example, it's |
534 * better to convert format (float<->int, endianness, etc) than the number of |
565 * better to convert format (float<->int, endianness, etc) than the number of |
601 else |
632 else |
602 gst_structure_set (s, "depth", GST_TYPE_INT_RANGE, depth, 32, NULL); |
633 gst_structure_set (s, "depth", GST_TYPE_INT_RANGE, depth, 32, NULL); |
603 } |
634 } |
604 } |
635 } |
605 |
636 |
|
637 allow_mixing = TRUE; |
606 if (gst_structure_get_int (structure, "channels", &channels)) { |
638 if (gst_structure_get_int (structure, "channels", &channels)) { |
607 if (channels == 8) |
639 gboolean unpositioned; |
608 gst_structure_set (s, "channels", G_TYPE_INT, 8, NULL); |
640 |
|
641 /* we don't support mixing for channels without channel positions */ |
|
642 if (structure_has_fixed_channel_positions (structure, &unpositioned)) |
|
643 allow_mixing = (unpositioned == FALSE); |
|
644 } |
|
645 |
|
646 if (!allow_mixing) { |
|
647 gst_structure_set (s, "channels", G_TYPE_INT, channels, NULL); |
|
648 if (gst_structure_has_field (structure, "channel-positions")) |
|
649 gst_structure_set_value (s, "channel-positions", |
|
650 gst_structure_get_value (structure, "channel-positions")); |
|
651 } else { |
|
652 if (channels == 0) |
|
653 gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, 11, NULL); |
|
654 else if (channels == 11) |
|
655 gst_structure_set (s, "channels", G_TYPE_INT, 11, NULL); |
609 else |
656 else |
610 gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, channels, 8, NULL); |
657 gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, channels, 11, NULL); |
|
658 gst_structure_remove_field (s, "channel-positions"); |
611 } |
659 } |
612 gst_caps_append_structure (ret, s); |
660 gst_caps_append_structure (ret, s); |
613 |
661 |
614 /* Same, plus a float<->int conversion */ |
662 /* Same, plus a float<->int conversion */ |
615 append_with_other_format (ret, s, isfloat); |
663 append_with_other_format (ret, s, isfloat); |
634 |
682 |
635 /* Channel conversions to fewer channels is only done if needed - generally |
683 /* Channel conversions to fewer channels is only done if needed - generally |
636 * it's very bad to drop channels entirely. |
684 * it's very bad to drop channels entirely. |
637 */ |
685 */ |
638 s = gst_structure_copy (s); |
686 s = gst_structure_copy (s); |
639 gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, 8, NULL); |
687 if (allow_mixing) { |
|
688 gst_structure_set (s, "channels", GST_TYPE_INT_RANGE, 1, 11, NULL); |
|
689 gst_structure_remove_field (s, "channel-positions"); |
|
690 } else { |
|
691 /* allow_mixing can only be FALSE if we got a fixed number of channels */ |
|
692 gst_structure_set (s, "channels", G_TYPE_INT, channels, NULL); |
|
693 if (gst_structure_has_field (structure, "channel-positions")) |
|
694 gst_structure_set_value (s, "channel-positions", |
|
695 gst_structure_get_value (structure, "channel-positions")); |
|
696 } |
640 gst_caps_append_structure (ret, s); |
697 gst_caps_append_structure (ret, s); |
641 |
698 |
642 /* Same, plus a float<->int conversion */ |
699 /* Same, plus a float<->int conversion */ |
643 append_with_other_format (ret, s, isfloat); |
700 append_with_other_format (ret, s, isfloat); |
644 |
701 |
774 } |
831 } |
775 |
832 |
776 /* check if the output has a channel layout (or a list of layouts) */ |
833 /* check if the output has a channel layout (or a list of layouts) */ |
777 out_layout = gst_structure_get_value (outs, "channel-positions"); |
834 out_layout = gst_structure_get_value (outs, "channel-positions"); |
778 |
835 |
|
836 /* get the channel layout of the input if any */ |
|
837 in_layout = gst_structure_get_value (ins, "channel-positions"); |
|
838 |
779 if (out_layout == NULL) { |
839 if (out_layout == NULL) { |
780 if (out_chans <= 2) |
840 if (out_chans <= 2 && (in_chans != out_chans || in_layout == NULL)) |
781 return; /* nothing to do, default layout will be assumed */ |
841 return; /* nothing to do, default layout will be assumed */ |
782 GST_WARNING_OBJECT (base, "downstream caps contain no channel layout"); |
842 GST_WARNING_OBJECT (base, "downstream caps contain no channel layout"); |
783 } |
843 } |
784 |
844 |
785 if (in_chans == out_chans) { |
845 if (in_chans == out_chans && in_layout != NULL) { |
786 const GValue *in_layout; |
|
787 GValue res = { 0, }; |
846 GValue res = { 0, }; |
788 |
|
789 in_layout = gst_structure_get_value (ins, "channel-positions"); |
|
790 g_return_if_fail (in_layout != NULL); |
|
791 |
847 |
792 /* same number of channels and no output layout: just use input layout */ |
848 /* same number of channels and no output layout: just use input layout */ |
793 if (out_layout == NULL) { |
849 if (out_layout == NULL) { |
794 gst_structure_set_value (outs, "channel-positions", in_layout); |
850 gst_structure_set_value (outs, "channel-positions", in_layout); |
795 return; |
851 return; |
942 static GstFlowReturn |
998 static GstFlowReturn |
943 gst_audio_convert_transform_ip (GstBaseTransform * base, GstBuffer * buf) |
999 gst_audio_convert_transform_ip (GstBaseTransform * base, GstBuffer * buf) |
944 { |
1000 { |
945 /* nothing to do here */ |
1001 /* nothing to do here */ |
946 return GST_FLOW_OK; |
1002 return GST_FLOW_OK; |
|
1003 } |
|
1004 |
|
1005 static void |
|
1006 gst_audio_convert_create_silence_buffer (GstAudioConvert * this, gpointer dst, |
|
1007 gint size) |
|
1008 { |
|
1009 if (this->ctx.out.is_int && !this->ctx.out.sign) { |
|
1010 gint i; |
|
1011 |
|
1012 switch (this->ctx.out.width) { |
|
1013 case 8:{ |
|
1014 guint8 zero = 0x80 >> (8 - this->ctx.out.depth); |
|
1015 |
|
1016 memset (dst, zero, size); |
|
1017 break; |
|
1018 } |
|
1019 case 16:{ |
|
1020 guint16 *data = (guint16 *) dst; |
|
1021 guint16 zero = 0x8000 >> (16 - this->ctx.out.depth); |
|
1022 |
|
1023 if (this->ctx.out.endianness == G_LITTLE_ENDIAN) |
|
1024 zero = GUINT16_TO_LE (zero); |
|
1025 else |
|
1026 zero = GUINT16_TO_BE (zero); |
|
1027 |
|
1028 size /= 2; |
|
1029 |
|
1030 for (i = 0; i < size; i++) |
|
1031 data[i] = zero; |
|
1032 break; |
|
1033 } |
|
1034 case 24:{ |
|
1035 guint32 zero = 0x800000 >> (24 - this->ctx.out.depth); |
|
1036 guint8 *data = (guint8 *) dst; |
|
1037 |
|
1038 if (this->ctx.out.endianness == G_LITTLE_ENDIAN) { |
|
1039 for (i = 0; i < size; i += 3) { |
|
1040 data[i] = zero & 0xff; |
|
1041 data[i + 1] = (zero >> 8) & 0xff; |
|
1042 data[i + 2] = (zero >> 16) & 0xff; |
|
1043 } |
|
1044 } else { |
|
1045 for (i = 0; i < size; i += 3) { |
|
1046 data[i + 2] = zero & 0xff; |
|
1047 data[i + 1] = (zero >> 8) & 0xff; |
|
1048 data[i] = (zero >> 16) & 0xff; |
|
1049 } |
|
1050 } |
|
1051 break; |
|
1052 } |
|
1053 case 32:{ |
|
1054 guint32 *data = (guint32 *) dst; |
|
1055 guint32 zero = (0x80000000 >> (32 - this->ctx.out.depth)); |
|
1056 |
|
1057 if (this->ctx.out.endianness == G_LITTLE_ENDIAN) |
|
1058 zero = GUINT32_TO_LE (zero); |
|
1059 else |
|
1060 zero = GUINT32_TO_BE (zero); |
|
1061 |
|
1062 size /= 4; |
|
1063 |
|
1064 for (i = 0; i < size; i++) |
|
1065 data[i] = zero; |
|
1066 break; |
|
1067 } |
|
1068 default: |
|
1069 memset (dst, 0, size); |
|
1070 g_return_if_reached (); |
|
1071 break; |
|
1072 } |
|
1073 } else { |
|
1074 memset (dst, 0, size); |
|
1075 } |
947 } |
1076 } |
948 |
1077 |
949 static GstFlowReturn |
1078 static GstFlowReturn |
950 gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf, |
1079 gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf, |
951 GstBuffer * outbuf) |
1080 GstBuffer * outbuf) |