diff options
Diffstat (limited to 'toxav/codec.c')
-rw-r--r-- | toxav/codec.c | 680 |
1 files changed, 408 insertions, 272 deletions
diff --git a/toxav/codec.c b/toxav/codec.c index de802526..bf94115e 100644 --- a/toxav/codec.c +++ b/toxav/codec.c | |||
@@ -1,8 +1,6 @@ | |||
1 | /** codec.c | 1 | /** codec.c |
2 | * | 2 | * |
3 | * Audio and video codec intitialization, encoding/decoding and playback | 3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. |
4 | * | ||
5 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
6 | * | 4 | * |
7 | * This file is part of Tox. | 5 | * This file is part of Tox. |
8 | * | 6 | * |
@@ -31,6 +29,7 @@ | |||
31 | 29 | ||
32 | #include <stdio.h> | 30 | #include <stdio.h> |
33 | #include <stdlib.h> | 31 | #include <stdlib.h> |
32 | #include <stdbool.h> | ||
34 | #include <math.h> | 33 | #include <math.h> |
35 | #include <assert.h> | 34 | #include <assert.h> |
36 | #include <time.h> | 35 | #include <time.h> |
@@ -39,12 +38,12 @@ | |||
39 | #include "rtp.h" | 38 | #include "rtp.h" |
40 | #include "codec.h" | 39 | #include "codec.h" |
41 | 40 | ||
41 | #define DEFAULT_JBUF 6 | ||
42 | |||
42 | /* Good quality encode. */ | 43 | /* Good quality encode. */ |
43 | #define MAX_DECODE_TIME_US 0 | 44 | #define MAX_DECODE_TIME_US 0 |
44 | 45 | ||
45 | // TODO this has to be exchanged in msi | ||
46 | #define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ | 46 | #define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ |
47 | #define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/ | ||
48 | #define VIDEOFRAME_HEADER_SIZE 0x2 | 47 | #define VIDEOFRAME_HEADER_SIZE 0x2 |
49 | 48 | ||
50 | /* FIXME: Might not be enough */ | 49 | /* FIXME: Might not be enough */ |
@@ -61,12 +60,12 @@ typedef struct { | |||
61 | Payload **packets; | 60 | Payload **packets; |
62 | } PayloadBuffer; | 61 | } PayloadBuffer; |
63 | 62 | ||
64 | static _Bool buffer_full(const PayloadBuffer *b) | 63 | static bool buffer_full(const PayloadBuffer *b) |
65 | { | 64 | { |
66 | return (b->end + 1) % b->size == b->start; | 65 | return (b->end + 1) % b->size == b->start; |
67 | } | 66 | } |
68 | 67 | ||
69 | static _Bool buffer_empty(const PayloadBuffer *b) | 68 | static bool buffer_empty(const PayloadBuffer *b) |
70 | { | 69 | { |
71 | return b->end == b->start; | 70 | return b->end == b->start; |
72 | } | 71 | } |
@@ -120,7 +119,7 @@ static void buffer_free(PayloadBuffer *b) | |||
120 | } | 119 | } |
121 | 120 | ||
122 | /* JITTER BUFFER WORK */ | 121 | /* JITTER BUFFER WORK */ |
123 | typedef struct _JitterBuffer { | 122 | typedef struct JitterBuffer_s { |
124 | RTPMessage **queue; | 123 | RTPMessage **queue; |
125 | uint32_t size; | 124 | uint32_t size; |
126 | uint32_t capacity; | 125 | uint32_t capacity; |
@@ -224,129 +223,186 @@ static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *success) | |||
224 | return NULL; | 223 | return NULL; |
225 | } | 224 | } |
226 | 225 | ||
227 | static int init_video_decoder(CSSession *cs) | 226 | static int convert_bw_to_sampling_rate(int bw) |
228 | { | 227 | { |
229 | int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION); | 228 | switch(bw) |
230 | 229 | { | |
231 | if ( rc != VPX_CODEC_OK) { | 230 | case OPUS_BANDWIDTH_NARROWBAND: return 8000; |
232 | LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); | 231 | case OPUS_BANDWIDTH_MEDIUMBAND: return 12000; |
233 | return -1; | 232 | case OPUS_BANDWIDTH_WIDEBAND: return 16000; |
233 | case OPUS_BANDWIDTH_SUPERWIDEBAND: return 24000; | ||
234 | case OPUS_BANDWIDTH_FULLBAND: return 48000; | ||
235 | default: return -1; | ||
234 | } | 236 | } |
235 | |||
236 | return 0; | ||
237 | } | 237 | } |
238 | 238 | ||
239 | static int init_audio_decoder(CSSession *cs) | ||
240 | { | ||
241 | int rc; | ||
242 | cs->audio_decoder = opus_decoder_create(cs->audio_decoder_sample_rate, cs->audio_decoder_channels, &rc ); | ||
243 | 239 | ||
244 | if ( rc != OPUS_OK ) { | ||
245 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | ||
246 | return -1; | ||
247 | } | ||
248 | 240 | ||
249 | return 0; | 241 | /* PUBLIC */ |
250 | } | ||
251 | 242 | ||
252 | static int init_video_encoder(CSSession *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate) | 243 | void cs_do(CSSession *cs) |
253 | { | 244 | { |
254 | vpx_codec_enc_cfg_t cfg; | 245 | /* Codec session should always be protected by call mutex so no need to check for cs validity |
255 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); | 246 | */ |
256 | 247 | ||
257 | if (rc != VPX_CODEC_OK) { | 248 | if (!cs) |
258 | LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); | 249 | return; |
259 | return -1; | 250 | |
260 | } | 251 | Payload *p; |
261 | 252 | int rc; | |
262 | cfg.rc_target_bitrate = video_bitrate; | 253 | |
263 | cfg.g_w = max_width; | 254 | int success = 0; |
264 | cfg.g_h = max_height; | 255 | |
265 | cfg.g_pass = VPX_RC_ONE_PASS; | 256 | pthread_mutex_lock(cs->queue_mutex); |
266 | cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS; | 257 | |
267 | cfg.g_lag_in_frames = 0; | 258 | if (cs->audio_decoder) { /* If receiving enabled */ |
268 | cfg.kf_min_dist = 0; | 259 | RTPMessage *msg; |
269 | cfg.kf_max_dist = 48; | 260 | |
270 | cfg.kf_mode = VPX_KF_AUTO; | 261 | uint16_t fsize = 5760; /* Max frame size for 48 kHz */ |
271 | 262 | int16_t tmp[fsize * 2]; | |
272 | rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION); | 263 | |
273 | 264 | while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { | |
274 | if ( rc != VPX_CODEC_OK) { | 265 | pthread_mutex_unlock(cs->queue_mutex); |
275 | LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); | 266 | |
276 | return -1; | 267 | if (success == 2) { |
268 | rc = opus_decode(cs->audio_decoder, 0, 0, tmp, fsize, 1); | ||
269 | } else { | ||
270 | /* Get values from packet and decode. | ||
271 | * It also checks for validity of an opus packet | ||
272 | */ | ||
273 | rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data)); | ||
274 | if (rc != -1) { | ||
275 | cs->last_packet_sampling_rate = rc; | ||
276 | cs->last_pack_channels = opus_packet_get_nb_channels(msg->data); | ||
277 | |||
278 | cs->last_packet_frame_duration = | ||
279 | ( opus_packet_get_samples_per_frame(msg->data, cs->last_packet_sampling_rate) * 1000 ) | ||
280 | / cs->last_packet_sampling_rate; | ||
281 | } else { | ||
282 | LOGGER_WARNING("Failed to load packet values!"); | ||
283 | rtp_free_msg(NULL, msg); | ||
284 | continue; | ||
285 | } | ||
286 | |||
287 | rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, fsize, 0); | ||
288 | rtp_free_msg(NULL, msg); | ||
289 | } | ||
290 | |||
291 | if (rc < 0) { | ||
292 | LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); | ||
293 | } else if (cs->acb.first) { | ||
294 | /* Play */ | ||
295 | cs->acb.first(cs->agent, cs->friend_id, tmp, rc, | ||
296 | cs->last_pack_channels, cs->last_packet_sampling_rate, cs->acb.second); | ||
297 | } | ||
298 | |||
299 | pthread_mutex_lock(cs->queue_mutex); | ||
300 | } | ||
277 | } | 301 | } |
278 | 302 | ||
279 | rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8); | 303 | if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) { |
280 | 304 | /* Decode video */ | |
281 | if ( rc != VPX_CODEC_OK) { | 305 | buffer_read(cs->vbuf_raw, &p); |
282 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | 306 | |
283 | return -1; | 307 | /* Leave space for (possibly) other thread to queue more data after we read it here */ |
308 | pthread_mutex_unlock(cs->queue_mutex); | ||
309 | |||
310 | rc = vpx_codec_decode(cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); | ||
311 | free(p); | ||
312 | |||
313 | if (rc != VPX_CODEC_OK) { | ||
314 | LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); | ||
315 | } else { | ||
316 | vpx_codec_iter_t iter = NULL; | ||
317 | vpx_image_t *dest = vpx_codec_get_frame(cs->v_decoder, &iter); | ||
318 | |||
319 | /* Play decoded images */ | ||
320 | for (; dest; dest = vpx_codec_get_frame(cs->v_decoder, &iter)) { | ||
321 | if (cs->vcb.first) | ||
322 | cs->vcb.first(cs->agent, cs->friend_id, dest->d_w, dest->d_h, | ||
323 | (const uint8_t**)dest->planes, dest->stride, cs->vcb.second); | ||
324 | |||
325 | vpx_img_free(dest); | ||
326 | } | ||
327 | } | ||
328 | |||
329 | return; | ||
284 | } | 330 | } |
285 | 331 | ||
286 | cs->max_width = max_width; | 332 | pthread_mutex_unlock(cs->queue_mutex); |
287 | cs->max_height = max_height; | ||
288 | cs->video_bitrate = video_bitrate; | ||
289 | |||
290 | return 0; | ||
291 | } | 333 | } |
292 | 334 | ||
293 | static int init_audio_encoder(CSSession *cs) | 335 | CSSession *cs_new(uint32_t peer_video_frame_piece_size) |
294 | { | 336 | { |
295 | int rc = OPUS_OK; | 337 | CSSession *cs = calloc(sizeof(CSSession), 1); |
296 | cs->audio_encoder = opus_encoder_create(cs->audio_encoder_sample_rate, | 338 | |
297 | cs->audio_encoder_channels, OPUS_APPLICATION_AUDIO, &rc); | 339 | if (!cs) { |
298 | 340 | LOGGER_WARNING("Allocation failed! Application might misbehave!"); | |
299 | if ( rc != OPUS_OK ) { | 341 | return NULL; |
300 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
301 | return -1; | ||
302 | } | 342 | } |
303 | 343 | ||
304 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_encoder_bitrate)); | 344 | |
305 | 345 | if (create_recursive_mutex(cs->queue_mutex) != 0) { | |
306 | if ( rc != OPUS_OK ) { | 346 | LOGGER_WARNING("Failed to create recursive mutex!"); |
307 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | 347 | free(cs); |
308 | return -1; | 348 | return NULL; |
309 | } | 349 | } |
350 | |||
351 | |||
352 | cs->peer_video_frame_piece_size = peer_video_frame_piece_size; | ||
353 | |||
354 | return cs; | ||
355 | } | ||
310 | 356 | ||
311 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); | 357 | void cs_kill(CSSession *cs) |
358 | { | ||
359 | if (!cs) | ||
360 | return; | ||
361 | |||
362 | /* NOTE: queue_message() will not be called since | ||
363 | * the callback is unregistered before cs_kill is called. | ||
364 | */ | ||
365 | |||
366 | cs_disable_audio_sending(cs); | ||
367 | cs_disable_audio_receiving(cs); | ||
368 | cs_disable_video_sending(cs); | ||
369 | cs_disable_video_receiving(cs); | ||
370 | |||
371 | pthread_mutex_destroy(cs->queue_mutex); | ||
372 | |||
373 | LOGGER_DEBUG("Terminated codec state: %p", cs); | ||
374 | free(cs); | ||
375 | } | ||
312 | 376 | ||
313 | if ( rc != OPUS_OK ) { | ||
314 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
315 | return -1; | ||
316 | } | ||
317 | 377 | ||
318 | return 0; | ||
319 | } | ||
320 | 378 | ||
321 | /* PUBLIC */ | 379 | void cs_init_video_splitter_cycle(CSSession* cs) |
322 | int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length) | ||
323 | { | 380 | { |
324 | if (!cs || !length || length > cs->max_video_frame_size) { | ||
325 | LOGGER_ERROR("Invalid CodecState or video frame size: %u", length); | ||
326 | return cs_ErrorSplittingVideoPayload; | ||
327 | } | ||
328 | |||
329 | cs->split_video_frame[0] = cs->frameid_out++; | 381 | cs->split_video_frame[0] = cs->frameid_out++; |
330 | cs->split_video_frame[1] = 0; | 382 | cs->split_video_frame[1] = 0; |
383 | } | ||
384 | |||
385 | int cs_update_video_splitter_cycle(CSSession *cs, const uint8_t *payload, uint16_t length) | ||
386 | { | ||
331 | cs->processing_video_frame = payload; | 387 | cs->processing_video_frame = payload; |
332 | cs->processing_video_frame_size = length; | 388 | cs->processing_video_frame_size = length; |
333 | 389 | ||
334 | return ((length - 1) / cs->video_frame_piece_size) + 1; | 390 | return ((length - 1) / VIDEOFRAME_PIECE_SIZE) + 1; |
335 | } | 391 | } |
336 | 392 | ||
337 | const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size) | 393 | const uint8_t *cs_iterate_split_video_frame(CSSession *cs, uint16_t *size) |
338 | { | 394 | { |
339 | if (!cs || !size) return NULL; | 395 | if (!cs || !size) return NULL; |
340 | 396 | ||
341 | if (cs->processing_video_frame_size > cs->video_frame_piece_size) { | 397 | if (cs->processing_video_frame_size > VIDEOFRAME_PIECE_SIZE) { |
342 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, | 398 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, |
343 | cs->processing_video_frame, | 399 | cs->processing_video_frame, |
344 | cs->video_frame_piece_size); | 400 | VIDEOFRAME_PIECE_SIZE); |
345 | 401 | ||
346 | cs->processing_video_frame += cs->video_frame_piece_size; | 402 | cs->processing_video_frame += VIDEOFRAME_PIECE_SIZE; |
347 | cs->processing_video_frame_size -= cs->video_frame_piece_size; | 403 | cs->processing_video_frame_size -= VIDEOFRAME_PIECE_SIZE; |
348 | 404 | ||
349 | *size = cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE; | 405 | *size = VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE; |
350 | } else { | 406 | } else { |
351 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, | 407 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, |
352 | cs->processing_video_frame, | 408 | cs->processing_video_frame, |
@@ -360,82 +416,19 @@ const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size) | |||
360 | return cs->split_video_frame; | 416 | return cs->split_video_frame; |
361 | } | 417 | } |
362 | 418 | ||
363 | void cs_do(CSSession *cs) | ||
364 | { | ||
365 | /* Codec session should always be protected by call mutex so no need to check for cs validity | ||
366 | */ | ||
367 | |||
368 | if (!cs) return; | ||
369 | |||
370 | Payload *p; | ||
371 | int rc; | ||
372 | |||
373 | int success = 0; | ||
374 | |||
375 | pthread_mutex_lock(cs->queue_mutex); | ||
376 | RTPMessage *msg; | ||
377 | |||
378 | while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { | ||
379 | pthread_mutex_unlock(cs->queue_mutex); | ||
380 | |||
381 | uint16_t fsize = ((cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000); | ||
382 | int16_t tmp[fsize * cs->audio_decoder_channels]; | ||
383 | |||
384 | if (success == 2) { | ||
385 | rc = opus_decode(cs->audio_decoder, 0, 0, tmp, fsize, 1); | ||
386 | } else { | ||
387 | rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, fsize, 0); | ||
388 | rtp_free_msg(NULL, msg); | ||
389 | } | ||
390 | |||
391 | if (rc < 0) { | ||
392 | LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); | ||
393 | } else if (cs->acb.first) { | ||
394 | /* Play */ | ||
395 | cs->acb.first(cs->agent, cs->call_idx, tmp, rc, cs->acb.second); | ||
396 | } | ||
397 | |||
398 | pthread_mutex_lock(cs->queue_mutex); | ||
399 | } | ||
400 | |||
401 | if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) { | ||
402 | /* Decode video */ | ||
403 | buffer_read(cs->vbuf_raw, &p); | ||
404 | 419 | ||
405 | /* Leave space for (possibly) other thread to queue more data after we read it here */ | ||
406 | pthread_mutex_unlock(cs->queue_mutex); | ||
407 | |||
408 | rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); | ||
409 | free(p); | ||
410 | |||
411 | if (rc != VPX_CODEC_OK) { | ||
412 | LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); | ||
413 | } else { | ||
414 | vpx_codec_iter_t iter = NULL; | ||
415 | vpx_image_t *dest = vpx_codec_get_frame(&cs->v_decoder, &iter); | ||
416 | 420 | ||
417 | /* Play decoded images */ | 421 | int cs_set_sending_video_resolution(CSSession *cs, uint16_t width, uint16_t height) |
418 | for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) { | ||
419 | if (cs->vcb.first) | ||
420 | cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second); | ||
421 | |||
422 | vpx_img_free(dest); | ||
423 | } | ||
424 | } | ||
425 | |||
426 | return; | ||
427 | } | ||
428 | |||
429 | pthread_mutex_unlock(cs->queue_mutex); | ||
430 | } | ||
431 | |||
432 | int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height) | ||
433 | { | 422 | { |
434 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | 423 | if (!cs->v_encoding) |
435 | 424 | return -1; | |
425 | |||
426 | /* TODO FIXME reference is safe? */ | ||
427 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; | ||
428 | |||
436 | if (cfg.g_w == width && cfg.g_h == height) | 429 | if (cfg.g_w == width && cfg.g_h == height) |
437 | return 0; | 430 | return 0; |
438 | 431 | /* | |
439 | if (width * height > cs->max_width * cs->max_height) { | 432 | if (width * height > cs->max_width * cs->max_height) { |
440 | vpx_codec_ctx_t v_encoder = cs->v_encoder; | 433 | vpx_codec_ctx_t v_encoder = cs->v_encoder; |
441 | 434 | ||
@@ -446,12 +439,12 @@ int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t heig | |||
446 | 439 | ||
447 | vpx_codec_destroy(&v_encoder); | 440 | vpx_codec_destroy(&v_encoder); |
448 | return 0; | 441 | return 0; |
449 | } | 442 | }*/ |
450 | 443 | ||
451 | LOGGER_DEBUG("New video resolution: %u %u", width, height); | 444 | LOGGER_DEBUG("New video resolution: %u %u", width, height); |
452 | cfg.g_w = width; | 445 | cfg.g_w = width; |
453 | cfg.g_h = height; | 446 | cfg.g_h = height; |
454 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | 447 | int rc = vpx_codec_enc_config_set(cs->v_encoder, &cfg); |
455 | 448 | ||
456 | if ( rc != VPX_CODEC_OK) { | 449 | if ( rc != VPX_CODEC_OK) { |
457 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | 450 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); |
@@ -461,138 +454,282 @@ int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t heig | |||
461 | return 0; | 454 | return 0; |
462 | } | 455 | } |
463 | 456 | ||
464 | int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate) | 457 | int cs_set_sending_video_bitrate(CSSession *cs, uint32_t bitrate) |
465 | { | 458 | { |
466 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | 459 | if (!cs->v_encoding) |
467 | 460 | return -1; | |
468 | if (cfg.rc_target_bitrate == video_bitrate) | 461 | |
462 | /* TODO FIXME reference is safe? */ | ||
463 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; | ||
464 | if (cfg.rc_target_bitrate == bitrate) | ||
469 | return 0; | 465 | return 0; |
470 | 466 | ||
471 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); | 467 | LOGGER_DEBUG("New video bitrate: %u", bitrate); |
472 | cfg.rc_target_bitrate = video_bitrate; | 468 | cfg.rc_target_bitrate = bitrate; |
473 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | 469 | |
474 | 470 | int rc = vpx_codec_enc_config_set(cs->v_encoder, &cfg); | |
475 | if ( rc != VPX_CODEC_OK) { | 471 | if ( rc != VPX_CODEC_OK) { |
476 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | 472 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); |
477 | return cs_ErrorSettingVideoBitrate; | 473 | return cs_ErrorSettingVideoBitrate; |
478 | } | 474 | } |
479 | 475 | ||
480 | cs->video_bitrate = video_bitrate; | ||
481 | return 0; | 476 | return 0; |
482 | } | 477 | } |
483 | 478 | ||
484 | CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video) | 479 | int cs_enable_video_sending(CSSession* cs, uint32_t bitrate) |
485 | { | 480 | { |
486 | CSSession *cs = calloc(sizeof(CSSession), 1); | 481 | if (cs->v_encoding) |
487 | 482 | return 0; | |
488 | if (!cs) { | 483 | |
489 | LOGGER_WARNING("Allocation failed! Application might misbehave!"); | 484 | vpx_codec_enc_cfg_t cfg; |
490 | return NULL; | 485 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); |
486 | |||
487 | if (rc != VPX_CODEC_OK) { | ||
488 | LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); | ||
489 | return -1; | ||
491 | } | 490 | } |
492 | 491 | ||
493 | if (create_recursive_mutex(cs->queue_mutex) != 0) { | 492 | rc = vpx_codec_enc_init_ver(cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, |
494 | LOGGER_WARNING("Failed to create recursive mutex!"); | 493 | VPX_ENCODER_ABI_VERSION); |
495 | free(cs); | 494 | |
496 | return NULL; | 495 | if ( rc != VPX_CODEC_OK) { |
496 | LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); | ||
497 | return -1; | ||
497 | } | 498 | } |
498 | 499 | ||
499 | if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) { | 500 | /* So that we can use cs_disable_video_sending to clean up */ |
500 | LOGGER_WARNING("Jitter buffer creaton failed!"); | 501 | cs->v_encoding = true; |
501 | goto error; | 502 | |
503 | if ( !(cs->split_video_frame = calloc(VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE, 1)) ) | ||
504 | goto FAILURE; | ||
505 | |||
506 | cfg.rc_target_bitrate = bitrate; | ||
507 | cfg.g_w = 800; | ||
508 | cfg.g_h = 600; | ||
509 | cfg.g_pass = VPX_RC_ONE_PASS; | ||
510 | cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS; | ||
511 | cfg.g_lag_in_frames = 0; | ||
512 | cfg.kf_min_dist = 0; | ||
513 | cfg.kf_max_dist = 48; | ||
514 | cfg.kf_mode = VPX_KF_AUTO; | ||
515 | |||
516 | |||
517 | rc = vpx_codec_control(cs->v_encoder, VP8E_SET_CPUUSED, 8); | ||
518 | |||
519 | if ( rc != VPX_CODEC_OK) { | ||
520 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
521 | goto FAILURE; | ||
502 | } | 522 | } |
523 | |||
524 | return 0; | ||
525 | |||
526 | FAILURE: | ||
527 | cs_disable_video_sending(cs); | ||
528 | return -1; | ||
529 | } | ||
503 | 530 | ||
504 | cs->audio_encoder_bitrate = cs_self->audio_bitrate; | 531 | int cs_enable_video_receiving(CSSession* cs) |
505 | cs->audio_encoder_sample_rate = cs_self->audio_sample_rate; | 532 | { |
506 | cs->audio_encoder_channels = cs_self->audio_channels; | 533 | if (cs->v_decoding) |
507 | cs->audio_encoder_frame_duration = cs_self->audio_frame_duration; | 534 | return 0; |
508 | |||
509 | cs->audio_decoder_bitrate = cs_peer->audio_bitrate; | ||
510 | cs->audio_decoder_sample_rate = cs_peer->audio_sample_rate; | ||
511 | cs->audio_decoder_channels = cs_peer->audio_channels; | ||
512 | cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration; | ||
513 | |||
514 | |||
515 | cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? cs_AudioEncoding : 0; | ||
516 | cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? cs_AudioDecoding : 0; | ||
517 | |||
518 | if ( !(cs->capabilities & cs_AudioEncoding) || !(cs->capabilities & cs_AudioDecoding) ) goto error; | ||
519 | |||
520 | if ((cs->support_video = has_video)) { | ||
521 | cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE; | ||
522 | cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE; | ||
523 | |||
524 | cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width, | ||
525 | cs_self->max_video_height, cs_self->video_bitrate) ) ? cs_VideoEncoding : 0; | ||
526 | cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? cs_VideoDecoding : 0; | ||
527 | |||
528 | if ( !(cs->capabilities & cs_VideoEncoding) || !(cs->capabilities & cs_VideoDecoding) ) goto error; | ||
529 | |||
530 | if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error; | ||
531 | |||
532 | if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) ) | ||
533 | goto error; | ||
534 | 535 | ||
535 | if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error; | 536 | int rc = vpx_codec_dec_init_ver(cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, |
537 | NULL, 0, VPX_DECODER_ABI_VERSION); | ||
538 | |||
539 | if ( rc != VPX_CODEC_OK) { | ||
540 | LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); | ||
541 | return -1; | ||
536 | } | 542 | } |
543 | |||
544 | /* So that we can use cs_disable_video_sending to clean up */ | ||
545 | cs->v_decoding = true; | ||
546 | |||
547 | if ( !(cs->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1)) ) | ||
548 | goto FAILURE; | ||
549 | |||
550 | if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) | ||
551 | goto FAILURE; | ||
552 | |||
553 | return 0; | ||
554 | |||
555 | FAILURE: | ||
556 | cs_disable_video_receiving(cs); | ||
557 | return -1; | ||
558 | } | ||
537 | 559 | ||
538 | return cs; | 560 | void cs_disable_video_sending(CSSession* cs) |
539 | 561 | { | |
540 | error: | 562 | if (cs->v_encoding) { |
541 | LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); | 563 | cs->v_encoding = false; |
542 | 564 | ||
543 | pthread_mutex_destroy(cs->queue_mutex); | 565 | free(cs->split_video_frame); |
544 | 566 | cs->split_video_frame = NULL; | |
545 | if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder); | 567 | |
546 | 568 | vpx_codec_destroy(cs->v_encoder); | |
547 | if ( cs->audio_decoder ) opus_decoder_destroy(cs->audio_decoder); | 569 | } |
548 | 570 | } | |
549 | |||
550 | if (has_video) { | ||
551 | if ( cs->capabilities & cs_VideoDecoding ) vpx_codec_destroy(&cs->v_decoder); | ||
552 | |||
553 | if ( cs->capabilities & cs_VideoEncoding ) vpx_codec_destroy(&cs->v_encoder); | ||
554 | 571 | ||
572 | void cs_disable_video_receiving(CSSession* cs) | ||
573 | { | ||
574 | if (cs->v_decoding) { | ||
575 | cs->v_decoding = false; | ||
576 | |||
555 | buffer_free(cs->vbuf_raw); | 577 | buffer_free(cs->vbuf_raw); |
556 | 578 | cs->vbuf_raw = NULL; | |
557 | free(cs->frame_buf); | 579 | free(cs->frame_buf); |
558 | free(cs->split_video_frame); | 580 | cs->frame_buf = NULL; |
581 | |||
582 | vpx_codec_destroy(cs->v_decoder); | ||
559 | } | 583 | } |
584 | } | ||
560 | 585 | ||
561 | jbuf_free(cs->j_buf); | ||
562 | free(cs); | ||
563 | 586 | ||
564 | return NULL; | ||
565 | } | ||
566 | 587 | ||
567 | void cs_kill(CSSession *cs) | 588 | int cs_set_sending_audio_bitrate(CSSession *cs, int32_t rate) |
568 | { | 589 | { |
569 | if (!cs) return; | 590 | if (cs->audio_encoder == NULL) |
591 | return -1; | ||
592 | |||
593 | int rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(rate)); | ||
594 | |||
595 | if ( rc != OPUS_OK ) { | ||
596 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
597 | return -1; | ||
598 | } | ||
599 | |||
600 | return 0; | ||
601 | } | ||
570 | 602 | ||
571 | /* queue_message will not be called since it's unregistered before cs_kill is called */ | 603 | int cs_set_sending_audio_sampling_rate(CSSession* cs, int32_t rate) |
572 | pthread_mutex_destroy(cs->queue_mutex); | 604 | { |
605 | /* TODO Find a better way? */ | ||
606 | if (cs->audio_encoder == NULL) | ||
607 | return -1; | ||
608 | |||
609 | int rc = OPUS_OK; | ||
610 | int bitrate = 0; | ||
611 | int channels = cs->encoder_channels; | ||
612 | |||
613 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_BITRATE(&bitrate)); | ||
614 | |||
615 | if ( rc != OPUS_OK ) { | ||
616 | LOGGER_ERROR("Error while getting encoder ctl: %s", opus_strerror(rc)); | ||
617 | return -1; | ||
618 | } | ||
619 | |||
620 | cs_disable_audio_sending(cs); | ||
621 | return cs_enable_audio_sending(cs, bitrate, channels); | ||
622 | } | ||
573 | 623 | ||
624 | int cs_set_sending_audio_channels(CSSession* cs, int32_t count) | ||
625 | { | ||
626 | /* TODO Find a better way? */ | ||
627 | if (cs->audio_encoder == NULL) | ||
628 | return -1; | ||
629 | |||
630 | if (cs->encoder_channels == count) | ||
631 | return 0; | ||
632 | |||
633 | int rc = OPUS_OK; | ||
634 | int bitrate = 0; | ||
635 | |||
636 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_BITRATE(&bitrate)); | ||
637 | |||
638 | if ( rc != OPUS_OK ) { | ||
639 | LOGGER_ERROR("Error while getting encoder ctl: %s", opus_strerror(rc)); | ||
640 | return -1; | ||
641 | } | ||
642 | |||
643 | cs_disable_audio_sending(cs); | ||
644 | return cs_enable_audio_sending(cs, bitrate, count); | ||
645 | } | ||
574 | 646 | ||
575 | if ( cs->audio_encoder ) | 647 | void cs_disable_audio_sending(CSSession* cs) |
648 | { | ||
649 | if ( cs->audio_encoder ) { | ||
576 | opus_encoder_destroy(cs->audio_encoder); | 650 | opus_encoder_destroy(cs->audio_encoder); |
651 | cs->audio_encoder = NULL; | ||
652 | cs->encoder_channels = 0; | ||
653 | } | ||
654 | } | ||
577 | 655 | ||
578 | if ( cs->audio_decoder ) | 656 | void cs_disable_audio_receiving(CSSession* cs) |
657 | { | ||
658 | if ( cs->audio_decoder ) { | ||
579 | opus_decoder_destroy(cs->audio_decoder); | 659 | opus_decoder_destroy(cs->audio_decoder); |
660 | cs->audio_decoder = NULL; | ||
661 | jbuf_free(cs->j_buf); | ||
662 | cs->j_buf = NULL; | ||
663 | |||
664 | /* It's used for measuring iteration interval so this has to be some value. | ||
665 | * To avoid unecessary checking we set this to 500 | ||
666 | */ | ||
667 | cs->last_packet_frame_duration = 500; | ||
668 | } | ||
669 | } | ||
580 | 670 | ||
581 | if ( cs->capabilities & cs_VideoDecoding ) | 671 | int cs_enable_audio_sending(CSSession* cs, uint32_t bitrate, int channels) |
582 | vpx_codec_destroy(&cs->v_decoder); | 672 | { |
583 | 673 | if (cs->audio_encoder) | |
584 | if ( cs->capabilities & cs_VideoEncoding ) | 674 | return 0; |
585 | vpx_codec_destroy(&cs->v_encoder); | 675 | |
586 | 676 | int rc = OPUS_OK; | |
587 | jbuf_free(cs->j_buf); | 677 | cs->audio_encoder = opus_encoder_create(48000, channels, OPUS_APPLICATION_AUDIO, &rc); |
588 | buffer_free(cs->vbuf_raw); | 678 | |
589 | free(cs->frame_buf); | 679 | if ( rc != OPUS_OK ) { |
590 | free(cs->split_video_frame); | 680 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); |
591 | 681 | return -1; | |
592 | LOGGER_DEBUG("Terminated codec state: %p", cs); | 682 | } |
593 | free(cs); | 683 | |
684 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(bitrate)); | ||
685 | |||
686 | if ( rc != OPUS_OK ) { | ||
687 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
688 | goto FAILURE; | ||
689 | } | ||
690 | |||
691 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); | ||
692 | |||
693 | if ( rc != OPUS_OK ) { | ||
694 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
695 | goto FAILURE; | ||
696 | } | ||
697 | |||
698 | cs->encoder_channels = channels; | ||
699 | return 0; | ||
700 | |||
701 | FAILURE: | ||
702 | cs_disable_audio_sending(cs); | ||
703 | return -1; | ||
594 | } | 704 | } |
595 | 705 | ||
706 | int cs_enable_audio_receiving(CSSession* cs) | ||
707 | { | ||
708 | if (cs->audio_decoder) | ||
709 | return 0; | ||
710 | |||
711 | int rc; | ||
712 | cs->audio_decoder = opus_decoder_create(48000, 2, &rc ); | ||
713 | |||
714 | if ( rc != OPUS_OK ) { | ||
715 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | ||
716 | return -1; | ||
717 | } | ||
718 | |||
719 | |||
720 | if ( !(cs->j_buf = jbuf_new(DEFAULT_JBUF)) ) { | ||
721 | LOGGER_WARNING("Jitter buffer creaton failed!"); | ||
722 | opus_decoder_destroy(cs->audio_decoder); | ||
723 | cs->audio_decoder = NULL; | ||
724 | return -1; | ||
725 | } | ||
726 | |||
727 | /* It's used for measuring iteration interval so this has to be some value. | ||
728 | * To avoid unecessary checking we set this to 500 | ||
729 | */ | ||
730 | cs->last_packet_frame_duration = 500; | ||
731 | return 0; | ||
732 | } | ||
596 | 733 | ||
597 | 734 | ||
598 | 735 | ||
@@ -607,7 +744,7 @@ void queue_message(RTPSession *session, RTPMessage *msg) | |||
607 | if (!cs) return; | 744 | if (!cs) return; |
608 | 745 | ||
609 | /* Audio */ | 746 | /* Audio */ |
610 | if (session->payload_type == msi_TypeAudio % 128) { | 747 | if (session->payload_type == rtp_TypeAudio % 128) { |
611 | pthread_mutex_lock(cs->queue_mutex); | 748 | pthread_mutex_lock(cs->queue_mutex); |
612 | int ret = jbuf_write(cs->j_buf, msg); | 749 | int ret = jbuf_write(cs->j_buf, msg); |
613 | pthread_mutex_unlock(cs->queue_mutex); | 750 | pthread_mutex_unlock(cs->queue_mutex); |
@@ -664,10 +801,10 @@ void queue_message(RTPSession *session, RTPMessage *msg) | |||
664 | 801 | ||
665 | uint8_t piece_number = packet[1]; | 802 | uint8_t piece_number = packet[1]; |
666 | 803 | ||
667 | uint32_t length_before_piece = ((piece_number - 1) * cs->video_frame_piece_size); | 804 | uint32_t length_before_piece = ((piece_number - 1) * cs->peer_video_frame_piece_size); |
668 | uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE); | 805 | uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE); |
669 | 806 | ||
670 | if (framebuf_new_length > cs->max_video_frame_size) { | 807 | if (framebuf_new_length > MAX_VIDEOFRAME_SIZE) { |
671 | goto end; | 808 | goto end; |
672 | } | 809 | } |
673 | 810 | ||
@@ -678,9 +815,8 @@ void queue_message(RTPSession *session, RTPMessage *msg) | |||
678 | packet + VIDEOFRAME_HEADER_SIZE, | 815 | packet + VIDEOFRAME_HEADER_SIZE, |
679 | packet_size - VIDEOFRAME_HEADER_SIZE); | 816 | packet_size - VIDEOFRAME_HEADER_SIZE); |
680 | 817 | ||
681 | if (framebuf_new_length > cs->frame_size) { | 818 | if (framebuf_new_length > cs->frame_size) |
682 | cs->frame_size = framebuf_new_length; | 819 | cs->frame_size = framebuf_new_length; |
683 | } | ||
684 | 820 | ||
685 | end: | 821 | end: |
686 | rtp_free_msg(NULL, msg); | 822 | rtp_free_msg(NULL, msg); |