diff options
author | Sanchayan Maity <sanchayan@asymptotic.io> | 2021-02-20 11:56:49 +0530 |
---|---|---|
committer | PulseAudio Marge Bot <pulseaudio-maintainers@lists.freedesktop.org> | 2021-07-30 13:10:08 +0000 |
commit | 86d1dd0d70d6943cb67346c6187171444f764774 (patch) | |
tree | d9cc39ce261fbc15270806b2727ba042fe4a6e31 | |
parent | 0b9ef4cd0ac2b38f6c2eb9b4673ec8ae16235659 (diff) |
rtp: Enable support for OPUS
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/510>
-rw-r--r-- | src/modules/rtp/module-rtp-recv.c | 2 | ||||
-rw-r--r-- | src/modules/rtp/module-rtp-send.c | 21 | ||||
-rw-r--r-- | src/modules/rtp/rtp-common.c | 21 | ||||
-rw-r--r-- | src/modules/rtp/rtp-gstreamer.c | 140 | ||||
-rw-r--r-- | src/modules/rtp/rtp-native.c | 4 | ||||
-rw-r--r-- | src/modules/rtp/rtp.h | 8 | ||||
-rw-r--r-- | src/modules/rtp/sdp.c | 21 | ||||
-rw-r--r-- | src/modules/rtp/sdp.h | 4 |
8 files changed, 174 insertions, 47 deletions
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index a9b42bbc5..6f06ada33 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -568,7 +568,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in pa_memblock_unref(silence.memblock); - if (!(s->rtp_context = pa_rtp_context_new_recv(fd, sdp_info->payload, &s->sdp_info.sample_spec))) + if (!(s->rtp_context = pa_rtp_context_new_recv(fd, sdp_info->payload, &s->sdp_info.sample_spec, sdp_info->enable_opus))) goto fail; pa_hashmap_put(s->userdata->by_origin, s->sdp_info.origin, s); diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index 5a4c6fc06..fd5b63abe 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -67,6 +67,7 @@ PA_MODULE_USAGE( "ttl=<ttl value> " "inhibit_auto_suspend=<always|never|only_with_non_monitor_sources>" "stream_name=<name of the stream>" + "enable_opus=<enable OPUS codec>" ); #define DEFAULT_PORT 46000 @@ -92,6 +93,7 @@ static const char* const valid_modargs[] = { "ttl", "inhibit_auto_suspend", "stream_name", + "enable_opus", NULL }; @@ -228,6 +230,7 @@ int pa__init(pa_module*m) { socklen_t k; char hn[128], *n; bool loop = false; + bool enable_opus = false; enum inhibit_auto_suspend inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES; const char *inhibit_auto_suspend_str; pa_source_output_new_data data; @@ -249,6 +252,11 @@ int pa__init(pa_module*m) { goto fail; } + if (pa_modargs_get_value_boolean(ma, "enable_opus", &enable_opus) < 0) { + pa_log("Failed to parse \"use_opus\" parameter."); + goto fail; + } + if ((inhibit_auto_suspend_str = pa_modargs_get_value(ma, "inhibit_auto_suspend", NULL))) { if (pa_streq(inhibit_auto_suspend_str, "always")) inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ALWAYS; @@ -263,7 +271,7 @@ int pa__init(pa_module*m) { } ss = s->sample_spec; - pa_rtp_sample_spec_fixup(&ss); + pa_rtp_sample_spec_fixup(&ss, enable_opus); cm = s->channel_map; if (pa_modargs_get_sample_spec(ma, &ss) < 0) { pa_log("Failed to parse sample specification"); @@ -275,6 +283,11 @@ int pa__init(pa_module*m) { goto fail; } + if (enable_opus && ss.rate != 48000) { + pa_log_warn("OPUS requires sample rate as 48 KHz. Setting rate=48000."); + ss.rate = 48000; + } + if (ss.channels != cm.channels) pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_AIFF); @@ -476,19 +489,19 @@ int pa__init(pa_module*m) { p = pa_sdp_build(af, (void*) &((struct sockaddr_in*) &sa_dst)->sin_addr, (void*) &dst_sa4.sin_addr, - n, (uint16_t) port, payload, &ss); + n, (uint16_t) port, payload, &ss, enable_opus); #ifdef HAVE_IPV6 } else { p = pa_sdp_build(af, (void*) &((struct sockaddr_in6*) &sa_dst)->sin6_addr, (void*) &dst_sa6.sin6_addr, - n, (uint16_t) port, payload, &ss); + n, (uint16_t) port, payload, &ss, enable_opus); #endif } pa_xfree(n); - if (!(u->rtp_context = pa_rtp_context_new_send(fd, payload, mtu, &ss))) + if (!(u->rtp_context = pa_rtp_context_new_send(fd, payload, mtu, &ss, enable_opus))) goto fail; pa_sap_context_init_send(&u->sap_context, sap_fd, p); diff --git a/src/modules/rtp/rtp-common.c b/src/modules/rtp/rtp-common.c index 65e2c7acd..e4dd9f05e 100644 --- a/src/modules/rtp/rtp-common.c +++ b/src/modules/rtp/rtp-common.c @@ -52,6 +52,12 @@ pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec ss->rate = 44100; break; + case 127: + ss->channels = 2; + ss->format = PA_SAMPLE_S16LE; + ss->rate = 48000; + break; + default: return NULL; } @@ -59,10 +65,12 @@ pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec return ss; } -pa_sample_spec *pa_rtp_sample_spec_fixup(pa_sample_spec * ss) { +pa_sample_spec *pa_rtp_sample_spec_fixup(pa_sample_spec * ss, bool enable_opus) { pa_assert(ss); - if (!pa_rtp_sample_spec_valid(ss)) + if (!pa_rtp_sample_spec_valid(ss) && enable_opus) + ss->format = PA_SAMPLE_S16LE; + else if (!pa_rtp_sample_spec_valid(ss) || !enable_opus) ss->format = PA_SAMPLE_S16BE; pa_assert(pa_rtp_sample_spec_valid(ss)); @@ -75,22 +83,25 @@ int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) { if (!pa_sample_spec_valid(ss)) return 0; - return ss->format == PA_SAMPLE_S16BE; + return ss->format == PA_SAMPLE_S16BE || ss->format == PA_SAMPLE_S16LE; } const char* pa_rtp_format_to_string(pa_sample_format_t f) { switch (f) { case PA_SAMPLE_S16BE: + case PA_SAMPLE_S16LE: return "L16"; default: return NULL; } } -pa_sample_format_t pa_rtp_string_to_format(const char *s) { +pa_sample_format_t pa_rtp_string_to_format(const char *s, bool enable_opus) { pa_assert(s); - if (pa_streq(s, "L16")) + if (pa_streq(s, "L16") && enable_opus) + return PA_SAMPLE_S16LE; + else if (pa_streq(s, "L16")) return PA_SAMPLE_S16BE; else return PA_SAMPLE_INVALID; diff --git a/src/modules/rtp/rtp-gstreamer.c b/src/modules/rtp/rtp-gstreamer.c index 28d367bfb..cb498a95d 100644 --- a/src/modules/rtp/rtp-gstreamer.c +++ b/src/modules/rtp/rtp-gstreamer.c @@ -45,6 +45,14 @@ #define MAKE_ELEMENT(v, e) MAKE_ELEMENT_NAMED((v), (e), NULL) #define RTP_HEADER_SIZE 12 +/* + * As per RFC 7587, the RTP payload type for OPUS is to be assigned + * dynamically. Considering that pa_rtp_payload_from_sample_spec uses + * 127 for anything other than format == S16BE and rate == 44.1 KHz, + * we use 127 for OPUS here as rate == 48 KHz for OPUS. + */ +#define RTP_OPUS_PAYLOAD_TYPE 127 + struct pa_rtp_context { pa_fdsem *fdsem; pa_sample_spec ss; @@ -61,20 +69,21 @@ struct pa_rtp_context { size_t mtu; }; -static GstCaps* caps_from_sample_spec(const pa_sample_spec *ss) { - if (ss->format != PA_SAMPLE_S16BE) +static GstCaps* caps_from_sample_spec(const pa_sample_spec *ss, bool enable_opus) { + if (ss->format != PA_SAMPLE_S16BE && ss->format != PA_SAMPLE_S16LE) return NULL; return gst_caps_new_simple("audio/x-raw", - "format", G_TYPE_STRING, "S16BE", + "format", G_TYPE_STRING, enable_opus ? "S16LE" : "S16BE", "rate", G_TYPE_INT, (int) ss->rate, "channels", G_TYPE_INT, (int) ss->channels, "layout", G_TYPE_STRING, "interleaved", NULL); } -static bool init_send_pipeline(pa_rtp_context *c, int fd, uint8_t payload, size_t mtu, const pa_sample_spec *ss) { +static bool init_send_pipeline(pa_rtp_context *c, int fd, uint8_t payload, size_t mtu, const pa_sample_spec *ss, bool enable_opus) { GstElement *appsrc = NULL, *pay = NULL, *capsf = NULL, *rtpbin = NULL, *sink = NULL; + GstElement *opusenc = NULL; GstCaps *caps; GSocket *socket; GInetSocketAddress *addr; @@ -83,7 +92,12 @@ static bool init_send_pipeline(pa_rtp_context *c, int fd, uint8_t payload, size_ gchar *addr_str; MAKE_ELEMENT(appsrc, "appsrc"); - MAKE_ELEMENT(pay, "rtpL16pay"); + if (enable_opus) { + MAKE_ELEMENT(opusenc, "opusenc"); + MAKE_ELEMENT(pay, "rtpopuspay"); + } else { + MAKE_ELEMENT(pay, "rtpL16pay"); + } MAKE_ELEMENT(capsf, "capsfilter"); MAKE_ELEMENT(rtpbin, "rtpbin"); MAKE_ELEMENT(sink, "udpsink"); @@ -92,7 +106,10 @@ static bool init_send_pipeline(pa_rtp_context *c, int fd, uint8_t payload, size_ gst_bin_add_many(GST_BIN(c->pipeline), appsrc, pay, capsf, rtpbin, sink, NULL); - caps = caps_from_sample_spec(ss); + if (enable_opus) + gst_bin_add_many(GST_BIN(c->pipeline), opusenc, NULL); + + caps = caps_from_sample_spec(ss, enable_opus); if (!caps) { pa_log("Unsupported format to payload"); goto fail; @@ -125,17 +142,33 @@ static bool init_send_pipeline(pa_rtp_context *c, int fd, uint8_t payload, size_ gst_caps_unref(caps); /* Force the payload type that we want */ - caps = gst_caps_new_simple("application/x-rtp", "payload", G_TYPE_INT, (int) payload, NULL); + if (enable_opus) + caps = gst_caps_new_simple("application/x-rtp", "payload", G_TYPE_INT, (int) RTP_OPUS_PAYLOAD_TYPE, "encoding-name", G_TYPE_STRING, "OPUS", NULL); + else + caps = gst_caps_new_simple("application/x-rtp", "payload", G_TYPE_INT, (int) payload, "encoding-name", G_TYPE_STRING, "L16", NULL); + g_object_set(capsf, "caps", caps, NULL); gst_caps_unref(caps); - if (!gst_element_link(appsrc, pay) || - !gst_element_link(pay, capsf) || - !gst_element_link_pads(capsf, "src", rtpbin, "send_rtp_sink_0") || - !gst_element_link_pads(rtpbin, "send_rtp_src_0", sink, "sink")) { + if (enable_opus) { + if (!gst_element_link(appsrc, opusenc) || + !gst_element_link(opusenc, pay) || + !gst_element_link(pay, capsf) || + !gst_element_link_pads(capsf, "src", rtpbin, "send_rtp_sink_0") || + !gst_element_link_pads(rtpbin, "send_rtp_src_0", sink, "sink")) { - pa_log("Could not set up send pipeline"); - goto fail; + pa_log("Could not set up send pipeline"); + goto fail; + } + } else { + if (!gst_element_link(appsrc, pay) || + !gst_element_link(pay, capsf) || + !gst_element_link_pads(capsf, "src", rtpbin, "send_rtp_sink_0") || + !gst_element_link_pads(rtpbin, "send_rtp_src_0", sink, "sink")) { + + pa_log("Could not set up send pipeline"); + goto fail; + } } if (gst_element_set_state(c->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { @@ -154,6 +187,8 @@ fail: /* These weren't yet added to pipeline, so we still have a ref */ if (appsrc) gst_object_unref(appsrc); + if (opusenc) + gst_object_unref(opusenc); if (pay) gst_object_unref(pay); if (capsf) @@ -167,7 +202,7 @@ fail: return false; } -pa_rtp_context* pa_rtp_context_new_send(int fd, uint8_t payload, size_t mtu, const pa_sample_spec *ss) { +pa_rtp_context* pa_rtp_context_new_send(int fd, uint8_t payload, size_t mtu, const pa_sample_spec *ss, bool enable_opus) { pa_rtp_context *c = NULL; GError *error = NULL; @@ -175,6 +210,9 @@ pa_rtp_context* pa_rtp_context_new_send(int fd, uint8_t payload, size_t mtu, con pa_log_info("Initialising GStreamer RTP backend for send"); + if (enable_opus) + pa_log_info("Using OPUS encoding for RTP send"); + c = pa_xnew0(pa_rtp_context, 1); c->ss = *ss; @@ -187,7 +225,7 @@ pa_rtp_context* pa_rtp_context_new_send(int fd, uint8_t payload, size_t mtu, con goto fail; } - if (!init_send_pipeline(c, fd, payload, mtu, ss)) + if (!init_send_pipeline(c, fd, payload, mtu, ss, enable_opus)) goto fail; return c; @@ -313,10 +351,18 @@ int pa_rtp_send(pa_rtp_context *c, pa_memblockq *q) { return 0; } -static GstCaps* rtp_caps_from_sample_spec(const pa_sample_spec *ss) { - if (ss->format != PA_SAMPLE_S16BE) +static GstCaps* rtp_caps_from_sample_spec(const pa_sample_spec *ss, bool enable_opus) { + if (ss->format != PA_SAMPLE_S16BE && ss->format != PA_SAMPLE_S16LE) return NULL; + if (enable_opus) + return gst_caps_new_simple("application/x-rtp", + "media", G_TYPE_STRING, "audio", + "encoding-name", G_TYPE_STRING, "OPUS", + "clock-rate", G_TYPE_INT, (int) 48000, + "payload", G_TYPE_INT, (int) RTP_OPUS_PAYLOAD_TYPE, + NULL); + return gst_caps_new_simple("application/x-rtp", "media", G_TYPE_STRING, "audio", "encoding-name", G_TYPE_STRING, "L16", @@ -373,22 +419,32 @@ static GstPadProbeReturn udpsrc_buffer_probe(GstPad *pad, GstPadProbeInfo *info, return GST_PAD_PROBE_OK; } -static bool init_receive_pipeline(pa_rtp_context *c, int fd, const pa_sample_spec *ss) { +static bool init_receive_pipeline(pa_rtp_context *c, int fd, const pa_sample_spec *ss, bool enable_opus) { GstElement *udpsrc = NULL, *rtpbin = NULL, *depay = NULL, *appsink = NULL; - GstCaps *caps; + GstElement *resample = NULL, *opusdec = NULL; + GstCaps *caps, *sink_caps; GstPad *pad; GSocket *socket; GError *error = NULL; MAKE_ELEMENT(udpsrc, "udpsrc"); MAKE_ELEMENT(rtpbin, "rtpbin"); - MAKE_ELEMENT_NAMED(depay, "rtpL16depay", "depay"); + if (enable_opus) { + MAKE_ELEMENT_NAMED(depay, "rtpopusdepay", "depay"); + MAKE_ELEMENT(opusdec, "opusdec"); + MAKE_ELEMENT(resample, "audioresample"); + } else { + MAKE_ELEMENT_NAMED(depay, "rtpL16depay", "depay"); + } MAKE_ELEMENT(appsink, "appsink"); c->pipeline = gst_pipeline_new(NULL); gst_bin_add_many(GST_BIN(c->pipeline), udpsrc, rtpbin, depay, appsink, NULL); + if (enable_opus) + gst_bin_add_many(GST_BIN(c->pipeline), opusdec, resample, NULL); + socket = g_socket_new_from_fd(fd, &error); if (error) { pa_log("Could not create socket: %s", error->message); @@ -396,7 +452,7 @@ static bool init_receive_pipeline(pa_rtp_context *c, int fd, const pa_sample_spe goto fail; } - caps = rtp_caps_from_sample_spec(ss); + caps = rtp_caps_from_sample_spec(ss, enable_opus); if (!caps) { pa_log("Unsupported format to payload"); goto fail; @@ -406,14 +462,37 @@ static bool init_receive_pipeline(pa_rtp_context *c, int fd, const pa_sample_spe g_object_set(rtpbin, "latency", 0, "buffer-mode", 0 /* none */, NULL); g_object_set(appsink, "sync", FALSE, "enable-last-sample", FALSE, NULL); + if (enable_opus) { + sink_caps = gst_caps_new_simple("audio/x-raw", + "format", G_TYPE_STRING, "S16LE", + "layout", G_TYPE_STRING, "interleaved", + "clock-rate", G_TYPE_INT, (int) ss->rate, + "channels", G_TYPE_INT, (int) ss->channels, + NULL); + g_object_set(appsink, "caps", sink_caps, NULL); + g_object_set(opusdec, "plc", TRUE, NULL); + gst_caps_unref(sink_caps); + } + gst_caps_unref(caps); g_object_unref(socket); - if (!gst_element_link_pads(udpsrc, "src", rtpbin, "recv_rtp_sink_0") || - !gst_element_link(depay, appsink)) { + if (enable_opus) { + if (!gst_element_link_pads(udpsrc, "src", rtpbin, "recv_rtp_sink_0") || + !gst_element_link(depay, opusdec) || + !gst_element_link(opusdec, resample) || + !gst_element_link(resample, appsink)) { - pa_log("Could not set up receive pipeline"); - goto fail; + pa_log("Could not set up receive pipeline"); + goto fail; + } + } else { + if (!gst_element_link_pads(udpsrc, "src", rtpbin, "recv_rtp_sink_0") || + !gst_element_link(depay, appsink)) { + + pa_log("Could not set up receive pipeline"); + goto fail; + } } g_signal_connect(G_OBJECT(rtpbin), "pad-added", G_CALLBACK(on_pad_added), c); @@ -446,6 +525,10 @@ fail: gst_object_unref(depay); if (rtpbin) gst_object_unref(rtpbin); + if (opusdec) + gst_object_unref(opusdec); + if (resample) + gst_object_unref(resample); if (appsink) gst_object_unref(appsink); } @@ -469,7 +552,7 @@ static GstFlowReturn appsink_new_sample(GstAppSink *appsink, gpointer userdata) return GST_FLOW_OK; } -pa_rtp_context* pa_rtp_context_new_recv(int fd, uint8_t payload, const pa_sample_spec *ss) { +pa_rtp_context* pa_rtp_context_new_recv(int fd, uint8_t payload, const pa_sample_spec *ss, bool enable_opus) { pa_rtp_context *c = NULL; GstAppSinkCallbacks callbacks = { 0, }; GError *error = NULL; @@ -478,6 +561,9 @@ pa_rtp_context* pa_rtp_context_new_recv(int fd, uint8_t payload, const pa_sample pa_log_info("Initialising GStreamer RTP backend for receive"); + if (enable_opus) + pa_log_info("Using OPUS encoding for RTP recv"); + c = pa_xnew0(pa_rtp_context, 1); c->fdsem = pa_fdsem_new(); @@ -491,7 +577,7 @@ pa_rtp_context* pa_rtp_context_new_recv(int fd, uint8_t payload, const pa_sample goto fail; } - if (!init_receive_pipeline(c, fd, ss)) + if (!init_receive_pipeline(c, fd, ss, enable_opus)) goto fail; callbacks.eos = appsink_eos; diff --git a/src/modules/rtp/rtp-native.c b/src/modules/rtp/rtp-native.c index 01e668cc6..86760a627 100644 --- a/src/modules/rtp/rtp-native.c +++ b/src/modules/rtp/rtp-native.c @@ -58,7 +58,7 @@ typedef struct pa_rtp_context { pa_memchunk memchunk; } pa_rtp_context; -pa_rtp_context* pa_rtp_context_new_send(int fd, uint8_t payload, size_t mtu, const pa_sample_spec *ss) { +pa_rtp_context* pa_rtp_context_new_send(int fd, uint8_t payload, size_t mtu, const pa_sample_spec *ss, bool enable_opus) { pa_rtp_context *c; pa_assert(fd >= 0); @@ -171,7 +171,7 @@ int pa_rtp_send(pa_rtp_context *c, pa_memblockq *q) { return 0; } -pa_rtp_context* pa_rtp_context_new_recv(int fd, uint8_t payload, const pa_sample_spec *ss) { +pa_rtp_context* pa_rtp_context_new_recv(int fd, uint8_t payload, const pa_sample_spec *ss, bool enable_opus) { pa_rtp_context *c; pa_log_info("Initialising native RTP backend for receive"); diff --git a/src/modules/rtp/rtp.h b/src/modules/rtp/rtp.h index 372df75be..38e41e777 100644 --- a/src/modules/rtp/rtp.h +++ b/src/modules/rtp/rtp.h @@ -30,13 +30,13 @@ typedef struct pa_rtp_context pa_rtp_context; int pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint8_t payload, size_t mtu, size_t frame_size); -pa_rtp_context* pa_rtp_context_new_send(int fd, uint8_t payload, size_t mtu, const pa_sample_spec *ss); +pa_rtp_context* pa_rtp_context_new_send(int fd, uint8_t payload, size_t mtu, const pa_sample_spec *ss, bool enable_opus); /* If the memblockq doesn't have a silence memchunk set, then the caller must * guarantee that the current read index doesn't point to a hole. */ int pa_rtp_send(pa_rtp_context *c, pa_memblockq *q); -pa_rtp_context* pa_rtp_context_new_recv(int fd, uint8_t payload, const pa_sample_spec *ss); +pa_rtp_context* pa_rtp_context_new_recv(int fd, uint8_t payload, const pa_sample_spec *ss, bool enable_opus); int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool, uint32_t *rtp_tstamp, struct timeval *tstamp); void pa_rtp_context_free(pa_rtp_context *c); @@ -44,13 +44,13 @@ void pa_rtp_context_free(pa_rtp_context *c); size_t pa_rtp_context_get_frame_size(pa_rtp_context *c); pa_rtpoll_item* pa_rtp_context_get_rtpoll_item(pa_rtp_context *c, pa_rtpoll *rtpoll); -pa_sample_spec* pa_rtp_sample_spec_fixup(pa_sample_spec *ss); +pa_sample_spec* pa_rtp_sample_spec_fixup(pa_sample_spec *ss, bool enable_opus); int pa_rtp_sample_spec_valid(const pa_sample_spec *ss); uint8_t pa_rtp_payload_from_sample_spec(const pa_sample_spec *ss); pa_sample_spec *pa_rtp_sample_spec_from_payload(uint8_t payload, pa_sample_spec *ss); const char* pa_rtp_format_to_string(pa_sample_format_t f); -pa_sample_format_t pa_rtp_string_to_format(const char *s); +pa_sample_format_t pa_rtp_string_to_format(const char *s, bool enable_opus); #endif diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c index 6a2e0c964..e130509df 100644 --- a/src/modules/rtp/sdp.c +++ b/src/modules/rtp/sdp.c @@ -39,8 +39,9 @@ #include "sdp.h" #include "rtp.h" -char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss) { +char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss, bool enable_opus) { uint32_t ntp; + uint32_t rate, channels; char buf_src[64], buf_dst[64], un[64]; const char *u, *f; @@ -53,7 +54,15 @@ char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, u pa_assert(af == AF_INET); #endif - pa_assert_se(f = pa_rtp_format_to_string(ss->format)); + if (enable_opus) { + f = "OPUS"; + rate = 48000; + channels = 2; + } else { + pa_assert_se(f = pa_rtp_format_to_string(ss->format)); + rate = ss->rate; + channels = ss->channels; + } if (!(u = pa_get_user_name(un, sizeof(un)))) u = "-"; @@ -78,7 +87,7 @@ char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, u af == AF_INET ? "IP4" : "IP6", buf_dst, (unsigned long) ntp, port, payload, - payload, f, ss->rate, ss->channels); + payload, f, rate, channels); } static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) { @@ -89,6 +98,9 @@ static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) { if (pa_startswith(c, "L16/")) { ss->format = PA_SAMPLE_S16BE; c += 4; + } else if (pa_startswith(c, "OPUS/")) { + ss->format = PA_SAMPLE_S16LE; + c += 5; } else return NULL; @@ -218,6 +230,9 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) { if (parse_sdp_sample_spec(&i->sample_spec, c)) ss_valid = true; + + if (pa_startswith(c, "OPUS/")) + i->enable_opus = true; } } } diff --git a/src/modules/rtp/sdp.h b/src/modules/rtp/sdp.h index 5e9b8fec8..28c755a27 100644 --- a/src/modules/rtp/sdp.h +++ b/src/modules/rtp/sdp.h @@ -37,9 +37,11 @@ typedef struct pa_sdp_info { pa_sample_spec sample_spec; uint8_t payload; + + bool enable_opus; } pa_sdp_info; -char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss); +char *pa_sdp_build(int af, const void *src, const void *dst, const char *name, uint16_t port, uint8_t payload, const pa_sample_spec *ss, bool enable_opus); pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *info, int is_goodbye); |