Project

General

Profile

Feature #1372 » iptv_input.patch

HTTP IPTV input support - Zdeněk Kopřivík, 2013-10-04 10:16

View differences:

new/src/iptv_input.c 2013-10-04 08:00:04.000000000 +0200
40 40
#include "tsdemux.h"
41 41
#include "psi.h"
42 42
#include "settings.h"
43
#include "tcp.h"
43 44

  
44 45
static int iptv_thread_running;
45 46
static int iptv_epollfd;
......
48 49
struct service_list iptv_all_services; /* All IPTV services */
49 50
static struct service_list iptv_active_services; /* Currently enabled */
50 51

  
52

  
53
/*
54
 * URL processing - TODO: move to a library
55
 */
56
typedef struct
57
url
58
{
59
  char       buf[2048];
60
  const char *scheme;
61
  const char *host;
62
  uint16_t   port;
63
  const char *path;  
64
} url_t;
65

  
66
static int
67
url_parse ( url_t *up, const char *urlstr )
68
{
69
  char *t1, *t2;
70
  strcpy(up->buf, urlstr);
71

  
72
  /* Scheme */
73
  up->scheme = t1 = up->buf;
74
  if (!(t2 = strstr(t1, "://"))) {
75
    return 1;
76
  }
77
  *t2 = 0;
78

  
79
  /* Host */
80
  up->host = t1 = t2 + 3;
81
  up->path = NULL;
82
  if ((t2 = strstr(t1, "/"))) {
83
    *t2 = 0;
84
    up->path = t2 + 1;
85
  }
86

  
87
  /* Port */
88
  up->port = 0;
89
  if (!(t2 = strstr(up->host, ":"))) {
90
    if (!strcmp(up->scheme, "https"))
91
      up->port = 443;
92
    else if (!strcmp(up->scheme, "http"))
93
      up->port = 80;
94
  } else {
95
    *t2 = 0;
96
    up->port = atoi(t2+1);
97
  }
98

  
99
  return 0;
100
}
101

  
102

  
51 103
/**
52 104
 * PAT parser. We only parse a single program. CRC has already been verified
53 105
 */
