Project

General

Profile

Feature #1372 » iptv_input_v3.patch

HTTP IPTV input support v3 - Zdeněk Kopřivík, 2014-05-05 16:26

View differences:

tvheadend-3.4patch2/src/iptv_input.c 2014-04-04 16: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
 */
......
101 153
  uint16_t pid = ((tsb[1] & 0x1f) << 8) | tsb[2];
102 154

103 155
  if(pid == 0) {
104

105 156
    if(t->s_pat_section == NULL)
106 157
      t->s_pat_section = calloc(1, sizeof(psi_section_t));
107 158
    psi_section_reassemble(t->s_pat_section, tsb, 1, iptv_got_pat, t);
......
124 175
static void *
125 176
iptv_thread(void *aux)
126 177
{
127
  int nfds, fd, r, j, hlen;
128
  uint8_t tsb[65536], *buf;
178
  int nfds, fd, type, tlen = sizeof(int), r, j, hlen, err = 0;
179
  uint8_t *tsb, *buf, real_buf[65536];
129 180
  struct epoll_event ev;
130 181
  service_t *t;
131 182

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

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

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

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

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

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

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

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

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

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;
222
        // Skip over extension header (last 2 bytes of header is length)
223
        hlen += ((tsb[hlen + 2] << 8) | tsb[hlen + 3]) * 4;
224
        // Add the extension header itself (EHL does not inc header)
225
        hlen += 4;
173 226
      }
174 227

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

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

182 235
    pthread_mutex_lock(&iptv_recvmutex);
183
    
236

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

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

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

255
        for(j = 0; j <= r - 188; j += 188) {
256
          iptv_ts_input(t, buf + j);
257
        }
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 - 188; 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, timeout;
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
    timeout = 2000;
324
    while (1) {
325
      if (!(c = read(fd, buf+i, 1))) {
326
        usleep(100000);
327
        timeout-=100;
328
        if (timeout <= 0) {
329
          tvhlog(LOG_ERR, "IPTV", "Timeout waiting for HTTP header");
330
          return -1;
331
        }
332
        continue;
333
      }
334
      i++;
335
      if (i == 4 && !strncmp(buf, "\r\n\r\n", 4))
336
        break;
337
      memmove(buf, buf+1, 3); i = 3;
258 338
    }
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;
339
  }
340
  else {
341
    /* Now, open the real socket for UDP */
342
    if(t->s_iptv_group.s_addr!=0) {
343
      fd = tvh_socket(AF_INET, SOCK_DGRAM, 0);
271 344
    }
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);
345
    else {
346
      fd = tvh_socket(AF_INET6, SOCK_DGRAM, 0);
347
    }
348
    if(fd == -1) {
349
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot open socket", t->s_identifier);
285 350
      return -1;
286 351
    }
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));
352

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(fd, SIOCGIFINDEX, &ifr)) {
358
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s", t->s_identifier, t->s_iptv_iface);
298 359
      close(fd);
299 360
      return -1;
300 361
    }
301
  }
302 362

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

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

404
      if(setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m6, sizeof(struct ipv6_mreq)) == -1) {
405
        inet_ntop(AF_INET6, m6.ipv6mr_multiaddr.s6_addr, straddr, sizeof(straddr));
406
        tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot join %s -- %s", t->s_identifier, straddr, strerror(errno));
407
        close(fd);
408
        return -1;
409
      }
410
    }
303 411

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));
412
    int resize = 262142;
413
    if(setsockopt(fd,SOL_SOCKET,SO_RCVBUF, &resize, sizeof(resize)) == -1)
414
      tvhlog(LOG_WARNING, "IPTV", "Can not icrease UDP receive buffer size to %d -- %s", resize, strerror(errno));
415
  }
309 416

310 417
  memset(&ev, 0, sizeof(ev));
311 418
  ev.events = EPOLLIN;
312 419
  ev.data.fd = fd;
313 420
  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));
421
    tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot add to epoll set -- %s", t->s_identifier, strerror(errno));
316 422
    close(fd);
