diff options
author | irungentoo <irungentoo@gmail.com> | 2014-11-24 20:24:59 -0500 |
---|---|---|
committer | irungentoo <irungentoo@gmail.com> | 2014-11-24 20:24:59 -0500 |
commit | 279c33c01a9ca721b986aabf72fa982936b20c9e (patch) | |
tree | 4c0d289c6d74788989024485d44a4f5c7fec6efc /toxav/codec.c | |
parent | c67bc3399faae541d64309412326ae92262b1282 (diff) | |
parent | 386c9748d48d3bb4513e8e5c32e2b30a4d6a00d4 (diff) |
Merge branch 'master' of https://github.com/mannol/toxcore
Diffstat (limited to 'toxav/codec.c')
-rw-r--r-- | toxav/codec.c | 606 |
1 files changed, 506 insertions, 100 deletions
diff --git a/toxav/codec.c b/toxav/codec.c index 2f00319f..e0b004a4 100644 --- a/toxav/codec.c +++ b/toxav/codec.c | |||
@@ -32,11 +32,107 @@ | |||
32 | #include <stdlib.h> | 32 | #include <stdlib.h> |
33 | #include <math.h> | 33 | #include <math.h> |
34 | #include <assert.h> | 34 | #include <assert.h> |
35 | #include <time.h> | ||
35 | 36 | ||
37 | #include "msi.h" | ||
36 | #include "rtp.h" | 38 | #include "rtp.h" |
37 | #include "codec.h" | 39 | #include "codec.h" |
38 | 40 | ||
39 | JitterBuffer *create_queue(unsigned int capacity) | 41 | /* Assume 24 fps*/ |
42 | #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) | ||
43 | #define MAX_DECODE_TIME_US 0 | ||
44 | |||
45 | // TODO this has to be exchanged in msi | ||
46 | #define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ | ||
47 | #define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/ | ||
48 | #define VIDEOFRAME_HEADER_SIZE 0x2 | ||
49 | |||
50 | /* FIXME: Might not be enough */ | ||
51 | #define VIDEO_DECODE_BUFFER_SIZE 20 | ||
52 | |||
53 | #define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; } | ||
54 | #define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; } | ||
55 | |||
56 | typedef ARRAY(uint8_t) Payload; | ||
57 | |||
58 | static PAIR(CSVideoCallback, void *) vpcallback; | ||
59 | static PAIR(CSAudioCallback, void *) apcallback; | ||
60 | |||
61 | typedef struct { | ||
62 | uint16_t size; /* Max size */ | ||
63 | uint16_t start; | ||
64 | uint16_t end; | ||
65 | Payload **packets; | ||
66 | } PayloadBuffer; | ||
67 | |||
68 | static _Bool buffer_full(const PayloadBuffer *b) | ||
69 | { | ||
70 | return (b->end + 1) % b->size == b->start; | ||
71 | } | ||
72 | |||
73 | static _Bool buffer_empty(const PayloadBuffer *b) | ||
74 | { | ||
75 | return b->end == b->start; | ||
76 | } | ||
77 | |||
78 | static void buffer_write(PayloadBuffer *b, Payload *p) | ||
79 | { | ||
80 | b->packets[b->end] = p; | ||
81 | b->end = (b->end + 1) % b->size; | ||
82 | |||
83 | if (b->end == b->start) b->start = (b->start + 1) % b->size; /* full, overwrite */ | ||
84 | } | ||
85 | |||
86 | static void buffer_read(PayloadBuffer *b, Payload **p) | ||
87 | { | ||
88 | *p = b->packets[b->start]; | ||
89 | b->start = (b->start + 1) % b->size; | ||
90 | } | ||
91 | |||
92 | static void buffer_clear(PayloadBuffer *b) | ||
93 | { | ||
94 | while (!buffer_empty(b)) { | ||
95 | Payload *p; | ||
96 | buffer_read(b, &p); | ||
97 | free(p); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | static PayloadBuffer *buffer_new(int size) | ||
102 | { | ||
103 | PayloadBuffer *buf = calloc(sizeof(PayloadBuffer), 1); | ||
104 | |||
105 | if (!buf) return NULL; | ||
106 | |||
107 | buf->size = size + 1; /* include empty elem */ | ||
108 | |||
109 | if (!(buf->packets = calloc(buf->size, sizeof(Payload *)))) { | ||
110 | free(buf); | ||
111 | return NULL; | ||
112 | } | ||
113 | |||
114 | return buf; | ||
115 | } | ||
116 | |||
117 | static void buffer_free(PayloadBuffer *b) | ||
118 | { | ||
119 | if (b) { | ||
120 | buffer_clear(b); | ||
121 | free(b->packets); | ||
122 | free(b); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | /* JITTER BUFFER WORK */ | ||
127 | typedef struct _JitterBuffer { | ||
128 | RTPMessage **queue; | ||
129 | uint32_t size; | ||
130 | uint32_t capacity; | ||
131 | uint16_t bottom; | ||
132 | uint16_t top; | ||
133 | } JitterBuffer; | ||
134 | |||
135 | static JitterBuffer *jbuf_new(uint32_t capacity) | ||
40 | { | 136 | { |
41 | unsigned int size = 1; | 137 | unsigned int size = 1; |
42 | 138 | ||
@@ -58,7 +154,7 @@ JitterBuffer *create_queue(unsigned int capacity) | |||
58 | return q; | 154 | return q; |
59 | } | 155 | } |
60 | 156 | ||
61 | static void clear_queue(JitterBuffer *q) | 157 | static void jbuf_clear(JitterBuffer *q) |
62 | { | 158 | { |
63 | for (; q->bottom != q->top; ++q->bottom) { | 159 | for (; q->bottom != q->top; ++q->bottom) { |
64 | if (q->queue[q->bottom % q->size]) { | 160 | if (q->queue[q->bottom % q->size]) { |
@@ -68,25 +164,25 @@ static void clear_queue(JitterBuffer *q) | |||
68 | } | 164 | } |
69 | } | 165 | } |
70 | 166 | ||
71 | void terminate_queue(JitterBuffer *q) | 167 | static void jbuf_free(JitterBuffer *q) |
72 | { | 168 | { |
73 | if (!q) return; | 169 | if (!q) return; |
74 | 170 | ||
75 | clear_queue(q); | 171 | jbuf_clear(q); |
76 | free(q->queue); | 172 | free(q->queue); |
77 | free(q); | 173 | free(q); |
78 | } | 174 | } |
79 | 175 | ||
80 | void queue(JitterBuffer *q, RTPMessage *pk) | 176 | static void jbuf_write(JitterBuffer *q, RTPMessage *m) |
81 | { | 177 | { |
82 | uint16_t sequnum = pk->header->sequnum; | 178 | uint16_t sequnum = m->header->sequnum; |
83 | 179 | ||
84 | unsigned int num = sequnum % q->size; | 180 | unsigned int num = sequnum % q->size; |
85 | 181 | ||
86 | if ((uint32_t)(sequnum - q->bottom) > q->size) { | 182 | if ((uint32_t)(sequnum - q->bottom) > q->size) { |
87 | clear_queue(q); | 183 | jbuf_clear(q); |
88 | q->bottom = sequnum; | 184 | q->bottom = sequnum; |
89 | q->queue[num] = pk; | 185 | q->queue[num] = m; |
90 | q->top = sequnum + 1; | 186 | q->top = sequnum + 1; |
91 | return; | 187 | return; |
92 | } | 188 | } |
@@ -94,14 +190,16 @@ void queue(JitterBuffer *q, RTPMessage *pk) | |||
94 | if (q->queue[num]) | 190 | if (q->queue[num]) |
95 | return; | 191 | return; |
96 | 192 | ||
97 | q->queue[num] = pk; | 193 | q->queue[num] = m; |
98 | 194 | ||
99 | if ((sequnum - q->bottom) >= (q->top - q->bottom)) | 195 | if ((sequnum - q->bottom) >= (q->top - q->bottom)) |
100 | q->top = sequnum + 1; | 196 | q->top = sequnum + 1; |
101 | } | 197 | } |
102 | 198 | ||
103 | /* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */ | 199 | /* Success is 0 when there is nothing to dequeue, |
104 | RTPMessage *dequeue(JitterBuffer *q, int *success) | 200 | * 1 when there's a good packet, |
201 | * 2 when there's a lost packet */ | ||
202 | static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *success) | ||
105 | { | 203 | { |
106 | if (q->top == q->bottom) { | 204 | if (q->top == q->bottom) { |
107 | *success = 0; | 205 | *success = 0; |
@@ -128,8 +226,7 @@ RTPMessage *dequeue(JitterBuffer *q, int *success) | |||
128 | return NULL; | 226 | return NULL; |
129 | } | 227 | } |
130 | 228 | ||
131 | 229 | static int init_video_decoder(CSSession *cs) | |
132 | int init_video_decoder(CodecState *cs) | ||
133 | { | 230 | { |
134 | int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION); | 231 | int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION); |
135 | 232 | ||
@@ -141,63 +238,20 @@ int init_video_decoder(CodecState *cs) | |||
141 | return 0; | 238 | return 0; |
142 | } | 239 | } |
143 | 240 | ||
144 | int init_audio_decoder(CodecState *cs, uint32_t audio_channels) | 241 | static int init_audio_decoder(CSSession *cs) |
145 | { | 242 | { |
146 | int rc; | 243 | int rc; |
147 | cs->audio_decoder = opus_decoder_create(cs->audio_sample_rate, audio_channels, &rc ); | 244 | cs->audio_decoder = opus_decoder_create(cs->audio_decoder_sample_rate, cs->audio_decoder_channels, &rc ); |
148 | 245 | ||
149 | if ( rc != OPUS_OK ) { | 246 | if ( rc != OPUS_OK ) { |
150 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | 247 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); |
151 | return -1; | 248 | return -1; |
152 | } | 249 | } |
153 | 250 | ||
154 | cs->audio_decoder_channels = audio_channels; | ||
155 | return 0; | 251 | return 0; |
156 | } | 252 | } |
157 | 253 | ||
158 | int reconfigure_video_encoder_resolution(CodecState *cs, uint16_t width, uint16_t height) | 254 | static int init_video_encoder(CSSession *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate) |
159 | { | ||
160 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | ||
161 | |||
162 | if (cfg.g_w == width && cfg.g_h == height) | ||
163 | return 0; | ||
164 | |||
165 | if (width * height > cs->max_width * cs->max_height) | ||
166 | return -1; | ||
167 | |||
168 | LOGGER_DEBUG("New video resolution: %u %u", width, height); | ||
169 | cfg.g_w = width; | ||
170 | cfg.g_h = height; | ||
171 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | ||
172 | |||
173 | if ( rc != VPX_CODEC_OK) { | ||
174 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
175 | return -1; | ||
176 | } | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | int reconfigure_video_encoder_bitrate(CodecState *cs, uint32_t video_bitrate) | ||
182 | { | ||
183 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | ||
184 | |||
185 | if (cfg.rc_target_bitrate == video_bitrate) | ||
186 | return 0; | ||
187 | |||
188 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); | ||
189 | cfg.rc_target_bitrate = video_bitrate; | ||
190 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | ||
191 | |||
192 | if ( rc != VPX_CODEC_OK) { | ||
193 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
194 | return -1; | ||
195 | } | ||
196 | |||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | int init_video_encoder(CodecState *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate) | ||
201 | { | 255 | { |
202 | vpx_codec_enc_cfg_t cfg; | 256 | vpx_codec_enc_cfg_t cfg; |
203 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); | 257 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); |
@@ -219,7 +273,6 @@ int init_video_encoder(CodecState *cs, uint16_t max_width, uint16_t max_height, | |||
219 | 273 | ||
220 | cs->max_width = max_width; | 274 | cs->max_width = max_width; |
221 | cs->max_height = max_height; | 275 | cs->max_height = max_height; |
222 | cs->bitrate = video_bitrate; | ||
223 | 276 | ||
224 | rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION); | 277 | rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION); |
225 | 278 | ||
@@ -238,17 +291,18 @@ int init_video_encoder(CodecState *cs, uint16_t max_width, uint16_t max_height, | |||
238 | return 0; | 291 | return 0; |
239 | } | 292 | } |
240 | 293 | ||
241 | int init_audio_encoder(CodecState *cs, uint32_t audio_channels) | 294 | static int init_audio_encoder(CSSession *cs) |
242 | { | 295 | { |
243 | int rc = OPUS_OK; | 296 | int rc = OPUS_OK; |
244 | cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, audio_channels, OPUS_APPLICATION_AUDIO, &rc); | 297 | cs->audio_encoder = opus_encoder_create(cs->audio_encoder_sample_rate, |
298 | cs->audio_encoder_channels, OPUS_APPLICATION_AUDIO, &rc); | ||
245 | 299 | ||
246 | if ( rc != OPUS_OK ) { | 300 | if ( rc != OPUS_OK ) { |
247 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | 301 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); |
248 | return -1; | 302 | return -1; |
249 | } | 303 | } |
250 | 304 | ||
251 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); | 305 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_encoder_bitrate)); |
252 | 306 | ||
253 | if ( rc != OPUS_OK ) { | 307 | if ( rc != OPUS_OK ) { |
254 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | 308 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); |
@@ -262,57 +316,309 @@ int init_audio_encoder(CodecState *cs, uint32_t audio_channels) | |||
262 | return -1; | 316 | return -1; |
263 | } | 317 | } |
264 | 318 | ||
265 | cs->audio_encoder_channels = audio_channels; | ||
266 | return 0; | 319 | return 0; |
267 | } | 320 | } |
268 | 321 | ||
322 | static float calculate_sum_sq (int16_t *n, uint16_t k) | ||
323 | { | ||
324 | float result = 0; | ||
325 | uint16_t i = 0; | ||
326 | |||
327 | for ( ; i < k; i ++) result += (float) (n[i] * n[i]); | ||
269 | 328 | ||
270 | CodecState *codec_init_session ( uint32_t audio_bitrate, | 329 | return result; |
271 | uint16_t audio_frame_duration, | 330 | } |
272 | uint32_t audio_sample_rate, | 331 | |
273 | uint32_t encoder_audio_channels, | 332 | |
274 | uint32_t decoder_audio_channels, | 333 | |
275 | uint32_t audio_VAD_tolerance_ms, | 334 | /* PUBLIC */ |
276 | uint16_t max_video_width, | 335 | int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length) |
277 | uint16_t max_video_height, | ||
278 | uint32_t video_bitrate ) | ||
279 | { | 336 | { |
280 | CodecState *retu = calloc(sizeof(CodecState), 1); | 337 | if (!cs || !length || length > cs->max_video_frame_size) { |
338 | LOGGER_ERROR("Invalid CodecState or video frame size: %u", length); | ||
339 | return -1; | ||
340 | } | ||
281 | 341 | ||
282 | if (!retu) return NULL; | 342 | cs->split_video_frame[0] = cs->frameid_out++; |
343 | cs->split_video_frame[1] = 0; | ||
344 | cs->processing_video_frame = payload; | ||
345 | cs->processing_video_frame_size = length; | ||
283 | 346 | ||
284 | retu->audio_bitrate = audio_bitrate; | 347 | return ((length - 1) / cs->video_frame_piece_size) + 1; |
285 | retu->audio_sample_rate = audio_sample_rate; | 348 | } |
286 | 349 | ||
287 | /* Encoders */ | 350 | const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size) |
288 | if (!max_video_width || !max_video_height) { /* Disable video */ | 351 | { |
289 | /*video_width = 320; | 352 | if (!cs || !size) return NULL; |
290 | video_height = 240; */ | 353 | |
354 | if (cs->processing_video_frame_size > cs->video_frame_piece_size) { | ||
355 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, | ||
356 | cs->processing_video_frame, | ||
357 | cs->video_frame_piece_size); | ||
358 | |||
359 | cs->processing_video_frame += cs->video_frame_piece_size; | ||
360 | cs->processing_video_frame_size -= cs->video_frame_piece_size; | ||
361 | |||
362 | *size = cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE; | ||
291 | } else { | 363 | } else { |
292 | retu->capabilities |= ( 0 == init_video_encoder(retu, max_video_width, max_video_height, | 364 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, |
293 | video_bitrate) ) ? v_encoding : 0; | 365 | cs->processing_video_frame, |
294 | retu->capabilities |= ( 0 == init_video_decoder(retu) ) ? v_decoding : 0; | 366 | cs->processing_video_frame_size); |
367 | |||
368 | *size = cs->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE; | ||
369 | } | ||
370 | |||
371 | cs->split_video_frame[1]++; | ||
372 | |||
373 | return cs->split_video_frame; | ||
374 | } | ||
375 | |||
376 | void cs_do(CSSession *cs) | ||
377 | { | ||
378 | if (!cs) return; | ||
379 | |||
380 | pthread_mutex_lock(cs->queue_mutex); | ||
381 | /* | ||
382 | if (!cs->active) { | ||
383 | pthread_mutex_unlock(cs->queue_mutex); | ||
384 | return; | ||
385 | } | ||
386 | |||
387 | /* Iterate over whole buffers and call playback callback * / | ||
388 | if (cs->abuf_ready) while (!DecodedAudioBuffer_empty(cs->abuf_ready)) { | ||
389 | DecodedAudio* p; | ||
390 | DecodedAudioBuffer_read(cs->abuf_ready, &p); | ||
391 | if (apcallback.first) | ||
392 | apcallback.first(cs->agent, cs->call_idx, p->data, p->size, apcallback.second); | ||
393 | |||
394 | free(p); | ||
395 | } | ||
396 | |||
397 | if (cs->vbuf_ready) while (!DecodedVideoBuffer_empty(cs->vbuf_ready)) { | ||
398 | vpx_image_t* p; | ||
399 | DecodedVideoBuffer_read(cs->vbuf_ready, &p); | ||
400 | if (vpcallback.first) | ||
401 | vpcallback.first(cs->agent, cs->call_idx, p, vpcallback.second); | ||
402 | |||
403 | vpx_img_free(p); | ||
404 | } | ||
405 | */ | ||
406 | |||
407 | Payload *p; | ||
408 | int rc; | ||
409 | |||
410 | if (cs->abuf_raw && !buffer_empty(cs->abuf_raw)) { | ||
411 | /* Decode audio */ | ||
412 | buffer_read(cs->abuf_raw, &p); | ||
413 | |||
414 | uint16_t fsize = (cs->audio_decoder_channels * | ||
415 | (cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000); | ||
416 | int16_t tmp[fsize]; | ||
417 | |||
418 | rc = opus_decode(cs->audio_decoder, p->data, p->size, tmp, fsize, (p->size == 0)); | ||
419 | free(p); | ||
420 | |||
421 | if (rc < 0) | ||
422 | LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); | ||
423 | else | ||
424 | /* Play */ | ||
425 | apcallback.first(cs->agent, cs->call_idx, tmp, rc, apcallback.second); | ||
426 | } | ||
427 | |||
428 | if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) { | ||
429 | /* Decode video */ | ||
430 | buffer_read(cs->vbuf_raw, &p); | ||
431 | |||
432 | rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); | ||
433 | free(p); | ||
434 | |||
435 | if (rc != VPX_CODEC_OK) { | ||
436 | LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); | ||
437 | } else { | ||
438 | vpx_codec_iter_t iter = NULL; | ||
439 | vpx_image_t *dest = vpx_codec_get_frame(&cs->v_decoder, &iter); | ||
440 | |||
441 | /* Play decoded images */ | ||
442 | for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) { | ||
443 | vpcallback.first(cs->agent, cs->call_idx, dest, vpcallback.second); | ||
444 | vpx_img_free(dest); | ||
445 | } | ||
446 | } | ||
447 | } | ||
448 | |||
449 | pthread_mutex_unlock(cs->queue_mutex); | ||
450 | } | ||
451 | |||
452 | void cs_register_audio_callback(CSAudioCallback cb, void *data) | ||
453 | { | ||
454 | apcallback.first = cb; | ||
455 | apcallback.second = data; | ||
456 | } | ||
457 | |||
458 | void cs_register_video_callback(CSVideoCallback cb, void *data) | ||
459 | { | ||
460 | vpcallback.first = cb; | ||
461 | vpcallback.second = data; | ||
462 | } | ||
463 | |||
464 | int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height) | ||
465 | { | ||
466 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | ||
467 | |||
468 | if (cfg.g_w == width && cfg.g_h == height) | ||
469 | return 0; | ||
470 | |||
471 | if (width * height > cs->max_width * cs->max_height) | ||
472 | return -1; | ||
473 | |||
474 | LOGGER_DEBUG("New video resolution: %u %u", width, height); | ||
475 | cfg.g_w = width; | ||
476 | cfg.g_h = height; | ||
477 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | ||
478 | |||
479 | if ( rc != VPX_CODEC_OK) { | ||
480 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
481 | return -1; | ||
482 | } | ||
483 | |||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate) | ||
488 | { | ||
489 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | ||
490 | |||
491 | if (cfg.rc_target_bitrate == video_bitrate) | ||
492 | return 0; | ||
493 | |||
494 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); | ||
495 | cfg.rc_target_bitrate = video_bitrate; | ||
496 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | ||
497 | |||
498 | if ( rc != VPX_CODEC_OK) { | ||
499 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
500 | return -1; | ||
295 | } | 501 | } |
296 | 502 | ||
297 | retu->capabilities |= ( 0 == init_audio_encoder(retu, encoder_audio_channels) ) ? a_encoding : 0; | 503 | return 0; |
298 | retu->capabilities |= ( 0 == init_audio_decoder(retu, decoder_audio_channels) ) ? a_decoding : 0; | 504 | } |
505 | |||
506 | CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video) | ||
507 | { | ||
508 | CSSession *cs = calloc(sizeof(CSSession), 1); | ||
299 | 509 | ||
300 | if ( retu->capabilities == 0 ) { /* everything failed */ | 510 | if (!cs) { |
301 | free (retu); | 511 | LOGGER_WARNING("Allocation failed! Application might misbehave!"); |
302 | return NULL; | 512 | return NULL; |
303 | } | 513 | } |
304 | 514 | ||
515 | if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) { | ||
516 | LOGGER_WARNING("Jitter buffer creaton failed!"); | ||
517 | goto error; | ||
518 | } | ||
519 | |||
520 | cs->audio_encoder_bitrate = cs_self->audio_bitrate; | ||
521 | cs->audio_encoder_sample_rate = cs_self->audio_sample_rate; | ||
522 | cs->audio_encoder_channels = cs_self->audio_channels; | ||
523 | cs->audio_encoder_frame_duration = cs_self->audio_frame_duration; | ||
524 | |||
525 | cs->audio_decoder_bitrate = cs_peer->audio_bitrate; | ||
526 | cs->audio_decoder_sample_rate = cs_peer->audio_sample_rate; | ||
527 | cs->audio_decoder_channels = cs_peer->audio_channels; | ||
528 | cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration; | ||
305 | 529 | ||
306 | retu->EVAD_tolerance = audio_VAD_tolerance_ms > audio_frame_duration ? | ||
307 | audio_VAD_tolerance_ms / audio_frame_duration : audio_frame_duration; | ||
308 | 530 | ||
309 | return retu; | 531 | cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? a_encoding : 0; |
532 | cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? a_decoding : 0; | ||
533 | |||
534 | if ( !(cs->capabilities & a_encoding) || !(cs->capabilities & a_decoding) ) goto error; | ||
535 | |||
536 | if ( !(cs->abuf_raw = buffer_new(jbuf_size)) ) goto error; | ||
537 | |||
538 | pthread_mutexattr_t attr; | ||
539 | |||
540 | if (pthread_mutexattr_init(&attr) != 0) goto error; | ||
541 | |||
542 | if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) { | ||
543 | if (pthread_mutexattr_destroy(&attr) != 0) | ||
544 | LOGGER_WARNING("Failed to destroy mutex attribute!"); | ||
545 | |||
546 | goto error; | ||
547 | } | ||
548 | |||
549 | if (pthread_mutex_init(cs->queue_mutex, &attr) != 0) { | ||
550 | pthread_mutexattr_destroy(&attr); | ||
551 | goto error; | ||
552 | } | ||
553 | |||
554 | if ((cs->support_video = has_video)) { | ||
555 | cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE; | ||
556 | cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE; | ||
557 | |||
558 | cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width, | ||
559 | cs_self->max_video_height, cs_self->video_bitrate) ) ? v_encoding : 0; | ||
560 | cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? v_decoding : 0; | ||
561 | |||
562 | if ( !(cs->capabilities & v_encoding) || !(cs->capabilities & v_decoding) ) goto error; | ||
563 | |||
564 | if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error; | ||
565 | |||
566 | if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) ) | ||
567 | goto error; | ||
568 | |||
569 | if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error; | ||
570 | } | ||
571 | |||
572 | if (pthread_mutexattr_destroy(&attr) != 0) | ||
573 | LOGGER_WARNING("Failed to destroy mutex attribute!"); | ||
574 | |||
575 | |||
576 | cs->active = 1; | ||
577 | return cs; | ||
578 | |||
579 | error: | ||
580 | LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); | ||
581 | |||
582 | buffer_free(cs->abuf_raw); | ||
583 | |||
584 | if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder); | ||
585 | |||
586 | if ( cs->audio_decoder ) opus_decoder_destroy(cs->audio_decoder); | ||
587 | |||
588 | |||
589 | if (has_video) { | ||
590 | if ( cs->capabilities & v_decoding ) vpx_codec_destroy(&cs->v_decoder); | ||
591 | |||
592 | if ( cs->capabilities & v_encoding ) vpx_codec_destroy(&cs->v_encoder); | ||
593 | |||
594 | buffer_free(cs->vbuf_raw); | ||
595 | |||
596 | free(cs->frame_buf); | ||
597 | free(cs->split_video_frame); | ||
598 | } | ||
599 | |||
600 | jbuf_free(cs->j_buf); | ||
601 | free(cs); | ||
602 | |||
603 | return NULL; | ||
310 | } | 604 | } |
311 | 605 | ||
312 | void codec_terminate_session ( CodecState *cs ) | 606 | void cs_kill(CSSession *cs) |
313 | { | 607 | { |
314 | if (!cs) return; | 608 | if (!cs) return; |
315 | 609 | ||
610 | /* Lock running mutex and signal that cs is no longer active */ | ||
611 | pthread_mutex_lock(cs->queue_mutex); | ||
612 | cs->active = 0; | ||
613 | |||
614 | /* Wait threads to close */ | ||
615 | pthread_mutex_unlock(cs->queue_mutex); | ||
616 | pthread_mutex_lock(cs->queue_mutex); | ||
617 | pthread_mutex_unlock(cs->queue_mutex); | ||
618 | |||
619 | pthread_mutex_destroy(cs->queue_mutex); | ||
620 | |||
621 | |||
316 | if ( cs->audio_encoder ) | 622 | if ( cs->audio_encoder ) |
317 | opus_encoder_destroy(cs->audio_encoder); | 623 | opus_encoder_destroy(cs->audio_encoder); |
318 | 624 | ||
@@ -325,21 +631,21 @@ void codec_terminate_session ( CodecState *cs ) | |||
325 | if ( cs->capabilities & v_encoding ) | 631 | if ( cs->capabilities & v_encoding ) |
326 | vpx_codec_destroy(&cs->v_encoder); | 632 | vpx_codec_destroy(&cs->v_encoder); |
327 | 633 | ||
634 | jbuf_free(cs->j_buf); | ||
635 | buffer_free(cs->abuf_raw); | ||
636 | buffer_free(cs->vbuf_raw); | ||
637 | free(cs->frame_buf); | ||
638 | |||
328 | LOGGER_DEBUG("Terminated codec state: %p", cs); | 639 | LOGGER_DEBUG("Terminated codec state: %p", cs); |
329 | free(cs); | 640 | free(cs); |
330 | } | 641 | } |
331 | 642 | ||
332 | static float calculate_sum_sq (int16_t *n, uint16_t k) | 643 | void cs_set_vad_treshold(CSSession *cs, uint32_t treshold, uint16_t frame_duration) |
333 | { | 644 | { |
334 | float result = 0; | 645 | cs->EVAD_tolerance = treshold > frame_duration ? treshold / frame_duration : frame_duration; |
335 | uint16_t i = 0; | ||
336 | |||
337 | for ( ; i < k; i ++) result += (float) (n[i] * n[i]); | ||
338 | |||
339 | return result; | ||
340 | } | 646 | } |
341 | 647 | ||
342 | int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy) | 648 | int cs_calculate_vad(CSSession *cs, int16_t *PCM, uint16_t frame_size, float energy) |
343 | { | 649 | { |
344 | float frame_energy = sqrt(calculate_sum_sq(PCM, frame_size)) / frame_size; | 650 | float frame_energy = sqrt(calculate_sum_sq(PCM, frame_size)) / frame_size; |
345 | 651 | ||
@@ -355,3 +661,103 @@ int energy_VAD(CodecState *cs, int16_t *PCM, uint16_t frame_size, float energy) | |||
355 | 661 | ||
356 | return 0; | 662 | return 0; |
357 | } | 663 | } |
664 | |||
665 | |||
666 | |||
667 | |||
668 | /* Called from RTP */ | ||
669 | void queue_message(RTPSession *session, RTPMessage *msg) | ||
670 | { | ||
671 | CSSession *cs = session->cs; | ||
672 | |||
673 | if (!cs || !cs->active) return; | ||
674 | |||
675 | /* Audio */ | ||
676 | if (session->payload_type == type_audio % 128) { | ||
677 | jbuf_write(cs->j_buf, msg); | ||
678 | |||
679 | pthread_mutex_lock(cs->queue_mutex); | ||
680 | int success = 0; | ||
681 | |||
682 | while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { | ||
683 | Payload *p; | ||
684 | |||
685 | if (success == 2) { | ||
686 | p = malloc(sizeof(Payload)); | ||
687 | |||
688 | if (p) p->size = 0; | ||
689 | |||
690 | } else { | ||
691 | p = malloc(sizeof(Payload) + msg->length); | ||
692 | |||
693 | if (p) { | ||
694 | p->size = msg->length; | ||
695 | memcpy(p->data, msg->data, msg->length); | ||
696 | } | ||
697 | |||
698 | rtp_free_msg(NULL, msg); | ||
699 | } | ||
700 | |||
701 | if (p) { | ||
702 | buffer_write(cs->abuf_raw, p); | ||
703 | } else { | ||
704 | LOGGER_WARNING("Allocation failed! Program might misbehave!"); | ||
705 | } | ||
706 | } | ||
707 | |||
708 | pthread_mutex_unlock(cs->queue_mutex); | ||
709 | } | ||
710 | /* Video */ | ||
711 | else { | ||
712 | uint8_t *packet = msg->data; | ||
713 | uint32_t packet_size = msg->length; | ||
714 | |||
715 | if (packet_size < VIDEOFRAME_HEADER_SIZE) | ||
716 | goto end; | ||
717 | |||
718 | if (packet[0] > cs->frameid_in || (msg->header->timestamp > cs->last_timestamp)) { /* New frame */ | ||
719 | /* Flush last frames' data and get ready for this frame */ | ||
720 | Payload *p = malloc(sizeof(Payload) + cs->frame_size); | ||
721 | |||
722 | if (p) { | ||
723 | pthread_mutex_lock(cs->queue_mutex); | ||
724 | |||
725 | if (buffer_full(cs->vbuf_raw)) { | ||
726 | LOGGER_DEBUG("Dropped video frame"); | ||
727 | Payload *tp; | ||
728 | buffer_read(cs->vbuf_raw, &tp); | ||
729 | free(tp); | ||
730 | } else { | ||
731 | p->size = cs->frame_size; | ||
732 | memcpy(p->data, cs->frame_buf, cs->frame_size); | ||
733 | } | ||
734 | |||
735 | buffer_write(cs->vbuf_raw, p); | ||
736 | pthread_mutex_unlock(cs->queue_mutex); | ||
737 | } else { | ||
738 | LOGGER_WARNING("Allocation failed! Program might misbehave!"); | ||
739 | goto end; | ||
740 | } | ||
741 | |||
742 | cs->last_timestamp = msg->header->timestamp; | ||
743 | cs->frameid_in = packet[0]; | ||
744 | memset(cs->frame_buf, 0, cs->frame_size); | ||
745 | cs->frame_size = 0; | ||
746 | |||
747 | } else if (packet[0] < cs->frameid_in) { /* Old frame; drop */ | ||
748 | LOGGER_DEBUG("Old packet: %u", packet[0]); | ||
749 | goto end; | ||
750 | } | ||
751 | |||
752 | /* Otherwise it's part of the frame so just process */ | ||
753 | /* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */ | ||
754 | memcpy(cs->frame_buf + cs->frame_size, | ||
755 | packet + VIDEOFRAME_HEADER_SIZE, | ||
756 | packet_size - VIDEOFRAME_HEADER_SIZE); | ||
757 | |||
758 | cs->frame_size += packet_size - VIDEOFRAME_HEADER_SIZE; | ||
759 | |||
760 | end: | ||
761 | rtp_free_msg(NULL, msg); | ||
762 | } | ||
763 | } \ No newline at end of file | ||