forked from sipwise/rtpengine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcodec.c
5672 lines (4885 loc) · 173 KB
/
codec.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "codec.h"
#include <glib.h>
#include <assert.h>
#include <inttypes.h>
#include <sys/types.h>
#include "call.h"
#include "log.h"
#include "rtplib.h"
#include "codeclib.h"
#include "ssrc.h"
#include "rtcp.h"
#include "call_interfaces.h"
#include "dtmf.h"
#include "dtmflib.h"
#include "t38.h"
#include "media_player.h"
#include "timerthread.h"
#include "log_funcs.h"
#include "mqtt.h"
#include "audio_player.h"
#ifdef WITH_TRANSCODING
#include "fix_frame_channel_layout.h"
#endif
struct codec_timer {
struct timerthread_obj tt_obj;
struct timeval next;
void (*timer_func)(struct codec_timer *);
};
struct mqtt_timer {
struct codec_timer ct;
struct mqtt_timer **self;
call_t *call;
struct call_media *media;
};
struct timer_callback {
struct codec_timer ct;
void (*timer_callback_func)(call_t *, void *);
call_t *call;
void *ptr;
};
typedef void (*raw_input_func_t)(struct media_packet *mp, unsigned int);
static void __buffer_delay_raw(struct delay_buffer *dbuf, struct codec_handler *handler,
raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate);
static codec_handler_func handler_func_passthrough;
static struct timerthread codec_timers_thread;
static void rtp_payload_type_copy(rtp_payload_type *dst, const rtp_payload_type *src);
static void codec_store_add_raw_order(struct codec_store *cs, rtp_payload_type *pt);
static rtp_payload_type *codec_store_find_compatible(struct codec_store *cs,
const rtp_payload_type *pt);
static void __rtp_payload_type_add_name(codec_names_ht, rtp_payload_type *pt);
static void codec_calc_lost(struct ssrc_ctx *ssrc, uint16_t seq);
static struct codec_handler codec_handler_stub = {
.source_pt.payload_type = -1,
.dest_pt.payload_type = -1,
.handler_func = handler_func_passthrough,
.kernelize = 1,
.passthrough = 1,
};
static void __ht_queue_del(codec_names_ht ht, const str *key, int pt) {
GQueue *q = t_hash_table_lookup(ht, key);
if (!q)
return;
g_queue_remove_all(q, GINT_TO_POINTER(pt));
}
static rtp_pt_list *__codec_store_delete_link(rtp_pt_list *link, struct codec_store *cs) {
rtp_payload_type *pt = link->data;
t_hash_table_remove(cs->codecs, GINT_TO_POINTER(pt->payload_type));
__ht_queue_del(cs->codec_names, &pt->encoding, pt->payload_type);
__ht_queue_del(cs->codec_names, &pt->encoding_with_params, pt->payload_type);
__ht_queue_del(cs->codec_names, &pt->encoding_with_full_params, pt->payload_type);
__auto_type next = link->next;
if (cs->supp_link == link)
cs->supp_link = next;
t_queue_delete_link(&cs->codec_prefs, link);
payload_type_free(pt);
return next;
}
#ifdef WITH_TRANSCODING
#include <spandsp/telephony.h>
#include <spandsp/super_tone_rx.h>
#include <spandsp/logging.h>
#include <spandsp/dtmf.h>
#include "resample.h"
#include "dtmf_rx_fillin.h"
struct codec_ssrc_handler;
struct transcode_packet;
struct dtx_packet;
TYPED_GQUEUE(dtx_packet, struct dtx_packet)
struct dtx_buffer {
struct codec_timer ct;
mutex_t lock;
struct codec_ssrc_handler *csh;
int ptime; // ms per packet
int tspp; // timestamp increment per packet
unsigned int clockrate;
call_t *call;
dtx_packet_q packets;
struct media_packet last_mp;
unsigned long head_ts;
uint32_t ssrc;
time_t start;
};
struct dtx_packet {
struct transcode_packet *packet;
struct media_packet mp;
struct codec_ssrc_handler *decoder_handler; // holds reference
struct codec_ssrc_handler *input_handler; // holds reference
int (*dtx_func)(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch,
struct transcode_packet *packet, struct media_packet *mp);
};
typedef int (*encoder_input_func_t)(encoder_t *enc, AVFrame *frame,
int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2);
typedef int (*packet_input_func_t)(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch,
struct transcode_packet *packet,
unsigned long ts_delay,
int payload_type,
struct media_packet *mp);
struct delay_frame;
TYPED_GQUEUE(delay_frame, struct delay_frame)
struct delay_buffer {
struct codec_timer ct;
call_t *call;
struct codec_handler *handler;
mutex_t lock;
unsigned int delay;
delay_frame_q frames; // in reverse order: newest packet first, oldest last
};
struct delay_frame {
AVFrame *frame;
struct media_packet mp;
struct transcode_packet *packet;
unsigned long ts_delay;
int payload_type;
unsigned int clockrate;
uint32_t ts;
encoder_input_func_t encoder_func;
raw_input_func_t raw_func;
packet_input_func_t packet_func;
struct codec_handler *handler;
struct codec_ssrc_handler *ch;
struct codec_ssrc_handler *input_ch;
int seq_adj;
};
struct silence_event {
uint64_t start;
uint64_t end;
};
TYPED_GQUEUE(silence_event, struct silence_event)
struct codec_ssrc_handler {
struct ssrc_entry h; // must be first
struct codec_handler *handler;
decoder_t *decoder;
encoder_t *encoder;
codec_chain_t *chain;
format_t encoder_format;
int bitrate;
int ptime;
int bytes_per_packet;
struct codec_scheduler csch;
GString *sample_buffer;
struct dtx_buffer *dtx_buffer;
// DTMF DSP stuff
dtmf_rx_state_t *dtmf_dsp;
resample_t dtmf_resampler;
format_t dtmf_format;
uint64_t dtmf_ts, last_dtmf_event_ts;
dtmf_event_q dtmf_events;
struct dtmf_event dtmf_event; // for replacing PCM with DTMF event
struct dtmf_event dtmf_state; // state tracker for DTMF actions
// silence detection
silence_event_q silence_events;
// DTMF audio suppression
unsigned long dtmf_start_ts;
// DTMF send delay
unsigned long dtmf_first_duration;
uint64_t skip_pts;
unsigned int rtp_mark:1;
};
struct transcode_packet {
seq_packet_t p; // must be first
unsigned long ts;
str *payload;
struct codec_handler *handler;
unsigned int marker:1,
bypass_seq:1;
int (*packet_func)(struct codec_ssrc_handler *, struct codec_ssrc_handler *, struct transcode_packet *,
struct media_packet *);
int (*dup_func)(struct codec_ssrc_handler *, struct codec_ssrc_handler *, struct transcode_packet *,
struct media_packet *);
struct rtp_header rtp;
};
struct codec_tracker {
GHashTable *touched; // 8000, 16000, etc, for each audio codec that was touched (added, removed, etc)
int all_touched;
};
struct rtcp_timer {
struct codec_timer ct;
call_t *call;
struct call_media *media;
};
static codec_handler_func handler_func_passthrough_ssrc;
static codec_handler_func handler_func_transcode;
static codec_handler_func handler_func_playback;
static codec_handler_func handler_func_inject_dtmf;
static codec_handler_func handler_func_dtmf;
static codec_handler_func handler_func_t38;
static struct ssrc_entry *__ssrc_handler_transcode_new(void *p);
static struct ssrc_entry *__ssrc_handler_decode_new(void *p);
static struct ssrc_entry *__ssrc_handler_new(void *p);
static void __ssrc_handler_stop(void *p, void *dummy);
static void __free_ssrc_handler(void *);
static void __transcode_packet_free(struct transcode_packet *);
static int packet_decode(struct codec_ssrc_handler *, struct codec_ssrc_handler *,
struct transcode_packet *, struct media_packet *);
static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2);
static int packet_decoded_fifo(decoder_t *decoder, AVFrame *frame, void *u1, void *u2);
static int packet_decoded_direct(decoder_t *decoder, AVFrame *frame, void *u1, void *u2);
static int packet_decoded_audio_player(decoder_t *decoder, AVFrame *frame, void *u1, void *u2);
static void codec_touched(struct codec_store *cs, rtp_payload_type *pt);
static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *ch,
struct codec_ssrc_handler *input_handler,
struct transcode_packet *packet, struct media_packet *mp,
int (*dtx_func)(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch,
struct transcode_packet *packet,
struct media_packet *mp));
static void __dtx_shutdown(struct dtx_buffer *dtxb);
static struct codec_handler *__input_handler(struct codec_handler *h, struct media_packet *mp);
static void __delay_frame_process(struct delay_buffer *, struct delay_frame *dframe);
static void __dtx_restart(struct codec_handler *h);
static void __delay_buffer_setup(struct delay_buffer **dbufp,
struct codec_handler *h, call_t *call, unsigned int delay);
static void __delay_buffer_shutdown(struct delay_buffer *dbuf, bool);
static void delay_buffer_stop(struct delay_buffer **pcmbp);
static int __buffer_delay_packet(struct delay_buffer *dbuf,
struct codec_ssrc_handler *ch,
struct codec_ssrc_handler *input_ch,
struct transcode_packet *packet,
unsigned long ts_delay,
int payload_type,
packet_input_func_t packet_func, struct media_packet *mp, unsigned int clockrate);
static void __buffer_delay_seq(struct delay_buffer *dbuf, struct media_packet *mp, int seq_adj);
static struct codec_handler codec_handler_stub_ssrc = {
.source_pt.payload_type = -1,
.dest_pt.payload_type = -1,
.handler_func = handler_func_passthrough_ssrc,
.kernelize = 1,
.passthrough = 1,
};
static void __handler_shutdown(struct codec_handler *handler) {
ssrc_hash_foreach(handler->ssrc_hash, __ssrc_handler_stop, (void *) true);
free_ssrc_hash(&handler->ssrc_hash);
if (handler->delay_buffer) {
__delay_buffer_shutdown(handler->delay_buffer, true);
delay_buffer_stop(&handler->delay_buffer);
}
if (handler->ssrc_handler)
obj_put(&handler->ssrc_handler->h);
handler->ssrc_handler = NULL;
handler->kernelize = 0;
handler->transcoder = 0;
handler->output_handler = handler; // reset to default
handler->packet_decoded = packet_decoded_fifo;
handler->dtmf_payload_type = -1;
handler->real_dtmf_payload_type = -1;
handler->cn_payload_type = -1;
handler->pcm_dtmf_detect = 0;
handler->passthrough = 0;
handler->payload_len = 0;
codec_handler_free(&handler->dtmf_injector);
if (handler->stats_entry) {
g_atomic_int_add(&handler->stats_entry->num_transcoders, -1);
handler->stats_entry = NULL;
g_free(handler->stats_chain);
}
}
static void __codec_handler_free(struct codec_handler *h) {
__handler_shutdown(h);
payload_type_clear(&h->source_pt);
payload_type_clear(&h->dest_pt);
g_slice_free1(sizeof(*h), h);
}
void codec_handler_free(struct codec_handler **handler) {
if (!handler || !*handler)
return;
__codec_handler_free(*handler);
*handler = NULL;
}
static struct codec_handler *__handler_new(const rtp_payload_type *pt, struct call_media *media,
struct call_media *sink)
{
struct codec_handler *handler = g_slice_alloc0(sizeof(*handler));
handler->source_pt.payload_type = -1;
if (pt)
rtp_payload_type_copy(&handler->source_pt, pt);
handler->dest_pt.payload_type = -1;
handler->output_handler = handler; // default
handler->dtmf_payload_type = -1;
handler->real_dtmf_payload_type = -1;
handler->cn_payload_type = -1;
handler->packet_encoded = packet_encoded_rtp;
handler->packet_decoded = packet_decoded_fifo;
handler->media = media;
handler->sink = sink;
return handler;
}
static void __make_passthrough(struct codec_handler *handler, int dtmf_pt, int cn_pt) {
__handler_shutdown(handler);
ilogs(codec, LOG_DEBUG, "Using passthrough handler for " STR_FORMAT "/"
STR_FORMAT " (%i) with DTMF %i, CN %i",
STR_FMT(&handler->source_pt.encoding_with_params),
STR_FMT0(&handler->source_pt.format_parameters),
handler->source_pt.payload_type,
dtmf_pt, cn_pt);
if (handler->source_pt.codec_def && handler->source_pt.codec_def->dtmf)
handler->handler_func = handler_func_dtmf;
else {
handler->handler_func = handler_func_passthrough;
handler->kernelize = 1;
}
rtp_payload_type_copy(&handler->dest_pt, &handler->source_pt);
handler->ssrc_hash = create_ssrc_hash_full(__ssrc_handler_new, handler);
handler->dtmf_payload_type = dtmf_pt;
handler->cn_payload_type = cn_pt;
handler->passthrough = 1;
#ifdef WITH_TRANSCODING
if (handler->media->buffer_delay) {
__delay_buffer_setup(&handler->delay_buffer, handler, handler->media->call,
handler->media->buffer_delay);
handler->kernelize = 0;
}
#endif
}
// converts existing passthrough handler to SSRC passthrough
static void __convert_passthrough_ssrc(struct codec_handler *handler) {
ilogs(codec, LOG_DEBUG, "Using passthrough handler with new SSRC for " STR_FORMAT "/" STR_FORMAT,
STR_FMT(&handler->source_pt.encoding_with_params),
STR_FMT0(&handler->source_pt.format_parameters));
if (handler->handler_func == handler_func_passthrough)
handler->handler_func = handler_func_passthrough_ssrc;
}
static void __reset_sequencer(void *p, void *dummy) {
struct ssrc_entry_call *s = p;
if (s->sequencers)
g_hash_table_destroy(s->sequencers);
s->sequencers = NULL;
}
static void __make_transcoder_full(struct codec_handler *handler, rtp_payload_type *dest,
GHashTable *output_transcoders, int dtmf_payload_type, bool pcm_dtmf_detect,
int cn_payload_type, int (*packet_decoded)(decoder_t *, AVFrame *, void *, void *),
struct ssrc_entry *(*ssrc_handler_new_func)(void *p))
{
assert(handler->source_pt.codec_def != NULL);
assert(dest->codec_def != NULL);
// don't reset handler if it already matches what we want
if (!handler->transcoder)
goto reset;
if (!rtp_payload_type_eq_exact(dest, &handler->dest_pt))
goto reset;
if (handler->handler_func != handler_func_transcode)
goto reset;
if (handler->packet_decoded != packet_decoded)
goto reset;
if (handler->cn_payload_type != cn_payload_type)
goto reset;
if (handler->dtmf_payload_type != dtmf_payload_type)
goto reset;
if ((pcm_dtmf_detect ? 1 : 0) != handler->pcm_dtmf_detect)
goto reset;
ilogs(codec, LOG_DEBUG, "Leaving transcode context for " STR_FORMAT "/" STR_FORMAT
" (%i) -> " STR_FORMAT "/" STR_FORMAT " (%i) intact",
STR_FMT(&handler->source_pt.encoding_with_params),
STR_FMT0(&handler->source_pt.format_parameters),
handler->source_pt.payload_type,
STR_FMT(&dest->encoding_with_params),
STR_FMT0(&dest->format_parameters),
dest->payload_type);
goto no_handler_reset;
reset:
__handler_shutdown(handler);
rtp_payload_type_copy(&handler->dest_pt, dest);
if (dest->codec_def->format_answer)
dest->codec_def->format_answer(&handler->dest_pt, &handler->source_pt);
handler->handler_func = handler_func_transcode;
handler->packet_decoded = packet_decoded;
handler->transcoder = 1;
handler->dtmf_payload_type = dtmf_payload_type;
handler->cn_payload_type = cn_payload_type;
handler->pcm_dtmf_detect = pcm_dtmf_detect ? 1 : 0;
// DTMF transcoder/scaler?
if (handler->source_pt.codec_def && handler->source_pt.codec_def->dtmf)
handler->handler_func = handler_func_dtmf;
ilogs(codec, LOG_DEBUG, "Created transcode context for " STR_FORMAT "/" STR_FORMAT " (%i) -> " STR_FORMAT
"/" STR_FORMAT " (%i) with DTMF output %i and CN output %i",
STR_FMT(&handler->source_pt.encoding_with_params),
STR_FMT0(&handler->source_pt.format_parameters),
handler->source_pt.payload_type,
STR_FMT(&dest->encoding_with_params),
STR_FMT0(&dest->format_parameters),
dest->payload_type,
dtmf_payload_type, cn_payload_type);
handler->ssrc_hash = create_ssrc_hash_full(ssrc_handler_new_func, handler);
// stats entry
handler->stats_chain = g_strdup_printf(STR_FORMAT " -> " STR_FORMAT,
STR_FMT(&handler->source_pt.encoding_with_params),
STR_FMT(&dest->encoding_with_params));
mutex_lock(&rtpe_codec_stats_lock);
struct codec_stats *stats_entry =
t_hash_table_lookup(rtpe_codec_stats, handler->stats_chain);
if (!stats_entry) {
stats_entry = g_slice_alloc0(sizeof(*stats_entry));
stats_entry->chain = strdup(handler->stats_chain);
t_hash_table_insert(rtpe_codec_stats, stats_entry->chain, stats_entry);
stats_entry->chain_brief = g_strdup_printf(STR_FORMAT "_" STR_FORMAT,
STR_FMT(&handler->source_pt.encoding_with_params),
STR_FMT(&dest->encoding_with_params));
}
handler->stats_entry = stats_entry;
mutex_unlock(&rtpe_codec_stats_lock);
g_atomic_int_inc(&stats_entry->num_transcoders);
ssrc_hash_foreach(handler->media->monologue->ssrc_hash, __reset_sequencer, NULL);
no_handler_reset:
__delay_buffer_setup(&handler->delay_buffer, handler, handler->media->call, handler->media->buffer_delay);
__dtx_restart(handler);
// check if we have multiple decoders transcoding to the same output PT
struct codec_handler *output_handler = NULL;
if (output_transcoders)
output_handler = g_hash_table_lookup(output_transcoders,
GINT_TO_POINTER(dest->payload_type));
if (output_handler) {
ilogs(codec, LOG_DEBUG, "Using existing encoder context");
handler->output_handler = output_handler;
}
else {
if (output_transcoders)
g_hash_table_insert(output_transcoders, GINT_TO_POINTER(dest->payload_type), handler);
handler->output_handler = handler; // make sure we don't have a stale pointer
}
}
static void __make_transcoder(struct codec_handler *handler, rtp_payload_type *dest,
GHashTable *output_transcoders, int dtmf_payload_type, bool pcm_dtmf_detect,
int cn_payload_type)
{
__make_transcoder_full(handler, dest, output_transcoders, dtmf_payload_type, pcm_dtmf_detect,
cn_payload_type, packet_decoded_fifo, __ssrc_handler_transcode_new);
}
static void __make_audio_player_decoder(struct codec_handler *handler, rtp_payload_type *dest,
bool pcm_dtmf_detect)
{
__make_transcoder_full(handler, dest, NULL, -1, pcm_dtmf_detect, -1, packet_decoded_audio_player,
__ssrc_handler_decode_new);
}
// used for generic playback (audio_player, t38_gateway)
struct codec_handler *codec_handler_make_playback(const rtp_payload_type *src_pt,
const rtp_payload_type *dst_pt, unsigned long last_ts, struct call_media *media,
uint32_t ssrc)
{
struct codec_handler *handler = __handler_new(src_pt, media, NULL);
rtp_payload_type_copy(&handler->dest_pt, dst_pt);
handler->handler_func = handler_func_playback;
handler->ssrc_handler = (void *) __ssrc_handler_transcode_new(handler);
if (!handler->ssrc_handler) {
codec_handler_free(&handler);
return NULL;
}
handler->ssrc_handler->csch.first_ts = last_ts;
handler->ssrc_handler->h.ssrc = ssrc;
while (handler->ssrc_handler->csch.first_ts == 0)
handler->ssrc_handler->csch.first_ts = ssl_random();
handler->ssrc_handler->rtp_mark = 1;
ilogs(codec, LOG_DEBUG, "Created media playback context for " STR_FORMAT "/" STR_FORMAT
" -> " STR_FORMAT "/" STR_FORMAT "",
STR_FMT(&src_pt->encoding_with_params),
STR_FMT0(&src_pt->format_parameters),
STR_FMT(&dst_pt->encoding_with_params),
STR_FMT0(&dst_pt->format_parameters));
return handler;
}
// used for "play media" player
struct codec_handler *codec_handler_make_media_player(const rtp_payload_type *src_pt,
const rtp_payload_type *dst_pt, unsigned long last_ts, struct call_media *media,
uint32_t ssrc)
{
struct codec_handler *h = codec_handler_make_playback(src_pt, dst_pt, last_ts, media, ssrc);
if (!h)
return NULL;
if (audio_player_is_active(media)) {
h->packet_decoded = packet_decoded_audio_player;
if (!audio_player_pt_match(media, dst_pt))
ilogs(codec, LOG_WARN, "Codec mismatch between audio player and media player (wanted: "
STR_FORMAT "/" STR_FORMAT ")",
STR_FMT(&dst_pt->encoding_with_params),
STR_FMT0(&dst_pt->format_parameters));
}
return h;
}
struct codec_handler *codec_handler_make_dummy(const rtp_payload_type *dst_pt, struct call_media *media)
{
struct codec_handler *handler = __handler_new(NULL, media, NULL);
rtp_payload_type_copy(&handler->dest_pt, dst_pt);
return handler;
}
// does not init/parse a=fmtp
static void ensure_codec_def_type(rtp_payload_type *pt, enum media_type type) {
if (pt->codec_def)
return;
pt->codec_def = codec_find(&pt->encoding, type);
if (!pt->codec_def)
return;
if (!pt->codec_def->support_encoding || !pt->codec_def->support_decoding)
pt->codec_def = NULL;
}
// does init/parse a=fmtp
void ensure_codec_def(rtp_payload_type *pt, struct call_media *media) {
if (!media)
return;
ensure_codec_def_type(pt, media->type_id);
if (pt->codec_def)
codec_parse_fmtp(pt->codec_def, &pt->format, &pt->format_parameters, NULL);
}
// only called from codec_handlers_update()
static void __make_passthrough_gsl(struct codec_handler *handler, GSList **handlers,
rtp_payload_type *dtmf_pt, rtp_payload_type *cn_pt,
bool use_ssrc_passthrough)
{
__make_passthrough(handler, dtmf_pt ? dtmf_pt->payload_type : -1,
cn_pt ? cn_pt->payload_type : -1);
if (use_ssrc_passthrough)
__convert_passthrough_ssrc(handler);
*handlers = g_slist_prepend(*handlers, handler);
}
static void __track_supp_codec(GHashTable *supplemental_sinks, rtp_payload_type *pt) {
if (!pt->codec_def || !pt->codec_def->supplemental)
return;
GHashTable *supp_sinks = g_hash_table_lookup(supplemental_sinks, pt->codec_def->rtpname);
if (!supp_sinks)
return;
if (!g_hash_table_lookup(supp_sinks, GUINT_TO_POINTER(pt->clock_rate)))
g_hash_table_insert(supp_sinks, GUINT_TO_POINTER(pt->clock_rate), pt);
}
static void __check_codec_list(GHashTable **supplemental_sinks, rtp_payload_type **pref_dest_codec,
struct call_media *sink, rtp_pt_q *sink_list)
{
// first initialise and populate the list of supp sinks
GHashTable *ss = *supplemental_sinks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
(GDestroyNotify) g_hash_table_destroy);
for (GList *l = codec_supplemental_codecs->head; l; l = l->next) {
codec_def_t *def = l->data;
g_hash_table_replace(ss, (void *) def->rtpname,
g_hash_table_new(g_direct_hash, g_direct_equal));
}
rtp_payload_type *pdc = NULL;
rtp_payload_type *first_tc_codec = NULL;
for (__auto_type l = sink->codecs.codec_prefs.head; l; l = l->next) {
rtp_payload_type *pt = l->data;
ensure_codec_def(pt, sink);
if (!pt->codec_def) // not supported, next
continue;
// fix up ptime
if (pt->ptime <= 0)
pt->ptime = pt->codec_def->default_ptime;
if (sink->ptime > 0)
pt->ptime = sink->ptime;
if (!pdc && !pt->codec_def->supplemental)
pdc = pt;
if (pt->accepted) {
// codec is explicitly marked as accepted
if (!first_tc_codec && !pt->codec_def->supplemental)
first_tc_codec = pt;
}
__track_supp_codec(ss, pt);
}
if (first_tc_codec)
pdc = first_tc_codec;
if (pdc && pref_dest_codec) {
*pref_dest_codec = pdc;
ilogs(codec, LOG_DEBUG, "Default sink codec is " STR_FORMAT "/" STR_FORMAT " (%i)",
STR_FMT(&(*pref_dest_codec)->encoding_with_params),
STR_FMT0(&(*pref_dest_codec)->format_parameters),
(*pref_dest_codec)->payload_type);
}
}
static rtp_payload_type *__supp_payload_type(GHashTable *supplemental_sinks, int clockrate,
const char *codec)
{
GHashTable *supp_sinks = g_hash_table_lookup(supplemental_sinks, codec);
if (!supp_sinks)
return NULL;
if (!g_hash_table_size(supp_sinks))
return NULL;
// find the codec entry with a matching clock rate
rtp_payload_type *pt = g_hash_table_lookup(supp_sinks,
GUINT_TO_POINTER(clockrate));
return pt;
}
static int __unused_pt_number(struct call_media *media, struct call_media *other_media,
struct codec_store *extra_cs,
rtp_payload_type *pt)
{
int num = pt ? pt->payload_type : -1;
rtp_payload_type *pt_match;
if (num < 0)
num = 96; // default first dynamic payload type number
while (1) {
if ((pt_match = t_hash_table_lookup(media->codecs.codecs, GINT_TO_POINTER(num))))
goto next;
if (other_media) {
if ((pt_match = t_hash_table_lookup(other_media->codecs.codecs,
GINT_TO_POINTER(num))))
goto next;
}
if (extra_cs) {
if ((pt_match = t_hash_table_lookup(extra_cs->codecs,
GINT_TO_POINTER(num))))
goto next;
}
// OK
break;
next:
// is this actually the same?
if (pt && rtp_payload_type_eq_nf(pt, pt_match))
break;
num++;
if (num < 96) // if an RFC type was taken already
num = 96;
else if (num >= 128)
return -1;
}
return num;
}
static void __check_dtmf_injector(struct call_media *receiver, struct call_media *sink,
struct codec_handler *parent,
GHashTable *output_transcoders)
{
if (!ML_ISSET(sink->monologue, INJECT_DTMF))
return;
if (parent->dtmf_payload_type != -1)
return;
if (parent->dtmf_injector)
return;
if (parent->source_pt.codec_def->supplemental)
return;
// synthesise input rtp payload type
rtp_payload_type src_pt = {
.payload_type = -1,
.clock_rate = parent->source_pt.clock_rate,
.channels = parent->source_pt.channels,
};
str_init(&src_pt.encoding, "DTMF injector");
str_init(&src_pt.encoding_with_params, "DTMF injector");
str_init(&src_pt.encoding_with_full_params, "DTMF injector");
static const str tp_event = STR_CONST_INIT("telephone-event");
src_pt.codec_def = codec_find(&tp_event, MT_AUDIO);
if (!src_pt.codec_def) {
ilogs(codec, LOG_ERR, "RTP payload type 'telephone-event' is not defined");
return;
}
parent->dtmf_injector = __handler_new(&src_pt, receiver, sink);
__make_transcoder(parent->dtmf_injector, &parent->dest_pt, output_transcoders, -1, 0, -1);
parent->dtmf_injector->handler_func = handler_func_inject_dtmf;
}
static struct codec_handler *__get_pt_handler(struct call_media *receiver, rtp_payload_type *pt,
struct call_media *sink)
{
ensure_codec_def(pt, receiver);
struct codec_handler *handler;
handler = codec_handler_lookup(receiver->codec_handlers, pt->payload_type, sink);
if (handler) {
// make sure existing handler matches this PT
if (!rtp_payload_type_eq_exact(pt, &handler->source_pt)) {
ilogs(codec, LOG_DEBUG, "Resetting codec handler for PT %i", pt->payload_type);
t_hash_table_remove(receiver->codec_handlers, handler);
__handler_shutdown(handler);
handler = NULL;
g_atomic_pointer_set(&receiver->codec_handler_cache, NULL);
}
}
if (!handler) {
ilogs(codec, LOG_DEBUG, "Creating codec handler for " STR_FORMAT "/" STR_FORMAT " (%i)",
STR_FMT(&pt->encoding_with_params),
STR_FMT0(&pt->format_parameters),
pt->payload_type);
handler = __handler_new(pt, receiver, sink);
t_hash_table_insert(receiver->codec_handlers, handler, handler);
t_queue_push_tail(&receiver->codec_handlers_store, handler);
}
// figure out our ptime
if (pt->ptime <= 0 && pt->codec_def)
pt->ptime = pt->codec_def->default_ptime;
if (receiver->ptime > 0)
pt->ptime = receiver->ptime;
return handler;
}
static void __check_t38_decoder(struct call_media *t38_media) {
if (t38_media->t38_handler)
return;
ilogs(codec, LOG_DEBUG, "Creating T.38 packet handler");
t38_media->t38_handler = __handler_new(NULL, t38_media, NULL);
t38_media->t38_handler->handler_func = handler_func_t38;
}
static int packet_encoded_t38(encoder_t *enc, void *u1, void *u2) {
struct media_packet *mp = u2;
if (!mp->media)
return 0;
return t38_gateway_input_samples(mp->media->t38_gateway,
(int16_t *) enc->avpkt->data, enc->avpkt->size / 2);
}
static void __generator_stop(struct call_media *media) {
if (media->t38_gateway) {
t38_gateway_stop(media->t38_gateway);
t38_gateway_put(&media->t38_gateway);
}
}
static void __generator_stop_all(struct call_media *media) {
__generator_stop(media);
audio_player_stop(media);
}
static void __t38_options_from_flags(struct t38_options *t_opts, const sdp_ng_flags *flags) {
#define t38_opt(name) t_opts->name = flags ? flags->t38_ ## name : 0
t38_opt(no_ecm);
t38_opt(no_v17);
t38_opt(no_v27ter);
t38_opt(no_v29);
t38_opt(no_v34);
t38_opt(no_iaf);
}
static void __check_t38_gateway(struct call_media *pcm_media, struct call_media *t38_media,
const struct stream_params *sp, const sdp_ng_flags *flags)
{
struct t38_options t_opts = {0,};
if (sp)
t_opts = sp->t38_options;
else {
// create our own options
if (flags && flags->t38_fec)
t_opts.fec_span = 3;
t_opts.max_ec_entries = 3;
}
__t38_options_from_flags(&t_opts, flags);
MEDIA_SET(pcm_media, GENERATOR);
MEDIA_SET(t38_media, GENERATOR);
if (t38_gateway_pair(t38_media, pcm_media, &t_opts))
return;
// need a packet handler on the T.38 side
__check_t38_decoder(t38_media);
// for each codec type supported by the pcm_media, we create a codec handler that
// links to the T.38 encoder
for (__auto_type l = pcm_media->codecs.codec_prefs.head; l; l = l->next) {
rtp_payload_type *pt = l->data;
struct codec_handler *handler = __get_pt_handler(pcm_media, pt, t38_media);
if (!pt->codec_def) {
// should not happen
ilogs(codec, LOG_WARN, "Unsupported codec " STR_FORMAT "/" STR_FORMAT
" for T.38 transcoding",
STR_FMT(&pt->encoding_with_params),
STR_FMT0(&pt->format_parameters));
continue;
}
ilogs(codec, LOG_DEBUG, "Creating T.38 encoder for " STR_FORMAT "/" STR_FORMAT,
STR_FMT(&pt->encoding_with_params),
STR_FMT0(&pt->format_parameters));
__make_transcoder(handler, &pcm_media->t38_gateway->pcm_pt, NULL, -1, false, -1);
handler->packet_decoded = packet_decoded_direct;
handler->packet_encoded = packet_encoded_t38;
}
}
// call must be locked in W
static int codec_handler_udptl_update(struct call_media *receiver, struct call_media *sink,
const sdp_ng_flags *flags)
{
// anything to do?
if (proto_is(sink->protocol, PROTO_UDPTL))
return 0;
if (sink->type_id == MT_AUDIO && proto_is_rtp(sink->protocol) && receiver->type_id == MT_IMAGE) {
if (!str_cmp(&receiver->format_str, "t38")) {
__check_t38_gateway(sink, receiver, NULL, flags);
return 1;
}
}
ilogs(codec, LOG_WARN, "Unsupported non-RTP protocol: " STR_FORMAT "/" STR_FORMAT
" -> " STR_FORMAT "/" STR_FORMAT,
STR_FMT(&receiver->type), STR_FMT(&receiver->format_str),
STR_FMT(&sink->type), STR_FMT(&sink->format_str));
return 0;
}
// call must be locked in W
// for transcoding RTP types to non-RTP
static int codec_handler_non_rtp_update(struct call_media *receiver, struct call_media *sink,
const sdp_ng_flags *flags, const struct stream_params *sp)
{
if (proto_is(sink->protocol, PROTO_UDPTL) && !str_cmp(&sink->format_str, "t38")) {
__check_t38_gateway(receiver, sink, sp, flags);
return 1;
}
ilogs(codec, LOG_WARN, "Unsupported non-RTP protocol: " STR_FORMAT "/" STR_FORMAT
" -> " STR_FORMAT "/" STR_FORMAT,
STR_FMT(&receiver->type), STR_FMT(&receiver->format_str),
STR_FMT(&sink->type), STR_FMT(&sink->format_str));
return 0;
}
static void __rtcp_timer_free(void *p) {
struct rtcp_timer *rt = p;
if (rt->call)
obj_put(rt->call);
}
static void __rtcp_timer_run(struct codec_timer *);
// master lock held in W
static void __codec_rtcp_timer_schedule(struct call_media *media) {
struct rtcp_timer *rt = media->rtcp_timer;
if (!rt) {
media->rtcp_timer = rt = obj_alloc0("rtcp_timer", sizeof(*rt), __rtcp_timer_free);
rt->ct.tt_obj.tt = &codec_timers_thread;
rt->call = obj_get(media->call);
rt->media = media;
rt->ct.next = rtpe_now;
rt->ct.timer_func = __rtcp_timer_run;
}
timeval_add_usec(&rt->ct.next, rtpe_config.rtcp_interval * 1000 + (ssl_random() % 1000000));
timerthread_obj_schedule_abs(&rt->ct.tt_obj, &rt->ct.next);
}
// no lock held
static void __rtcp_timer_run(struct codec_timer *ct) {
struct rtcp_timer *rt = (void *) ct;
// check scheduling
rwlock_lock_w(&rt->call->master_lock);
struct call_media *media = rt->media;
log_info_media(media);
if (media->rtcp_timer != rt || !proto_is_rtp(media->protocol) || !MEDIA_ISSET(media, RTCP_GEN)) {
if (media->rtcp_timer == rt)
rtcp_timer_stop(&media->rtcp_timer);
rwlock_unlock_w(&rt->call->master_lock);
goto out;
}
timeval_add_usec(&ct->next, rtpe_config.rtcp_interval * 1000 + (ssl_random() % 1000000));
__codec_rtcp_timer_schedule(media);
// switch locks to be more graceful
rwlock_unlock_w(&rt->call->master_lock);
rwlock_lock_r(&rt->call->master_lock);
// copy out references to SSRCs for lock-free handling
struct ssrc_ctx *ssrc_out[RTPE_NUM_SSRC_TRACKING] = {NULL,};
if (media->streams.head) {
struct packet_stream *ps = media->streams.head->data;
mutex_lock(&ps->out_lock);
for (unsigned int u = 0; u < RTPE_NUM_SSRC_TRACKING; u++) {
if (!ps->ssrc_out[u]) // end of list
break;
ssrc_out[u] = ps->ssrc_out[u];
ssrc_ctx_hold(ssrc_out[u]);
}
mutex_unlock(&ps->out_lock);
}
for (unsigned int u = 0; u < RTPE_NUM_SSRC_TRACKING; u++) {
if (!ssrc_out[u]) // end of list
break;
// coverity[use : FALSE]
rtcp_send_report(media, ssrc_out[u]);
}
rwlock_unlock_r(&rt->call->master_lock);
for (unsigned int u = 0; u < RTPE_NUM_SSRC_TRACKING; u++) {