diff --git a/daemon/.ycm_extra_conf.py b/daemon/.ycm_extra_conf.py index ca641f1a7..92f5a5a15 100644 --- a/daemon/.ycm_extra_conf.py +++ b/daemon/.ycm_extra_conf.py @@ -29,6 +29,7 @@ flags = [ '-DRE_PLUGIN_DIR="/usr/lib/rtpengine"', '-DWITH_IPTABLES_OPTION', '-DWITH_TRANSCODING', + '-DHAVE_BCG729', '-O2', '-fstack-protector', '--param=ssp-buffer-size=4', diff --git a/daemon/Makefile b/daemon/Makefile index 63e65a885..a8f2a6ed6 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -3,6 +3,35 @@ TARGET= rtpengine with_iptables_option ?= yes with_transcoding ?= yes +# look for bcg729 +# /usr/src +ifneq (,$(wildcard /usr/src/bcg729/include/bcg729/decoder.h)) +have_bcg729 := yes +bcg729_inc := /usr/src/bcg729/include/ +bcg729_lib := /usr/src/bcg729/src/ +else +# rfuchs dev +ifneq (,$(wildcard $(HOME)/src/bcg729/include/bcg729/decoder.h)) +have_bcg729 := yes +bcg729_inc := $(HOME)/src/bcg729/include/ +bcg729_lib := $(HOME)/src/bcg729/src/ +else +# home directory +ifneq (,$(wildcard $(HOME)/bcg729/include/bcg729/decoder.h)) +have_bcg729 := yes +bcg729_inc := $(HOME)/bcg729/include/ +bcg729_lib := $(HOME)/bcg729/src/ +else +# included toplevel +ifneq (,$(wildcard ../bcg729/include/bcg729/decoder.h)) +have_bcg729 := yes +bcg729_inc := ../bcg729/include/ +bcg729_lib := ../bcg729/src/ +endif +endif +endif +endif + CFLAGS= -g -Wall -pthread -fno-strict-aliasing CFLAGS+= -std=c99 CFLAGS+= `pkg-config --cflags glib-2.0` @@ -31,6 +60,9 @@ CFLAGS+= -DWITH_TRANSCODING else CFLAGS+= -DWITHOUT_CODECLIB endif +ifeq ($(have_bcg729),yes) +CFLAGS+= -DHAVE_BCG729 -I$(bcg729_inc) +endif CFLAGS+= -DRE_PLUGIN_DIR="\"/usr/lib/rtpengine\"" @@ -64,6 +96,9 @@ LDFLAGS+= `pkg-config --libs libavutil` LDFLAGS+= `pkg-config --libs libavresample` LDFLAGS+= `pkg-config --libs libavfilter` endif +ifeq ($(have_bcg729),yes) +LDFLAGS+= -L$(bcg729_lib) -lbcg729 +endif include ../lib/lib.Makefile diff --git a/daemon/codec.c b/daemon/codec.c index b609fbb16..3dbad938d 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -140,7 +140,7 @@ static void __ensure_codec_def(struct rtp_payload_type *pt, struct call_media *m pt->codec_def = codec_find(&pt->encoding, media->type_id); if (!pt->codec_def) return; - if (pt->codec_def->avcodec_id != -1 && (!pt->codec_def->encoder || !pt->codec_def->decoder)) + if (!pt->codec_def->pseudocodec && (!pt->codec_def->support_encoding || !pt->codec_def->support_decoding)) pt->codec_def = NULL; } static GList *__delete_receiver_codec(struct call_media *receiver, GList *link) { @@ -176,7 +176,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) { struct rtp_payload_type *pt = l->data; __ensure_codec_def(pt, sink); - if (!pt->codec_def || pt->codec_def->avcodec_id == -1) // not supported, next + if (!pt->codec_def || pt->codec_def->pseudocodec) // not supported, next continue; // fix up ptime @@ -219,7 +219,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) continue; } - if (pt->codec_def->avcodec_id != -1) { + if (!pt->codec_def->pseudocodec) { ilog(LOG_DEBUG, "Accepting offered codec " STR_FORMAT " due to transcoding", STR_FMT(&pt->encoding_with_params)); MEDIA_SET(receiver, TRANSCODE); @@ -272,7 +272,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) // check our own support for this codec __ensure_codec_def(pt, receiver); - if (!pt->codec_def || pt->codec_def->avcodec_id == -1) { + if (!pt->codec_def || pt->codec_def->pseudocodec) { // not supported, or not a real audio codec __make_passthrough(handler); passthrough_handlers = g_slist_prepend(passthrough_handlers, handler); @@ -750,9 +750,9 @@ static struct rtp_payload_type *codec_make_payload_type(const str *codec_str, st if (media->type_id && dec->media_type != media->type_id) return (void *) 0x1; // we must support both encoding and decoding - if (!dec->encoder) + if (!dec->support_decoding) return NULL; - if (!dec->decoder) + if (!dec->support_encoding) return NULL; if (dec->rfc_payload_type >= 0) { diff --git a/lib/.ycm_extra_conf.py b/lib/.ycm_extra_conf.py index 2336c492f..6e2d633f5 100644 --- a/lib/.ycm_extra_conf.py +++ b/lib/.ycm_extra_conf.py @@ -23,9 +23,11 @@ flags = [ '-D__DEBUG=1', '-D__YCM=1', '-I../daemon', + '-I/home/dfx/src/bcg729/include', '-DRTPENGINE_VERSION="dummy"', '-DRE_PLUGIN_DIR="/usr/lib/rtpengine"', '-DWITH_IPTABLES_OPTION', + '-DHAVE_BCG729', '-O2', '-fstack-protector', '--param=ssp-buffer-size=4', diff --git a/lib/codeclib.c b/lib/codeclib.c index a0b866b11..4e7095a42 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -4,6 +4,10 @@ #include #include #include +#ifdef HAVE_BCG729 +#include +#include +#endif #include "str.h" #include "log.h" #include "loglib.h" @@ -34,7 +38,7 @@ static packetizer_f packetizer_samplestream; // flat stream of samples static format_init_f opus_init; static set_options_f opus_set_options; -static void avc_def_init(codec_def_t *, int); +static void avc_def_init(codec_def_t *); static const char *avc_decoder_init(decoder_t *); static int avc_decoder_input(decoder_t *dec, const str *data, GQueue *out); static void avc_decoder_close(decoder_t *); @@ -55,6 +59,26 @@ static const codec_type_t codec_type_avcodec = { .encoder_close = avc_encoder_close, }; +#ifdef HAVE_BCG729 +static void bcg729_def_init(codec_def_t *); +static const char *bcg729_decoder_init(decoder_t *); +static int bcg729_decoder_input(decoder_t *dec, const str *data, GQueue *out); +static void bcg729_decoder_close(decoder_t *); +static const char *bcg729_encoder_init(encoder_t *enc); +static int bcg729_encoder_input(encoder_t *enc, AVFrame **frame); +static void bcg729_encoder_close(encoder_t *enc); + +static const codec_type_t codec_type_bcg729 = { + .def_init = bcg729_def_init, + .decoder_init = bcg729_decoder_init, + .decoder_input = bcg729_decoder_input, + .decoder_close = bcg729_decoder_close, + .encoder_init = bcg729_encoder_init, + .encoder_input = bcg729_encoder_input, + .encoder_close = bcg729_encoder_close, +}; +#endif + static codec_def_t __codec_defs[] = { @@ -115,17 +139,31 @@ static codec_def_t __codec_defs[] = { .media_type = MT_AUDIO, .codec_type = &codec_type_avcodec, }, +#ifndef HAVE_BCG729 { .rtpname = "G729", .avcodec_id = AV_CODEC_ID_G729, .clockrate_mult = 1, .default_clockrate = 8000, .default_channels = 1, - .default_ptime = 20, + .default_ptime = 10, .packetizer = packetizer_passthrough, .media_type = MT_AUDIO, .codec_type = &codec_type_avcodec, }, +#else + { + .rtpname = "G729", + .avcodec_id = -1, + .clockrate_mult = 1, + .default_clockrate = 8000, + .default_channels = 1, + .default_ptime = 10, + .packetizer = packetizer_passthrough, + .media_type = MT_AUDIO, + .codec_type = &codec_type_bcg729, + }, +#endif { .rtpname = "speex", .avcodec_id = AV_CODEC_ID_SPEEX, @@ -276,6 +314,7 @@ static codec_def_t __codec_defs[] = { .avcodec_name = NULL, .packetizer = packetizer_passthrough, .media_type = MT_AUDIO, + .pseudocodec = 1, }, // for file writing { @@ -600,7 +639,7 @@ static void avlog_ilog(void *ptr, int loglevel, const char *fmt, va_list ap) { } -static void avc_def_init(codec_def_t *def, int print) { +static void avc_def_init(codec_def_t *def) { // look up AVCodec structs if (def->avcodec_name) { def->encoder = avcodec_find_encoder_by_name(def->avcodec_name); @@ -614,28 +653,10 @@ static void avc_def_init(codec_def_t *def, int print) { } // check if we have support if we are supposed to if (def->avcodec_name || def->avcodec_id >= 0) { - if (print) { - if (def->encoder && def->decoder) - printf("%20s: fully supported\n", def->rtpname); - else if (def->decoder) - printf("%20s: supported for decoding only\n", def->rtpname); - else if (def->encoder) - printf("%20s: supported for encoding only\n", def->rtpname); - else - printf("%20s: not supported\n", def->rtpname); - } - else { - if (!def->encoder && !def->decoder) - ilog(LOG_DEBUG, "Codec %s is not supported by codec library", - def->rtpname); - else if (!def->encoder) { - ilog(LOG_DEBUG, "Codec %s is only supported for decoding " - "by codec library", def->rtpname); - } - else if (!def->decoder) - ilog(LOG_DEBUG, "Codec %s is only supported for encoding " - "by codec library", def->rtpname); - } + if (def->encoder) + def->support_encoding = 1; + if (def->decoder) + def->support_decoding = 1; } } @@ -673,7 +694,33 @@ void codeclib_init(int print) { def->rfc_payload_type = -1; if (def->codec_type && def->codec_type->def_init) - def->codec_type->def_init(def, print); + def->codec_type->def_init(def); + + if (def->pseudocodec) + continue; + + if (print) { + if (def->support_encoding && def->support_decoding) + printf("%20s: fully supported\n", def->rtpname); + else if (def->support_decoding) + printf("%20s: supported for decoding only\n", def->rtpname); + else if (def->support_encoding) + printf("%20s: supported for encoding only\n", def->rtpname); + else + printf("%20s: not supported\n", def->rtpname); + } + else { + if (!def->support_encoding && !def->support_decoding) + ilog(LOG_DEBUG, "Codec %s is not supported by codec library", + def->rtpname); + else if (!def->support_encoding) { + ilog(LOG_DEBUG, "Codec %s is only supported for decoding " + "by codec library", def->rtpname); + } + else if (!def->support_decoding) + ilog(LOG_DEBUG, "Codec %s is only supported for encoding " + "by codec library", def->rtpname); + } } } @@ -856,6 +903,7 @@ static const char *avc_encoder_init(encoder_t *enc) { enc->u.avc.avcctx->sample_fmt = enc->actual_format.format; enc->u.avc.avcctx->time_base = (AVRational){1,enc->actual_format.clockrate}; enc->u.avc.avcctx->bit_rate = enc->bitrate; + enc->samples_per_frame = enc->actual_format.clockrate * enc->ptime / 1000; if (enc->u.avc.avcctx->frame_size) enc->samples_per_frame = enc->u.avc.avcctx->frame_size; @@ -1026,10 +1074,10 @@ int encoder_input_data(encoder_t *enc, AVFrame *frame, if (ret < 0) return -1; - //av_write_frame(output->fmtctx, &output->avpkt); - callback(enc, u1, u2); - if (enc->avpkt.size) { + //av_write_frame(output->fmtctx, &output->avpkt); + callback(enc, u1, u2); + //output->fifo_pts += output->frame->nb_samples; enc->mux_dts = enc->avpkt.dts + 1; // min next expected dts @@ -1168,3 +1216,94 @@ static void opus_set_options(encoder_t *enc) { ilog(LOG_WARN, "Failed to set Opus frame_duration option (error code %i)", ret); // XXX additional opus options } + + + +#ifdef HAVE_BCG729 +static void bcg729_def_init(codec_def_t *def) { + // test init + bcg729EncoderChannelContextStruct *e = initBcg729EncoderChannel(0); + bcg729DecoderChannelContextStruct *d = initBcg729DecoderChannel(); + if (e) { + def->support_encoding = 1; + closeBcg729EncoderChannel(e); + } + if (d) { + def->support_decoding = 1; + closeBcg729DecoderChannel(d); + } +} + +static const char *bcg729_decoder_init(decoder_t *dec) { + dec->u.bcg729 = initBcg729DecoderChannel(); + if (!dec->u.bcg729) + return "failed to initialize bcg729"; + return NULL; +} + +static int bcg729_decoder_input(decoder_t *dec, const str *data, GQueue *out) { + AVFrame *frame = av_frame_alloc(); + frame->nb_samples = 80; + frame->format = AV_SAMPLE_FMT_S16; + frame->sample_rate = dec->in_format.clockrate; // 8000 + frame->channel_layout = av_get_default_channel_layout(dec->in_format.channels); // 1 channel + if (av_frame_get_buffer(frame, 0) < 0) + abort(); + + // XXX handle lost packets and comfort noise + bcg729Decoder(dec->u.bcg729, (void *) data->s, data->len, 0, 0, 0, (void *) frame->extended_data[0]); + + g_queue_push_tail(out, frame); + + return 0; +} + +static void bcg729_decoder_close(decoder_t *dec) { + if (dec->u.bcg729) + closeBcg729DecoderChannel(dec->u.bcg729); + dec->u.bcg729 = NULL; +} + +static const char *bcg729_encoder_init(encoder_t *enc) { + enc->u.bcg729 = initBcg729EncoderChannel(0); // no VAD + if (!enc->u.bcg729) + return "failed to initialize bcg729"; + + enc->actual_format.format = AV_SAMPLE_FMT_S16; + enc->actual_format.channels = 1; + enc->actual_format.clockrate = 8000; + enc->samples_per_frame = 80; + + return NULL; +} + +static int bcg729_encoder_input(encoder_t *enc, AVFrame **frame) { + if (!*frame) + return 0; + + if ((*frame)->nb_samples != 80) { + ilog(LOG_ERR, "bcg729: input %u samples instead of 80", (*frame)->nb_samples); + return -1; + } + + av_new_packet(&enc->avpkt, 10); + unsigned char len = 0; + + bcg729Encoder(enc->u.bcg729, (void *) (*frame)->extended_data[0], enc->avpkt.data, &len); + if (!len) { + av_packet_unref(&enc->avpkt); + return 0; + } + + enc->avpkt.size = len; + enc->avpkt.pts = (*frame)->pts; + + return 0; +} + +static void bcg729_encoder_close(encoder_t *enc) { + if (enc->u.bcg729) + closeBcg729EncoderChannel(enc->u.bcg729); + enc->u.bcg729 = NULL; +} +#endif diff --git a/lib/codeclib.h b/lib/codeclib.h index 4b1b8fa4b..299b9f6be 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -13,6 +13,10 @@ typedef struct codec_def_s codec_def_t; #include #include #include +#ifdef HAVE_BCG729 +#include +#include +#endif #include "str.h" @@ -49,7 +53,7 @@ enum media_type { }; struct codec_type_s { - void (*def_init)(codec_def_t *, int print); + void (*def_init)(codec_def_t *); const char *(*decoder_init)(decoder_t *); int (*decoder_input)(decoder_t *, const str *data, GQueue *); @@ -80,6 +84,9 @@ struct codec_def_s { // filled in by codeclib_init() str rtpname_str; int rfc_payload_type; + int support_encoding, + support_decoding, + pseudocodec; const codec_type_t *codec_type; @@ -112,6 +119,9 @@ struct decoder_s { AVCodecContext *avcctx; AVPacket avpkt; } avc; +#ifdef HAVE_BCG729 + bcg729DecoderChannelContextStruct *bcg729; +#endif } u; unsigned long rtp_ts; @@ -131,6 +141,9 @@ struct encoder_s { AVCodec *codec; AVCodecContext *avcctx; } avc; +#ifdef HAVE_BCG729 + bcg729EncoderChannelContextStruct *bcg729; +#endif } u; AVPacket avpkt; AVAudioFifo *fifo; diff --git a/perl/NGCP/Rtpclient/SDP.pm b/perl/NGCP/Rtpclient/SDP.pm index fa50e8384..6570af5a1 100644 --- a/perl/NGCP/Rtpclient/SDP.pm +++ b/perl/NGCP/Rtpclient/SDP.pm @@ -126,6 +126,7 @@ use IO::Socket; my %codec_map = ( PCMA => { payload_type => 8 }, PCMU => { payload_type => 0 }, + G729 => { payload_type => 18 }, ); my %payload_type_map = map {$codec_map{$_}{payload_type} => $_} keys(%codec_map); diff --git a/perl/NGCP/Rtpengine/Test.pm b/perl/NGCP/Rtpengine/Test.pm index a439ed219..1552eb257 100644 --- a/perl/NGCP/Rtpengine/Test.pm +++ b/perl/NGCP/Rtpengine/Test.pm @@ -367,10 +367,13 @@ sub _input { defined($component) or return; # not one of ours # must be RTP or RTCP input - my $exp = shift(@{$self->{media_receive_queues}->[$component]}) or die; if (!$self->{args}->{no_data_check}) { + my $exp = shift(@{$self->{media_receive_queues}->[$component]}) or die; $$input eq $exp or die; } + else { + @{$self->{media_receive_queues}->[$component]} = (); + } $self->{media_packets_received}->[$component]++; $self->{client_components}->[$component] and diff --git a/t/test-transcode.pl b/t/test-transcode.pl index 2cdb79866..721c1fb80 100755 --- a/t/test-transcode.pl +++ b/t/test-transcode.pl @@ -7,8 +7,8 @@ use IO::Socket; my $r = NGCP::Rtpengine::Test->new(); my ($a, $b) = $r->client_pair( - {sockdomain => &Socket::AF_INET, codecs => [qw(PCMA)], no_data_check => 1}, - {sockdomain => &Socket::AF_INET, codecs => [qw(PCMU)], no_data_check => 1} + {sockdomain => &Socket::AF_INET, codecs => [qw(PCMU)], no_data_check => 1}, + {sockdomain => &Socket::AF_INET, codecs => [qw(G729)], no_data_check => 1} ); $r->timer_once(3, sub { @@ -18,7 +18,7 @@ $r->timer_once(3, sub { }); $r->timer_once(10, sub { $r->stop(); }); -$a->offer($b, ICE => 'remove', label => "caller", codec => { transcode => ['PCMU']}); +$a->offer($b, ICE => 'remove', label => "caller", codec => { transcode => ['G729']}); $b->start_rtp(); $b->start_rtcp();