git migration: Refactor the ASTERISK_FILE_VERSION macro
[asterisk/asterisk.git] / main / smoother.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Frame smoother manipulation routines
22  *
23  * \author Mark Spencer <markster@digium.com>
24  */
25
26 /*** MODULEINFO
27         <support_level>core</support_level>
28  ***/
29
30 #include "asterisk.h"
31
32 ASTERISK_REGISTER_FILE()
33
34 #include "asterisk/_private.h"
35 #include "asterisk/frame.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/time.h"
38 #include "asterisk/utils.h"
39 #include "asterisk/format.h"
40 #include "asterisk/codec.h"
41 #include "asterisk/smoother.h"
42
43 #define SMOOTHER_SIZE 8000
44
45 struct ast_smoother {
46         int size;
47         struct ast_format *format;
48         int flags;
49         float samplesperbyte;
50         unsigned int opt_needs_swap:1;
51         struct ast_frame f;
52         struct timeval delivery;
53         char data[SMOOTHER_SIZE];
54         char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET];
55         struct ast_frame *opt;
56         int len;
57 };
58
59 static int smoother_frame_feed(struct ast_smoother *s, struct ast_frame *f, int swap)
60 {
61         if (s->flags & AST_SMOOTHER_FLAG_G729) {
62                 if (s->len % 10) {
63                         ast_log(LOG_NOTICE, "Dropping extra frame of G.729 since we already have a VAD frame at the end\n");
64                         return 0;
65                 }
66         }
67         if (swap) {
68                 ast_swapcopy_samples(s->data + s->len, f->data.ptr, f->samples);
69         } else {
70                 memcpy(s->data + s->len, f->data.ptr, f->datalen);
71         }
72         /* If either side is empty, reset the delivery time */
73         if (!s->len || ast_tvzero(f->delivery) || ast_tvzero(s->delivery)) {    /* XXX really ? */
74                 s->delivery = f->delivery;
75         }
76         s->len += f->datalen;
77
78         return 0;
79 }
80
81 void ast_smoother_reset(struct ast_smoother *s, int bytes)
82 {
83         ao2_cleanup(s->format);
84         memset(s, 0, sizeof(*s));
85         s->size = bytes;
86 }
87
88 void ast_smoother_reconfigure(struct ast_smoother *s, int bytes)
89 {
90         /* if there is no change, then nothing to do */
91         if (s->size == bytes) {
92                 return;
93         }
94         /* set the new desired output size */
95         s->size = bytes;
96         /* if there is no 'optimized' frame in the smoother,
97          *   then there is nothing left to do
98          */
99         if (!s->opt) {
100                 return;
101         }
102         /* there is an 'optimized' frame here at the old size,
103          * but it must now be put into the buffer so the data
104          * can be extracted at the new size
105          */
106         smoother_frame_feed(s, s->opt, s->opt_needs_swap);
107         s->opt = NULL;
108 }
109
110 struct ast_smoother *ast_smoother_new(int size)
111 {
112         struct ast_smoother *s;
113         if (size < 1)
114                 return NULL;
115         if ((s = ast_calloc(1, sizeof(*s))))
116                 ast_smoother_reset(s, size);
117         return s;
118 }
119
120 int ast_smoother_get_flags(struct ast_smoother *s)
121 {
122         return s->flags;
123 }
124
125 void ast_smoother_set_flags(struct ast_smoother *s, int flags)
126 {
127         s->flags = flags;
128 }
129
130 int ast_smoother_test_flag(struct ast_smoother *s, int flag)
131 {
132         return (s->flags & flag);
133 }
134
135 int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap)
136 {
137         if (f->frametype != AST_FRAME_VOICE) {
138                 ast_log(LOG_WARNING, "Huh?  Can't smooth a non-voice frame!\n");
139                 return -1;
140         }
141         if (!s->format) {
142                 s->format = ao2_bump(f->subclass.format);
143                 s->samplesperbyte = (float)f->samples / (float)f->datalen;
144         } else if (ast_format_cmp(s->format, f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
145                 ast_log(LOG_WARNING, "Smoother was working on %s format frames, now trying to feed %s?\n",
146                         ast_format_get_name(s->format), ast_format_get_name(f->subclass.format));
147                 return -1;
148         }
149         if (s->len + f->datalen > SMOOTHER_SIZE) {
150                 ast_log(LOG_WARNING, "Out of smoother space\n");
151                 return -1;
152         }
153         if (((f->datalen == s->size) ||
154              ((f->datalen < 10) && (s->flags & AST_SMOOTHER_FLAG_G729))) &&
155             !s->opt &&
156             !s->len &&
157             (f->offset >= AST_MIN_OFFSET)) {
158                 /* Optimize by sending the frame we just got
159                    on the next read, thus eliminating the douple
160                    copy */
161                 if (swap)
162                         ast_swapcopy_samples(f->data.ptr, f->data.ptr, f->samples);
163                 s->opt = f;
164                 s->opt_needs_swap = swap ? 1 : 0;
165                 return 0;
166         }
167
168         return smoother_frame_feed(s, f, swap);
169 }
170
171 struct ast_frame *ast_smoother_read(struct ast_smoother *s)
172 {
173         struct ast_frame *opt;
174         int len;
175
176         /* IF we have an optimization frame, send it */
177         if (s->opt) {
178                 if (s->opt->offset < AST_FRIENDLY_OFFSET)
179                         ast_log(LOG_WARNING, "Returning a frame of inappropriate offset (%d).\n",
180                                                         s->opt->offset);
181                 opt = s->opt;
182                 s->opt = NULL;
183                 return opt;
184         }
185
186         /* Make sure we have enough data */
187         if (s->len < s->size) {
188                 /* Or, if this is a G.729 frame with VAD on it, send it immediately anyway */
189                 if (!((s->flags & AST_SMOOTHER_FLAG_G729) && (s->len % 10)))
190                         return NULL;
191         }
192         len = s->size;
193         if (len > s->len)
194                 len = s->len;
195         /* Make frame */
196         s->f.frametype = AST_FRAME_VOICE;
197         s->f.subclass.format = s->format;
198         s->f.data.ptr = s->framedata + AST_FRIENDLY_OFFSET;
199         s->f.offset = AST_FRIENDLY_OFFSET;
200         s->f.datalen = len;
201         /* Samples will be improper given VAD, but with VAD the concept really doesn't even exist */
202         s->f.samples = len * s->samplesperbyte; /* XXX rounding */
203         s->f.delivery = s->delivery;
204         /* Fill Data */
205         memcpy(s->f.data.ptr, s->data, len);
206         s->len -= len;
207         /* Move remaining data to the front if applicable */
208         if (s->len) {
209                 /* In principle this should all be fine because if we are sending
210                    G.729 VAD, the next timestamp will take over anyawy */
211                 memmove(s->data, s->data + len, s->len);
212                 if (!ast_tvzero(s->delivery)) {
213                         /* If we have delivery time, increment it, otherwise, leave it at 0 */
214                         s->delivery = ast_tvadd(s->delivery, ast_samp2tv(s->f.samples,
215                                 ast_format_get_sample_rate(s->format)));
216                 }
217         }
218         /* Return frame */
219         return &s->f;
220 }
221
222 void ast_smoother_free(struct ast_smoother *s)
223 {
224         ao2_cleanup(s->format);
225         ast_free(s);
226 }
227