diff options
author | mannol <eniz_vukovic@hotmail.com> | 2015-01-24 23:29:54 +0100 |
---|---|---|
committer | mannol <eniz_vukovic@hotmail.com> | 2015-01-24 23:29:54 +0100 |
commit | 1450c22d01cbb5185ee8eac14657ddf3301d7e48 (patch) | |
tree | 366f795f56689b0fb53dfe52941fb9c813429e8b /toxav/codec.c | |
parent | e57fb8c12ea7de1a5070ea0fc6f14c8e242c409f (diff) |
Current progress
Diffstat (limited to 'toxav/codec.c')
-rw-r--r-- | toxav/codec.c | 529 |
1 files changed, 343 insertions, 186 deletions
diff --git a/toxav/codec.c b/toxav/codec.c index dae35d54..fd2f9f93 100644 --- a/toxav/codec.c +++ b/toxav/codec.c | |||
@@ -31,6 +31,7 @@ | |||
31 | 31 | ||
32 | #include <stdio.h> | 32 | #include <stdio.h> |
33 | #include <stdlib.h> | 33 | #include <stdlib.h> |
34 | #include <stdbool.h> | ||
34 | #include <math.h> | 35 | #include <math.h> |
35 | #include <assert.h> | 36 | #include <assert.h> |
36 | #include <time.h> | 37 | #include <time.h> |
@@ -39,6 +40,9 @@ | |||
39 | #include "rtp.h" | 40 | #include "rtp.h" |
40 | #include "codec.h" | 41 | #include "codec.h" |
41 | 42 | ||
43 | |||
44 | #define DEFAULT_JBUF 6 | ||
45 | |||
42 | /* Good quality encode. */ | 46 | /* Good quality encode. */ |
43 | #define MAX_ENCODE_TIME_US VPX_DL_GOOD_QUALITY | 47 | #define MAX_ENCODE_TIME_US VPX_DL_GOOD_QUALITY |
44 | #define MAX_DECODE_TIME_US 0 | 48 | #define MAX_DECODE_TIME_US 0 |
@@ -62,12 +66,12 @@ typedef struct { | |||
62 | Payload **packets; | 66 | Payload **packets; |
63 | } PayloadBuffer; | 67 | } PayloadBuffer; |
64 | 68 | ||
65 | static _Bool buffer_full(const PayloadBuffer *b) | 69 | static bool buffer_full(const PayloadBuffer *b) |
66 | { | 70 | { |
67 | return (b->end + 1) % b->size == b->start; | 71 | return (b->end + 1) % b->size == b->start; |
68 | } | 72 | } |
69 | 73 | ||
70 | static _Bool buffer_empty(const PayloadBuffer *b) | 74 | static bool buffer_empty(const PayloadBuffer *b) |
71 | { | 75 | { |
72 | return b->end == b->start; | 76 | return b->end == b->start; |
73 | } | 77 | } |
@@ -121,7 +125,7 @@ static void buffer_free(PayloadBuffer *b) | |||
121 | } | 125 | } |
122 | 126 | ||
123 | /* JITTER BUFFER WORK */ | 127 | /* JITTER BUFFER WORK */ |
124 | typedef struct _JitterBuffer { | 128 | typedef struct { |
125 | RTPMessage **queue; | 129 | RTPMessage **queue; |
126 | uint32_t size; | 130 | uint32_t size; |
127 | uint32_t capacity; | 131 | uint32_t capacity; |
@@ -225,99 +229,9 @@ static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *success) | |||
225 | return NULL; | 229 | return NULL; |
226 | } | 230 | } |
227 | 231 | ||
228 | static int init_video_decoder(CSSession *cs) | ||
229 | { | ||
230 | int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION); | ||
231 | |||
232 | if ( rc != VPX_CODEC_OK) { | ||
233 | LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); | ||
234 | return -1; | ||
235 | } | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | static int init_audio_decoder(CSSession *cs) | ||
241 | { | ||
242 | int rc; | ||
243 | cs->audio_decoder = opus_decoder_create(cs->audio_decoder_sample_rate, cs->audio_decoder_channels, &rc ); | ||
244 | |||
245 | if ( rc != OPUS_OK ) { | ||
246 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | ||
247 | return -1; | ||
248 | } | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int init_video_encoder(CSSession *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate) | ||
254 | { | ||
255 | vpx_codec_enc_cfg_t cfg; | ||
256 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); | ||
257 | |||
258 | if (rc != VPX_CODEC_OK) { | ||
259 | LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); | ||
260 | return -1; | ||
261 | } | ||
262 | |||
263 | cfg.rc_target_bitrate = video_bitrate; | ||
264 | cfg.g_w = max_width; | ||
265 | cfg.g_h = max_height; | ||
266 | cfg.g_pass = VPX_RC_ONE_PASS; | ||
267 | cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS; | ||
268 | cfg.g_lag_in_frames = 0; | ||
269 | cfg.kf_min_dist = 0; | ||
270 | cfg.kf_max_dist = 48; | ||
271 | cfg.kf_mode = VPX_KF_AUTO; | ||
272 | |||
273 | rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION); | ||
274 | |||
275 | if ( rc != VPX_CODEC_OK) { | ||
276 | LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); | ||
277 | return -1; | ||
278 | } | ||
279 | |||
280 | rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8); | ||
281 | |||
282 | if ( rc != VPX_CODEC_OK) { | ||
283 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
284 | return -1; | ||
285 | } | ||
286 | |||
287 | cs->max_width = max_width; | ||
288 | cs->max_height = max_height; | ||
289 | cs->video_bitrate = video_bitrate; | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int init_audio_encoder(CSSession *cs) | ||
295 | { | ||
296 | int rc = OPUS_OK; | ||
297 | cs->audio_encoder = opus_encoder_create(cs->audio_encoder_sample_rate, | ||
298 | cs->audio_encoder_channels, OPUS_APPLICATION_AUDIO, &rc); | ||
299 | |||
300 | if ( rc != OPUS_OK ) { | ||
301 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
302 | return -1; | ||
303 | } | ||
304 | |||
305 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_encoder_bitrate)); | ||
306 | 232 | ||
307 | if ( rc != OPUS_OK ) { | ||
308 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
309 | return -1; | ||
310 | } | ||
311 | 233 | ||
312 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); | ||
313 | 234 | ||
314 | if ( rc != OPUS_OK ) { | ||
315 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
316 | return -1; | ||
317 | } | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | 235 | ||
322 | /* PUBLIC */ | 236 | /* PUBLIC */ |
323 | int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length) | 237 | int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length) |
@@ -406,17 +320,17 @@ void cs_do(CSSession *cs) | |||
406 | /* Leave space for (possibly) other thread to queue more data after we read it here */ | 320 | /* Leave space for (possibly) other thread to queue more data after we read it here */ |
407 | pthread_mutex_unlock(cs->queue_mutex); | 321 | pthread_mutex_unlock(cs->queue_mutex); |
408 | 322 | ||
409 | rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); | 323 | rc = vpx_codec_decode(cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); |
410 | free(p); | 324 | free(p); |
411 | 325 | ||
412 | if (rc != VPX_CODEC_OK) { | 326 | if (rc != VPX_CODEC_OK) { |
413 | LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); | 327 | LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); |
414 | } else { | 328 | } else { |
415 | vpx_codec_iter_t iter = NULL; | 329 | vpx_codec_iter_t iter = NULL; |
416 | vpx_image_t *dest = vpx_codec_get_frame(&cs->v_decoder, &iter); | 330 | vpx_image_t *dest = vpx_codec_get_frame(cs->v_decoder, &iter); |
417 | 331 | ||
418 | /* Play decoded images */ | 332 | /* Play decoded images */ |
419 | for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) { | 333 | for (; dest; dest = vpx_codec_get_frame(cs->v_decoder, &iter)) { |
420 | if (cs->vcb.first) | 334 | if (cs->vcb.first) |
421 | cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second); | 335 | cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second); |
422 | 336 | ||
@@ -430,13 +344,17 @@ void cs_do(CSSession *cs) | |||
430 | pthread_mutex_unlock(cs->queue_mutex); | 344 | pthread_mutex_unlock(cs->queue_mutex); |
431 | } | 345 | } |
432 | 346 | ||
433 | int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height) | 347 | int cs_set_sending_video_resolution(CSSession *cs, uint16_t width, uint16_t height) |
434 | { | 348 | { |
435 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | 349 | if (!cs->v_encoding) |
436 | 350 | return -1; | |
351 | |||
352 | /* TODO FIXME reference is safe? */ | ||
353 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; | ||
354 | |||
437 | if (cfg.g_w == width && cfg.g_h == height) | 355 | if (cfg.g_w == width && cfg.g_h == height) |
438 | return 0; | 356 | return 0; |
439 | 357 | /* | |
440 | if (width * height > cs->max_width * cs->max_height) { | 358 | if (width * height > cs->max_width * cs->max_height) { |
441 | vpx_codec_ctx_t v_encoder = cs->v_encoder; | 359 | vpx_codec_ctx_t v_encoder = cs->v_encoder; |
442 | 360 | ||
@@ -447,12 +365,12 @@ int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t heig | |||
447 | 365 | ||
448 | vpx_codec_destroy(&v_encoder); | 366 | vpx_codec_destroy(&v_encoder); |
449 | return 0; | 367 | return 0; |
450 | } | 368 | }*/ |
451 | 369 | ||
452 | LOGGER_DEBUG("New video resolution: %u %u", width, height); | 370 | LOGGER_DEBUG("New video resolution: %u %u", width, height); |
453 | cfg.g_w = width; | 371 | cfg.g_w = width; |
454 | cfg.g_h = height; | 372 | cfg.g_h = height; |
455 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | 373 | int rc = vpx_codec_enc_config_set(cs->v_encoder, &cfg); |
456 | 374 | ||
457 | if ( rc != VPX_CODEC_OK) { | 375 | if ( rc != VPX_CODEC_OK) { |
458 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | 376 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); |
@@ -462,27 +380,107 @@ int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t heig | |||
462 | return 0; | 380 | return 0; |
463 | } | 381 | } |
464 | 382 | ||
465 | int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate) | 383 | int cs_set_sending_video_bitrate(CSSession *cs, uint32_t bitrate) |
466 | { | 384 | { |
467 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | 385 | if (!cs->v_encoding) |
468 | 386 | return -1; | |
469 | if (cfg.rc_target_bitrate == video_bitrate) | 387 | |
388 | /* TODO FIXME reference is safe? */ | ||
389 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; | ||
390 | if (cfg.rc_target_bitrate == bitrate) | ||
470 | return 0; | 391 | return 0; |
471 | 392 | ||
472 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); | 393 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); |
473 | cfg.rc_target_bitrate = video_bitrate; | 394 | cfg.rc_target_bitrate = bitrate; |
474 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | 395 | |
475 | 396 | int rc = vpx_codec_enc_config_set(cs->v_encoder, &cfg); | |
476 | if ( rc != VPX_CODEC_OK) { | 397 | if ( rc != VPX_CODEC_OK) { |
477 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | 398 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); |
478 | return cs_ErrorSettingVideoBitrate; | 399 | return cs_ErrorSettingVideoBitrate; |
479 | } | 400 | } |
480 | 401 | ||
481 | cs->video_bitrate = video_bitrate; | ||
482 | return 0; | 402 | return 0; |
483 | } | 403 | } |
484 | 404 | ||
485 | CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video) | 405 | int cs_set_sending_audio_bitrate(CSSession *cs, int32_t rate) |
406 | { | ||
407 | if (cs->audio_encoder == NULL) | ||
408 | return -1; | ||
409 | |||
410 | int rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(rate)); | ||
411 | |||
412 | if ( rc != OPUS_OK ) { | ||
413 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
414 | return -1; | ||
415 | } | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | int cs_set_sending_audio_sampling_rate(CSSession* cs, int32_t rate) | ||
421 | { | ||
422 | /* TODO Find a better way? */ | ||
423 | if (cs->audio_encoder == NULL) | ||
424 | return -1; | ||
425 | |||
426 | int rc = OPUS_OK; | ||
427 | int last_rate = 0; | ||
428 | |||
429 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(&last_rate)); | ||
430 | |||
431 | if ( rc != OPUS_OK ) { | ||
432 | LOGGER_ERROR("Error while getting encoder ctl: %s", opus_strerror(rc)); | ||
433 | return -1; | ||
434 | } | ||
435 | |||
436 | if (rate == last_rate) | ||
437 | return 0; | ||
438 | |||
439 | OpusEncoder* new_enc = opus_encoder_create(rate, cs->channels, OPUS_APPLICATION_AUDIO, &rc); | ||
440 | |||
441 | if ( rc != OPUS_OK ) { | ||
442 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
443 | return -1; | ||
444 | } | ||
445 | |||
446 | opus_encoder_destroy(cs->audio_encoder); | ||
447 | cs->audio_encoder = new_enc; | ||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | int cs_set_sending_audio_channels(CSSession* cs, int32_t count) | ||
452 | { | ||
453 | /* TODO Find a better way? */ | ||
454 | if (cs->audio_encoder == NULL) | ||
455 | return -1; | ||
456 | |||
457 | if (cs->channels == count) | ||
458 | return 0; | ||
459 | |||
460 | int rc = OPUS_OK; | ||
461 | int bitrate = 0; | ||
462 | |||
463 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(&bitrate)); | ||
464 | |||
465 | if ( rc != OPUS_OK ) { | ||
466 | LOGGER_ERROR("Error while getting encoder ctl: %s", opus_strerror(rc)); | ||
467 | return -1; | ||
468 | } | ||
469 | |||
470 | cs->channels = count; | ||
471 | OpusEncoder* new_enc = opus_encoder_create(bitrate, cs->channels, OPUS_APPLICATION_AUDIO, &rc); | ||
472 | |||
473 | if ( rc != OPUS_OK ) { | ||
474 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
475 | return -1; | ||
476 | } | ||
477 | |||
478 | opus_encoder_destroy(cs->audio_encoder); | ||
479 | cs->audio_encoder = new_enc; | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | CSSession *cs_new(uint32_t s_audio_b, uint32_t p_audio_b, uint32_t s_video_b, uint32_t p_video_b) | ||
486 | { | 484 | { |
487 | CSSession *cs = calloc(sizeof(CSSession), 1); | 485 | CSSession *cs = calloc(sizeof(CSSession), 1); |
488 | 486 | ||
@@ -491,106 +489,265 @@ CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, | |||
491 | return NULL; | 489 | return NULL; |
492 | } | 490 | } |
493 | 491 | ||
494 | if (create_recursive_mutex(cs->queue_mutex) != 0) { | 492 | /* TODO this has to be exchanged in msi */ |
495 | LOGGER_WARNING("Failed to create recursive mutex!"); | 493 | cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE; |
496 | free(cs); | 494 | cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE; |
497 | return NULL; | 495 | |
496 | if (s_audio_b > 0 && 0 != cs_enable_audio_sending(cs, s_audio_b)) { /* Sending audio enabled */ | ||
497 | LOGGER_WARNING("Failed to enable audio sending!"); | ||
498 | goto FAILURE; | ||
498 | } | 499 | } |
499 | 500 | ||
500 | if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) { | 501 | if (p_audio_b > 0 && 0 != cs_enable_audio_receiving(cs)) { /* Receiving audio enabled */ |
501 | LOGGER_WARNING("Jitter buffer creaton failed!"); | 502 | LOGGER_WARNING("Failed to enable audio receiving!"); |
502 | goto error; | 503 | goto FAILURE; |
503 | } | 504 | } |
504 | 505 | ||
505 | cs->audio_encoder_bitrate = cs_self->audio_bitrate; | 506 | if (s_video_b > 0 && 0 != cs_enable_video_sending(cs, s_video_b)) { /* Sending video enabled */ |
506 | cs->audio_encoder_sample_rate = cs_self->audio_sample_rate; | 507 | LOGGER_WARNING("Failed to enable video sending!"); |
507 | cs->audio_encoder_channels = cs_self->audio_channels; | 508 | goto FAILURE; |
508 | cs->audio_encoder_frame_duration = cs_self->audio_frame_duration; | 509 | } |
509 | 510 | ||
510 | cs->audio_decoder_bitrate = cs_peer->audio_bitrate; | 511 | if (p_video_b > 0 && 0 != cs_enable_video_receiving(cs)) { /* Receiving video enabled */ |
511 | cs->audio_decoder_sample_rate = cs_peer->audio_sample_rate; | 512 | LOGGER_WARNING("Failed to enable video receiving!"); |
512 | cs->audio_decoder_channels = cs_peer->audio_channels; | 513 | goto FAILURE; |
513 | cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration; | ||
514 | |||
515 | |||
516 | cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? cs_AudioEncoding : 0; | ||
517 | cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? cs_AudioDecoding : 0; | ||
518 | |||
519 | if ( !(cs->capabilities & cs_AudioEncoding) || !(cs->capabilities & cs_AudioDecoding) ) goto error; | ||
520 | |||
521 | if ((cs->support_video = has_video)) { | ||
522 | cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE; | ||
523 | cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE; | ||
524 | |||
525 | cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width, | ||
526 | cs_self->max_video_height, cs_self->video_bitrate) ) ? cs_VideoEncoding : 0; | ||
527 | cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? cs_VideoDecoding : 0; | ||
528 | |||
529 | if ( !(cs->capabilities & cs_VideoEncoding) || !(cs->capabilities & cs_VideoDecoding) ) goto error; | ||
530 | |||
531 | if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error; | ||
532 | |||
533 | if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) ) | ||
534 | goto error; | ||
535 | |||
536 | if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error; | ||
537 | } | 514 | } |
538 | 515 | ||
539 | return cs; | 516 | return cs; |
540 | 517 | ||
541 | error: | 518 | FAILURE: |
542 | LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); | 519 | LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); |
543 | 520 | ||
544 | pthread_mutex_destroy(cs->queue_mutex); | 521 | cs_disable_audio_sending(cs); |
545 | 522 | cs_disable_audio_receiving(cs); | |
546 | if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder); | 523 | cs_disable_video_sending(cs); |
547 | 524 | cs_disable_video_receiving(cs); | |
548 | if ( cs->audio_decoder ) opus_decoder_destroy(cs->audio_decoder); | 525 | |
549 | 526 | free(cs); | |
550 | |||
551 | if (has_video) { | ||
552 | if ( cs->capabilities & cs_VideoDecoding ) vpx_codec_destroy(&cs->v_decoder); | ||
553 | 527 | ||
554 | if ( cs->capabilities & cs_VideoEncoding ) vpx_codec_destroy(&cs->v_encoder); | 528 | return NULL; |
529 | } | ||
555 | 530 | ||
556 | buffer_free(cs->vbuf_raw); | 531 | void cs_kill(CSSession *cs) |
532 | { | ||
533 | if (!cs) | ||
534 | return; | ||
557 | 535 | ||
558 | free(cs->frame_buf); | 536 | /* NOTE: queue_message() will not be called since |
559 | free(cs->split_video_frame); | 537 | * the callback is unregistered before cs_kill is called. |
560 | } | 538 | */ |
539 | |||
540 | cs_disable_audio_sending(cs); | ||
541 | cs_disable_audio_receiving(cs); | ||
542 | cs_disable_video_sending(cs); | ||
543 | cs_disable_video_receiving(cs); | ||
561 | 544 | ||
562 | jbuf_free(cs->j_buf); | 545 | LOGGER_DEBUG("Terminated codec state: %p", cs); |
563 | free(cs); | 546 | free(cs); |
547 | } | ||
564 | 548 | ||
565 | return NULL; | 549 | int cs_enable_audio_sending(CSSession* cs, uint32_t bitrate) |
550 | { | ||
551 | if (cs->audio_encoder) | ||
552 | return 0; | ||
553 | |||
554 | /** | ||
555 | * Encoder is initialized with default values. These values (Sampling rate, channel count) | ||
556 | * change on the fly from toxav. | ||
557 | */ | ||
558 | |||
559 | int rc = OPUS_OK; | ||
560 | cs->audio_encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_AUDIO, &rc); | ||
561 | |||
562 | if ( rc != OPUS_OK ) { | ||
563 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
564 | return -1; | ||
565 | } | ||
566 | |||
567 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(bitrate)); | ||
568 | |||
569 | if ( rc != OPUS_OK ) { | ||
570 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
571 | goto FAILURE; | ||
572 | } | ||
573 | |||
574 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); | ||
575 | |||
576 | if ( rc != OPUS_OK ) { | ||
577 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
578 | goto FAILURE; | ||
579 | } | ||
580 | |||
581 | cs->channels = 2; | ||
582 | return 0; | ||
583 | |||
584 | FAILURE: | ||
585 | cs_disable_audio_sending(cs); | ||
586 | return -1; | ||
566 | } | 587 | } |
567 | 588 | ||
568 | void cs_kill(CSSession *cs) | 589 | int cs_enable_audio_receiving(CSSession* cs) |
569 | { | 590 | { |
570 | if (!cs) return; | 591 | if (cs->audio_decoder) |
592 | return 0; | ||
593 | |||
594 | /** | ||
595 | * Decoder is initialized with default values. These values (Sampling rate, channel count) | ||
596 | * change on the fly from toxav. | ||
597 | */ | ||
598 | |||
599 | int rc; | ||
600 | cs->audio_decoder = opus_decoder_create(48000, 2, &rc ); | ||
601 | |||
602 | if ( rc != OPUS_OK ) { | ||
603 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | ||
604 | return -1; | ||
605 | } | ||
606 | |||
607 | |||
608 | if ( !(cs->j_buf = jbuf_new(DEFAULT_JBUF)) ) { | ||
609 | LOGGER_WARNING("Jitter buffer creaton failed!"); | ||
610 | opus_decoder_destroy(cs->audio_decoder); | ||
611 | cs->audio_decoder = NULL; | ||
612 | return -1; | ||
613 | } | ||
614 | |||
615 | |||
616 | return 0; | ||
617 | } | ||
571 | 618 | ||
572 | /* queue_message will not be called since it's unregistered before cs_kill is called */ | 619 | int cs_enable_video_sending(CSSession* cs, uint32_t bitrate) |
573 | pthread_mutex_destroy(cs->queue_mutex); | 620 | { |
621 | if (cs->v_encoding) | ||
622 | return 0; | ||
623 | |||
624 | vpx_codec_enc_cfg_t cfg; | ||
625 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); | ||
626 | |||
627 | if (rc != VPX_CODEC_OK) { | ||
628 | LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); | ||
629 | return -1; | ||
630 | } | ||
631 | |||
632 | rc = vpx_codec_enc_init_ver(cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, | ||
633 | VPX_ENCODER_ABI_VERSION); | ||
634 | |||
635 | if ( rc != VPX_CODEC_OK) { | ||
636 | LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); | ||
637 | return -1; | ||
638 | } | ||
639 | |||
640 | /* So that we can use cs_disable_video_sending to clean up */ | ||
641 | cs->v_encoding = true; | ||
642 | |||
643 | if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) ) | ||
644 | goto FAILURE; | ||
645 | |||
646 | cfg.rc_target_bitrate = bitrate; | ||
647 | cfg.g_w = 800; | ||
648 | cfg.g_h = 600; | ||
649 | cfg.g_pass = VPX_RC_ONE_PASS; | ||
650 | cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS; | ||
651 | cfg.g_lag_in_frames = 0; | ||
652 | cfg.kf_min_dist = 0; | ||
653 | cfg.kf_max_dist = 48; | ||
654 | cfg.kf_mode = VPX_KF_AUTO; | ||
655 | |||
656 | |||
657 | rc = vpx_codec_control(cs->v_encoder, VP8E_SET_CPUUSED, 8); | ||
658 | |||
659 | if ( rc != VPX_CODEC_OK) { | ||
660 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
661 | goto FAILURE; | ||
662 | } | ||
663 | |||
664 | return 0; | ||
665 | |||
666 | FAILURE: | ||
667 | cs_disable_video_sending(cs); | ||
668 | return -1; | ||
669 | } | ||
574 | 670 | ||
671 | int cs_enable_video_receiving(CSSession* cs) | ||
672 | { | ||
673 | if (cs->v_decoding) | ||
674 | return 0; | ||
675 | |||
676 | if (create_recursive_mutex(cs->queue_mutex) != 0) { | ||
677 | LOGGER_WARNING("Failed to create recursive mutex!"); | ||
678 | return -1; | ||
679 | } | ||
680 | |||
681 | int rc = vpx_codec_dec_init_ver(cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, | ||
682 | NULL, 0, VPX_DECODER_ABI_VERSION); | ||
683 | |||
684 | if ( rc != VPX_CODEC_OK) { | ||
685 | LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); | ||
686 | |||
687 | pthread_mutex_destroy(cs->queue_mutex); | ||
688 | return -1; | ||
689 | } | ||
690 | |||
691 | /* So that we can use cs_disable_video_sending to clean up */ | ||
692 | cs->v_decoding = true; | ||
693 | |||
694 | if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) | ||
695 | goto FAILURE; | ||
696 | |||
697 | if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) | ||
698 | goto FAILURE; | ||
699 | |||
700 | return 0; | ||
701 | |||
702 | FAILURE: | ||
703 | cs_disable_video_receiving(cs); | ||
704 | return -1; | ||
705 | } | ||
575 | 706 | ||
576 | if ( cs->audio_encoder ) | 707 | void cs_disable_audio_sending(CSSession* cs) |
708 | { | ||
709 | if ( cs->audio_encoder ) { | ||
577 | opus_encoder_destroy(cs->audio_encoder); | 710 | opus_encoder_destroy(cs->audio_encoder); |
711 | cs->audio_encoder = NULL; | ||
712 | cs->channels = 0; | ||
713 | } | ||
714 | } | ||
578 | 715 | ||
579 | if ( cs->audio_decoder ) | 716 | void cs_disable_audio_receiving(CSSession* cs) |
717 | { | ||
718 | if ( cs->audio_decoder ) { | ||
580 | opus_decoder_destroy(cs->audio_decoder); | 719 | opus_decoder_destroy(cs->audio_decoder); |
720 | cs->audio_decoder = NULL; | ||
721 | jbuf_free(cs->j_buf); | ||
722 | cs->j_buf = NULL; | ||
723 | } | ||
724 | } | ||
581 | 725 | ||
582 | if ( cs->capabilities & cs_VideoDecoding ) | 726 | void cs_disable_video_sending(CSSession* cs) |
583 | vpx_codec_destroy(&cs->v_decoder); | 727 | { |
584 | 728 | if (cs->v_encoding) { | |
585 | if ( cs->capabilities & cs_VideoEncoding ) | 729 | cs->v_encoding = false; |
586 | vpx_codec_destroy(&cs->v_encoder); | 730 | |
587 | 731 | free(cs->split_video_frame); | |
588 | jbuf_free(cs->j_buf); | 732 | cs->split_video_frame = NULL; |
589 | buffer_free(cs->vbuf_raw); | 733 | |
590 | free(cs->frame_buf); | 734 | vpx_codec_destroy(cs->v_encoder); |
735 | } | ||
736 | } | ||
591 | 737 | ||
592 | LOGGER_DEBUG("Terminated codec state: %p", cs); | 738 | void cs_disable_video_receiving(CSSession* cs) |
593 | free(cs); | 739 | { |
740 | if (cs->v_decoding) { | ||
741 | cs->v_decoding = false; | ||
742 | |||
743 | buffer_free(cs->vbuf_raw); | ||
744 | cs->vbuf_raw = NULL; | ||
745 | free(cs->frame_buf); | ||
746 | cs->frame_buf = NULL; | ||
747 | |||
748 | vpx_codec_destroy(cs->v_decoder); | ||
749 | pthread_mutex_destroy(cs->queue_mutex); | ||
750 | } | ||
594 | } | 751 | } |
595 | 752 | ||
596 | 753 | ||