aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Klingbeil <klingm@amazon.com>2023-05-17 12:46:09 -0400
committerJean-Marc Valin <jmvalin@amazon.com>2023-06-16 13:01:38 -0400
commit4b9c620a8dbee1c2d164310ac8ace44fff97b162 (patch)
tree233bdf27d03372bc13704f2c046071a0e4d361c4
parentdf2f98f809a40cb380bf4570aa6522d12beac18c (diff)
downloadlibopus-4b9c620a8dbee1c2d164310ac8ace44fff97b162.tar.gz
Fixup extensions implementation and add unit tests
Add unit tests for opus_packet_extensions_count, opus_packet_extensions_parse, and opus_packet_extensions_generate. Add various assertions for NULL buffers or negative length arguments. Add explicit check for invalid length in opus_packet_extensions_count. Check for extension id >127 in opus_packet_extensions_generate. Check for invalid length input in opus_packet_extensions_generate. Signed-off-by: Jean-Marc Valin <jmvalin@amazon.com>
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--CMakeLists.txt12
-rw-r--r--Makefile.am5
-rw-r--r--Makefile.mips14
-rw-r--r--Makefile.unix14
-rwxr-xr-xautogen.sh1
-rw-r--r--cmake/OpusSources.cmake5
-rw-r--r--src/extensions.c21
-rw-r--r--src/opus_private.h2
-rw-r--r--tests/meson.build1
-rw-r--r--tests/test_opus_common.h1
-rw-r--r--tests/test_opus_extensions.c313
13 files changed, 383 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore
index 837619f9..5a49669c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,7 @@ tests/*test
tests/test_opus_api
tests/test_opus_decode
tests/test_opus_encode
+tests/test_opus_extensions
tests/test_opus_padding
tests/test_opus_projection
celt/arm/armopts.s
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d3303350..f97fc895 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -81,6 +81,7 @@ cmake:
apt-get install -y cmake ninja-build git
- !reference [.snippets, git_prep]
script:
+ - ./autogen.sh
- mkdir build
- cmake -S . -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DOPUS_BUILD_TESTING=ON -DOPUS_BUILD_PROGRAMS=ON
- cmake --build build
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9d824cdc..4d2e1666 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -274,6 +274,7 @@ target_include_directories(
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/opus>
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/lpcnet/include
celt
silk)
@@ -351,6 +352,7 @@ endif()
add_sources_group(opus silk ${silk_headers} ${silk_sources})
add_sources_group(opus celt ${celt_headers} ${celt_sources})
+add_sources_group(opus lpcnet ${lpcnet_headers} ${lpcnet_sources})
if(OPUS_FIXED_POINT)
add_sources_group(opus silk ${silk_sources_fixed})
@@ -643,4 +645,14 @@ if(BUILD_TESTING AND NOT BUILD_SHARED_LIBS)
-DTEST_EXECUTABLE=$<TARGET_FILE:test_opus_encode>
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
-P "${PROJECT_SOURCE_DIR}/cmake/RunTest.cmake")
+
+ add_executable(test_opus_extensions ${test_opus_extensions_sources})
+ target_include_directories(test_opus_extensions
+ PRIVATE ${CMAKE_CURRENT_BINARY_DIR} celt)
+ target_link_libraries(test_opus_extensions PRIVATE opus)
+ target_compile_definitions(test_opus_extensions PRIVATE OPUS_BUILD)
+ add_test(NAME test_opus_extensions COMMAND ${CMAKE_COMMAND}
+ -DTEST_EXECUTABLE=$<TARGET_FILE:test_opus_extensions>
+ -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+ -P "${PROJECT_SOURCE_DIR}/cmake/RunTest.cmake")
endif()
diff --git a/Makefile.am b/Makefile.am
index 0d888d62..37dbe34f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -113,6 +113,7 @@ noinst_PROGRAMS = celt/tests/test_unit_cwrs32 \
tests/test_opus_api \
tests/test_opus_decode \
tests/test_opus_encode \
+ tests/test_opus_extensions \
tests/test_opus_padding \
tests/test_opus_projection \
trivial_example
@@ -129,6 +130,7 @@ TESTS = celt/tests/test_unit_cwrs32 \
tests/test_opus_api \
tests/test_opus_decode \
tests/test_opus_encode \
+ tests/test_opus_extensions \
tests/test_opus_padding \
tests/test_opus_projection
@@ -152,6 +154,9 @@ tests_test_opus_api_LDADD = libopus.la $(NE10_LIBS) $(LIBM)
tests_test_opus_encode_SOURCES = tests/test_opus_encode.c tests/opus_encode_regressions.c tests/test_opus_common.h
tests_test_opus_encode_LDADD = libopus.la $(NE10_LIBS) $(LIBM)
+tests_test_opus_extensions_SOURCES = tests/test_opus_extensions.c tests/test_opus_common.h
+tests_test_opus_extensions_LDADD = libopus.la $(NE10_LIBS) $(LIBM)
+
tests_test_opus_decode_SOURCES = tests/test_opus_decode.c tests/test_opus_common.h
tests_test_opus_decode_LDADD = libopus.la $(NE10_LIBS) $(LIBM)
diff --git a/Makefile.mips b/Makefile.mips
index e9bfc22e..8e8f837c 100644
--- a/Makefile.mips
+++ b/Makefile.mips
@@ -102,13 +102,16 @@ TESTOPUSDECODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSDECODE_SRCS_C))
TESTOPUSENCODE_SRCS_C = tests/test_opus_encode.c tests/opus_encode_regressions.c
TESTOPUSENCODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSENCODE_SRCS_C))
+TESTOPUSEXTENSIONS_SRCS_C = tests/test_opus_extensions.c
+TESTOPUSEXTENSIONS_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSEXTENSIONS_SRCS_C))
+
TESTOPUSPADDING_SRCS_C = tests/test_opus_padding.c
TESTOPUSPADDING_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSPADDING_SRCS_C))
OPUSCOMPARE_SRCS_C = src/opus_compare.c
OPUSCOMPARE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OPUSCOMPARE_SRCS_C))
-TESTS := test_opus_api test_opus_decode test_opus_encode test_opus_padding
+TESTS := test_opus_api test_opus_decode test_opus_encode test_opus_extensions test_opus_padding
# Rules
all: lib opus_demo opus_compare $(TESTS)
@@ -133,6 +136,9 @@ test_opus_decode$(EXESUFFIX): $(TESTOPUSDECODE_OBJS) $(TARGET)
test_opus_encode$(EXESUFFIX): $(TESTOPUSENCODE_OBJS) $(TARGET)
$(LINK.o.cmdline)
+test_opus_extensions$(EXESUFFIX): $(TESTOPUSEXTENSIONS_OBJS) $(TARGET)
+ $(LINK.o.cmdline)
+
test_opus_padding$(EXESUFFIX): $(TESTOPUSPADDING_OBJS) $(TARGET)
$(LINK.o.cmdline)
@@ -154,8 +160,10 @@ force:
clean:
rm -f opus_demo$(EXESUFFIX) opus_compare$(EXESUFFIX) $(TARGET) \
test_opus_api$(EXESUFFIX) test_opus_decode$(EXESUFFIX) \
- test_opus_encode$(EXESUFFIX) test_opus_padding$(EXESUFFIX) \
+ test_opus_encode$(EXESUFFIX) test_opus_extensions$(EXESUFFIX) \
+ test_opus_padding$(EXESUFFIX)
$(OBJS) $(OPUSDEMO_OBJS) $(OPUSCOMPARE_OBJS) $(TESTOPUSAPI_OBJS) \
- $(TESTOPUSDECODE_OBJS) $(TESTOPUSENCODE_OBJS) $(TESTOPUSPADDING_OBJS)
+ $(TESTOPUSDECODE_OBJS) $(TESTOPUSENCODE_OBJS) \
+ $(TESTOPUSEXTENSIONS_OBJS) $(TESTOPUSPADDING_OBJS)
.PHONY: all lib clean force check
diff --git a/Makefile.unix b/Makefile.unix
index 90a48f0c..5a09194f 100644
--- a/Makefile.unix
+++ b/Makefile.unix
@@ -100,13 +100,16 @@ TESTOPUSDECODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSDECODE_SRCS_C))
TESTOPUSENCODE_SRCS_C = tests/test_opus_encode.c tests/opus_encode_regressions.c
TESTOPUSENCODE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSENCODE_SRCS_C))
+TESTOPUSEXTENSIONS_SRCS_C = tests/test_opus_extensions.c
+TESTOPUSEXTENSIONS_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSEXTENSIONS_SRCS_C))
+
TESTOPUSPADDING_SRCS_C = tests/test_opus_padding.c
TESTOPUSPADDING_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(TESTOPUSPADDING_SRCS_C))
OPUSCOMPARE_SRCS_C = src/opus_compare.c
OPUSCOMPARE_OBJS := $(patsubst %.c,%$(OBJSUFFIX),$(OPUSCOMPARE_SRCS_C))
-TESTS := test_opus_api test_opus_decode test_opus_encode test_opus_padding
+TESTS := test_opus_api test_opus_decode test_opus_encode test_opus_extensions test_opus_padding
# Rules
all: lib opus_demo opus_compare $(TESTS)
@@ -131,6 +134,9 @@ test_opus_decode$(EXESUFFIX): $(TESTOPUSDECODE_OBJS) $(TARGET)
test_opus_encode$(EXESUFFIX): $(TESTOPUSENCODE_OBJS) $(TARGET)
$(LINK.o.cmdline)
+test_opus_extensions$(EXESUFFIX): $(TESTOPUSEXTENSIONS_OBJS) $(TARGET)
+ $(LINK.o.cmdline)
+
test_opus_padding$(EXESUFFIX): $(TESTOPUSPADDING_OBJS) $(TARGET)
$(LINK.o.cmdline)
@@ -152,8 +158,10 @@ force:
clean:
rm -f opus_demo$(EXESUFFIX) opus_compare$(EXESUFFIX) $(TARGET) \
test_opus_api$(EXESUFFIX) test_opus_decode$(EXESUFFIX) \
- test_opus_encode$(EXESUFFIX) test_opus_padding$(EXESUFFIX) \
+ test_opus_encode$(EXESUFFIX) test_opus_extensions$(EXESUFFIX) \
+ test_opus_padding$(EXESUFFIX)
$(OBJS) $(OPUSDEMO_OBJS) $(OPUSCOMPARE_OBJS) $(TESTOPUSAPI_OBJS) \
- $(TESTOPUSDECODE_OBJS) $(TESTOPUSENCODE_OBJS) $(TESTOPUSPADDING_OBJS)
+ $(TESTOPUSDECODE_OBJS) $(TESTOPUSENCODE_OBJS) \
+ $(TESTOPUSEXTENSIONS_OBJS) $(TESTOPUSPADDING_OBJS)
.PHONY: all lib clean force check
diff --git a/autogen.sh b/autogen.sh
index 7c079157..748909b9 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -9,6 +9,7 @@ set -e
srcdir=`dirname $0`
test -n "$srcdir" && cd "$srcdir"
+git submodule update --init
(cd lpcnet; ./download_model.sh 9daefbb)
echo "Updating build configuration files, please wait...."
diff --git a/cmake/OpusSources.cmake b/cmake/OpusSources.cmake
index b47f8c69..cf8b8748 100644
--- a/cmake/OpusSources.cmake
+++ b/cmake/OpusSources.cmake
@@ -37,12 +37,17 @@ get_opus_sources(CELT_SOURCES_ARM_NEON_INTR celt_sources.mk
celt_sources_arm_neon_intr)
get_opus_sources(CELT_SOURCES_ARM_NE10 celt_sources.mk celt_sources_arm_ne10)
+get_opus_sources(LPCNET_HEAD lpcnet_headers.mk lpcnet_headers)
+get_opus_sources(LPCNET_SOURCES lpcnet_sources.mk lpcnet_sources)
+
get_opus_sources(opus_demo_SOURCES Makefile.am opus_demo_sources)
get_opus_sources(opus_custom_demo_SOURCES Makefile.am opus_custom_demo_sources)
get_opus_sources(opus_compare_SOURCES Makefile.am opus_compare_sources)
get_opus_sources(tests_test_opus_api_SOURCES Makefile.am test_opus_api_sources)
get_opus_sources(tests_test_opus_encode_SOURCES Makefile.am
test_opus_encode_sources)
+get_opus_sources(tests_test_opus_extensions_SOURCES Makefile.am
+ test_opus_extensions_sources)
get_opus_sources(tests_test_opus_decode_SOURCES Makefile.am
test_opus_decode_sources)
get_opus_sources(tests_test_opus_padding_SOURCES Makefile.am
diff --git a/src/extensions.c b/src/extensions.c
index 1c286fcd..344f0923 100644
--- a/src/extensions.c
+++ b/src/extensions.c
@@ -99,15 +99,21 @@ opus_int32 opus_packet_extensions_count(const unsigned char *data, opus_int32 le
opus_int32 curr_len;
opus_int32 count=0;
const unsigned char *curr_data = data;
+
+ celt_assert(len >= 0);
+ celt_assert(data != NULL || len == 0);
+
curr_len = len;
while (curr_len > 0)
{
int id;
opus_int32 header_size;
id = *curr_data>>1;
+ curr_len = skip_extension(&curr_data, curr_len, &header_size);
+ if (curr_len < 0)
+ return OPUS_INVALID_PACKET;
if (id > 1)
count++;
- curr_len = skip_extension(&curr_data, curr_len, &header_size);
}
return count;
}
@@ -120,6 +126,11 @@ opus_int32 opus_packet_extensions_parse(const unsigned char *data, opus_int32 le
int curr_frame=0;
opus_int32 count=0;
+ celt_assert(len >= 0);
+ celt_assert(data != NULL || len == 0);
+ celt_assert(nb_extensions != NULL);
+ celt_assert(extensions != NULL || *nb_extensions == 0);
+
curr_data = data;
curr_len = len;
while (curr_len > 0)
@@ -181,10 +192,14 @@ opus_int32 opus_packet_extensions_generate(unsigned char *data, opus_int32 len,
int curr_frame = 0;
opus_int32 pos = 0;
opus_int32 written = 0;
+
+ celt_assert(len >= 0);
+ celt_assert(data != NULL || len == 0);
+
for (i=0;i<nb_extensions;i++)
{
max_frame = IMAX(max_frame, extensions[i].frame);
- if (extensions[i].id < 2)
+ if (extensions[i].id < 2 || extensions[i].id > 127)
return OPUS_BAD_ARG;
}
if (max_frame >= 48) return OPUS_BAD_ARG;
@@ -225,6 +240,8 @@ opus_int32 opus_packet_extensions_generate(unsigned char *data, opus_int32 len,
} else {
int last;
opus_int32 length_bytes;
+ if (extensions[i].len < 0)
+ return OPUS_BAD_ARG;
last = (written == nb_extensions - 1);
length_bytes = 1 + extensions[i].len/255;
if (last)
diff --git a/src/opus_private.h b/src/opus_private.h
index 4ba6e3d3..478ea1a6 100644
--- a/src/opus_private.h
+++ b/src/opus_private.h
@@ -214,6 +214,8 @@ opus_int32 opus_packet_extensions_parse(const unsigned char *data, opus_int32 le
opus_int32 opus_packet_extensions_generate(unsigned char *data, opus_int32 len, const opus_extension_data *extensions, int nb_extensions, int pad);
+opus_int32 opus_packet_extensions_count(const unsigned char *data, opus_int32 len);
+
opus_int32 opus_packet_pad_impl(unsigned char *data, opus_int32 len, opus_int32 new_len, int pad, const opus_extension_data *extensions, int nb_extensions);
#endif /* OPUS_PRIVATE_H */
diff --git a/tests/meson.build b/tests/meson.build
index 5f3ac9df..3246b660 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -3,6 +3,7 @@ opus_tests = [
['test_opus_api'],
['test_opus_decode', [], 60],
['test_opus_encode', 'opus_encode_regressions.c', 120],
+ ['test_opus_extensions'],
['test_opus_padding'],
['test_opus_projection'],
]
diff --git a/tests/test_opus_common.h b/tests/test_opus_common.h
index 5fb924f4..a03733a9 100644
--- a/tests/test_opus_common.h
+++ b/tests/test_opus_common.h
@@ -82,4 +82,5 @@ static OPUS_INLINE void _test_failed(const char *file, int line)
}
#define test_failed() _test_failed(__FILE__, __LINE__);
#define opus_test_assert(cond) {if (!(cond)) {test_failed();}}
+#define expect_true(cond, msg) {if (!(cond)) {fprintf(stderr, "FAIL - %s\n", msg); test_failed();}}
void regression_test(void);
diff --git a/tests/test_opus_extensions.c b/tests/test_opus_extensions.c
new file mode 100644
index 00000000..13e6b1d9
--- /dev/null
+++ b/tests/test_opus_extensions.c
@@ -0,0 +1,313 @@
+/* Copyright (c) 2023 Amazon
+ Written by Michael Klingbeil */
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+/* including sources directly to test internal APIs */
+#define CELT_C /* to make celt_assert work */
+#include "../src/extensions.c"
+#include "test_opus_common.h"
+
+void test_extensions_generate_success(void)
+{
+ static const opus_extension_data ext[] = {
+ {2, 0, (const unsigned char *)"a", 1},
+ {32, 10, (const unsigned char *)"DRED", 4},
+ {33, 1, (const unsigned char *)"NOT DRED", 8},
+ {3, 4, (const unsigned char *)NULL, 0}
+ };
+
+ int result;
+ unsigned char packet[32];
+ const unsigned char *p = packet;
+ result = opus_packet_extensions_generate(packet, 23+4, ext, 4, 1);
+ expect_true(result == 23+4, "expected length 23+4");
+
+ /* expect padding */
+ expect_true(p[0] == 1 && p[1] == 1 && p[2] == 1 && p[3] == 1, "expected padding");
+ p += 4;
+
+ /* extension ID=2 */
+ expect_true((p[0] >> 1) == 2, "expected extension id 2");
+ /* For extension IDs 1 through 31, L=0 means that no data follows the
+ extension, whereas L=1 means that exactly one byte of extension data follows. */
+ expect_true((p[0] & 0x01) == 1, "expected L-bit set");
+ /* content */
+ expect_true(p[1] == 'a', "expected extension content");
+ p += 2;
+
+ /* next byte should increment the frame count, ID=1, L=0 */
+ expect_true(p[0] == 0x02, "bad frame separator");
+ p += 1;
+ /* extension ID=33 */
+ expect_true((p[0] >> 1) == 33, "expected extension id 33");
+ /* For IDs 32 to 127, L=0 signals that the extension data takes up the
+ rest of the padding, and L=1 signals that a length indicator follows. */
+ expect_true((p[0] & 0x01) == 1, "expected L-bit set");
+ /* content */
+ expect_true(p[1] == ext[2].len, "expected length");
+ p += 2;
+ expect_true(0 == memcmp(p, ext[2].data, ext[2].len), "expected extension content");
+ p += ext[2].len;
+
+ /* advance to frame 4, increment by 3 */
+ /* next byte should increment the frame count, ID=1, L=1 */
+ expect_true(p[0] == 0x03, "bad frame separator");
+ expect_true(p[1] == 0x03, "bad frame increment");
+ p += 2;
+ /* extension ID=3 */
+ expect_true((p[0] >> 1) == 3, "expected extension id 3");
+ /* For extension IDs 1 through 31, L=0 means that no data follows the
+ extension, whereas L=1 means that exactly one byte of extension data follows. */
+ expect_true((p[0] & 0x01) == 0, "expected L-bit unset");
+ p += 1;
+
+ /* advance to frame 10, increment by 6 */
+ /* next byte should increment the frame count, ID=1, L=1 */
+ expect_true(p[0] == 0x03, "bad frame separator");
+ expect_true(p[1] == 0x06, "bad frame increment");
+ p += 2;
+ /* extension ID=32 */
+ expect_true((p[0] >> 1) == 32, "expected extension id 32");
+ /* For IDs 32 to 127, L=0 signals that the extension data takes up the
+ rest of the padding */
+ expect_true((p[0] & 0x01) == 0, "expected L-bit unset");
+ p += 1;
+ expect_true(0 == memcmp(p, ext[3].data, ext[3].len), "expected extension content");
+}
+
+void test_extensions_generate_zero(void)
+{
+ int result;
+ unsigned char packet[32];
+
+ /* zero length packet, zero extensions */
+ result = opus_packet_extensions_generate(packet, 0, NULL, 0, 1);
+ expect_true(result == 0, "expected length 0");
+}
+
+void test_extensions_generate_no_padding(void)
+{
+ static const opus_extension_data ext[] = {
+ {2, 0, (const unsigned char *)"a", 1},
+ {32, 10, (const unsigned char *)"DRED", 4},
+ {33, 1, (const unsigned char *)"NOT DRED", 8},
+ {3, 4, (const unsigned char *)NULL, 0}
+ };
+
+ int result;
+ unsigned char packet[32];
+ result = opus_packet_extensions_generate(packet, sizeof(packet), ext, 4, 0);
+ expect_true(result == 23, "expected length 23");
+}
+
+void test_extensions_generate_fail(void)
+{
+ static const opus_extension_data ext[] = {
+ {2, 0, (const unsigned char *)"a", 1},
+ {32, 10, (const unsigned char *)"DRED", 4},
+ {33, 1, (const unsigned char *)"NOT DRED", 8},
+ {3, 4, (const unsigned char *)NULL, 0}
+ };
+
+ int result;
+ unsigned char packet[100];
+
+ /* buffer too small */
+ result = opus_packet_extensions_generate(packet, 4, ext, 4, 1);
+ expect_true(result == OPUS_BUFFER_TOO_SMALL, "expected OPUS_BUFFER_TOO_SMALL");
+
+ /* invalid id */
+ {
+ static const opus_extension_data id_too_big[] = {
+ {256, 0, (const unsigned char *)"a", 1},
+ };
+ result = opus_packet_extensions_generate(packet, sizeof(packet), id_too_big, 1, 1);
+ expect_true(result == OPUS_BAD_ARG, "expected OPUS_BAD_ARG");
+ }
+
+ /* invalid id */
+ {
+ static const opus_extension_data id_too_small[] = {
+ {1, 0, (const unsigned char *)"a", 1},
+ };
+ result = opus_packet_extensions_generate(packet, sizeof(packet), id_too_small, 1, 1);
+ expect_true(result == OPUS_BAD_ARG, "expected OPUS_BAD_ARG");
+ }
+
+ /* frame index too big */
+ {
+ static const opus_extension_data frame_too_big[] = {
+ {33, 48, (const unsigned char *)"a", 1},
+ };
+ result = opus_packet_extensions_generate(packet, sizeof(packet), frame_too_big, 1, 1);
+ expect_true(result == OPUS_BAD_ARG, "expected OPUS_BAD_ARG");
+ }
+
+ /* size too big for extension IDs 1 through 31 */
+ {
+ static const opus_extension_data size_too_big[] = {
+ {2, 0, (const unsigned char *)"abcd", 4},
+ };
+ result = opus_packet_extensions_generate(packet, sizeof(packet), size_too_big, 1, 1);
+ expect_true(result == OPUS_BAD_ARG, "expected OPUS_BAD_ARG");
+ }
+
+ /* negative size for extension IDs 1 through 31 */
+ {
+ static const opus_extension_data neg_size[] = {
+ {2, 0, NULL, -4},
+ };
+ result = opus_packet_extensions_generate(packet, sizeof(packet), neg_size, 1, 1);
+ expect_true(result == OPUS_BAD_ARG, "expected OPUS_BAD_ARG");
+ }
+
+ /* negative size for extension IDs 32 through 127 */
+ {
+ static const opus_extension_data neg_size_33[] = {
+ {33, 0, NULL, -4},
+ };
+ result = opus_packet_extensions_generate(packet, sizeof(packet), neg_size_33, 1, 1);
+ expect_true(result == OPUS_BAD_ARG, "expected OPUS_BAD_ARG");
+ }
+}
+
+void test_extensions_parse_success(void)
+{
+ static const opus_extension_data ext[] = {
+ {2, 0, (const unsigned char *)"a", 1},
+ {32, 10, (const unsigned char *)"DRED", 4},
+ {33, 1, (const unsigned char *)"NOT DRED", 8},
+ {3, 4, (const unsigned char *)NULL, 0}
+ };
+ opus_extension_data ext_out[10];
+ int nb_ext;
+ int len, result;
+ unsigned char packet[32];
+
+ nb_ext = 10;
+ len = opus_packet_extensions_generate(packet, 32, ext, 4, 1);
+ expect_true(len == 32, "expected length 32");
+ result = opus_packet_extensions_count(packet, len);
+ expect_true(result == 4, "expected opus_packet_extensions_count 4");
+ result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext);
+ expect_true(nb_ext == 4, "expected 4 extensions");
+
+ expect_true(ext_out[0].id == 2, "expected id 2");
+ expect_true(ext_out[0].frame == 0, "expected frame 0");
+ expect_true(ext_out[0].len == 1, "expected len 1");
+ expect_true(0 == memcmp(ext_out[0].data, ext[0].data, 1), "expected data");
+
+ expect_true(ext_out[1].id == 33, "expected id 33");
+ expect_true(ext_out[1].frame == 1, "expected frame 1");
+ expect_true(ext_out[1].len == 8, "expected len 8");
+ expect_true(0 == memcmp(ext_out[1].data, ext[2].data, 8), "expected data");
+
+ expect_true(ext_out[2].id == 3, "expected id 3");
+ expect_true(ext_out[2].frame == 4, "expected frame 4");
+ expect_true(ext_out[2].len == 0, "expected len 0");
+
+ expect_true(ext_out[3].id == 32, "expected id 32");
+ expect_true(ext_out[3].frame == 10, "expected frame 10");
+ expect_true(ext_out[3].len == 4, "expected len 4");
+ expect_true(0 == memcmp(ext_out[3].data, ext[1].data, 4), "expected data");
+}
+
+void test_extensions_parse_zero(void)
+{
+ static const opus_extension_data ext[] = {
+ {32, 1, (const unsigned char *)"DRED", 4},
+ };
+ int nb_ext;
+ int len, result;
+ unsigned char packet[32];
+
+ len = opus_packet_extensions_generate(packet, 32, ext, 1, 1);
+ expect_true(len == 32, "expected length 32");
+
+ nb_ext = 0;
+ result = opus_packet_extensions_parse(packet, len, NULL, &nb_ext);
+ expect_true(result == OPUS_BUFFER_TOO_SMALL, "expected OPUS_BUFFER_TOO_SMALL");
+}
+
+void test_extensions_parse_fail(void)
+{
+ static const opus_extension_data ext[] = {
+ {2, 0, (const unsigned char *)"a", 1},
+ {33, 1, (const unsigned char *)"NOT DRED", 8},
+ {3, 4, (const unsigned char *)NULL, 0},
+ {32, 10, (const unsigned char *)"DRED", 4}
+ };
+ opus_extension_data ext_out[10];
+ int nb_ext;
+ int len, result;
+ unsigned char packet[32];
+
+ /* create invalid length */
+ len = opus_packet_extensions_generate(packet, sizeof(packet), ext, 4, 0);
+ packet[4] = 255;
+ nb_ext = 10;
+ result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext);
+ expect_true(result == OPUS_INVALID_PACKET, "expected OPUS_INVALID_PACKET");
+ result = opus_packet_extensions_count(packet, len);
+ expect_true(result == OPUS_INVALID_PACKET, "expected OPUS_INVALID_PACKET");
+
+ /* create invalid frame increment */
+ nb_ext = 10;
+ len = opus_packet_extensions_generate(packet, sizeof(packet), ext, 4, 0);
+ packet[14] = 255;
+ result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext);
+ expect_true(result == OPUS_INVALID_PACKET, "expected OPUS_INVALID_PACKET");
+ /* note, opus_packet_extensions_count does not read the invalid frame increment
+ and tells us that we have 4 extensions */
+ result = opus_packet_extensions_count(packet, len);
+ expect_true(result == 4, "expected opus_packet_extensions_count to return 4");
+
+ /* not enough space */
+ nb_ext = 1;
+ len = opus_packet_extensions_generate(packet, sizeof(packet), ext, 4, 0);
+ result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext);
+ expect_true(result == OPUS_BUFFER_TOO_SMALL, "expected OPUS_BUFFER_TOO_SMALL");
+}
+
+int main(void)
+{
+ test_extensions_generate_success();
+ test_extensions_generate_zero();
+ test_extensions_generate_no_padding();
+ test_extensions_generate_fail();
+ test_extensions_parse_success();
+ test_extensions_parse_zero();
+ test_extensions_parse_fail();
+ fprintf(stderr,"Tests completed successfully.\n");
+ return 0;
+}