mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-29 10:38:27 +00:00
dsd: Add code for DSD audio support
Related to: https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/issues/972 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3901>
This commit is contained in:
parent
82ffdfb13c
commit
8c5a8f4466
16 changed files with 2546 additions and 3 deletions
|
@ -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",
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
1068
subprojects/gst-plugins-base/gst-libs/gst/audio/gstdsd.c
Normal file
1068
subprojects/gst-plugins-base/gst-libs/gst/audio/gstdsd.c
Normal file
File diff suppressed because it is too large
Load diff
338
subprojects/gst-plugins-base/gst-libs/gst/audio/gstdsd.h
Normal file
338
subprojects/gst-plugins-base/gst-libs/gst/audio/gstdsd.h
Normal 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
|
109
subprojects/gst-plugins-base/gst-libs/gst/audio/gstdsdformat.c
Normal file
109
subprojects/gst-plugins-base/gst-libs/gst/audio/gstdsdformat.c
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
397
subprojects/gst-plugins-base/gst/dsd/gstdsdconvert.c
Normal file
397
subprojects/gst-plugins-base/gst/dsd/gstdsdconvert.c
Normal 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;
|
||||
}
|
||||
}
|
35
subprojects/gst-plugins-base/gst/dsd/gstdsdconvert.h
Normal file
35
subprojects/gst-plugins-base/gst/dsd/gstdsdconvert.h
Normal 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
|
14
subprojects/gst-plugins-base/gst/dsd/meson.build
Normal file
14
subprojects/gst-plugins-base/gst/dsd/meson.build
Normal 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]
|
42
subprojects/gst-plugins-base/gst/dsd/plugin.c
Normal file
42
subprojects/gst-plugins-base/gst/dsd/plugin.c
Normal 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)
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
385
subprojects/gst-plugins-base/tests/check/libs/dsd.c
Normal file
385
subprojects/gst-plugins-base/tests/check/libs/dsd.c
Normal 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);
|
|
@ -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' ],
|
||||
|
|
Loading…
Reference in a new issue