Mercurial > hg > audiostuff
comparison spandsp-0.0.6pre17/src/t38_non_ecm_buffer.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
| author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
|---|---|
| date | Fri, 25 Jun 2010 15:50:58 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 3:c6c5a16ce2f2 | 4:26cd8f1ef0b1 |
|---|---|
| 1 /* | |
| 2 * SpanDSP - a series of DSP components for telephony | |
| 3 * | |
| 4 * t38_non_ecm_buffer.c - A rate adapting buffer for T.38 non-ECM image | |
| 5 * and TCF data | |
| 6 * | |
| 7 * Written by Steve Underwood <steveu@coppice.org> | |
| 8 * | |
| 9 * Copyright (C) 2005, 2006, 2007, 2008 Steve Underwood | |
| 10 * | |
| 11 * All rights reserved. | |
| 12 * | |
| 13 * This program is free software; you can redistribute it and/or modify | |
| 14 * it under the terms of the GNU Lesser General Public License version 2.1, | |
| 15 * as published by the Free Software Foundation. | |
| 16 * | |
| 17 * This program is distributed in the hope that it will be useful, | |
| 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 20 * GNU Lesser General Public License for more details. | |
| 21 * | |
| 22 * You should have received a copy of the GNU Lesser General Public | |
| 23 * License along with this program; if not, write to the Free Software | |
| 24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 25 * | |
| 26 * $Id: t38_non_ecm_buffer.c,v 1.9.4.1 2009/12/19 06:43:28 steveu Exp $ | |
| 27 */ | |
| 28 | |
| 29 /*! \file */ | |
| 30 | |
| 31 #if defined(HAVE_CONFIG_H) | |
| 32 #include "config.h" | |
| 33 #endif | |
| 34 | |
| 35 #include <inttypes.h> | |
| 36 #include <stdlib.h> | |
| 37 #include <stdio.h> | |
| 38 #include <fcntl.h> | |
| 39 #include <time.h> | |
| 40 #include <string.h> | |
| 41 #if defined(HAVE_TGMATH_H) | |
| 42 #include <tgmath.h> | |
| 43 #endif | |
| 44 #if defined(HAVE_MATH_H) | |
| 45 #include <math.h> | |
| 46 #endif | |
| 47 #include "floating_fudge.h" | |
| 48 #include <assert.h> | |
| 49 | |
| 50 #include "spandsp/telephony.h" | |
| 51 #include "spandsp/logging.h" | |
| 52 #include "spandsp/queue.h" | |
| 53 #include "spandsp/dc_restore.h" | |
| 54 #include "spandsp/bit_operations.h" | |
| 55 #include "spandsp/async.h" | |
| 56 #include "spandsp/t38_non_ecm_buffer.h" | |
| 57 | |
| 58 #include "spandsp/private/t38_non_ecm_buffer.h" | |
| 59 | |
| 60 /* Phases */ | |
| 61 enum | |
| 62 { | |
| 63 TCF_AT_INITIAL_ALL_ONES = 0, | |
| 64 TCF_AT_ALL_ZEROS = 1, | |
| 65 IMAGE_WAITING_FOR_FIRST_EOL = 2, | |
| 66 IMAGE_IN_PROGRESS = 3 | |
| 67 }; | |
| 68 | |
| 69 static void restart_buffer(t38_non_ecm_buffer_state_t *s) | |
| 70 { | |
| 71 /* This should be called when draining the buffer is complete, which should | |
| 72 occur before any fresh data can possibly arrive to begin refilling it. */ | |
| 73 s->octet = 0xFF; | |
| 74 s->flow_control_fill_octet = 0xFF; | |
| 75 s->input_phase = (s->image_data_mode) ? IMAGE_WAITING_FOR_FIRST_EOL : TCF_AT_INITIAL_ALL_ONES; | |
| 76 s->bit_stream = 0xFFFF; | |
| 77 s->out_ptr = 0; | |
| 78 s->in_ptr = 0; | |
| 79 s->latest_eol_ptr = 0; | |
| 80 s->data_finished = FALSE; | |
| 81 } | |
| 82 /*- End of function --------------------------------------------------------*/ | |
| 83 | |
| 84 SPAN_DECLARE_NONSTD(int) t38_non_ecm_buffer_get_bit(void *user_data) | |
| 85 { | |
| 86 t38_non_ecm_buffer_state_t *s; | |
| 87 int bit; | |
| 88 | |
| 89 s = (t38_non_ecm_buffer_state_t *) user_data; | |
| 90 | |
| 91 if (s->bit_no <= 0) | |
| 92 { | |
| 93 /* We need another byte */ | |
| 94 if (s->out_ptr != s->latest_eol_ptr) | |
| 95 { | |
| 96 s->octet = s->data[s->out_ptr]; | |
| 97 s->out_ptr = (s->out_ptr + 1) & (T38_NON_ECM_TX_BUF_LEN - 1); | |
| 98 } | |
| 99 else | |
| 100 { | |
| 101 if (s->data_finished) | |
| 102 { | |
| 103 /* The queue is empty, and we have received the end of data signal. This must | |
| 104 really be the end to transmission. */ | |
| 105 restart_buffer(s); | |
| 106 return SIG_STATUS_END_OF_DATA; | |
| 107 } | |
| 108 /* The queue is blocked, but this does not appear to be the end of the data. Idle with | |
| 109 fill octets, which should be safe at this point. */ | |
| 110 s->octet = s->flow_control_fill_octet; | |
| 111 s->flow_control_fill_octets++; | |
| 112 } | |
| 113 s->out_octets++; | |
| 114 s->bit_no = 8; | |
| 115 } | |
| 116 s->bit_no--; | |
| 117 bit = (s->octet >> 7) & 1; | |
| 118 s->octet <<= 1; | |
| 119 return bit; | |
| 120 } | |
| 121 /*- End of function --------------------------------------------------------*/ | |
| 122 | |
| 123 SPAN_DECLARE(void) t38_non_ecm_buffer_push(t38_non_ecm_buffer_state_t *s) | |
| 124 { | |
| 125 /* Don't flow control the data any more. Just push out the remainder of the data | |
| 126 in the buffer as fast as we can, and shut down. */ | |
| 127 s->latest_eol_ptr = s->in_ptr; | |
| 128 s->data_finished = TRUE; | |
| 129 } | |
| 130 /*- End of function --------------------------------------------------------*/ | |
| 131 | |
| 132 SPAN_DECLARE(void) t38_non_ecm_buffer_inject(t38_non_ecm_buffer_state_t *s, const uint8_t *buf, int len) | |
| 133 { | |
| 134 int i; | |
| 135 int upper; | |
| 136 int lower; | |
| 137 | |
| 138 /* TCF consists of: | |
| 139 - zero or more ones, followed by | |
| 140 - about 1.5s of zeros | |
| 141 There may be a little junk at the end, as the modem shuts down. | |
| 142 | |
| 143 We can stuff with extra ones in the initial period of all ones, and we can stuff with extra | |
| 144 zeros once the zeros start. The thing we need to be wary about is the odd zero bit in the | |
| 145 midst of the ones, due to a bit error. */ | |
| 146 | |
| 147 /* Non-ECM image data consists of: | |
| 148 - zero or more ones, followed by | |
| 149 - zero or more zeros, followed by | |
| 150 - an EOL (end of line), which marks the start of the image, followed by | |
| 151 - a succession of data rows, with an EOL at the end of each, followed by | |
| 152 - an RTC (return to control) | |
| 153 There may be a little junk at the end, as the modem shuts down. | |
| 154 | |
| 155 An EOL 11 zeros followed by a one in a T.4 1D image or 11 zeros followed by a one followed | |
| 156 by a one or a zero in a T.4 2D image. An RTC consists of 6 EOLs in succession, with no | |
| 157 pixel data between them. | |
| 158 | |
| 159 We can stuff with ones until we get the first EOL into our buffer, then we can stuff with | |
| 160 zeros in front of each EOL at any point up the the RTC. We should not pad between the EOLs | |
| 161 which make up the RTC. Most FAX machines don't care about this, but a few will not recognise | |
| 162 the RTC if here is padding between the EOLs. | |
| 163 | |
| 164 We need to buffer whole rows before we output their beginning, so there is no possibility | |
| 165 of underflow mid-row. */ | |
| 166 | |
| 167 /* FoIP has latency issues, because of the fairly tight timeouts in the T.30 spec. We must | |
| 168 ensure our buffering does everything needed to avoid underflows, and to meet the minimum | |
| 169 row length requirements imposed by many mechanical FAX machines. We cannot, however, | |
| 170 afford to bulk up the data, by sending superfluous bytes. The resulting loop delay could | |
| 171 provoke an erroneous timeout of the acknowledgement signal. */ | |
| 172 | |
| 173 i = 0; | |
| 174 switch (s->input_phase) | |
| 175 { | |
| 176 case TCF_AT_INITIAL_ALL_ONES: | |
| 177 /* Dump initial 0xFF bytes. We will add enough of our own to makes things flow | |
| 178 smoothly. If we don't strip these off, we might end up delaying the start of | |
| 179 forwarding by a substantial amount, as we could end up with a large block of 0xFF | |
| 180 bytes before the real data begins. This is especially true with PC FAX | |
| 181 systems. This test is very simplistic, as bit errors could confuse it. */ | |
| 182 for ( ; i < len; i++) | |
| 183 { | |
| 184 if (buf[i] != 0xFF) | |
| 185 { | |
| 186 s->input_phase = TCF_AT_ALL_ZEROS; | |
| 187 s->flow_control_fill_octet = 0x00; | |
| 188 break; | |
| 189 } | |
| 190 } | |
| 191 /* Fall through */ | |
| 192 case TCF_AT_ALL_ZEROS: | |
| 193 for ( ; i < len; i++) | |
| 194 { | |
| 195 s->data[s->in_ptr] = buf[i]; | |
| 196 s->latest_eol_ptr = s->in_ptr; | |
| 197 /* TODO: We can't buffer overflow, since we wrap around. However, the tail could | |
| 198 overwrite itself if things fall badly behind. */ | |
| 199 s->in_ptr = (s->in_ptr + 1) & (T38_NON_ECM_TX_BUF_LEN - 1); | |
| 200 s->in_octets++; | |
| 201 } | |
| 202 break; | |
| 203 case IMAGE_WAITING_FOR_FIRST_EOL: | |
| 204 /* Dump anything up to the first EOL. Let the output side stuff with 0xFF bytes while waiting | |
| 205 for that first EOL. What occurs before the first EOL is expected to be a period of all ones | |
| 206 and then a period of all zeros. We really don't care what junk might be there. By definition, | |
| 207 the image only starts at the first EOL. */ | |
| 208 for ( ; i < len; i++) | |
| 209 { | |
| 210 if (buf[i]) | |
| 211 { | |
| 212 /* There might be an EOL here. Look for at least 11 zeros, followed by a one, split | |
| 213 between two octets. Between those two octets we can insert numerous zero octets | |
| 214 as a means of flow control. Note that we stuff in blocks of 8 bits, and not at | |
| 215 the minimal level. */ | |
| 216 /* Or'ing with 0x800 here is to avoid zero words looking like they have -1 | |
| 217 trailing zeros */ | |
| 218 upper = bottom_bit(s->bit_stream | 0x800); | |
| 219 lower = top_bit(buf[i]); | |
| 220 if ((upper - lower) > (11 - 8)) | |
| 221 { | |
| 222 /* This is an EOL - our first row is beginning. */ | |
| 223 s->input_phase = IMAGE_IN_PROGRESS; | |
| 224 /* Start a new row */ | |
| 225 s->row_bits = lower - 8; | |
| 226 s->latest_eol_ptr = s->in_ptr; | |
| 227 s->flow_control_fill_octet = 0x00; | |
| 228 | |
| 229 /* If we push out two bytes of zero, and our latest non-zero byte | |
| 230 we should definitely form a proper EOL to begin things, with a | |
| 231 few harmless extra zero bits at the front. */ | |
| 232 s->data[s->in_ptr] = 0x00; | |
| 233 s->in_ptr = (s->in_ptr + 1) & (T38_NON_ECM_TX_BUF_LEN - 1); | |
| 234 s->data[s->in_ptr] = 0x00; | |
| 235 s->in_ptr = (s->in_ptr + 1) & (T38_NON_ECM_TX_BUF_LEN - 1); | |
| 236 s->data[s->in_ptr] = buf[i]; | |
| 237 s->in_ptr = (s->in_ptr + 1) & (T38_NON_ECM_TX_BUF_LEN - 1); | |
| 238 s->in_octets += 3; | |
| 239 s->bit_stream = (s->bit_stream << 8) | buf[i]; | |
| 240 i++; | |
| 241 break; | |
| 242 } | |
| 243 } | |
| 244 s->bit_stream = (s->bit_stream << 8) | buf[i]; | |
| 245 } | |
| 246 if (i >= len) | |
| 247 break; | |
| 248 /* Fall through */ | |
| 249 case IMAGE_IN_PROGRESS: | |
| 250 /* Now we have seen an EOL, we can stuff with zeros just in front of that EOL, or any | |
| 251 subsequent EOL that does not immediately follow a previous EOL (i.e. a candidate RTC). | |
| 252 We need to track our way through the image data, allowing the output side to only send | |
| 253 up to the last EOL. This prevents the possibility of underflow mid-row, where we cannot | |
| 254 safely stuff anything in the bit stream. */ | |
| 255 for ( ; i < len; i++) | |
| 256 { | |
| 257 if (buf[i]) | |
| 258 { | |
| 259 /* There might be an EOL here. Look for at least 11 zeros, followed by a one, split | |
| 260 between two octets. Between those two octets we can insert numerous zero octets | |
| 261 as a means of flow control. Note that we stuff in blocks of 8 bits, and not at | |
| 262 the minimal level. */ | |
| 263 /* Or'ing with 0x800 here is to avoid zero words looking like they have -1 | |
| 264 trailing zeros */ | |
| 265 upper = bottom_bit(s->bit_stream | 0x800); | |
| 266 lower = top_bit(buf[i]); | |
| 267 if ((upper - lower) > (11 - 8)) | |
| 268 { | |
| 269 /* This is an EOL. */ | |
| 270 s->row_bits += (8 - lower); | |
| 271 /* Make sure we don't stretch back to back EOLs, as that could spoil the RTC. | |
| 272 This is a slightly crude check, as we don't know if we are processing a T.4 1D | |
| 273 or T.4 2D image. Accepting 12 or 12 bits apart as meaning back to back is fine, | |
| 274 as no 1D image row could be 1 bit long. */ | |
| 275 if (s->row_bits < 12 || s->row_bits > 13) | |
| 276 { | |
| 277 /* If the row is too short, extend it in chunks of a whole byte. */ | |
| 278 /* TODO: extend by the precise amount we should, instead of this | |
| 279 rough approach. */ | |
| 280 while (s->row_bits < s->min_bits_per_row) | |
| 281 { | |
| 282 s->min_row_bits_fill_octets++; | |
| 283 s->data[s->in_ptr] = 0; | |
| 284 s->row_bits += 8; | |
| 285 /* TODO: We can't buffer overflow, since we wrap around. However, | |
| 286 the tail could overwrite itself if things fall badly behind. */ | |
| 287 s->in_ptr = (s->in_ptr + 1) & (T38_NON_ECM_TX_BUF_LEN - 1); | |
| 288 } | |
| 289 /* This is now the limit for the output side, before it starts | |
| 290 stuffing. */ | |
| 291 s->latest_eol_ptr = s->in_ptr; | |
| 292 } | |
| 293 /* Start a new row */ | |
| 294 s->row_bits = lower - 8; | |
| 295 s->in_rows++; | |
| 296 } | |
| 297 } | |
| 298 s->bit_stream = (s->bit_stream << 8) | buf[i]; | |
| 299 s->data[s->in_ptr] = buf[i]; | |
| 300 s->row_bits += 8; | |
| 301 /* TODO: We can't buffer overflow, since we wrap around. However, the tail could overwrite | |
| 302 itself if things fall badly behind. */ | |
| 303 s->in_ptr = (s->in_ptr + 1) & (T38_NON_ECM_TX_BUF_LEN - 1); | |
| 304 s->in_octets++; | |
| 305 } | |
| 306 break; | |
| 307 } | |
| 308 } | |
| 309 /*- End of function --------------------------------------------------------*/ | |
| 310 | |
| 311 SPAN_DECLARE(void) t38_non_ecm_buffer_report_input_status(t38_non_ecm_buffer_state_t *s, logging_state_t *logging) | |
| 312 { | |
| 313 if (s->in_octets || s->min_row_bits_fill_octets) | |
| 314 { | |
| 315 span_log(logging, | |
| 316 SPAN_LOG_FLOW, | |
| 317 "%d+%d incoming non-ECM octets, %d rows.\n", | |
| 318 s->in_octets, | |
| 319 s->min_row_bits_fill_octets, | |
| 320 s->in_rows); | |
| 321 s->in_octets = 0; | |
| 322 s->in_rows = 0; | |
| 323 s->min_row_bits_fill_octets = 0; | |
| 324 } | |
| 325 } | |
| 326 /*- End of function --------------------------------------------------------*/ | |
| 327 | |
| 328 SPAN_DECLARE(void) t38_non_ecm_buffer_report_output_status(t38_non_ecm_buffer_state_t *s, logging_state_t *logging) | |
| 329 { | |
| 330 if (s->out_octets || s->flow_control_fill_octets) | |
| 331 { | |
| 332 span_log(logging, | |
| 333 SPAN_LOG_FLOW, | |
| 334 "%d+%d outgoing non-ECM octets, %d rows.\n", | |
| 335 s->out_octets - s->flow_control_fill_octets, | |
| 336 s->flow_control_fill_octets, | |
| 337 s->out_rows); | |
| 338 s->out_octets = 0; | |
| 339 s->out_rows = 0; | |
| 340 s->flow_control_fill_octets = 0; | |
| 341 } | |
| 342 } | |
| 343 /*- End of function --------------------------------------------------------*/ | |
| 344 | |
| 345 SPAN_DECLARE(void) t38_non_ecm_buffer_set_mode(t38_non_ecm_buffer_state_t *s, int mode, int min_bits_per_row) | |
| 346 { | |
| 347 s->image_data_mode = mode; | |
| 348 s->min_bits_per_row = min_bits_per_row; | |
| 349 } | |
| 350 /*- End of function --------------------------------------------------------*/ | |
| 351 | |
| 352 SPAN_DECLARE(t38_non_ecm_buffer_state_t *) t38_non_ecm_buffer_init(t38_non_ecm_buffer_state_t *s, int mode, int min_bits_per_row) | |
| 353 { | |
| 354 if (s == NULL) | |
| 355 { | |
| 356 if ((s = (t38_non_ecm_buffer_state_t *) malloc(sizeof(*s))) == NULL) | |
| 357 return NULL; | |
| 358 } | |
| 359 memset(s, 0, sizeof(*s)); | |
| 360 s->image_data_mode = mode; | |
| 361 s->min_bits_per_row = min_bits_per_row; | |
| 362 restart_buffer(s); | |
| 363 return s; | |
| 364 } | |
| 365 /*- End of function --------------------------------------------------------*/ | |
| 366 | |
| 367 SPAN_DECLARE(int) t38_non_ecm_buffer_release(t38_non_ecm_buffer_state_t *s) | |
| 368 { | |
| 369 return 0; | |
| 370 } | |
| 371 /*- End of function --------------------------------------------------------*/ | |
| 372 | |
| 373 SPAN_DECLARE(int) t38_non_ecm_buffer_free(t38_non_ecm_buffer_state_t *s) | |
| 374 { | |
| 375 if (s) | |
| 376 free(s); | |
| 377 return 0; | |
| 378 } | |
| 379 /*- End of function --------------------------------------------------------*/ | |
| 380 /*- End of file ------------------------------------------------------------*/ |