......
124 176
static void *
125 177
iptv_thread(void *aux)
126 178
{
127
  int nfds, fd, r, j, hlen;
128
  uint8_t tsb[65536], *buf;
179
  int nfds, fd, type, tlen = sizeof(int), r, j, hlen, err = 0;
180
  uint8_t *tsb, *buf, real_buf[65536];
129 181
  struct epoll_event ev;
130 182
  service_t *t;
131 183

  
132 184
  while(1) {
133 185
    nfds = epoll_wait(iptv_epollfd, &ev, 1, -1);
134 186
    if(nfds == -1) {
135
      tvhlog(LOG_ERR, "IPTV", "epoll() error -- %s, sleeping 1 second",
136
	     strerror(errno));
187
      tvhlog(LOG_ERR, "IPTV", "epoll() error -- %s, sleeping 1 second", strerror(errno));
137 188
      sleep(1);
138 189
      continue;
139 190
    }
......
142 193
      continue;
143 194

  
144 195
    fd = ev.data.fd;
145
    r = read(fd, tsb, sizeof(tsb));
196
    tsb = real_buf + 188;
197
    r = read(fd, tsb, sizeof(real_buf) - 188);
146 198

  
147
    if(r > 1 && tsb[0] == 0x47 && (r % 188) == 0) {
199
    getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, (socklen_t *)&tlen);
200

  
201
    if(type == SOCK_STREAM || (r > 1 && tsb[0] == 0x47 && (r % 188) == 0)) {
148 202
      /* Looks like raw TS in UDP */
149 203
      buf = tsb;
150 204
    } else {
151 205
      /* Check for valid RTP packets */
152 206
      if(r < 12)
153
	continue;
207
        continue;
154 208

  
155 209
      if((tsb[0] & 0xc0) != 0x80)
156
	continue;
210
        continue;
157 211

  
158 212
      if((tsb[1] & 0x7f) != 33)
159
	continue;
160
      
213
        continue;
214

  
161 215
      hlen = (tsb[0] & 0xf) * 4 + 12;
162 216

  
163 217
      if(tsb[0] & 0x10) {
164
	// Extension (X bit) == true
218
        // Extension (X bit) == true
165 219

  
166
	if(r < hlen + 4)
167
	  continue; // Packet size < hlen + extension header
220
        if(r < hlen + 4)
221
          continue; // Packet size < hlen + extension header
168 222

  
169
	// Skip over extension header (last 2 bytes of header is length)
170
	hlen += ((tsb[hlen + 2] << 8) | tsb[hlen + 3]) * 4;
171
	// Add the extension header itself (EHL does not inc header)
172
	hlen += 4;
223
        // Skip over extension header (last 2 bytes of header is length)
224
        hlen += ((tsb[hlen + 2] << 8) | tsb[hlen + 3]) * 4;
225
        // Add the extension header itself (EHL does not inc header)
226
        hlen += 4;
173 227
      }
174 228

  
175 229
      if(r < hlen || (r - hlen) % 188 != 0)
176
	continue;
230
        continue;
177 231

  
178 232
      buf = tsb + hlen;
179 233
      r -= hlen;
180 234
    }
181 235

  
182 236
    pthread_mutex_lock(&iptv_recvmutex);
183
    
237

  
184 238
    LIST_FOREACH(t, &iptv_active_services, s_active_link) {
185 239
      if(t->s_iptv_fd != fd)
186
	continue;
187
      
188
      for(j = 0; j < r; j += 188)
189
	iptv_ts_input(t, buf + j);
240
        continue;
241

  
242
      if(t->s_iptv_url != NULL) {
243
        buf = tsb - t->s_iptv_tsb_len;
244
        r += t->s_iptv_tsb_len;
245
        memcpy(buf, t->s_iptv_tsb, t->s_iptv_tsb_len);
246

  
247
        // Attempt to re-sync a TS stream (3 valid sync's in a row)
248
        if(buf[0] != 0x47) {
249
          err = 1;
250
          while (err && (r > 376)) {
251
            buf++; r--;
252
            err = (buf[0] != 0x47) || (buf[188] != 0x47) || (buf[376] != 0x47);
253
          }
254
        }
255

  
256
        for(j = 0; j <= r - 188; j += 188) {
257
          iptv_ts_input(t, buf + j);
258
        }
259
        t->s_iptv_tsb_len = r % 188;
260
        memcpy(t->s_iptv_tsb, buf+((r/188)*188), t->s_iptv_tsb_len);
261
      }
262
      else{
263
        for(j = 0; j < r; j += 188)
264
          iptv_ts_input(t, buf + j);
265
      }
190 266
    }
191 267
    pthread_mutex_unlock(&iptv_recvmutex);
192 268
  }
......
201 277
iptv_service_start(service_t *t, unsigned int weight, int force_start)
202 278
{
203 279
  pthread_t tid;
204
  int fd;
280
  int fd, c, i;
205 281
  char straddr[INET6_ADDRSTRLEN];
282
  char buf[1024];
283
  url_t url;
206 284
  struct ip_mreqn m;
207 285
  struct ipv6_mreq m6;
208 286
  struct sockaddr_in sin;
......
218 296
    pthread_create(&tid, NULL, iptv_thread, NULL);
219 297
  }
220 298

  
221
  /* Now, open the real socket for UDP */
222
  if(t->s_iptv_group.s_addr!=0) {
223
    fd = tvh_socket(AF_INET, SOCK_DGRAM, 0);
224
  
225
  }
226
  else {
227
    fd = tvh_socket(AF_INET6, SOCK_DGRAM, 0);
228
  }
229
  if(fd == -1) {
230
    tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot open socket", t->s_identifier);
231
    return -1;
232
  }
233

  
234
  /* First, resolve interface name */
235
  memset(&ifr, 0, sizeof(ifr));
236
  snprintf(ifr.ifr_name, IFNAMSIZ, "%s", t->s_iptv_iface);
237
  ifr.ifr_name[IFNAMSIZ - 1] = 0;
238
  if(ioctl(fd, SIOCGIFINDEX, &ifr)) {
239
    tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s", 
240
	   t->s_identifier, t->s_iptv_iface);
241
    close(fd);
242
    return -1;
243
  }
