Carlos Rafael Giani 2022-10-17 10:00:07 +02:00 committed by GStreamer Marge Bot
parent 82ffdfb13c
commit 8c5a8f4466
16 changed files with 2546 additions and 3 deletions

View file

@ -1121,6 +1121,7 @@
"GST_AUDIO_RING_BUFFER_CAST",
"GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3",
"GST_AUDIO_RING_BUFFER_FORMAT_TYPE_A_LAW",
"GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DSD",
"GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DTS",
"GST_AUDIO_RING_BUFFER_FORMAT_TYPE_EAC3",
"GST_AUDIO_RING_BUFFER_FORMAT_TYPE_FLAC",
@ -1136,6 +1137,14 @@
"GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW",
"GST_AUDIO_RING_BUFFER_GET_COND",
"GST_AUDIO_RING_BUFFER_SIGNAL",
"GST_AUDIO_RING_BUFFER_SPEC_FORMAT_TYPE",
"GST_AUDIO_RING_BUFFER_SPEC_INFO",
"GST_AUDIO_RING_BUFFER_SPEC_LATENCY_TIME",
"GST_AUDIO_RING_BUFFER_SPEC_BUFFER_TIME",
"GST_AUDIO_RING_BUFFER_SPEC_SEGSIZE",
"GST_AUDIO_RING_BUFFER_SPEC_SEGTOTAL",
"GST_AUDIO_RING_BUFFER_SPEC_SEGLATENCY",
"GST_AUDIO_RING_BUFFER_SPEC_DSD_FORMAT",
"GST_AUDIO_RING_BUFFER_STATE_ERROR",
"GST_AUDIO_RING_BUFFER_STATE_PAUSED",
"GST_AUDIO_RING_BUFFER_STATE_STARTED",
@ -1651,6 +1660,16 @@
"GST_DISCOVERER_URI_INVALID",
"GST_DMABUF_ALLOCATOR_CAST",
"GST_DO_CHECK_TEST_ENVIRONMENT",
"GST_DSD_INFO_CHANNELS",
"GST_DSD_INFO_FORMAT",
"GST_DSD_INFO_IS_VALID",
"GST_DSD_INFO_LAYOUT",
"GST_DSD_INFO_POSITION",
"GST_DSD_INFO_RATE",
"GST_DSD_INFO_REVERSED_BYTES",
"GST_DSD_INFO_STRIDE",
"GST_DSD_PLANE_OFFSET_META_API_TYPE",
"GST_DSD_PLANE_OFFSET_META_INFO",
"GST_DVB_SERVICE_ADVANCED_CODEC_DIGITAL_RADIO_SOUND",
"GST_DVB_SERVICE_ADVANCED_CODEC_HD_DIGITAL_TELEVISION",
"GST_DVB_SERVICE_ADVANCED_CODEC_HD_NVOD_REFERENCE",
@ -7125,6 +7144,7 @@
"GstAudioRingBufferClass::stop",
"GstAudioRingBufferFormatType",
"GstAudioRingBufferSpec",
"GstAudioRingBufferSpec.ABI.abi.dsd_format",
"GstAudioRingBufferSpec.buffer_time",
"GstAudioRingBufferSpec.caps",
"GstAudioRingBufferSpec.info",
@ -19583,6 +19603,7 @@
"_FLAG",
"_GST_AUDIO_FORMAT_NE",
"_GST_AUDIO_FORMAT_OE",
"_GST_DSD_FORMAT_NE",
"_GST_FAST_READ",
"_GST_FAST_READ_SWAP",
"_GST_FAST_WRITE",
@ -38376,6 +38397,7 @@
"gst_buffer_get_audio_level_meta",
"gst_buffer_get_audio_meta",
"gst_buffer_get_custom_meta",
"gst_buffer_get_dsd_plane_offset_meta",
"gst_buffer_get_flags",
"gst_buffer_get_gl_sync_meta",
"gst_buffer_get_max_memory",
@ -39120,6 +39142,8 @@
"gst_dmabuf_allocator_alloc_with_flags",
"gst_dmabuf_allocator_new",
"gst_dmabuf_memory_get_fd",
"gst_dsd_plane_offset_meta_api_get_type",
"gst_dsd_plane_offset_meta_get_info",
"gst_dynamic_type_factory_load",
"gst_dynamic_type_register",
"gst_egl_get_error_string",

View file

