From 9f21e5918573511bcf05435abae942f210b5ae28 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 13 Oct 2015 22:17:57 +0200 Subject: [PATCH] IPTV: add basic m3u parser, fixes #3106 --- src/http.h | 1 + src/httpc.c | 74 +++++++++++--------- src/input/mpegts/iptv/iptv_http.c | 101 +++++++++++++++++++++++++-- src/input/mpegts/iptv/iptv_private.h | 2 +- 4 files changed, 137 insertions(+), 41 deletions(-) diff --git a/src/http.h b/src/http.h index 0ccfa84e7..fb53aebaf 100644 --- a/src/http.h +++ b/src/http.h @@ -358,6 +358,7 @@ int http_client_send( http_client_t *hc, http_cmd_t cmd, http_arg_list_t *header, void *body, size_t body_size ); void http_client_basic_auth( http_client_t *hc, http_arg_list_t *h, const char *user, const char *pass ); +int http_client_simple_reconnect ( http_client_t *hc, const url_t *u ); int http_client_simple( http_client_t *hc, const url_t *url); int http_client_clear_state( http_client_t *hc ); int http_client_run( http_client_t *hc ); diff --git a/src/httpc.c b/src/httpc.c index b317c6872..ad4bead4d 100644 --- a/src/httpc.c +++ b/src/httpc.c @@ -1140,12 +1140,48 @@ http_client_basic_args ( http_client_t *hc, http_arg_list_t *h, const url_t *url http_client_basic_auth(hc, h, url->user, url->pass); } +int +http_client_simple_reconnect ( http_client_t *hc, const url_t *u ) +{ + http_arg_list_t h; + tvhpoll_t *efd; + int r; + + if (strcmp(u->scheme, hc->hc_scheme) || + strcmp(u->host, hc->hc_host) || + http_port(hc, u->scheme, u->port) != hc->hc_port || + !hc->hc_keepalive) { + efd = hc->hc_efd; + http_client_shutdown(hc, 1, 1); + r = http_client_reconnect(hc, hc->hc_version, + u->scheme, u->host, u->port); + if (r < 0) + return r; + r = hc->hc_verify_peer; + hc->hc_verify_peer = -1; + http_client_ssl_peer_verify(hc, r); + hc->hc_efd = efd; + } + + http_client_flush(hc, 0); + + http_client_basic_args(hc, &h, u, hc->hc_keepalive); + hc->hc_reconnected = 1; + hc->hc_shutdown = 0; + hc->hc_pevents = 0; + + r = http_client_send(hc, hc->hc_cmd, u->path, u->query, &h, NULL, 0); + if (r < 0) + return r; + + hc->hc_reconnected = 1; + return HTTP_CON_RECEIVING; +} + static int http_client_redirected ( http_client_t *hc ) { char *location, *location2; - http_arg_list_t h; - tvhpoll_t *efd; url_t u; int r; @@ -1172,40 +1208,10 @@ http_client_redirected ( http_client_t *hc ) } free(location); - if (strcmp(u.scheme, hc->hc_scheme) || - strcmp(u.host, hc->hc_host) || - http_port(hc, u.scheme, u.port) != hc->hc_port || - !hc->hc_keepalive) { - efd = hc->hc_efd; - http_client_shutdown(hc, 1, 1); - r = http_client_reconnect(hc, hc->hc_version, - u.scheme, u.host, u.port); - if (r < 0) { - urlreset(&u); - return r; - } - r = hc->hc_verify_peer; - hc->hc_verify_peer = -1; - http_client_ssl_peer_verify(hc, r); - hc->hc_efd = efd; - } - - http_client_flush(hc, 0); + r = http_client_simple_reconnect(hc, &u); - http_client_basic_args(hc, &h, &u, hc->hc_keepalive); - hc->hc_reconnected = 1; - hc->hc_shutdown = 0; - hc->hc_pevents = 0; - - r = http_client_send(hc, hc->hc_cmd, u.path, u.query, &h, NULL, 0); - if (r < 0) { - urlreset(&u); - return r; - } - - hc->hc_reconnected = 1; urlreset(&u); - return 1; + return r; } int diff --git a/src/input/mpegts/iptv/iptv_http.c b/src/input/mpegts/iptv/iptv_http.c index 1f136f438..4f78fb89c 100644 --- a/src/input/mpegts/iptv/iptv_http.c +++ b/src/input/mpegts/iptv/iptv_http.c @@ -21,21 +21,69 @@ #include "iptv_private.h" #include "http.h" +/* + * M3U parser + */ +static char * +iptv_http_m3u(char *data) +{ + char *url; + + while (*data && *data != '\n') data++; + if (*data) data++; + while (*data) { + if (strncmp(data, "#EXTINF:", 8) == 0) { + while (*data && *data != '\n') data++; + if (*data) { *data = '\0'; data++; } + continue; + } + while (*data && *data <= ' ') data++; + url = data; + while (*data && *data != '\n') data++; + if (*data) { *data = '\0'; data++; } + if (*url) + return strdup(url); + } + return NULL; +} + /* * Connected */ static int iptv_http_header ( http_client_t *hc ) { + iptv_mux_t *im = hc->hc_aux; + char *argv[3], *s; + int n; + if (hc->hc_aux == NULL) return 0; /* multiple headers for redirections */ - if (hc->hc_code == HTTP_STATUS_OK) { - pthread_mutex_lock(&global_lock); - iptv_input_mux_started(hc->hc_aux); - pthread_mutex_unlock(&global_lock); + if (hc->hc_code != HTTP_STATUS_OK) + return 0; + + s = http_arg_get(&hc->hc_args, "Content-Type"); + if (s) { + n = http_tokenize(s, argv, ARRAY_SIZE(argv), ';'); + printf("mime: '%s'\n", s); + if (n > 0 && + (strcasecmp(s, "audio/mpegurl") == 0 || + strcasecmp(s, "audio/x-mpegurl") == 0)) { + if (im->im_m3u_header > 10) { + im->im_m3u_header = 0; + return 0; + } + im->im_m3u_header++; + return 0; + } } + + im->im_m3u_header = 0; + pthread_mutex_lock(&global_lock); + iptv_input_mux_started(hc->hc_aux); + pthread_mutex_unlock(&global_lock); return 0; } @@ -48,13 +96,18 @@ iptv_http_data { iptv_mux_t *im = hc->hc_aux; - if (im == NULL) + if (im == NULL || hc->hc_code != HTTP_STATUS_OK) + return 0; + + if (im->im_m3u_header) { + sbuf_append(&im->mm_iptv_buffer, buf, len); return 0; + } pthread_mutex_lock(&iptv_lock); - tsdebug_write((mpegts_mux_t *)im, buf, len); sbuf_append(&im->mm_iptv_buffer, buf, len); + tsdebug_write((mpegts_mux_t *)im, buf, len); if (len > 0) iptv_input_recv_packets(im, len); @@ -64,6 +117,41 @@ iptv_http_data return 0; } +/* + * Complete data + */ +static int +iptv_http_complete + ( http_client_t *hc ) +{ + iptv_mux_t *im = hc->hc_aux; + char *url; + url_t u; + int r; + + if (im->im_m3u_header) { + im->im_m3u_header = 0; + sbuf_append(&im->mm_iptv_buffer, "", 1); + url = iptv_http_m3u((char *)im->mm_iptv_buffer.sb_data); + if (url == NULL) { + tvherror("iptv", "m3u contents parsing failed"); + return 0; + } + memset(&u, 0, sizeof(u)); + if (!urlparse(url, &u)) { + hc->hc_keepalive = 0; + r = http_client_simple_reconnect(hc, &u); + if (r < 0) + tvherror("iptv", "cannot reopen http client: %d'", r); + } else { + tvherror("iptv", "m3u url invalid '%s'", url); + } + free(url); + return 0; + } + return 0; +} + /* * Setup HTTP(S) connection */ @@ -79,6 +167,7 @@ iptv_http_start return SM_CODE_TUNING_FAILED; hc->hc_hdr_received = iptv_http_header; hc->hc_data_received = iptv_http_data; + hc->hc_data_complete = iptv_http_complete; hc->hc_handle_location = 1; /* allow redirects */ hc->hc_io_size = 128*1024; /* increase buffering */ http_client_register(hc); /* register to the HTTP thread */ diff --git a/src/input/mpegts/iptv/iptv_private.h b/src/input/mpegts/iptv/iptv_private.h index 1af8fe386..3a675bcfd 100644 --- a/src/input/mpegts/iptv/iptv_private.h +++ b/src/input/mpegts/iptv/iptv_private.h @@ -130,7 +130,7 @@ struct iptv_mux void *im_data; int im_delete_flag; - + int im_m3u_header; }; iptv_mux_t* iptv_mux_create0 -- 2.47.3