299
  if(t->s_iptv_url != NULL){
300
    if(url_parse(&url, t->s_iptv_url))
301
      return -1;
244 302

  
245
  /* Bind to IPv4 multicast group */
246
  if(t->s_iptv_group.s_addr!=0) {
247
    memset(&sin, 0, sizeof(sin));
248
    sin.sin_family = AF_INET;
249
    sin.sin_port = htons(t->s_iptv_port);
250
    sin.sin_addr.s_addr = t->s_iptv_group.s_addr;
251
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &m, sizeof(struct ip_mreqn));
252
    if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
253
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot bind %s:%d -- %s",
254
           t->s_identifier, inet_ntoa(sin.sin_addr), t->s_iptv_port,
255
           strerror(errno));
256
      close(fd);
303
    /* MAKE CONNECTION */
304
    // TODO: move connection to thread
305
    // TODO: this is really only for testing and to allow use of TVH webserver as input
306
    tvhlog(LOG_DEBUG, "IPTV", "connecting to HTTP %s:%d", url.host, url.port);
307
    fd = tcp_connect(url.host, url.port, buf, sizeof(buf), 10);
308
    if (fd < 0) {
309
      tvhlog(LOG_ERR, "IPTV", "tcp_connect() failed %s", buf);
257 310
      return -1;
311
    }  
312

  
313
    /* Send request (VERY basic) */
314
    c = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.1\r\n", url.path);
315
    tvh_write(fd, buf, c);
316
    c = snprintf(buf, sizeof(buf), "Hostname: %s\r\n", url.host);
317
    tvh_write(fd, buf, c);
318
    tvh_write(fd, "\r\n", 2);
319

  
320
    /* Read back header */
321
    // TODO: do this properly
322
    i = 0;
323
    while (1) {
324
      if (!(c = read(fd, buf+i, 1)))
325
        continue;
326
      i++;
327
      if (i == 4 && !strncmp(buf, "\r\n\r\n", 4))
328
        break;
329
      memmove(buf, buf+1, 3); i = 3;
330
    }
331
  }
332
  else{
333
    /* Now, open the real socket for UDP */
334
    if(t->s_iptv_group.s_addr!=0) {
335
      fd = tvh_socket(AF_INET, SOCK_DGRAM, 0);
258 336
    }
259
    /* Join IPv4 group */
260
    memset(&m, 0, sizeof(m));
261
    m.imr_multiaddr.s_addr = t->s_iptv_group.s_addr;
262
    m.imr_address.s_addr = 0;
263
    m.imr_ifindex = ifr.ifr_ifindex;
264

  
265
      if(setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &m,
266
                sizeof(struct ip_mreqn)) == -1) {
267
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s",
268
           t->s_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno));
269
      close(fd);
270
      return -1;
337
    else {
338
      fd = tvh_socket(AF_INET6, SOCK_DGRAM, 0);
271 339
    }
272
  } else {
273
    /* Bind to IPv6 multicast group */
274
    memset(&sin6, 0, sizeof(sin6));
275
    sin6.sin6_family = AF_INET6;
276
    sin6.sin6_port = htons(t->s_iptv_port);
277
    sin6.sin6_addr = t->s_iptv_group6;
278
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &m6, sizeof(struct ipv6_mreq));
279
    if(bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) {
280
      inet_ntop(AF_INET6, &sin6.sin6_addr, straddr, sizeof(straddr));
281
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot bind %s:%d -- %s",
282
           t->s_identifier, straddr, t->s_iptv_port,
283
           strerror(errno));
284
      close(fd);
340
    if(fd == -1) {
341
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot open socket", t->s_identifier);
285 342
      return -1;
286 343
    }
287
    /* Join IPv6 group */
288
    memset(&m6, 0, sizeof(m6));
289
    m6.ipv6mr_multiaddr = t->s_iptv_group6;
290
    m6.ipv6mr_interface = ifr.ifr_ifindex;
291

  
292
    if(setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m6,
293
                sizeof(struct ipv6_mreq)) == -1) {
294
      inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr,
295
		straddr, sizeof(straddr));
296
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s",
297
           t->s_identifier, straddr, strerror(errno));
344

  
345
    /* First, resolve interface name */
346
    memset(&ifr, 0, sizeof(ifr));
347
    snprintf(ifr.ifr_name, IFNAMSIZ, "%s", t->s_iptv_iface);
348
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
349
    if(ioctl(fd, SIOCGIFINDEX, &ifr)) {
350
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s", t->s_identifier, t->s_iptv_iface);
298 351
      close(fd);
299 352
      return -1;
300 353
    }
301
  }
302 354

  
355
    /* Bind to IPv4 multicast group */
