diff options
-rw-r--r-- | toxav/codec.c | 594 | ||||
-rw-r--r-- | toxav/codec.h | 60 | ||||
-rw-r--r-- | toxav/rtp.c | 4 | ||||
-rw-r--r-- | toxav/rtp.h | 2 | ||||
-rw-r--r-- | toxav/toxav.c | 16 | ||||
-rw-r--r-- | toxav/toxav_new.c | 250 | ||||
-rw-r--r-- | toxav/toxav_new.h | 8 |
7 files changed, 587 insertions, 347 deletions
diff --git a/toxav/codec.c b/toxav/codec.c index fd2f9f93..43120e0f 100644 --- a/toxav/codec.c +++ b/toxav/codec.c | |||
@@ -229,271 +229,127 @@ static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *success) | |||
229 | return NULL; | 229 | return NULL; |
230 | } | 230 | } |
231 | 231 | ||
232 | 232 | static int convert_bw_to_sampling_rate(int bw) | |
233 | |||
234 | |||
235 | |||
236 | /* PUBLIC */ | ||
237 | int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length) | ||
238 | { | 233 | { |
239 | if (!cs || !length || length > cs->max_video_frame_size) { | 234 | switch(bw) |
240 | LOGGER_ERROR("Invalid CodecState or video frame size: %u", length); | 235 | { |
241 | return cs_ErrorSplittingVideoPayload; | 236 | case OPUS_BANDWIDTH_NARROWBAND: return 8000; |
237 | case OPUS_BANDWIDTH_MEDIUMBAND: return 12000; | ||
238 | case OPUS_BANDWIDTH_WIDEBAND: return 16000; | ||
239 | case OPUS_BANDWIDTH_SUPERWIDEBAND: return 24000; | ||
240 | case OPUS_BANDWIDTH_FULLBAND: return 48000; | ||
241 | default: return -1; | ||
242 | } | 242 | } |
243 | |||
244 | cs->split_video_frame[0] = cs->frameid_out++; | ||
245 | cs->split_video_frame[1] = 0; | ||
246 | cs->processing_video_frame = payload; | ||
247 | cs->processing_video_frame_size = length; | ||
248 | |||
249 | return ((length - 1) / cs->video_frame_piece_size) + 1; | ||
250 | } | 243 | } |
251 | 244 | ||
252 | const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size) | ||
253 | { | ||
254 | if (!cs || !size) return NULL; | ||
255 | |||
256 | if (cs->processing_video_frame_size > cs->video_frame_piece_size) { | ||
257 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, | ||
258 | cs->processing_video_frame, | ||
259 | cs->video_frame_piece_size); | ||
260 | |||
261 | cs->processing_video_frame += cs->video_frame_piece_size; | ||
262 | cs->processing_video_frame_size -= cs->video_frame_piece_size; | ||
263 | |||
264 | *size = cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE; | ||
265 | } else { | ||
266 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, | ||
267 | cs->processing_video_frame, | ||
268 | cs->processing_video_frame_size); | ||
269 | |||
270 | *size = cs->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE; | ||
271 | } | ||
272 | 245 | ||
273 | cs->split_video_frame[1]++; | ||
274 | 246 | ||
275 | return cs->split_video_frame; | 247 | /* PUBLIC */ |
276 | } | ||
277 | 248 | ||
278 | void cs_do(CSSession *cs) | 249 | void cs_do(CSSession *cs) |
279 | { | 250 | { |
280 | /* Codec session should always be protected by call mutex so no need to check for cs validity | 251 | /* Codec session should always be protected by call mutex so no need to check for cs validity |
281 | */ | 252 | */ |
282 | 253 | ||
283 | if (!cs) return; | 254 | if (!cs) |
284 | 255 | return; | |
256 | |||
285 | Payload *p; | 257 | Payload *p; |
286 | int rc; | 258 | int rc; |
287 | 259 | ||
288 | int success = 0; | 260 | int success = 0; |
289 | 261 | ||
290 | pthread_mutex_lock(cs->queue_mutex); | 262 | pthread_mutex_lock(cs->queue_mutex); |
291 | RTPMessage *msg; | 263 | RTPMessage *msg; |
292 | 264 | ||
265 | uint16_t fsize = 5760; /* Max frame size for 48 kHz */ | ||
266 | int16_t tmp[fsize * 2]; | ||
267 | |||
293 | while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { | 268 | while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { |
294 | pthread_mutex_unlock(cs->queue_mutex); | 269 | pthread_mutex_unlock(cs->queue_mutex); |
295 | 270 | ||
296 | uint16_t fsize = ((cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000); | ||
297 | int16_t tmp[fsize * cs->audio_decoder_channels]; | ||
298 | |||
299 | if (success == 2) { | 271 | if (success == 2) { |
300 | rc = opus_decode(cs->audio_decoder, 0, 0, tmp, fsize, 1); | 272 | rc = opus_decode(cs->audio_decoder, 0, 0, tmp, fsize, 1); |
301 | } else { | 273 | } else { |
274 | /* Get values from packet and decode. | ||
275 | * It also checks for validity of an opus packet | ||
276 | */ | ||
277 | rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data)); | ||
278 | if (rc != -1) { | ||
279 | cs->last_packet_sampling_rate = rc; | ||
280 | cs->last_pack_channels = opus_packet_get_nb_channels(msg->data); | ||
281 | |||
282 | cs->last_packet_frame_duration = | ||
283 | ( opus_packet_get_samples_per_frame(msg->data, cs->last_packet_sampling_rate) * 1000 ) | ||
284 | / cs->last_packet_sampling_rate; | ||
285 | |||
286 | } else { | ||
287 | LOGGER_WARNING("Failed to load packet values!"); | ||
288 | rtp_free_msg(NULL, msg); | ||
289 | continue; | ||
290 | } | ||
291 | |||
302 | rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, fsize, 0); | 292 | rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, fsize, 0); |
303 | rtp_free_msg(NULL, msg); | 293 | rtp_free_msg(NULL, msg); |
304 | } | 294 | } |
305 | 295 | ||
306 | if (rc < 0) { | 296 | if (rc < 0) { |
307 | LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); | 297 | LOGGER_WARNING("Decoding error: %s", opus_strerror(rc)); |
308 | } else if (cs->acb.first) { | 298 | } else if (((ToxAV*)cs->agent)->acb.first) { |
309 | /* Play */ | 299 | /* Play */ |
310 | cs->acb.first(cs->agent, cs->call_idx, tmp, rc, cs->acb.second); | 300 | ((ToxAV*)cs->agent)->acb.first(cs->agent, cs->call_idx, tmp, rc, |
301 | ((ToxAV*)cs->agent)->acb.second); | ||
311 | } | 302 | } |
312 | 303 | ||
313 | pthread_mutex_lock(cs->queue_mutex); | 304 | pthread_mutex_lock(cs->queue_mutex); |
314 | } | 305 | } |
315 | 306 | ||
316 | if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) { | 307 | if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) { |
317 | /* Decode video */ | 308 | /* Decode video */ |
318 | buffer_read(cs->vbuf_raw, &p); | 309 | buffer_read(cs->vbuf_raw, &p); |
319 | 310 | ||
320 | /* Leave space for (possibly) other thread to queue more data after we read it here */ | 311 | /* Leave space for (possibly) other thread to queue more data after we read it here */ |
321 | pthread_mutex_unlock(cs->queue_mutex); | 312 | pthread_mutex_unlock(cs->queue_mutex); |
322 | 313 | ||
323 | rc = vpx_codec_decode(cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); | 314 | rc = vpx_codec_decode(cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); |
324 | free(p); | 315 | free(p); |
325 | 316 | ||
326 | if (rc != VPX_CODEC_OK) { | 317 | if (rc != VPX_CODEC_OK) { |
327 | LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); | 318 | LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); |
328 | } else { | 319 | } else { |
329 | vpx_codec_iter_t iter = NULL; | 320 | vpx_codec_iter_t iter = NULL; |
330 | vpx_image_t *dest = vpx_codec_get_frame(cs->v_decoder, &iter); | 321 | vpx_image_t *dest = vpx_codec_get_frame(cs->v_decoder, &iter); |
331 | 322 | ||
332 | /* Play decoded images */ | 323 | /* Play decoded images */ |
333 | for (; dest; dest = vpx_codec_get_frame(cs->v_decoder, &iter)) { | 324 | for (; dest; dest = vpx_codec_get_frame(cs->v_decoder, &iter)) { |
334 | if (cs->vcb.first) | 325 | if (((ToxAV*)cs->agent)->vcb.first) |
335 | cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second); | 326 | ((ToxAV*)cs->agent)->vcb.first(cs->agent, cs->call_idx, dest, |
336 | 327 | ((ToxAV*)cs->agent)->vcb.second); | |
337 | vpx_img_free(dest); | 328 | |
329 | vpx_img_free(dest); | ||
338 | } | 330 | } |
339 | } | 331 | } |
340 | |||
341 | return; | ||
342 | } | ||
343 | |||
344 | pthread_mutex_unlock(cs->queue_mutex); | ||
345 | } | ||
346 | |||
347 | int cs_set_sending_video_resolution(CSSession *cs, uint16_t width, uint16_t height) | ||
348 | { | ||
349 | if (!cs->v_encoding) | ||
350 | return -1; | ||
351 | |||
352 | /* TODO FIXME reference is safe? */ | ||
353 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; | ||
354 | |||
355 | if (cfg.g_w == width && cfg.g_h == height) | ||
356 | return 0; | ||
357 | /* | ||
358 | if (width * height > cs->max_width * cs->max_height) { | ||
359 | vpx_codec_ctx_t v_encoder = cs->v_encoder; | ||
360 | |||
361 | if (init_video_encoder(cs, width, height, cs->video_bitrate) == -1) { | ||
362 | cs->v_encoder = v_encoder; | ||
363 | return cs_ErrorSettingVideoResolution; | ||
364 | } | ||
365 | |||
366 | vpx_codec_destroy(&v_encoder); | ||
367 | return 0; | ||
368 | }*/ | ||
369 | |||
370 | LOGGER_DEBUG("New video resolution: %u %u", width, height); | ||
371 | cfg.g_w = width; | ||
372 | cfg.g_h = height; | ||
373 | int rc = vpx_codec_enc_config_set(cs->v_encoder, &cfg); | ||
374 | |||
375 | if ( rc != VPX_CODEC_OK) { | ||
376 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
377 | return cs_ErrorSettingVideoResolution; | ||
378 | } | ||
379 | |||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | int cs_set_sending_video_bitrate(CSSession *cs, uint32_t bitrate) | ||
384 | { | ||
385 | if (!cs->v_encoding) | ||
386 | return -1; | ||
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) | ||
391 | return 0; | ||
392 | |||
393 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); | ||
394 | cfg.rc_target_bitrate = bitrate; | ||
395 | |||
396 | int rc = vpx_codec_enc_config_set(cs->v_encoder, &cfg); | ||
397 | if ( rc != VPX_CODEC_OK) { | ||
398 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
399 | return cs_ErrorSettingVideoBitrate; | ||
400 | } | ||
401 | |||
402 | return 0; | ||
403 | } | ||
404 | |||
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 | 332 | ||
439 | OpusEncoder* new_enc = opus_encoder_create(rate, cs->channels, OPUS_APPLICATION_AUDIO, &rc); | 333 | return; |
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 | } | 334 | } |
477 | 335 | ||
478 | opus_encoder_destroy(cs->audio_encoder); | 336 | pthread_mutex_unlock(cs->queue_mutex); |
479 | cs->audio_encoder = new_enc; | ||
480 | return 0; | ||
481 | } | 337 | } |
482 | 338 | ||
483 | CSSession *cs_new(uint32_t s_audio_b, uint32_t p_audio_b, uint32_t s_video_b, uint32_t p_video_b) | 339 | CSSession *cs_new(uint32_t s_audio_b, uint32_t p_audio_b, uint32_t s_video_b, uint32_t p_video_b) |
484 | { | 340 | { |
485 | CSSession *cs = calloc(sizeof(CSSession), 1); | 341 | CSSession *cs = calloc(sizeof(CSSession), 1); |
486 | 342 | ||
487 | if (!cs) { | 343 | if (!cs) { |
488 | LOGGER_WARNING("Allocation failed! Application might misbehave!"); | 344 | LOGGER_WARNING("Allocation failed! Application might misbehave!"); |
489 | return NULL; | 345 | return NULL; |
490 | } | 346 | } |
491 | 347 | ||
492 | /* TODO this has to be exchanged in msi */ | 348 | /* TODO this has to be exchanged in msi */ |
493 | cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE; | 349 | cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE; |
494 | cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE; | 350 | cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE; |
495 | 351 | ||
496 | if (s_audio_b > 0 && 0 != cs_enable_audio_sending(cs, s_audio_b)) { /* Sending audio enabled */ | 352 | if (s_audio_b > 0 && 0 != cs_enable_audio_sending(cs, s_audio_b, 2)) { /* Sending audio enabled */ |
497 | LOGGER_WARNING("Failed to enable audio sending!"); | 353 | LOGGER_WARNING("Failed to enable audio sending!"); |
498 | goto FAILURE; | 354 | goto FAILURE; |
499 | } | 355 | } |
@@ -502,29 +358,29 @@ CSSession *cs_new(uint32_t s_audio_b, uint32_t p_audio_b, uint32_t s_video_b, ui | |||
502 | LOGGER_WARNING("Failed to enable audio receiving!"); | 358 | LOGGER_WARNING("Failed to enable audio receiving!"); |
503 | goto FAILURE; | 359 | goto FAILURE; |
504 | } | 360 | } |
505 | 361 | ||
506 | if (s_video_b > 0 && 0 != cs_enable_video_sending(cs, s_video_b)) { /* Sending video enabled */ | 362 | if (s_video_b > 0 && 0 != cs_enable_video_sending(cs, s_video_b)) { /* Sending video enabled */ |
507 | LOGGER_WARNING("Failed to enable video sending!"); | 363 | LOGGER_WARNING("Failed to enable video sending!"); |
508 | goto FAILURE; | 364 | goto FAILURE; |
509 | } | 365 | } |
510 | 366 | ||
511 | if (p_video_b > 0 && 0 != cs_enable_video_receiving(cs)) { /* Receiving video enabled */ | 367 | if (p_video_b > 0 && 0 != cs_enable_video_receiving(cs)) { /* Receiving video enabled */ |
512 | LOGGER_WARNING("Failed to enable video receiving!"); | 368 | LOGGER_WARNING("Failed to enable video receiving!"); |
513 | goto FAILURE; | 369 | goto FAILURE; |
514 | } | 370 | } |
515 | 371 | ||
516 | return cs; | 372 | return cs; |
517 | 373 | ||
518 | FAILURE: | 374 | FAILURE: |
519 | LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); | 375 | LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); |
520 | 376 | ||
521 | cs_disable_audio_sending(cs); | 377 | cs_disable_audio_sending(cs); |
522 | cs_disable_audio_receiving(cs); | 378 | cs_disable_audio_receiving(cs); |
523 | cs_disable_video_sending(cs); | 379 | cs_disable_video_sending(cs); |
524 | cs_disable_video_receiving(cs); | 380 | cs_disable_video_receiving(cs); |
525 | 381 | ||
526 | free(cs); | 382 | free(cs); |
527 | 383 | ||
528 | return NULL; | 384 | return NULL; |
529 | } | 385 | } |
530 | 386 | ||
@@ -532,7 +388,7 @@ void cs_kill(CSSession *cs) | |||
532 | { | 388 | { |
533 | if (!cs) | 389 | if (!cs) |
534 | return; | 390 | return; |
535 | 391 | ||
536 | /* NOTE: queue_message() will not be called since | 392 | /* NOTE: queue_message() will not be called since |
537 | * the callback is unregistered before cs_kill is called. | 393 | * the callback is unregistered before cs_kill is called. |
538 | */ | 394 | */ |
@@ -541,78 +397,110 @@ void cs_kill(CSSession *cs) | |||
541 | cs_disable_audio_receiving(cs); | 397 | cs_disable_audio_receiving(cs); |
542 | cs_disable_video_sending(cs); | 398 | cs_disable_video_sending(cs); |
543 | cs_disable_video_receiving(cs); | 399 | cs_disable_video_receiving(cs); |
544 | 400 | ||
545 | LOGGER_DEBUG("Terminated codec state: %p", cs); | 401 | LOGGER_DEBUG("Terminated codec state: %p", cs); |
546 | free(cs); | 402 | free(cs); |
547 | } | 403 | } |
548 | 404 | ||
549 | int cs_enable_audio_sending(CSSession* cs, uint32_t bitrate) | 405 | |
406 | |||
407 | void cs_init_video_splitter_cycle(CSSession* cs) | ||
550 | { | 408 | { |
551 | if (cs->audio_encoder) | 409 | cs->split_video_frame[0] = cs->frameid_out++; |
552 | return 0; | 410 | cs->split_video_frame[1] = 0; |
553 | 411 | } | |
554 | /** | 412 | |
555 | * Encoder is initialized with default values. These values (Sampling rate, channel count) | 413 | int cs_update_video_splitter_cycle(CSSession *cs, const uint8_t *payload, uint16_t length) |
556 | * change on the fly from toxav. | 414 | { |
557 | */ | 415 | cs->processing_video_frame = payload; |
558 | 416 | cs->processing_video_frame_size = length; | |
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 | 417 | ||
569 | if ( rc != OPUS_OK ) { | 418 | return ((length - 1) / cs->video_frame_piece_size) + 1; |
570 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | 419 | } |
571 | goto FAILURE; | 420 | |
421 | const uint8_t *cs_iterate_split_video_frame(CSSession *cs, uint16_t *size) | ||
422 | { | ||
423 | if (!cs || !size) return NULL; | ||
424 | |||
425 | if (cs->processing_video_frame_size > cs->video_frame_piece_size) { | ||
426 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, | ||
427 | cs->processing_video_frame, | ||
428 | cs->video_frame_piece_size); | ||
429 | |||
430 | cs->processing_video_frame += cs->video_frame_piece_size; | ||
431 | cs->processing_video_frame_size -= cs->video_frame_piece_size; | ||
432 | |||
433 | *size = cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE; | ||
434 | } else { | ||
435 | memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE, | ||
436 | cs->processing_video_frame, | ||
437 | cs->processing_video_frame_size); | ||
438 | |||
439 | *size = cs->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE; | ||
572 | } | 440 | } |
441 | |||
442 | cs->split_video_frame[1]++; | ||
443 | |||
444 | return cs->split_video_frame; | ||
445 | } | ||
446 | |||
447 | |||
448 | |||
449 | int cs_set_sending_video_resolution(CSSession *cs, uint16_t width, uint16_t height) | ||
450 | { | ||
451 | if (!cs->v_encoding) | ||
452 | return -1; | ||
573 | 453 | ||
574 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); | 454 | /* TODO FIXME reference is safe? */ |
455 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; | ||
575 | 456 | ||
576 | if ( rc != OPUS_OK ) { | 457 | if (cfg.g_w == width && cfg.g_h == height) |
577 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | 458 | return 0; |
578 | goto FAILURE; | 459 | /* |
460 | if (width * height > cs->max_width * cs->max_height) { | ||
461 | vpx_codec_ctx_t v_encoder = cs->v_encoder; | ||
462 | |||
463 | if (init_video_encoder(cs, width, height, cs->video_bitrate) == -1) { | ||
464 | cs->v_encoder = v_encoder; | ||
465 | return cs_ErrorSettingVideoResolution; | ||
466 | } | ||
467 | |||
468 | vpx_codec_destroy(&v_encoder); | ||
469 | return 0; | ||
470 | }*/ | ||
471 | |||
472 | LOGGER_DEBUG("New video resolution: %u %u", width, height); | ||
473 | cfg.g_w = width; | ||
474 | cfg.g_h = height; | ||
475 | int rc = vpx_codec_enc_config_set(cs->v_encoder, &cfg); | ||
476 | |||
477 | if ( rc != VPX_CODEC_OK) { | ||
478 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
479 | return cs_ErrorSettingVideoResolution; | ||
579 | } | 480 | } |
580 | 481 | ||
581 | cs->channels = 2; | ||
582 | return 0; | 482 | return 0; |
583 | |||
584 | FAILURE: | ||
585 | cs_disable_audio_sending(cs); | ||
586 | return -1; | ||
587 | } | 483 | } |
588 | 484 | ||
589 | int cs_enable_audio_receiving(CSSession* cs) | 485 | int cs_set_sending_video_bitrate(CSSession *cs, uint32_t bitrate) |
590 | { | 486 | { |
591 | if (cs->audio_decoder) | 487 | if (!cs->v_encoding) |
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; | 488 | return -1; |
605 | } | ||
606 | 489 | ||
490 | /* TODO FIXME reference is safe? */ | ||
491 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; | ||
492 | if (cfg.rc_target_bitrate == bitrate) | ||
493 | return 0; | ||
494 | |||
495 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); | ||
496 | cfg.rc_target_bitrate = bitrate; | ||
607 | 497 | ||
608 | if ( !(cs->j_buf = jbuf_new(DEFAULT_JBUF)) ) { | 498 | int rc = vpx_codec_enc_config_set(cs->v_encoder, &cfg); |
609 | LOGGER_WARNING("Jitter buffer creaton failed!"); | 499 | if ( rc != VPX_CODEC_OK) { |
610 | opus_decoder_destroy(cs->audio_decoder); | 500 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); |
611 | cs->audio_decoder = NULL; | 501 | return cs_ErrorSettingVideoBitrate; |
612 | return -1; | ||
613 | } | 502 | } |
614 | 503 | ||
615 | |||
616 | return 0; | 504 | return 0; |
617 | } | 505 | } |
618 | 506 | ||
@@ -704,25 +592,6 @@ FAILURE: | |||
704 | return -1; | 592 | return -1; |
705 | } | 593 | } |
706 | 594 | ||
707 | void cs_disable_audio_sending(CSSession* cs) | ||
708 | { | ||
709 | if ( cs->audio_encoder ) { | ||
710 | opus_encoder_destroy(cs->audio_encoder); | ||
711 | cs->audio_encoder = NULL; | ||
712 | cs->channels = 0; | ||
713 | } | ||
714 | } | ||
715 | |||
716 | void cs_disable_audio_receiving(CSSession* cs) | ||
717 | { | ||
718 | if ( 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 | } | ||
725 | |||
726 | void cs_disable_video_sending(CSSession* cs) | 595 | void cs_disable_video_sending(CSSession* cs) |
727 | { | 596 | { |
728 | if (cs->v_encoding) { | 597 | if (cs->v_encoding) { |
@@ -752,6 +621,163 @@ void cs_disable_video_receiving(CSSession* cs) | |||
752 | 621 | ||
753 | 622 | ||
754 | 623 | ||
624 | int cs_set_sending_audio_bitrate(CSSession *cs, int32_t rate) | ||
625 | { | ||
626 | if (cs->audio_encoder == NULL) | ||
627 | return -1; | ||
628 | |||
629 | int rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(rate)); | ||
630 | |||
631 | if ( rc != OPUS_OK ) { | ||
632 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
633 | return -1; | ||
634 | } | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | int cs_set_sending_audio_sampling_rate(CSSession* cs, int32_t rate) | ||
640 | { | ||
641 | /* TODO Find a better way? */ | ||
642 | if (cs->audio_encoder == NULL) | ||
643 | return -1; | ||
644 | |||
645 | int rc = OPUS_OK; | ||
646 | int bitrate = 0; | ||
647 | int channels = cs->encoder_channels; | ||
648 | |||
649 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_BITRATE(&bitrate)); | ||
650 | |||
651 | if ( rc != OPUS_OK ) { | ||
652 | LOGGER_ERROR("Error while getting encoder ctl: %s", opus_strerror(rc)); | ||
653 | return -1; | ||
654 | } | ||
655 | |||
656 | cs_disable_audio_sending(cs); | ||
657 | return cs_enable_audio_sending(cs, bitrate, channels); | ||
658 | } | ||
659 | |||
660 | int cs_set_sending_audio_channels(CSSession* cs, int32_t count) | ||
661 | { | ||
662 | /* TODO Find a better way? */ | ||
663 | if (cs->audio_encoder == NULL) | ||
664 | return -1; | ||
665 | |||
666 | if (cs->encoder_channels == count) | ||
667 | return 0; | ||
668 | |||
669 | int rc = OPUS_OK; | ||
670 | int bitrate = 0; | ||
671 | |||
672 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_BITRATE(&bitrate)); | ||
673 | |||
674 | if ( rc != OPUS_OK ) { | ||
675 | LOGGER_ERROR("Error while getting encoder ctl: %s", opus_strerror(rc)); | ||
676 | return -1; | ||
677 | } | ||
678 | |||
679 | cs_disable_audio_sending(cs); | ||
680 | return cs_enable_audio_sending(cs, bitrate, count); | ||
681 | } | ||
682 | |||
683 | void cs_disable_audio_sending(CSSession* cs) | ||
684 | { | ||
685 | if ( cs->audio_encoder ) { | ||
686 | opus_encoder_destroy(cs->audio_encoder); | ||
687 | cs->audio_encoder = NULL; | ||
688 | cs->encoder_channels = 0; | ||
689 | } | ||
690 | } | ||
691 | |||
692 | void cs_disable_audio_receiving(CSSession* cs) | ||
693 | { | ||
694 | if ( cs->audio_decoder ) { | ||
695 | opus_decoder_destroy(cs->audio_decoder); | ||
696 | cs->audio_decoder = NULL; | ||
697 | jbuf_free(cs->j_buf); | ||
698 | cs->j_buf = NULL; | ||
699 | |||
700 | /* It's used for measuring iteration interval so this has to be some value. | ||
701 | * To avoid unecessary checking we set this to 500 | ||
702 | */ | ||
703 | cs->last_packet_frame_duration = 500; | ||
704 | } | ||
705 | } | ||
706 | |||
707 | int cs_enable_audio_sending(CSSession* cs, uint32_t bitrate, int channels) | ||
708 | { | ||
709 | if (cs->audio_encoder) | ||
710 | return 0; | ||
711 | |||
712 | /** | ||
713 | * Encoder is initialized with default values. These values (Sampling rate, channel count) | ||
714 | * change on the fly from toxav. | ||
715 | */ | ||
716 | |||
717 | int rc = OPUS_OK; | ||
718 | cs->audio_encoder = opus_encoder_create(48000, channels, OPUS_APPLICATION_AUDIO, &rc); | ||
719 | |||
720 | if ( rc != OPUS_OK ) { | ||
721 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
722 | return -1; | ||
723 | } | ||
724 | |||
725 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(bitrate)); | ||
726 | |||
727 | if ( rc != OPUS_OK ) { | ||
728 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
729 | goto FAILURE; | ||
730 | } | ||
731 | |||
732 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); | ||
733 | |||
734 | if ( rc != OPUS_OK ) { | ||
735 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
736 | goto FAILURE; | ||
737 | } | ||
738 | |||
739 | cs->encoder_channels = channels; | ||
740 | return 0; | ||
741 | |||
742 | FAILURE: | ||
743 | cs_disable_audio_sending(cs); | ||
744 | return -1; | ||
745 | } | ||
746 | |||
747 | int cs_enable_audio_receiving(CSSession* cs) | ||
748 | { | ||
749 | if (cs->audio_decoder) | ||
750 | return 0; | ||
751 | |||
752 | /** | ||
753 | * Decoder is initialized with default values. These values (Sampling rate, channel count) | ||
754 | * change on the fly from toxav. | ||
755 | */ | ||
756 | |||
757 | int rc; | ||
758 | cs->audio_decoder = opus_decoder_create(48000, 2, &rc ); | ||
759 | |||
760 | if ( rc != OPUS_OK ) { | ||
761 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | ||
762 | return -1; | ||
763 | } | ||
764 | |||
765 | |||
766 | if ( !(cs->j_buf = jbuf_new(DEFAULT_JBUF)) ) { | ||
767 | LOGGER_WARNING("Jitter buffer creaton failed!"); | ||
768 | opus_decoder_destroy(cs->audio_decoder); | ||
769 | cs->audio_decoder = NULL; | ||
770 | return -1; | ||
771 | } | ||
772 | |||
773 | /* It's used for measuring iteration interval so this has to be some value. | ||
774 | * To avoid unecessary checking we set this to 500 | ||
775 | */ | ||
776 | cs->last_packet_frame_duration = 500; | ||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | |||
755 | 781 | ||
756 | /* Called from RTP */ | 782 | /* Called from RTP */ |
757 | void queue_message(RTPSession *session, RTPMessage *msg) | 783 | void queue_message(RTPSession *session, RTPMessage *msg) |
diff --git a/toxav/codec.h b/toxav/codec.h index 92262ef8..951d6d2f 100644 --- a/toxav/codec.h +++ b/toxav/codec.h | |||
@@ -96,7 +96,7 @@ typedef struct _CSSession { | |||
96 | uint32_t video_frame_piece_size; | 96 | uint32_t video_frame_piece_size; |
97 | uint32_t max_video_frame_size; | 97 | uint32_t max_video_frame_size; |
98 | 98 | ||
99 | /* Reassembling */ | 99 | /* Splitting */ |
100 | uint8_t *split_video_frame; | 100 | uint8_t *split_video_frame; |
101 | const uint8_t *processing_video_frame; | 101 | const uint8_t *processing_video_frame; |
102 | uint16_t processing_video_frame_size; | 102 | uint16_t processing_video_frame_size; |
@@ -110,10 +110,13 @@ typedef struct _CSSession { | |||
110 | 110 | ||
111 | /* audio encoding */ | 111 | /* audio encoding */ |
112 | OpusEncoder *audio_encoder; | 112 | OpusEncoder *audio_encoder; |
113 | int32_t channels; | 113 | int32_t encoder_channels; |
114 | 114 | ||
115 | /* audio decoding */ | 115 | /* audio decoding */ |
116 | OpusDecoder *audio_decoder; | 116 | OpusDecoder *audio_decoder; |
117 | int32_t last_pack_channels; | ||
118 | int32_t last_packet_sampling_rate; | ||
119 | int32_t last_packet_frame_duration; | ||
117 | struct _JitterBuffer *j_buf; | 120 | struct _JitterBuffer *j_buf; |
118 | 121 | ||
119 | 122 | ||
@@ -127,54 +130,55 @@ typedef struct _CSSession { | |||
127 | * | 130 | * |
128 | * | 131 | * |
129 | */ | 132 | */ |
130 | 133 | void *agent; /* Pointer to ToxAV TODO make this pointer to ToxAV*/ | |
131 | /* Callbacks */ | ||
132 | PAIR(CSAudioCallback, void *) acb; | ||
133 | PAIR(CSVideoCallback, void *) vcb; | ||
134 | |||
135 | void *agent; /* Pointer to ToxAv */ | ||
136 | int32_t call_idx; | 134 | int32_t call_idx; |
137 | 135 | ||
138 | pthread_mutex_t queue_mutex[1]; | 136 | pthread_mutex_t queue_mutex[1]; |
139 | } CSSession; | 137 | } CSSession; |
140 | 138 | ||
141 | int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length); | ||
142 | const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size); | ||
143 | 139 | ||
144 | /** | 140 | /** |
145 | * Call playback callbacks | 141 | * Generic |
146 | */ | 142 | */ |
147 | void cs_do(CSSession *cs); | 143 | void cs_do(CSSession *cs); |
148 | 144 | ||
149 | /** | 145 | /* Make sure to be called BEFORE corresponding rtp_new */ |
150 | * Reconfigure video settings; return 0 on success or -1 on failure. | 146 | CSSession *cs_new(uint32_t s_audio_b, uint32_t p_audio_b, uint32_t s_video_b, uint32_t p_video_b); |
147 | /* Make sure to be called AFTER corresponding rtp_kill */ | ||
148 | void cs_kill(CSSession *cs); | ||
149 | |||
150 | |||
151 | /** | ||
152 | * VIDEO HANDLING | ||
151 | */ | 153 | */ |
154 | void cs_init_video_splitter_cycle(CSSession *cs); | ||
155 | int cs_update_video_splitter_cycle(CSSession* cs, const uint8_t* payload, uint16_t length); | ||
156 | const uint8_t *cs_iterate_split_video_frame(CSSession *cs, uint16_t *size); | ||
157 | |||
152 | int cs_set_sending_video_resolution(CSSession *cs, uint16_t width, uint16_t height); | 158 | int cs_set_sending_video_resolution(CSSession *cs, uint16_t width, uint16_t height); |
153 | int cs_set_sending_video_bitrate(CSSession *cs, uint32_t bitrate); | 159 | int cs_set_sending_video_bitrate(CSSession *cs, uint32_t bitrate); |
154 | 160 | ||
161 | int cs_enable_video_sending(CSSession* cs, uint32_t bitrate); | ||
162 | int cs_enable_video_receiving(CSSession* cs); | ||
163 | |||
164 | void cs_disable_video_sending(CSSession* cs); | ||
165 | void cs_disable_video_receiving(CSSession* cs); | ||
166 | |||
167 | /** | ||
168 | * AUDIO HANDLING | ||
169 | */ | ||
155 | int cs_set_sending_audio_bitrate(CSSession* cs, int32_t rate); | 170 | int cs_set_sending_audio_bitrate(CSSession* cs, int32_t rate); |
156 | /* NOTE: Try not to call these a lot */ | ||
157 | int cs_set_sending_audio_sampling_rate(CSSession* cs, int32_t rate); | 171 | int cs_set_sending_audio_sampling_rate(CSSession* cs, int32_t rate); |
158 | int cs_set_sending_audio_channels(CSSession* cs, int32_t count); | 172 | int cs_set_sending_audio_channels(CSSession* cs, int32_t count); |
159 | 173 | ||
160 | /** | 174 | int cs_enable_audio_sending(CSSession* cs, uint32_t bitrate, int channels); |
161 | * Make sure to be called BEFORE corresponding rtp_new | ||
162 | */ | ||
163 | CSSession *cs_new(uint32_t s_audio_b, uint32_t p_audio_b, uint32_t s_video_b, uint32_t p_video_b); | ||
164 | /** | ||
165 | * Make sure to be called AFTER corresponding rtp_kill | ||
166 | */ | ||
167 | void cs_kill(CSSession *cs); | ||
168 | |||
169 | int cs_enable_audio_sending(CSSession* cs, uint32_t bitrate); | ||
170 | int cs_enable_audio_receiving(CSSession* cs); | 175 | int cs_enable_audio_receiving(CSSession* cs); |
171 | int cs_enable_video_sending(CSSession* cs, uint32_t bitrate); | ||
172 | int cs_enable_video_receiving(CSSession* cs); | ||
173 | 176 | ||
174 | void cs_disable_audio_sending(CSSession* cs); | 177 | void cs_disable_audio_sending(CSSession* cs); |
175 | void cs_disable_audio_receiving(CSSession* cs); | 178 | void cs_disable_audio_receiving(CSSession* cs); |
176 | void cs_disable_video_sending(CSSession* cs); | 179 | |
177 | void cs_disable_video_receiving(CSSession* cs); | 180 | |
181 | |||
178 | 182 | ||
179 | /* Internal. Called from rtp_handle_message */ | 183 | /* Internal. Called from rtp_handle_message */ |
180 | void queue_message(RTPSession *session, RTPMessage *msg); | 184 | void queue_message(RTPSession *session, RTPMessage *msg); |
diff --git a/toxav/rtp.c b/toxav/rtp.c index ba93e781..a50dd7ce 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c | |||
@@ -488,13 +488,13 @@ int rtp_register_for_receiving(RTPSession* session) | |||
488 | rtp_handle_packet, session); | 488 | rtp_handle_packet, session); |
489 | } | 489 | } |
490 | 490 | ||
491 | int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ) | 491 | int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) |
492 | { | 492 | { |
493 | RTPMessage *msg = rtp_new_message (session, data, length); | 493 | RTPMessage *msg = rtp_new_message (session, data, length); |
494 | 494 | ||
495 | if ( !msg ) return -1; | 495 | if ( !msg ) return -1; |
496 | 496 | ||
497 | if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) { | 497 | if ( -1 == send_custom_lossy_packet(session->m, session->dest, msg->data, msg->length) ) { |
498 | LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); | 498 | LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); |
499 | rtp_free_msg ( session, msg ); | 499 | rtp_free_msg ( session, msg ); |
500 | return rtp_ErrorSending; | 500 | return rtp_ErrorSending; |
diff --git a/toxav/rtp.h b/toxav/rtp.h index b25b13ba..03b44719 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h | |||
@@ -121,7 +121,7 @@ int rtp_register_for_receiving (RTPSession *session); | |||
121 | /** | 121 | /** |
122 | * Sends msg to _RTPSession::dest | 122 | * Sends msg to _RTPSession::dest |
123 | */ | 123 | */ |
124 | int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ); | 124 | int rtp_send_msg ( RTPSession* session, const uint8_t* data, uint16_t length ); |
125 | 125 | ||
126 | /** | 126 | /** |
127 | * Dealloc msg. | 127 | * Dealloc msg. |
diff --git a/toxav/toxav.c b/toxav/toxav.c index 68402020..ee7f49a6 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c | |||
@@ -176,14 +176,14 @@ uint32_t toxav_do_interval(ToxAv *av) | |||
176 | void toxav_do(ToxAv *av) | 176 | void toxav_do(ToxAv *av) |
177 | { | 177 | { |
178 | msi_do(av->msi_session); | 178 | msi_do(av->msi_session); |
179 | 179 | ||
180 | uint64_t start = current_time_monotonic(); | 180 | uint64_t start = current_time_monotonic(); |
181 | 181 | ||
182 | uint32_t i = 0; | 182 | uint32_t i = 0; |
183 | 183 | ||
184 | for (; i < av->max_calls; i ++) { | 184 | for (; i < av->max_calls; i ++) { |
185 | pthread_mutex_lock(av->calls[i].mutex_control); | 185 | pthread_mutex_lock(av->calls[i].mutex_control); |
186 | 186 | ||
187 | if (av->calls[i].active) { | 187 | if (av->calls[i].active) { |
188 | pthread_mutex_lock(av->calls[i].mutex_do); | 188 | pthread_mutex_lock(av->calls[i].mutex_do); |
189 | pthread_mutex_unlock(av->calls[i].mutex_control); | 189 | pthread_mutex_unlock(av->calls[i].mutex_control); |
@@ -193,12 +193,12 @@ void toxav_do(ToxAv *av) | |||
193 | pthread_mutex_unlock(av->calls[i].mutex_control); | 193 | pthread_mutex_unlock(av->calls[i].mutex_control); |
194 | } | 194 | } |
195 | } | 195 | } |
196 | 196 | ||
197 | uint64_t end = current_time_monotonic(); | 197 | uint64_t end = current_time_monotonic(); |
198 | 198 | ||
199 | /* TODO maybe use variable for sizes */ | 199 | /* TODO maybe use variable for sizes */ |
200 | av->dectmsstotal += end - start; | 200 | av->dectmsstotal += end - start; |
201 | 201 | ||
202 | if (++av->dectmsscount == 3) { | 202 | if (++av->dectmsscount == 3) { |
203 | av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */; | 203 | av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */; |
204 | av->dectmsscount = 0; | 204 | av->dectmsscount = 0; |
@@ -432,7 +432,7 @@ static int toxav_send_rtp_payload(ToxAv *av, | |||
432 | int i; | 432 | int i; |
433 | 433 | ||
434 | for (i = 0; i < parts; i++) { | 434 | for (i = 0; i < parts; i++) { |
435 | iter = cs_get_split_video_frame(call->cs, &part_size); | 435 | iter = cs_iterate_split_video_frame(call->cs, &part_size); |
436 | 436 | ||
437 | if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0) | 437 | if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0) |
438 | return av_ErrorSendingPayload; | 438 | return av_ErrorSendingPayload; |
diff --git a/toxav/toxav_new.c b/toxav/toxav_new.c index d6c1872c..857d5a83 100644 --- a/toxav/toxav_new.c +++ b/toxav/toxav_new.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <stdlib.h> | 34 | #include <stdlib.h> |
35 | #include <string.h> | 35 | #include <string.h> |
36 | 36 | ||
37 | #define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) | ||
37 | 38 | ||
38 | enum { | 39 | enum { |
39 | audio_index, | 40 | audio_index, |
@@ -75,6 +76,8 @@ struct toxAV | |||
75 | int32_t dmssc; /** Measure count */ | 76 | int32_t dmssc; /** Measure count */ |
76 | int32_t dmsst; /** Last cycle total */ | 77 | int32_t dmsst; /** Last cycle total */ |
77 | int32_t dmssa; /** Average decoding time in ms */ | 78 | int32_t dmssa; /** Average decoding time in ms */ |
79 | |||
80 | uint32_t interval; /** Calculated interval */ | ||
78 | }; | 81 | }; |
79 | 82 | ||
80 | 83 | ||
@@ -130,6 +133,7 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) | |||
130 | goto FAILURE; | 133 | goto FAILURE; |
131 | } | 134 | } |
132 | 135 | ||
136 | av->interval = 200; | ||
133 | av->msi->agent_handler = av; | 137 | av->msi->agent_handler = av; |
134 | 138 | ||
135 | msi_register_callback(av->msi, i_toxav_msi_callback_invite, msi_OnInvite, NULL); | 139 | msi_register_callback(av->msi, i_toxav_msi_callback_invite, msi_OnInvite, NULL); |
@@ -144,7 +148,7 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) | |||
144 | msi_register_callback(av->msi, i_toxav_msi_callback_state_change, msi_OnSelfCSChange, NULL); | 148 | msi_register_callback(av->msi, i_toxav_msi_callback_state_change, msi_OnSelfCSChange, NULL); |
145 | 149 | ||
146 | 150 | ||
147 | if (error) | 151 | if (error) |
148 | *error = rc; | 152 | *error = rc; |
149 | 153 | ||
150 | return av; | 154 | return av; |
@@ -175,12 +179,32 @@ Tox* toxav_get_tox(ToxAV* av) | |||
175 | 179 | ||
176 | uint32_t toxav_iteration_interval(const ToxAV* av) | 180 | uint32_t toxav_iteration_interval(const ToxAV* av) |
177 | { | 181 | { |
178 | 182 | return av->interval; | |
179 | } | 183 | } |
180 | 184 | ||
181 | void toxav_iteration(ToxAV* av) | 185 | void toxav_iteration(ToxAV* av) |
182 | { | 186 | { |
183 | 187 | msi_do(av->msi); | |
188 | |||
189 | uint64_t start = current_time_monotonic(); | ||
190 | uint32_t rc = 200 + av->dmssa; /* If no call is active interval is 200 */ | ||
191 | |||
192 | IToxAVCall* i = av->calls[av->calls_head]; | ||
193 | for (; i; i = i->next) { | ||
194 | if (i->active) { | ||
195 | cs_do(i->cs); | ||
196 | rc = MIN(i->cs->last_packet_frame_duration, rc); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | av->interval = rc < av->dmssa ? 0 : rc - av->dmssa; | ||
201 | av->dmsst += current_time_monotonic() - start; | ||
202 | |||
203 | if (++av->dmssc == 3) { | ||
204 | av->dmssa = av->dmsst / 3 + 2 /* NOTE Magic Offset for precission */; | ||
205 | av->dmssc = 0; | ||
206 | av->dmsst = 0; | ||
207 | } | ||
184 | } | 208 | } |
185 | 209 | ||
186 | bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) | 210 | bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) |
@@ -261,52 +285,244 @@ void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* u | |||
261 | 285 | ||
262 | bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error) | 286 | bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error) |
263 | { | 287 | { |
264 | 288 | TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK; | |
289 | |||
290 | if (m_friend_exists(av->m, friend_number)) { | ||
291 | rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND; | ||
292 | goto END; | ||
293 | } | ||
294 | |||
295 | |||
296 | IToxAVCall* call = i_toxav_get_call(av, friend_number); | ||
297 | if (call == NULL) { | ||
298 | rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; | ||
299 | goto END; | ||
300 | } | ||
301 | |||
302 | /* TODO rest of these */ | ||
303 | switch (control) | ||
304 | { | ||
305 | case TOXAV_CALL_CONTROL_RESUME: { | ||
306 | |||
307 | } break; | ||
308 | |||
309 | case TOXAV_CALL_CONTROL_PAUSE: { | ||
310 | |||
311 | } break; | ||
312 | |||
313 | case TOXAV_CALL_CONTROL_CANCEL: { | ||
314 | if (av->msi->calls[call->call_idx]->state == msi_CallActive) { | ||
315 | /* Hang up */ | ||
316 | msi_hangup(av->msi, call->call_idx); | ||
317 | } else if (av->msi->calls[call->call_idx]->state == msi_CallRequested) { | ||
318 | /* Reject the call */ | ||
319 | msi_reject(av->msi, call->call_idx); | ||
320 | } else if (av->msi->calls[call->call_idx]->state == msi_CallRequesting) { | ||
321 | /* Cancel the call */ | ||
322 | msi_cancel(av->msi, call->call_idx); | ||
323 | } | ||
324 | } break; | ||
325 | |||
326 | case TOXAV_CALL_CONTROL_MUTE_AUDIO: { | ||
327 | |||
328 | } break; | ||
329 | |||
330 | case TOXAV_CALL_CONTROL_MUTE_VIDEO: { | ||
331 | |||
332 | } break; | ||
333 | } | ||
334 | |||
335 | END: | ||
336 | if (error) | ||
337 | *error = rc; | ||
338 | |||
339 | return rc == TOXAV_ERR_CALL_CONTROL_OK; | ||
265 | } | 340 | } |
266 | 341 | ||
267 | bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, TOXAV_ERR_BIT_RATE* error) | 342 | bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, TOXAV_ERR_BIT_RATE* error) |
268 | { | 343 | { |
269 | 344 | /* TODO */ | |
270 | } | 345 | } |
271 | 346 | ||
272 | bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, TOXAV_ERR_BIT_RATE* error) | 347 | bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, TOXAV_ERR_BIT_RATE* error) |
273 | { | 348 | { |
274 | 349 | /* TODO */ | |
275 | } | 350 | } |
276 | 351 | ||
277 | void toxav_callback_request_video_frame(ToxAV* av, toxav_request_video_frame_cb* function, void* user_data) | 352 | void toxav_callback_request_video_frame(ToxAV* av, toxav_request_video_frame_cb* function, void* user_data) |
278 | { | 353 | { |
279 | 354 | /* TODO */ | |
280 | } | 355 | } |
281 | 356 | ||
282 | bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, const uint8_t* a, TOXAV_ERR_SEND_FRAME* error) | 357 | bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, TOXAV_ERR_SEND_FRAME* error) |
283 | { | 358 | { |
284 | 359 | TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; | |
360 | IToxAVCall* call; | ||
361 | |||
362 | if (m_friend_exists(av->m, friend_number)) { | ||
363 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; | ||
364 | goto END; | ||
365 | } | ||
366 | |||
367 | call = i_toxav_get_call(av, friend_number); | ||
368 | if (call == NULL) { | ||
369 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; | ||
370 | goto END; | ||
371 | } | ||
372 | |||
373 | if (av->msi->calls[call->call_idx]->state != msi_CallActive) { | ||
374 | /* TODO */ | ||
375 | rc = TOXAV_ERR_SEND_FRAME_NOT_REQUESTED; | ||
376 | goto END; | ||
377 | } | ||
378 | |||
379 | if ( y == NULL || u == NULL || v == NULL ) { | ||
380 | rc = TOXAV_ERR_SEND_FRAME_NULL; | ||
381 | goto END; | ||
382 | } | ||
383 | |||
384 | if ( cs_set_sending_video_resolution(call->cs, width, height) != 0 ) { | ||
385 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | ||
386 | goto END; | ||
387 | } | ||
388 | |||
389 | { /* Encode */ | ||
390 | vpx_image_t img; | ||
391 | img.w = img.h = img.d_w = img.d_h = 0; | ||
392 | vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, width, height, 1); | ||
393 | |||
394 | /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes." | ||
395 | * http://fourcc.org/yuv.php#IYUV | ||
396 | */ | ||
397 | memcpy(img.planes[VPX_PLANE_Y], y, width * height); | ||
398 | memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2)); | ||
399 | memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2)); | ||
400 | |||
401 | int vrc = vpx_codec_encode(call->cs->v_encoder, &img, | ||
402 | call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); | ||
403 | |||
404 | vpx_img_free(&img); /* FIXME don't free? */ | ||
405 | if ( vrc != VPX_CODEC_OK) { | ||
406 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc)); | ||
407 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | ||
408 | goto END; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | ++call->cs->frame_counter; | ||
413 | |||
414 | { /* Split and send */ | ||
415 | vpx_codec_iter_t iter = NULL; | ||
416 | const vpx_codec_cx_pkt_t *pkt; | ||
417 | |||
418 | cs_init_video_splitter_cycle(call->cs); | ||
419 | |||
420 | while ( (pkt = vpx_codec_get_cx_data(call->cs->v_encoder, &iter)) ) { | ||
421 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { | ||
422 | int parts = cs_update_video_splitter_cycle(call->cs, pkt->data.frame.buf, | ||
423 | pkt->data.frame.sz); | ||
424 | |||
425 | if (parts < 0) /* Should never happen though */ | ||
426 | continue; | ||
427 | |||
428 | uint16_t part_size; | ||
429 | const uint8_t *iter; | ||
430 | |||
431 | int i; | ||
432 | for (i = 0; i < parts; i++) { | ||
433 | iter = cs_iterate_split_video_frame(call->cs, &part_size); | ||
434 | |||
435 | if (rtp_send_msg(call->rtps[video_index], iter, part_size) < 0) | ||
436 | goto END; | ||
437 | } | ||
438 | } | ||
439 | } | ||
440 | } | ||
441 | |||
442 | END: | ||
443 | if (error) | ||
444 | *error = rc; | ||
445 | |||
446 | return rc == TOXAV_ERR_SEND_FRAME_OK; | ||
285 | } | 447 | } |
286 | 448 | ||
287 | void toxav_callback_request_audio_frame(ToxAV* av, toxav_request_audio_frame_cb* function, void* user_data) | 449 | void toxav_callback_request_audio_frame(ToxAV* av, toxav_request_audio_frame_cb* function, void* user_data) |
288 | { | 450 | { |
289 | 451 | /* TODO */ | |
290 | } | 452 | } |
291 | 453 | ||
292 | bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME* error) | 454 | bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME* error) |
293 | { | 455 | { |
294 | 456 | TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK; | |
457 | IToxAVCall* call; | ||
458 | |||
459 | if (m_friend_exists(av->m, friend_number)) { | ||
460 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND; | ||
461 | goto END; | ||
462 | } | ||
463 | |||
464 | call = i_toxav_get_call(av, friend_number); | ||
465 | if (call == NULL) { | ||
466 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; | ||
467 | goto END; | ||
468 | } | ||
469 | |||
470 | if (av->msi->calls[call->call_idx]->state != msi_CallActive) { | ||
471 | /* TODO */ | ||
472 | rc = TOXAV_ERR_SEND_FRAME_NOT_REQUESTED; | ||
473 | goto END; | ||
474 | } | ||
475 | |||
476 | if ( pcm == NULL ) { | ||
477 | rc = TOXAV_ERR_SEND_FRAME_NULL; | ||
478 | goto END; | ||
479 | } | ||
480 | |||
481 | if ( channels != 1 || channels != 2 ) { | ||
482 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | ||
483 | goto END; | ||
484 | } | ||
485 | |||
486 | { /* Encode and send */ | ||
487 | /* TODO redundant? */ | ||
488 | cs_set_sending_audio_channels(call->cs, channels); | ||
489 | cs_set_sending_audio_sampling_rate(call->cs, sampling_rate); | ||
490 | |||
491 | uint8_t dest[sample_count * channels * 2 /* sizeof(uint16_t) */]; | ||
492 | int vrc = opus_encode(call->cs->audio_encoder, pcm, sample_count, dest, sizeof (dest)); | ||
493 | |||
494 | if (vrc < 0) { | ||
495 | LOGGER_WARNING("Failed to encode frame"); | ||
496 | rc = TOXAV_ERR_SEND_FRAME_INVALID; | ||
497 | goto END; | ||
498 | } | ||
499 | |||
500 | vrc = rtp_send_msg(call->rtps[audio_index], dest, vrc); | ||
501 | /* TODO check for error? */ | ||
502 | } | ||
503 | |||
504 | END: | ||
505 | if (error) | ||
506 | *error = rc; | ||
507 | |||
508 | return rc == TOXAV_ERR_SEND_FRAME_OK; | ||
295 | } | 509 | } |
296 | 510 | ||
297 | void toxav_callback_receive_video_frame(ToxAV* av, toxav_receive_video_frame_cb* function, void* user_data) | 511 | void toxav_callback_receive_video_frame(ToxAV* av, toxav_receive_video_frame_cb* function, void* user_data) |
298 | { | 512 | { |
299 | 513 | av->vcb.first = function; | |
514 | av->vcb.second = user_data; | ||
300 | } | 515 | } |
301 | 516 | ||
302 | void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb* function, void* user_data) | 517 | void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb* function, void* user_data) |
303 | { | 518 | { |
304 | 519 | av->acb.first = function; | |
520 | av->acb.second = user_data; | ||
305 | } | 521 | } |
306 | 522 | ||
307 | 523 | ||
308 | /******************************************************************************* | 524 | /******************************************************************************* |
309 | * | 525 | * |
310 | * :: Internal | 526 | * :: Internal |
311 | * | 527 | * |
312 | ******************************************************************************/ | 528 | ******************************************************************************/ |
@@ -616,12 +832,6 @@ bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call) | |||
616 | call->cs->agent = av; | 832 | call->cs->agent = av; |
617 | call->cs->call_idx = call->call_idx; | 833 | call->cs->call_idx = call->call_idx; |
618 | 834 | ||
619 | call->cs->acb.first = av->acb.first; | ||
620 | call->cs->acb.second = av->acb.second; | ||
621 | |||
622 | call->cs->vcb.first = av->vcb.first; | ||
623 | call->cs->vcb.second = av->vcb.second; | ||
624 | |||
625 | 835 | ||
626 | if (c_self->audio_bitrate > 0 || c_peer->audio_bitrate > 0) { /* Prepare audio rtp */ | 836 | if (c_self->audio_bitrate > 0 || c_peer->audio_bitrate > 0) { /* Prepare audio rtp */ |
627 | call->rtps[audio_index] = rtp_new(msi_TypeAudio, av->m, av->msi->calls[call->call_idx]->peers[0]); | 837 | call->rtps[audio_index] = rtp_new(msi_TypeAudio, av->m, av->msi->calls[call->call_idx]->peers[0]); |
diff --git a/toxav/toxav_new.h b/toxav/toxav_new.h index 78e79357..038ee99a 100644 --- a/toxav/toxav_new.h +++ b/toxav/toxav_new.h | |||
@@ -374,8 +374,9 @@ void toxav_callback_request_video_frame(ToxAV *av, toxav_request_video_frame_cb | |||
374 | * | 374 | * |
375 | * This is called in response to receiving the `request_video_frame` event. | 375 | * This is called in response to receiving the `request_video_frame` event. |
376 | * | 376 | * |
377 | * Each plane should contain (width * height) pixels. The Alpha plane can be | 377 | * Y - plane should be of size: height * width |
378 | * NULL, in which case every pixel is assumed fully opaque. | 378 | * U - plane should be of size: (height/2) * (width/2) |
379 | * V - plane should be of size: (height/2) * (width/2) | ||
379 | * | 380 | * |
380 | * @param friend_number The friend number of the friend to which to send a video | 381 | * @param friend_number The friend number of the friend to which to send a video |
381 | * frame. | 382 | * frame. |
@@ -384,11 +385,10 @@ void toxav_callback_request_video_frame(ToxAV *av, toxav_request_video_frame_cb | |||
384 | * @param y Y (Luminance) plane data. | 385 | * @param y Y (Luminance) plane data. |
385 | * @param u U (Chroma) plane data. | 386 | * @param u U (Chroma) plane data. |
386 | * @param v V (Chroma) plane data. | 387 | * @param v V (Chroma) plane data. |
387 | * @param a A (Alpha) plane data. | ||
388 | */ | 388 | */ |
389 | bool toxav_send_video_frame(ToxAV *av, uint32_t friend_number, | 389 | bool toxav_send_video_frame(ToxAV *av, uint32_t friend_number, |
390 | uint16_t width, uint16_t height, | 390 | uint16_t width, uint16_t height, |
391 | uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a, | 391 | uint8_t const *y, uint8_t const *u, uint8_t const *v, |
392 | TOXAV_ERR_SEND_FRAME *error); | 392 | TOXAV_ERR_SEND_FRAME *error); |
393 | /** | 393 | /** |
394 | * The function type for the `request_audio_frame` callback. | 394 | * The function type for the `request_audio_frame` callback. |