blob: 218d83771da11726be656531df89d535b1434aee [file] [log] [blame]
Philipp Maier40def492017-12-16 03:42:15 +07001/*
2 * (C) 2017 by sysmocom - s.f.m.c. GmbH
3 * (C) 2017 by Philipp Maier <pmaier@sysmocom.de>
4 *
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
Philipp Maier40def492017-12-16 03:42:15 +070017 */
18
19#include <stdbool.h>
20#include <string.h>
21#include <stdint.h>
22#include <errno.h>
23
24#include <osmocom/core/bitvec.h>
25
26#include <osmocom/codec/gsm610_bits.h>
27#include <osmocom/codec/codec.h>
28#include <osmocom/codec/ecu.h>
29
30/* See also GSM 06.11, chapter 6 Example solution */
31#define GSM610_XMAXC_REDUCE 4
32#define GSM610_XMAXC_LEN 6
33
34/**
35 * Reduce the XMAXC field. When the XMAXC field reaches
36 * zero the function will return true.
37 */
38static bool reduce_xmaxcr(struct bitvec *frame_bitvec,
39 const unsigned int index)
40{
41 unsigned int field_index;
42 uint64_t field;
43
44 field_index = index;
45 field = bitvec_read_field(frame_bitvec, &field_index, GSM610_XMAXC_LEN);
46 if (field > GSM610_XMAXC_REDUCE)
47 field -= GSM610_XMAXC_REDUCE;
48 else
49 field = 0;
50
51 field_index = index;
52 bitvec_write_field(frame_bitvec, &field_index, field, GSM610_XMAXC_LEN);
53
54 return field == 0;
55}
56
57/**
58 * Reduce all XMAXC fields in the frame. When all XMAXC fields
59 * reach zero, then the function will return true.
60 */
61static bool reduce_xmaxcr_all(struct bitvec *frame_bitvec)
62{
Pau Espin Pedrol9bb966a2018-07-20 13:58:52 +020063 bool silent = true;
Philipp Maier40def492017-12-16 03:42:15 +070064
Pau Espin Pedrol9bb966a2018-07-20 13:58:52 +020065 silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC00);
66 silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC10);
67 silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC20);
68 silent &= reduce_xmaxcr(frame_bitvec, GSM610_RTP_XMAXC30);
Philipp Maier40def492017-12-16 03:42:15 +070069
70 return silent;
71}
72
73/* Use certain modifications to conceal the errors in a full rate frame */
74static int conceal_frame(uint8_t *frame)
75{
76 struct bitvec *frame_bitvec;
77 unsigned int len;
78 bool silent;
79 int rc = 0;
80
81 /* In case we already deal with a silent frame,
82 * there is nothing to, we just abort immediately */
83 if (osmo_fr_check_sid(frame, GSM_FR_BYTES))
84 return 0;
85
86 /* Attempt to allocate memory for bitvec */
87 frame_bitvec = bitvec_alloc(GSM_FR_BYTES, NULL);
88 if (!frame_bitvec)
89 return -ENOMEM;
90
91 /* Convert a frame to bitvec */
92 len = bitvec_unpack(frame_bitvec, frame);
93 if (len != GSM_FR_BYTES) {
94 rc = -EIO;
95 goto leave;
96 }
97
98 /* Fudge frame parameters */
99 silent = reduce_xmaxcr_all(frame_bitvec);
100
101 /* If we reached silence level, mute the frame
102 * completely, this also means that we can
103 * save the bitvec_pack operation */
104 if (silent) {
105 memset(frame, 0x00, GSM_FR_BYTES);
106 frame[0] = 0xd0;
107 goto leave;
108 }
109
110 /* Convert back to packed byte form */
111 len = bitvec_pack(frame_bitvec, frame);
112 if (len != GSM_FR_BYTES) {
113 rc = -EIO;
114 goto leave;
115 }
116
117leave:
118 bitvec_free(frame_bitvec);
119 return rc;
120}
121
Sylvain Munaut35f8ba02019-05-15 17:34:13 +0200122/*!
Philipp Maier40def492017-12-16 03:42:15 +0700123 * To be called when a good frame is received.
124 * This function will then create a backup of the frame
125 * and reset the internal state.
Sylvain Munaut35f8ba02019-05-15 17:34:13 +0200126 * \param[in] state The state object for the ECU
127 * \param[out] frame The valid frame (GSM_FR_BYTES bytes in RTP payload format)
Philipp Maier40def492017-12-16 03:42:15 +0700128 */
Harald Weltec144f3a2019-08-01 20:02:40 +0200129void osmo_ecu_fr_reset(struct osmo_ecu_fr_state *state, const uint8_t *frame)
Philipp Maier40def492017-12-16 03:42:15 +0700130{
131 state->subsequent_lost_frame = false;
132 memcpy(state->frame_backup, frame, GSM_FR_BYTES);
133}
134
Sylvain Munaut35f8ba02019-05-15 17:34:13 +0200135/*!
Philipp Maier40def492017-12-16 03:42:15 +0700136 * To be called when a bad frame is received.
137 * This function will then generate a replacement frame
138 * that can be used to conceal the dropout.
Sylvain Munaut35f8ba02019-05-15 17:34:13 +0200139 * \param[in] state The state object for the ECU
140 * \param[out] frame The buffer to fill with GSM_FR_BYTES of replacement frame
141 * \returns 0 if the frame was sucessfully filled
Philipp Maier40def492017-12-16 03:42:15 +0700142 */
143int osmo_ecu_fr_conceal(struct osmo_ecu_fr_state *state, uint8_t *frame)
144{
145 int rc;
146
147 /* For subsequent frames we run the error concealment
148 * functions on the backed up frame before we restore
149 * the backup */
150 if (state->subsequent_lost_frame) {
151 rc = conceal_frame(state->frame_backup);
152 if (rc)
153 return rc;
154 }
155
156 /* Restore the backed up frame and set flag in case
157 * we receive even more bad frames */
158 memcpy(frame, state->frame_backup, GSM_FR_BYTES);
159 state->subsequent_lost_frame = true;
160
161 return 0;
162}
Harald Welte750d8312019-08-01 20:05:05 +0200163
164/***********************************************************************
165 * Integration with ECU core
166 ***********************************************************************/
167
168static struct osmo_ecu_state *ecu_fr_init(void *ctx, enum osmo_ecu_codec codec)
169{
170 struct osmo_ecu_state *st;
171 size_t size = sizeof(*st) + sizeof(struct osmo_ecu_fr_state);
172
173 st = talloc_named_const(ctx, size, "ecu_state_FR");
174 if (!st)
175 return NULL;
176
177 memset(st, 0, size);
178 st->codec = codec;
179
180 return st;
181}
182
183static int ecu_fr_frame_in(struct osmo_ecu_state *st, bool bfi, const uint8_t *frame,
184 unsigned int frame_bytes)
185{
186 struct osmo_ecu_fr_state *fr = (struct osmo_ecu_fr_state *) &st->data;
187 if (bfi)
188 return 0;
189
190 osmo_ecu_fr_reset(fr, frame);
191 return 0;
192}
193
194static int ecu_fr_frame_out(struct osmo_ecu_state *st, uint8_t *frame_out)
195{
196 struct osmo_ecu_fr_state *fr = (struct osmo_ecu_fr_state *) &st->data;
197
198 if (osmo_ecu_fr_conceal(fr, frame_out) == 0)
199 return GSM_FR_BYTES;
200 else
201 return -1;
202}
203
204static const struct osmo_ecu_ops osmo_ecu_ops_fr = {
205 .init = ecu_fr_init,
206 .frame_in = ecu_fr_frame_in,
207 .frame_out = ecu_fr_frame_out,
208};
209
210static __attribute__((constructor)) void on_dso_load_ecu_fr(void)
211{
212 osmo_ecu_register(&osmo_ecu_ops_fr, OSMO_ECU_CODEC_FR);
213}