356
    if(t->s_iptv_group.s_addr!=0) {
357
      memset(&sin, 0, sizeof(sin));
358
      sin.sin_family = AF_INET;
359
      sin.sin_port = htons(t->s_iptv_port);
360
      sin.sin_addr.s_addr = t->s_iptv_group.s_addr;
361
      setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &m, sizeof(struct ip_mreqn));
362
      if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
363
        tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot bind %s:%d -- %s", t->s_identifier, inet_ntoa(sin.sin_addr), t->s_iptv_port, strerror(errno));
364
        close(fd);
365
        return -1;
366
      }
367
      /* Join IPv4 group */
368
      memset(&m, 0, sizeof(m));
369
      m.imr_multiaddr.s_addr = t->s_iptv_group.s_addr;
370
      m.imr_address.s_addr = 0;
371
      m.imr_ifindex = ifr.ifr_ifindex;
372

  
373
      if(setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &m, sizeof(struct ip_mreqn)) == -1) {
374
        tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s", t->s_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno));
375
        close(fd);
376
        return -1;
377
      }
378
    } else {
379
      /* Bind to IPv6 multicast group */
380
      memset(&sin6, 0, sizeof(sin6));
381
      sin6.sin6_family = AF_INET6;
382
      sin6.sin6_port = htons(t->s_iptv_port);
383
      sin6.sin6_addr = t->s_iptv_group6;
384
      setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &m6, sizeof(struct ipv6_mreq));
385
      if(bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) {
386
        inet_ntop(AF_INET6, &sin6.sin6_addr, straddr, sizeof(straddr));
387
        tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot bind %s:%d -- %s", t->s_identifier, straddr, t->s_iptv_port, strerror(errno));
388
        close(fd);
389
        return -1;
390
      }
391
      /* Join IPv6 group */
392
      memset(&m6, 0, sizeof(m6));
393
      m6.ipv6mr_multiaddr = t->s_iptv_group6;
394
      m6.ipv6mr_interface = ifr.ifr_ifindex;
395

  
396
      if(setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m6, sizeof(struct ipv6_mreq)) == -1) {
397
        inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr, straddr, sizeof(straddr));
398
        tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s", t->s_identifier, straddr, strerror(errno));
399
        close(fd);
400
        return -1;
401
      }
402
    }
303 403

  
304
  int resize = 262142;
305
  if(setsockopt(fd,SOL_SOCKET,SO_RCVBUF, &resize, sizeof(resize)) == -1)
306
    tvhlog(LOG_WARNING, "IPTV",
307
	   "Can not icrease UDP receive buffer size to %d -- %s",
308
	   resize, strerror(errno));
404
    int resize = 262142;
405
    if(setsockopt(fd,SOL_SOCKET,SO_RCVBUF, &resize, sizeof(resize)) == -1)
406
      tvhlog(LOG_WARNING, "IPTV", "Can not icrease UDP receive buffer size to %d -- %s", resize, strerror(errno));
407
  }
309 408

  
310 409
  memset(&ev, 0, sizeof(ev));
311 410
  ev.events = EPOLLIN;
312 411
  ev.data.fd = fd;
313 412
  if(epoll_ctl(iptv_epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
314
    tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot add to epoll set -- %s", 
315
	   t->s_identifier, strerror(errno));
413
    tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot add to epoll set -- %s", t->s_identifier, strerror(errno));
316 414
    close(fd);
317 415
    return -1;
318 416
  }
......
350 448

  
351 449
  assert(t->s_iptv_fd >= 0);
352 450

  
353
  /* First, resolve interface name */
354
  memset(&ifr, 0, sizeof(ifr));
355
  snprintf(ifr.ifr_name, IFNAMSIZ, "%s", t->s_iptv_iface);
356
  ifr.ifr_name[IFNAMSIZ - 1] = 0;
357
  if(ioctl(t->s_iptv_fd, SIOCGIFINDEX, &ifr)) {
358
    tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s",
359
	   t->s_identifier, t->s_iptv_iface);
360
  }
361

  
362
  if(t->s_iptv_group.s_addr != 0) {
363

  
364
    struct ip_mreqn m;
365
    memset(&m, 0, sizeof(m));
366
    /* Leave multicast group */
367
    m.imr_multiaddr.s_addr = t->s_iptv_group.s_addr;
368
    m.imr_address.s_addr = 0;
369
    m.imr_ifindex = ifr.ifr_ifindex;
370
    
371
    if(setsockopt(t->s_iptv_fd, SOL_IP, IP_DROP_MEMBERSHIP, &m,
372
		  sizeof(struct ip_mreqn)) == -1) {
373
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s",
374
	     t->s_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno));
