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