317 423
    return -1;
318 424
  }
......
350 456

351 457
  assert(t->s_iptv_fd >= 0);
352 458

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];
459
  if(t->s_iptv_url == NULL) {
460
    /* First, resolve interface name */
461
    memset(&ifr, 0, sizeof(ifr));
462
    snprintf(ifr.ifr_name, IFNAMSIZ, "%s", t->s_iptv_iface);
463
    ifr.ifr_name[IFNAMSIZ - 1] = 0;
464
    if(ioctl(t->s_iptv_fd, SIOCGIFINDEX, &ifr)) {
465
      tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot find interface %s", t->s_identifier, t->s_iptv_iface);
466
    }
467

468
    if(t->s_iptv_group.s_addr != 0) {
469
      struct ip_mreqn m;
470
      memset(&m, 0, sizeof(m));
471
      /* Leave multicast group */
472
      m.imr_multiaddr.s_addr = t->s_iptv_group.s_addr;
473
      m.imr_address.s_addr = 0;
474
      m.imr_ifindex = ifr.ifr_ifindex;
378 475

379
    struct ipv6_mreq m6;
380
    memset(&m6, 0, sizeof(m6));
381

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));
476
      if(setsockopt(t->s_iptv_fd, SOL_IP, IP_DROP_MEMBERSHIP, &m, sizeof(struct ip_mreqn)) == -1) {
477
        tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s", t->s_identifier, inet_ntoa(m.imr_multiaddr), strerror(errno));
478
      }
479
    } else {
480
      char straddr[INET6_ADDRSTRLEN];
389 481

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

485
      m6.ipv6mr_multiaddr = t->s_iptv_group6;
486
      m6.ipv6mr_interface = ifr.ifr_ifindex;
394 487

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

491
        tvhlog(LOG_ERR, "IPTV", "\"%s\" cannot leave %s -- %s", t->s_identifier, straddr, strerror(errno));
492
      }
493
    }
396 494
  }
397 495
  close(t->s_iptv_fd); // Automatically removes fd from epoll set
398 496

......
423 521
  if(t->s_iptv_iface)
424 522
    htsmsg_add_str(m, "interface", t->s_iptv_iface);
425 523

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);
524
  if(t->s_iptv_url != 0){
525
      htsmsg_add_str(m, "url", t->s_iptv_url);
526
  }
527
  else{
528
    if(t->s_iptv_group.s_addr!= 0) {
529
      inet_ntop(AF_INET, &t->s_iptv_group, abuf, sizeof(abuf));
530
      htsmsg_add_str(m, "group", abuf);
531
    }
429 532
  }
430 533
  if(IN6_IS_ADDR_MULTICAST(t->s_iptv_group6.s6_addr) ) {
431 534
    inet_ntop(AF_INET6, &t->s_iptv_group6, abuf6, sizeof(abuf6));
......
435 538
    htsmsg_add_str(m, "channelname", t->s_ch->ch_name);
436 539
    htsmsg_add_u32(m, "mapped", 1);
437 540
  }
438
  
541

439 542
  pthread_mutex_lock(&t->s_stream_mutex);
543
  service_make_nicename(t);
440 544
  psi_save_service_settings(m, t);
441 545
  pthread_mutex_unlock(&t->s_stream_mutex);
442
  
443
  hts_settings_save(m, "iptvservices/%s",
444
		    t->s_identifier);
546

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

446 549
  htsmsg_destroy(m);
447 550
}
......
453 556
static int
454 557
iptv_service_quality(service_t *t)
455 558
{
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)
559
  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 560
    return 0;
460 561

461 562
  return 100;
......
481 582

482 583
  si->si_type = S_MPEG_TS;
483 584
  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));
585
  if(t->s_iptv_url != 0){
586
    si->si_mux = strdup(t->s_iptv_url);
486 587
  }