375
    }
376
  } else {
377
    char straddr[INET6_ADDRSTRLEN];
378

  
379
    struct ipv6_mreq m6;
380
    memset(&m6, 0, sizeof(m6));
451
  if(t->s_iptv_url == NULL) {
452
    /* First, resolve interface name */
453
    memset(&ifr, 0, sizeof(ifr));
454
    snprintf(ifr.ifr_name, IFNAMSIZ, "%s", t->s_iptv_iface);
455
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
456
    if(ioctl(t->s_iptv_fd, SIOCGIFINDEX, &ifr)) {
457
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s", t->s_identifier, t->s_iptv_iface);
458
    }
459

  
460
    if(t->s_iptv_group.s_addr != 0) {
461
      struct ip_mreqn m;
462
      memset(&m, 0, sizeof(m));
463
      /* Leave multicast group */
464
      m.imr_multiaddr.s_addr = t->s_iptv_group.s_addr;
465
      m.imr_address.s_addr = 0;
466
      m.imr_ifindex = ifr.ifr_ifindex;
381 467

  
382
    m6.ipv6mr_multiaddr = t->s_iptv_group6;
383
    m6.ipv6mr_interface = ifr.ifr_ifindex;
384

  
385
    if(setsockopt(t->s_iptv_fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &m6,
386
		  sizeof(struct ipv6_mreq)) == -1) {
387
      inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr,
388
		straddr, sizeof(straddr));
468
      if(setsockopt(t->s_iptv_fd, SOL_IP, IP_DROP_MEMBERSHIP, &m, sizeof(struct ip_mreqn)) == -1) {
469
        tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s", t->s_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno));
470
      }
471
    } else {
472
      char straddr[INET6_ADDRSTRLEN];
389 473

  
390
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s",
391
	     t->s_identifier, straddr, strerror(errno));
392
    }
474
      struct ipv6_mreq m6;
475
      memset(&m6, 0, sizeof(m6));
393 476

  
477
      m6.ipv6mr_multiaddr = t->s_iptv_group6;
478
      m6.ipv6mr_interface = ifr.ifr_ifindex;
394 479

  
480
      if(setsockopt(t->s_iptv_fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &m6, sizeof(struct ipv6_mreq)) == -1) {
481
        inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr, straddr, sizeof(straddr));
395 482

  
483
        tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s", t->s_identifier, straddr, strerror(errno));
484
      }
485
    }
396 486
  }
397 487
  close(t->s_iptv_fd); // Automatically removes fd from epoll set
398 488

  
......
423 513
  if(t->s_iptv_iface)
424 514
    htsmsg_add_str(m, "interface", t->s_iptv_iface);
