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
|
/*
Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <iostream>
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <sys/types.h>
#include <sys/time.h>
#include "audio_rx.h"
#include "log.h"
#include "phone.h"
#include "rtp_telephone_event.h"
#include "userintf.h"
#include "line.h"
#include "sys_settings.h"
#include "sequence_number.h"
#include "audits/memman.h"
extern t_phone *phone;
#define SAMPLE_BUF_SIZE (audio_encoder->get_ptime() * audio_encoder->get_sample_rate()/1000 *\
AUDIO_SAMPLE_SIZE/8)
// Debug macro to print timestamp
#define DEBUG_TS(s) { gettimeofday(&debug_timer, NULL);\
cout << "DEBUG: ";\
cout << debug_timer.tv_sec * 1000 +\
debug_timer.tv_usec / 1000;\
cout << " " << (s) << endl;\
}
//////////
// PRIVATE
//////////
bool t_audio_rx::get_sound_samples(unsigned short &sound_payload_size, bool &silence) {
int status;
struct timespec sleeptimer;
//struct timeval debug_timer;
silence = false;
mtx_3way.lock();
if (is_3way && !is_main_rx_3way) {
// We are not the main receiver in a 3-way call, so
// get the sound samples from the local media buffer.
// This buffer will be filled by the main receiver.
if (!media_3way_peer_rx->get(input_sample_buf, SAMPLE_BUF_SIZE)) {
// The mutex is unlocked before going to sleep.
// First I had the mutex unlock after the sleep.
// That worked fine with LinuxThreading, but it does
// not work with NPTL. It causes a deadlock when
// the main receiver calls post_media_peer_rx_3way
// as NPTL does not fair scheduling. This thread
// simly gets the lock again and the main receiver
// dies from starvation.
mtx_3way.unlock();
// There is not enough data yet. Sleep for 1 ms.
sleeptimer.tv_sec = 0;
sleeptimer.tv_nsec = 1000000;
nanosleep(&sleeptimer, NULL);
return false;
}
mtx_3way.unlock();
} else {
// Don't keep the 3way mutex locked while waiting for the DSP.
mtx_3way.unlock();
// Get the sound samples from the DSP
status = input_device->read(input_sample_buf, SAMPLE_BUF_SIZE);
if (status != SAMPLE_BUF_SIZE) {
if (!logged_capture_failure) {
// Log this failure only once
log_file->write_header("t_audio_rx::get_sound_samples",
LOG_NORMAL, LOG_WARNING);
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": sound capture failed.\n");
log_file->write_raw("Status: ");
log_file->write_raw(status);
log_file->write_endl();
log_file->write_footer();
logged_capture_failure = true;
}
stop_running = true;
return false;
}
// If line is muted, then fill sample buffer with silence.
// Note that we keep reading the dsp, to prevent the DSP buffers
// from filling up.
if (get_line()->get_is_muted()) {
memset(input_sample_buf, 0, SAMPLE_BUF_SIZE);
}
}
// Convert buffer to a buffer of shorts as the samples are 16 bits
short *sb = (short *)input_sample_buf;
mtx_3way.lock();
if (is_3way) {
// Send the sound samples to the other receiver if we
// are the main receiver.
// There may be no other receiver when one of the far-ends
// has put the call on-hold.
if (is_main_rx_3way && peer_rx_3way) {
peer_rx_3way->post_media_peer_rx_3way(input_sample_buf, SAMPLE_BUF_SIZE,
audio_encoder->get_sample_rate());
}
// Mix the sound samples with the 3rd party
if (media_3way_peer_tx->get(mix_buf_3way, SAMPLE_BUF_SIZE)) {
short *mix_sb = (short *)mix_buf_3way;
for (int i = 0; i < SAMPLE_BUF_SIZE / 2; i++) {
sb[i] = mix_linear_pcm(sb[i], mix_sb[i]);
}
}
}
mtx_3way.unlock();
/*** PREPROCESSING & ENCODING ***/
bool preprocessing_silence = false;
#ifdef HAVE_SPEEX
// speex acoustic echo cancellation
if (audio_session->get_do_echo_cancellation() && !audio_session->get_echo_captured_last()) {
spx_int16_t *input_buf = new spx_int16_t[SAMPLE_BUF_SIZE/2];
MEMMAN_NEW_ARRAY(input_buf);
for (int i = 0; i < SAMPLE_BUF_SIZE / 2; i++) {
input_buf[i] = sb[i];
}
speex_echo_capture(audio_session->get_speex_echo_state(), input_buf, sb);
audio_session->set_echo_captured_last(true);
MEMMAN_DELETE_ARRAY(input_buf);
delete [] input_buf;
}
// preprocessing
preprocessing_silence = !speex_preprocess_run(speex_preprocess_state, sb);
// According to the speex API documentation the return value
// from speex_preprocess_run() is only defined when VAD is
// enabled. So to be safe, reset the return value, if VAD is
// disabled.
if (!speex_dsp_vad) preprocessing_silence = false;
#endif
// encoding
sound_payload_size = audio_encoder->encode(sb, nsamples, payload, payload_size, silence);
// recognizing silence (both from preprocessing and encoding)
silence = silence || preprocessing_silence;
return true;
}
bool t_audio_rx::get_dtmf_event(void) {
// DTMF events are not supported in a 3-way conference
if (is_3way) return false;
if (!sema_dtmf_q.try_down()) {
// No DTMF event available
return false;
}
// Get next DTMF event
mtx_dtmf_q.lock();
t_dtmf_event dtmf_event = dtmf_queue.front();
dtmf_queue.pop();
mtx_dtmf_q.unlock();
ui->cb_async_send_dtmf(get_line()->get_line_number(), dtmf_event.dtmf_tone);
// Create DTMF player
if (dtmf_event.inband) {
dtmf_player = new t_inband_dtmf_player(this, audio_encoder, user_config,
dtmf_event.dtmf_tone, timestamp, nsamples);
MEMMAN_NEW(dtmf_player);
// Log DTMF event
log_file->write_header("t_audio_rx::get_dtmf_event", LOG_NORMAL);
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": start inband DTMF tone - ");
log_file->write_raw(dtmf_event.dtmf_tone);
log_file->write_endl();
log_file->write_footer();
} else {
// The telephone events may have a different sampling rate than
// the audio codec. Change nsamples accordingly.
nsamples = audio_sample_rate(CODEC_TELEPHONE_EVENT)/1000 *
audio_encoder->get_ptime();
dtmf_player = new t_rtp_event_dtmf_player(this, audio_encoder, user_config,
dtmf_event.dtmf_tone, timestamp, nsamples);
MEMMAN_NEW(dtmf_player);
// Log DTMF event
log_file->write_header("t_audio_rx::get_dtmf_event", LOG_NORMAL);
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": start DTMF event - ");
log_file->write_raw(dtmf_event.dtmf_tone);
log_file->write_endl();
log_file->write_raw("Payload type: ");
log_file->write_raw(pt_telephone_event);
log_file->write_endl();
log_file->write_footer();
// Set RTP payload format
// HACK: the sample rate for telephone events is 8000, but the
// ccRTP stack does not handle it well when the sample rate
// changes. When the sample rate of the audio codec is kept
// on the ccRTP session settings, then all works fine.
rtp_session->setPayloadFormat(DynamicPayloadFormat(pt_telephone_event,
audio_encoder->get_sample_rate()));
// should be this: audio_sample_rate(CODEC_TELEPHONE_EVENT)
// As all RTP event contain the same timestamp, the ccRTP stack will
// discard packets when the timestamp gets to old.
// Increase the expire timeout value to prevent this.
rtp_session->setExpireTimeout((JITTER_BUF_MS +
user_config->get_dtmf_duration() + user_config->get_dtmf_pause()) * 1000);
}
return true;
}
void t_audio_rx::set_sound_payload_format(void) {
nsamples = audio_encoder->get_sample_rate()/1000 * audio_encoder->get_ptime();
rtp_session->setPayloadFormat(DynamicPayloadFormat(audio_encoder->get_payload_id(),
audio_encoder->get_sample_rate()));
}
//////////
// PUBLIC
//////////
t_audio_rx::t_audio_rx(t_audio_session *_audio_session,
t_audio_io *_input_device, t_twinkle_rtp_session *_rtp_session,
t_audio_codec _codec, unsigned short _payload_id,
unsigned short _ptime) : sema_dtmf_q(0)
{
audio_session = _audio_session;
user_config = audio_session->get_line()->get_user();
assert(user_config);
input_device = _input_device;
rtp_session = _rtp_session;
dtmf_player = NULL;
is_running = false;
stop_running = false;
logged_capture_failure = false;
use_nat_keepalive = phone->use_nat_keepalive(user_config);
pt_telephone_event = -1;
// Create audio encoder
switch (_codec) {
case CODEC_G711_ALAW:
audio_encoder = new t_g711a_audio_encoder(_payload_id, _ptime, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_G711_ULAW:
audio_encoder = new t_g711u_audio_encoder(_payload_id, _ptime, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_GSM:
audio_encoder = new t_gsm_audio_encoder(_payload_id, _ptime, user_config);
MEMMAN_NEW(audio_encoder);
break;
#ifdef HAVE_SPEEX
case CODEC_SPEEX_NB:
audio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,
t_speex_audio_encoder::MODE_NB, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_SPEEX_WB:
audio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,
t_speex_audio_encoder::MODE_WB, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_SPEEX_UWB:
audio_encoder = new t_speex_audio_encoder(_payload_id, _ptime,
t_speex_audio_encoder::MODE_UWB, user_config);
MEMMAN_NEW(audio_encoder);
break;
#endif
#ifdef HAVE_ILBC
case CODEC_ILBC:
audio_encoder = new t_ilbc_audio_encoder(_payload_id, _ptime, user_config);
MEMMAN_NEW(audio_encoder);
break;
#endif
case CODEC_G726_16:
audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
t_g726_audio_encoder::BIT_RATE_16, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_G726_24:
audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
t_g726_audio_encoder::BIT_RATE_24, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_G726_32:
audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
t_g726_audio_encoder::BIT_RATE_32, user_config);
MEMMAN_NEW(audio_encoder);
break;
case CODEC_G726_40:
audio_encoder = new t_g726_audio_encoder(_payload_id, _ptime,
t_g726_audio_encoder::BIT_RATE_40, user_config);
MEMMAN_NEW(audio_encoder);
break;
#ifdef HAVE_BCG729
case CODEC_G729A:
audio_encoder = new t_g729a_audio_encoder(_payload_id, _ptime,
user_config);
MEMMAN_NEW(audio_encoder);
break;
#endif
default:
assert(false);
}
payload_size = audio_encoder->get_max_payload_size();
input_sample_buf = new unsigned char[SAMPLE_BUF_SIZE];
MEMMAN_NEW_ARRAY(input_sample_buf);
payload = new unsigned char[payload_size];
MEMMAN_NEW_ARRAY(payload);
nsamples = audio_encoder->get_sample_rate()/1000 * audio_encoder->get_ptime();
// Initialize 3-way settings to 'null'
media_3way_peer_tx = NULL;
media_3way_peer_rx = NULL;
peer_rx_3way = NULL;
mix_buf_3way = NULL;
is_3way = false;
is_main_rx_3way = false;
#ifdef HAVE_SPEEX
// initializing speex preprocessing state
speex_preprocess_state = speex_preprocess_state_init(nsamples, audio_encoder->get_sample_rate());
int arg;
float farg;
// Noise reduction
arg = (user_config->get_speex_dsp_nrd() ? 1 : 0);
speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_DENOISE, &arg);
arg = -30;
speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &arg);
// Automatic gain control
arg = (user_config->get_speex_dsp_agc() ? 1 : 0);
speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_AGC, &arg);
farg = (float) (user_config->get_speex_dsp_agc_level()) * 327.68f;
speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &farg);
arg = 30;
speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &arg);
// Voice activity detection
arg = (user_config->get_speex_dsp_vad() ? 1 : 0);
speex_dsp_vad = (bool)arg;
speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_VAD, &arg);
// Acoustic echo cancellation
if (audio_session->get_do_echo_cancellation()) {
speex_preprocess_ctl(speex_preprocess_state, SPEEX_PREPROCESS_SET_ECHO_STATE,
audio_session->get_speex_echo_state());
}
#endif
}
t_audio_rx::~t_audio_rx() {
struct timespec sleeptimer;
if (is_running) {
stop_running = true;
do {
sleeptimer.tv_sec = 0;
sleeptimer.tv_nsec = 10000000;
nanosleep(&sleeptimer, NULL);
} while (is_running);
}
#ifdef HAVE_SPEEX
// cleaning speex preprocessing
if (audio_session->get_do_echo_cancellation()) {
speex_echo_state_reset(audio_session->get_speex_echo_state());
}
speex_preprocess_state_destroy(speex_preprocess_state);
#endif
MEMMAN_DELETE_ARRAY(input_sample_buf);
delete [] input_sample_buf;
MEMMAN_DELETE_ARRAY(payload);
delete [] payload;
MEMMAN_DELETE(audio_encoder);
delete audio_encoder;
// Clean up resources for 3-way conference calls
if (media_3way_peer_tx) {
MEMMAN_DELETE(media_3way_peer_tx);
delete media_3way_peer_tx;
}
if (media_3way_peer_rx) {
MEMMAN_DELETE(media_3way_peer_rx);
delete media_3way_peer_rx;
}
if (mix_buf_3way) {
MEMMAN_DELETE_ARRAY(mix_buf_3way);
delete [] mix_buf_3way;
}
if (dtmf_player) {
MEMMAN_DELETE(dtmf_player);
delete dtmf_player;
}
}
void t_audio_rx::set_running(bool running) {
is_running = running;
}
// NOTE: no operations on the phone object are allowed inside the run() method.
// Such an operation needs a lock on the transaction layer. The destructor
// on audio_rx is called while this lock is locked. The destructor waits
// in a busy loop for the run() method to finish. If the run() method would
// need the phone lock, this would lead to a dead lock (and a long trip
// in debug hell!)
void t_audio_rx::run(void) {
//struct timeval debug_timer;
unsigned short sound_payload_size;
uint32 dtmf_rtp_timestamp;
phone->add_prohibited_thread();
ui->add_prohibited_thread();
// This flag indicates if we are currently in a silence period.
// The start of a new stream is assumed to start in silence, such
// that the very first RTP packet will be marked.
bool silence_period = true;
uint64 silence_nsamples = 0; // duration in samples
// This flag indicates if a sound frame can be suppressed
bool suppress_samples = false;
// The running flag is set already in t_audio_session::run to prevent
// a crash when the thread gets destroyed before it starts running.
// is_running = true;
// For a 3-way conference only the main receiver has access
// to the dsp.
if (!is_3way || is_main_rx_3way) {
// Enable recording
if (sys_config->equal_audio_dev(sys_config->get_dev_speaker(),
sys_config->get_dev_mic()))
{
input_device->enable(true, true);
} else {
input_device->enable(false, true);
}
// If the stream is stopped for call-hold, then the buffer might
// be filled with old sound samples.
input_device->flush(false, true);
}
// Synchronize the timestamp driven by the sampling rate
// of the recording with the timestamp of the RTP session.
// As the RTP session is already created in advance, the
// RTP clock is a bit ahead already.
timestamp = rtp_session->getCurrentTimestamp() + nsamples;
// This loop keeps running until the stop_running flag is set to true.
// When a call is being released the stop_running flag is set to true.
// At that moment the lock on the transaction layer (phone) is taken.
// So do not use operations that take the phone lock, otherwise a
// dead lock may occur during call release.
while (true) {
if (stop_running) break;
if (dtmf_player) {
rtp_session->setMark(false);
// Skip samples from sound card
input_device->read(input_sample_buf, SAMPLE_BUF_SIZE);
sound_payload_size = dtmf_player->get_payload(
payload, payload_size, timestamp, dtmf_rtp_timestamp);
silence_period = false;
} else if (get_dtmf_event()) {
// RFC 2833
// Set marker in first RTP packet of a DTMF event
rtp_session->setMark(true);
// Skip samples from sound card
input_device->read(input_sample_buf, SAMPLE_BUF_SIZE);
assert(dtmf_player);
sound_payload_size = dtmf_player->get_payload(
payload, payload_size, timestamp, dtmf_rtp_timestamp);
silence_period = false;
} else if (get_sound_samples(sound_payload_size, suppress_samples)) {
if (suppress_samples && use_nat_keepalive) {
if (!silence_period) silence_nsamples = 0;
// Send a silence packet at the NAT keep alive interval
// to keep the NAT bindings for RTP fresh.
silence_nsamples += SAMPLE_BUF_SIZE / 2;
if (silence_nsamples >
(uint64_t)user_config->get_timer_nat_keepalive() * 1000 *
audio_encoder->get_sample_rate())
{
suppress_samples = false;
}
}
if (silence_period && !suppress_samples) {
// RFC 3551 4.1
// Set marker bit in first RTP packet after silence
rtp_session->setMark(true);
} else {
rtp_session->setMark(false);
}
silence_period = suppress_samples;
} else {
continue;
}
// If timestamp is more than 1 payload size ahead of the clock of
// the ccRTP stack, then drop the current payload and do not advance
// the timestamp. This will happen if the DSP delivers more
// sound samples than the set sample rate. To compensate for this
// samples must be dropped.
uint32 current_timestamp = rtp_session->getCurrentTimestamp();
if (seq32_t(timestamp) <= seq32_t(current_timestamp + nsamples)) {
if (dtmf_player) {
// Send DTMF payload
rtp_session->putData(dtmf_rtp_timestamp, payload,
sound_payload_size);
// If DTMF has ended then set payload back to sound
if (dtmf_player->finished()) {
set_sound_payload_format();
MEMMAN_DELETE(dtmf_player);
delete dtmf_player;
dtmf_player = NULL;
}
} else if (!suppress_samples) {
// Send sound samples
// Set the expire timeout to the jitter buffer size.
// This allows for old packets still to be sent out.
rtp_session->setExpireTimeout(MAX_OUT_AUDIO_DELAY_MS * 1000);
rtp_session->putData(timestamp, payload, sound_payload_size);
}
timestamp += nsamples;
} else {
log_file->write_header("t_audio_rx::run", LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": discarded surplus of sound samples.\n");
log_file->write_raw("Timestamp: ");
log_file->write_raw(timestamp);
log_file->write_endl();
log_file->write_raw("Current timestamp: ");
log_file->write_raw(current_timestamp);
log_file->write_endl();
log_file->write_raw("nsamples: ");
log_file->write_raw(nsamples);
log_file->write_endl();
log_file->write_footer();
}
// If there is enough data in the DSP buffers to fill another
// RTP packet then do not sleep, but immediately go to the
// next cycle to play out the data. Probably this thread did
// not get enough time, so the buffer filled up. The far end
// jitter buffer has to cope with the jitter caused by this.
if (is_3way && !is_main_rx_3way) {
if (media_3way_peer_rx->size_content() >= SAMPLE_BUF_SIZE) {
continue;
}
} else {
if (input_device->get_buffer_space(true) >= SAMPLE_BUF_SIZE) continue;
}
// There is no data left in the DSP buffers to play out anymore.
// So the timestamp must be in sync with the clock of the ccRTP
// stack. It might get behind if the sound cards samples a bit
// slower than the set sample rate. Advance the timestamp to get
// in sync again.
current_timestamp = rtp_session->getCurrentTimestamp();
if (seq32_t(timestamp) <= seq32_t(current_timestamp -
(JITTER_BUF_MS / audio_encoder->get_ptime()) * nsamples))
{
timestamp += nsamples * (JITTER_BUF_MS / audio_encoder->get_ptime());
log_file->write_header("t_audio_rx::run", LOG_NORMAL, LOG_DEBUG);
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": timestamp forwarded by ");
log_file->write_raw(nsamples * (JITTER_BUF_MS /
audio_encoder->get_ptime()));
log_file->write_endl();
log_file->write_raw("Timestamp: ");
log_file->write_raw(timestamp);
log_file->write_endl();
log_file->write_raw("Current timestamp: ");
log_file->write_raw(current_timestamp);
log_file->write_endl();
log_file->write_raw("nsamples: ");
log_file->write_raw(nsamples);
log_file->write_endl();
log_file->write_footer();
}
}
phone->remove_prohibited_thread();
ui->remove_prohibited_thread();
is_running = false;
}
void t_audio_rx::set_pt_telephone_event(int pt) {
pt_telephone_event = pt;
}
void t_audio_rx::push_dtmf(char digit, bool inband) {
// Ignore invalid DTMF digits
if (!is_valid_dtmf_sym(digit)) return;
// Ignore DTMF tones in a 3-way conference
if (is_3way) return;
t_dtmf_event dtmf_event;
dtmf_event.dtmf_tone = char2dtmf_ev(digit);
dtmf_event.inband = inband;
mtx_dtmf_q.lock();
dtmf_queue.push(dtmf_event);
mtx_dtmf_q.unlock();
sema_dtmf_q.up();
}
t_line *t_audio_rx::get_line(void) const {
return audio_session->get_line();
}
void t_audio_rx::join_3way(bool main_rx, t_audio_rx *peer_rx) {
mtx_3way.lock();
if (is_3way) {
log_file->write_header("t_audio_rx::join_3way", LOG_NORMAL);
log_file->write_raw("ERROR: audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(" - 3way is already active.\n");
log_file->write_footer();
mtx_3way.unlock();
return;
}
// Logging
log_file->write_header("t_audio_rx::join_3way");
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": join 3-way.\n");
if (main_rx) {
log_file->write_raw("Role is: mixer.\n");
} else {
log_file->write_raw("Role is: non-mixing.\n");
}
if (peer_rx) {
log_file->write_raw("A peer receiver already exists.\n");
} else {
log_file->write_raw("A peer receiver does not exist.\n");
}
log_file->write_footer();
// Create media buffers for the 2 far-ends of a 3-way call.
// The size of the media buffer is the size of the jitter buffer.
// This allows for jitter in the RTP streams and also for
// incompatible payload sizes. Eg. 1 far-end may send 20ms paylaods,
// while the other sends 30ms payloads. The outgoing RTP stream might
// even have another payload size.
// When the data has been captured from the soundcard, it will be
// checked if there is enough data available in the media buffers, i.e.
// the same amount of data as captured from the soundcard for mixing.
// If there is it will be retrieved and mixed.
// If there isn't the captured sound will simply be sent on its own
// to the far-end. Meanwhile the buffer will fill up with data such
// that from the next captured sample there will be sufficient data
// for mixing.
media_3way_peer_tx = new t_media_buffer(
JITTER_BUF_SIZE(audio_encoder->get_sample_rate()));
MEMMAN_NEW(media_3way_peer_tx);
media_3way_peer_rx = new t_media_buffer(
JITTER_BUF_SIZE(audio_encoder->get_sample_rate()));
MEMMAN_NEW(media_3way_peer_rx);
// Create a mix buffer for one sample frame.
mix_buf_3way = new unsigned char[SAMPLE_BUF_SIZE];
MEMMAN_NEW_ARRAY(mix_buf_3way);
peer_rx_3way = peer_rx;
is_3way = true;
is_main_rx_3way = main_rx;
// Stop DTMF tones as these are not supported in a 3way
if (dtmf_player) {
MEMMAN_DELETE(dtmf_player);
delete dtmf_player;
dtmf_player = NULL;
}
mtx_3way.unlock();
}
void t_audio_rx::set_peer_rx_3way(t_audio_rx *peer_rx) {
mtx_3way.lock();
if (!is_3way) {
mtx_3way.unlock();
return;
}
// Logging
log_file->write_header("t_audio_rx::set_peer_rx_3way");
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
if (peer_rx) {
log_file->write_raw(": set peer receiver.\n");
} else {
log_file->write_raw(": erase peer receiver.\n");
}
if (is_main_rx_3way) {
log_file->write_raw("Role is: mixer.\n");
} else {
log_file->write_raw("Role is: non-mixing.\n");
}
log_file->write_footer();
peer_rx_3way = peer_rx;
mtx_3way.unlock();
}
void t_audio_rx::set_main_rx_3way(bool main_rx) {
mtx_3way.lock();
if (!is_3way) {
mtx_3way.unlock();
return;
}
// Logging
log_file->write_header("t_audio_rx::set_main_rx_3way");
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
if (main_rx) {
log_file->write_raw(": change role to: mixer.\n");
} else {
log_file->write_raw(": change role to: non-mixing.\n");
}
log_file->write_footer();
// Initialize the DSP if we become the mixer and we were not before
if (main_rx && !is_main_rx_3way) {
// Enable recording
if (sys_config->equal_audio_dev(sys_config->get_dev_speaker(),
sys_config->get_dev_mic()))
{
input_device->enable(true, true);
} else {
input_device->enable(false, true);
}
// If the stream is stopped for call-hold, then the buffer might
// be filled with old sound samples.
input_device->flush(false, true);
}
is_main_rx_3way = main_rx;
mtx_3way.unlock();
}
void t_audio_rx::stop_3way(void) {
mtx_3way.lock();
if (!is_3way) {
log_file->write_header("t_audio_rx::stop_3way");
log_file->write_raw("ERROR: audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(" - 3way is not active.\n");
log_file->write_footer();
mtx_3way.unlock();
return;
}
// Logging
log_file->write_header("t_audio_rx::stop_3way");
log_file->write_raw("Audio rx line ");
log_file->write_raw(get_line()->get_line_number()+1);
log_file->write_raw(": stop 3-way.\n");
log_file->write_footer();
is_3way = false;
is_main_rx_3way = false;
peer_rx_3way = NULL;
MEMMAN_DELETE(media_3way_peer_tx);
delete media_3way_peer_tx;
media_3way_peer_tx = NULL;
MEMMAN_DELETE(media_3way_peer_rx);
delete media_3way_peer_rx;
media_3way_peer_rx = NULL;
MEMMAN_DELETE_ARRAY(mix_buf_3way);
delete [] mix_buf_3way;
mix_buf_3way = NULL;
mtx_3way.unlock();
}
void t_audio_rx::post_media_peer_tx_3way(unsigned char *media, int len,
unsigned short peer_sample_rate)
{
mtx_3way.lock();
if (!is_3way) {
// This is not a 3-way call. This is not necessarily an
// error condition. The 3rd party may be in the process of
// leaving the conference.
// Simply discard the posted media
mtx_3way.unlock();
return;
}
if (peer_sample_rate != audio_encoder->get_sample_rate()) {
// Resample media from peer to sample rate of this receiver
int output_len = (len / 2) * audio_encoder->get_sample_rate() / peer_sample_rate;
short *output_buf = new short[output_len];
MEMMAN_NEW_ARRAY(output_buf);
int resample_len = resample((short *)media, len / 2, peer_sample_rate,
output_buf, output_len, audio_encoder->get_sample_rate());
media_3way_peer_tx->add((unsigned char *)output_buf, resample_len * 2);
MEMMAN_DELETE_ARRAY(output_buf);
delete [] output_buf;
} else {
media_3way_peer_tx->add(media, len);
}
mtx_3way.unlock();
}
void t_audio_rx::post_media_peer_rx_3way(unsigned char *media, int len,
unsigned short peer_sample_rate)
{
mtx_3way.lock();
if (!is_3way) {
// This is not a 3-way call. This is not necessarily an
// error condition. The 3rd party may be in the process of
// leaving the conference.
// Simply discard the posted media
mtx_3way.unlock();
return;
}
if (peer_sample_rate != audio_encoder->get_sample_rate()) {
// Resample media from peer to sample rate of this receiver
int output_len = (len / 2) * audio_encoder->get_sample_rate() / peer_sample_rate;
short *output_buf = new short[output_len];
MEMMAN_NEW_ARRAY(output_buf);
int resample_len = resample((short *)media, len / 2, peer_sample_rate,
output_buf, output_len, audio_encoder->get_sample_rate());
media_3way_peer_rx->add((unsigned char *)output_buf, resample_len * 2);
MEMMAN_DELETE_ARRAY(output_buf);
delete [] output_buf;
} else {
media_3way_peer_rx->add(media, len);
}
mtx_3way.unlock();
}
bool t_audio_rx::get_is_main_rx_3way(void) const {
return is_main_rx_3way;
}
|