64#include <sys/socket.h>
81#define EWS_DISABLE_SNPRINTF_COMPAT
82#include "EmbeddableWebServer.h"
85#define MOD_NAME "[SDP] "
86#define SDP_FILE "ug.sdp"
112 struct Server http_server;
133static struct sdp *
new_sdp(
bool ipv6,
const char *receiver);
135static bool sdp_run_http_server(
struct sdp *
sdp,
int port);
136static void sdp_stop_http_server(
struct sdp *
sdp);
141 struct sdp *
sdp = calloc(1,
sizeof(*
sdp));
146 const char *ip_loopback;
150 ip_loopback =
"127.0.0.1";
153 const char *connection_address = ip_loopback;
154 const char *origin_address = ip_loopback;
155 struct sockaddr_storage addrs[20];
156 size_t len =
sizeof addrs;
158 for (
int i = 0; i < (int)(
len /
sizeof addrs[0]); ++i) {
160 bool ipv6 = addrs[i].ss_family == AF_INET6;
161 size_t sa_len = ipv6 ?
sizeof(
struct sockaddr_in6) : sizeof(struct sockaddr_in);
162 getnameinfo((struct sockaddr *) &addrs[i], sa_len, hostname, sizeof(hostname), NULL, 0, NI_NUMERICHOST);
163 origin_address = hostname;
168 if (is_addr_multicast(receiver)) {
169 connection_address = receiver;
175 "s=Ultragrid streams\r\n");
181 serverInit(&
sdp->http_server);
230 rtpmapLine[0] =
'\0';
247 const int sdp_ch_count =
249 const char *sdp_codec_name = NULL;
252 sdp_codec_name =
"PCMU";
255 sdp_codec_name =
"PCMA";
258 sdp_codec_name =
"opus";
261 sdp_codec_name =
"MPA";
267 snprintf(rtpmapLine,
STR_LEN,
"a=rtpmap:%u %s/%d/%d\r\n",
282 assert(0 &&
"[SDP] SDP creation failed\n");
296 "m=audio %d RTP/AVP %d\r\n", port, pt);
306 rtpmapLine[0] =
'\0';
308 const char *rtpmap_codec = NULL;
313 rtpmap_codec =
"JPEG";
317 rtpmap_codec =
"H264";
324 assert(rtpmap_codec != NULL);
325 snprintf(rtpmapLine,
STR_LEN,
"a=rtpmap:%d %s/90000\r\n",
345 assert(0 &&
"[SDP] SDP creation failed\n");
358 "m=video %d RTP/AVP %d\r\n", port, pt);
366static void strappend(
char **dst,
size_t *dst_alloc_len,
const char *src)
368 if (*dst_alloc_len < strlen(*dst) + strlen(src) + 1) {
369 *dst_alloc_len = strlen(*dst) + strlen(src) + 1;
370 *dst = realloc(*dst, *dst_alloc_len);
372 strncat(*dst, src, *dst_alloc_len - strlen(*dst) - 1);
377 char *buf = calloc(1, 1);
389 printf(
"Printed version:\n%s", buf);
404 if (fprintf(fOut,
"%s", buf) != (
int) strlen(buf)) {
421 serverDeInit(&
sdp->http_server);
432#define ROBOTS_TXT "User-agent: *\r\nDisallow: /\r\n"
433#define SECURITY_TXT "Contact: http://www.ultragrid.cz/contact\r\n"
435struct Response* createResponseForRequest(
const struct Request* request,
struct Connection* connection) {
449 if (strcasecmp(request->pathDecoded,
"/robots.txt") == 0 ||
450 strcasecmp(request->pathDecoded,
"/.well-known/security.txt") == 0 ||
451 strcasecmp(request->pathDecoded,
"/security.txt") == 0) {
452 struct Response*
response = responseAlloc(200,
"OK",
"text/plain", 0);
453 heapStringSetToCString(&
response->body, strcasecmp(request->pathDecoded,
"/robots.txt") == 0 ? ROBOTS_TXT : SECURITY_TXT);
459 struct Response*
response = responseAlloc(200,
"OK",
"application/sdp", 0);
460 heapStringSetToCString(&
response->body, sdp_content);
464static uint16_t portInHostOrder;
466static THREAD_RETURN_TYPE STDCALL_ON_WIN32 acceptConnectionsThread(
void*
param) {
467 struct sockaddr_storage ss = { 0 };
470 size_t sa_len =
sdp->
ip_version == 4 ?
sizeof(
struct sockaddr_in) : sizeof(struct sockaddr_in6);
471 if (sdp->ip_version == 4) {
472 struct sockaddr_in *sin = (
struct sockaddr_in *) &ss;
473 sin->sin_addr.s_addr = htonl(INADDR_ANY);
474 sin->sin_port = htons(portInHostOrder);
476 struct sockaddr_in6 *sin6 = (
struct sockaddr_in6 *) &ss;
477 sin6->sin6_addr = in6addr_any;
478 sin6->sin6_port = htons(portInHostOrder);
480 acceptConnectionsUntilStopped(
param, (
struct sockaddr *) &ss, sa_len);
482 return (THREAD_RETURN_TYPE) 0;
490print_std_rtp_urls(
struct sdp *
sdp) {
491 const char *
const bind_addr =
sdp->
ip_version == 6 ?
"[::]" :
"0.0.0.0";
496 "%*[^ ] %d RTP/AVP %d", &port, &pt) == 2 &&
498 MSG(NOTICE,
"audio can be played directly with rtp://%s:%d\n",
504 "%*[^ ] %d RTP/AVP %d", &port, &pt) == 2 &&
506 MSG(NOTICE,
"video can be played directly with rtp://%s:%d\n",
512static void print_http_path(
struct sdp *
sdp) {
513 struct sockaddr_storage addrs[20];
514 size_t len =
sizeof addrs;
516 bool found_preferred_addr =
false;
517#define PREFERRED_ADDR(addr) \
518 (!is_addr_loopback((struct sockaddr *) (addr)) && \
519 !is_addr_linklocal((struct sockaddr *) (addr)))
520 for (
size_t i = 0; i <
len /
sizeof addrs[0]; ++i) {
521 if (PREFERRED_ADDR(&addrs[i])) {
522 found_preferred_addr =
true;
525 for (
size_t i = 0; i <
len /
sizeof addrs[0]; ++i) {
526 if (!found_preferred_addr || PREFERRED_ADDR(&addrs[i])) {
528 bool ipv6 = addrs[i].ss_family == AF_INET6;
529 size_t sa_len = ipv6 ?
sizeof(
struct sockaddr_in6) : sizeof(struct sockaddr_in);
530 getnameinfo((struct sockaddr *) &addrs[i], sa_len, hostname, sizeof(hostname), NULL, 0, NI_NUMERICHOST);
534 snprintf(recv_str,
sizeof recv_str,
"ANY receiver");
536 snprintf(recv_str,
sizeof recv_str,
"Receiver \"%s\"",
sdp_receiver);
539 "%s can play SDP with URL "
540 "http://%s%s%s:%u/%s\n",
541 recv_str, ipv6 ?
"[" :
"", hostname, ipv6 ?
"]" :
"",
547 print_std_rtp_urls(
sdp);
551static bool sdp_run_http_server(
struct sdp *
sdp,
int port)
553 assert(port >= 0 && port < 65536);
556 portInHostOrder = port;
557 sdp->http_server.tag =
sdp;
560 print_http_path(
sdp);
564void sdp_stop_http_server(
struct sdp *
sdp)
567 serverMutexLock(&
sdp->http_server);
568 sdp->http_server.shouldRun =
false;
569 serverMutexUnlock(&
sdp->http_server);
586 if (strcmp(opts,
"help") == 0) {
588 color_printf(
"\t" TBOLD(
"uv " TRED(
"--protocol sdp")
"[:autorun][:file=<name>|no][:port=<http_port>]")
"\n");
590 color_printf(
"\t" TBOLD(
"autorun")
" - automatically send to the address that requested the SDP over HTTP without giving an address (use with caution!)\n");
596 char opts_c[strlen(opts) + 1];
598 strcpy(opts_c, opts);
599 char *
item, *save_ptr;
600 while ((
item = strtok_r(tmp,
":", &save_ptr)) != NULL) {
601 if (strstr(
item,
"port=") ==
item) {
603 }
else if (strstr(
item,
"file=") ==
item) {
605 }
else if (strstr(
item,
"autorun") ==
item) {
626 bool prefer_pt =
false;
627 if (rtpmap_codec_name[0] ==
'?') {
628 rtpmap_codec_name += 1;
633 if (strlen(rtpmap_codec_name) > 1) {
636 MSG(WARNING,
"Codec %s not known to UltraGrid!\n",
641 if (!prefer_pt && c !=
VC_NONE) {
647 MSG(WARNING,
"No mapping of PT=%d to video coddec in UG!\n", pt);
656 "Opus")
", " TBOLD(
"PCMA")
" (A-law), " TBOLD(
"PCMU")
" (u-law)\n");
658 "Supported video codecs: " TBOLD(
"H.264")
", " TBOLD(
"JPEG")
"\n");
codec_t get_codec_from_name(const char *name)
This file contains definition of some common audio types.
int color_printf(const char *format,...)
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_VERBOSE
#define LOG_LEVEL_WARNING
less severe errors
#define snprintf_ch(str,...)
shortcut for snprintf(var, sizeof var...), var must be a char array
@ STR_LEN
"standard" string length placeholder
const char * ug_strerror(int errnum)
bool is_addr_linklocal(struct sockaddr *sa)
bool get_local_addresses(struct sockaddr_storage *addrs, size_t *len, int ip_version)
bool is_addr_loopback(struct sockaddr *sa)
#define PT_ITU_T_G711_PCMU
#define PT_ITU_T_G711_PCMA
int get_audio_rtp_pt_rtpmap(audio_codec_t codec, int sample_rate, int channels, char rtpmapLine[STR_LEN])
static struct sdp * new_sdp(bool ipv6, const char *receiver)
static int new_stream(struct sdp *sdp)
int sdp_add_audio(bool ipv6, int port, int sample_rate, int channels, audio_codec_t codec, address_callback_t addr_callback, void *addr_callback_udata)
static void clean_sdp(struct sdp *sdp)
static char sdp_filename[MAX_PATH_SIZE]
static bool want_sdp_audio
void sdp_set_properties(const char *receiver, bool has_sdp_video, bool has_sdp_audio)
int get_video_rtp_pt_rtpmap(codec_t codec, char rtpmapLine[STR_LEN])
int sdp_set_options(const char *opts)
static bool want_sdp_video
static bool gen_sdp(void)
int sdp_add_video(bool ipv6, int port, codec_t codec, address_callback_t addr_callback, void *addr_callback_udata)
void sdp_print_supported_codecs(void)
static void strappend(char **dst, size_t *dst_alloc_len, const char *src)
static char sdp_receiver[1024]
static struct sdp * sdp_state
codec_t get_video_codec_from_pt_rtpmap(int pt, const char *rtpmap_codec_name)
get UG video codec to given params
void(* address_callback_t)(void *udata, const char *address)
void * audio_address_callback_udata
char session_name[STR_LEN]
struct stream_info stream[MAX_STREAMS]
pthread_t http_server_thr
void * video_address_callback_udata
void(* audio_address_callback)(void *udata, const char *address)
void(* video_address_callback)(void *udata, const char *address)
This file contains some common data types.
@ JPEG
JPEG image, restart intervals may be used. Compatible with GPUJPEG.
@ MJPG
JPEG image, without restart intervals.