425 515

  
426
  if(t->s_iptv_group.s_addr!= 0) {
427
    inet_ntop(AF_INET, &t->s_iptv_group, abuf, sizeof(abuf));
428
    htsmsg_add_str(m, "group", abuf);
516
  if(t->s_iptv_url != 0){
517
      htsmsg_add_str(m, "url", t->s_iptv_url);
518
  }
519
  else{
520
    if(t->s_iptv_group.s_addr!= 0) {
521
      inet_ntop(AF_INET, &t->s_iptv_group, abuf, sizeof(abuf));
522
      htsmsg_add_str(m, "group", abuf);
523
    }
429 524
  }
430 525
  if(IN6_IS_ADDR_MULTICAST(t->s_iptv_group6.s6_addr) ) {
431 526
    inet_ntop(AF_INET6, &t->s_iptv_group6, abuf6, sizeof(abuf6));
......
435 530
    htsmsg_add_str(m, "channelname", t->s_ch->ch_name);
436 531
    htsmsg_add_u32(m, "mapped", 1);
437 532
  }
438
  
533

  
439 534
  pthread_mutex_lock(&t->s_stream_mutex);
535
  service_make_nicename(t);
440 536
  psi_save_service_settings(m, t);
441 537
  pthread_mutex_unlock(&t->s_stream_mutex);
442
  
443
  hts_settings_save(m, "iptvservices/%s",
444
		    t->s_identifier);
538

  
539
  hts_settings_save(m, "iptvservices/%s", t->s_identifier);
445 540

  
446 541
  htsmsg_destroy(m);
447 542
}
......
453 548
static int
454 549
iptv_service_quality(service_t *t)
455 550
{
456
  if(t->s_iptv_iface == NULL || 
457
     (t->s_iptv_group.s_addr == 0 && t->s_iptv_group6.s6_addr == 0) ||
458
     t->s_iptv_port == 0)
551
  if(t->s_iptv_url == NULL && (t->s_iptv_iface == NULL || (t->s_iptv_group.s_addr == 0 && t->s_iptv_group6.s6_addr == 0) || t->s_iptv_port == 0))
459 552
    return 0;
460 553

  
461 554
  return 100;
......
481 574

  
482 575
  si->si_type = S_MPEG_TS;
483 576
  si->si_adapter = t->s_iptv_iface ? strdup(t->s_iptv_iface) : NULL;
484
  if(t->s_iptv_group.s_addr != 0) {
485
    si->si_mux = strdup(inet_ntoa(t->s_iptv_group));
577
  if(t->s_iptv_url != 0){
578
    si->si_mux = strdup(t->s_iptv_url);
486 579
  }
487
  else {
488
    inet_ntop(AF_INET6, &t->s_iptv_group6, straddr, sizeof(straddr));
489
    si->si_mux = strdup(straddr);
580
  else{
581
    if(t->s_iptv_group.s_addr != 0) {
582
      si->si_mux = strdup(inet_ntoa(t->s_iptv_group));
583
    }
584
    else {
585
      inet_ntop(AF_INET6, &t->s_iptv_group6, straddr, sizeof(straddr));
586
      si->si_mux = strdup(straddr);
587
    }
490 588
  }
491 589
}
492 590

  
......
528 626

  
529 627
    LIST_FOREACH(t, &iptv_all_services, s_group_link)
530 628
      if(!strcmp(t->s_identifier, id))
531
	return t;
629
        return t;
532 630
  }
533 631

  
534 632
  if(create == 0)
......
588 686
    else
589 687
      old = 1;
590 688
  }
591
  
