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 <time.h> 2553a5a1b3Sopenharmony_ci#include <stdlib.h> 2653a5a1b3Sopenharmony_ci#include <sys/types.h> 2753a5a1b3Sopenharmony_ci#include <sys/socket.h> 2853a5a1b3Sopenharmony_ci#include <netinet/in.h> 2953a5a1b3Sopenharmony_ci#include <string.h> 3053a5a1b3Sopenharmony_ci 3153a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3253a5a1b3Sopenharmony_ci#include <pulse/util.h> 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3553a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3653a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 3753a5a1b3Sopenharmony_ci#include <pulsecore/arpa-inet.h> 3853a5a1b3Sopenharmony_ci 3953a5a1b3Sopenharmony_ci#include "sdp.h" 4053a5a1b3Sopenharmony_ci#include "rtp.h" 4153a5a1b3Sopenharmony_ci 4253a5a1b3Sopenharmony_cichar *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) { 4353a5a1b3Sopenharmony_ci uint32_t ntp; 4453a5a1b3Sopenharmony_ci uint32_t rate, channels; 4553a5a1b3Sopenharmony_ci char buf_src[64], buf_dst[64], un[64]; 4653a5a1b3Sopenharmony_ci const char *u, *f; 4753a5a1b3Sopenharmony_ci 4853a5a1b3Sopenharmony_ci pa_assert(src); 4953a5a1b3Sopenharmony_ci pa_assert(dst); 5053a5a1b3Sopenharmony_ci 5153a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6 5253a5a1b3Sopenharmony_ci pa_assert(af == AF_INET || af == AF_INET6); 5353a5a1b3Sopenharmony_ci#else 5453a5a1b3Sopenharmony_ci pa_assert(af == AF_INET); 5553a5a1b3Sopenharmony_ci#endif 5653a5a1b3Sopenharmony_ci 5753a5a1b3Sopenharmony_ci if (enable_opus) { 5853a5a1b3Sopenharmony_ci f = "OPUS"; 5953a5a1b3Sopenharmony_ci rate = 48000; 6053a5a1b3Sopenharmony_ci channels = 2; 6153a5a1b3Sopenharmony_ci } else { 6253a5a1b3Sopenharmony_ci pa_assert_se(f = pa_rtp_format_to_string(ss->format)); 6353a5a1b3Sopenharmony_ci rate = ss->rate; 6453a5a1b3Sopenharmony_ci channels = ss->channels; 6553a5a1b3Sopenharmony_ci } 6653a5a1b3Sopenharmony_ci 6753a5a1b3Sopenharmony_ci if (!(u = pa_get_user_name(un, sizeof(un)))) 6853a5a1b3Sopenharmony_ci u = "-"; 6953a5a1b3Sopenharmony_ci 7053a5a1b3Sopenharmony_ci ntp = (uint32_t) time(NULL) + 2208988800U; 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_ci pa_assert_se(inet_ntop(af, src, buf_src, sizeof(buf_src))); 7353a5a1b3Sopenharmony_ci pa_assert_se(inet_ntop(af, dst, buf_dst, sizeof(buf_dst))); 7453a5a1b3Sopenharmony_ci 7553a5a1b3Sopenharmony_ci return pa_sprintf_malloc( 7653a5a1b3Sopenharmony_ci PA_SDP_HEADER 7753a5a1b3Sopenharmony_ci "o=%s %lu 0 IN %s %s\n" 7853a5a1b3Sopenharmony_ci "s=%s\n" 7953a5a1b3Sopenharmony_ci "c=IN %s %s\n" 8053a5a1b3Sopenharmony_ci "t=%lu 0\n" 8153a5a1b3Sopenharmony_ci "a=recvonly\n" 8253a5a1b3Sopenharmony_ci "m=audio %u RTP/AVP %i\n" 8353a5a1b3Sopenharmony_ci "a=rtpmap:%i %s/%u/%u\n" 8453a5a1b3Sopenharmony_ci "a=type:broadcast\n", 8553a5a1b3Sopenharmony_ci u, (unsigned long) ntp, af == AF_INET ? "IP4" : "IP6", buf_src, 8653a5a1b3Sopenharmony_ci name, 8753a5a1b3Sopenharmony_ci af == AF_INET ? "IP4" : "IP6", buf_dst, 8853a5a1b3Sopenharmony_ci (unsigned long) ntp, 8953a5a1b3Sopenharmony_ci port, payload, 9053a5a1b3Sopenharmony_ci payload, f, rate, channels); 9153a5a1b3Sopenharmony_ci} 9253a5a1b3Sopenharmony_ci 9353a5a1b3Sopenharmony_cistatic pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) { 9453a5a1b3Sopenharmony_ci unsigned rate, channels; 9553a5a1b3Sopenharmony_ci pa_assert(ss); 9653a5a1b3Sopenharmony_ci pa_assert(c); 9753a5a1b3Sopenharmony_ci 9853a5a1b3Sopenharmony_ci if (pa_startswith(c, "L16/")) { 9953a5a1b3Sopenharmony_ci ss->format = PA_SAMPLE_S16BE; 10053a5a1b3Sopenharmony_ci c += 4; 10153a5a1b3Sopenharmony_ci } else if (pa_startswith(c, "OPUS/")) { 10253a5a1b3Sopenharmony_ci ss->format = PA_SAMPLE_S16LE; 10353a5a1b3Sopenharmony_ci c += 5; 10453a5a1b3Sopenharmony_ci } else 10553a5a1b3Sopenharmony_ci return NULL; 10653a5a1b3Sopenharmony_ci 10753a5a1b3Sopenharmony_ci if (sscanf(c, "%u/%u", &rate, &channels) == 2) { 10853a5a1b3Sopenharmony_ci ss->rate = (uint32_t) rate; 10953a5a1b3Sopenharmony_ci ss->channels = (uint8_t) channels; 11053a5a1b3Sopenharmony_ci } else if (sscanf(c, "%u", &rate) == 2) { 11153a5a1b3Sopenharmony_ci ss->rate = (uint32_t) rate; 11253a5a1b3Sopenharmony_ci ss->channels = 1; 11353a5a1b3Sopenharmony_ci } else 11453a5a1b3Sopenharmony_ci return NULL; 11553a5a1b3Sopenharmony_ci 11653a5a1b3Sopenharmony_ci if (!pa_sample_spec_valid(ss)) 11753a5a1b3Sopenharmony_ci return NULL; 11853a5a1b3Sopenharmony_ci 11953a5a1b3Sopenharmony_ci return ss; 12053a5a1b3Sopenharmony_ci} 12153a5a1b3Sopenharmony_ci 12253a5a1b3Sopenharmony_cipa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) { 12353a5a1b3Sopenharmony_ci uint16_t port = 0; 12453a5a1b3Sopenharmony_ci bool ss_valid = false; 12553a5a1b3Sopenharmony_ci 12653a5a1b3Sopenharmony_ci pa_assert(t); 12753a5a1b3Sopenharmony_ci pa_assert(i); 12853a5a1b3Sopenharmony_ci 12953a5a1b3Sopenharmony_ci i->origin = i->session_name = NULL; 13053a5a1b3Sopenharmony_ci i->salen = 0; 13153a5a1b3Sopenharmony_ci i->payload = 255; 13253a5a1b3Sopenharmony_ci i->enable_opus = false; 13353a5a1b3Sopenharmony_ci 13453a5a1b3Sopenharmony_ci if (!pa_startswith(t, PA_SDP_HEADER)) { 13553a5a1b3Sopenharmony_ci pa_log("Failed to parse SDP data: invalid header."); 13653a5a1b3Sopenharmony_ci goto fail; 13753a5a1b3Sopenharmony_ci } 13853a5a1b3Sopenharmony_ci 13953a5a1b3Sopenharmony_ci t += sizeof(PA_SDP_HEADER)-1; 14053a5a1b3Sopenharmony_ci 14153a5a1b3Sopenharmony_ci while (*t) { 14253a5a1b3Sopenharmony_ci size_t l; 14353a5a1b3Sopenharmony_ci 14453a5a1b3Sopenharmony_ci l = strcspn(t, "\n"); 14553a5a1b3Sopenharmony_ci 14653a5a1b3Sopenharmony_ci if (l <= 2) { 14753a5a1b3Sopenharmony_ci pa_log("Failed to parse SDP data: line too short: >%s<.", t); 14853a5a1b3Sopenharmony_ci goto fail; 14953a5a1b3Sopenharmony_ci } 15053a5a1b3Sopenharmony_ci 15153a5a1b3Sopenharmony_ci if (pa_startswith(t, "o=")) 15253a5a1b3Sopenharmony_ci i->origin = pa_xstrndup(t+2, l-2); 15353a5a1b3Sopenharmony_ci else if (pa_startswith(t, "s=")) 15453a5a1b3Sopenharmony_ci i->session_name = pa_xstrndup(t+2, l-2); 15553a5a1b3Sopenharmony_ci else if (pa_startswith(t, "c=IN IP4 ")) { 15653a5a1b3Sopenharmony_ci char a[64]; 15753a5a1b3Sopenharmony_ci size_t k; 15853a5a1b3Sopenharmony_ci 15953a5a1b3Sopenharmony_ci k = l-8 > sizeof(a) ? sizeof(a) : l-8; 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci pa_strlcpy(a, t+9, k); 16253a5a1b3Sopenharmony_ci a[strcspn(a, "/")] = 0; 16353a5a1b3Sopenharmony_ci 16453a5a1b3Sopenharmony_ci if (inet_pton(AF_INET, a, &((struct sockaddr_in*) &i->sa)->sin_addr) <= 0) { 16553a5a1b3Sopenharmony_ci pa_log("Failed to parse SDP data: bad address: >%s<.", a); 16653a5a1b3Sopenharmony_ci goto fail; 16753a5a1b3Sopenharmony_ci } 16853a5a1b3Sopenharmony_ci 16953a5a1b3Sopenharmony_ci ((struct sockaddr_in*) &i->sa)->sin_family = AF_INET; 17053a5a1b3Sopenharmony_ci ((struct sockaddr_in*) &i->sa)->sin_port = 0; 17153a5a1b3Sopenharmony_ci i->salen = sizeof(struct sockaddr_in); 17253a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6 17353a5a1b3Sopenharmony_ci } else if (pa_startswith(t, "c=IN IP6 ")) { 17453a5a1b3Sopenharmony_ci char a[64]; 17553a5a1b3Sopenharmony_ci size_t k; 17653a5a1b3Sopenharmony_ci 17753a5a1b3Sopenharmony_ci k = l-8 > sizeof(a) ? sizeof(a) : l-8; 17853a5a1b3Sopenharmony_ci 17953a5a1b3Sopenharmony_ci pa_strlcpy(a, t+9, k); 18053a5a1b3Sopenharmony_ci a[strcspn(a, "/")] = 0; 18153a5a1b3Sopenharmony_ci 18253a5a1b3Sopenharmony_ci if (inet_pton(AF_INET6, a, &((struct sockaddr_in6*) &i->sa)->sin6_addr) <= 0) { 18353a5a1b3Sopenharmony_ci pa_log("Failed to parse SDP data: bad address: >%s<.", a); 18453a5a1b3Sopenharmony_ci goto fail; 18553a5a1b3Sopenharmony_ci } 18653a5a1b3Sopenharmony_ci 18753a5a1b3Sopenharmony_ci ((struct sockaddr_in6*) &i->sa)->sin6_family = AF_INET6; 18853a5a1b3Sopenharmony_ci ((struct sockaddr_in6*) &i->sa)->sin6_port = 0; 18953a5a1b3Sopenharmony_ci i->salen = sizeof(struct sockaddr_in6); 19053a5a1b3Sopenharmony_ci#endif 19153a5a1b3Sopenharmony_ci } else if (pa_startswith(t, "m=audio ")) { 19253a5a1b3Sopenharmony_ci 19353a5a1b3Sopenharmony_ci if (i->payload > 127) { 19453a5a1b3Sopenharmony_ci int _port, _payload; 19553a5a1b3Sopenharmony_ci 19653a5a1b3Sopenharmony_ci if (sscanf(t+8, "%i RTP/AVP %i", &_port, &_payload) == 2) { 19753a5a1b3Sopenharmony_ci 19853a5a1b3Sopenharmony_ci if (_port <= 0 || _port > 0xFFFF) { 19953a5a1b3Sopenharmony_ci pa_log("Failed to parse SDP data: invalid port %i.", _port); 20053a5a1b3Sopenharmony_ci goto fail; 20153a5a1b3Sopenharmony_ci } 20253a5a1b3Sopenharmony_ci 20353a5a1b3Sopenharmony_ci if (_payload < 0 || _payload > 127) { 20453a5a1b3Sopenharmony_ci pa_log("Failed to parse SDP data: invalid payload %i.", _payload); 20553a5a1b3Sopenharmony_ci goto fail; 20653a5a1b3Sopenharmony_ci } 20753a5a1b3Sopenharmony_ci 20853a5a1b3Sopenharmony_ci port = (uint16_t) _port; 20953a5a1b3Sopenharmony_ci i->payload = (uint8_t) _payload; 21053a5a1b3Sopenharmony_ci 21153a5a1b3Sopenharmony_ci if (pa_rtp_sample_spec_from_payload(i->payload, &i->sample_spec)) 21253a5a1b3Sopenharmony_ci ss_valid = true; 21353a5a1b3Sopenharmony_ci } 21453a5a1b3Sopenharmony_ci } 21553a5a1b3Sopenharmony_ci } else if (pa_startswith(t, "a=rtpmap:")) { 21653a5a1b3Sopenharmony_ci 21753a5a1b3Sopenharmony_ci if (i->payload <= 127) { 21853a5a1b3Sopenharmony_ci char c[64]; 21953a5a1b3Sopenharmony_ci int _payload; 22053a5a1b3Sopenharmony_ci int len; 22153a5a1b3Sopenharmony_ci 22253a5a1b3Sopenharmony_ci if (sscanf(t + 9, "%i %n", &_payload, &len) == 1) { 22353a5a1b3Sopenharmony_ci if (_payload < 0 || _payload > 127) { 22453a5a1b3Sopenharmony_ci pa_log("Failed to parse SDP data: invalid payload %i.", _payload); 22553a5a1b3Sopenharmony_ci goto fail; 22653a5a1b3Sopenharmony_ci } 22753a5a1b3Sopenharmony_ci if (_payload == i->payload) { 22853a5a1b3Sopenharmony_ci strncpy(c, t + 9 + len, 63); 22953a5a1b3Sopenharmony_ci c[63] = 0; 23053a5a1b3Sopenharmony_ci c[strcspn(c, "\n")] = 0; 23153a5a1b3Sopenharmony_ci 23253a5a1b3Sopenharmony_ci if (parse_sdp_sample_spec(&i->sample_spec, c)) 23353a5a1b3Sopenharmony_ci ss_valid = true; 23453a5a1b3Sopenharmony_ci 23553a5a1b3Sopenharmony_ci if (pa_startswith(c, "OPUS/")) 23653a5a1b3Sopenharmony_ci i->enable_opus = true; 23753a5a1b3Sopenharmony_ci } 23853a5a1b3Sopenharmony_ci } 23953a5a1b3Sopenharmony_ci } 24053a5a1b3Sopenharmony_ci } 24153a5a1b3Sopenharmony_ci 24253a5a1b3Sopenharmony_ci t += l; 24353a5a1b3Sopenharmony_ci 24453a5a1b3Sopenharmony_ci if (*t == '\n') 24553a5a1b3Sopenharmony_ci t++; 24653a5a1b3Sopenharmony_ci } 24753a5a1b3Sopenharmony_ci 24853a5a1b3Sopenharmony_ci if (!i->origin || (!is_goodbye && (!i->salen || i->payload > 127 || !ss_valid || port == 0))) { 24953a5a1b3Sopenharmony_ci pa_log("Failed to parse SDP data: missing data."); 25053a5a1b3Sopenharmony_ci goto fail; 25153a5a1b3Sopenharmony_ci } 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_ci if (((struct sockaddr*) &i->sa)->sa_family == AF_INET) 25453a5a1b3Sopenharmony_ci ((struct sockaddr_in*) &i->sa)->sin_port = htons(port); 25553a5a1b3Sopenharmony_ci else 25653a5a1b3Sopenharmony_ci ((struct sockaddr_in6*) &i->sa)->sin6_port = htons(port); 25753a5a1b3Sopenharmony_ci 25853a5a1b3Sopenharmony_ci return i; 25953a5a1b3Sopenharmony_ci 26053a5a1b3Sopenharmony_cifail: 26153a5a1b3Sopenharmony_ci pa_xfree(i->origin); 26253a5a1b3Sopenharmony_ci pa_xfree(i->session_name); 26353a5a1b3Sopenharmony_ci 26453a5a1b3Sopenharmony_ci return NULL; 26553a5a1b3Sopenharmony_ci} 26653a5a1b3Sopenharmony_ci 26753a5a1b3Sopenharmony_civoid pa_sdp_info_destroy(pa_sdp_info *i) { 26853a5a1b3Sopenharmony_ci pa_assert(i); 26953a5a1b3Sopenharmony_ci 27053a5a1b3Sopenharmony_ci pa_xfree(i->origin); 27153a5a1b3Sopenharmony_ci pa_xfree(i->session_name); 27253a5a1b3Sopenharmony_ci} 273