FreeCalypso > hg > themwi-rtp-lib
annotate src/twjit.c @ 42:334d883b96ba
twrtp_jibuf_create: make config argument const
While this config structure is not a constant in the mathematical
sense of the term (it is expected that vty config changes may happen
while twjit instance is alive), twjit functions never write to it,
only read, hence it is 'const' in the not-quite-mathematical C-standard
sense.
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Fri, 20 Dec 2024 22:47:20 +0000 |
parents | bda6b24385f7 |
children |
rev | line source |
---|---|
3
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
1 /* |
5
1bb26347e253
twjit: split into separate base/in/out modules
Mychaela Falconia <falcon@freecalypso.org>
parents:
3
diff
changeset
|
2 * Themyscira Wireless jitter buffer implementation: main body. |
3
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
3 */ |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
4 |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
5 #include <stdint.h> |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
6 #include <stdbool.h> |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
7 #include <string.h> |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
8 |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
9 #include <osmocom/core/linuxlist.h> |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
10 #include <osmocom/core/msgb.h> |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
11 #include <osmocom/core/talloc.h> |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
12 #include <osmocom/core/utils.h> |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
13 |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
14 #include <themwi/rtp/twjit.h> |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
15 |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
16 void twrtp_jibuf_init_defaults(struct twrtp_jibuf_config *config) |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
17 { |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
18 memset(config, 0, sizeof(struct twrtp_jibuf_config)); |
41
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
19 |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
20 /* While the theoretical minimum starting fill level is 1, the |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
21 * practically useful minimum (achieving lowest latency, but not |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
22 * incurring underruns in normal healthy operation) is 2 for typical |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
23 * network configurations that combine elements with "perfect" 20 ms |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
24 * timing (T1/E1 interfaces, external IP-PSTN links, software |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
25 * transcoders timed by system clock etc) and GSM-to-IP OsmoBTS |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
26 * whose 20 ms timing contains the small inherent jitter of TDMA. */ |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
27 config->bd_start = 2; |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
28 |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
29 /* The high water mark setting determines when the standing queue |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
30 * thinning mechanism kicks in. A standing queue that is longer |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
31 * than the starting fill level will occur when the flow starts |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
32 * during a network latency spike, but then the network latency |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
33 * goes down. If this setting is too high, deep standing queues |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
34 * will persist, adding needless latency to speech or CSD. |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
35 * If this setting is too low, the thinning mechanism will be |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
36 * too invasive, needlessly and perhaps frequently deleting a quantum |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
37 * of speech or data from the stream and incurring a phase shift. |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
38 * Starting fill level plus 2 seems like a good default. */ |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
39 config->bd_hiwat = 4; |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
40 |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
41 /* When the standing queue thinning mechanism does kick in, |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
42 * it drops every Nth packet, where N is the thinning interval. |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
43 * Given that this mechanism forcibly deletes a quantum of speech |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
44 * or data from the stream, these induced disruptions should be |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
45 * spaced out, and the managing operator should also keep in mind |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
46 * that the incurred phase shift may be a problem for some |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
47 * applications, particularly CSD. Our current default is |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
48 * a prime number, reducing the probability that the thinning |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
49 * mechanism will interfere badly with intrinsic features of the |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
50 * stream being thinned. 17 quantum units at 20 ms per quantum |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
51 * is 340 ms, which should be sufficiently long spacing to make |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
52 * speech quantum deletions tolerable. */ |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
53 config->thinning_int = 17; |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
54 |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
55 /* With RTP timestamps being 32 bits and with the usual RTP |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
56 * clock rate of 8000 timestamp units per second, a packet may |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
57 * arrive that claims to be as far as 3 days into the future. |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
58 * Such aberrant RTP packets are jocularly referred to as |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
59 * time travelers. Assuming that actual time travel either |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
60 * does not exist at all or at least does not happen in the |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
61 * present context, we reason that when such "time traveler" RTP |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
62 * packets do arrive, we must be dealing with the effect of a |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
63 * software bug or misdesign or misconfiguration in whatever |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
64 * foreign network element is sending us RTP. In any case, |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
65 * irrespective of the cause, we must be prepared for the |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
66 * possibility of seeming "time travel" in the incoming RTP stream. |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
67 * We implement an arbitrary threshold: if the received RTP ts |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
68 * is too far into the future, we treat that packet as the |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
69 * beginning of a new stream, same as SSRC change or non-quantum |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
70 * ts increment. This threshold has 1 s granularity, which is |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
71 * sufficient for its intended purpose of catching gross errors. |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
72 * The minimum setting of this threshold is 1 s, but let's |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
73 * default to 10 s, being generous to networks with really bad |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
74 * latency. */ |
bda6b24385f7
twjit config: revisit default settings
Mychaela Falconia <falcon@freecalypso.org>
parents:
39
diff
changeset
|
75 config->max_future_sec = 10; |
3
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
76 } |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
77 |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
78 /* create and destroy functions */ |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
79 |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
80 struct twrtp_jibuf_inst * |
42
334d883b96ba
twrtp_jibuf_create: make config argument const
Mychaela Falconia <falcon@freecalypso.org>
parents:
41
diff
changeset
|
81 twrtp_jibuf_create(void *ctx, const struct twrtp_jibuf_config *config) |
3
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
82 { |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
83 struct twrtp_jibuf_inst *twjit; |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
84 |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
85 twjit = talloc_zero(ctx, struct twrtp_jibuf_inst); |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
86 if (!twjit) |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
87 return NULL; |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
88 |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
89 twjit->ext_config = config; |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
90 twjit->state = TWJIT_STATE_EMPTY; |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
91 INIT_LLIST_HEAD(&twjit->sb[0].queue); |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
92 INIT_LLIST_HEAD(&twjit->sb[1].queue); |
15
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
93 /* default of 8 kHz clock, 20 ms quantum */ |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
94 twrtp_jibuf_set_ts_quant(twjit, 8, 20); |
3
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
95 |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
96 return twjit; |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
97 } |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
98 |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
99 void twrtp_jibuf_destroy(struct twrtp_jibuf_inst *twjit) |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
100 { |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
101 msgb_queue_free(&twjit->sb[0].queue); |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
102 msgb_queue_free(&twjit->sb[1].queue); |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
103 talloc_free(twjit); |
d10ea5dc61b3
twjit: initial import from previous work repository
Mychaela Falconia <falcon@freecalypso.org>
parents:
diff
changeset
|
104 } |
15
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
105 |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
106 /* basic housekeeping */ |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
107 |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
108 void twrtp_jibuf_set_ts_quant(struct twrtp_jibuf_inst *twjit, |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
109 uint16_t clock_khz, uint16_t quantum_ms) |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
110 { |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
111 twjit->ts_quantum = (uint32_t) quantum_ms * clock_khz; |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
112 twjit->quanta_per_sec = 1000 / quantum_ms; |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
113 twjit->ts_units_per_ms = clock_khz; |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
114 twjit->ts_units_per_sec = (uint32_t) clock_khz * 1000; |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
115 twjit->ns_to_ts_units = 1000000 / clock_khz; |
355de6301404
twjit: simplify create API, factor out clock & quantum config
Mychaela Falconia <falcon@freecalypso.org>
parents:
8
diff
changeset
|
116 } |
16
58e9719d1a84
twjit: add reset function
Mychaela Falconia <falcon@freecalypso.org>
parents:
15
diff
changeset
|
117 |
58e9719d1a84
twjit: add reset function
Mychaela Falconia <falcon@freecalypso.org>
parents:
15
diff
changeset
|
118 void twrtp_jibuf_reset(struct twrtp_jibuf_inst *twjit) |
58e9719d1a84
twjit: add reset function
Mychaela Falconia <falcon@freecalypso.org>
parents:
15
diff
changeset
|
119 { |
58e9719d1a84
twjit: add reset function
Mychaela Falconia <falcon@freecalypso.org>
parents:
15
diff
changeset
|
120 msgb_queue_free(&twjit->sb[0].queue); |
58e9719d1a84
twjit: add reset function
Mychaela Falconia <falcon@freecalypso.org>
parents:
15
diff
changeset
|
121 msgb_queue_free(&twjit->sb[1].queue); |
58e9719d1a84
twjit: add reset function
Mychaela Falconia <falcon@freecalypso.org>
parents:
15
diff
changeset
|
122 twjit->state = TWJIT_STATE_EMPTY; |
58e9719d1a84
twjit: add reset function
Mychaela Falconia <falcon@freecalypso.org>
parents:
15
diff
changeset
|
123 twjit->sb[0].depth = 0; |
58e9719d1a84
twjit: add reset function
Mychaela Falconia <falcon@freecalypso.org>
parents:
15
diff
changeset
|
124 twjit->sb[1].depth = 0; |
58e9719d1a84
twjit: add reset function
Mychaela Falconia <falcon@freecalypso.org>
parents:
15
diff
changeset
|
125 twjit->got_first_packet = false; |
58e9719d1a84
twjit: add reset function
Mychaela Falconia <falcon@freecalypso.org>
parents:
15
diff
changeset
|
126 } |