153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2006 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <stdio.h>
2553a5a1b3Sopenharmony_ci#include <sys/socket.h>
2653a5a1b3Sopenharmony_ci#include <netinet/in.h>
2753a5a1b3Sopenharmony_ci#include <errno.h>
2853a5a1b3Sopenharmony_ci#include <unistd.h>
2953a5a1b3Sopenharmony_ci
3053a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
3153a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
3253a5a1b3Sopenharmony_ci#include <pulse/util.h>
3353a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3453a5a1b3Sopenharmony_ci
3553a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
3653a5a1b3Sopenharmony_ci#include <pulsecore/module.h>
3753a5a1b3Sopenharmony_ci#include <pulsecore/source.h>
3853a5a1b3Sopenharmony_ci#include <pulsecore/source-output.h>
3953a5a1b3Sopenharmony_ci#include <pulsecore/memblockq.h>
4053a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
4153a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
4253a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h>
4353a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h>
4453a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h>
4553a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
4653a5a1b3Sopenharmony_ci#include <pulsecore/socket-util.h>
4753a5a1b3Sopenharmony_ci#include <pulsecore/arpa-inet.h>
4853a5a1b3Sopenharmony_ci
4953a5a1b3Sopenharmony_ci#include "rtp.h"
5053a5a1b3Sopenharmony_ci#include "sdp.h"
5153a5a1b3Sopenharmony_ci#include "sap.h"
5253a5a1b3Sopenharmony_ci
5353a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering");
5453a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Read data from source and send it to the network via RTP/SAP/SDP");
5553a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
5653a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false);
5753a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
5853a5a1b3Sopenharmony_ci        "source=<name of the source> "
5953a5a1b3Sopenharmony_ci        "format=<sample format> "
6053a5a1b3Sopenharmony_ci        "channels=<number of channels> "
6153a5a1b3Sopenharmony_ci        "rate=<sample rate> "
6253a5a1b3Sopenharmony_ci        "destination_ip=<destination IP address> "
6353a5a1b3Sopenharmony_ci        "source_ip=<source IP address> "
6453a5a1b3Sopenharmony_ci        "port=<port number> "
6553a5a1b3Sopenharmony_ci        "mtu=<maximum transfer unit> "
6653a5a1b3Sopenharmony_ci        "loop=<loopback to local host?> "
6753a5a1b3Sopenharmony_ci        "ttl=<ttl value> "
6853a5a1b3Sopenharmony_ci        "inhibit_auto_suspend=<always|never|only_with_non_monitor_sources>"
6953a5a1b3Sopenharmony_ci        "stream_name=<name of the stream>"
7053a5a1b3Sopenharmony_ci        "enable_opus=<enable OPUS codec>"
7153a5a1b3Sopenharmony_ci);
7253a5a1b3Sopenharmony_ci
7353a5a1b3Sopenharmony_ci#define DEFAULT_PORT 46000
7453a5a1b3Sopenharmony_ci#define DEFAULT_TTL 1
7553a5a1b3Sopenharmony_ci#define SAP_PORT 9875
7653a5a1b3Sopenharmony_ci#define DEFAULT_SOURCE_IP "0.0.0.0"
7753a5a1b3Sopenharmony_ci#define DEFAULT_DESTINATION_IP "224.0.0.56"
7853a5a1b3Sopenharmony_ci#define MEMBLOCKQ_MAXLENGTH (1024*170)
7953a5a1b3Sopenharmony_ci#define DEFAULT_MTU 1280
8053a5a1b3Sopenharmony_ci#define SAP_INTERVAL (5*PA_USEC_PER_SEC)
8153a5a1b3Sopenharmony_ci
8253a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = {
8353a5a1b3Sopenharmony_ci    "source",
8453a5a1b3Sopenharmony_ci    "format",
8553a5a1b3Sopenharmony_ci    "channels",
8653a5a1b3Sopenharmony_ci    "rate",
8753a5a1b3Sopenharmony_ci    "destination", /* Compatbility */
8853a5a1b3Sopenharmony_ci    "destination_ip",
8953a5a1b3Sopenharmony_ci    "source_ip",
9053a5a1b3Sopenharmony_ci    "port",
9153a5a1b3Sopenharmony_ci    "mtu" ,
9253a5a1b3Sopenharmony_ci    "loop",
9353a5a1b3Sopenharmony_ci    "ttl",
9453a5a1b3Sopenharmony_ci    "inhibit_auto_suspend",
9553a5a1b3Sopenharmony_ci    "stream_name",
9653a5a1b3Sopenharmony_ci    "enable_opus",
9753a5a1b3Sopenharmony_ci    NULL
9853a5a1b3Sopenharmony_ci};
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_cienum inhibit_auto_suspend {
10153a5a1b3Sopenharmony_ci    INHIBIT_AUTO_SUSPEND_ALWAYS,
10253a5a1b3Sopenharmony_ci    INHIBIT_AUTO_SUSPEND_NEVER,
10353a5a1b3Sopenharmony_ci    INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES
10453a5a1b3Sopenharmony_ci};
10553a5a1b3Sopenharmony_ci
10653a5a1b3Sopenharmony_cistruct userdata {
10753a5a1b3Sopenharmony_ci    pa_module *module;
10853a5a1b3Sopenharmony_ci
10953a5a1b3Sopenharmony_ci    pa_source_output *source_output;
11053a5a1b3Sopenharmony_ci    pa_memblockq *memblockq;
11153a5a1b3Sopenharmony_ci
11253a5a1b3Sopenharmony_ci    pa_rtp_context *rtp_context;
11353a5a1b3Sopenharmony_ci    pa_sap_context sap_context;
11453a5a1b3Sopenharmony_ci
11553a5a1b3Sopenharmony_ci    pa_time_event *sap_event;
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_ci    enum inhibit_auto_suspend inhibit_auto_suspend;
11853a5a1b3Sopenharmony_ci};
11953a5a1b3Sopenharmony_ci
12053a5a1b3Sopenharmony_ci/* Called from I/O thread context */
12153a5a1b3Sopenharmony_cistatic int source_output_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
12253a5a1b3Sopenharmony_ci    struct userdata *u;
12353a5a1b3Sopenharmony_ci    pa_assert_se(u = PA_SOURCE_OUTPUT(o)->userdata);
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci    switch (code) {
12653a5a1b3Sopenharmony_ci        case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY:
12753a5a1b3Sopenharmony_ci            *((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->source_output->sample_spec);
12853a5a1b3Sopenharmony_ci
12953a5a1b3Sopenharmony_ci            /* Fall through, the default handler will add in the extra
13053a5a1b3Sopenharmony_ci             * latency added by the resampler */
13153a5a1b3Sopenharmony_ci            break;
13253a5a1b3Sopenharmony_ci    }
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    return pa_source_output_process_msg(o, code, data, offset, chunk);
13553a5a1b3Sopenharmony_ci}
13653a5a1b3Sopenharmony_ci
13753a5a1b3Sopenharmony_ci/* Called from I/O thread context */
13853a5a1b3Sopenharmony_cistatic void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
13953a5a1b3Sopenharmony_ci    struct userdata *u;
14053a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
14153a5a1b3Sopenharmony_ci    pa_assert_se(u = o->userdata);
14253a5a1b3Sopenharmony_ci
14353a5a1b3Sopenharmony_ci    if (pa_memblockq_push(u->memblockq, chunk) < 0) {
14453a5a1b3Sopenharmony_ci        pa_log_warn("Failed to push chunk into memblockq.");
14553a5a1b3Sopenharmony_ci        return;
14653a5a1b3Sopenharmony_ci    }
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_ci    pa_rtp_send(u->rtp_context, u->memblockq);
14953a5a1b3Sopenharmony_ci}
15053a5a1b3Sopenharmony_ci
15153a5a1b3Sopenharmony_cistatic pa_source_output_flags_t get_dont_inhibit_auto_suspend_flag(pa_source *source,
15253a5a1b3Sopenharmony_ci                                                                   enum inhibit_auto_suspend inhibit_auto_suspend) {
15353a5a1b3Sopenharmony_ci    pa_assert(source);
15453a5a1b3Sopenharmony_ci
15553a5a1b3Sopenharmony_ci    switch (inhibit_auto_suspend) {
15653a5a1b3Sopenharmony_ci        case INHIBIT_AUTO_SUSPEND_ALWAYS:
15753a5a1b3Sopenharmony_ci            return 0;
15853a5a1b3Sopenharmony_ci
15953a5a1b3Sopenharmony_ci        case INHIBIT_AUTO_SUSPEND_NEVER:
16053a5a1b3Sopenharmony_ci            return PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND;
16153a5a1b3Sopenharmony_ci
16253a5a1b3Sopenharmony_ci        case INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES:
16353a5a1b3Sopenharmony_ci            return source->monitor_of ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0;
16453a5a1b3Sopenharmony_ci    }
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci    pa_assert_not_reached();
16753a5a1b3Sopenharmony_ci}
16853a5a1b3Sopenharmony_ci
16953a5a1b3Sopenharmony_ci/* Called from the main thread. */
17053a5a1b3Sopenharmony_cistatic void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
17153a5a1b3Sopenharmony_ci    struct userdata *u;
17253a5a1b3Sopenharmony_ci
17353a5a1b3Sopenharmony_ci    pa_assert(o);
17453a5a1b3Sopenharmony_ci
17553a5a1b3Sopenharmony_ci    u = o->userdata;
17653a5a1b3Sopenharmony_ci
17753a5a1b3Sopenharmony_ci    if (!dest)
17853a5a1b3Sopenharmony_ci        return;
17953a5a1b3Sopenharmony_ci
18053a5a1b3Sopenharmony_ci    o->flags &= ~PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND;
18153a5a1b3Sopenharmony_ci    o->flags |= get_dont_inhibit_auto_suspend_flag(dest, u->inhibit_auto_suspend);
18253a5a1b3Sopenharmony_ci}
18353a5a1b3Sopenharmony_ci
18453a5a1b3Sopenharmony_ci/* Called from main context */
18553a5a1b3Sopenharmony_cistatic void source_output_kill_cb(pa_source_output* o) {
18653a5a1b3Sopenharmony_ci    struct userdata *u;
18753a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
18853a5a1b3Sopenharmony_ci    pa_assert_se(u = o->userdata);
18953a5a1b3Sopenharmony_ci
19053a5a1b3Sopenharmony_ci    pa_module_unload_request(u->module, true);
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_ci    pa_source_output_unlink(u->source_output);
19353a5a1b3Sopenharmony_ci    pa_source_output_unref(u->source_output);
19453a5a1b3Sopenharmony_ci    u->source_output = NULL;
19553a5a1b3Sopenharmony_ci}
19653a5a1b3Sopenharmony_ci
19753a5a1b3Sopenharmony_cistatic void sap_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
19853a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
19953a5a1b3Sopenharmony_ci
20053a5a1b3Sopenharmony_ci    pa_assert(m);
20153a5a1b3Sopenharmony_ci    pa_assert(t);
20253a5a1b3Sopenharmony_ci    pa_assert(u);
20353a5a1b3Sopenharmony_ci
20453a5a1b3Sopenharmony_ci    pa_sap_send(&u->sap_context, 0);
20553a5a1b3Sopenharmony_ci
20653a5a1b3Sopenharmony_ci    pa_core_rttime_restart(u->module->core, t, pa_rtclock_now() + SAP_INTERVAL);
20753a5a1b3Sopenharmony_ci}
20853a5a1b3Sopenharmony_ci
20953a5a1b3Sopenharmony_ciint pa__init(pa_module*m) {
21053a5a1b3Sopenharmony_ci    struct userdata *u;
21153a5a1b3Sopenharmony_ci    pa_modargs *ma = NULL;
21253a5a1b3Sopenharmony_ci    const char *dst_addr;
21353a5a1b3Sopenharmony_ci    const char *src_addr;
21453a5a1b3Sopenharmony_ci    uint32_t port = DEFAULT_PORT, mtu;
21553a5a1b3Sopenharmony_ci    uint32_t ttl = DEFAULT_TTL;
21653a5a1b3Sopenharmony_ci    sa_family_t af;
21753a5a1b3Sopenharmony_ci    int fd = -1, sap_fd = -1;
21853a5a1b3Sopenharmony_ci    pa_source *s;
21953a5a1b3Sopenharmony_ci    pa_sample_spec ss;
22053a5a1b3Sopenharmony_ci    pa_channel_map cm;
22153a5a1b3Sopenharmony_ci    struct sockaddr_in dst_sa4, dst_sap_sa4, src_sa4, src_sap_sa4;
22253a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
22353a5a1b3Sopenharmony_ci    struct sockaddr_in6 dst_sa6, dst_sap_sa6, src_sa6, src_sap_sa6;
22453a5a1b3Sopenharmony_ci#endif
22553a5a1b3Sopenharmony_ci    struct sockaddr_storage sa_dst;
22653a5a1b3Sopenharmony_ci    pa_source_output *o = NULL;
22753a5a1b3Sopenharmony_ci    uint8_t payload;
22853a5a1b3Sopenharmony_ci    char *p;
22953a5a1b3Sopenharmony_ci    int r, j;
23053a5a1b3Sopenharmony_ci    socklen_t k;
23153a5a1b3Sopenharmony_ci    char hn[128], *n;
23253a5a1b3Sopenharmony_ci    bool loop = false;
23353a5a1b3Sopenharmony_ci    bool enable_opus = false;
23453a5a1b3Sopenharmony_ci    enum inhibit_auto_suspend inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES;
23553a5a1b3Sopenharmony_ci    const char *inhibit_auto_suspend_str;
23653a5a1b3Sopenharmony_ci    pa_source_output_new_data data;
23753a5a1b3Sopenharmony_ci
23853a5a1b3Sopenharmony_ci    pa_assert(m);
23953a5a1b3Sopenharmony_ci
24053a5a1b3Sopenharmony_ci    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
24153a5a1b3Sopenharmony_ci        pa_log("Failed to parse module arguments");
24253a5a1b3Sopenharmony_ci        goto fail;
24353a5a1b3Sopenharmony_ci    }
24453a5a1b3Sopenharmony_ci
24553a5a1b3Sopenharmony_ci    if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) {
24653a5a1b3Sopenharmony_ci        pa_log("Source does not exist.");
24753a5a1b3Sopenharmony_ci        goto fail;
24853a5a1b3Sopenharmony_ci    }
24953a5a1b3Sopenharmony_ci
25053a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "loop", &loop) < 0) {
25153a5a1b3Sopenharmony_ci        pa_log("Failed to parse \"loop\" parameter.");
25253a5a1b3Sopenharmony_ci        goto fail;
25353a5a1b3Sopenharmony_ci    }
25453a5a1b3Sopenharmony_ci
25553a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "enable_opus", &enable_opus) < 0) {
25653a5a1b3Sopenharmony_ci        pa_log("Failed to parse \"use_opus\" parameter.");
25753a5a1b3Sopenharmony_ci        goto fail;
25853a5a1b3Sopenharmony_ci    }
25953a5a1b3Sopenharmony_ci
26053a5a1b3Sopenharmony_ci    if ((inhibit_auto_suspend_str = pa_modargs_get_value(ma, "inhibit_auto_suspend", NULL))) {
26153a5a1b3Sopenharmony_ci        if (pa_streq(inhibit_auto_suspend_str, "always"))
26253a5a1b3Sopenharmony_ci            inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ALWAYS;
26353a5a1b3Sopenharmony_ci        else if (pa_streq(inhibit_auto_suspend_str, "never"))
26453a5a1b3Sopenharmony_ci            inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_NEVER;
26553a5a1b3Sopenharmony_ci        else if (pa_streq(inhibit_auto_suspend_str, "only_with_non_monitor_sources"))
26653a5a1b3Sopenharmony_ci            inhibit_auto_suspend = INHIBIT_AUTO_SUSPEND_ONLY_WITH_NON_MONITOR_SOURCES;
26753a5a1b3Sopenharmony_ci        else {
26853a5a1b3Sopenharmony_ci            pa_log("Failed to parse the \"inhibit_auto_suspend\" parameter.");
26953a5a1b3Sopenharmony_ci            goto fail;
27053a5a1b3Sopenharmony_ci        }
27153a5a1b3Sopenharmony_ci    }
27253a5a1b3Sopenharmony_ci
27353a5a1b3Sopenharmony_ci    ss = s->sample_spec;
27453a5a1b3Sopenharmony_ci    pa_rtp_sample_spec_fixup(&ss, enable_opus);
27553a5a1b3Sopenharmony_ci    cm = s->channel_map;
27653a5a1b3Sopenharmony_ci    if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
27753a5a1b3Sopenharmony_ci        pa_log("Failed to parse sample specification");
27853a5a1b3Sopenharmony_ci        goto fail;
27953a5a1b3Sopenharmony_ci    }
28053a5a1b3Sopenharmony_ci
28153a5a1b3Sopenharmony_ci    if (!pa_rtp_sample_spec_valid(&ss)) {
28253a5a1b3Sopenharmony_ci        pa_log("Specified sample type not compatible with RTP");
28353a5a1b3Sopenharmony_ci        goto fail;
28453a5a1b3Sopenharmony_ci    }
28553a5a1b3Sopenharmony_ci
28653a5a1b3Sopenharmony_ci    if (enable_opus && ss.rate != 48000) {
28753a5a1b3Sopenharmony_ci        pa_log_warn("OPUS requires sample rate as 48 KHz. Setting rate=48000.");
28853a5a1b3Sopenharmony_ci        ss.rate = 48000;
28953a5a1b3Sopenharmony_ci    }
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_ci    if (ss.channels != cm.channels)
29253a5a1b3Sopenharmony_ci        pa_channel_map_init_auto(&cm, ss.channels, PA_CHANNEL_MAP_AIFF);
29353a5a1b3Sopenharmony_ci
29453a5a1b3Sopenharmony_ci    payload = pa_rtp_payload_from_sample_spec(&ss);
29553a5a1b3Sopenharmony_ci
29653a5a1b3Sopenharmony_ci    mtu = (uint32_t) pa_frame_align(DEFAULT_MTU, &ss);
29753a5a1b3Sopenharmony_ci
29853a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_u32(ma, "mtu", &mtu) < 0 || mtu < 1 || mtu % pa_frame_size(&ss) != 0) {
29953a5a1b3Sopenharmony_ci        pa_log("Invalid MTU.");
30053a5a1b3Sopenharmony_ci        goto fail;
30153a5a1b3Sopenharmony_ci    }
30253a5a1b3Sopenharmony_ci
30353a5a1b3Sopenharmony_ci    port = DEFAULT_PORT + ((uint32_t) (rand() % 512) << 1);
30453a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_u32(ma, "port", &port) < 0 || port < 1 || port > 0xFFFF) {
30553a5a1b3Sopenharmony_ci        pa_log("port= expects a numerical argument between 1 and 65535.");
30653a5a1b3Sopenharmony_ci        goto fail;
30753a5a1b3Sopenharmony_ci    }
30853a5a1b3Sopenharmony_ci
30953a5a1b3Sopenharmony_ci    if (port & 1)
31053a5a1b3Sopenharmony_ci        pa_log_warn("Port number not even as suggested in RFC3550!");
31153a5a1b3Sopenharmony_ci
31253a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_u32(ma, "ttl", &ttl) < 0 || ttl < 1 || ttl > 0xFF) {
31353a5a1b3Sopenharmony_ci        pa_log("ttl= expects a numerical argument between 1 and 255.");
31453a5a1b3Sopenharmony_ci        goto fail;
31553a5a1b3Sopenharmony_ci    }
31653a5a1b3Sopenharmony_ci
31753a5a1b3Sopenharmony_ci    src_addr = pa_modargs_get_value(ma, "source_ip", DEFAULT_SOURCE_IP);
31853a5a1b3Sopenharmony_ci
31953a5a1b3Sopenharmony_ci    if (inet_pton(AF_INET, src_addr, &src_sa4.sin_addr) > 0) {
32053a5a1b3Sopenharmony_ci        src_sa4.sin_family = af = AF_INET;
32153a5a1b3Sopenharmony_ci        src_sa4.sin_port = htons(0);
32253a5a1b3Sopenharmony_ci        memset(&src_sa4.sin_zero, 0, sizeof(src_sa4.sin_zero));
32353a5a1b3Sopenharmony_ci        src_sap_sa4 = src_sa4;
32453a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
32553a5a1b3Sopenharmony_ci    } else if (inet_pton(AF_INET6, src_addr, &src_sa6.sin6_addr) > 0) {
32653a5a1b3Sopenharmony_ci        src_sa6.sin6_family = af = AF_INET6;
32753a5a1b3Sopenharmony_ci        src_sa6.sin6_port = htons(0);
32853a5a1b3Sopenharmony_ci        src_sa6.sin6_flowinfo = 0;
32953a5a1b3Sopenharmony_ci        src_sa6.sin6_scope_id = 0;
33053a5a1b3Sopenharmony_ci        src_sap_sa6 = src_sa6;
33153a5a1b3Sopenharmony_ci#endif
33253a5a1b3Sopenharmony_ci    } else {
33353a5a1b3Sopenharmony_ci        pa_log("Invalid source address '%s'", src_addr);
33453a5a1b3Sopenharmony_ci        goto fail;
33553a5a1b3Sopenharmony_ci    }
33653a5a1b3Sopenharmony_ci
33753a5a1b3Sopenharmony_ci    dst_addr = pa_modargs_get_value(ma, "destination", NULL);
33853a5a1b3Sopenharmony_ci    if (dst_addr == NULL)
33953a5a1b3Sopenharmony_ci        dst_addr = pa_modargs_get_value(ma, "destination_ip", DEFAULT_DESTINATION_IP);
34053a5a1b3Sopenharmony_ci
34153a5a1b3Sopenharmony_ci    if (inet_pton(AF_INET, dst_addr, &dst_sa4.sin_addr) > 0) {
34253a5a1b3Sopenharmony_ci        dst_sa4.sin_family = af = AF_INET;
34353a5a1b3Sopenharmony_ci        dst_sa4.sin_port = htons((uint16_t) port);
34453a5a1b3Sopenharmony_ci        memset(&dst_sa4.sin_zero, 0, sizeof(dst_sa4.sin_zero));
34553a5a1b3Sopenharmony_ci        dst_sap_sa4 = dst_sa4;
34653a5a1b3Sopenharmony_ci        dst_sap_sa4.sin_port = htons(SAP_PORT);
34753a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
34853a5a1b3Sopenharmony_ci    } else if (inet_pton(AF_INET6, dst_addr, &dst_sa6.sin6_addr) > 0) {
34953a5a1b3Sopenharmony_ci        dst_sa6.sin6_family = af = AF_INET6;
35053a5a1b3Sopenharmony_ci        dst_sa6.sin6_port = htons((uint16_t) port);
35153a5a1b3Sopenharmony_ci        dst_sa6.sin6_flowinfo = 0;
35253a5a1b3Sopenharmony_ci        dst_sa6.sin6_scope_id = 0;
35353a5a1b3Sopenharmony_ci        dst_sap_sa6 = dst_sa6;
35453a5a1b3Sopenharmony_ci        dst_sap_sa6.sin6_port = htons(SAP_PORT);
35553a5a1b3Sopenharmony_ci#endif
35653a5a1b3Sopenharmony_ci    } else {
35753a5a1b3Sopenharmony_ci        pa_log("Invalid destination '%s'", dst_addr);
35853a5a1b3Sopenharmony_ci        goto fail;
35953a5a1b3Sopenharmony_ci    }
36053a5a1b3Sopenharmony_ci
36153a5a1b3Sopenharmony_ci    if ((fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
36253a5a1b3Sopenharmony_ci        pa_log("socket() failed: %s", pa_cstrerror(errno));
36353a5a1b3Sopenharmony_ci        goto fail;
36453a5a1b3Sopenharmony_ci    }
36553a5a1b3Sopenharmony_ci
36653a5a1b3Sopenharmony_ci    if (af == AF_INET && bind(fd, (struct sockaddr*) &src_sa4, sizeof(src_sa4)) < 0) {
36753a5a1b3Sopenharmony_ci        pa_log("bind() failed: %s", pa_cstrerror(errno));
36853a5a1b3Sopenharmony_ci        goto fail;
36953a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
37053a5a1b3Sopenharmony_ci    } else if (af == AF_INET6 && bind(fd, (struct sockaddr*) &src_sa6, sizeof(src_sa6)) < 0) {
37153a5a1b3Sopenharmony_ci        pa_log("bind() failed: %s", pa_cstrerror(errno));
37253a5a1b3Sopenharmony_ci        goto fail;
37353a5a1b3Sopenharmony_ci#endif
37453a5a1b3Sopenharmony_ci    }
37553a5a1b3Sopenharmony_ci
37653a5a1b3Sopenharmony_ci    if (af == AF_INET && connect(fd, (struct sockaddr*) &dst_sa4, sizeof(dst_sa4)) < 0) {
37753a5a1b3Sopenharmony_ci        pa_log("connect() failed: %s", pa_cstrerror(errno));
37853a5a1b3Sopenharmony_ci        goto fail;
37953a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
38053a5a1b3Sopenharmony_ci    } else if (af == AF_INET6 && connect(fd, (struct sockaddr*) &dst_sa6, sizeof(dst_sa6)) < 0) {
38153a5a1b3Sopenharmony_ci        pa_log("connect() failed: %s", pa_cstrerror(errno));
38253a5a1b3Sopenharmony_ci        goto fail;
38353a5a1b3Sopenharmony_ci#endif
38453a5a1b3Sopenharmony_ci    }
38553a5a1b3Sopenharmony_ci
38653a5a1b3Sopenharmony_ci    if ((sap_fd = pa_socket_cloexec(af, SOCK_DGRAM, 0)) < 0) {
38753a5a1b3Sopenharmony_ci        pa_log("socket() failed: %s", pa_cstrerror(errno));
38853a5a1b3Sopenharmony_ci        goto fail;
38953a5a1b3Sopenharmony_ci    }
39053a5a1b3Sopenharmony_ci
39153a5a1b3Sopenharmony_ci    if (af == AF_INET && bind(sap_fd, (struct sockaddr*) &src_sap_sa4, sizeof(src_sap_sa4)) < 0) {
39253a5a1b3Sopenharmony_ci        pa_log("bind() failed: %s", pa_cstrerror(errno));
39353a5a1b3Sopenharmony_ci        goto fail;
39453a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
39553a5a1b3Sopenharmony_ci    } else if (af == AF_INET6 && bind(sap_fd, (struct sockaddr*) &src_sap_sa6, sizeof(src_sap_sa6)) < 0) {
39653a5a1b3Sopenharmony_ci        pa_log("bind() failed: %s", pa_cstrerror(errno));
39753a5a1b3Sopenharmony_ci        goto fail;
39853a5a1b3Sopenharmony_ci#endif
39953a5a1b3Sopenharmony_ci    }
40053a5a1b3Sopenharmony_ci
40153a5a1b3Sopenharmony_ci    if (af == AF_INET && connect(sap_fd, (struct sockaddr*) &dst_sap_sa4, sizeof(dst_sap_sa4)) < 0) {
40253a5a1b3Sopenharmony_ci        pa_log("connect() failed: %s", pa_cstrerror(errno));
40353a5a1b3Sopenharmony_ci        goto fail;
40453a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
40553a5a1b3Sopenharmony_ci    } else if (af == AF_INET6 && connect(sap_fd, (struct sockaddr*) &dst_sap_sa6, sizeof(dst_sap_sa6)) < 0) {
40653a5a1b3Sopenharmony_ci        pa_log("connect() failed: %s", pa_cstrerror(errno));
40753a5a1b3Sopenharmony_ci        goto fail;
40853a5a1b3Sopenharmony_ci#endif
40953a5a1b3Sopenharmony_ci    }
41053a5a1b3Sopenharmony_ci
41153a5a1b3Sopenharmony_ci    j = loop;
41253a5a1b3Sopenharmony_ci    if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0 ||
41353a5a1b3Sopenharmony_ci        setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &j, sizeof(j)) < 0) {
41453a5a1b3Sopenharmony_ci        pa_log("IP_MULTICAST_LOOP failed: %s", pa_cstrerror(errno));
41553a5a1b3Sopenharmony_ci        goto fail;
41653a5a1b3Sopenharmony_ci    }
41753a5a1b3Sopenharmony_ci
41853a5a1b3Sopenharmony_ci    if (ttl != DEFAULT_TTL) {
41953a5a1b3Sopenharmony_ci        int _ttl = (int) ttl;
42053a5a1b3Sopenharmony_ci
42153a5a1b3Sopenharmony_ci        if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) {
42253a5a1b3Sopenharmony_ci            pa_log("IP_MULTICAST_TTL failed: %s", pa_cstrerror(errno));
42353a5a1b3Sopenharmony_ci            goto fail;
42453a5a1b3Sopenharmony_ci        }
42553a5a1b3Sopenharmony_ci
42653a5a1b3Sopenharmony_ci        if (setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) {
42753a5a1b3Sopenharmony_ci            pa_log("IP_MULTICAST_TTL (sap) failed: %s", pa_cstrerror(errno));
42853a5a1b3Sopenharmony_ci            goto fail;
42953a5a1b3Sopenharmony_ci        }
43053a5a1b3Sopenharmony_ci    }
43153a5a1b3Sopenharmony_ci
43253a5a1b3Sopenharmony_ci    /* If the socket queue is full, let's drop packets */
43353a5a1b3Sopenharmony_ci    pa_make_fd_nonblock(fd);
43453a5a1b3Sopenharmony_ci    pa_make_udp_socket_low_delay(fd);
43553a5a1b3Sopenharmony_ci
43653a5a1b3Sopenharmony_ci    pa_source_output_new_data_init(&data);
43753a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "RTP Monitor Stream");
43853a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, "rtp.source", src_addr);
43953a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, "rtp.destination", dst_addr);
44053a5a1b3Sopenharmony_ci    pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu);
44153a5a1b3Sopenharmony_ci    pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port);
44253a5a1b3Sopenharmony_ci    pa_proplist_setf(data.proplist, "rtp.ttl", "%lu", (unsigned long) ttl);
44353a5a1b3Sopenharmony_ci    data.driver = __FILE__;
44453a5a1b3Sopenharmony_ci    data.module = m;
44553a5a1b3Sopenharmony_ci    pa_source_output_new_data_set_source(&data, s, false, true);
44653a5a1b3Sopenharmony_ci    pa_source_output_new_data_set_sample_spec(&data, &ss);
44753a5a1b3Sopenharmony_ci    pa_source_output_new_data_set_channel_map(&data, &cm);
44853a5a1b3Sopenharmony_ci    data.flags |= get_dont_inhibit_auto_suspend_flag(s, inhibit_auto_suspend);
44953a5a1b3Sopenharmony_ci
45053a5a1b3Sopenharmony_ci    pa_source_output_new(&o, m->core, &data);
45153a5a1b3Sopenharmony_ci    pa_source_output_new_data_done(&data);
45253a5a1b3Sopenharmony_ci
45353a5a1b3Sopenharmony_ci    if (!o) {
45453a5a1b3Sopenharmony_ci        pa_log("failed to create source output.");
45553a5a1b3Sopenharmony_ci        goto fail;
45653a5a1b3Sopenharmony_ci    }
45753a5a1b3Sopenharmony_ci
45853a5a1b3Sopenharmony_ci    o->parent.process_msg = source_output_process_msg;
45953a5a1b3Sopenharmony_ci    o->push = source_output_push_cb;
46053a5a1b3Sopenharmony_ci    o->moving = source_output_moving_cb;
46153a5a1b3Sopenharmony_ci    o->kill = source_output_kill_cb;
46253a5a1b3Sopenharmony_ci
46353a5a1b3Sopenharmony_ci    pa_log_info("Configured source latency of %llu ms.",
46453a5a1b3Sopenharmony_ci                (unsigned long long) pa_source_output_set_requested_latency(o, pa_bytes_to_usec(mtu, &o->sample_spec)) / PA_USEC_PER_MSEC);
46553a5a1b3Sopenharmony_ci
46653a5a1b3Sopenharmony_ci    m->userdata = o->userdata = u = pa_xnew(struct userdata, 1);
46753a5a1b3Sopenharmony_ci    u->module = m;
46853a5a1b3Sopenharmony_ci    u->source_output = o;
46953a5a1b3Sopenharmony_ci
47053a5a1b3Sopenharmony_ci    u->memblockq = pa_memblockq_new(
47153a5a1b3Sopenharmony_ci            "module-rtp-send memblockq",
47253a5a1b3Sopenharmony_ci            0,
47353a5a1b3Sopenharmony_ci            MEMBLOCKQ_MAXLENGTH,
47453a5a1b3Sopenharmony_ci            MEMBLOCKQ_MAXLENGTH,
47553a5a1b3Sopenharmony_ci            &ss,
47653a5a1b3Sopenharmony_ci            1,
47753a5a1b3Sopenharmony_ci            0,
47853a5a1b3Sopenharmony_ci            0,
47953a5a1b3Sopenharmony_ci            NULL);
48053a5a1b3Sopenharmony_ci
48153a5a1b3Sopenharmony_ci    k = sizeof(sa_dst);
48253a5a1b3Sopenharmony_ci    pa_assert_se((r = getsockname(fd, (struct sockaddr*) &sa_dst, &k)) >= 0);
48353a5a1b3Sopenharmony_ci
48453a5a1b3Sopenharmony_ci    n = pa_xstrdup(pa_modargs_get_value(ma, "stream_name", NULL));
48553a5a1b3Sopenharmony_ci    if (n == NULL)
48653a5a1b3Sopenharmony_ci        n = pa_sprintf_malloc("PulseAudio RTP Stream on %s", pa_get_fqdn(hn, sizeof(hn)));
48753a5a1b3Sopenharmony_ci
48853a5a1b3Sopenharmony_ci    if (af == AF_INET) {
48953a5a1b3Sopenharmony_ci        p = pa_sdp_build(af,
49053a5a1b3Sopenharmony_ci                     (void*) &((struct sockaddr_in*) &sa_dst)->sin_addr,
49153a5a1b3Sopenharmony_ci                     (void*) &dst_sa4.sin_addr,
49253a5a1b3Sopenharmony_ci                     n, (uint16_t) port, payload, &ss, enable_opus);
49353a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
49453a5a1b3Sopenharmony_ci    } else {
49553a5a1b3Sopenharmony_ci        p = pa_sdp_build(af,
49653a5a1b3Sopenharmony_ci                     (void*) &((struct sockaddr_in6*) &sa_dst)->sin6_addr,
49753a5a1b3Sopenharmony_ci                     (void*) &dst_sa6.sin6_addr,
49853a5a1b3Sopenharmony_ci                     n, (uint16_t) port, payload, &ss, enable_opus);
49953a5a1b3Sopenharmony_ci#endif
50053a5a1b3Sopenharmony_ci    }
50153a5a1b3Sopenharmony_ci
50253a5a1b3Sopenharmony_ci    pa_xfree(n);
50353a5a1b3Sopenharmony_ci
50453a5a1b3Sopenharmony_ci    if (!(u->rtp_context = pa_rtp_context_new_send(fd, payload, mtu, &ss, enable_opus)))
50553a5a1b3Sopenharmony_ci        goto fail;
50653a5a1b3Sopenharmony_ci    pa_sap_context_init_send(&u->sap_context, sap_fd, p);
50753a5a1b3Sopenharmony_ci
50853a5a1b3Sopenharmony_ci    pa_log_info("RTP stream initialized with mtu %u on %s:%u from %s ttl=%u, payload=%u",
50953a5a1b3Sopenharmony_ci            mtu, dst_addr, port, src_addr, ttl, payload);
51053a5a1b3Sopenharmony_ci    pa_log_info("SDP-Data:\n%s\nEOF", p);
51153a5a1b3Sopenharmony_ci
51253a5a1b3Sopenharmony_ci    pa_sap_send(&u->sap_context, 0);
51353a5a1b3Sopenharmony_ci
51453a5a1b3Sopenharmony_ci    u->sap_event = pa_core_rttime_new(m->core, pa_rtclock_now() + SAP_INTERVAL, sap_event_cb, u);
51553a5a1b3Sopenharmony_ci    u->inhibit_auto_suspend = inhibit_auto_suspend;
51653a5a1b3Sopenharmony_ci
51753a5a1b3Sopenharmony_ci    pa_source_output_put(u->source_output);
51853a5a1b3Sopenharmony_ci
51953a5a1b3Sopenharmony_ci    pa_modargs_free(ma);
52053a5a1b3Sopenharmony_ci
52153a5a1b3Sopenharmony_ci    return 0;
52253a5a1b3Sopenharmony_ci
52353a5a1b3Sopenharmony_cifail:
52453a5a1b3Sopenharmony_ci    if (ma)
52553a5a1b3Sopenharmony_ci        pa_modargs_free(ma);
52653a5a1b3Sopenharmony_ci
52753a5a1b3Sopenharmony_ci    if (fd >= 0)
52853a5a1b3Sopenharmony_ci        pa_close(fd);
52953a5a1b3Sopenharmony_ci
53053a5a1b3Sopenharmony_ci    if (sap_fd >= 0)
53153a5a1b3Sopenharmony_ci        pa_close(sap_fd);
53253a5a1b3Sopenharmony_ci
53353a5a1b3Sopenharmony_ci    return -1;
53453a5a1b3Sopenharmony_ci}
53553a5a1b3Sopenharmony_ci
53653a5a1b3Sopenharmony_civoid pa__done(pa_module*m) {
53753a5a1b3Sopenharmony_ci    struct userdata *u;
53853a5a1b3Sopenharmony_ci    pa_assert(m);
53953a5a1b3Sopenharmony_ci
54053a5a1b3Sopenharmony_ci    if (!(u = m->userdata))
54153a5a1b3Sopenharmony_ci        return;
54253a5a1b3Sopenharmony_ci
54353a5a1b3Sopenharmony_ci    if (u->sap_event)
54453a5a1b3Sopenharmony_ci        m->core->mainloop->time_free(u->sap_event);
54553a5a1b3Sopenharmony_ci
54653a5a1b3Sopenharmony_ci    if (u->source_output) {
54753a5a1b3Sopenharmony_ci        pa_source_output_unlink(u->source_output);
54853a5a1b3Sopenharmony_ci        pa_source_output_unref(u->source_output);
54953a5a1b3Sopenharmony_ci    }
55053a5a1b3Sopenharmony_ci
55153a5a1b3Sopenharmony_ci    pa_rtp_context_free(u->rtp_context);
55253a5a1b3Sopenharmony_ci
55353a5a1b3Sopenharmony_ci    pa_sap_send(&u->sap_context, 1);
55453a5a1b3Sopenharmony_ci    pa_sap_context_destroy(&u->sap_context);
55553a5a1b3Sopenharmony_ci
55653a5a1b3Sopenharmony_ci    if (u->memblockq)
55753a5a1b3Sopenharmony_ci        pa_memblockq_free(u->memblockq);
55853a5a1b3Sopenharmony_ci
55953a5a1b3Sopenharmony_ci    pa_xfree(u);
56053a5a1b3Sopenharmony_ci}
561