689

  
592 690
  HTSMSG_FOREACH(f, l) {
593 691
    if((c = htsmsg_get_map_by_field(f)) == NULL)
594 692
      continue;
595 693

  
596 694
    if(htsmsg_get_u32(c, "pmt", &pmt))
597 695
      continue;
598
    
696

  
599 697
    t = iptv_service_find(f->hmf_name, 1);
600 698
    t->s_pmt_pid = pmt;
601 699

  
602 700
    tvh_str_update(&t->s_iptv_iface, htsmsg_get_str(c, "interface"));
603 701

  
604
    if((s = htsmsg_get_str(c, "group")) != NULL){
605
      if (!inet_pton(AF_INET, s, &t->s_iptv_group.s_addr)) {
606
         inet_pton(AF_INET6, s, &t->s_iptv_group6.s6_addr);
702
    if((s = htsmsg_get_str(c, "url")) != NULL){
703
      tvh_str_update(&t->s_iptv_url, htsmsg_get_str(c, "url"));
704
    }
705
    else{
706
      if((s = htsmsg_get_str(c, "group")) != NULL){
707
        if (!inet_pton(AF_INET, s, &t->s_iptv_group.s_addr)) {
708
          inet_pton(AF_INET6, s, &t->s_iptv_group6.s6_addr);
709
        }
607 710
      }
608 711
    }
609
    
712

  
610 713
    if(!htsmsg_get_u32(c, "port", &u32))
611 714
      t->s_iptv_port = u32;
612 715

  
......
622 725
    service_make_nicename(t);
623 726
    psi_load_service_settings(c, t);
624 727
    pthread_mutex_unlock(&t->s_stream_mutex);
625
    
728

  
626 729
    s = htsmsg_get_str(c, "channelname");
627 730
    if(htsmsg_get_u32(c, "mapped", &u32))
628 731
      u32 = 0;
629
    
732

  
630 733
    if(s && u32)
631 734
      service_map_channel(t, channel_find_by_name(s, 1, 0), 0);
632 735

  
new/src/service.h 2013-10-04 08:00:04.000000000 +0200
377 377
   * IPTV members
378 378
   */
379 379
  char *s_iptv_iface;
380
  char *s_iptv_url;
380 381
  struct in_addr s_iptv_group;
381 382
  struct in6_addr s_iptv_group6;
382 383
  uint16_t s_iptv_port;
383 384
  int s_iptv_fd;
385
  uint8_t s_iptv_tsb[188];
386
  int s_iptv_tsb_len;
384 387

  
385 388
  /**
386 389
   * For per-transport PAT/PMT parsers, allocated on demand
new/src/webui/extjs.c 2013-10-04 08:00:04.000000000 +0200
1744 1744
      save = 1;
1745 1745
    }
1746 1746

  
1747
    t->s_iptv_url = NULL;
1748

  
1747 1749
    if((s = htsmsg_get_str(c, "group")) != NULL) {
1748
      if(!inet_pton(AF_INET, s, &t->s_iptv_group.s_addr)){
1749
      	inet_pton(AF_INET6, s, &t->s_iptv_group6.s6_addr);
1750
      if(!strncmp(s, "http", 4)){
1751
        tvh_str_update(&t->s_iptv_url, s);
1752
      }
1753
      else{
1754
        if(!inet_pton(AF_INET, s, &t->s_iptv_group.s_addr)){
1755
          inet_pton(AF_INET6, s, &t->s_iptv_group6.s6_addr);
1756
        }
1750 1757
      }
1751 1758
      save = 1;
1752 1759
    }
......
1772 1779
  htsmsg_add_str(r, "channelname", t->s_ch ? t->s_ch->ch_name : "");
1773 1780
  htsmsg_add_str(r, "interface", t->s_iptv_iface ?: "");
1774 1781

  
1775
  if(t->s_iptv_group.s_addr != 0){
1776
    inet_ntop(AF_INET, &t->s_iptv_group, abuf, sizeof(abuf));
1777
    htsmsg_add_str(r, "group", t->s_iptv_group.s_addr ? abuf : "");
1782
  if(t->s_iptv_url != 0){
1783
    htsmsg_add_str(r, "group", t->s_iptv_url ?: "");
1778 1784
  }
1779
  else {
1780
    inet_ntop(AF_INET6, &t->s_iptv_group6, abuf6, sizeof(abuf6));
1781
    htsmsg_add_str(r, "group", t->s_iptv_group6.s6_addr ? abuf6 : "");
1785
  else{
1786
    if(t->s_iptv_group.s_addr != 0){
1787
      inet_ntop(AF_INET, &t->s_iptv_group, abuf, sizeof(abuf));
1788
      htsmsg_add_str(r, "group", t->s_iptv_group.s_addr ? abuf : "");
1789
    }
1790
    else {
1791
      inet_ntop(AF_INET6, &t->s_iptv_group6, abuf6, sizeof(abuf6));
1792
      htsmsg_add_str(r, "group", t->s_iptv_group6.s6_addr ? abuf6 : "");
1793
    }
1782 1794
  }
1783 1795

  
1784 1796
  htsmsg_add_u32(r, "port", t->s_iptv_port);
new/src/webui/static/app/iptv.js 2013-10-04 08:00:04.000000000 +0200
48 48
		{
49 49
			header : "Channel name",
50 50
			dataIndex : 'channelname',
51
			width : 150,
51
			width : 100,
52 52
			renderer : function(value, metadata, record, row, col, store) {
53 53
				return value ? value
54 54
					: '<span class="tvh-grid-unset">Unmapped</span>';
......
67 67
		{
68 68
			header : "Interface",
69 69
			dataIndex : 'interface',
70
			width : 100,
70
			width : 50,
71 71
			renderer : function(value, metadata, record, row, col, store) {
72 72
				return value ? value : '<span class="tvh-grid-unset">Unset</span>';
73 73
			},
......
76 76
			})
77 77
		},
78 78
		{
79
			header : "Group",
79
			header : "Address / Group",
80 80
			dataIndex : 'group',
81
			width : 100,
81
			width : 150,
82 82
			renderer : function(value, metadata, record, row, col, store) {
83 83
				return value ? value : '<span class="tvh-grid-unset">Unset</span>';
84 84
			},
......
89 89
		{
90 90
			header : "UDP Port",
91 91
			dataIndex : 'port',
92
			width : 60,
92
			width : 50,
93 93
			editor : new fm.NumberField({
94 94
				minValue : 1,
95 95
				maxValue : 65535
(1-1/6)