From 72199376a661eef136f2bae4b203b27cc5b3ee28 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Tue, 28 Jan 2020 12:01:32 +0100 Subject: [PATCH] Improve dcall --- src/dcall.c | 137 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 55 deletions(-) diff --git a/src/dcall.c b/src/dcall.c index f261701..20d42ac 100644 --- a/src/dcall.c +++ b/src/dcall.c @@ -1,83 +1,96 @@ #include +#include +#include +#include +#include -int create_rx_chain(GstElement *pipeline) { +struct dcall_elements { + GstElement *pipeline; GstElement *rx_tap, *rx_jitterbuffer, *rx_depay, *rx_opusdec, *rx_resample, *rx_echocancel, *rx_sink; + GstElement *tx_tap, *tx_echocancel, *tx_queue, *tx_resample, *tx_opusenc, *tx_pay, *tx_sink; + char* remote_host; +}; - rx_tap = gst_element_factory_make("udpsrc", "rx-tap"); - rx_jitterbuffer = gst_element_factory_make("rtpjitterbuffer", "rx-jitterbuffer"); - rx_depay = gst_element_factory_make("rtpopusdepay", "rx-depay"); - rx_opusdec = gst_element_factory_make("opusdec", "rx-opusdec"); - rx_resample = gst_element_factory_make("audioresample", "rx-audioresample"); - rx_echocancel = gst_element_factory_make("webrtcechoprobe", "rx-echocancel"); - rx_sink = gst_element_factory_make("autoaudiosink", "rx-sink"); +int create_rx_chain(struct dcall_elements* de) { + de->rx_tap = gst_element_factory_make("udpsrc", "rx-tap"); + de->rx_jitterbuffer = gst_element_factory_make("rtpjitterbuffer", "rx-jitterbuffer"); + de->rx_depay = gst_element_factory_make("rtpopusdepay", "rx-depay"); + de->rx_opusdec = gst_element_factory_make("opusdec", "rx-opusdec"); + de->rx_resample = gst_element_factory_make("audioresample", "rx-audioresample"); + de->rx_echocancel = gst_element_factory_make("webrtcechoprobe", "rx-echocancel"); + de->rx_sink = gst_element_factory_make("autoaudiosink", "rx-sink"); - if (!rx_tap || !rx_jitterbuffer || !rx_depay || !rx_opusdec || !rx_resample || !rx_echocancel || !rx_sink) { + if (!de->rx_tap || !de->rx_jitterbuffer || !de->rx_depay || !de->rx_opusdec || !de->rx_resample || !de->rx_echocancel || !de->rx_sink) { g_printerr ("One element of the rx chain could not be created. Exiting.\n"); return -1; } - g_object_set(G_OBJECT (rx_tap), "port", 5000, NULL); + g_object_set(G_OBJECT (de->rx_tap), "port", 5000, NULL); //g_object_set(G_OBJECT (rx_tap), "address", "127.0.0.1", NULL); - g_object_set(G_OBJECT (rx_tap), "caps", gst_caps_new_simple("application/x-rtp", "media", G_TYPE_STRING, "audio", NULL), NULL); + g_object_set(G_OBJECT (de->rx_tap), "caps", gst_caps_new_simple("application/x-rtp", "media", G_TYPE_STRING, "audio", NULL), NULL); - g_object_set(G_OBJECT (rx_jitterbuffer), "do-lost", TRUE, NULL); - g_object_set(G_OBJECT (rx_jitterbuffer), "do-retransmission", FALSE, NULL); - g_object_set(G_OBJECT (rx_jitterbuffer), "latency", 50, NULL); + g_object_set(G_OBJECT (de->rx_jitterbuffer), "do-lost", TRUE, NULL); + g_object_set(G_OBJECT (de->rx_jitterbuffer), "do-retransmission", FALSE, NULL); + g_object_set(G_OBJECT (de->rx_jitterbuffer), "latency", 50, NULL); - g_object_set(G_OBJECT (rx_opusdec), "plc", TRUE, NULL); - g_object_set(G_OBJECT (rx_opusdec), "use-inband-fec", FALSE, NULL); + g_object_set(G_OBJECT (de->rx_opusdec), "plc", TRUE, NULL); + g_object_set(G_OBJECT (de->rx_opusdec), "use-inband-fec", FALSE, NULL); - gst_bin_add_many (GST_BIN (pipeline), rx_tap, rx_jitterbuffer, rx_depay, rx_opusdec, rx_resample, rx_echocancel, rx_sink, NULL); - gst_element_link_many (rx_tap, rx_jitterbuffer, rx_depay, rx_opusdec, rx_resample, rx_echocancel, rx_sink, NULL); + gst_bin_add_many (GST_BIN (de->pipeline), de->rx_tap, de->rx_jitterbuffer, de->rx_depay, de->rx_opusdec, de->rx_resample, de->rx_echocancel, de->rx_sink, NULL); + gst_element_link_many (de->rx_tap, de->rx_jitterbuffer, de->rx_depay, de->rx_opusdec, de->rx_resample, de->rx_echocancel, de->rx_sink, NULL); return 0; } -int create_tx_chain(GstElement *pipeline, char* remote_host) { - GstElement *tx_tap, *tx_echocancel, *tx_queue, *tx_resample, *tx_opusenc, *tx_pay, *tx_sink; - - tx_tap = gst_element_factory_make("autoaudiosrc", "tx-tap"); - tx_echocancel = gst_element_factory_make("webrtcdsp", "tx-echocancel"); +int create_tx_chain(struct dcall_elements* de) { + de->tx_tap = gst_element_factory_make("autoaudiosrc", "tx-tap"); + de->tx_echocancel = gst_element_factory_make("webrtcdsp", "tx-echocancel"); //tx_queue = gst_element_factory_make("queue", "tx-queue"); - tx_resample = gst_element_factory_make("audioresample", "tx-resample"); - tx_opusenc = gst_element_factory_make("opusenc", "tx-opusenc"); - tx_pay = gst_element_factory_make("rtpopuspay", "tx-rtpopuspay"); - tx_sink = gst_element_factory_make("udpsink", "tx-sink"); + de->tx_resample = gst_element_factory_make("audioresample", "tx-resample"); + de->tx_opusenc = gst_element_factory_make("opusenc", "tx-opusenc"); + de->tx_pay = gst_element_factory_make("rtpopuspay", "tx-rtpopuspay"); + de->tx_sink = gst_element_factory_make("udpsink", "tx-sink"); - if (!tx_tap || !tx_echocancel || /*!tx_queue ||*/ !tx_resample || !tx_opusenc || !tx_pay || !tx_sink) { + if (!de->tx_tap || !de->tx_echocancel || /*!de->tx_queue ||*/ !de->tx_resample || !de->tx_opusenc || !de->tx_pay || !de->tx_sink) { g_printerr("One element of the tx chain could not be created. Exiting.\n"); return -1; } - gst_util_set_object_arg(G_OBJECT(tx_opusenc), "audio-type", "voice"); - g_object_set(G_OBJECT(tx_opusenc), "inband-fec", FALSE, NULL); - g_object_set(G_OBJECT(tx_opusenc), "frame-size", 40, NULL); - g_object_set(G_OBJECT(tx_opusenc), "bitrate", 32000, NULL); - g_object_set(G_OBJECT(tx_opusenc), "dtx", FALSE, NULL); // gstreamer dtx opus implem. is broken + gst_util_set_object_arg(G_OBJECT(de->tx_opusenc), "audio-type", "voice"); + g_object_set(G_OBJECT(de->tx_opusenc), "inband-fec", FALSE, NULL); + g_object_set(G_OBJECT(de->tx_opusenc), "frame-size", 40, NULL); + g_object_set(G_OBJECT(de->tx_opusenc), "bitrate", 32000, NULL); + g_object_set(G_OBJECT(de->tx_opusenc), "dtx", FALSE, NULL); // gstreamer dtx opus implem. is broken - g_object_set(G_OBJECT(tx_sink), "host", remote_host, NULL); - g_object_set(G_OBJECT(tx_sink), "port", 5000, NULL); - g_object_set(G_OBJECT(tx_sink), "async", FALSE, NULL); + g_object_set(G_OBJECT(de->tx_sink), "host", de->remote_host, NULL); + g_object_set(G_OBJECT(de->tx_sink), "port", 5000, NULL); + g_object_set(G_OBJECT(de->tx_sink), "async", FALSE, NULL); - g_object_set(G_OBJECT(tx_echocancel), "echo-cancel", TRUE, NULL); - g_object_set(G_OBJECT(tx_echocancel), "extended-filter", TRUE, NULL); - g_object_set(G_OBJECT(tx_echocancel), "gain-control", TRUE, NULL); - g_object_set(G_OBJECT(tx_echocancel), "high-pass-filter", TRUE, NULL); - g_object_set(G_OBJECT(tx_echocancel), "limiter", FALSE, NULL); - g_object_set(G_OBJECT(tx_echocancel), "noise-suppression", TRUE, NULL); - g_object_set(G_OBJECT(tx_echocancel), "probe", "rx-echocancel", NULL); - g_object_set(G_OBJECT(tx_echocancel), "voice-detection", FALSE, NULL); + g_object_set(G_OBJECT(de->tx_echocancel), "echo-cancel", TRUE, NULL); + g_object_set(G_OBJECT(de->tx_echocancel), "extended-filter", TRUE, NULL); + g_object_set(G_OBJECT(de->tx_echocancel), "gain-control", TRUE, NULL); + g_object_set(G_OBJECT(de->tx_echocancel), "high-pass-filter", TRUE, NULL); + g_object_set(G_OBJECT(de->tx_echocancel), "limiter", FALSE, NULL); + g_object_set(G_OBJECT(de->tx_echocancel), "noise-suppression", TRUE, NULL); + g_object_set(G_OBJECT(de->tx_echocancel), "probe", "rx-echocancel", NULL); + g_object_set(G_OBJECT(de->tx_echocancel), "voice-detection", FALSE, NULL); - gst_bin_add_many(GST_BIN(pipeline), tx_tap, tx_echocancel, /*tx_queue,*/ tx_resample, tx_opusenc, tx_pay, tx_sink, NULL); - gst_element_link_many(tx_tap, tx_echocancel, /*tx_queue,*/ tx_resample, tx_opusenc, tx_pay, tx_sink, NULL); + gst_bin_add_many(GST_BIN(de->pipeline), de->tx_tap, de->tx_echocancel, /*tx_queue,*/ de->tx_resample, de->tx_opusenc, de->tx_pay, de->tx_sink, NULL); + gst_element_link_many(de->tx_tap, de->tx_echocancel, /*tx_queue,*/ de->tx_resample, de->tx_opusenc, de->tx_pay, de->tx_sink, NULL); return 0; } +gboolean stop_handler(gpointer user_data) { + GMainLoop *loop = user_data; + g_main_loop_quit(loop); + return TRUE; +} + int main(int argc, char *argv[]) { GMainLoop *loop; - GstElement *pipeline; char *remote_host; + struct dcall_elements de; /* Check input arguments */ if (argc != 2) { @@ -89,24 +102,38 @@ int main(int argc, char *argv[]) { gst_init (&argc, &argv); loop = g_main_loop_new (NULL, FALSE); - pipeline = gst_pipeline_new ("pipeline"); - if (!pipeline) { + de.pipeline = gst_pipeline_new ("pipeline"); + if (!de.pipeline) { g_printerr ("Pipeline could not be created. Exiting.\n"); return -1; } - if (create_rx_chain (pipeline) != 0) return -1; - if (create_tx_chain (pipeline, remote_host) != 0) return -1; + if (create_rx_chain (&de) != 0) return -1; + if (create_tx_chain (&de) != 0) return -1; - gst_element_set_state (pipeline, GST_STATE_PLAYING); + gst_element_set_state (de.pipeline, GST_STATE_PLAYING); + + g_unix_signal_add (SIGTERM, stop_handler, loop); + g_unix_signal_add (SIGINT, stop_handler, loop); g_print ("Running...\n"); g_main_loop_run (loop); - g_print ("Returned, stopping playback\n"); - gst_element_set_state (pipeline, GST_STATE_NULL); + g_print ("Main loop stopped...\n"); - gst_object_unref (GST_OBJECT (pipeline)); + GstStructure *stats; + guint64 num_pushed, num_lost, num_late, num_duplicates; + g_object_get(de.rx_jitterbuffer, "stats", &stats, NULL); + + gst_structure_get_uint64(stats, "num-pushed", &num_pushed); + gst_structure_get_uint64(stats, "num-lost", &num_lost); + gst_structure_get_uint64(stats, "num-late", &num_late); + gst_structure_get_uint64(stats, "num-duplicates", &num_duplicates); + g_print("pkt_delivered=%ld, pkt_lost=%ld, pkt_late=%ld, pkt_duplicates=%ld\n", num_pushed, num_lost, num_late, num_duplicates); + + gst_element_set_state (de.pipeline, GST_STATE_NULL); + + gst_object_unref (GST_OBJECT (de.pipeline)); g_main_loop_unref (loop); return 0;