@ -2121,6 +2121,44 @@
"tracers": {},
"url": "Unknown package origin"
},
"dsd": {
"description": "elements for processing DSD audio",
"elements": {
"dsdconvert": {
"author": "Carlos Rafael Giani <crg7475@mailbox.org>",
"description": "Convert between different DSD grouping formats",
"hierarchy": [
"GstDsdConvert",
"GstBaseTransform",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"klass": "Filter/Converter/Audio",
"pad-templates": {
"sink": {
"caps": "audio/x-dsd:\n format: { DSDU32BE, DSDU16BE, DSDU8, DSDU32LE, DSDU16LE }\n rate: [ 1, 2147483647 ]\n layout: { (string)interleaved, (string)non-interleaved }\n reversed-bytes: { (boolean)false, (boolean)true }\n channels: [ 1, 2147483647 ]\n",
"direction": "sink",
"presence": "always"
},
"src": {
"caps": "audio/x-dsd:\n format: { DSDU32BE, DSDU16BE, DSDU8, DSDU32LE, DSDU16LE }\n rate: [ 1, 2147483647 ]\n layout: { (string)interleaved, (string)non-interleaved }\n reversed-bytes: { (boolean)false, (boolean)true }\n channels: [ 1, 2147483647 ]\n",
"direction": "src",
"presence": "always"
}
},
"rank": "secondary"
}
},
"filename": "gstdsd",
"license": "LGPL",
"other-types": {},
"package": "GStreamer Base Plug-ins",
"source": "gst-plugins-base",
"tracers": {},
"url": "Unknown package origin"
},
"encoding": {
"description": "various encoding-related elements",
"elements": {

View file

@ -35,6 +35,7 @@
#include <gst/audio/audio-resampler.h>
#include <gst/audio/gstaudiostreamalign.h>
#include <gst/audio/gstaudioaggregator.h>
#include <gst/audio/gstdsd.h>
G_BEGIN_DECLS

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,338 @@
/* GStreamer
* Copyright (C) 2023 Carlos Rafael Giani <crg7475@mailbox.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <gst/audio/gstdsdformat.h>
G_BEGIN_DECLS
/**
* GST_DSD_MEDIA_TYPE:
*
* The GStreamer media type for DSD.
*
* Since: 1.24
*/
#define GST_DSD_MEDIA_TYPE "audio/x-dsd"
/**
* GST_DSD_CAPS_MAKE:
* @format: string format that describes the DSD bits grouping,
* as string (e.g. "DSDU32BE", "DSDU8", etc.)
*
* Generic caps string for DSD audio, for use in pad templates.
*
* Since: 1.24
*/
#define GST_DSD_CAPS_MAKE(format) \
GST_DSD_MEDIA_TYPE ", " \
"format = (string) " format ", " \
"rate = " GST_AUDIO_RATE_RANGE ", " \
"layout = (string) { interleaved, non-interleaved }, " \
"reversed-bytes = (gboolean) { false, true }, " \
"channels = " GST_AUDIO_CHANNELS_RANGE
/**
* GST_DSD_MAKE_DSD_RATE_44x:
*
* Calculates a valid DSD-44x rate (in bytes) from commonly used rate
* multiplier specifications like DSD64, DSD128 etc.
*
* For example, to get the rate for DSD64-44x, use 64 as the multiplier
* argument.
*
* Since: 1.24
*/
#define GST_DSD_MAKE_DSD_RATE_44x(multiplier) \
((gint) ((gint64) multiplier) * 44100 / 8)
/**
* GST_DSD_MAKE_DSD_RATE_48x:
*
* Calculates a valid DSD-48x rate (in bytes) from commonly used rate
* multiplier specifications like DSD64, DSD128 etc.
*
* For example, to get the rate for DSD64-48x, use 64 as the multiplier
* argument.
*
* Since: 1.24
*/
#define GST_DSD_MAKE_DSD_RATE_48x(multiplier) \
((gint) ((gint64) multiplier) * 48000 / 8)
/**
* GST_DSD_SILENCE_PATTERN_BYTE:
*
* Silence pattern for DSD data.
*
* In DSD, a nullbyte does not correspond to silence. To fill memory regions
* with "DSD silence", these regions must be filled with byte 0x69 instead
* (this is the DSD silence pattern). This constant provides that pattern
* in a more readable fashion.
*
* Since: 1.24
*/
#define GST_DSD_SILENCE_PATTERN_BYTE (0x69)
typedef struct _GstDsdInfo GstDsdInfo;
/**
* GstDsdInfo:
* @format: DSD grouping format
* @rate: DSD rate
* @channels: number of channels (must be at least 1)
* @layout: audio layout
* @reversed_bytes: true if the DSD bits in the data bytes are reversed,
* that is, the least significant bit comes first
* @positions: positions for each channel
*
* Information describing DSD audio properties.
*
* In DSD, the "sample format" is the bit. Unlike PCM, there are no further
* "sample formats" in DSD. However, in software, DSD bits are grouped into
* bytes (since dealing with individual bits is impractical), and these bytes
* in turn are grouped into words. This becomes relevant when interleaving
* channels and transmitting DSD data through audio APIs. The different
* types of grouping DSD bytes are referred to as the "DSD grouping forma"
* or just "DSD format". #GstDsdFormat has a list of valid ways of grouping
* DSD bytes into words.
*
* DSD rates are equivalent to PCM sample rates, except that they specify
* how many DSD bytes are consumed per second. This refers to the bytes per
* second _per channel_; the rate does not change when the number of channel
* changes. (Strictly speaking, it would be more correct to measure the
* *bits* per second, since the bit is the DSD "sample format", but it is
* more practical to use bytes.) In DSD, bit rates are always an integer
* multiple of the CD audio rate (44100) or the DAT rate (48000). DSD64-44x
* is 44100 * 64 = 2822400 bits per second, or 352800 bytes per second
* (the latter would be used in this info structure). DSD64-48x is
* 48000 * 64 = 3072000 bits per second, or 384000 bytes per second.
* #GST_DSD_MAKE_DSD_RATE_44x can be used for specifying DSD-44x rates,
* *and #GST_DSD_MAKE_DSD_RATE_48x can be used for specifying DSD-48x ones.
* Also, since DSD-48x is less well known, when the multiplier is given
* without the 44x/48x specifier, 44x is typically implied.
*
* It is important to know that in DSD, different format widths correspond
* to different playtimes. That is, a word with 32 DSD bits covers two times
* as much playtime as a word with 16 DSD bits. This is in contrast to PCM,
* where one word (= one PCM sample) always covers a time period of 1/samplerate,
* no matter how many bits a PCM sample is made of. For this reason, DSD
* and PCM widths and strides cannot be used the same way.
*
* Multiple channels are arranged in DSD data either interleaved or non-
* interleaved. This is similar to PCM. Interleaved layouts rotate between
* channels and words. First, word 0 of channel 0 is present. Then word
* 0 of channel 1 follows. Then word 0 of channel 2 etc. until all
* channels are through, then comes word 1 of channel 0 etc.
*
* Non-interleaved data is planar. First, all words of channel 0 are
* present, then all words of channel 1 etc. Unlike interleaved data,
* non-interleaved data can be sparse, that is, there can be space in
* between the planes. the @positions array specifies the plane offsets.
*
* In uncommon cases, the DSD bits in the data bytes can be stored in reverse
* order. For example, normally, in DSDU8, the first byte contains DSD bits
* 0 to 7, and the most significant bit of that byte is DSD bit 0. If this
* order is reversed, then bit 7 is the first one instead. In that ase,
* @reversed_bytes is set to TRUE.
*
* Use the provided macros to access the info in this structure.
*
* Since: 1.24
*/
struct _GstDsdInfo {
GstDsdFormat format;
gint rate;
gint channels;
GstAudioLayout layout;
gboolean reversed_bytes;
GstAudioChannelPosition positions[64];
GstAudioFlags flags;
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
#define GST_TYPE_DSD_INFO (gst_dsd_info_get_type ())
GST_AUDIO_API
GType gst_dsd_info_get_type (void);
#define GST_DSD_INFO_IS_VALID(i) ((i)->format < GST_NUM_DSD_FORMATS && (i)->rate > 0 && (i)->channels > 0)
#define GST_DSD_INFO_FORMAT(info) ((info)->format)
#define GST_DSD_INFO_RATE(info) ((info)->rate)
#define GST_DSD_INFO_CHANNELS(info) ((info)->channels)
#define GST_DSD_INFO_LAYOUT(info) ((info)->layout)
#define GST_DSD_INFO_REVERSED_BYTES(info) ((info)->reversed_bytes)
#define GST_DSD_INFO_POSITION(info,c) ((info)->position[c])
/**
* GST_DSD_INFO_STRIDE:
*
* Calculates the stride for a given #GstDsdInfo.
*
* Note that this is only useful if the info's audio layout
* is GST_AUDIO_LAYOUT_INTERLEAVED.
*
* Since: 1.24
*/
#define GST_DSD_INFO_STRIDE(info) (gst_dsd_format_get_width((info)->format) * (info)->channels)
/*** GstDsdPlaneOffsetMeta ***/
#define GST_DSD_PLANE_OFFSET_META_API_TYPE (gst_dsd_plane_offset_meta_api_get_type())
#define GST_DSD_PLANE_OFFSET_META_INFO (gst_dsd_plane_offset_meta_get_info())
/**
* GST_META_TAG_DSD_PLANE_OFFSETS_STR:
*
* This metadata stays relevant as long as the DSD plane offsets are unchanged.
*
* Since: 1.24
*/
#define GST_META_TAG_DSD_PLANE_OFFSETS_STR "dsdplaneoffsets"
typedef struct _GstDsdPlaneOffsetMeta GstDsdPlaneOffsetMeta;
/**
* GstDsdPlaneOffsetMeta:
* @meta: parent #GstMeta
* @num_channels: number of channels in the DSD data
* @num_bytes_per_channel: the number of valid bytes per channel in the buffer
* @offsets: the offsets (in bytes) where each channel plane starts in the buffer
*
* Buffer metadata describing planar DSD contents in the buffer. This is not needed
* for interleaved DSD data, and is required for non-interleaved (= planar) data.
*
* The different channels in @offsets are always in the GStreamer channel order.
* Zero-copy channel reordering can be implemented by swapping the values in
* @offsets.
*
* It is not allowed for channels to overlap in memory,
* i.e. for each i in [0, channels), the range
* [@offsets[i], @offsets[i] + @num_bytes_per_channel) must not overlap
* with any other such range.
*
* It is, however, allowed to have parts of the buffer memory unused, by using
* @offsets and @num_bytes_per_channel in such a way that leave gaps on it.
* This is used to implement zero-copy clipping in non-interleaved buffers.
*
* Obviously, due to the above, it is not safe to infer the
* number of valid bytes from the size of the buffer. You should always
* use the @num_bytes_per_channel variable of this metadata.
*
* Since: 1.24
*/
struct _GstDsdPlaneOffsetMeta {
GstMeta meta;
gint num_channels;
gsize num_bytes_per_channel;
gsize *offsets;
/*< private >*/
gsize priv_offsets_arr[8];
gpointer _gst_reserved[GST_PADDING];
};
GST_AUDIO_API
GType gst_dsd_plane_offset_meta_api_get_type (void);
GST_AUDIO_API
const GstMetaInfo * gst_dsd_plane_offset_meta_get_info (void);
#define gst_buffer_get_dsd_plane_offset_meta(b) \
((GstDsdPlaneOffsetMeta*)gst_buffer_get_meta((b), GST_DSD_PLANE_OFFSET_META_API_TYPE))
GST_AUDIO_API
GstDsdPlaneOffsetMeta * gst_buffer_add_dsd_plane_offset_meta (GstBuffer *buffer,
gint num_channels,
gsize num_bytes_per_channel,
gsize offsets[]);
GST_AUDIO_API
GstDsdInfo * gst_dsd_info_new (void);
GST_AUDIO_API
GstDsdInfo * gst_dsd_info_new_from_caps (const GstCaps * caps);
GST_AUDIO_API
void gst_dsd_info_init (GstDsdInfo * info);
GST_AUDIO_API
void gst_dsd_info_set_format (GstDsdInfo * info,
GstDsdFormat format,
gint rate,
gint channels,
const GstAudioChannelPosition * positions);
GST_AUDIO_API
GstDsdInfo * gst_dsd_info_copy (const GstDsdInfo * info);
GST_AUDIO_API
void gst_dsd_info_free (GstDsdInfo * info);
GST_AUDIO_API
gboolean gst_dsd_info_from_caps (GstDsdInfo *info,
const GstCaps *caps);
GST_AUDIO_API
GstCaps * gst_dsd_info_to_caps (const GstDsdInfo *info);
GST_AUDIO_API
gboolean gst_dsd_info_is_equal (const GstDsdInfo *info,
const GstDsdInfo *other);
GST_AUDIO_API
void gst_dsd_convert (const guint8 *input_data,
guint8 *output_data,
GstDsdFormat input_format,
GstDsdFormat output_format,
GstAudioLayout input_layout,
GstAudioLayout output_layout,
const gsize *input_plane_offsets,
const gsize *output_plane_offsets,
gsize num_dsd_bytes,
gint num_channels,
gboolean reverse_bytes);
/**
* gst_dsd_format_is_le:
* @format: The format.
*
* Useful for determining whether a format is a little-endian.
* GST_DSD_FORMAT_U8 and GST_DSD_FORMAT_UNKNOWN
* are not considered little-endian.
*
* Returns: TRUE if the format is a little-endian one.
*/
static inline gboolean
gst_dsd_format_is_le (GstDsdFormat format)
{
switch (format) {
case GST_DSD_FORMAT_U16LE:
case GST_DSD_FORMAT_U32LE:
return TRUE;
default:
return FALSE;
}
}
G_END_DECLS

View file

@ -0,0 +1,109 @@
/* GStreamer
* Copyright (C) 2023 Carlos Rafael Giani <crg7475@mailbox.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "gstdsdformat.h"
/**
* gst_dsd_format_from_string:
* @format: a format string
*
* Convert the @format string to its #GstDsdFormat.
*
* Returns: the #GstDsdFormat for @format or GST_DSD_FORMAT_UNKNOWN when the
* string is not a known format.
*
* Since: 1.24
*/
GstDsdFormat
gst_dsd_format_from_string (const gchar * str)
{
if (g_strcmp0 (str, "DSDU8") == 0)
return GST_DSD_FORMAT_U8;
else if (g_strcmp0 (str, "DSDU16LE") == 0)
return GST_DSD_FORMAT_U16LE;
else if (g_strcmp0 (str, "DSDU16BE") == 0)
return GST_DSD_FORMAT_U16BE;
else if (g_strcmp0 (str, "DSDU32LE") == 0)
return GST_DSD_FORMAT_U32LE;
else if (g_strcmp0 (str, "DSDU32BE") == 0)
return GST_DSD_FORMAT_U32BE;
else
return GST_DSD_FORMAT_UNKNOWN;
}
/**
* gst_dsd_format_to_string:
* @format: a #GstDsdFormat
*
* Returns a string containing a descriptive name for
* the #GstDsdFormat if there is one, or NULL otherwise.
*
* Returns: the name corresponding to @format
*
* Since: 1.24
*/
const gchar *
gst_dsd_format_to_string (GstDsdFormat format)
{
switch (format) {
case GST_DSD_FORMAT_U8:
return "DSDU8";
case GST_DSD_FORMAT_U16LE:
return "DSDU16LE";
case GST_DSD_FORMAT_U16BE:
return "DSDU16BE";
case GST_DSD_FORMAT_U32LE:
return "DSDU32LE";
case GST_DSD_FORMAT_U32BE:
return "DSDU32BE";
default:
return NULL;
}
}
/**
* gst_dsd_format_get_width:
* @format: a #GstDsdFormat
*
* Returns: Number of bytes in this DSD grouping format.
*
* Since: 1.24
*/
guint
gst_dsd_format_get_width (GstDsdFormat format)
{
switch (format) {
case GST_DSD_FORMAT_U8:
return 1;
case GST_DSD_FORMAT_U16LE:
return 2;
case GST_DSD_FORMAT_U16BE:
return 2;
case GST_DSD_FORMAT_U32LE:
return 4;
case GST_DSD_FORMAT_U32BE:
return 4;
default:
return 0;
}
}

View file

@ -0,0 +1,86 @@
/* GStreamer
* Copyright (C) 2023 Carlos Rafael Giani <crg7475@mailbox.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <gst/gst.h>
#include <gst/audio/audio-prelude.h>
G_BEGIN_DECLS
#if G_BYTE_ORDER == G_BIG_ENDIAN
#define _GST_DSD_FORMAT_NE(fmt) GST_DSD_FORMAT_ ## fmt ## BE
#elif G_BYTE_ORDER == G_LITTLE_ENDIAN
#define _GST_DSD_FORMAT_NE(fmt) GST_DSD_FORMAT_ ## fmt ## LE
#endif
/**
* GstDsdFormat:
* @GST_NUM_DSD_FORMATS: number of valid DSD formats
* @GST_DSD_FORMAT_UNKNOWN: unknown / invalid DSD format
* @GST_DSD_FORMAT_U8: 8 DSD bits in 1 byte
* @GST_DSD_FORMAT_U16LE: 16 DSD bits in 2 bytes, little endian order
* @GST_DSD_FORMAT_U16BE: 16 DSD bits in 2 bytes, big endian order
* @GST_DSD_FORMAT_U32LE: 32 DSD bits in 4 bytes, little endian order
* @GST_DSD_FORMAT_U32BE: 32 DSD bits in 4 bytes, big endian order
* @GST_DSD_FORMAT_U16: 16 DSD bits in 2 bytes, native endianness
* @GST_DSD_FORMAT_U32: 32 DSD bits in 4 bytes, native endianness
*
* Enum value describing how DSD bits are grouped.
*
* Since: 1.24
*/
typedef enum {
GST_DSD_FORMAT_U8 = 0,
GST_DSD_FORMAT_U16LE,
GST_DSD_FORMAT_U16BE,
GST_DSD_FORMAT_U32LE,
GST_DSD_FORMAT_U32BE,
GST_NUM_DSD_FORMATS,
GST_DSD_FORMAT_UNKNOWN = 0xffffffff,
/* native endianness equivalents */
GST_DSD_FORMAT_U16 = _GST_DSD_FORMAT_NE(U16),
GST_DSD_FORMAT_U32 = _GST_DSD_FORMAT_NE(U32)
} GstDsdFormat;
/**
* GST_DSD_FORMATS_ALL:
*
* List of all DSD formats, for use in template caps strings.
*
* Big endian formats are preferred, since little-endian ones flip around
* the DSD bytes, and most DSD hardware uses big endian formats.
*
* Since: 1.24
*/
#define GST_DSD_FORMATS_ALL "{ DSDU32BE, DSDU16BE, DSDU8, DSDU32LE, DSDU16LE }"
GST_AUDIO_API
GstDsdFormat gst_dsd_format_from_string (const gchar *str);
GST_AUDIO_API
const gchar * gst_dsd_format_to_string (GstDsdFormat format);
GST_AUDIO_API
guint gst_dsd_format_get_width (GstDsdFormat format);
G_END_DECLS

View file

@ -24,6 +24,8 @@ audio_src = files([
'gstaudioutilsprivate.c',
'streamvolume.c',
'gstaudiostreamalign.c',
'gstdsd.c',
'gstdsdformat.c',
])
audio_mkenum_headers = files([
@ -41,6 +43,8 @@ audio_mkenum_headers = files([
'gstaudiocdsrc.h',
'gstaudiobasesink.h',
'gstaudiostreamalign.h',
'gstdsd.h',
'gstdsdformat.h',
])
# FIXME: check headers

View file

@ -0,0 +1,397 @@
/* GStreamer
* Copyright (C) 2023 Carlos Rafael Giani <crg7475@mailbox.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstdsdconvert.h"
/**
* SECTION:element-dsdconvert
*
* Dsdconvert converts between DSD grouping formats and byte reversals.
* See #GstDsdInfo and @gst_dsd_convert for details about the conversion.
* Neither the DSD rate nor the channel count can be changed; this only
* converts the grouping format.
*
* Since: 1.24
*/
GST_DEBUG_CATEGORY_STATIC (dsd_convert_debug);
#define GST_CAT_DEFAULT dsd_convert_debug
#define STATIC_CAPS \
GST_STATIC_CAPS (GST_DSD_CAPS_MAKE (GST_DSD_FORMATS_ALL))
static GstStaticPadTemplate static_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
STATIC_CAPS);
static GstStaticPadTemplate static_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
STATIC_CAPS);
struct _GstDsdConvert
{
GstBaseTransform parent;
GstDsdInfo in_info;
GstDsdInfo out_info;
GstAdapter *input_adapter;
};
#define gst_dsd_convert_parent_class parent_class
G_DEFINE_TYPE (GstDsdConvert, gst_dsd_convert, GST_TYPE_BASE_TRANSFORM);
GST_ELEMENT_REGISTER_DEFINE (dsdconvert, "dsdconvert",
GST_RANK_SECONDARY, GST_TYPE_DSD_CONVERT);
static gboolean gst_dsd_convert_set_caps (GstBaseTransform * base,
GstCaps * incaps, GstCaps * outcaps);
static GstFlowReturn gst_dsd_convert_prepare_output_buffer (GstBaseTransform *
trans, GstBuffer * input, GstBuffer ** outbuf);
static GstCaps *gst_dsd_convert_transform_caps (GstBaseTransform * base,
GstPadDirection direction, GstCaps * caps, GstCaps * filter);
static gboolean gst_dsd_convert_transform_size (GstBaseTransform * base,
GstPadDirection direction, GstCaps * caps, gsize size, GstCaps * othercaps,
gsize * othersize);
static GstFlowReturn gst_dsd_convert_transform (GstBaseTransform * base,
GstBuffer * inbuf, GstBuffer * outbuf);
static void
gst_dsd_convert_class_init (GstDsdConvertClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstBaseTransformClass *basetransform_class = GST_BASE_TRANSFORM_CLASS (klass);
GST_DEBUG_CATEGORY_INIT (dsd_convert_debug, "dsdconvert", 0,
"DSD grouping format converter");
gst_element_class_add_static_pad_template (element_class,
&static_sink_template);
gst_element_class_add_static_pad_template (element_class,
&static_src_template);
basetransform_class->set_caps = GST_DEBUG_FUNCPTR (gst_dsd_convert_set_caps);
basetransform_class->prepare_output_buffer =
GST_DEBUG_FUNCPTR (gst_dsd_convert_prepare_output_buffer);
basetransform_class->transform_caps =
GST_DEBUG_FUNCPTR (gst_dsd_convert_transform_caps);
basetransform_class->transform_size =
GST_DEBUG_FUNCPTR (gst_dsd_convert_transform_size);
basetransform_class->transform =
GST_DEBUG_FUNCPTR (gst_dsd_convert_transform);
gst_element_class_set_static_metadata (element_class, "DSD converter",
"Filter/Converter/Audio",
"Convert between different DSD grouping formats",
"Carlos Rafael Giani <crg7475@mailbox.org>");
}
static void
gst_dsd_convert_init (G_GNUC_UNUSED GstDsdConvert * self)
{
}
static gboolean
gst_dsd_convert_set_caps (GstBaseTransform * base,
GstCaps * incaps, GstCaps * outcaps)
{
GstDsdConvert *self = GST_DSD_CONVERT (base);
gboolean can_passthrough;
if (!gst_dsd_info_from_caps (&self->in_info, incaps))
goto invalid_in;
if (!gst_dsd_info_from_caps (&self->out_info, outcaps))
goto invalid_out;
can_passthrough = gst_dsd_info_is_equal (&self->in_info, &self->out_info);
gst_base_transform_set_passthrough (base, can_passthrough);
return TRUE;
invalid_in:
{
GST_ERROR_OBJECT (base, "invalid input caps");
return FALSE;
}
invalid_out:
{
GST_ERROR_OBJECT (base, "invalid output caps");
return FALSE;
}
}
static GstFlowReturn
gst_dsd_convert_prepare_output_buffer (GstBaseTransform * trans,
GstBuffer * input, GstBuffer ** outbuf)
{
GstFlowReturn flow_ret;
GstDsdConvert *self = GST_DSD_CONVERT_CAST (trans);
/* The point of this prepare_buffer override is to add the plane
* offset meta if the outgoing data uses a non-interleaved layout. */
flow_ret =
GST_BASE_TRANSFORM_CLASS (parent_class)->prepare_output_buffer (trans,
input, outbuf);
if (flow_ret != GST_FLOW_OK)
return flow_ret;
if (GST_DSD_INFO_LAYOUT (&self->out_info) == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
g_assert (*outbuf != NULL);
GST_LOG_OBJECT (trans, "adding dsd plane offset meta to output buffer");
/* Add the meta, with num_channels set to 0 and offsets to NULL. That's
* because we do not yet know these quantities - they need to instead
* be set in gst_dsd_convert_transform(). */
gst_buffer_add_dsd_plane_offset_meta (*outbuf,
GST_DSD_INFO_CHANNELS (&self->out_info), 0, NULL);
}
return GST_FLOW_OK;
}
static gboolean
remove_format_from_structure (GstCapsFeatures * features,
GstStructure * structure, gpointer user_data G_GNUC_UNUSED)
{
gst_structure_remove_fields (structure, "format", "layout",
"reversed-bytes", NULL);
return TRUE;
}
static GstCaps *
gst_dsd_convert_transform_caps (GstBaseTransform * base,
GstPadDirection direction, GstCaps * caps, GstCaps * filter)
{
GstCaps *tmp, *tmp2, *template_caps;
GstCaps *result;
tmp = gst_caps_copy (caps);
/* Remove any existing format, layout, reversed-bytes fields. */
gst_caps_map_in_place (tmp, remove_format_from_structure, NULL);
/* Then fill in the removed fields with those from the template caps. */
template_caps = gst_static_pad_template_get_caps (&static_sink_template);
tmp2 = gst_caps_intersect_full (tmp, template_caps, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (tmp);
gst_caps_unref (template_caps);
tmp = tmp2;
if (filter) {
tmp2 = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
gst_caps_unref (tmp);
tmp = tmp2;
}
result = tmp;
GST_DEBUG_OBJECT (base, "transformed %" GST_PTR_FORMAT " into %"
GST_PTR_FORMAT, caps, result);
return result;
}
static gboolean
gst_dsd_convert_transform_size (GstBaseTransform * base,
GstPadDirection direction, GstCaps * caps, gsize size,
GstCaps * othercaps, gsize * othersize)
{
GstDsdConvert *self = (GstDsdConvert *) (base);
GstDsdInfo info;
GstDsdInfo otherinfo;
guint width, otherwidth, maxwidth;
g_return_val_if_fail (caps != NULL, FALSE);
g_return_val_if_fail (othercaps != NULL, FALSE);
g_return_val_if_fail (othersize != NULL, FALSE);
if (!gst_dsd_info_from_caps (&info, caps))
goto invalid_caps;
if (!gst_dsd_info_from_caps (&otherinfo, othercaps))
goto invalid_othercaps;
width = gst_dsd_format_get_width (GST_DSD_INFO_FORMAT (&info));
otherwidth = gst_dsd_format_get_width (GST_DSD_INFO_FORMAT (&otherinfo));
maxwidth = MAX (width, otherwidth);
*othersize = (size / maxwidth) * maxwidth;
GST_LOG_OBJECT (self, "transformed size %" G_GSIZE_FORMAT " to othersize %"
G_GSIZE_FORMAT "; width: %u otherwidth: %u", size, *othersize, width,
otherwidth);
return TRUE;
invalid_caps:
{
GST_INFO_OBJECT (base, "failed to parse caps to transform size");
return FALSE;
}
invalid_othercaps:
{
GST_INFO_OBJECT (base, "failed to parse othercaps to transform size");
return FALSE;
}
}
static GstFlowReturn
gst_dsd_convert_transform (GstBaseTransform * base,
GstBuffer * inbuf, GstBuffer * outbuf)
{
GstDsdConvert *self = GST_DSD_CONVERT (base);
GstFlowReturn flow_ret = GST_FLOW_OK;
GstMapInfo in_map_info, out_map_info;
gboolean inbuf_mapped = FALSE;
gboolean outbuf_mapped = FALSE;
gboolean need_to_reverse_bytes;
const gsize *input_plane_offsets = NULL;
const gsize *output_plane_offsets = NULL;
GstDsdPlaneOffsetMeta *in_dsd_plane_ofs_meta = NULL;
GstDsdPlaneOffsetMeta *out_dsd_plane_ofs_meta = NULL;
gint num_channels;
gsize num_dsd_bytes = 0;
g_return_val_if_fail (inbuf != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (outbuf != NULL, GST_FLOW_ERROR);
GST_LOG_OBJECT (self, "about to transform input buffer %" GST_PTR_FORMAT
"; output buffer size: %" G_GSIZE_FORMAT, inbuf,
gst_buffer_get_size (outbuf));
num_channels = GST_DSD_INFO_CHANNELS (&self->in_info);
/* Get the plane offset metas if the audio layouts are non-interleaved.
* Some of the quantities necessary for converting from/to non-interleaved
* data are known only later, which is why not all of them are set here. */
if (GST_DSD_INFO_LAYOUT (&self->in_info) == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
in_dsd_plane_ofs_meta = gst_buffer_get_dsd_plane_offset_meta (inbuf);
if (G_UNLIKELY (in_dsd_plane_ofs_meta == NULL))
goto in_dsd_plane_ofs_meta_missing;
input_plane_offsets = in_dsd_plane_ofs_meta->offsets;
num_dsd_bytes = in_dsd_plane_ofs_meta->num_bytes_per_channel * num_channels;
}
if (GST_DSD_INFO_LAYOUT (&self->out_info) == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
out_dsd_plane_ofs_meta = gst_buffer_get_dsd_plane_offset_meta (outbuf);
if (G_UNLIKELY (out_dsd_plane_ofs_meta == NULL))
goto out_dsd_plane_ofs_meta_missing;
}
/* Map the input and output buffers now. */
if (!gst_buffer_map (inbuf, &in_map_info, GST_MAP_READ))
goto in_map_failed;
inbuf_mapped = TRUE;
if (!gst_buffer_map (outbuf, &out_map_info, GST_MAP_WRITE))
goto out_map_failed;
outbuf_mapped = TRUE;
/* In case of interleaved input audio, we only know now how many DSD bytes
* there are, because then, the amount equals the number of bytes in the
* buffer divided by the number of channels. This does not apply to
* non-interleaved (= planar) data, since in such data, there can be
* a space between the planes. */
if (GST_DSD_INFO_LAYOUT (&self->in_info) == GST_AUDIO_LAYOUT_INTERLEAVED)
num_dsd_bytes = in_map_info.size;
need_to_reverse_bytes = GST_DSD_INFO_REVERSED_BYTES (&self->in_info) !=
GST_DSD_INFO_REVERSED_BYTES (&self->out_info);
/* We now have the necessary info to complete the output plane offset meta
* (which is present if we are producing non-interleaved data). */
if (GST_DSD_INFO_LAYOUT (&self->out_info) == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
gint channel_idx;
out_dsd_plane_ofs_meta->num_bytes_per_channel =
num_dsd_bytes / num_channels;
for (channel_idx = 0; channel_idx < num_channels; ++channel_idx) {
out_dsd_plane_ofs_meta->offsets[channel_idx] =
out_dsd_plane_ofs_meta->num_bytes_per_channel * channel_idx;
}
output_plane_offsets = out_dsd_plane_ofs_meta->offsets;
}
/* Do the actual conversion. */
gst_dsd_convert (in_map_info.data, out_map_info.data,
GST_DSD_INFO_FORMAT (&self->in_info),
GST_DSD_INFO_FORMAT (&self->out_info),
GST_DSD_INFO_LAYOUT (&self->in_info),
GST_DSD_INFO_LAYOUT (&self->out_info), input_plane_offsets,
output_plane_offsets, num_dsd_bytes, num_channels, need_to_reverse_bytes);
finish:
if (inbuf_mapped)
gst_buffer_unmap (inbuf, &in_map_info);
if (outbuf_mapped)
gst_buffer_unmap (outbuf, &out_map_info);
return flow_ret;
in_dsd_plane_ofs_meta_missing:
{
GST_ERROR_OBJECT (base,
"input buffer has no DSD plane offset meta; buffer details: %"
GST_PTR_FORMAT, inbuf);
flow_ret = GST_FLOW_ERROR;
goto finish;
}
out_dsd_plane_ofs_meta_missing:
{
GST_ERROR_OBJECT (base,
"output buffer has no DSD plane offset meta; buffer details: %"
GST_PTR_FORMAT, outbuf);
flow_ret = GST_FLOW_ERROR;
goto finish;
}
in_map_failed:
{
GST_ERROR_OBJECT (base, "could not map input buffer; buffer details: %"
GST_PTR_FORMAT, inbuf);
flow_ret = GST_FLOW_ERROR;
goto finish;
}
out_map_failed:
{
GST_ERROR_OBJECT (base, "could not map output buffer; buffer details: %"
GST_PTR_FORMAT, outbuf);
flow_ret = GST_FLOW_ERROR;
goto finish;
}
}

View file

@ -0,0 +1,35 @@
/* GStreamer
* Copyright (C) 2023 Carlos Rafael Giani <crg7475@mailbox.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#include <gst/audio/gstdsd.h>
G_BEGIN_DECLS
#define GST_TYPE_DSD_CONVERT (gst_dsd_convert_get_type())
G_DECLARE_FINAL_TYPE (GstDsdConvert, gst_dsd_convert,
GST, DSD_CONVERT, GstBaseTransform)
#define GST_DSD_CONVERT_CAST(obj) ((GstDsdConvert *)(obj))
GST_ELEMENT_REGISTER_DECLARE (dsdconvert);
G_END_DECLS

View file

@ -0,0 +1,14 @@
dsd_sources = [
'gstdsdconvert.c',
'plugin.c'
]
gstdsd = library('gstdsd',
dsd_sources,
c_args : gst_plugins_base_args,
include_directories: [configinc, libsinc],
dependencies : [audio_dep, gst_base_dep],
install : true,
install_dir : plugins_install_dir,
)
plugins += [gstdsd]

View file

@ -0,0 +1,42 @@
/* GStreamer
* Copyright (C) 2023 Carlos Rafael Giani <crg7475@mailbox.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/**
* plugin-dsd:
*
* Since: 1.24
*/
#include "gstdsdconvert.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
return GST_ELEMENT_REGISTER (dsdconvert, plugin);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
dsd,
"elements for processing DSD audio",
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

View file

@ -1,7 +1,7 @@
foreach plugin : ['adder', 'app', 'audioconvert', 'audiomixer', 'audiorate', 'audioresample',
'audiotestsrc', 'compositor', 'encoding', 'gio', 'overlaycomposition', 'pbtypes', 'playback',
'rawparse', 'subparse', 'tcp', 'typefind', 'videoconvertscale', 'videorate',
'videotestsrc', 'volume']
'audiotestsrc', 'compositor', 'dsd', 'encoding', 'gio', 'overlaycomposition',
'pbtypes', 'playback', 'rawparse', 'subparse', 'tcp', 'typefind',
'videoconvertscale', 'videorate', 'videotestsrc', 'volume']
if not get_option(plugin).disabled()
subdir(plugin)
endif

View file

@ -37,6 +37,7 @@ option('audioresample', type : 'feature', value : 'auto')
option('audiotestsrc', type : 'feature', value : 'auto')
option('compositor', type : 'feature', value : 'auto')
option('drm', type : 'feature', value : 'auto')
option('dsd', type : 'feature', value : 'auto')
option('encoding', type : 'feature', value : 'auto')
option('gio', type : 'feature', value : 'auto')
option('gio-typefinder', type : 'feature', value : 'auto')

View file

@ -0,0 +1,385 @@
/* GStreamer
* Copyright (C) 2023 Carlos Rafael Giani <crg7475@mailbox.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <gst/check/gstcheck.h>
#include <gst/audio/gstdsd.h>
#define NUM_CHANNELS (2)
#define NUM_BYTES_PER_CHANNEL (16)
#define NUM_PATTERN_BYTES (NUM_CHANNELS * NUM_BYTES_PER_CHANNEL)
typedef struct
{
GstDsdFormat format;
guint8 bytes[NUM_CHANNELS * NUM_BYTES_PER_CHANNEL];
} DsdTestPattern;
/* The following test patterns contain bytes 0x00 to 0x0F for the left channel
* and 0x80 to 0x8F for the right channel. These bytes are grouped in the test
* patterns according to their (non-)interleaved layout and grouping format. */
/* *INDENT-OFF* */
static const DsdTestPattern interleaved_dsd_test_patterns[] = {
{
GST_DSD_FORMAT_U8,
{
0x00, 0x80, 0x01, 0x81, 0x02, 0x82, 0x03, 0x83, 0x04, 0x84, 0x05, 0x85, 0x06, 0x86, 0x07, 0x87,
0x08, 0x88, 0x09, 0x89, 0x0A, 0x8A, 0x0B, 0x8B, 0x0C, 0x8C, 0x0D, 0x8D, 0x0E, 0x8E, 0x0F, 0x8F
}
},
{
GST_DSD_FORMAT_U16LE,
{
0x01, 0x00, 0x81, 0x80, 0x03, 0x02, 0x83, 0x82, 0x05, 0x04, 0x85, 0x84, 0x07, 0x06, 0x87, 0x86,
0x09, 0x08, 0x89, 0x88, 0x0B, 0x0A, 0x8B, 0x8A, 0x0D, 0x0C, 0x8D, 0x8C, 0x0F, 0x0E, 0x8F, 0x8E
}
},
{
GST_DSD_FORMAT_U16BE,
{
0x00, 0x01, 0x80, 0x81, 0x02, 0x03, 0x82, 0x83, 0x04, 0x05, 0x84, 0x85, 0x06, 0x07, 0x86, 0x87,
0x08, 0x09, 0x88, 0x89, 0x0A, 0x0B, 0x8A, 0x8B, 0x0C, 0x0D, 0x8C, 0x8D, 0x0E, 0x0F, 0x8E, 0x8F
}
},
{
GST_DSD_FORMAT_U32LE,
{
0x03, 0x02, 0x01, 0x00, 0x83, 0x82, 0x81, 0x80, 0x07, 0x06, 0x05, 0x04, 0x87, 0x86, 0x85, 0x84,
0x0B, 0x0A, 0x09, 0x08, 0x8B, 0x8A, 0x89, 0x88, 0x0F, 0x0E, 0x0D, 0x0C, 0x8F, 0x8E, 0x8D, 0x8C
}
},
{
GST_DSD_FORMAT_U32BE,
{
0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0x83, 0x04, 0x05, 0x06, 0x07, 0x84, 0x85, 0x86, 0x87,
0x08, 0x09, 0x0A, 0x0B, 0x88, 0x89, 0x8A, 0x8B, 0x0C, 0x0D, 0x0E, 0x0F, 0x8C, 0x8D, 0x8E, 0x8F
}
}
};
/* *INDENT-ON* */
static const int num_interleaved_dsd_test_patterns =
sizeof (interleaved_dsd_test_patterns) / sizeof (DsdTestPattern);
/* *INDENT-OFF* */
static const DsdTestPattern non_interleaved_dsd_test_patterns[] = {
{
GST_DSD_FORMAT_U8,
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F
}
},
{
GST_DSD_FORMAT_U16LE,
{
0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x09, 0x08, 0x0B, 0x0A, 0x0D, 0x0C, 0x0F, 0x0E,
0x81, 0x80, 0x83, 0x82, 0x85, 0x84, 0x87, 0x86, 0x89, 0x88, 0x8B, 0x8A, 0x8D, 0x8C, 0x8F, 0x8E
}
},
{
GST_DSD_FORMAT_U16BE,
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
}
},
{
GST_DSD_FORMAT_U32LE,
{
0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x0B, 0x0A, 0x09, 0x08, 0x0F, 0x0E, 0x0D, 0x0C,
0x83, 0x82, 0x81, 0x80, 0x87, 0x86, 0x85, 0x84, 0x8B, 0x8A, 0x89, 0x88, 0x8F, 0x8E, 0x8D, 0x8C,
}
},
{
GST_DSD_FORMAT_U32BE,
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
}
},
};
/* *INDENT-ON* */
static const int num_non_interleaved_dsd_test_patterns =
sizeof (non_interleaved_dsd_test_patterns) / sizeof (DsdTestPattern);
static const gsize dsd_plane_offsets[NUM_CHANNELS] = { 0, 16 };
GST_START_TEST (test_dsd_conversion_interleaved_to_interleaved)
{
int in_idx, out_idx;
guint8 actual_output_pattern[NUM_PATTERN_BYTES];
for (in_idx = 0; in_idx < num_interleaved_dsd_test_patterns; ++in_idx) {
for (out_idx = 0; out_idx < num_interleaved_dsd_test_patterns; ++out_idx) {
guint8 const *input_pattern = interleaved_dsd_test_patterns[in_idx].bytes;
guint8 const *expected_output_pattern =
interleaved_dsd_test_patterns[out_idx].bytes;
GstDsdFormat input_format = interleaved_dsd_test_patterns[in_idx].format;
GstDsdFormat output_format =
interleaved_dsd_test_patterns[out_idx].format;
gboolean conversion_ok;
gst_dsd_convert (input_pattern, actual_output_pattern, input_format,
output_format, GST_AUDIO_LAYOUT_INTERLEAVED,
GST_AUDIO_LAYOUT_INTERLEAVED, NULL, NULL, NUM_PATTERN_BYTES,
NUM_CHANNELS, FALSE);
conversion_ok = memcmp (actual_output_pattern, expected_output_pattern,
NUM_PATTERN_BYTES) == 0;
if (!conversion_ok) {
GST_MEMDUMP ("expected:", expected_output_pattern, NUM_PATTERN_BYTES);
GST_MEMDUMP ("actual :", actual_output_pattern, NUM_PATTERN_BYTES);
}
fail_unless (conversion_ok, "DSD conversion output incorrect");
}
}
}
GST_END_TEST;
GST_START_TEST (test_dsd_conversion_interleaved_to_non_interleaved)
{
int in_idx, out_idx;
guint8 actual_output_pattern[NUM_PATTERN_BYTES];
for (in_idx = 0; in_idx < num_interleaved_dsd_test_patterns; ++in_idx) {
for (out_idx = 0; out_idx < num_non_interleaved_dsd_test_patterns;
++out_idx) {
guint8 const *input_pattern = interleaved_dsd_test_patterns[in_idx].bytes;
guint8 const *expected_output_pattern =
non_interleaved_dsd_test_patterns[out_idx].bytes;
GstDsdFormat input_format = interleaved_dsd_test_patterns[in_idx].format;
GstDsdFormat output_format =
non_interleaved_dsd_test_patterns[out_idx].format;
gboolean conversion_ok;
gst_dsd_convert (input_pattern, actual_output_pattern, input_format,
output_format, GST_AUDIO_LAYOUT_INTERLEAVED,
GST_AUDIO_LAYOUT_NON_INTERLEAVED, NULL, dsd_plane_offsets,
NUM_PATTERN_BYTES, NUM_CHANNELS, FALSE);
conversion_ok = memcmp (actual_output_pattern, expected_output_pattern,
NUM_PATTERN_BYTES) == 0;
if (!conversion_ok) {
GST_MEMDUMP ("expected:", expected_output_pattern, NUM_PATTERN_BYTES);
GST_MEMDUMP ("actual :", actual_output_pattern, NUM_PATTERN_BYTES);
}
fail_unless (conversion_ok, "DSD conversion output incorrect");
}
}
}
GST_END_TEST;
GST_START_TEST (test_dsd_conversion_non_interleaved_to_interleaved)
{
int in_idx, out_idx;
guint8 actual_output_pattern[NUM_PATTERN_BYTES];
for (in_idx = 0; in_idx < num_non_interleaved_dsd_test_patterns; ++in_idx) {
for (out_idx = 0; out_idx < num_interleaved_dsd_test_patterns; ++out_idx) {
guint8 const *input_pattern =
non_interleaved_dsd_test_patterns[in_idx].bytes;
guint8 const *expected_output_pattern =
interleaved_dsd_test_patterns[out_idx].bytes;
GstDsdFormat input_format =
non_interleaved_dsd_test_patterns[in_idx].format;
GstDsdFormat output_format =
interleaved_dsd_test_patterns[out_idx].format;
gboolean conversion_ok;
gst_dsd_convert (input_pattern,
actual_output_pattern,
input_format,
output_format,
GST_AUDIO_LAYOUT_NON_INTERLEAVED,
GST_AUDIO_LAYOUT_INTERLEAVED,
dsd_plane_offsets, NULL, NUM_PATTERN_BYTES, NUM_CHANNELS, FALSE);
conversion_ok = memcmp (actual_output_pattern, expected_output_pattern,
NUM_PATTERN_BYTES) == 0;
if (!conversion_ok) {
GST_MEMDUMP ("expected:", expected_output_pattern, NUM_PATTERN_BYTES);
GST_MEMDUMP ("actual :", actual_output_pattern, NUM_PATTERN_BYTES);
}
fail_unless (conversion_ok, "DSD conversion output incorrect");
}
}
}
GST_END_TEST;
GST_START_TEST (test_dsd_conversion_non_interleaved_to_non_interleaved)
{
int in_idx, out_idx;
guint8 actual_output_pattern[NUM_PATTERN_BYTES];
for (in_idx = 0; in_idx < num_non_interleaved_dsd_test_patterns; ++in_idx) {
for (out_idx = 0; out_idx < num_non_interleaved_dsd_test_patterns;
++out_idx) {
guint8 const *input_pattern =
non_interleaved_dsd_test_patterns[in_idx].bytes;
guint8 const *expected_output_pattern =
non_interleaved_dsd_test_patterns[out_idx].bytes;
GstDsdFormat input_format =
non_interleaved_dsd_test_patterns[in_idx].format;
GstDsdFormat output_format =
non_interleaved_dsd_test_patterns[out_idx].format;
gboolean conversion_ok;
gst_dsd_convert (input_pattern,
actual_output_pattern,
input_format,
output_format,
GST_AUDIO_LAYOUT_NON_INTERLEAVED,
GST_AUDIO_LAYOUT_NON_INTERLEAVED,
dsd_plane_offsets,
dsd_plane_offsets, NUM_PATTERN_BYTES, NUM_CHANNELS, FALSE);
conversion_ok = memcmp (actual_output_pattern, expected_output_pattern,
NUM_PATTERN_BYTES) == 0;
if (!conversion_ok) {
GST_MEMDUMP ("expected:", expected_output_pattern, NUM_PATTERN_BYTES);
GST_MEMDUMP ("actual :", actual_output_pattern, NUM_PATTERN_BYTES);
}
fail_unless (conversion_ok, "DSD conversion output incorrect");
}
}
}
GST_END_TEST;
GST_START_TEST (test_dsd_info_from_caps)
{
GstDsdInfo info;
GstCaps *full_caps, *minimal_caps;
full_caps = gst_caps_new_simple (GST_DSD_MEDIA_TYPE,
"format", G_TYPE_STRING, "DSDU16LE",
"rate", G_TYPE_INT, GST_DSD_MAKE_DSD_RATE_44x (128),
"channels", G_TYPE_INT, 2,
"layout", G_TYPE_STRING, "non-interleaved",
"channel-mask", GST_TYPE_BITMASK, 0x3, NULL);
fail_unless (gst_dsd_info_from_caps (&info, full_caps));
fail_unless_equals_int (info.format, GST_DSD_FORMAT_U16LE);
fail_unless_equals_int (info.rate, GST_DSD_MAKE_DSD_RATE_44x (128));
fail_unless_equals_int (info.channels, 2);
fail_unless_equals_int (info.layout, GST_AUDIO_LAYOUT_NON_INTERLEAVED);
fail_unless_equals_int (info.positions[0],
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT);
fail_unless_equals_int (info.positions[1],
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT);
gst_caps_unref (full_caps);
minimal_caps = gst_caps_new_simple (GST_DSD_MEDIA_TYPE,
"format", G_TYPE_STRING, "DSDU16LE",
"rate", G_TYPE_INT, GST_DSD_MAKE_DSD_RATE_44x (128),
"channels", G_TYPE_INT, 2, NULL);
fail_unless (gst_dsd_info_from_caps (&info, minimal_caps));
fail_unless_equals_int (info.format, GST_DSD_FORMAT_U16LE);
fail_unless_equals_int (info.rate, GST_DSD_MAKE_DSD_RATE_44x (128));
fail_unless_equals_int (info.channels, 2);
fail_unless_equals_int (info.layout, GST_AUDIO_LAYOUT_INTERLEAVED);
fail_unless_equals_int (info.positions[0],
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT);
fail_unless_equals_int (info.positions[1],
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT);
gst_caps_unref (minimal_caps);
}
GST_END_TEST;
GST_START_TEST (test_dsd_info_to_caps)
{
GstStructure *str;
GstCaps *caps;
gint rate;
gint channels;
guint64 channel_mask;
GstDsdInfo info = {
.format = GST_DSD_FORMAT_U16LE,
.rate = GST_DSD_MAKE_DSD_RATE_44x (64),
.channels = 2,
.layout = GST_AUDIO_LAYOUT_INTERLEAVED,
.positions = {GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}
,
.flags = GST_AUDIO_FLAG_NONE
};
caps = gst_dsd_info_to_caps (&info);
fail_if (caps == NULL, "caps were not created");
fail_unless_equals_int (gst_caps_get_size (caps), 1);
str = gst_caps_get_structure (caps, 0);
fail_if (str == NULL, "could not get structure");
fail_unless (gst_structure_has_name (str, GST_DSD_MEDIA_TYPE));
fail_unless (gst_structure_has_field_typed (str, "format", G_TYPE_STRING));
fail_unless_equals_string (gst_structure_get_string (str, "format"),
"DSDU16LE");
fail_unless (gst_structure_get_int (str, "rate", &rate));
fail_unless_equals_int (rate, GST_DSD_MAKE_DSD_RATE_44x (64));
fail_unless (gst_structure_get_int (str, "channels", &channels));
fail_unless_equals_int (channels, 2);
fail_unless_equals_string (gst_structure_get_string (str, "layout"),
"interleaved");
fail_unless (gst_structure_get (str, "channel-mask", GST_TYPE_BITMASK,
&channel_mask, NULL));
fail_unless_equals_uint64 (channel_mask, 0x3);
gst_caps_unref (caps);
}
GST_END_TEST;
static Suite *
dsd_suite (void)
{
Suite *s = suite_create ("dsd");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_dsd_conversion_interleaved_to_interleaved);
tcase_add_test (tc_chain, test_dsd_conversion_interleaved_to_non_interleaved);
tcase_add_test (tc_chain, test_dsd_conversion_non_interleaved_to_interleaved);
tcase_add_test (tc_chain,
test_dsd_conversion_non_interleaved_to_non_interleaved);
tcase_add_test (tc_chain, test_dsd_info_from_caps);
tcase_add_test (tc_chain, test_dsd_info_to_caps);
return s;
}
GST_CHECK_MAIN (dsd);

View file

@ -10,6 +10,7 @@ base_tests = [
[ 'libs/audiosink.c' ],
[ 'libs/baseaudiovisualizer.c' ],
[ 'libs/discoverer.c' ],
[ 'libs/dsd.c' ],
[ 'libs/fft.c' ],
[ 'libs/libsabi.c', false, [ gstgl_dep, gstglx11_dep, gstglwayland_dep, gstglegl_dep ] ],
[ 'libs/mikey.c' ],