diff --git a/Makefile b/Makefile index 2e497792..99316358 100644 --- a/Makefile +++ b/Makefile @@ -280,6 +280,7 @@ SRCS-${CONFIG_IPTV} += \ src/input/mpegts/iptv/iptv_service.c \ src/input/mpegts/iptv/iptv_http.c \ src/input/mpegts/iptv/iptv_udp.c \ + src/input/mpegts/iptv/iptv_pipe.c # TSfile SRCS-$(CONFIG_TSFILE) += \ diff --git a/docs/html/config_muxes.html b/docs/html/config_muxes.html index 209e2213..3c616b23 100644 --- a/docs/html/config_muxes.html +++ b/docs/html/config_muxes.html @@ -73,6 +73,31 @@
URL
Mux URL. +
+
udp:// +
Raw MPEG-TS UDP packets + +
rtp:// +
MPEG-TS UDP packets with RTP header + +
http:// +
HTTP stream (MPEG-TS) + +
https:// +
Secure HTTP stream (MPEG-TS) + +
pipe:// +
Read standard output from an external program. + If the program name does not have the first + character '/', the PATH environment variable + is used to find the program name in all + directories specified by PATH. + Additional arguments may be separated + using spaces. A raw MPEG-TS stream is + expected. + +
+
# Services
The number of services found on this mux. diff --git a/src/input/mpegts/iptv/iptv.c b/src/input/mpegts/iptv/iptv.c index 53b616e5..254ec727 100644 --- a/src/input/mpegts/iptv/iptv.c +++ b/src/input/mpegts/iptv/iptv.c @@ -220,6 +220,7 @@ iptv_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) iptv_mux_t *im = (iptv_mux_t*)mmi->mmi_mux; iptv_handler_t *ih; char buf[256]; + const char *scheme; url_t url; /* Already active */ @@ -229,15 +230,25 @@ iptv_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) /* Parse URL */ mpegts_mux_nice_name((mpegts_mux_t*)im, buf, sizeof(buf)); memset(&url, 0, sizeof(url)); - if (urlparse(im->mm_iptv_url ?: "", &url)) { - tvherror("iptv", "%s - invalid URL [%s]", buf, im->mm_iptv_url); - return ret; + + if (im->mm_iptv_url && !strncmp(im->mm_iptv_url, "pipe://", 7)) { + + scheme = "pipe"; + + } else { + + if (urlparse(im->mm_iptv_url ?: "", &url)) { + tvherror("iptv", "%s - invalid URL [%s]", buf, im->mm_iptv_url); + return ret; + } + scheme = url.scheme; + } /* Find scheme handler */ - ih = iptv_handler_find(url.scheme ?: ""); + ih = iptv_handler_find(scheme ?: ""); if (!ih) { - tvherror("iptv", "%s - unsupported scheme [%s]", buf, url.scheme ?: "none"); + tvherror("iptv", "%s - unsupported scheme [%s]", buf, scheme ?: "none"); return ret; } @@ -245,7 +256,7 @@ iptv_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) pthread_mutex_lock(&iptv_lock); im->mm_active = mmi; // Note: must set here else mux_started call // will not realise we're ready to accept pid open calls - ret = ih->start(im, &url); + ret = ih->start(im, im->mm_iptv_url, &url); if (!ret) im->im_handler = ih; else @@ -577,6 +588,7 @@ void iptv_init ( void ) /* Register handlers */ iptv_http_init(); iptv_udp_init(); + iptv_pipe_init(); iptv_input = calloc(1, sizeof(iptv_input_t)); diff --git a/src/input/mpegts/iptv/iptv_http.c b/src/input/mpegts/iptv/iptv_http.c index 546567e9..26c5fef8 100644 --- a/src/input/mpegts/iptv/iptv_http.c +++ b/src/input/mpegts/iptv/iptv_http.c @@ -62,7 +62,7 @@ iptv_http_data */ static int iptv_http_start - ( iptv_mux_t *im, const url_t *u ) + ( iptv_mux_t *im, const char *raw, const url_t *u ) { http_client_t *hc; int r; diff --git a/src/input/mpegts/iptv/iptv_mux.c b/src/input/mpegts/iptv/iptv_mux.c index 76fd2adf..6858690c 100644 --- a/src/input/mpegts/iptv/iptv_mux.c +++ b/src/input/mpegts/iptv/iptv_mux.c @@ -30,7 +30,7 @@ static int iptv_mux_url_set ( void *p, const void *v ) { iptv_mux_t *im = p; - const char *str = v; + const char *str = v, *x; char *buf, port[16] = ""; size_t len; url_t url; @@ -43,6 +43,17 @@ iptv_mux_url_set ( void *p, const void *v ) im->mm_iptv_url_sane = NULL; return 1; } + if (!strncmp(str, "pipe://", 7)) { + x = str + strlen(str); + while (x != str && *x <= ' ') + x--; + ((char *)x)[1] = '\0'; + free(im->mm_iptv_url); + free(im->mm_iptv_url_sane); + im->mm_iptv_url = strdup(str); + im->mm_iptv_url_sane = strdup(str); + return 1; + } memset(&url, 0, sizeof(url)); if (!urlparse(str, &url)) { free(im->mm_iptv_url); diff --git a/src/input/mpegts/iptv/iptv_pipe.c b/src/input/mpegts/iptv/iptv_pipe.c new file mode 100644 index 00000000..2c676640 --- /dev/null +++ b/src/input/mpegts/iptv/iptv_pipe.c @@ -0,0 +1,131 @@ +/* + * IPTV - pipe handler + * + * Copyright (C) 2014 Jaroslav Kysela + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "tvheadend.h" +#include "iptv_private.h" +#include "spawn.h" + +#include +#include +#include +#include +#include + +/* + * Connect UDP/RTP + */ +static int +iptv_pipe_start ( iptv_mux_t *im, const char *_raw, const url_t *url ) +{ + char *argv[32], *f, *raw, *s; + int i = 1, rd; + + if (strncmp(_raw, "pipe://", 7)) + return -1; + + s = raw = tvh_strdupa(_raw + 7); + + argv[0] = NULL; + + while (*s && *s != ' ') + s++; + if (*s == ' ') { + *(char *)s = '\0'; + s++; + } + + while (*s && i < ARRAY_SIZE(argv) - 1) { + f = s; + while (*s && *s != ' ') + s++; + if (f != s) { + *(char *)s = '\0'; + argv[i++] = f; + s++; + } + } + argv[i] = NULL; + + if (spawn_and_give_stdout(raw, argv, &rd, 1)) { + tvherror("iptv", "Unable to start pipe '%s' (wrong executable?)", raw); + return -1; + } + + fcntl(rd, F_SETFD, fcntl(rd, F_GETFD) | FD_CLOEXEC); + fcntl(rd, F_SETFL, fcntl(rd, F_GETFL) | O_NONBLOCK); + + im->mm_iptv_fd = rd; + + iptv_input_mux_started(im); + return 0; +} + +static void +iptv_pipe_stop + ( iptv_mux_t *im ) +{ + int rd = im->mm_iptv_fd; + if (rd > 0) + close(rd); + im->mm_iptv_fd = -1; +} + +static ssize_t +iptv_pipe_read ( iptv_mux_t *im ) +{ + int r, rd = im->mm_iptv_fd; + ssize_t res = 0; + char buf[64*1024]; + + while (rd > 0 && res < 1024*1024) { + r = read(rd, buf, sizeof(buf)); + if (r < 0) { + if (ERRNO_AGAIN(errno)) + continue; + break; + } + if (!r) { + close(rd); + im->mm_iptv_fd = -1; + break; + } + sbuf_append(&im->mm_iptv_buffer, buf, r); + res += r; + } + + return res; +} + +/* + * Initialise pipe handler + */ + +void +iptv_pipe_init ( void ) +{ + static iptv_handler_t ih[] = { + { + .scheme = "pipe", + .start = iptv_pipe_start, + .stop = iptv_pipe_stop, + .read = iptv_pipe_read, + }, + }; + iptv_handler_register(ih, ARRAY_SIZE(ih)); +} diff --git a/src/input/mpegts/iptv/iptv_private.h b/src/input/mpegts/iptv/iptv_private.h index 32f0620f..749a4869 100644 --- a/src/input/mpegts/iptv/iptv_private.h +++ b/src/input/mpegts/iptv/iptv_private.h @@ -40,7 +40,7 @@ typedef struct iptv_handler iptv_handler_t; struct iptv_handler { const char *scheme; - int (*start) ( iptv_mux_t *im, const url_t *url ); + int (*start) ( iptv_mux_t *im, const char *raw, const url_t *url ); void (*stop) ( iptv_mux_t *im ); ssize_t (*read) ( iptv_mux_t *im ); @@ -118,6 +118,7 @@ void iptv_mux_load_all ( void ); void iptv_http_init ( void ); void iptv_udp_init ( void ); +void iptv_pipe_init ( void ); #endif /* __IPTV_PRIVATE_H__ */ diff --git a/src/input/mpegts/iptv/iptv_udp.c b/src/input/mpegts/iptv/iptv_udp.c index 06009fdb..42ba3efa 100644 --- a/src/input/mpegts/iptv/iptv_udp.c +++ b/src/input/mpegts/iptv/iptv_udp.c @@ -32,7 +32,7 @@ * Connect UDP/RTP */ static int -iptv_udp_start ( iptv_mux_t *im, const url_t *url ) +iptv_udp_start ( iptv_mux_t *im, const char *raw, const url_t *url ) { char name[256]; udp_connection_t *conn; diff --git a/src/spawn.c b/src/spawn.c index fef83ef1..736c2804 100644 --- a/src/spawn.c +++ b/src/spawn.c @@ -136,6 +136,13 @@ find_exec ( const char *name, char *out, size_t len ) DIR *dir; struct dirent *de; struct stat st; + if (name[0] == '/') { + if (lstat(name, &st)) return 0; + if (!S_ISREG(st.st_mode) || !(st.st_mode & S_IEXEC)) return 0; + strncpy(out, name, len); + out[len-1] = '\0'; + return 1; + } if (!(path = getenv("PATH"))) return 0; path = strdup(path); tmp = strtok_r(path, ":", &tmp2); @@ -147,6 +154,7 @@ find_exec ( const char *name, char *out, size_t len ) if (lstat(bin, &st)) continue; if (!S_ISREG(st.st_mode) || !(st.st_mode & S_IEXEC)) continue; strncpy(out, bin, len); + out[len-1] = '\0'; ret = 1; break; }