487
  else {
488
    inet_ntop(AF_INET6, &t->s_iptv_group6, straddr, sizeof(straddr));
489
    si->si_mux = strdup(straddr);
588
  else{
589
    if(t->s_iptv_group.s_addr != 0) {
590
      si->si_mux = strdup(inet_ntoa(t->s_iptv_group));
591
    }
592
    else {
593
      inet_ntop(AF_INET6, &t->s_iptv_group6, straddr, sizeof(straddr));
594
      si->si_mux = strdup(straddr);
595
    }
490 596
  }
491 597
}
492 598

......
528 634

529 635
    LIST_FOREACH(t, &iptv_all_services, s_group_link)
530 636
      if(!strcmp(t->s_identifier, id))
531
	return t;
637
        return t;
532 638
  }
533 639

534 640
  if(create == 0)
......
588 694
    else
589 695
      old = 1;
590 696
  }
591
  
697

592 698
  HTSMSG_FOREACH(f, l) {
593 699
    if((c = htsmsg_get_map_by_field(f)) == NULL)
594 700
      continue;
595 701

596 702
    if(htsmsg_get_u32(c, "pmt", &pmt))
597 703
      continue;
598
    
704

599 705
    t = iptv_service_find(f->hmf_name, 1);
600 706
    t->s_pmt_pid = pmt;
601 707

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

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);
710
    if((s = htsmsg_get_str(c, "url")) != NULL){
711
      tvh_str_update(&t->s_iptv_url, htsmsg_get_str(c, "url"));
712
    }
713
    else{
714
      if((s = htsmsg_get_str(c, "group")) != NULL){
715
        if (!inet_pton(AF_INET, s, &t->s_iptv_group.s_addr)) {
716
          inet_pton(AF_INET6, s, &t->s_iptv_group6.s6_addr);
717
        }
607 718
      }
608 719
    }
609
    
720

610 721
    if(!htsmsg_get_u32(c, "port", &u32))
611 722
      t->s_iptv_port = u32;
612 723

......
622 733
    service_make_nicename(t);
623 734
    psi_load_service_settings(c, t);
624 735
    pthread_mutex_unlock(&t->s_stream_mutex);
625
    
736

626 737
    s = htsmsg_get_str(c, "channelname");
627 738
    if(htsmsg_get_u32(c, "mapped", &u32))
628 739
      u32 = 0;
629
    
740

630 741
    if(s && u32)
631 742
      service_map_channel(t, channel_find_by_name(s, 1, 0), 0);
632 743

tvheadend-3.4patch2/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
tvheadend-3.4patch2/src/version.c 2013-10-02 08:00:02.000000000 +0200
1
const char *tvheadend_version = "";
tvheadend-3.4patch2/src/webui/extjs.c 2014-04-24 16:00:04.000000000 +0200
1745 1745
    }
1746 1746

1747 1747
    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);
1748
      if(!strncmp(s, "http", 4)){
1749
        tvh_str_update(&t->s_iptv_url, s);
1750
      }
1751
      else {
1752
        t->s_iptv_url = NULL;
1753

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
    }
1753
    
1754 1760

1755 1761
    save |= tvh_str_update(&t->s_iptv_iface, htsmsg_get_str(c, "interface"));
1756 1762
    if(save)
......
1772 1778
  htsmsg_add_str(r, "channelname", t->s_ch ? t->s_ch->ch_name : "");
1773 1779
  htsmsg_add_str(r, "interface", t->s_iptv_iface ?: "");
1774 1780

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

1784 1795
  htsmsg_add_u32(r, "port", t->s_iptv_port);
......
1796 1807
  service_t *a = *(service_t **)A;
1797 1808
  service_t *b = *(service_t **)B;
1798 1809

1799
  return memcmp(&a->s_iptv_group, &b->s_iptv_group, 4);
1810
  if(a->s_iptv_url != 0 && b->s_iptv_url != 0)
1811
    return strcmp(a->s_iptv_url, b->s_iptv_url);
1812
  else
1813
    return memcmp(&a->s_iptv_group, &b->s_iptv_group, 4);
1800 1814
}
1801 1815

1802 1816
/**
tvheadend-3.4patch2/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
(6-6/6)