summaryrefslogtreecommitdiff
path: root/toxav/codec.c
diff options
context:
space:
mode:
authorirungentoo <irungentoo@gmail.com>2014-06-22 08:39:26 -0400
committerirungentoo <irungentoo@gmail.com>2014-06-22 08:39:26 -0400
commit87be366f795e8993b731fe6b3be3ce192720d47c (patch)
treef2ca82c0a270deb2346488401430469f58c22cfc /toxav/codec.c
parente6699f60eff70075f62d062470f5fec3bd56f497 (diff)
parent77150081ba9ff13641463fbc929ea7ec3e029440 (diff)
Merge branch 'master' of https://github.com/mannol1/toxcore
Diffstat (limited to 'toxav/codec.c')
-rw-r--r--toxav/codec.c343
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
39const uint16_t min_jbuf_size = 10;
40const uint16_t min_readiness_idx = 6; /* when is buffer ready to dqq */
41
42int 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
59JitterBuffer *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
81void 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 */
91RTPMessage *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
140void 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
184int 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
196int 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
210int 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
234int 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
263CodecState *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
303void 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
318inline 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
328int 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}