/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/gstdsd.c
Line | Count | Source |
1 | | /* GStreamer |
2 | | * Copyright (C) 2023 Carlos Rafael Giani <crg7475@mailbox.org> |
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., 51 Franklin St, Fifth Floor, |
17 | | * Boston, MA 02110-1301, USA. |
18 | | */ |
19 | | |
20 | | #ifdef HAVE_CONFIG_H |
21 | | # include "config.h" |
22 | | #endif |
23 | | |
24 | | #include <string.h> |
25 | | #include "gstdsd.h" |
26 | | |
27 | | #ifndef GST_DISABLE_GST_DEBUG |
28 | | #define GST_CAT_DEFAULT ensure_debug_category() |
29 | | static GstDebugCategory * |
30 | | ensure_debug_category (void) |
31 | 0 | { |
32 | 0 | static gsize cat_gonce = 0; |
33 | |
|
34 | 0 | if (g_once_init_enter (&cat_gonce)) { |
35 | 0 | gsize cat_done; |
36 | |
|
37 | 0 | cat_done = (gsize) _gst_debug_category_new ("gst-dsd", 0, "GStreamer DSD"); |
38 | |
|
39 | 0 | g_once_init_leave (&cat_gonce, cat_done); |
40 | 0 | } |
41 | |
|
42 | 0 | return (GstDebugCategory *) cat_gonce; |
43 | 0 | } |
44 | | #else |
45 | | #define ensure_debug_category() /* NOOP */ |
46 | | #endif /* GST_DISABLE_GST_DEBUG */ |
47 | | |
48 | | static const guint8 byte_bit_reversal_table[256] = { |
49 | | 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, |
50 | | 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, |
51 | | 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, |
52 | | 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, |
53 | | 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, |
54 | | 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, |
55 | | 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, |
56 | | 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, |
57 | | 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, |
58 | | 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, |
59 | | 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, |
60 | | 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, |
61 | | 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, |
62 | | 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, |
63 | | 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, |
64 | | 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, |
65 | | 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, |
66 | | 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, |
67 | | 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, |
68 | | 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, |
69 | | 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, |
70 | | 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, |
71 | | 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, |
72 | | 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, |
73 | | 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, |
74 | | 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, |
75 | | 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, |
76 | | 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, |
77 | | 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, |
78 | | 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, |
79 | | 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, |
80 | | 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, |
81 | | }; |
82 | | |
83 | | static const char * |
84 | | layout_to_string (GstAudioLayout layout) |
85 | 0 | { |
86 | 0 | const char *layout_str = NULL; |
87 | |
|
88 | 0 | switch (layout) { |
89 | 0 | case GST_AUDIO_LAYOUT_INTERLEAVED: |
90 | 0 | layout_str = "interleaved"; |
91 | 0 | break; |
92 | 0 | case GST_AUDIO_LAYOUT_NON_INTERLEAVED: |
93 | 0 | layout_str = "non-interleaved"; |
94 | 0 | break; |
95 | 0 | default: |
96 | 0 | g_return_val_if_reached (NULL); |
97 | 0 | } |
98 | | |
99 | 0 | return layout_str; |
100 | 0 | } |
101 | | |
102 | | static gboolean |
103 | | gst_dsd_plane_offset_meta_init (GstMeta * meta, gpointer params, |
104 | | GstBuffer * buffer) |
105 | 0 | { |
106 | 0 | GstDsdPlaneOffsetMeta *ofs_meta = (GstDsdPlaneOffsetMeta *) meta; |
107 | 0 | ofs_meta->offsets = NULL; |
108 | |
|
109 | 0 | return TRUE; |
110 | 0 | } |
111 | | |
112 | | static void |
113 | | gst_dsd_plane_offset_meta_free (GstMeta * meta, GstBuffer * buffer) |
114 | 0 | { |
115 | 0 | GstDsdPlaneOffsetMeta *ofs_meta = (GstDsdPlaneOffsetMeta *) meta; |
116 | |
|
117 | 0 | if (ofs_meta->offsets && ofs_meta->offsets != ofs_meta->priv_offsets_arr) |
118 | 0 | g_free (ofs_meta->offsets); |
119 | 0 | } |
120 | | |
121 | | static gboolean |
122 | | gst_dsd_plane_offset_meta_transform (GstBuffer * dest, GstMeta * meta, |
123 | | GstBuffer * buffer, GQuark type, gpointer data) |
124 | 0 | { |
125 | 0 | GstDsdPlaneOffsetMeta *smeta, *dmeta; |
126 | |
|
127 | 0 | smeta = (GstDsdPlaneOffsetMeta *) meta; |
128 | |
|
129 | 0 | if (GST_META_TRANSFORM_IS_COPY (type)) { |
130 | 0 | dmeta = gst_buffer_add_dsd_plane_offset_meta (dest, smeta->num_channels, |
131 | 0 | smeta->num_bytes_per_channel, smeta->offsets); |
132 | 0 | if (!dmeta) |
133 | 0 | return FALSE; |
134 | 0 | } else { |
135 | | /* return FALSE, if transform type is not supported */ |
136 | 0 | return FALSE; |
137 | 0 | } |
138 | | |
139 | 0 | return TRUE; |
140 | 0 | } |
141 | | |
142 | | GType |
143 | | gst_dsd_plane_offset_meta_api_get_type (void) |
144 | 0 | { |
145 | 0 | static GType type; |
146 | 0 | static const gchar *tags[] = { |
147 | 0 | GST_META_TAG_AUDIO_STR, |
148 | 0 | GST_META_TAG_DSD_PLANE_OFFSETS_STR, |
149 | 0 | NULL |
150 | 0 | }; |
151 | |
|
152 | 0 | if (g_once_init_enter (&type)) { |
153 | 0 | GType _type = gst_meta_api_type_register ("GstDsdPlaneOffsetMetaAPI", tags); |
154 | 0 | g_once_init_leave (&type, _type); |
155 | 0 | } |
156 | 0 | return type; |
157 | 0 | } |
158 | | |
159 | | const GstMetaInfo * |
160 | | gst_dsd_plane_offset_meta_get_info (void) |
161 | 0 | { |
162 | 0 | static const GstMetaInfo *dsd_plane_offset_meta_info = NULL; |
163 | |
|
164 | 0 | if (g_once_init_enter ((GstMetaInfo **) & dsd_plane_offset_meta_info)) { |
165 | 0 | const GstMetaInfo *meta = |
166 | 0 | gst_meta_register (GST_DSD_PLANE_OFFSET_META_API_TYPE, |
167 | 0 | "GstDsdPlaneOffsetMeta", |
168 | 0 | sizeof (GstDsdPlaneOffsetMeta), |
169 | 0 | gst_dsd_plane_offset_meta_init, |
170 | 0 | gst_dsd_plane_offset_meta_free, |
171 | 0 | gst_dsd_plane_offset_meta_transform); |
172 | 0 | g_once_init_leave ((GstMetaInfo **) & dsd_plane_offset_meta_info, |
173 | 0 | (GstMetaInfo *) meta); |
174 | 0 | } |
175 | 0 | return dsd_plane_offset_meta_info; |
176 | 0 | } |
177 | | |
178 | | /** |
179 | | * gst_buffer_add_dsd_plane_offset_meta: |
180 | | * @buffer: a #GstBuffer |
181 | | * @num_channels: Number of channels in the DSD data |
182 | | * @num_bytes_per_channel: Number of bytes per channel |
183 | | * @offsets: (array length=num_channels) (nullable): the offsets (in bytes) where each channel plane starts |
184 | | * in the buffer |
185 | | * |
186 | | * Allocates and attaches a #GstDsdPlaneOffsetMeta on @buffer, which must be |
187 | | * writable for that purpose. The fields of the #GstDsdPlaneOffsetMeta are |
188 | | * directly populated from the arguments of this function. |
189 | | * |
190 | | * If @offsets is NULL, then the meta's offsets field is left uninitialized. |
191 | | * This is useful if for example offset values are to be calculated in the |
192 | | * meta's offsets field in-place. Similarly, @num_bytes_per_channel can be |
193 | | * set to 0, but only if @offsets is NULL. This is useful if the number of |
194 | | * bytes per channel is known only later. |
195 | | * |
196 | | * It is not allowed for channels to overlap in memory, |
197 | | * i.e. for each i in [0, channels), the range |
198 | | * [@offsets[i], @offsets[i] + @num_bytes_per_channel) must not overlap |
199 | | * with any other such range. This function will assert if the parameters |
200 | | * specified cause this restriction to be violated. |
201 | | * |
202 | | * It is, obviously, also not allowed to specify parameters that would cause |
203 | | * out-of-bounds memory access on @buffer. This is also checked, which means |
204 | | * that you must add enough memory on the @buffer before adding this meta. |
205 | | * |
206 | | * This meta is only needed for non-interleaved (= planar) DSD data. |
207 | | * |
208 | | * Returns: (transfer none): the #GstDsdPlaneOffsetMeta that was attached |
209 | | * on the @buffer |
210 | | * |
211 | | * Since: 1.24 |
212 | | */ |
213 | | GstDsdPlaneOffsetMeta * |
214 | | gst_buffer_add_dsd_plane_offset_meta (GstBuffer * buffer, gint num_channels, |
215 | | gsize num_bytes_per_channel, gsize offsets[]) |
216 | 0 | { |
217 | 0 | GstDsdPlaneOffsetMeta *meta; |
218 | 0 | gint i; |
219 | 0 | #ifndef G_DISABLE_CHECKS |
220 | 0 | gsize max_offset = 0; |
221 | 0 | gint j; |
222 | 0 | #endif |
223 | |
|
224 | 0 | g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); |
225 | 0 | g_return_val_if_fail (num_channels >= 1, NULL); |
226 | 0 | g_return_val_if_fail (!offsets || (num_bytes_per_channel >= 1), NULL); |
227 | | |
228 | 0 | meta = (GstDsdPlaneOffsetMeta *) gst_buffer_add_meta (buffer, |
229 | 0 | GST_DSD_PLANE_OFFSET_META_INFO, NULL); |
230 | |
|
231 | 0 | meta->num_channels = num_channels; |
232 | 0 | meta->num_bytes_per_channel = num_bytes_per_channel; |
233 | |
|
234 | 0 | if (G_UNLIKELY (num_channels > 8)) |
235 | 0 | meta->offsets = g_new (gsize, num_channels); |
236 | 0 | else |
237 | 0 | meta->offsets = meta->priv_offsets_arr; |
238 | |
|
239 | 0 | if (offsets) { |
240 | 0 | for (i = 0; i < num_channels; i++) { |
241 | 0 | meta->offsets[i] = offsets[i]; |
242 | 0 | #ifndef G_DISABLE_CHECKS |
243 | 0 | max_offset = MAX (max_offset, offsets[i]); |
244 | 0 | for (j = 0; j < num_channels; j++) { |
245 | 0 | if (i != j && !(offsets[j] + num_bytes_per_channel <= offsets[i] |
246 | 0 | || offsets[i] + num_bytes_per_channel <= offsets[j])) { |
247 | 0 | g_critical ("GstDsdPlaneOffsetMeta properties would cause channel " |
248 | 0 | "memory areas to overlap! offsets: %" G_GSIZE_FORMAT " (%d), %" |
249 | 0 | G_GSIZE_FORMAT " (%d) with %" G_GSIZE_FORMAT " bytes per channel", |
250 | 0 | offsets[i], i, offsets[j], j, num_bytes_per_channel); |
251 | 0 | gst_buffer_remove_meta (buffer, (GstMeta *) meta); |
252 | 0 | return NULL; |
253 | 0 | } |
254 | 0 | } |
255 | 0 | #endif |
256 | 0 | } |
257 | | |
258 | 0 | #ifndef G_DISABLE_CHECKS |
259 | 0 | if (max_offset + num_bytes_per_channel > gst_buffer_get_size (buffer)) { |
260 | 0 | g_critical ("GstDsdPlaneOffsetMeta properties would cause " |
261 | 0 | "out-of-bounds memory access on the buffer: max_offset %" |
262 | 0 | G_GSIZE_FORMAT ", %" G_GSIZE_FORMAT " bytes per channel, " |
263 | 0 | "buffer size %" G_GSIZE_FORMAT, max_offset, num_bytes_per_channel, |
264 | 0 | gst_buffer_get_size (buffer)); |
265 | 0 | gst_buffer_remove_meta (buffer, (GstMeta *) meta); |
266 | 0 | return NULL; |
267 | 0 | } |
268 | 0 | #endif |
269 | 0 | } |
270 | | |
271 | 0 | return meta; |
272 | 0 | } |
273 | | |
274 | 0 | G_DEFINE_BOXED_TYPE (GstDsdInfo, gst_dsd_info, |
275 | 0 | (GBoxedCopyFunc) gst_dsd_info_copy, (GBoxedFreeFunc) gst_dsd_info_free); |
276 | 0 |
|
277 | 0 | /** |
278 | 0 | * gst_dsd_info_new: |
279 | 0 | * |
280 | 0 | * Allocate a new #GstDsdInfo that is also initialized with |
281 | 0 | * gst_dsd_info_init(). |
282 | 0 | * |
283 | 0 | * Returns: a new #GstDsdInfo. free with gst_dsd_info_free(). |
284 | 0 | * |
285 | 0 | * Since: 1.24 |
286 | 0 | */ |
287 | 0 | GstDsdInfo * |
288 | 0 | gst_dsd_info_new (void) |
289 | 0 | { |
290 | 0 | GstDsdInfo *info; |
291 | |
|
292 | 0 | info = g_slice_new (GstDsdInfo); |
293 | 0 | gst_dsd_info_init (info); |
294 | |
|
295 | 0 | return info; |
296 | 0 | } |
297 | | |
298 | | /** |
299 | | * gst_dsd_info_new_from_caps: |
300 | | * @caps: a #GstCaps |
301 | | * |
302 | | * Parse @caps to generate a #GstDsdInfo. |
303 | | * |
304 | | * Returns: A #GstDsdInfo, or %NULL if @caps couldn't be parsed |
305 | | * |
306 | | * Since: 1.24 |
307 | | */ |
308 | | GstDsdInfo * |
309 | | gst_dsd_info_new_from_caps (const GstCaps * caps) |
310 | 0 | { |
311 | 0 | GstDsdInfo *ret; |
312 | |
|
313 | 0 | g_return_val_if_fail (caps != NULL, NULL); |
314 | | |
315 | 0 | ret = gst_dsd_info_new (); |
316 | |
|
317 | 0 | if (gst_dsd_info_from_caps (ret, caps)) { |
318 | 0 | return ret; |
319 | 0 | } else { |
320 | 0 | gst_dsd_info_free (ret); |
321 | 0 | return NULL; |
322 | 0 | } |
323 | 0 | } |
324 | | |
325 | | /** |
326 | | * gst_dsd_info_init: |
327 | | * @info: (out caller-allocates): a #GstDsdInfo |
328 | | * |
329 | | * Initialize @info with default values. |
330 | | * |
331 | | * Since: 1.24 |
332 | | */ |
333 | | void |
334 | | gst_dsd_info_init (GstDsdInfo * info) |
335 | 0 | { |
336 | 0 | g_return_if_fail (info != NULL); |
337 | | |
338 | 0 | memset (info, 0, sizeof (GstDsdInfo)); |
339 | 0 | info->format = GST_DSD_FORMAT_UNKNOWN; |
340 | 0 | } |
341 | | |
342 | | /** |
343 | | * gst_dsd_info_set_format: |
344 | | * @info: a #GstDsdInfo |
345 | | * @format: the format |
346 | | * @rate: the DSD rate |
347 | | * @channels: the number of channels |
348 | | * @positions: (array fixed-size=64) (nullable): the channel positions |
349 | | * |
350 | | * Set the default info for the DSD info of @format and @rate and @channels. |
351 | | * |
352 | | * Note: This initializes @info first, no values are preserved. |
353 | | * |
354 | | * Since: 1.24 |
355 | | */ |
356 | | void |
357 | | gst_dsd_info_set_format (GstDsdInfo * info, GstDsdFormat format, |
358 | | gint rate, gint channels, const GstAudioChannelPosition * positions) |
359 | 0 | { |
360 | 0 | gint i; |
361 | |
|
362 | 0 | g_return_if_fail (info != NULL); |
363 | 0 | g_return_if_fail (format != GST_DSD_FORMAT_UNKNOWN); |
364 | 0 | g_return_if_fail (channels <= 64 || positions == NULL); |
365 | | |
366 | 0 | gst_dsd_info_init (info); |
367 | |
|
368 | 0 | info->format = format; |
369 | 0 | info->rate = rate; |
370 | 0 | info->channels = channels; |
371 | 0 | info->layout = GST_AUDIO_LAYOUT_INTERLEAVED; |
372 | 0 | info->flags = GST_AUDIO_FLAG_NONE; |
373 | |
|
374 | 0 | memset (&info->positions, 0xff, sizeof (info->positions)); |
375 | |
|
376 | 0 | if (!positions && channels == 1) { |
377 | 0 | info->positions[0] = GST_AUDIO_CHANNEL_POSITION_MONO; |
378 | 0 | return; |
379 | 0 | } else if (!positions && channels == 2) { |
380 | 0 | info->positions[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; |
381 | 0 | info->positions[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; |
382 | 0 | return; |
383 | 0 | } else { |
384 | 0 | if (!positions |
385 | 0 | || !gst_audio_check_valid_channel_positions (positions, channels, |
386 | 0 | TRUE)) { |
387 | 0 | if (positions) |
388 | 0 | g_warning ("Invalid channel positions"); |
389 | 0 | } else { |
390 | 0 | memcpy (&info->positions, positions, |
391 | 0 | info->channels * sizeof (info->positions[0])); |
392 | 0 | if (info->positions[0] == GST_AUDIO_CHANNEL_POSITION_NONE) |
393 | 0 | info->flags |= GST_AUDIO_FLAG_UNPOSITIONED; |
394 | 0 | return; |
395 | 0 | } |
396 | 0 | } |
397 | | |
398 | | /* Otherwise a NONE layout */ |
399 | 0 | info->flags |= GST_AUDIO_FLAG_UNPOSITIONED; |
400 | 0 | for (i = 0; i < MIN (64, channels); i++) |
401 | 0 | info->positions[i] = GST_AUDIO_CHANNEL_POSITION_NONE; |
402 | 0 | } |
403 | | |
404 | | /** |
405 | | * gst_dsd_info_copy: |
406 | | * @info: a #GstDsdInfo |
407 | | * |
408 | | * Copy a GstDsdInfo structure. |
409 | | * |
410 | | * Returns: a new #GstDsdInfo. free with gst_dsd_info_free. |
411 | | * |
412 | | * Since: 1.24 |
413 | | */ |
414 | | GstDsdInfo * |
415 | | gst_dsd_info_copy (const GstDsdInfo * info) |
416 | 0 | { |
417 | 0 | return g_slice_dup (GstDsdInfo, info); |
418 | 0 | } |
419 | | |
420 | | /** |
421 | | * gst_dsd_info_free: |
422 | | * @info: a #GstDsdInfo |
423 | | * |
424 | | * Free a GstDsdInfo structure previously allocated with gst_dsd_info_new() |
425 | | * or gst_dsd_info_copy(). |
426 | | * |
427 | | * Since: 1.24 |
428 | | */ |
429 | | void |
430 | | gst_dsd_info_free (GstDsdInfo * info) |
431 | 0 | { |
432 | 0 | g_slice_free (GstDsdInfo, info); |
433 | 0 | } |
434 | | |
435 | | /** |
436 | | * gst_dsd_info_from_caps: |
437 | | * @info: (out caller-allocates): a #GstDsdInfo |
438 | | * @caps: a #GstCaps |
439 | | * |
440 | | * Parse @caps and update @info. |
441 | | * |
442 | | * Returns: TRUE if @caps could be parsed |
443 | | * |
444 | | * Since: 1.24 |
445 | | */ |
446 | | gboolean |
447 | | gst_dsd_info_from_caps (GstDsdInfo * info, const GstCaps * caps) |
448 | 0 | { |
449 | 0 | GstStructure *fmt_structure; |
450 | 0 | const gchar *media_type GST_UNUSED_CHECKS; |
451 | 0 | const gchar *format_str = NULL; |
452 | 0 | const gchar *layout_str = NULL; |
453 | 0 | gboolean reversed_bytes = FALSE; |
454 | 0 | GstAudioFlags flags = GST_AUDIO_FLAG_NONE; |
455 | |
|
456 | 0 | guint64 channel_mask = 0; |
457 | |
|
458 | 0 | g_return_val_if_fail (info != NULL, FALSE); |
459 | 0 | g_return_val_if_fail (caps != NULL, FALSE); |
460 | 0 | g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); |
461 | | |
462 | 0 | fmt_structure = gst_caps_get_structure (caps, 0); |
463 | 0 | #ifndef G_DISABLE_CHECKS |
464 | 0 | media_type = gst_structure_get_name (fmt_structure); |
465 | 0 | g_return_val_if_fail (g_strcmp0 (media_type, GST_DSD_MEDIA_TYPE) == 0, FALSE); |
466 | 0 | #endif |
467 | | |
468 | | /* Parse the format */ |
469 | | |
470 | 0 | format_str = gst_structure_get_string (fmt_structure, "format"); |
471 | 0 | if (format_str == NULL) { |
472 | 0 | GST_ERROR ("caps have no format field; caps: %" GST_PTR_FORMAT, caps); |
473 | 0 | goto error; |
474 | 0 | } |
475 | | |
476 | 0 | info->format = gst_dsd_format_from_string (format_str); |
477 | 0 | if (info->format == GST_DSD_FORMAT_UNKNOWN) { |
478 | 0 | GST_ERROR ("caps have unsupported/invalid format field; caps: %" |
479 | 0 | GST_PTR_FORMAT, caps); |
480 | 0 | goto error; |
481 | 0 | } |
482 | | |
483 | | /* Parse the rate */ |
484 | | |
485 | 0 | if (!gst_structure_get_int (fmt_structure, "rate", &(info->rate))) { |
486 | 0 | GST_ERROR ("caps have no rate field; caps: %" GST_PTR_FORMAT, caps); |
487 | 0 | goto error; |
488 | 0 | } |
489 | | |
490 | 0 | if (info->rate < 1) { |
491 | 0 | GST_ERROR ("caps have invalid rate field; caps: %" GST_PTR_FORMAT, caps); |
492 | 0 | goto error; |
493 | 0 | } |
494 | | |
495 | | /* Parse the channels and the channel mask */ |
496 | | |
497 | 0 | if (!gst_structure_get_int (fmt_structure, "channels", &(info->channels))) { |
498 | 0 | GST_ERROR ("caps have no channels field; caps: %" GST_PTR_FORMAT, caps); |
499 | 0 | goto error; |
500 | 0 | } |
501 | | |
502 | 0 | if (info->channels < 1) { |
503 | 0 | GST_ERROR ("caps have invalid channels field; caps: %" GST_PTR_FORMAT, |
504 | 0 | caps); |
505 | 0 | goto error; |
506 | 0 | } |
507 | | |
508 | 0 | if (!gst_structure_get (fmt_structure, "channel-mask", GST_TYPE_BITMASK, |
509 | 0 | &channel_mask, NULL) || (channel_mask == 0 && info->channels == 1) |
510 | 0 | ) { |
511 | 0 | switch (info->channels) { |
512 | 0 | case 1: |
513 | 0 | info->positions[0] = GST_AUDIO_CHANNEL_POSITION_MONO; |
514 | 0 | break; |
515 | | |
516 | 0 | case 2: |
517 | 0 | info->positions[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; |
518 | 0 | info->positions[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; |
519 | 0 | break; |
520 | | |
521 | 0 | default: |
522 | 0 | GST_ERROR |
523 | 0 | ("caps indicate multichannel DSD data but they do not contain channel-mask field; caps: %" |
524 | 0 | GST_PTR_FORMAT, caps); |
525 | 0 | goto error; |
526 | 0 | } |
527 | 0 | } else if (channel_mask == 0) { |
528 | 0 | gint i; |
529 | 0 | flags |= GST_AUDIO_FLAG_UNPOSITIONED; |
530 | 0 | for (i = 0; i < MIN (64, info->channels); i++) |
531 | 0 | info->positions[i] = GST_AUDIO_CHANNEL_POSITION_NONE; |
532 | 0 | } else { |
533 | 0 | if (!gst_audio_channel_positions_from_mask (info->channels, channel_mask, |
534 | 0 | info->positions)) { |
535 | 0 | GST_ERROR ("invalid channel mask 0x%016" G_GINT64_MODIFIER |
536 | 0 | "x for %d channels", channel_mask, info->channels); |
537 | 0 | goto error; |
538 | 0 | } |
539 | 0 | } |
540 | | |
541 | | /* Parse the layout */ |
542 | | |
543 | 0 | layout_str = gst_structure_get_string (fmt_structure, "layout"); |
544 | 0 | if (layout_str == NULL || g_strcmp0 (layout_str, "interleaved") == 0) |
545 | 0 | info->layout = GST_AUDIO_LAYOUT_INTERLEAVED; |
546 | 0 | else if (g_strcmp0 (layout_str, "non-interleaved") == 0) |
547 | 0 | info->layout = GST_AUDIO_LAYOUT_NON_INTERLEAVED; |
548 | 0 | else { |
549 | 0 | GST_ERROR ("caps contain invalid layout field; caps: %" GST_PTR_FORMAT, |
550 | 0 | caps); |
551 | 0 | goto error; |
552 | 0 | } |
553 | | |
554 | 0 | gst_structure_get (fmt_structure, "reversed-bytes", G_TYPE_BOOLEAN, |
555 | 0 | &reversed_bytes, NULL); |
556 | |
|
557 | 0 | info->flags = flags; |
558 | 0 | info->reversed_bytes = reversed_bytes; |
559 | |
|
560 | 0 | return TRUE; |
561 | | |
562 | 0 | error: |
563 | 0 | return FALSE; |
564 | 0 | } |
565 | | |
566 | | /** |
567 | | * gst_dsd_info_to_caps: |
568 | | * @info: a #GstDsdInfo |
569 | | * |
570 | | * Convert the values of @info into a #GstCaps. |
571 | | * |
572 | | * Returns: (transfer full): the new #GstCaps containing the |
573 | | * info of @info. |
574 | | * |
575 | | * Since: 1.24 |
576 | | */ |
577 | | GstCaps * |
578 | | gst_dsd_info_to_caps (const GstDsdInfo * info) |
579 | 0 | { |
580 | 0 | GstCaps *caps; |
581 | 0 | const gchar *format; |
582 | 0 | GstAudioFlags flags; |
583 | |
|
584 | 0 | g_return_val_if_fail (info != NULL, NULL); |
585 | 0 | g_return_val_if_fail (info->format > GST_DSD_FORMAT_UNKNOWN |
586 | 0 | && info->format < GST_NUM_DSD_FORMATS, NULL); |
587 | 0 | g_return_val_if_fail (info->rate >= 1, NULL); |
588 | 0 | g_return_val_if_fail (info->channels >= 1, NULL); |
589 | | |
590 | 0 | format = gst_dsd_format_to_string (info->format); |
591 | 0 | g_return_val_if_fail (format != NULL, NULL); |
592 | | |
593 | 0 | flags = info->flags; |
594 | 0 | if ((flags & GST_AUDIO_FLAG_UNPOSITIONED) && info->channels > 1 |
595 | 0 | && info->positions[0] != GST_AUDIO_CHANNEL_POSITION_NONE) { |
596 | 0 | flags &= ~GST_AUDIO_FLAG_UNPOSITIONED; |
597 | 0 | GST_WARNING ("Unpositioned audio channel position flag set but " |
598 | 0 | "channel positions present"); |
599 | 0 | } else if (!(flags & GST_AUDIO_FLAG_UNPOSITIONED) && info->channels > 1 |
600 | 0 | && info->positions[0] == GST_AUDIO_CHANNEL_POSITION_NONE) { |
601 | 0 | flags |= GST_AUDIO_FLAG_UNPOSITIONED; |
602 | 0 | GST_WARNING ("Unpositioned audio channel position flag not set " |
603 | 0 | "but no channel positions present"); |
604 | 0 | } |
605 | |
|
606 | 0 | caps = gst_caps_new_simple (GST_DSD_MEDIA_TYPE, |
607 | 0 | "format", G_TYPE_STRING, format, |
608 | 0 | "rate", G_TYPE_INT, info->rate, |
609 | 0 | "channels", G_TYPE_INT, info->channels, |
610 | 0 | "layout", G_TYPE_STRING, layout_to_string (info->layout), |
611 | 0 | "reversed-bytes", G_TYPE_BOOLEAN, info->reversed_bytes, NULL); |
612 | |
|
613 | 0 | if (info->channels > 1 |
614 | 0 | || info->positions[0] != GST_AUDIO_CHANNEL_POSITION_MONO) { |
615 | 0 | guint64 channel_mask = 0; |
616 | |
|
617 | 0 | if ((flags & GST_AUDIO_FLAG_UNPOSITIONED)) { |
618 | 0 | channel_mask = 0; |
619 | 0 | } else { |
620 | 0 | if (!gst_audio_channel_positions_to_mask (info->positions, info->channels, |
621 | 0 | TRUE, &channel_mask)) |
622 | 0 | goto invalid_channel_positions; |
623 | 0 | } |
624 | | |
625 | 0 | if (info->channels == 1 |
626 | 0 | && info->positions[0] == GST_AUDIO_CHANNEL_POSITION_MONO) { |
627 | | /* Default mono special case */ |
628 | 0 | } else { |
629 | 0 | gst_caps_set_simple (caps, "channel-mask", GST_TYPE_BITMASK, channel_mask, |
630 | 0 | NULL); |
631 | 0 | } |
632 | 0 | } |
633 | | |
634 | 0 | return caps; |
635 | | |
636 | 0 | invalid_channel_positions: |
637 | 0 | GST_ERROR ("Invalid channel positions"); |
638 | 0 | gst_caps_unref (caps); |
639 | 0 | return NULL; |
640 | 0 | } |
641 | | |
642 | | /** |
643 | | * gst_dsd_info_is_equal: |
644 | | * @info: a #GstDsdInfo |
645 | | * @other: a #GstDsdInfo |
646 | | * |
647 | | * Compares two #GstDsdInfo and returns whether they are equal or not |
648 | | * |
649 | | * Returns: %TRUE if @info and @other are equal, else %FALSE. |
650 | | * |
651 | | * Since: 1.24 |
652 | | */ |
653 | | gboolean |
654 | | gst_dsd_info_is_equal (const GstDsdInfo * info, const GstDsdInfo * other) |
655 | 0 | { |
656 | 0 | if (info == other) |
657 | 0 | return TRUE; |
658 | | |
659 | 0 | if (GST_DSD_INFO_FORMAT (info) != GST_DSD_INFO_FORMAT (other)) |
660 | 0 | return FALSE; |
661 | 0 | if (GST_DSD_INFO_RATE (info) != GST_DSD_INFO_RATE (other)) |
662 | 0 | return FALSE; |
663 | 0 | if (GST_DSD_INFO_CHANNELS (info) != GST_DSD_INFO_CHANNELS (other)) |
664 | 0 | return FALSE; |
665 | 0 | if (GST_DSD_INFO_LAYOUT (info) != GST_DSD_INFO_LAYOUT (other)) |
666 | 0 | return FALSE; |
667 | 0 | if (GST_DSD_INFO_REVERSED_BYTES (info) != GST_DSD_INFO_REVERSED_BYTES (other)) |
668 | 0 | return FALSE; |
669 | 0 | if (memcmp (info->positions, other->positions, |
670 | 0 | GST_AUDIO_INFO_CHANNELS (info) * sizeof (GstAudioChannelPosition)) != |
671 | 0 | 0) |
672 | 0 | return FALSE; |
673 | | |
674 | 0 | return TRUE; |
675 | 0 | } |
676 | | |
677 | | static void |
678 | | gst_dsd_convert_copy_bytes_same_format (const guint8 * input_data, |
679 | | guint8 * output_data, GstDsdFormat format, gsize num_bytes, |
680 | | gboolean reverse_byte_bits) |
681 | 0 | { |
682 | 0 | if (reverse_byte_bits) { |
683 | 0 | guint index; |
684 | 0 | for (index = 0; index < num_bytes; ++index) |
685 | 0 | output_data[index] = byte_bit_reversal_table[input_data[index]]; |
686 | 0 | } else |
687 | 0 | memcpy (output_data, input_data, num_bytes); |
688 | 0 | } |
689 | | |
690 | | /* The conversion functions work by figuring out the index in the input |
691 | | * data that corresponds to the current index in the output data. The DSD |
692 | | * bits are grouped into "words" according to the DSD format. For example, |
693 | | * if input_format is GST_DSD_FORMAT_U16LE, then the input data is |
694 | | * grouped into 16-bit (= 2 byte) words. The in/out_word_index values |
695 | | * are the word indices into the input/output data. in/out_word_offset |
696 | | * values are the offsets *within* the words that are currently being |
697 | | * accessed. in/out_index are the combination of these values. |
698 | | * position is the offset in the time axis (= the position value that |
699 | | * would be used for seeking). In PCM terms, this is the equivalent of |
700 | | * (byte_offset / bytes_per_frame). |
701 | | * |
702 | | * The calculations first figure out the position and channel_nr out |
703 | | * of out_index. Using these two values it is then possible to calculate |
704 | | * in_word_index, in_word_width, and ultimately, in_index. The final |
705 | | * step is then to copy the DSD byte from in_index in input_data to |
706 | | * out_index in output_data (with reversing the byte's bits if requested). |
707 | | * |
708 | | * Conversions to non-interleaved formats work a little differently: |
709 | | * instead of one out_index there is one plane_index, that is, the |
710 | | * output is produced per-plane. |
711 | | * |
712 | | * For example, with interleaved -> interleaved conversion, given stereo |
713 | | * data (-> num_channels is 2), U16BE input, and U32BE output, then |
714 | | * in_word_width is 2, out_word_width is 4, out_stride is 2*4 = 8. An |
715 | | * out_index 15 means (note that indices start at 0, so channel #1 is the |
716 | | * second channel): |
717 | | * |
718 | | * - out_word_index = out_index / out_word_width = 15 / 8 = 1 |
719 | | * out_index refers to word #1 in the output array |
720 | | * - out_word_offset = out_index - out_word_index * out_word_width = 15 - 1*8 = 7 |
721 | | * out_index refers to byte #7 in output word #1 |
722 | | * - channel_nr = out_word_index % num_channels = 1 % 2 = 1 |
723 | | * out_index is referring to a byte that belongs to channel #1 |
724 | | * - position = (out_index / out_stride) * out_word_width + out_word_offset = |
725 | | * (15/8) * 4 + 7 = 11 |
726 | | * out_index refers to time axis offset 11 (in bytes) |
727 | | * |
728 | | * Then: |
729 | | * - in_word_index = (position / in_word_width) * num_channels + channel_nrh = |
730 | | * (11/2) * 2 + 1 = 11 |
731 | | * - in_word_offset = position % in_word_width = 11 % 2 = 1 |
732 | | * - in_index = in_word_index * in_word_width + in_word_offset = 11 * 2 + 1 = 23 |
733 | | * |
734 | | * -> We copy the byte #23 in input_data to byte #15 in output_data. |
735 | | */ |
736 | | |
737 | | static void |
738 | | gst_dsd_convert_interleaved_to_interleaved (const guint8 * input_data, |
739 | | guint8 * output_data, GstDsdFormat input_format, GstDsdFormat output_format, |
740 | | gsize num_dsd_bytes, gint num_channels, gboolean reverse_byte_bits) |
741 | 0 | { |
742 | 0 | if (input_format != output_format) { |
743 | 0 | guint out_index; |
744 | 0 | guint in_word_width, out_word_width; |
745 | 0 | guint out_stride; |
746 | 0 | gboolean input_is_le = gst_dsd_format_is_le (input_format); |
747 | 0 | gboolean output_is_le = gst_dsd_format_is_le (output_format); |
748 | |
|
749 | 0 | in_word_width = gst_dsd_format_get_width (input_format); |
750 | 0 | out_word_width = gst_dsd_format_get_width (output_format); |
751 | 0 | out_stride = out_word_width * num_channels; |
752 | |
|
753 | 0 | for (out_index = 0; out_index < num_dsd_bytes; ++out_index) { |
754 | 0 | guint in_word_index, in_word_offset; |
755 | 0 | guint out_word_index, out_word_offset; |
756 | 0 | guint in_index; |
757 | 0 | guint channel_nr; |
758 | 0 | guint position; |
759 | 0 | guint8 input_byte; |
760 | |
|
761 | 0 | out_word_index = out_index / out_word_width; |
762 | 0 | out_word_offset = out_index % out_word_width; |
763 | 0 | if (output_is_le) |
764 | 0 | out_word_offset = out_word_width - 1 - out_word_offset; |
765 | |
|
766 | 0 | channel_nr = out_word_index % num_channels; |
767 | 0 | position = (out_index / out_stride) * out_word_width + out_word_offset; |
768 | |
|
769 | 0 | in_word_index = (position / in_word_width) * num_channels + channel_nr; |
770 | 0 | in_word_offset = position % in_word_width; |
771 | 0 | if (input_is_le) |
772 | 0 | in_word_offset = in_word_width - 1 - in_word_offset; |
773 | |
|
774 | 0 | in_index = in_word_index * in_word_width + in_word_offset; |
775 | |
|
776 | 0 | input_byte = input_data[in_index]; |
777 | 0 | output_data[out_index] = |
778 | 0 | reverse_byte_bits ? byte_bit_reversal_table[input_byte] : input_byte; |
779 | 0 | } |
780 | 0 | } else |
781 | 0 | gst_dsd_convert_copy_bytes_same_format (input_data, output_data, |
782 | 0 | input_format, num_dsd_bytes, reverse_byte_bits); |
783 | 0 | } |
784 | | |
785 | | static void |
786 | | gst_dsd_convert_interleaved_to_non_interleaved (const guint8 * input_data, |
787 | | guint8 * output_data, GstDsdFormat input_format, GstDsdFormat output_format, |
788 | | const gsize * output_plane_offsets, gsize num_dsd_bytes, gint num_channels, |
789 | | gboolean reverse_byte_bits) |
790 | 0 | { |
791 | 0 | guint plane_index; |
792 | 0 | guint in_word_width, out_word_width; |
793 | 0 | guint channel_nr; |
794 | 0 | gsize num_bytes_per_plane = num_dsd_bytes / num_channels; |
795 | 0 | gboolean input_is_le = gst_dsd_format_is_le (input_format); |
796 | 0 | gboolean output_is_le = gst_dsd_format_is_le (output_format); |
797 | |
|
798 | 0 | in_word_width = gst_dsd_format_get_width (input_format); |
799 | 0 | out_word_width = gst_dsd_format_get_width (output_format); |
800 | |
|
801 | 0 | for (channel_nr = 0; channel_nr < num_channels; ++channel_nr) { |
802 | 0 | for (plane_index = 0; plane_index < num_bytes_per_plane; ++plane_index) { |
803 | 0 | guint in_word_index, in_word_offset; |
804 | 0 | guint out_word_index, out_word_offset; |
805 | 0 | guint in_index; |
806 | 0 | guint out_index; |
807 | 0 | guint position; |
808 | 0 | guint8 input_byte; |
809 | |
|
810 | 0 | out_word_index = plane_index / out_word_width; |
811 | 0 | out_word_offset = plane_index % out_word_width; |
812 | 0 | if (output_is_le) |
813 | 0 | out_word_offset = out_word_width - 1 - out_word_offset; |
814 | |
|
815 | 0 | position = plane_index; |
816 | |
|
817 | 0 | in_word_index = (position / in_word_width) * num_channels + channel_nr; |
818 | 0 | in_word_offset = position % in_word_width; |
819 | 0 | if (input_is_le) |
820 | 0 | in_word_offset = in_word_width - 1 - in_word_offset; |
821 | |
|
822 | 0 | in_index = in_word_index * in_word_width + in_word_offset; |
823 | 0 | out_index = |
824 | 0 | output_plane_offsets[channel_nr] + out_word_index * out_word_width + |
825 | 0 | out_word_offset; |
826 | |
|
827 | 0 | input_byte = input_data[in_index]; |
828 | 0 | output_data[out_index] = |
829 | 0 | reverse_byte_bits ? byte_bit_reversal_table[input_byte] : input_byte; |
830 | 0 | } |
831 | 0 | } |
832 | 0 | } |
833 | | |
834 | | static void |
835 | | gst_dsd_convert_non_interleaved_to_interleaved (const guint8 * input_data, |
836 | | guint8 * output_data, GstDsdFormat input_format, GstDsdFormat output_format, |
837 | | const gsize * input_plane_offsets, gsize num_dsd_bytes, gint num_channels, |
838 | | gboolean reverse_byte_bits) |
839 | 0 | { |
840 | 0 | guint out_index; |
841 | 0 | guint in_word_width, out_word_width; |
842 | 0 | guint out_stride; |
843 | 0 | gboolean input_is_le = gst_dsd_format_is_le (input_format); |
844 | 0 | gboolean output_is_le = gst_dsd_format_is_le (output_format); |
845 | |
|
846 | 0 | in_word_width = gst_dsd_format_get_width (input_format); |
847 | 0 | out_word_width = gst_dsd_format_get_width (output_format); |
848 | 0 | out_stride = out_word_width * num_channels; |
849 | |
|
850 | 0 | for (out_index = 0; out_index < num_dsd_bytes; ++out_index) { |
851 | 0 | guint in_word_index, in_word_offset; |
852 | 0 | guint out_word_index, out_word_offset; |
853 | 0 | guint in_index; |
854 | 0 | guint channel_nr; |
855 | 0 | guint position; |
856 | 0 | guint8 input_byte; |
857 | |
|
858 | 0 | out_word_index = out_index / out_word_width; |
859 | 0 | out_word_offset = out_index % out_word_width; |
860 | 0 | if (output_is_le) |
861 | 0 | out_word_offset = out_word_width - 1 - out_word_offset; |
862 | |
|
863 | 0 | channel_nr = out_word_index % num_channels; |
864 | 0 | position = (out_index / out_stride) * out_word_width + out_word_offset; |
865 | |
|
866 | 0 | in_word_index = position / in_word_width; |
867 | 0 | in_word_offset = position % in_word_width; |
868 | 0 | if (input_is_le) |
869 | 0 | in_word_offset = in_word_width - 1 - in_word_offset; |
870 | |
|
871 | 0 | in_index = |
872 | 0 | input_plane_offsets[channel_nr] + in_word_index * in_word_width + |
873 | 0 | in_word_offset; |
874 | |
|
875 | 0 | input_byte = input_data[in_index]; |
876 | 0 | output_data[out_index] = |
877 | 0 | reverse_byte_bits ? byte_bit_reversal_table[input_byte] : input_byte; |
878 | 0 | } |
879 | 0 | } |
880 | | |
881 | | static void |
882 | | gst_dsd_convert_non_interleaved_to_non_interleaved (const guint8 * input_data, |
883 | | guint8 * output_data, GstDsdFormat input_format, GstDsdFormat output_format, |
884 | | const gsize * input_plane_offsets, const gsize * output_plane_offsets, |
885 | | gsize num_dsd_bytes, gint num_channels, gboolean reverse_byte_bits) |
886 | 0 | { |
887 | 0 | gboolean same_format = input_format == output_format; |
888 | 0 | gboolean same_plane_offsets = memcmp (input_plane_offsets, |
889 | 0 | output_plane_offsets, num_channels * sizeof (gsize)) == 0; |
890 | |
|
891 | 0 | if (same_format && same_plane_offsets) { |
892 | 0 | gst_dsd_convert_copy_bytes_same_format (input_data, output_data, |
893 | 0 | input_format, num_dsd_bytes, reverse_byte_bits); |
894 | 0 | } else if (same_format) { |
895 | 0 | gint channel_nr; |
896 | 0 | gsize num_bytes_per_plane = num_dsd_bytes / num_channels; |
897 | |
|
898 | 0 | if (reverse_byte_bits) { |
899 | 0 | guint plane_index; |
900 | 0 | guint8 input_byte; |
901 | |
|
902 | 0 | for (channel_nr = 0; channel_nr < num_channels; ++channel_nr) { |
903 | 0 | for (plane_index = 0; plane_index < num_bytes_per_plane; ++plane_index) { |
904 | 0 | guint in_index = input_plane_offsets[channel_nr] + plane_index; |
905 | 0 | guint out_index = output_plane_offsets[channel_nr] + plane_index; |
906 | 0 | input_byte = input_data[in_index]; |
907 | 0 | output_data[out_index] = byte_bit_reversal_table[input_byte]; |
908 | 0 | } |
909 | 0 | } |
910 | 0 | } else { |
911 | 0 | for (channel_nr = 0; channel_nr < num_channels; ++channel_nr) { |
912 | 0 | memcpy (output_data + output_plane_offsets[channel_nr], |
913 | 0 | input_data + input_plane_offsets[channel_nr], num_bytes_per_plane); |
914 | 0 | } |
915 | 0 | } |
916 | 0 | } else { |
917 | 0 | guint channel_nr; |
918 | 0 | guint plane_index; |
919 | 0 | gsize num_bytes_per_plane = num_dsd_bytes / num_channels; |
920 | 0 | guint in_word_width, out_word_width; |
921 | 0 | gboolean input_is_le = gst_dsd_format_is_le (input_format); |
922 | 0 | gboolean output_is_le = gst_dsd_format_is_le (output_format); |
923 | |
|
924 | 0 | in_word_width = gst_dsd_format_get_width (input_format); |
925 | 0 | out_word_width = gst_dsd_format_get_width (output_format); |
926 | |
|
927 | 0 | for (channel_nr = 0; channel_nr < num_channels; ++channel_nr) { |
928 | 0 | for (plane_index = 0; plane_index < num_bytes_per_plane; ++plane_index) { |
929 | 0 | guint in_word_index, in_word_offset; |
930 | 0 | guint out_word_index, out_word_offset; |
931 | 0 | guint in_index; |
932 | 0 | guint out_index; |
933 | 0 | guint position; |
934 | 0 | guint8 input_byte; |
935 | |
|
936 | 0 | out_word_index = plane_index / out_word_width; |
937 | 0 | out_word_offset = plane_index % out_word_width; |
938 | 0 | if (output_is_le) |
939 | 0 | out_word_offset = out_word_width - 1 - out_word_offset; |
940 | |
|
941 | 0 | position = plane_index; |
942 | |
|
943 | 0 | in_word_index = position / in_word_width; |
944 | 0 | in_word_offset = position % in_word_width; |
945 | 0 | if (input_is_le) |
946 | 0 | in_word_offset = in_word_width - 1 - in_word_offset; |
947 | |
|
948 | 0 | in_index = |
949 | 0 | input_plane_offsets[channel_nr] + in_word_index * in_word_width + |
950 | 0 | in_word_offset; |
951 | 0 | out_index = |
952 | 0 | output_plane_offsets[channel_nr] + out_word_index * out_word_width + |
953 | 0 | out_word_offset; |
954 | |
|
955 | 0 | input_byte = input_data[in_index]; |
956 | 0 | output_data[out_index] = |
957 | 0 | reverse_byte_bits ? byte_bit_reversal_table[input_byte] : |
958 | 0 | input_byte; |
959 | 0 | } |
960 | 0 | } |
961 | 0 | } |
962 | 0 | } |
963 | | |
964 | | /** |
965 | | * gst_dsd_convert: |
966 | | * @input_data: (array): the DSD format conversion's input source |
967 | | * @output_data: (array): the DSD format conversion's output destination |
968 | | * @input_format: DSD format of the input data to convert from |
969 | | * @output_format: DSD format of the output data to convert to |
970 | | * @input_layout: Input data layout |
971 | | * @output_layout: Output data layout |
972 | | * @input_plane_offsets: (array length=num_channels) (nullable): Plane offsets for non-interleaved input data |
973 | | * @output_plane_offsets: (array length=num_channels) (nullable): Plane offsets for non-interleaved output data |
974 | | * @num_dsd_bytes: How many bytes with DSD data to convert |
975 | | * @num_channels: Number of channels (must be at least 1) |
976 | | * @reverse_byte_bits: If TRUE, reverse the bits in each DSD byte |
977 | | * |
978 | | * Converts DSD data from one layout and grouping format to another. |
979 | | * @num_bytes must be an integer multiple of the width of both input |
980 | | * and output format. For example, if the input format is GST_DSD_FORMAT_U32LE, |
981 | | * and the output format is GST_DSD_FORMAT_U16BE, then @num_bytes must |
982 | | * be an integer multiple of both 4 (U32LE width) and 2 (U16BE width). |
983 | | * |
984 | | * @reverse_byte_bits is necessary if the bit order within the DSD bytes |
985 | | * needs to be reversed. This is rarely necessary, and is not to be |
986 | | * confused with the endianness of formats (which determines the ordering |
987 | | * of *bytes*). |
988 | | * |
989 | | * @input_plane_offsets must not be NULL if @input_layout is set to |
990 | | * #GST_AUDIO_LAYOUT_NON_INTERLEAVED. The same applies to @output_plane_offsets. |
991 | | * These plane offsets define the starting offset of the planes (there is |
992 | | * exactly one plane per channel) within @input_data and @output_data |
993 | | * respectively. If GST_AUDIO_LAYOUT_INTERLEAVED is used, the plane offsets |
994 | | * are ignored. |
995 | | * |
996 | | * Since: 1.24 |
997 | | */ |
998 | | void |
999 | | gst_dsd_convert (const guint8 * input_data, guint8 * output_data, |
1000 | | GstDsdFormat input_format, GstDsdFormat output_format, |
1001 | | GstAudioLayout input_layout, GstAudioLayout output_layout, |
1002 | | const gsize * input_plane_offsets, const gsize * output_plane_offsets, |
1003 | | gsize num_dsd_bytes, gint num_channels, gboolean reverse_byte_bits) |
1004 | 0 | { |
1005 | 0 | g_return_if_fail (input_data != NULL); |
1006 | 0 | g_return_if_fail (output_data != NULL); |
1007 | 0 | g_return_if_fail (input_format > GST_DSD_FORMAT_UNKNOWN |
1008 | 0 | && input_format < GST_NUM_DSD_FORMATS); |
1009 | 0 | g_return_if_fail (output_format > GST_DSD_FORMAT_UNKNOWN |
1010 | 0 | && output_format < GST_NUM_DSD_FORMATS); |
1011 | 0 | g_return_if_fail (input_layout == GST_AUDIO_LAYOUT_INTERLEAVED |
1012 | 0 | || input_plane_offsets != NULL); |
1013 | 0 | g_return_if_fail (output_layout == GST_AUDIO_LAYOUT_INTERLEAVED |
1014 | 0 | || output_plane_offsets != NULL); |
1015 | 0 | g_return_if_fail (num_dsd_bytes > 0); |
1016 | 0 | g_return_if_fail ( |
1017 | 0 | (num_dsd_bytes % gst_dsd_format_get_width (input_format)) == 0); |
1018 | 0 | g_return_if_fail ( |
1019 | 0 | (num_dsd_bytes % gst_dsd_format_get_width (output_format)) == 0); |
1020 | 0 | g_return_if_fail (num_channels > 0); |
1021 | | |
1022 | 0 | GST_LOG ("converting DSD: input: format %s layout %s output: format %s " |
1023 | 0 | "layout %s num channels: %d num DSD bytes: %" G_GSIZE_FORMAT " " |
1024 | 0 | "reverse byte bits: %d", gst_dsd_format_to_string (input_format), |
1025 | 0 | layout_to_string (input_layout), gst_dsd_format_to_string (output_format), |
1026 | 0 | layout_to_string (output_layout), num_channels, num_dsd_bytes, |
1027 | 0 | reverse_byte_bits); |
1028 | |
|
1029 | 0 | switch (input_layout) { |
1030 | 0 | case GST_AUDIO_LAYOUT_INTERLEAVED: |
1031 | 0 | switch (output_layout) { |
1032 | 0 | case GST_AUDIO_LAYOUT_INTERLEAVED: |
1033 | 0 | gst_dsd_convert_interleaved_to_interleaved (input_data, output_data, |
1034 | 0 | input_format, output_format, num_dsd_bytes, num_channels, |
1035 | 0 | reverse_byte_bits); |
1036 | 0 | break; |
1037 | | |
1038 | 0 | case GST_AUDIO_LAYOUT_NON_INTERLEAVED: |
1039 | 0 | gst_dsd_convert_interleaved_to_non_interleaved (input_data, |
1040 | 0 | output_data, input_format, output_format, output_plane_offsets, |
1041 | 0 | num_dsd_bytes, num_channels, reverse_byte_bits); |
1042 | 0 | break; |
1043 | | |
1044 | 0 | default: |
1045 | 0 | g_assert_not_reached (); |
1046 | 0 | } |
1047 | 0 | break; |
1048 | | |
1049 | 0 | case GST_AUDIO_LAYOUT_NON_INTERLEAVED: |
1050 | 0 | switch (output_layout) { |
1051 | 0 | case GST_AUDIO_LAYOUT_INTERLEAVED: |
1052 | 0 | gst_dsd_convert_non_interleaved_to_interleaved (input_data, |
1053 | 0 | output_data, input_format, output_format, input_plane_offsets, |
1054 | 0 | num_dsd_bytes, num_channels, reverse_byte_bits); |
1055 | 0 | break; |
1056 | | |
1057 | 0 | case GST_AUDIO_LAYOUT_NON_INTERLEAVED: |
1058 | 0 | gst_dsd_convert_non_interleaved_to_non_interleaved (input_data, |
1059 | 0 | output_data, input_format, output_format, input_plane_offsets, |
1060 | 0 | output_plane_offsets, num_dsd_bytes, num_channels, |
1061 | 0 | reverse_byte_bits); |
1062 | 0 | break; |
1063 | | |
1064 | 0 | default: |
1065 | 0 | g_assert_not_reached (); |
1066 | 0 | } |
1067 | 0 | break; |
1068 | | |
1069 | 0 | default: |
1070 | | g_assert_not_reached (); |
1071 | 0 | } |
1072 | 0 | } |