diff options
author | irungentoo <irungentoo@gmail.com> | 2014-06-22 08:39:26 -0400 |
---|---|---|
committer | irungentoo <irungentoo@gmail.com> | 2014-06-22 08:39:26 -0400 |
commit | 87be366f795e8993b731fe6b3be3ce192720d47c (patch) | |
tree | f2ca82c0a270deb2346488401430469f58c22cfc /toxav/codec.c | |
parent | e6699f60eff70075f62d062470f5fec3bd56f497 (diff) | |
parent | 77150081ba9ff13641463fbc929ea7ec3e029440 (diff) |
Merge branch 'master' of https://github.com/mannol1/toxcore
Diffstat (limited to 'toxav/codec.c')
-rw-r--r-- | toxav/codec.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/toxav/codec.c b/toxav/codec.c new file mode 100644 index 00000000..772b0a9d --- /dev/null +++ b/toxav/codec.c | |||
@@ -0,0 +1,343 @@ | |||
1 | /** media.c | ||
2 | * | ||
3 | * Audio and video codec intitialization, encoding/decoding and playback | ||
4 | * | ||
5 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
6 | * | ||
7 | * This file is part of Tox. | ||
8 | * | ||
9 | * Tox is free software: you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation, either version 3 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * Tox is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | |||
25 | #ifdef HAVE_CONFIG_H | ||
26 | #include "config.h" | ||
27 | #endif /* HAVE_CONFIG_H */ | ||
28 | |||
29 | #include "../toxcore/logger.h" | ||
30 | |||
31 | #include <stdio.h> | ||
32 | #include <stdlib.h> | ||
33 | #include <math.h> | ||
34 | #include <assert.h> | ||
35 | |||
36 | #include "rtp.h" | ||
37 | #include "codec.h" | ||
38 | |||
39 | const uint16_t min_jbuf_size = 10; | ||
40 | const uint16_t min_readiness_idx = 6; /* when is buffer ready to dqq */ | ||
41 | |||
42 | int empty_queue(JitterBuffer *q) | ||
43 | { | ||
44 | while (q->size > 0) { | ||
45 | rtp_free_msg(NULL, q->queue[q->front]); | ||
46 | q->front++; | ||
47 | |||
48 | if (q->front == q->capacity) | ||
49 | q->front = 0; | ||
50 | |||
51 | q->size--; | ||
52 | } | ||
53 | |||
54 | q->id_set = 0; | ||
55 | q->queue_ready = 0; | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | JitterBuffer *create_queue(int capacity) | ||
60 | { | ||
61 | JitterBuffer *q; | ||
62 | |||
63 | if ( !(q = calloc(sizeof(JitterBuffer), 1)) ) return NULL; | ||
64 | |||
65 | if (!(q->queue = calloc(sizeof(RTPMessage *), capacity))) { | ||
66 | free(q); | ||
67 | return NULL; | ||
68 | } | ||
69 | |||
70 | q->size = 0; | ||
71 | q->capacity = capacity >= min_jbuf_size ? capacity : min_jbuf_size; | ||
72 | q->front = 0; | ||
73 | q->rear = -1; | ||
74 | q->queue_ready = 0; | ||
75 | q->current_id = 0; | ||
76 | q->current_ts = 0; | ||
77 | q->id_set = 0; | ||
78 | return q; | ||
79 | } | ||
80 | |||
81 | void terminate_queue(JitterBuffer *q) | ||
82 | { | ||
83 | empty_queue(q); | ||
84 | free(q->queue); | ||
85 | free(q); | ||
86 | } | ||
87 | |||
88 | #define sequnum_older(sn_a, sn_b, ts_a, ts_b) (sn_a > sn_b || ts_a > ts_b) | ||
89 | |||
90 | /* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */ | ||
91 | RTPMessage *dequeue(JitterBuffer *q, int *success) | ||
92 | { | ||
93 | if (q->size == 0 || q->queue_ready == 0) { /* Empty queue */ | ||
94 | q->queue_ready = 0; | ||
95 | *success = 0; | ||
96 | return NULL; | ||
97 | } | ||
98 | |||
99 | int front = q->front; | ||
100 | |||
101 | if (q->id_set == 0) { | ||
102 | q->current_id = q->queue[front]->header->sequnum; | ||
103 | q->current_ts = q->queue[front]->header->timestamp; | ||
104 | q->id_set = 1; | ||
105 | } else { | ||
106 | int next_id = q->queue[front]->header->sequnum; | ||
107 | int next_ts = q->queue[front]->header->timestamp; | ||
108 | |||
109 | /* if this packet is indeed the expected packet */ | ||
110 | if (next_id == (q->current_id + 1) % MAX_SEQU_NUM) { | ||
111 | q->current_id = next_id; | ||
112 | q->current_ts = next_ts; | ||
113 | } else { | ||
114 | if (sequnum_older(next_id, q->current_id, next_ts, q->current_ts)) { | ||
115 | LOGGER_DEBUG("nextid: %d current: %d\n", next_id, q->current_id); | ||
116 | q->current_id = (q->current_id + 1) % MAX_SEQU_NUM; | ||
117 | *success = 2; /* tell the decoder the packet is lost */ | ||
118 | return NULL; | ||
119 | } else { | ||
120 | LOGGER_DEBUG("Packet too old"); | ||
121 | *success = 0; | ||
122 | return NULL; | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | q->size--; | ||
128 | q->front++; | ||
129 | |||
130 | if (q->front == q->capacity) | ||
131 | q->front = 0; | ||
132 | |||
133 | *success = 1; | ||
134 | q->current_id = q->queue[front]->header->sequnum; | ||
135 | q->current_ts = q->queue[front]->header->timestamp; | ||
136 | return q->queue[front]; | ||
137 | } | ||
138 | |||
139 | |||
140 | void queue(JitterBuffer *q, RTPMessage *pk) | ||
141 | { | ||
142 | if (q->size == q->capacity) { /* Full, empty queue */ | ||
143 | LOGGER_DEBUG("Queue full s(%d) c(%d), emptying...", q->size, q->capacity); | ||
144 | empty_queue(q); | ||
145 | } | ||
146 | |||
147 | if (q->size >= min_readiness_idx) q->queue_ready = 1; | ||
148 | |||
149 | ++q->size; | ||
150 | ++q->rear; | ||
151 | |||
152 | if (q->rear == q->capacity) q->rear = 0; | ||
153 | |||
154 | q->queue[q->rear] = pk; | ||
155 | |||
156 | int a; | ||
157 | int j; | ||
158 | a = q->rear; | ||
159 | |||
160 | for (j = 0; j < q->size - 1; ++j) { | ||
161 | int b = a - 1; | ||
162 | |||
163 | if (b < 0) | ||
164 | b += q->capacity; | ||
165 | |||
166 | if (sequnum_older(q->queue[b]->header->sequnum, q->queue[a]->header->sequnum, | ||
167 | q->queue[b]->header->timestamp, q->queue[a]->header->timestamp)) { | ||
168 | RTPMessage *temp; | ||
169 | temp = q->queue[a]; | ||
170 | q->queue[a] = q->queue[b]; | ||
171 | q->queue[b] = temp; | ||
172 | LOGGER_DEBUG("Had to swap"); | ||
173 | } else { | ||
174 | break; | ||
175 | } | ||
176 | |||
177 | a -= 1; | ||
178 | |||
179 | if (a < 0) a += q->capacity; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | |||
184 | int init_video_decoder(CodecState *cs) | ||
185 | { | ||
186 | int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION); | ||
187 | |||
188 | if ( rc != VPX_CODEC_OK) { | ||
189 | LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); | ||
190 | return -1; | ||
191 | } | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | int init_audio_decoder(CodecState *cs, uint32_t audio_channels) | ||
197 | { | ||
198 | int rc; | ||
199 | cs->audio_decoder = opus_decoder_create(cs->audio_sample_rate, audio_channels, &rc ); | ||
200 | |||
201 | if ( rc != OPUS_OK ) { | ||
202 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | ||
203 | return -1; | ||
204 | } | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | |||
210 | int init_video_encoder(CodecState *cs, uint16_t width, uint16_t height, uint32_t video_bitrate) | ||
211 | { | ||
212 | vpx_codec_enc_cfg_t cfg; | ||
213 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); | ||
214 | |||
215 | if (rc) { | ||
216 | LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); | ||
217 | return -1; | ||
218 | } | ||
219 | |||
220 | cfg.rc_target_bitrate = video_bitrate; | ||
221 | cfg.g_w = width; | ||
222 | cfg.g_h = height; | ||
223 | |||
224 | rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION); | ||
225 | |||
226 | if ( rc != VPX_CODEC_OK) { | ||
227 | LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); | ||
228 | return -1; | ||
229 | } | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | int init_audio_encoder(CodecState *cs, uint32_t audio_channels) | ||
235 | { | ||
236 | int rc = OPUS_OK; | ||
237 | cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, audio_channels, OPUS_APPLICATION_AUDIO, &rc); | ||
238 | |||
239 | if ( rc != OPUS_OK ) { | ||
240 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
241 | return -1; | ||
242 | } | ||
243 | |||
244 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); | ||
245 | |||
246 | if ( rc != OPUS_OK ) { | ||
247 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
248 | return -1; | ||
249 | } | ||
250 | |||
251 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); | ||
252 | |||
253 | if ( rc != OPUS_OK ) { | ||
254 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
255 | return -1; | ||
256 | } | ||
257 | |||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | |||
263 | CodecState *codec_init_session ( uint32_t audio_bitrate, | ||
264 | uint16_t audio_frame_duration, | ||
265 | uint32_t audio_sample_rate, | ||
266 | uint32_t audio_channels, | ||
267 | uint32_t audio_VAD_tolerance_ms, | ||
268 | uint16_t video_width, | ||
269 | uint16_t video_height, | ||
270 | uint32_t video_bitrate ) | ||
271 | { | ||
272 | CodecState *retu = calloc(sizeof(CodecState), 1); | ||
273 | |||
274 | if (!retu) return NULL; | ||
275 | |||
276 | retu->audio_bitrate = audio_bitrate; | ||
277 | retu->audio_sample_rate = audio_sample_rate; | ||
278 | |||
279 | /* Encoders */ | ||
280 | if (!video_width || !video_height) { /* Disable video */ | ||
281 | /*video_width = 320; | ||
282 | video_height = 240; */ | ||
283 | } else { | ||
284 | retu->capabilities |= ( 0 == init_video_encoder(retu, video_width, video_height, video_bitrate) ) ? v_encoding : 0; | ||
285 | retu->capabilities |= ( 0 == init_video_decoder(retu) ) ? v_decoding : 0; | ||
286 | } | ||
287 | |||
288 | retu->capabilities |= ( 0 == init_audio_encoder(retu, audio_channels) ) ? a_encoding : 0; | ||
289 | retu->capabilities |= ( 0 == init_audio_decoder(retu, audio_channels) ) ? a_decoding : 0; | ||
290 | |||
291 | if ( retu->capabilities == 0 ) { /* everything failed */ | ||
292 | free (retu); | ||
293 | return NULL; | ||
294 | } | ||
295 | |||
296 | |||
297 | retu->EVAD_tolerance = audio_VAD_tolerance_ms > audio_frame_duration ? | ||
298 | audio_VAD_tolerance_ms / audio_frame_duration : audio_frame_duration; | ||
299 | |||
300 | return retu; | ||
301 | } | ||
302 | |||
303 | void codec_terminate_session ( CodecState *cs ) | ||
304 | { | ||
305 | if ( cs->audio_encoder ) | ||
306 | opus_encoder_destroy(cs->audio_encoder); | ||
307 | |||
308 | if ( cs->audio_decoder ) | ||
309 | opus_decoder_destroy(cs->audio_decoder); | ||
310 | |||
311 | if ( cs->capabilities & v_decoding ) | ||
312 | vpx_codec_destroy(&cs->v_decoder); | ||
313 | |||
314 | if ( cs->capabilities & v_encoding ) | ||
315 | vpx_codec_destroy(&cs->v_encoder); | ||
316 | } | ||
317 | |||
318 | inline float calculate_sum_sq (int16_t *n, uint16_t k) | ||
319 | { | ||
320 | float result = 0; | ||
321 | uint16_t i = 0; | ||
322 | |||
323 | for ( ; i < k; i ++) result += (float) (n[i] * n[i]); | ||
324 | |||
325 | return result; | ||
326 | } | ||
327 | |||
328 | int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy) | ||
329 | { | ||
330 | float frame_energy = sqrt(calculate_sum_sq(PCM, frame_size)) / frame_size; | ||
331 | |||
332 | if ( frame_energy > energy) { | ||
333 | cs->EVAD_tolerance_cr = cs->EVAD_tolerance; /* Reset counter */ | ||
334 | return 1; | ||
335 | } | ||
336 | |||
337 | if ( cs->EVAD_tolerance_cr ) { | ||
338 | cs->EVAD_tolerance_cr --; | ||
339 | return 1; | ||
340 | } | ||
341 | |||
342 | return 0; | ||
343 | } | ||