/* GStreamer * Copyright (C) 2023 Carlos Rafael Giani * * 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 #include #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);