libosmocore/src/codec/ecu.c

129 lines
4.2 KiB
C
Raw Permalink Normal View History

/* Core infrastructure for ECU implementations */
/* (C) 2019 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
*/
/* As the developer and copyright holder of the related code, I hereby
* state that any ECU implementation using 'struct osmo_ecu_ops' and
* registering with the 'osmo_ecu_register()' function shall not be
* considered as a derivative work under any applicable copyright law;
* the copyleft terms of GPLv2 shall hence not apply to any such ECU
* implementation.
*
* The intent of the above exception is to allow anyone to combine third
* party Error Concealment Unit implementations with libosmocodec.
* including but not limited to such published by ETSI.
*
* -- Harald Welte <laforge@gnumonks.org> on August 1, 2019.
*/
#include <string.h>
#include <errno.h>
#include <osmocom/codec/ecu.h>
#include <osmocom/core/talloc.h>
static const struct osmo_ecu_ops *g_ecu_ops[_NUM_OSMO_ECU_CODECS];
/***********************************************************************
* high-level API for users
***********************************************************************/
/*! initialize an ECU instance for given codec.
* \param[in] ctx talloc context from which to allocate
* \param[in] codec codec for which to initialize/create ECU */
struct osmo_ecu_state *osmo_ecu_init(void *ctx, enum osmo_ecu_codec codec)
{
if (codec >= ARRAY_SIZE(g_ecu_ops))
return NULL;
if (!g_ecu_ops[codec] || !g_ecu_ops[codec]->init)
return NULL;
return g_ecu_ops[codec]->init(ctx, codec);
}
/*! destroy an ECU instance */
void osmo_ecu_destroy(struct osmo_ecu_state *st)
{
if (st->codec >= ARRAY_SIZE(g_ecu_ops))
return;
if (!g_ecu_ops[st->codec])
return;
if (!g_ecu_ops[st->codec]->destroy)
talloc_free(st);
else
g_ecu_ops[st->codec]->destroy(st);
}
/*! process a received frame a substitute/erroneous frame.
* \param[in] st ECU state/instance on which to operate
* \param[in] bfi Bad Frame Indication
* \param[in] frame received codec frame to be processed
* \param[in] frame_bytes number of bytes available in frame */
int osmo_ecu_frame_in(struct osmo_ecu_state *st, bool bfi,
const uint8_t *frame, unsigned int frame_bytes)
{
if (st->codec >= ARRAY_SIZE(g_ecu_ops))
return -EINVAL;
if (!g_ecu_ops[st->codec])
return -EBUSY;
return g_ecu_ops[st->codec]->frame_in(st, bfi, frame, frame_bytes);
}
/*! generate output data for a substitute/erroneous frame.
* \param[in] st ECU state/instance on which to operate
* \param[out] frame_out buffer for generated output frame
* \return number of bytes written to frame_out; negative on error */
int osmo_ecu_frame_out(struct osmo_ecu_state *st, uint8_t *frame_out)
{
if (st->codec >= ARRAY_SIZE(g_ecu_ops))
return -EINVAL;
if (!g_ecu_ops[st->codec])
return -EBUSY;
return g_ecu_ops[st->codec]->frame_out(st, frame_out);
}
ecu: add is_dtx_pause() method The ECU API of libosmocodec is unfortunately a peculiar non-3GPP entity: an ECU by itself, severed from Rx DTX handler functions, which is a logical/conceptual function with no place in the standard 3GPP architecture. The closest thing that exists in the standard architecture is the TFO spec (TS 28.062 section C.3.2.1.1) calling for an ECU application, but not comfort noise generation, in the case of destination leg doing DTXd - but even then it is not a totally "pure" ECU like libosmocodec API, it is an ECU plus a SID preener, and the SID preening transform is not possible within the constraints of existing libosmocodec ECU API. Hence truly correct handling of corner cases, particularly invalid SID, is sadly impossible in the current libosmocodec ECU framework. The only current user of this API is the UL path in osmo-bts-trx; however, as described in OS#6040, we would like to move that ECU call from osmo-bts-trx model-specific code to the common layer of osmo-bts. The current osmo-bts-trx incarnation avoids the SID handling problem by suppressing the call to ECU frame_out() after any SID (valid or invalid) was received on the air, thus pausing the RTP stream instead of emitting ECU output during DTXu pauses. We would like to retain the same behavior when we move this ECU call to the common layer, into its proper place _after_ the link quality check in l1sap - but the current method of flagging post-SID state in osmo-bts-trx will no longer work on the other side of that link quality check. As a workaround, have the ECU remember via a separate Boolean flag whether it is in post-SID state or not (was the most recent frame_in() any kind of SID or not), and provide is_dtx_pause() method to retrieve this flag - the relocated ECU call in osmo-bts UL path will use this new is_dtx_pause() method call to decide if it should call frame_out() or switch to pausing RTP output. Related: OS#6040 Change-Id: I3857be84bba12aaca0c2cca91458b7e13c5a642a
2023-06-23 18:42:11 +00:00
/*! check if the current state of this ECU is a DTX pause.
* \param[in] st ECU state/instance on which to operate
* \return true if DTX pause, false otherwise */
bool osmo_ecu_is_dtx_pause(struct osmo_ecu_state *st)
{
if (st->codec >= ARRAY_SIZE(g_ecu_ops))
return false;
if (!g_ecu_ops[st->codec])
return false;
if (!g_ecu_ops[st->codec]->is_dtx_pause)
return false;
return g_ecu_ops[st->codec]->is_dtx_pause(st);
}
/***********************************************************************
* low-level API for ECU implementations
***********************************************************************/
/*! register an ECU implementation for a given codec */
int osmo_ecu_register(const struct osmo_ecu_ops *ops, enum osmo_ecu_codec codec)
{
if (codec >= ARRAY_SIZE(g_ecu_ops))
return -EINVAL;
if (g_ecu_ops[codec])
return -EBUSY;
g_ecu_ops[codec] = ops;
return 0;
}