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 ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.