]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
IPTV: add basic m3u parser, fixes #3106
authorJaroslav Kysela <perex@perex.cz>
Tue, 13 Oct 2015 20:17:57 +0000 (22:17 +0200)
committerJaroslav Kysela <perex@perex.cz>
Tue, 13 Oct 2015 20:18:31 +0000 (22:18 +0200)
src/http.h
src/httpc.c
src/input/mpegts/iptv/iptv_http.c
src/input/mpegts/iptv/iptv_private.h

index 0ccfa84e7bdf449cce2acf389d8d42996e294fc8..fb53aebaf9af0c8e106f52154cd6bf4dcfe080a6 100644 (file)
@@ -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 );
index b317c6872694b5befc4e0932e73e7b98ff41ce92..ad4bead4d6cfd296abab9a496b7245d6df4ec7c5 100644 (file)
@@ -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
index 1f136f4384d5d083b6fee710c0a830aeaae055f2..4f78fb89c94328fdaff63d9528a2d226ce74bdbf 100644 (file)
 #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 */
index 1af8fe38668858b288ae4ff1b70837aa84f6ab5b..3a675bcfd5721bfcdfe70a35a39ec9c13c9d54d4 100644 (file)
@@ -130,7 +130,7 @@ struct iptv_mux
   void                 *im_data;
 
   int                   im_delete_flag;
-
+  int                   im_m3u_header;
 };
 
 iptv_mux_t* iptv_mux_create0