diff --git a/br-ext-chip-hisilicon/configs/hi3516cv6xx_ultimate_defconfig b/br-ext-chip-hisilicon/configs/hi3516cv6xx_ultimate_defconfig index f74f33918e..4d2d3d4152 100644 --- a/br-ext-chip-hisilicon/configs/hi3516cv6xx_ultimate_defconfig +++ b/br-ext-chip-hisilicon/configs/hi3516cv6xx_ultimate_defconfig @@ -80,6 +80,7 @@ BR2_PACKAGE_MAJESTIC_WEBUI=y BR2_PACKAGE_MOTORS=y BR2_PACKAGE_OPUS_OPENIPC=y BR2_PACKAGE_OPUS_OPENIPC_FIXED_POINT=y +BR2_PACKAGE_OPUS_OPENIPC_HISI_SHIM=y # protobuf-c is dynamically loaded by the cv6xx-flavored majestic binary # (libprotobuf-c.so.1 appears in DT_NEEDED but no other ultimate config # selects it — the vendor majestic build was linked against a private diff --git a/general/package/hisilicon-osdrv-hi3516cv6xx/hisilicon-osdrv-hi3516cv6xx.mk b/general/package/hisilicon-osdrv-hi3516cv6xx/hisilicon-osdrv-hi3516cv6xx.mk index 3c8a2bd2be..0b0d83299c 100644 --- a/general/package/hisilicon-osdrv-hi3516cv6xx/hisilicon-osdrv-hi3516cv6xx.mk +++ b/general/package/hisilicon-osdrv-hi3516cv6xx/hisilicon-osdrv-hi3516cv6xx.mk @@ -15,10 +15,15 @@ # V5 source mirror exists. The install list below is the transitive # NEEDED closure of the majestic binary against the vendor lib set # (computed via readelf -d), with libopus.so excluded because -# BR2_PACKAGE_OPUS_OPENIPC builds a source equivalent. The 13 libs not -# in the closure (libss_mpi_aibnr/bla/cipher/devstat/km/otp/smartae/ -# syskol/uvc, libss_bcd, libmbedtls_harden_adapt, libsvp_aicpu, -# libvqe_common) save ~660 KB and are intentionally omitted. +# BR2_PACKAGE_OPUS_OPENIPC + BR2_PACKAGE_OPUS_OPENIPC_HISI_SHIM build +# an open equivalent that covers both the standard opus_* API and the +# 6 HiSi-only ot_opus_* extensions used by libss_mpi_audio_adp.so (the +# shim source lives in general/package/opus-openipc/src/ot_opus_shim.c +# and is baked into libopus.so by the package's gated POST_EXTRACT +# hook). The 13 libs not in the closure (libss_mpi_aibnr/bla/cipher/ +# devstat/km/otp/smartae/syskol/uvc, libss_bcd, +# libmbedtls_harden_adapt, libsvp_aicpu, libvqe_common) save ~660 KB +# and are intentionally omitted. # ################################################################################ diff --git a/general/package/opus-openipc/Config.in b/general/package/opus-openipc/Config.in index ea81b5858f..97f84f68fd 100644 --- a/general/package/opus-openipc/Config.in +++ b/general/package/opus-openipc/Config.in @@ -23,4 +23,21 @@ config BR2_PACKAGE_OPUS_OPENIPC_FIXED_POINT Compile without floating point operations (for machines without a fast enough FPU). +config BR2_PACKAGE_OPUS_OPENIPC_HISI_SHIM + bool "bake in HiSilicon ot_opus_* shim" + help + Add an open-source implementation of the 6 ot_opus_* + wrapper functions (ot_opusenc_create/_destroy/_process_frame + + ot_opusdec_* counterparts) to libopus.so. + + The HiSilicon Hi3516CV6xx (V5) vendor MPP audio adapter + libss_mpi_audio_adp.so resolves these symbols at runtime + against the closed vendor libopus.so. Enabling this option + lets us drop the vendor blob and ship a single open libopus + that satisfies both the standard libopus.so.0 SONAME and the + vendor binary's unversioned libopus.so DT_NEEDED. + + Only needed on SoC families whose closed userspace expects + the ot_opus_* surface. Leave off otherwise. + endif diff --git a/general/package/opus-openipc/opus-openipc.mk b/general/package/opus-openipc/opus-openipc.mk index 38af40a66e..2a4f017380 100644 --- a/general/package/opus-openipc/opus-openipc.mk +++ b/general/package/opus-openipc/opus-openipc.mk @@ -14,6 +14,23 @@ OPUS_OPENIPC_INSTALL_STAGING = YES OPUS_OPENIPC_CFLAGS = $(TARGET_CFLAGS) +# Optional HiSilicon ot_opus_* shim. Drops src/ot_opus_shim.c into the +# tree, edits Makefile.am to add it to libopus_la_SOURCES, and turns on +# autoreconf so Makefile.in picks up the change. Only enabled when the +# closed vendor userspace on the target requires the ot_opus_* surface +# (Hi3516CV6xx). Off by default, so other SoCs neither pull host +# autotools deps nor pay the 6 dead symbols. +ifeq ($(BR2_PACKAGE_OPUS_OPENIPC_HISI_SHIM),y) +OPUS_OPENIPC_AUTORECONF = YES +define OPUS_OPENIPC_ADD_HISI_SHIM + mkdir -p $(@D)/src + cp $(OPUS_OPENIPC_PKGDIR)/src/ot_opus_shim.c $(@D)/src/ot_opus_shim.c + $(SED) 's|^libopus_la_SOURCES = \$$(CELT_SOURCES) \$$(SILK_SOURCES) \$$(OPUS_SOURCES)$$|& src/ot_opus_shim.c|' \ + $(@D)/Makefile.am +endef +OPUS_OPENIPC_POST_EXTRACT_HOOKS += OPUS_OPENIPC_ADD_HISI_SHIM +endif + ifeq ($(BR2_TOOLCHAIN_HAS_GCC_BUG_85180),y) OPUS_OPENIPC_CFLAGS += -O0 endif diff --git a/general/package/opus-openipc/src/ot_opus_shim.c b/general/package/opus-openipc/src/ot_opus_shim.c new file mode 100644 index 0000000000..36942f9cde --- /dev/null +++ b/general/package/opus-openipc/src/ot_opus_shim.c @@ -0,0 +1,159 @@ +/* + * HiSilicon ot_opus_* shim implementations. + * + * The Hi3516CV6xx (V5) vendor MPP audio adapter libss_mpi_audio_adp.so + * is a closed binary that calls 6 ot_opus_* wrapper functions; vendor + * ships their definitions only inside its prebuilt libopus.so, with no + * accompanying source. Mirroring the wrapper here against upstream + * Opus lets us drop the vendor blob and ship a single open libopus.so + * that satisfies both the standard `libopus.so.0` SONAME path and the + * vendor binary's unversioned `libopus.so` DT_NEEDED entry. + * + * The 6 entry points, their signatures and their error codes are + * derived from the public headers (out/include/ot_opusenc.h, + * out/include/ot_opusdec.h) and from the consumer source + * (mpp/cbb/audio/adp/src/ot_audio_opus_adp.c) shipped in HiSilicon's + * Hi3516CV610_SDK_V1.0.2.0. + */ + +#include +#include + +/* Opus builds with -fvisibility=hidden — force the wrappers public. */ +#define OT_OPUS_EXPORT __attribute__((visibility("default"))) + +enum { + OT_ERR_OPUSENC_NONE = 0, + OT_ERR_OPUSENC_NULL_POINTER = -1, + OT_ERR_OPUSENC_INVALID_PAYLOAD_LENGTH = -2, + OT_ERR_OPUSENC_ENCODE_FAILED = -3, +}; + +enum { + OT_ERR_OPUSDEC_NONE = 0, + OT_ERR_OPUSDEC_NOT_ENOUGH_DATA = -1, + OT_ERR_OPUSDEC_NULL_POINTER = -2, + OT_ERR_OPUSDEC_INVALID_PAYLOAD_LENGTH = -3, + OT_ERR_OPUSDEC_RANGE_MISMATCH = -4, + OT_ERR_OPUSDEC_DECODE_FAILED = -5, +}; + +#define OT_OPUSENC_MAX_PACKET 1500 +#define OT_OPUSDEC_MAX_OUT_SAMPLES 2880 + +typedef struct { + int sample_rate; + int channels; + int bit_rate; + int app; +} ot_opusenc_config; + +typedef struct { + int sample_rate; + int channels; +} ot_opusdec_config; + +OT_OPUS_EXPORT void *ot_opusenc_create(const ot_opusenc_config *config) +{ + int err = 0; + OpusEncoder *enc; + + if (config == NULL) { + return NULL; + } + enc = opus_encoder_create(config->sample_rate, config->channels, + config->app, &err); + if (enc == NULL || err != OPUS_OK) { + if (enc != NULL) { + opus_encoder_destroy(enc); + } + return NULL; + } + if (opus_encoder_ctl(enc, OPUS_SET_BITRATE(config->bit_rate)) != OPUS_OK) { + opus_encoder_destroy(enc); + return NULL; + } + return enc; +} + +OT_OPUS_EXPORT void ot_opusenc_destroy(void *encoder) +{ + if (encoder != NULL) { + opus_encoder_destroy((OpusEncoder *)encoder); + } +} + +OT_OPUS_EXPORT int ot_opusenc_process_frame(void *encoder, short *in_buf, int samples, + unsigned char *out_buf, unsigned int *out_valid_len) +{ + int n; + + if (encoder == NULL || in_buf == NULL || out_buf == NULL || + out_valid_len == NULL) { + return OT_ERR_OPUSENC_NULL_POINTER; + } + n = opus_encode((OpusEncoder *)encoder, in_buf, samples, out_buf, + OT_OPUSENC_MAX_PACKET); + if (n <= 0) { + return OT_ERR_OPUSENC_ENCODE_FAILED; + } + *out_valid_len = (unsigned int)n; + return OT_ERR_OPUSENC_NONE; +} + +OT_OPUS_EXPORT void *ot_opusdec_create(const ot_opusdec_config *config) +{ + int err = 0; + OpusDecoder *dec; + + if (config == NULL) { + return NULL; + } + dec = opus_decoder_create(config->sample_rate, config->channels, &err); + if (dec == NULL || err != OPUS_OK) { + if (dec != NULL) { + opus_decoder_destroy(dec); + } + return NULL; + } + return dec; +} + +OT_OPUS_EXPORT void ot_opusdec_destroy(void *decoder) +{ + if (decoder != NULL) { + opus_decoder_destroy((OpusDecoder *)decoder); + } +} + +/* + * The caller (ot_audio_opus_adp.c) passes one self-contained Opus packet + * per call and uses the (in_buf, in_left_byte) update to learn how many + * bytes were consumed. We treat the whole input slice as a single packet + * and report it fully consumed on success. + */ +OT_OPUS_EXPORT int ot_opusdec_process_frame(void *decoder, unsigned char **in_buf, + int *in_left_byte, short *out_buf, + unsigned int *out_bytes_per_chn) +{ + int samples; + + if (decoder == NULL || in_buf == NULL || *in_buf == NULL || + in_left_byte == NULL || out_buf == NULL || out_bytes_per_chn == NULL) { + return OT_ERR_OPUSDEC_NULL_POINTER; + } + if (*in_left_byte <= 0) { + return OT_ERR_OPUSDEC_NOT_ENOUGH_DATA; + } + + samples = opus_decode((OpusDecoder *)decoder, *in_buf, *in_left_byte, + out_buf, OT_OPUSDEC_MAX_OUT_SAMPLES, 0); + if (samples < 0) { + return OT_ERR_OPUSDEC_DECODE_FAILED; + } + + *out_bytes_per_chn = (unsigned int)(samples * (int)sizeof(short)); + *in_buf += *in_left_byte; + *in_left_byte = 0; + return OT_ERR_OPUSDEC_NONE; +}