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( $ 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=$ -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=$ + -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 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 +#include +#include +/* 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; +}