mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-26 02:00:33 +00:00
8c5a8f4466
Related to: https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/issues/972 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3901>
385 lines
14 KiB
C
385 lines
14 KiB
C
/* 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);
|