Browse Source

Initial import.

Georgi Chorbadzhiyski 12 years ago
commit
61560683cf
38 changed files with 5318 additions and 0 deletions
  1. 2
    0
      .gitignore
  2. 6
    0
      .gitmodules
  3. 56
    0
      Makefile
  4. 8
    0
      TODO
  5. 625
    0
      config.c
  6. 84
    0
      config.h
  7. 389
    0
      data.c
  8. 230
    0
      data.h
  9. 428
    0
      inidict.c
  10. 174
    0
      inidict.h
  11. 712
    0
      iniparser.c
  12. 309
    0
      iniparser.h
  13. 420
    0
      input.c
  14. 6
    0
      input.h
  15. 1
    0
      libfuncs
  16. 1
    0
      libtsfuncs
  17. 167
    0
      mptsd.c
  18. 12
    0
      mptsd.conf
  19. 63
    0
      mptsd_channels.conf
  20. 70
    0
      mptsd_epg.conf
  21. 50
    0
      mptsd_nit.conf
  22. 11
    0
      mptsd_valgrind
  23. 294
    0
      network.c
  24. 10
    0
      network.h
  25. 13
    0
      output.h
  26. 215
    0
      output_mix.c
  27. 249
    0
      output_psi.c
  28. 211
    0
      output_write.c
  29. 85
    0
      pidref.c
  30. 29
    0
      pidref.h
  31. 149
    0
      rc.mptsd
  32. 25
    0
      rc.mptsd.conf
  33. 43
    0
      sleep.c
  34. 6
    0
      sleep.h
  35. 27
    0
      web_pages.c
  36. 7
    0
      web_pages.h
  37. 122
    0
      web_server.c
  38. 9
    0
      web_server.h

+ 2
- 0
.gitignore View File

@@ -0,0 +1,2 @@
1
+*.o
2
+mptsd

+ 6
- 0
.gitmodules View File

@@ -0,0 +1,6 @@
1
+[submodule "libfuncs"]
2
+	path = libfuncs
3
+	url = git://github.com/gfto/libfuncs.git
4
+[submodule "libtsfuncs"]
5
+	path = libtsfuncs
6
+	url = git://github.com/gfto/libtsfuncs.git

+ 56
- 0
Makefile View File

@@ -0,0 +1,56 @@
1
+CC = $(CROSS)$(TARGET)gcc
2
+STRIP = $(CROSS)$(TARGET)strip
3
+BUILD_ID = $(shell date +%F_%R)
4
+GIT_VER = $(shell git describe --tags --dirty --always)
5
+CFLAGS = -ggdb -Wall -Wextra -Wshadow -Wformat-security -Wno-strict-aliasing -O2 -D_GNU_SOURCE -DBUILD_ID=\"$(BUILD_ID)\" -DGIT_VER=\"$(GIT_VER)\"
6
+RM = /bin/rm -f
7
+Q = @
8
+
9
+LIBS = -lpthread -lm -lrt
10
+
11
+FUNCS_DIR = libfuncs
12
+FUNCS_LIB = $(FUNCS_DIR)/libfuncs.a
13
+
14
+TSFUNCS_DIR = libtsfuncs
15
+TSFUNCS_LIB = $(TSFUNCS_DIR)/libtsfuncs.a
16
+
17
+mptsd_OBJS =  $(FUNCS_LIB) $(TSFUNCS_LIB) \
18
+	iniparser.o inidict.o pidref.o data.o config.o \
19
+	sleep.o network.o \
20
+	input.o \
21
+	output_psi.o output_mix.o output_write.o \
22
+	web_pages.o web_server.o \
23
+	mptsd.o
24
+
25
+PROGS = mptsd
26
+CLEAN_OBJS = $(PROGS) $(mptsd_OBJS) *~
27
+
28
+all: $(PROGS)
29
+
30
+$(FUNCS_LIB):
31
+	$(Q)echo "  MAKE	$(FUNCS_LIB)"
32
+	$(Q)$(MAKE) -s -C $(FUNCS_DIR)
33
+
34
+$(TSFUNCS_LIB):
35
+	$(Q)echo "  MAKE	$(TSFUNCS_LIB)"
36
+	$(Q)$(MAKE) -s -C $(TSFUNCS_DIR)
37
+
38
+mptsd: $(mptsd_OBJS)
39
+	$(Q)echo "  LINK	mptsd"
40
+	$(Q)$(CC) $(CFLAGS) $(mptsd_OBJS) $(LIBS) -o mptsd
41
+
42
+%.o: %.c data.h
43
+	$(Q)echo "  CC	mptsd		$<"
44
+	$(Q)$(CC) $(CFLAGS)  -c $<
45
+
46
+strip:
47
+	$(Q)echo "  STRIP	$(PROGS)"
48
+	$(Q)$(STRIP) $(PROGS)
49
+
50
+clean:
51
+	$(Q)echo "  RM	$(CLEAN_OBJS)"
52
+	$(Q)$(RM) $(CLEAN_OBJS)
53
+
54
+distclean: clean
55
+	$(Q)$(MAKE) -s -C $(TSFUNCS_DIR) clean
56
+	$(Q)$(MAKE) -s -C $(FUNCS_DIR) clean

+ 8
- 0
TODO View File

@@ -0,0 +1,8 @@
1
+mptsd todo
2
+- Add web access to stats
3
+- Support rewritting of incomming EIT tables
4
+- Add live reconfiguration
5
+  - Adding removing channels
6
+  - Changing of output address and bitrate
7
+  - Reloading of nit/eit
8
+- Add support for schedule eit

+ 625
- 0
config.c View File

@@ -0,0 +1,625 @@
1
+#include <stdio.h>
2
+#include <stdlib.h>
3
+#include <unistd.h>
4
+#include <string.h>
5
+#include <errno.h>
6
+#include <arpa/inet.h>
7
+#include <netinet/in.h>
8
+#include <sys/types.h>
9
+#include <sys/socket.h>
10
+#include <sys/types.h>
11
+#include <sys/stat.h>
12
+#include <sys/time.h>
13
+#include <sys/stat.h>
14
+#include <signal.h>
15
+#include <fcntl.h>
16
+#include <regex.h>
17
+#include <math.h>
18
+#include <netdb.h> // for uint32_t
19
+
20
+#include "libfuncs/log.h"
21
+#include "libfuncs/list.h"
22
+#include "libfuncs/server.h"
23
+
24
+#include "libtsfuncs/tsfuncs.h"
25
+
26
+#include "data.h"
27
+#include "sleep.h"
28
+#include "config.h"
29
+#include "inidict.h"
30
+#include "iniparser.h"
31
+
32
+extern char *server_sig;
33
+extern char *server_ver;
34
+extern char *copyright;
35
+
36
+int is_valid_url(char *url) {
37
+	regex_t re;
38
+	regmatch_t res[5];
39
+	int ret;
40
+	regcomp(&re, "^([a-z]+)://([^:/?]+):?([0-9]*)/?(.*)", REG_EXTENDED);
41
+	ret = regexec(&re,url,5,res,0);
42
+	regfree(&re);
43
+	return ret == 0;
44
+}
45
+
46
+CONFIG *config_alloc() {
47
+	CONFIG *conf = calloc(1, sizeof(CONFIG));
48
+	conf->inputs = list_new("input");
49
+	conf->nit    = list_new("nit");
50
+	conf->output = output_new();
51
+	return conf;
52
+}
53
+
54
+void config_free(CONFIG **pconf) {
55
+	CONFIG *conf = *pconf;
56
+	if (conf) {
57
+		conf->output->dienow = 1;
58
+
59
+		list_free(&conf->nit, NULL, nit_free);
60
+		list_free(&conf->inputs, NULL, input_free);
61
+		list_free(&conf->channels, NULL, channel_free);
62
+		output_free(&conf->output);
63
+
64
+		FREE(conf->ident);
65
+		FREE(conf->pidfile);
66
+		FREE(conf->server_addr);
67
+		FREE(conf->loghost);
68
+		FREE(conf->logident);
69
+		FREE(conf->global_conf);
70
+		FREE(conf->channels_conf);
71
+		FREE(conf->nit_conf);
72
+		FREE(conf->epg_conf);
73
+		FREE(conf->network_name);
74
+		FREE(conf->provider_name);
75
+		FREE(*pconf);
76
+	}
77
+}
78
+
79
+
80
+int config_load_channels(CONFIG *conf) {
81
+	int num_channels = 0, i, j;
82
+
83
+	LOGf("CONFIG: Loading channels config %s\n", conf->channels_conf);
84
+	dictionary *ini = iniparser_load(conf->channels_conf);
85
+	if (!ini) {
86
+		LOGf("CONFIG: Error loading channels config (%s)\n", conf->channels_conf);
87
+		return 0;
88
+	}
89
+
90
+	//iniparser_dump(ini, stdout);
91
+	LIST *channels = list_new("channels");
92
+
93
+	// Parse channels file
94
+	conf->provider_name = ini_get_string_copy(ini, NULL, "Global:provider_name");
95
+	conf->transport_stream_id = ini_get_int(ini, 0, "Global:transport_stream_id");
96
+	for (i=1;i<32;i++) {
97
+		CHANNEL *channel = NULL;
98
+		int service_id = ini_get_int(ini, 0, "Channel%d:service_id", i);
99
+		if (!service_id)
100
+			continue;
101
+
102
+		int is_radio = ini_get_bool(ini, 0, "Channel%d:radio", i);
103
+
104
+		char *id = ini_get_string(ini, NULL, "Channel%d:id", i);
105
+		if (!id) {
106
+			LOGf("CONFIG: Channel%d have no defined id\n", i);
107
+			continue;
108
+		}
109
+
110
+		char *name = ini_get_string(ini, NULL, "Channel%d:name", i);
111
+		if (!name) {
112
+			LOGf("CONFIG: Channel%d have no defined name\n", i);
113
+			continue;
114
+		}
115
+
116
+		for (j=1;j<8;j++) {
117
+			char *source = ini_get_string(ini, NULL, "Channel%d:source%d", i, j);
118
+			if (j == 1 && !source) {
119
+				source = ini_get_string(ini, NULL, "Channel%d:source", i);
120
+			}
121
+			if (source) {
122
+				if (!is_valid_url(source)) {
123
+					LOGf("CONFIG: Invalid url: %s\n", source);
124
+					continue;
125
+				}
126
+				// Init channel
127
+				if (channel == NULL) {
128
+					channel = channel_new(service_id, is_radio, id, name, source);
129
+				} else {
130
+					chansrc_add(channel, source);
131
+				}
132
+			}
133
+		}
134
+
135
+		char *worktime = ini_get_string(ini, NULL, "Channel%d:worktime", i);
136
+		int sh=0,sm=0,eh=0,em=0;
137
+		if (worktime && sscanf(worktime, "%02d:%02d-%02d:%02d", &sh, &sm, &eh, &em) == 4) {
138
+			channel->worktime_start = sh * 3600 + sm * 60;
139
+			channel->worktime_end   = eh * 3600 + em * 60;
140
+		}
141
+
142
+		// Channel is initialized, add it to channels list
143
+		if (channel) {
144
+			num_channels++;
145
+			list_add(channels, channel);
146
+		}
147
+	}
148
+//	iniparser_dump(ini, stderr);
149
+	iniparser_freedict(&ini);
150
+
151
+	// Check for channel changes
152
+	struct timeval tv;
153
+	gettimeofday(&tv, NULL);
154
+	unsigned int randstate = tv.tv_usec;
155
+	int cookie = rand_r(&randstate);
156
+
157
+	/* Save current channels config */
158
+	LIST *old_channels = conf->channels;
159
+
160
+	/* Switch new channels config */
161
+	conf->channels = channels;
162
+
163
+	/* Rewrite restreamer channels */
164
+	LNODE *lc, *lr, *lctmp, *lrtmp;
165
+	CHANNEL *chan;
166
+	list_lock(conf->inputs);	// Unlocked after second list_for_each(restreamer)
167
+	list_lock(conf->channels);
168
+	list_for_each(conf->channels, lc, lctmp) {
169
+		chan = lc->data;
170
+		list_for_each(conf->inputs, lr, lrtmp) {
171
+			if (strcmp(chan->name, ((INPUT *)lr->data)->name)==0) {
172
+				INPUT *restr = lr->data;
173
+				/* Mark the restreamer as valid */
174
+				restr->cookie = cookie;
175
+				/* Check if current source exists in new channel configuration */
176
+				int src_found = -1;
177
+				char *old_source = restr->channel->source;
178
+				for (i=0; i<chan->num_src; i++) {
179
+					if (strcmp(old_source, chan->sources[i]) == 0) {
180
+						src_found = i;
181
+					}
182
+				}
183
+				if (src_found > -1) {
184
+					/* New configuration contains existing source, just update the reference */
185
+					chansrc_set(chan, src_found);
186
+					restr->channel = chan;
187
+				} else {
188
+					/* New configuration *DO NOT* contain existing source. Force reconnect */
189
+					LOGf("INPUT: Source changed | Channel: %s srv_fd: %d Old:%s New:%s\n", chan->name, restr->sock, restr->channel->source, chan->source);
190
+					/* The order is important! */
191
+					chansrc_set(chan, chan->num_src-1); /* Set source to last one. On reconnect next source will be used. */
192
+					restr->channel = chan;
193
+					restr->reconnect = 1;
194
+				}
195
+				break;
196
+			}
197
+		}
198
+	}
199
+	list_unlock(conf->channels);
200
+
201
+	/* Kill restreamers that serve channels that no longer exist */
202
+	list_for_each(conf->inputs, lr, lrtmp) {
203
+		INPUT *r = lr->data;
204
+		/* This restreamer should no longer serve clients */
205
+		if (r->cookie != cookie) {
206
+			proxy_log(r, "Remove");
207
+			/* Replace channel reference with real object and instruct free_restreamer to free it */
208
+			r->channel = channel_new(r->channel->service_id, r->channel->radio, r->channel->id, r->channel->name, r->channel->source);
209
+			r->freechannel = 1;
210
+			r->dienow = 1;
211
+		}
212
+	}
213
+	list_unlock(conf->inputs);
214
+
215
+	/* Free old_channels */
216
+	list_free(&old_channels, NULL, channel_free);
217
+
218
+	LOGf("CONFIG: %d channels loaded\n", num_channels);
219
+	return num_channels;
220
+}
221
+
222
+int config_load_global(CONFIG *conf) {
223
+	LOGf("CONFIG: Loading global config %s\n", conf->global_conf);
224
+	dictionary *ini = iniparser_load(conf->global_conf);
225
+	if (!ini) {
226
+		LOGf("CONFIG: Error loading global config (%s)\n", conf->global_conf);
227
+		return 0;
228
+	}
229
+	conf->network_id		= ini_get_int(ini, 0,    "Global:network_id");
230
+	conf->timeouts.pat		= ini_get_int(ini, 100,  "Timeouts:pat");
231
+	conf->timeouts.pmt		= ini_get_int(ini, 200,  "Timeouts:pmt");
232
+	conf->timeouts.sdt		= ini_get_int(ini, 500,  "Timeouts:sdt");
233
+	conf->timeouts.nit		= ini_get_int(ini, 2000, "Timeouts:nit");
234
+	conf->timeouts.eit		= ini_get_int(ini, 1000, "Timeouts:eit");
235
+	conf->timeouts.tdt		= ini_get_int(ini, 7500, "Timeouts:tdt");
236
+	conf->timeouts.tot		= ini_get_int(ini, 1500, "Timeouts:tot");
237
+	conf->timeouts.stats	= ini_get_int(ini, 0,    "Timeouts:stats");
238
+	//iniparser_dump(ini, stderr);
239
+	iniparser_freedict(&ini);
240
+	return 1;
241
+}
242
+
243
+int config_load_nit(CONFIG *conf) {
244
+	LOGf("CONFIG: Loading nit config %s\n", conf->nit_conf);
245
+	dictionary *ini = iniparser_load(conf->nit_conf);
246
+	if (!ini) {
247
+		LOGf("CONFIG: Error loading nit config (%s)\n", conf->nit_conf);
248
+		return 0;
249
+	}
250
+	conf->network_name = ini_get_string_copy(ini, NULL, "Global:network_name");
251
+	int i;
252
+	for (i=1;i<32;i++) {
253
+		uint16_t ts_id = ini_get_int   (ini, 0, "Transponder%d:transport_stream_id", i);
254
+		char *freq     = ini_get_string(ini, NULL, "Transponder%d:frequency", i);
255
+		char *mod      = ini_get_string(ini, NULL, "Transponder%d:modulation", i);
256
+		char *sr       = ini_get_string(ini, NULL, "Transponder%d:symbol_rate", i);
257
+		if (ts_id && freq && mod && sr) {
258
+			if (strlen(freq) == 9 && strlen(sr) == 8) {
259
+				list_add(conf->nit, nit_new(ts_id, freq, mod, sr));
260
+			}
261
+		}
262
+	}
263
+	//iniparser_dump(ini, stderr);
264
+	iniparser_freedict(&ini);
265
+	return 1;
266
+}
267
+
268
+static void config_reset_channels_epg(CONFIG *conf) {
269
+	LNODE *lc, *lctmp;
270
+	list_lock(conf->channels);
271
+	list_for_each(conf->channels, lc, lctmp) {
272
+		CHANNEL *c = lc->data;
273
+		channel_free_epg(c);
274
+	}
275
+	list_unlock(conf->channels);
276
+	conf->epg_conf_mtime = 0;
277
+}
278
+
279
+static EPG_ENTRY *config_load_epg_entry(dictionary *ini, char *entry, char *channel) {
280
+	EPG_ENTRY *e = NULL;
281
+	time_t start = ini_get_int(ini, 0, "%s-%s:start", channel, entry);
282
+	int duration = ini_get_int(ini, 0, "%s-%s:duration", channel, entry);
283
+	char *event  = ini_get_string(ini, NULL, "%s-%s:event", channel, entry);
284
+	char *sdesc  = ini_get_string(ini, NULL, "%s-%s:sdescr", channel, entry);
285
+	char *ldesc  = ini_get_string(ini, NULL, "%s-%s:descr", channel, entry);
286
+	char *enc    = ini_get_string(ini, NULL, "%s-%s:encoding", channel, entry);
287
+	if (start && duration && event) {
288
+		e = epg_new(start, duration, enc, event, sdesc, ldesc);
289
+	}
290
+	return e;
291
+}
292
+
293
+static void config_channel_init_epg(CONFIG *conf, CHANNEL *c, EPG_ENTRY *now, EPG_ENTRY *next) {
294
+	int updated = 0;
295
+
296
+	if (epg_changed(now, c->epg_now)) {
297
+		LOGf("EPG  : %s | Now data changed\n", c->id);
298
+		updated++;
299
+	}
300
+
301
+	if (epg_changed(next, c->epg_next)) {
302
+		LOGf("EPG  : %s | Next data changed\n", c->id);
303
+		updated++;
304
+	}
305
+
306
+	if (!updated)
307
+		return;
308
+
309
+	// LOGf("EPG  : %s | Version %d\n", c->epg_version);
310
+	c->epg_version++;
311
+
312
+	struct ts_eit *eit_now = ts_eit_alloc_init_pf(c->service_id, conf->transport_stream_id, conf->network_id, 0, 1);
313
+	eit_now->section_header->version_number = c->epg_version;
314
+	if (now) {
315
+		ts_eit_add_short_event_descriptor(eit_now, now->event_id, 1, now->start, now->duration, now->event, now->short_desc);
316
+		ts_eit_add_extended_event_descriptor(eit_now, now->event_id, 1, now->start, now->duration, now->long_desc);
317
+	}
318
+	ts_eit_regenerate_packets(eit_now);
319
+
320
+	struct ts_eit *eit_next = ts_eit_alloc_init_pf(c->service_id, conf->transport_stream_id, conf->network_id, 1, 1);
321
+	eit_next->section_header->version_number = c->epg_version;
322
+	if (next) {
323
+		ts_eit_add_short_event_descriptor(eit_next, next->event_id, 1, next->start, next->duration, next->event, next->short_desc);
324
+		ts_eit_add_extended_event_descriptor(eit_next, next->event_id, 1, next->start, next->duration, next->long_desc);
325
+	}
326
+	ts_eit_regenerate_packets(eit_next);
327
+
328
+	channel_free_epg(c);
329
+
330
+	c->epg_now  = now;
331
+	c->epg_next = next;
332
+
333
+	if (now || next) {
334
+		c->eit_now  = eit_now;
335
+		c->eit_next = eit_next;
336
+		//LOGf(" ***** NOW ******\n");
337
+		//ts_eit_dump(eit_now);
338
+		//LOGf(" ***** NEXT *****\n");
339
+		//ts_eit_dump(eit_next);
340
+	} else {
341
+		ts_eit_free(&eit_now);
342
+		ts_eit_free(&eit_next);
343
+	}
344
+}
345
+
346
+
347
+int config_load_epg(CONFIG *conf) {
348
+	struct stat st;
349
+	if (stat(conf->epg_conf, &st) == 0) {
350
+		if (st.st_mtime > conf->epg_conf_mtime) {
351
+			// Set config last change time
352
+			conf->epg_conf_mtime = st.st_mtime;
353
+		} else {
354
+			// LOGf("CONFIG: EPG config not changed since last check.\n");
355
+			return 0; // The config has not changed!
356
+		}
357
+	} else {
358
+		// Config file not found!
359
+		LOGf("CONFIG: EPG config file is not found (%s)\n", conf->epg_conf);
360
+		config_reset_channels_epg(conf);
361
+		return 0;
362
+	}
363
+
364
+	// LOGf("CONFIG: Loading EPG config %s\n", conf->epg_conf);
365
+	dictionary *ini = iniparser_load(conf->epg_conf);
366
+	if (!ini) {
367
+		LOGf("CONFIG: Error parsing EPG config (%s)\n", conf->epg_conf);
368
+		config_reset_channels_epg(conf);
369
+		return 0;
370
+	}
371
+
372
+	LNODE *lc, *lctmp;
373
+	list_lock(conf->channels);
374
+	list_for_each(conf->channels, lc, lctmp) {
375
+		CHANNEL *c = lc->data;
376
+		EPG_ENTRY *enow  = config_load_epg_entry(ini, "now", c->id);
377
+		EPG_ENTRY *enext = config_load_epg_entry(ini, "next", c->id);
378
+		config_channel_init_epg(conf, c, enow, enext);
379
+	}
380
+	list_unlock(conf->channels);
381
+	//iniparser_dump(ini, stderr);
382
+	iniparser_freedict(&ini);
383
+
384
+	return 1;
385
+}
386
+
387
+extern char *program_id;
388
+
389
+static void show_usage() {
390
+	printf("%s\n", program_id);
391
+	puts(copyright);
392
+	puts("");
393
+	puts("Identification:");
394
+	puts("\t-i ident\tServer ident.               (default: ux/mptsd)");
395
+	puts("");
396
+	puts("Server settings:");
397
+	puts("\t-b addr\t\tLocal IP address to bind.   (default: 0.0.0.0)");
398
+	puts("\t-p port\t\tPort to listen.             (default: 0)");
399
+	puts("\t-d pidfile\tDaemonize with pidfile");
400
+	puts("\t-l host\t\tSyslog host                 (default: disabled)");
401
+	puts("\t-L port\t\tSyslog port                 (default: 514)");
402
+	puts("");
403
+	puts("Configuration files:");
404
+	puts("\t-g file\t\tGlobal configuration file   (default: mptsd.conf)");
405
+	puts("\t-c file\t\tChannels configuration file (default: mptsd_channels.conf)");
406
+	puts("\t-e file\t\tEPG configuration file      (default: mptsd_epg.conf)");
407
+	puts("\t-n file\t\tNIT configuration file      (default: mptsd_nit.conf)");
408
+	puts("");
409
+	puts("Output settings:");
410
+	puts("\t-O ip\t\tOutput udp address");
411
+	puts("\t-P ip\t\tOutput udp port             (default: 5000)");
412
+	puts("\t-o ip\t\tOutput interface address    (default: 0.0.0.0)");
413
+	puts("\t-t ttl\t\tSet multicast ttl           (default: 1)");
414
+	puts("");
415
+	puts("\t-B Mbps\t\tOutput bitrate in Mbps      (default: 38.00)");
416
+	puts("\t-m mode\t\tPCR mode (modes list bellow)");
417
+	puts("\t\t\t- Mode 0: do not touch PCRs (default)");
418
+	puts("\t\t\t- Mode 1: move PCRs to their calculated place");
419
+	puts("\t\t\t- Mode 2: rewrite PCRs using output bitrate");
420
+	puts("\t\t\t- Mode 3: move PCRs and rewrite them");
421
+	puts("");
422
+	puts("Other settings:");
423
+	puts("\t-q\t\tQuiet");
424
+	puts("\t-D\t\tDebug");
425
+	puts("");
426
+	puts("\t-W\t\tWrite output file");
427
+	puts("\t-E\t\tWrite input file");
428
+	puts("");
429
+}
430
+
431
+void config_load(CONFIG *conf, int argc, char **argv) {
432
+	int j, ttl;
433
+
434
+	conf->multicast_ttl = 1;
435
+	conf->output->out_port = 5000;
436
+	conf->output_bitrate = 38;
437
+	conf->logport = 514;
438
+	conf->server_port = 0;
439
+	conf->server_socket = -1;
440
+
441
+	while ((j = getopt(argc, argv, "i:b:p:g:c:n:e:d:t:o:O:P:l:L:B:m:qDHhWE")) != -1) {
442
+		switch (j) {
443
+			case 'i':
444
+				conf->ident = strdup(optarg);
445
+				conf->logident = strdup(optarg);
446
+				char *c = conf->logident;
447
+				while (*c) {
448
+					if (*c=='/')
449
+						*c='-';
450
+					c++;
451
+				}
452
+				break;
453
+			case 'b':
454
+				conf->server_addr = strdup(optarg);
455
+				break;
456
+			case 'p':
457
+				conf->server_port = atoi(optarg);
458
+				break;
459
+			case 'd':
460
+				conf->pidfile = strdup(optarg);
461
+				break;
462
+			case 'g':
463
+				conf->global_conf = strdup(optarg);
464
+				break;
465
+			case 'c':
466
+				conf->channels_conf = strdup(optarg);
467
+				break;
468
+			case 'n':
469
+				conf->nit_conf = strdup(optarg);
470
+				break;
471
+			case 'e':
472
+				conf->epg_conf = strdup(optarg);
473
+				break;
474
+			case 'o':
475
+				if (inet_aton(optarg, &conf->output_intf) == 0) {
476
+					fprintf(stderr, "Invalid interface address: %s\n", optarg);
477
+					exit(1);
478
+				}
479
+				break;
480
+			case 'O':
481
+				if (inet_aton(optarg, &(conf->output->out_host)) == 0) {
482
+					fprintf(stderr, "Invalid output address: %s\n", optarg);
483
+					exit(1);
484
+				}
485
+				break;
486
+			case 'P':
487
+				conf->output->out_port = atoi(optarg);
488
+				if (!conf->output->out_port || conf->output->out_port < 1024) {
489
+					fprintf(stderr, "Invalid output port: %s\n", optarg);
490
+					exit(1);
491
+				}
492
+				break;
493
+			case 'm':
494
+				conf->pcr_mode = atoi(optarg);
495
+				if (conf->pcr_mode < 0 || conf->pcr_mode > 4)
496
+					conf->pcr_mode = 0;
497
+				break;
498
+			case 't':
499
+				ttl = atoi(optarg);
500
+				conf->multicast_ttl = (ttl && ttl < 127) ? ttl : 1;
501
+				break;
502
+			case 'l':
503
+				conf->loghost = strdup(optarg);
504
+				conf->syslog_active = 1;
505
+				break;
506
+			case 'L':
507
+				conf->logport = atoi(optarg);
508
+				break;
509
+			case 'B':
510
+				conf->output_bitrate = atof(optarg);
511
+				if (conf->output_bitrate < 2 || conf->output_bitrate > 75) {
512
+					fprintf(stderr, "Invalid output bitrate: %.2f (valid 2-75)\n", conf->output_bitrate);
513
+					exit(1);
514
+				}
515
+				break;
516
+			case 'W':
517
+				conf->write_output_file = 1;
518
+				output_open_file(conf->output);
519
+				break;
520
+			case 'E':
521
+				conf->write_input_file = 1;
522
+				break;
523
+			case 'D':
524
+				conf->debug = 1;
525
+				break;
526
+			case 'q':
527
+				conf->quiet = 1;
528
+				break;
529
+			case 'H':
530
+			case 'h':
531
+				show_usage(0);
532
+				exit(0);
533
+				break;
534
+		}
535
+	}
536
+	if (!conf->output->out_host.s_addr) {
537
+		fprintf(stderr, "ERROR: Output address is not set (use -O x.x.x.x)\n");
538
+		show_usage();
539
+		goto ERR;
540
+	}
541
+	// Set defaults
542
+	if (!conf->ident) {
543
+		conf->ident = strdup("ux/mptsd");
544
+		conf->logident = strdup("ux-mptsd");
545
+	}
546
+	if (!conf->global_conf) {
547
+		conf->global_conf = strdup("mptsd.conf");
548
+	}
549
+	if (!conf->channels_conf) {
550
+		conf->channels_conf = strdup("mptsd_channels.conf");
551
+	}
552
+	if (!conf->nit_conf) {
553
+		conf->nit_conf = strdup("mptsd_nit.conf");
554
+	}
555
+	if (!conf->epg_conf) {
556
+		conf->epg_conf = strdup("mptsd_epg.conf");
557
+	}
558
+
559
+	// Align bitrate to 1 packet (1316 bytes)
560
+	conf->output_bitrate        *= 1000000; // In bytes
561
+	conf->output_packets_per_sec = ceil(conf->output_bitrate / (1316 * 8));
562
+	conf->output_bitrate         = conf->output_packets_per_sec * (1316 * 8);
563
+	conf->output_tmout           = 1000000 / conf->output_packets_per_sec;
564
+
565
+	if (conf->server_port)
566
+		init_server_socket(conf->server_addr, conf->server_port, &conf->server, &conf->server_socket);
567
+
568
+	if (!conf->quiet) {
569
+		printf("Configuration:\n");
570
+		printf("\tServer ident      : %s\n", conf->ident);
571
+		printf("\tGlobal config     : %s\n", conf->global_conf);
572
+		printf("\tChannels config   : %s\n", conf->channels_conf);
573
+		printf("\tNIT config        : %s\n", conf->nit_conf);
574
+		printf("\tOutput addr       : udp://%s:%d\n", inet_ntoa(conf->output->out_host), conf->output->out_port);
575
+		if (conf->output_intf.s_addr)
576
+			printf("\tOutput iface addr : %s\n", inet_ntoa(conf->output_intf));
577
+		printf("\tMulticast ttl     : %d\n", conf->multicast_ttl);
578
+		if (conf->syslog_active) {
579
+			printf("\tSyslog host       : %s\n", conf->loghost);
580
+			printf("\tSyslog port       : %d\n", conf->logport);
581
+		} else {
582
+			printf("\tSyslog disabled.\n");
583
+		}
584
+		printf("\tOutput bitrate    : %.0f bps, %.2f Kbps, %.2f Mbps\n", conf->output_bitrate, conf->output_bitrate / 1000, conf->output_bitrate / 1000000);
585
+		printf("\tOutput pkt tmout  : %ld us\n", conf->output_tmout);
586
+		printf("\tPackets per second: %ld\n", conf->output_packets_per_sec);
587
+		printf("\tPCR mode          : %s\n",
588
+			conf->pcr_mode == 0 ? "Do not touch PCRs" :
589
+			conf->pcr_mode == 1 ? "Move PCRs to their calculated place" :
590
+			conf->pcr_mode == 2 ? "Rewrite PCRs using output bitrate" :
591
+			conf->pcr_mode == 3 ? "Move PCRs and rewrite them" : "???"
592
+		);
593
+		if (conf->write_output_file)
594
+			printf("\tWrite output file\n");
595
+		if (conf->write_input_file)
596
+			printf("\tWrite input file(s)\n");
597
+	}
598
+
599
+	pthread_t sleepthread;
600
+	if (pthread_create(&sleepthread, NULL, &calibrate_sleep, conf) == 0) {
601
+		pthread_join(sleepthread, NULL);
602
+	} else {
603
+		perror("calibrate_thread");
604
+		exit(1);
605
+	}
606
+	if (!conf->output_tmout)
607
+		exit(1);
608
+
609
+	output_buffer_alloc(conf->output, conf->output_bitrate);
610
+
611
+	if (!config_load_global(conf))
612
+		goto ERR;
613
+	if (!config_load_nit(conf))
614
+		goto ERR;
615
+	if (!config_load_channels(conf))
616
+		goto ERR;
617
+
618
+	config_load_epg(conf);
619
+
620
+	return;
621
+
622
+ERR:
623
+	config_free(&conf);
624
+	exit(1);
625
+}

+ 84
- 0
config.h View File

@@ -0,0 +1,84 @@
1
+#ifndef CONFIG_H
2
+#define CONFIG_H
3
+
4
+#include <pthread.h>
5
+#include <arpa/inet.h>
6
+#include <netinet/in.h>
7
+
8
+#include "libfuncs/list.h"
9
+
10
+#include "data.h"
11
+
12
+typedef struct {
13
+	char			*ident;
14
+	char			*pidfile;
15
+
16
+	int				syslog_active;
17
+	char			*logident;
18
+	char			*loghost;
19
+	int				logport;
20
+
21
+	struct sockaddr_in	server;
22
+	char				*server_addr;
23
+	int					server_port;
24
+	int					server_socket;
25
+	pthread_t			server_thread;
26
+
27
+	int				multicast_ttl;
28
+	struct in_addr	output_intf;
29
+
30
+	char			*global_conf;
31
+	char			*channels_conf;
32
+	char			*nit_conf;
33
+	char			*epg_conf;
34
+
35
+	int				debug;
36
+	int				quiet;
37
+
38
+	int				write_input_file;
39
+	int				write_output_file;
40
+	int				pcr_mode;			// 0 - do not touch PCRs
41
+										// 1 - move PCRs to their calculated place
42
+										// 2 - rewrite PCRs using output bitrate
43
+										// 3 - move PCRs and rewrite them
44
+
45
+	uint16_t		network_id;			// For NIT && SDT
46
+	uint16_t		transport_stream_id;// For NIT
47
+	char 			*network_name;		// For NIT
48
+	char 			*provider_name;		// For SDT
49
+
50
+	double			output_bitrate;		// Output bitrate (bps)
51
+	long			output_tmout;		// Smooth interval miliseconds
52
+	long			usleep_overhead;		// How much more usecs uslep(1) takes
53
+	long			output_packets_per_sec;	// How much packets should be sent in one second
54
+
55
+	time_t			epg_conf_mtime;		// Last change time of epg config
56
+
57
+	LIST *			channels;			// List of channels
58
+	LIST *			inputs;				// Input threads
59
+	LIST *			nit;
60
+	OUTPUT *		output;				// Output
61
+
62
+	struct {
63
+		unsigned int	pat;			// DVB section id 0x00 (program_association_section)
64
+		unsigned int	pmt;			// DVB section id 0x02 (program_map_section)
65
+		unsigned int	nit;			// DVB section id 0x40 (network_information_section - actual_network)
66
+		unsigned int	sdt;			// DVB section id 0x42 (service_description_section - actual_transport_stream)
67
+		unsigned int	eit;			// DVB section id 0x4e (event_information_section - actual_transport_stream, present/following)
68
+		unsigned int	tdt;			// DVB section id 0x70 (time_date_section)
69
+		unsigned int	tot;			// DVB section id 0x73 (time_offset_section)
70
+		unsigned int	stats;			// Local
71
+	} timeouts;
72
+} CONFIG;
73
+
74
+
75
+CONFIG *	config_alloc	();
76
+void		config_free		(CONFIG **conf);
77
+void		config_load		(CONFIG *conf, int argc, char **argv);
78
+
79
+int			config_load_global		(CONFIG *conf);
80
+int			config_load_channels	(CONFIG *conf);
81
+int			config_load_nit			(CONFIG *conf);
82
+int			config_load_epg			(CONFIG *conf);
83
+
84
+#endif

+ 389
- 0
data.c View File

@@ -0,0 +1,389 @@
1
+#include <stdlib.h>
2
+#include <math.h>
3
+#include <ctype.h>
4
+#include <unistd.h>
5
+#include <string.h>
6
+#include <sys/time.h>
7
+#include <regex.h>
8
+#include <sys/types.h>
9
+#include <sys/stat.h>
10
+#include <fcntl.h>
11
+
12
+#include "libfuncs/io.h"
13
+#include "libfuncs/log.h"
14
+#include "libfuncs/list.h"
15
+#include "libfuncs/asyncdns.h"
16
+
17
+#include "libtsfuncs/tsfuncs.h"
18
+
19
+#include "data.h"
20
+#include "config.h"
21
+#include "output.h"
22
+
23
+extern CONFIG *config;
24
+
25
+channel_source get_sproto(char *url) {
26
+	return strncmp(url, "http", 4)==0 ? tcp_sock : udp_sock;
27
+}
28
+
29
+CHANSRC *chansrc_init(char *url) {
30
+	if (!url)
31
+		return NULL;
32
+	regex_t re;
33
+	regmatch_t res[5];
34
+	regcomp(&re, "^([a-z]+)://([^:/?]+):?([0-9]*)/?(.*)", REG_EXTENDED);
35
+	if (regexec(&re,url,5,res,0)==0) {
36
+		char *data = strdup(url);
37
+		char *proto, *host, *port, *path;
38
+		int iport;
39
+		proto= data+res[1].rm_so; data[res[1].rm_eo]=0;
40
+		host = data+res[2].rm_so; data[res[2].rm_eo]=0;
41
+		port = data+res[3].rm_so; data[res[3].rm_eo]=0;
42
+		path = data+res[4].rm_so; data[res[4].rm_eo]=0;
43
+		iport = atoi(port);
44
+		/* Setup */
45
+		CHANSRC *src = calloc(1, sizeof(CHANSRC));
46
+		src->proto = strdup(proto);
47
+		src->sproto= get_sproto(url);
48
+		src->host  = strdup(host);
49
+		src->port  = iport ? iport : 80;
50
+		src->path  = strdup(path);
51
+		FREE(data);
52
+		regfree(&re);
53
+		return src;
54
+	}
55
+	regfree(&re);
56
+	return NULL;
57
+}
58
+
59
+void chansrc_free(CHANSRC **purl) {
60
+	CHANSRC *url = *purl;
61
+	if (url) {
62
+		FREE(url->proto);
63
+		FREE(url->host);
64
+		FREE(url->path);
65
+		FREE(*purl);
66
+	}
67
+};
68
+
69
+void chansrc_add(CHANNEL *c, char *src) {
70
+	if (c->num_src >= MAX_CHANNEL_SOURCES-1)
71
+		return;
72
+	c->sources[c->num_src] = strdup(src);
73
+	if (c->num_src == 0) /* Set default source to first one */
74
+		c->source = c->sources[c->num_src];
75
+	c->num_src++;
76
+}
77
+
78
+void chansrc_next(CHANNEL *c) {
79
+	if (c->num_src <= 1)
80
+		return;
81
+	// uint8_t old_src = c->curr_src;
82
+	c->curr_src++;
83
+	if (c->curr_src >= MAX_CHANNEL_SOURCES-1 || c->sources[c->curr_src] == NULL)
84
+		c->curr_src = 0;
85
+	c->source = c->sources[c->curr_src];
86
+	// LOGf("CHAN : Switch source | Channel: %s OldSrc: %d %s NewSrc: %d %s\n", c->name, old_src, c->sources[old_src], c->curr_src, c->source);
87
+}
88
+
89
+void chansrc_set(CHANNEL *c, uint8_t src_id) {
90
+	if (src_id >= MAX_CHANNEL_SOURCES-1 || c->sources[src_id] == NULL)
91
+		return;
92
+	// uint8_t old_src = c->curr_src;
93
+	c->curr_src = src_id;
94
+	c->source = c->sources[c->curr_src];
95
+	// LOGf("CHAN : Set source    | Channel: %s OldSrc: %d %s NewSrc: %d %s\n", c->name, old_src, c->sources[old_src], c->curr_src, c->source);
96
+}
97
+
98
+
99
+
100
+
101
+
102
+CHANNEL *channel_new(int service_id, int is_radio, char *id, char *name, char *source) {
103
+	CHANNEL *c = calloc(1, sizeof(CHANNEL));
104
+	c->service_id = service_id;
105
+	c->radio = is_radio;
106
+	c->base_pid = service_id * 32; // The first pid is saved for PMT
107
+	c->pmt_pid = c->base_pid; // The first pid is saved for PMT
108
+	c->id = strdup(id);
109
+	c->name = strdup(name);
110
+	chansrc_add(c, source);
111
+	return c;
112
+}
113
+
114
+void channel_free_epg(CHANNEL *c) {
115
+	epg_free(&c->epg_now);
116
+	epg_free(&c->epg_next);
117
+
118
+	ts_eit_free(&c->eit_now);
119
+	ts_eit_free(&c->eit_next);
120
+}
121
+
122
+void channel_free(CHANNEL **pc) {
123
+	CHANNEL *c = *pc;
124
+	if (c) {
125
+		channel_free_epg(c);
126
+		FREE(c->id);
127
+		FREE(c->name);
128
+		int i;
129
+		for (i=c->num_src-1; i>=0; i--) {
130
+			FREE(c->sources[i]);
131
+		}
132
+		c->source = NULL;
133
+		FREE(*pc);
134
+	}
135
+}
136
+
137
+
138
+EPG_ENTRY *epg_new(time_t start, int duration, char *encoding, char *event, char *short_desc, char *long_desc) {
139
+	EPG_ENTRY *e;
140
+	if (!event)
141
+		return NULL;
142
+	e             = calloc(1, sizeof(EPG_ENTRY));
143
+	e->event_id   = (start / 60) &~ 0xffff0000;
144
+	e->start      = start;
145
+	e->duration   = duration;
146
+	if (encoding && strcmp(encoding, "iso-8859-5")==0) {
147
+		e->event      = init_dvb_string_iso_8859_5(event);
148
+		e->short_desc = init_dvb_string_iso_8859_5(short_desc);
149
+		e->long_desc  = init_dvb_string_iso_8859_5(long_desc);
150
+	} else {	// Default is utf-8
151
+		e->event      = init_dvb_string_utf8(event);
152
+		e->short_desc = init_dvb_string_utf8(short_desc);
153
+		e->long_desc  = init_dvb_string_utf8(long_desc);
154
+	}
155
+	return e;
156
+}
157
+
158
+void epg_free(EPG_ENTRY **pe) {
159
+	EPG_ENTRY *e = *pe;
160
+	if (e) {
161
+		FREE(e->event);
162
+		FREE(e->short_desc);
163
+		FREE(e->long_desc);
164
+		FREE(*pe);
165
+	}
166
+}
167
+
168
+
169
+// Return 1 if they are different
170
+// Return 0 if they are the same
171
+int epg_changed(EPG_ENTRY *a, EPG_ENTRY *b) {
172
+	if (!a && b) return 1;
173
+	if (!b && a) return 1;
174
+	if (!a && !b) return 0;
175
+	if (a->event_id != b->event_id) return 1;
176
+	if (a->start != b->start) return 1;
177
+	if (a->duration != b->duration) return 1;
178
+	if (xstrcmp(a->event, b->event) != 0) return 1;
179
+	if (xstrcmp(a->short_desc, b->short_desc) != 0) return 1;
180
+	if (xstrcmp(a->long_desc, b->long_desc) != 0) return 1;
181
+	return 0;
182
+}
183
+
184
+void input_stream_alloc(INPUT *input) {
185
+	input->stream.pidref = pidref_init(64, input->channel->base_pid);
186
+	input->stream.pat = ts_pat_alloc();
187
+	input->stream.pmt = ts_pmt_alloc();
188
+	input->stream.last_pat = ts_pat_alloc();
189
+	input->stream.last_pmt = ts_pmt_alloc();
190
+}
191
+
192
+void input_stream_free(INPUT *input) {
193
+	ts_pmt_free(&input->stream.pmt);
194
+	ts_pmt_free(&input->stream.pmt_rewritten);
195
+	ts_pmt_free(&input->stream.last_pmt);
196
+	ts_pat_free(&input->stream.pat);
197
+	ts_pat_free(&input->stream.pat_rewritten);
198
+	ts_pat_free(&input->stream.last_pat);
199
+	pidref_free(&input->stream.pidref);
200
+	input->stream.nit_pid    = 0;
201
+	input->stream.pmt_pid    = 0;
202
+	input->stream.pcr_pid    = 0;
203
+	input->stream.input_pcr  = 0;
204
+}
205
+
206
+void input_stream_reset(INPUT *input) {
207
+	input_stream_free(input);
208
+	input_stream_alloc(input);
209
+}
210
+
211
+INPUT * input_new(const char *name, CHANNEL *channel) {
212
+	char *tmp;
213
+	INPUT *r = calloc(1, sizeof(INPUT));
214
+
215
+	r->name = strdup(name);
216
+	r->sock = -1;
217
+	r->channel = channel;
218
+
219
+	if (config->write_input_file) {
220
+		asprintf(&tmp, "mptsd-input-%s.ts", channel->id);
221
+		r->ifd = open(tmp, O_CREAT | O_WRONLY | O_TRUNC, 0644);
222
+		FREE(tmp);
223
+	}
224
+
225
+	r->buf = cbuf_init(1428 * 1316, channel->id); // ~ 10000 x 188
226
+
227
+	input_stream_alloc(r);
228
+
229
+	return r;
230
+}
231
+
232
+void input_free(INPUT **pinput) {
233
+	INPUT *r = *pinput;
234
+	if (!r)
235
+		return;
236
+	if (r->sock > -1)
237
+		shutdown_fd(&(r->sock));
238
+	if (r->freechannel)
239
+		channel_free(&r->channel);
240
+	if (r->ifd)
241
+		close(r->ifd);
242
+
243
+	input_stream_free(r);
244
+
245
+	cbuf_free(&r->buf);
246
+
247
+	FREE(r->name);
248
+	FREE(*pinput);
249
+}
250
+
251
+
252
+
253
+OUTPUT *output_new() {
254
+	OUTPUT *o = calloc(1, sizeof(OUTPUT));
255
+	o->obuf_ms = 100;
256
+
257
+	o->psibuf = cbuf_init(50 * 1316, "psi");
258
+	if (!o->psibuf) {
259
+		LOGf("ERROR: Can't allocate PSI input buffer\n");
260
+		exit(1);
261
+	}
262
+	cbuf_poison(o->psibuf, 'Y');
263
+
264
+	return o;
265
+}
266
+
267
+void output_open_file(OUTPUT *o) {
268
+	o->ofd = open("mptsd-output.ts", O_CREAT | O_WRONLY | O_TRUNC, 0644);
269
+}
270
+
271
+void obuf_reset(OBUF *ob) {
272
+	int i;
273
+	memset(ob->buf, 0xff, ob->size);
274
+	for (i=0; i<ob->size; i+=188) {
275
+		ob->buf[i+0] = 0x47;
276
+		ob->buf[i+1] = 0x1f;
277
+		ob->buf[i+2] = 0xff;
278
+		ob->buf[i+3] = 0x00;
279
+	}
280
+	ob->written = 0;
281
+	ob->status  = obuf_empty;
282
+}
283
+
284
+void output_buffer_alloc(OUTPUT *o, double output_bitrate) {
285
+	if (!output_bitrate) {
286
+		LOGf("No output bitrate, can't determine buffer!\n");
287
+		exit(1);
288
+	}
289
+
290
+	o->output_bitrate = output_bitrate;
291
+
292
+	long pps  = ceil((double)output_bitrate / (FRAME_PACKET_SIZE * 8));	// Packets per second
293
+	long ppms = ceil((double)pps / ((double)1000 / o->obuf_ms));		// Packets per o->buffer_ms miliseconds
294
+	long obuf_size = ppms * 1316;
295
+
296
+	o->obuf[0].size   = obuf_size;
297
+	o->obuf[0].status = obuf_empty;
298
+	o->obuf[0].buf    = malloc(o->obuf[0].size);
299
+	obuf_reset(&o->obuf[0]);
300
+
301
+	o->obuf[1].size   = obuf_size;
302
+	o->obuf[1].status = obuf_empty;
303
+	o->obuf[1].buf    = malloc(o->obuf[0].size);
304
+	obuf_reset(&o->obuf[1]);
305
+
306
+	LOGf("\tOutput buf size   : %ld * 2 = %ld\n", obuf_size, obuf_size * 2);
307
+	LOGf("\tOutput buf packets: %ld (188 bytes)\n", obuf_size / 188);
308
+	LOGf("\tOutput buf frames : %ld (1316 bytes)\n", obuf_size / 1316);
309
+	LOGf("\tOutput buf ms     : %u ms\n", o->obuf_ms);
310
+}
311
+
312
+void output_free(OUTPUT **po) {
313
+	OUTPUT *o = *po;
314
+	if (!o)
315
+		return;
316
+	if (o->out_sock > -1)
317
+		shutdown_fd(&(o->out_sock));
318
+	if (o->ofd)
319
+		close(o->ofd);
320
+	cbuf_free(&o->psibuf);
321
+	FREE(o->obuf[0].buf);
322
+	FREE(o->obuf[1].buf);
323
+	output_psi_free(o);
324
+	FREE(*po);
325
+}
326
+
327
+
328
+NIT *nit_new(uint16_t ts_id, char *freq, char *mod, char *symbol_rate) {
329
+	char tmp[9];
330
+	unsigned i, pos;
331
+
332
+	NIT *n = calloc(1, sizeof(NIT));
333
+	if (strlen(freq) != 9 || strlen(symbol_rate) != 8)
334
+		return NULL;
335
+	n->freq        = strdup(freq);
336
+	n->modulation  = strdup(mod);
337
+	n->symbol_rate = strdup(symbol_rate);
338
+	n->ts_id       = ts_id;
339
+
340
+	n->_modulation =
341
+		strcmp(mod, "16-QAM") == 0  ? 0x01 :
342
+		strcmp(mod, "32-QAM") == 0  ? 0x02 :
343
+		strcmp(mod, "64-QAM") == 0  ? 0x03 :
344
+		strcmp(mod, "128-QAM") == 0 ? 0x04 :
345
+		strcmp(mod, "256-QAM") == 0 ? 0x05 : 0x00;
346
+
347
+	memset(tmp, 0, sizeof(tmp));
348
+	pos = 0;
349
+	for (i=0;i<strlen(freq);i++) {
350
+		if (isdigit(freq[i])) {
351
+			tmp[pos] = freq[i];
352
+			pos++;
353
+		}
354
+	}
355
+	n->_freq = strtol(tmp, NULL, 16);
356
+
357
+	memset(tmp, 0, sizeof(tmp));
358
+	pos = 0;
359
+	for (i=0;i<strlen(symbol_rate);i++) {
360
+		if (isdigit(symbol_rate[i])) {
361
+			tmp[pos] = symbol_rate[i];
362
+			pos++;
363
+		}
364
+	}
365
+	n->_symbol_rate = strtol(tmp, NULL, 16);
366
+
367
+	return n;
368
+}
369
+
370
+void nit_free(NIT **pn) {
371
+	NIT *n = *pn;
372
+	if (n) {
373
+		FREE(n->freq);
374
+		FREE(n->modulation);
375
+		FREE(n->symbol_rate);
376
+		FREE(*pn);
377
+	}
378
+}
379
+
380
+void proxy_log(INPUT *r, char *msg) {
381
+	LOGf("INPUT : [%-12s] %s fd: %d src: %s\n", r->channel->id, msg, r->sock, r->channel->source);
382
+}
383
+
384
+void proxy_close(LIST *inputs, INPUT **input) {
385
+	proxy_log(*input, "Stop");
386
+	// If there are no clients left, no "Timeout" messages will be logged
387
+	list_del_entry(inputs, *input);
388
+	input_free(input);
389
+}

+ 230
- 0
data.h View File

@@ -0,0 +1,230 @@
1
+#ifndef DATA_H
2
+#define DATA_H
3
+
4
+/* How much to wait for connection to be established with channel source (miliseconds) */
5
+#define PROXY_CONNECT_TIMEOUT 1000
6
+
7
+/* Seconds to sleep between retries (miliseconds) */
8
+#define PROXY_RETRY_TIMEOUT 1000
9
+
10
+/* 7 * 188 */
11
+#define FRAME_PACKET_SIZE 1316
12
+
13
+#include "libfuncs/libfuncs.h"
14
+
15
+#include "libtsfuncs/tsdata.h"
16
+
17
+#include "pidref.h"
18
+
19
+typedef enum { udp_sock, tcp_sock } channel_source;
20
+
21
+typedef struct {
22
+	channel_source sproto;
23
+	char *proto;
24
+	char *host;
25
+	char *path;
26
+	unsigned int port;
27
+} CHANSRC;
28
+
29
+#define MAX_CHANNEL_SOURCES 8
30
+
31
+typedef struct {
32
+	uint16_t	event_id;
33
+	time_t		start;
34
+	int			duration;
35
+	char *		event;
36
+	char *		short_desc;
37
+	char *		long_desc;
38
+} EPG_ENTRY;
39
+
40
+typedef struct {
41
+	/* Config */
42
+	int			base_pid;
43
+	int			service_id;
44
+	int			pmt_pid;
45
+	int			radio;
46
+	char *		id;
47
+	char *		name;
48
+	/* Sources */
49
+	char *		source; /* Full source url */
50
+	char *		sources[MAX_CHANNEL_SOURCES];
51
+	uint8_t		num_src;
52
+	uint8_t		curr_src;
53
+	int			worktime_start;
54
+	int			worktime_end;
55
+
56
+	/* EPG */
57
+	uint8_t			epg_version:5;
58
+
59
+	EPG_ENTRY *		epg_now;
60
+	EPG_ENTRY *		epg_next;
61
+
62
+	struct ts_eit *	eit_now;
63
+	struct ts_eit *	eit_next;
64
+} CHANNEL;
65
+
66
+typedef struct {
67
+	uint64_t pcr;
68
+	uint64_t last_pcr;
69
+	int bytes;
70
+	int ts_packets_per_output_bitrate;
71
+} PCR;
72
+
73
+typedef struct {
74
+	PIDREF *pidref;			/* Rewritten pids list */
75
+
76
+	uint16_t nit_pid;		/* Pid of the NIT, default 0x10 */
77
+	uint16_t pmt_pid;		/* Pid of the original PMT, used to replace PMT */
78
+	uint16_t pcr_pid;		/* PCR pid */
79
+
80
+	struct ts_pat *pat;		/* The PAT */
81
+	struct ts_pmt *pmt;		/* The PMT */
82
+
83
+	uint64_t input_pcr;		// Latest PCR entered into input buffer
84
+
85
+	uint8_t pid_pat_cont:4;
86
+	uint8_t pid_pmt_cont:4;
87
+	struct ts_pat *pat_rewritten;	/* The rewritten PAT */
88
+	struct ts_pmt *pmt_rewritten;	/* The rewritten PMT */
89
+
90
+	struct ts_pat *last_pat;		/* The last incoming PAT */
91
+	struct ts_pmt *last_pmt;		/* The last incoming PMT */
92
+} INPUT_STREAM;
93
+
94
+typedef struct {
95
+	char  *name;
96
+	CHANNEL *channel;
97
+	int sock;				/* Server socket */
98
+	struct sockaddr_in src_sockname;
99
+	int reconnect:1,		/* Set to 1 to force proxy reconnect */
100
+	    connected:1,		/* It's set to 1 when proxy is connected and serving clients */
101
+	    insert_eit:1,		/* When set to 1 input adds EIT table into stream (if there is info) */
102
+	    working:1,			/* Set to 1 if the input is in worktime */
103
+	    dienow:1,			/* Stop serving clients and exit now */
104
+	    freechannel:1;		/* Free channel data on object free (this is used in chanconf) */
105
+	int cookie;				/* Used in chanconf to determine if the restreamer is alrady checked */
106
+	int ifd;
107
+
108
+	pthread_t thread;
109
+
110
+	uint16_t output_pcr_pid;
111
+	uint64_t output_last_pcr;			// The PCR before latest outputed
112
+	uint64_t output_pcr;				// Latest outputed PCR
113
+	int output_pcr_packets_needed;		// Based on selected output rate how much packets should be between output_pcr and output_last_pcr
114
+	int outputed_packets;				// How much packets have been sent. This is reset on every PCR and incremented on every sent packet (every input and output padding)
115
+
116
+	int disabled;			/* Input is disabled, no data is fed to output buffers */
117
+	int input_ready;		/* Set to 1 from INPUT thread when input is ready to be mixed in output */
118
+
119
+	CBUF *buf;				// Input buffer */
120
+
121
+	INPUT_STREAM stream;
122
+} INPUT;
123
+
124
+typedef enum {
125
+	obuf_empty    = 0,		// Buffer is empty and can be used by mix thread
126
+	obuf_filling  = 1,		// Buffer is being filled by mix thread
127
+	obuf_full     = 2,		// Buffer is filled and can be used by write thread
128
+	obuf_emptying = 3,		// Buffer is being emptyed by write thread
129
+} OBUF_STATUS;
130
+
131
+typedef struct {
132
+	uint8_t *	buf;
133
+	int			size;		// Output buffer size (must be size % 1316 == 0)
134
+	int			written;
135
+	OBUF_STATUS	status;
136
+} OBUF;
137
+
138
+typedef struct {
139
+	struct in_addr out_host;
140
+	int out_port;
141
+	int out_sock;			/* The udp socket */
142
+	int ofd;
143
+	int dienow;				/* Instruct output to die */
144
+
145
+	pthread_t psi_thread;
146
+	pthread_t mix_thread;
147
+	pthread_t write_thread;
148
+
149
+	CBUF *psibuf;			// Input buffer */
150
+
151
+	unsigned int obuf_ms;	// How much miliseconds of data output buffer holds
152
+	OBUF obuf[2];			// Output buffers
153
+	double output_bitrate;	// Output bitrate (bps)
154
+
155
+	uint64_t traffic;
156
+
157
+	uint64_t traffic_period;
158
+	uint64_t padding_period;
159
+
160
+	uint8_t pid_pat_cont:4;
161
+	uint8_t pid_nit_cont:4;
162
+	uint8_t pid_sdt_cont:4;
163
+	uint8_t pid_eit_cont:4;
164
+	uint8_t pid_tdt_cont:4;
165
+
166
+	struct ts_pat *pat;
167
+	struct timeval pat_ts;
168
+
169
+	struct ts_sdt *sdt;
170
+	struct timeval sdt_ts;
171
+
172
+	struct ts_nit *nit;
173
+	struct timeval nit_ts;
174
+
175
+	struct ts_tdt *tdt;
176
+	struct timeval tdt_ts;
177
+
178
+	struct ts_tdt *tot;
179
+	struct timeval tot_ts;
180
+
181
+	struct timeval eit_ts;
182
+
183
+	uint64_t last_org_pcr[8193];	// Last PCR value indexed by PID x
184
+	uint64_t last_pcr[8193];		// Last PCR value indexed by PID x
185
+	uint64_t last_traffic[8193];	// Last traffic when PCR with PID x was seen
186
+} OUTPUT;
187
+
188
+typedef struct {
189
+	char		*freq;
190
+	char		*modulation;
191
+	char		*symbol_rate;
192
+	uint16_t	ts_id;
193
+	uint32_t	_freq;
194
+	uint8_t		_modulation;
195
+	uint32_t	_symbol_rate;
196
+} NIT;
197
+
198
+EPG_ENTRY *	epg_new			(time_t start, int duration, char *encoding, char *event, char *short_desc, char *long_desc);
199
+void		epg_free		(EPG_ENTRY **e);
200
+int			epg_changed		(EPG_ENTRY *a, EPG_ENTRY *b);
201
+
202
+CHANNEL *	channel_new		(int service_id, int is_radio, char *id, char *name, char *source);
203
+void		channel_free	(CHANNEL **c);
204
+void		channel_free_epg(CHANNEL *c);
205
+
206
+channel_source get_sproto(char *url);
207
+CHANSRC *	chansrc_init	(char *url);
208
+void		chansrc_free	(CHANSRC **url);
209
+void		chansrc_add		(CHANNEL *c, char *src);
210
+void		chansrc_next	(CHANNEL *c);
211
+void		chansrc_set		(CHANNEL *c, uint8_t src_id);
212
+
213
+INPUT *		input_new		(const char *name, CHANNEL *channel);
214
+void		input_free		(INPUT **input);
215
+
216
+void		input_stream_reset	(INPUT *input);
217
+
218
+OUTPUT *	output_new			();
219
+void		output_free			(OUTPUT **output);
220
+void		output_open_file	(OUTPUT *o);
221
+void		output_buffer_alloc	(OUTPUT *o, double output_bitrate);
222
+void		obuf_reset			(OBUF *ob);
223
+
224
+NIT *		nit_new			(uint16_t ts_id, char *freq, char *modulation, char *symbol_rate);
225
+void		nit_free		(NIT **nit);
226
+
227
+void		proxy_log		(INPUT *r, char *msg);
228
+void		proxy_close		(LIST *inputs, INPUT **input);
229
+
230
+#endif

+ 428
- 0
inidict.c View File

@@ -0,0 +1,428 @@
1
+/*
2
+Copyright (c) 2000-2007 by Nicolas Devillard.
3
+MIT License
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a
6
+copy of this software and associated documentation files (the "Software"),
7
+to deal in the Software without restriction, including without limitation
8
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+and/or sell copies of the Software, and to permit persons to whom the
10
+Software is furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in
13
+all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+DEALINGS IN THE SOFTWARE.
22
+*/
23
+
24
+/*-------------------------------------------------------------------------*/
25
+/**
26
+   @file	dictionary.c
27
+   @author	N. Devillard
28
+   @date	Sep 2007
29
+   @version	Revision: 1.27
30
+   @brief	Implements a dictionary for string variables.
31
+
32
+   This module implements a simple dictionary object, i.e. a list
33
+   of string/string associations. This object is useful to store e.g.
34
+   informations retrieved from a configuration file (ini files).
35
+*/
36
+/*--------------------------------------------------------------------------*/
37
+
38
+/*
39
+	Id: inidict.c,v 1.2 2010/04/26 09:35:52 gf Exp
40
+	Revision: 1.2
41
+*/
42
+/*---------------------------------------------------------------------------
43
+   								Includes
44
+ ---------------------------------------------------------------------------*/
45
+#include "inidict.h"
46
+
47
+#include <stdio.h>
48
+#include <stdlib.h>
49
+#include <string.h>
50
+#include <unistd.h>
51
+
52
+/** Maximum value size for integers and doubles. */
53
+#define MAXVALSZ	1024
54
+
55
+/** Minimal allocated number of entries in a dictionary */
56
+#define DICTMINSZ	128
57
+
58
+/** Invalid key token */
59
+#define DICT_INVALID_KEY    ((char*)-1)
60
+
61
+/*---------------------------------------------------------------------------
62
+  							Private functions
63
+ ---------------------------------------------------------------------------*/
64
+
65
+/* Doubles the allocated size associated to a pointer */
66
+/* 'size' is the current allocated size. */
67
+static void * mem_double(void * ptr, int size)
68
+{
69
+    void * newptr ;
70
+ 
71
+    newptr = calloc(2*size, 1);
72
+    if (newptr==NULL) {
73
+        return NULL ;
74
+    }
75
+    memcpy(newptr, ptr, size);
76
+    free(ptr);
77
+    return newptr ;
78
+}
79
+
80
+/*-------------------------------------------------------------------------*/
81
+/**
82
+  @brief    Duplicate a string
83
+  @param    s String to duplicate
84
+  @return   Pointer to a newly allocated string, to be freed with free()
85
+
86
+  This is a replacement for strdup(). This implementation is provided
87
+  for systems that do not have it.
88
+ */
89
+/*--------------------------------------------------------------------------*/
90
+static char * xstrdup(char * s)
91
+{
92
+    char * t ;
93
+    if (!s)
94
+        return NULL ;
95
+    t = malloc(strlen(s)+1) ;
96
+    if (t) {
97
+        strcpy(t,s);
98
+    }
99
+    return t ;
100
+}
101
+
102
+/*---------------------------------------------------------------------------
103
+  							Function codes
104
+ ---------------------------------------------------------------------------*/
105
+/*-------------------------------------------------------------------------*/
106
+/**
107
+  @brief	Compute the hash key for a string.
108
+  @param	key		Character string to use for key.
109
+  @return	1 unsigned int on at least 32 bits.
110
+
111
+  This hash function has been taken from an Article in Dr Dobbs Journal.
112
+  This is normally a collision-free function, distributing keys evenly.
113
+  The key is stored anyway in the struct so that collision can be avoided
114
+  by comparing the key itself in last resort.
115
+ */
116
+/*--------------------------------------------------------------------------*/
117
+unsigned dictionary_hash(char * key)
118
+{
119
+	int			len ;
120
+	unsigned	hash ;
121
+	int			i ;
122
+
123
+	len = strlen(key);
124
+	for (hash=0, i=0 ; i<len ; i++) {
125
+		hash += (unsigned)key[i] ;
126
+		hash += (hash<<10);
127
+		hash ^= (hash>>6) ;
128
+	}
129
+	hash += (hash <<3);
130
+	hash ^= (hash >>11);
131
+	hash += (hash <<15);
132
+	return hash ;
133
+}
134
+
135
+/*-------------------------------------------------------------------------*/
136
+/**
137
+  @brief	Create a new dictionary object.
138
+  @param	size	Optional initial size of the dictionary.
139
+  @return	1 newly allocated dictionary objet.
140
+
141
+  This function allocates a new dictionary object of given size and returns
142
+  it. If you do not know in advance (roughly) the number of entries in the
143
+  dictionary, give size=0.
144
+ */
145
+/*--------------------------------------------------------------------------*/
146
+dictionary * dictionary_new(int size)
147
+{
148
+	dictionary	*	d ;
149
+
150
+	/* If no size was specified, allocate space for DICTMINSZ */
151
+	if (size<DICTMINSZ) size=DICTMINSZ ;
152
+
153
+	if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) {
154
+		return NULL;
155
+	}
156
+	d->size = size ;
157
+	d->val  = (char **)calloc(size, sizeof(char*));
158
+	d->key  = (char **)calloc(size, sizeof(char*));
159
+	d->hash = (unsigned int *)calloc(size, sizeof(unsigned));
160
+	return d ;
161
+}
162
+
163
+/*-------------------------------------------------------------------------*/
164
+/**
165
+  @brief	Delete a dictionary object
166
+  @param	d	dictionary object to deallocate.
167
+  @return	void
168
+
169
+  Deallocate a dictionary object and all memory associated to it.
170
+ */
171
+/*--------------------------------------------------------------------------*/
172
+void dictionary_del(dictionary * d)
173
+{
174
+	int		i ;
175
+
176
+	if (d==NULL) return ;
177
+	for (i=0 ; i<d->size ; i++) {
178
+		if (d->key[i]!=NULL)
179
+			free(d->key[i]);
180
+		if (d->val[i]!=NULL)
181
+			free(d->val[i]);
182
+	}
183
+	free(d->val);
184
+	free(d->key);
185
+	free(d->hash);
186
+	free(d);
187
+	return ;
188
+}
189
+
190
+/*-------------------------------------------------------------------------*/
191
+/**
192
+  @brief	Get a value from a dictionary.
193
+  @param	d		dictionary object to search.
194
+  @param	key		Key to look for in the dictionary.
195
+  @param    def     Default value to return if key not found.
196
+  @return	1 pointer to internally allocated character string.
197
+
198
+  This function locates a key in a dictionary and returns a pointer to its
199
+  value, or the passed 'def' pointer if no such key can be found in
200
+  dictionary. The returned character pointer points to data internal to the
201
+  dictionary object, you should not try to free it or modify it.
202
+ */
203
+/*--------------------------------------------------------------------------*/
204
+char * dictionary_get(dictionary * d, char * key, char * def)
205
+{
206
+	unsigned	hash ;
207
+	int			i ;
208
+
209
+	hash = dictionary_hash(key);
210
+	for (i=0 ; i<d->size ; i++) {
211
+        if (d->key[i]==NULL)
212
+            continue ;
213
+        /* Compare hash */
214
+		if (hash==d->hash[i]) {
215
+            /* Compare string, to avoid hash collisions */
216
+            if (!strcmp(key, d->key[i])) {
217
+				return d->val[i] ;
218
+			}
219
+		}
220
+	}
221
+	return def ;
222
+}
223
+
224
+/*-------------------------------------------------------------------------*/
225
+/**
226
+  @brief    Set a value in a dictionary.
227
+  @param    d       dictionary object to modify.
228
+  @param    key     Key to modify or add.
229
+  @param    val     Value to add.
230
+  @return   int     0 if Ok, anything else otherwise
231
+
232
+  If the given key is found in the dictionary, the associated value is
233
+  replaced by the provided one. If the key cannot be found in the
234
+  dictionary, it is added to it.
235
+
236
+  It is Ok to provide a NULL value for val, but NULL values for the dictionary
237
+  or the key are considered as errors: the function will return immediately
238
+  in such a case.
239
+
240
+  Notice that if you dictionary_set a variable to NULL, a call to
241
+  dictionary_get will return a NULL value: the variable will be found, and
242
+  its value (NULL) is returned. In other words, setting the variable
243
+  content to NULL is equivalent to deleting the variable from the
244
+  dictionary. It is not possible (in this implementation) to have a key in
245
+  the dictionary without value.
246
+
247
+  This function returns non-zero in case of failure.
248
+ */
249
+/*--------------------------------------------------------------------------*/
250
+int dictionary_set(dictionary * d, char * key, char * val)
251
+{
252
+	int			i ;
253
+	unsigned	hash ;
254
+
255
+	if (d==NULL || key==NULL) return -1 ;
256
+	
257
+	/* Compute hash for this key */
258
+	hash = dictionary_hash(key) ;
259
+	/* Find if value is already in dictionary */
260
+	if (d->n>0) {
261
+		for (i=0 ; i<d->size ; i++) {
262
+            if (d->key[i]==NULL)
263
+                continue ;
264
+			if (hash==d->hash[i]) { /* Same hash value */
265
+				if (!strcmp(key, d->key[i])) {	 /* Same key */
266
+					/* Found a value: modify and return */
267
+					if (d->val[i]!=NULL)
268
+						free(d->val[i]);
269
+                    d->val[i] = val ? xstrdup(val) : NULL ;
270
+                    /* Value has been modified: return */
271
+					return 0 ;
272
+				}
273
+			}
274
+		}
275
+	}
276
+	/* Add a new value */
277
+	/* See if dictionary needs to grow */
278
+	if (d->n==d->size) {
279
+
280
+		/* Reached maximum size: reallocate dictionary */
281
+		d->val  = (char **)mem_double(d->val,  d->size * sizeof(char*)) ;
282
+		d->key  = (char **)mem_double(d->key,  d->size * sizeof(char*)) ;
283
+		d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ;
284
+        if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) {
285
+            /* Cannot grow dictionary */
286
+            return -1 ;
287
+        }
288
+		/* Double size */
289
+		d->size *= 2 ;
290
+	}
291
+
292
+    /* Insert key in the first empty slot */
293
+    for (i=0 ; i<d->size ; i++) {
294
+        if (d->key[i]==NULL) {
295
+            /* Add key here */
296
+            break ;
297
+        }
298
+    }
299
+	/* Copy key */
300
+	d->key[i]  = xstrdup(key);
301
+    d->val[i]  = val ? xstrdup(val) : NULL ;
302
+	d->hash[i] = hash;
303
+	d->n ++ ;
304
+	return 0 ;
305
+}
306
+
307
+/*-------------------------------------------------------------------------*/
308
+/**
309
+  @brief	Delete a key in a dictionary
310
+  @param	d		dictionary object to modify.
311
+  @param	key		Key to remove.
312
+  @return   void
313
+
314
+  This function deletes a key in a dictionary. Nothing is done if the
315
+  key cannot be found.
316
+ */
317
+/*--------------------------------------------------------------------------*/
318
+void dictionary_unset(dictionary * d, char * key)
319
+{
320
+	unsigned	hash ;
321
+	int			i ;
322
+
323
+	if (key == NULL) {
324
+		return;
325
+	}
326
+
327
+	hash = dictionary_hash(key);
328
+	for (i=0 ; i<d->size ; i++) {
329
+        if (d->key[i]==NULL)
330
+            continue ;
331
+        /* Compare hash */
332
+		if (hash==d->hash[i]) {
333
+            /* Compare string, to avoid hash collisions */
334
+            if (!strcmp(key, d->key[i])) {
335
+                /* Found key */
336
+                break ;
337
+			}
338
+		}
339
+	}
340
+    if (i>=d->size)
341
+        /* Key not found */
342
+        return ;
343
+
344
+    free(d->key[i]);
345
+    d->key[i] = NULL ;
346
+    if (d->val[i]!=NULL) {
347
+        free(d->val[i]);
348
+        d->val[i] = NULL ;
349
+    }
350
+    d->hash[i] = 0 ;
351
+    d->n -- ;
352
+    return ;
353
+}
354
+
355
+/*-------------------------------------------------------------------------*/
356
+/**
357
+  @brief	Dump a dictionary to an opened file pointer.
358
+  @param	d	Dictionary to dump
359
+  @param	f	Opened file pointer.
360
+  @return	void
361
+
362
+  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
363
+  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
364
+  output file pointers.
365
+ */
366
+/*--------------------------------------------------------------------------*/
367
+void dictionary_dump(dictionary * d, FILE * out)
368
+{
369
+	int		i ;
370
+
371
+	if (d==NULL || out==NULL) return ;
372
+	if (d->n<1) {
373
+		fprintf(out, "empty dictionary\n");
374
+		return ;
375
+	}
376
+	for (i=0 ; i<d->size ; i++) {
377
+        if (d->key[i]) {
378
+            fprintf(out, "%20s\t[%s]\n",
379
+                    d->key[i],
380
+                    d->val[i] ? d->val[i] : "UNDEF");
381
+        }
382
+	}
383
+	return ;
384
+}
385
+
386
+
387
+/* Test code */
388
+#ifdef TESTDIC
389
+#define NVALS 20000
390
+int main(int argc, char *argv[])
391
+{
392
+	dictionary	*	d ;
393
+	char	*	val ;
394
+	int			i ;
395
+	char		cval[90] ;
396
+
397
+	/* Allocate dictionary */
398
+	printf("allocating...\n");
399
+	d = dictionary_new(0);
400
+	
401
+	/* Set values in dictionary */
402
+	printf("setting %d values...\n", NVALS);
403
+	for (i=0 ; i<NVALS ; i++) {
404
+		sprintf(cval, "%04d", i);
405
+		dictionary_set(d, cval, "salut");
406
+	}
407
+	printf("getting %d values...\n", NVALS);
408
+	for (i=0 ; i<NVALS ; i++) {
409
+		sprintf(cval, "%04d", i);
410
+		val = dictionary_get(d, cval, DICT_INVALID_KEY);
411
+		if (val==DICT_INVALID_KEY) {
412
+			printf("cannot get value for key [%s]\n", cval);
413
+		}
414
+	}
415
+    printf("unsetting %d values...\n", NVALS);
416
+	for (i=0 ; i<NVALS ; i++) {
417
+		sprintf(cval, "%04d", i);
418
+		dictionary_unset(d, cval);
419
+	}
420
+    if (d->n != 0) {
421
+        printf("error deleting values\n");
422
+    }
423
+	printf("deallocating...\n");
424
+	dictionary_del(d);
425
+	return 0 ;
426
+}
427
+#endif
428
+/* vim: set ts=4 et sw=4 tw=75 */

+ 174
- 0
inidict.h View File

@@ -0,0 +1,174 @@
1
+
2
+/*-------------------------------------------------------------------------*/
3
+/**
4
+   @file    dictionary.h
5
+   @author  N. Devillard
6
+   @date    Sep 2007
7
+   @version Revision: 1.12
8
+   @brief   Implements a dictionary for string variables.
9
+
10
+   This module implements a simple dictionary object, i.e. a list
11
+   of string/string associations. This object is useful to store e.g.
12
+   informations retrieved from a configuration file (ini files).
13
+*/
14
+/*--------------------------------------------------------------------------*/
15
+
16
+/*
17
+	Id: dictionary.h,v 1.12 2007-11-23 21:37:00 ndevilla Exp
18
+	Author: ndevilla
19
+	Date: 2007-11-23 21:37:00
20
+	Revision: 1.12
21
+*/
22
+
23
+#ifndef _DICTIONARY_H_
24
+#define _DICTIONARY_H_
25
+
26
+/*---------------------------------------------------------------------------
27
+   								Includes
28
+ ---------------------------------------------------------------------------*/
29
+
30
+#include <stdio.h>
31
+#include <stdlib.h>
32
+#include <string.h>
33
+#include <unistd.h>
34
+
35
+/*---------------------------------------------------------------------------
36
+   								New types
37
+ ---------------------------------------------------------------------------*/
38
+
39
+
40
+/*-------------------------------------------------------------------------*/
41
+/**
42
+  @brief	Dictionary object
43
+
44
+  This object contains a list of string/string associations. Each
45
+  association is identified by a unique string key. Looking up values
46
+  in the dictionary is speeded up by the use of a (hopefully collision-free)
47
+  hash function.
48
+ */
49
+/*-------------------------------------------------------------------------*/
50
+typedef struct _dictionary_ {
51
+	int				n ;		/** Number of entries in dictionary */
52
+	int				size ;	/** Storage size */
53
+	char 		**	val ;	/** List of string values */
54
+	char 		**  key ;	/** List of string keys */
55
+	unsigned	 *	hash ;	/** List of hash values for keys */
56
+} dictionary ;
57
+
58
+
59
+/*---------------------------------------------------------------------------
60
+  							Function prototypes
61
+ ---------------------------------------------------------------------------*/
62
+
63
+/*-------------------------------------------------------------------------*/
64
+/**
65
+  @brief    Compute the hash key for a string.
66
+  @param    key     Character string to use for key.
67
+  @return   1 unsigned int on at least 32 bits.
68
+
69
+  This hash function has been taken from an Article in Dr Dobbs Journal.
70
+  This is normally a collision-free function, distributing keys evenly.
71
+  The key is stored anyway in the struct so that collision can be avoided
72
+  by comparing the key itself in last resort.
73
+ */
74
+/*--------------------------------------------------------------------------*/
75
+unsigned dictionary_hash(char * key);
76
+
77
+/*-------------------------------------------------------------------------*/
78
+/**
79
+  @brief    Create a new dictionary object.
80
+  @param    size    Optional initial size of the dictionary.
81
+  @return   1 newly allocated dictionary objet.
82
+
83
+  This function allocates a new dictionary object of given size and returns
84
+  it. If you do not know in advance (roughly) the number of entries in the
85
+  dictionary, give size=0.
86
+ */
87
+/*--------------------------------------------------------------------------*/
88
+dictionary * dictionary_new(int size);
89
+
90
+/*-------------------------------------------------------------------------*/
91
+/**
92
+  @brief    Delete a dictionary object
93
+  @param    d   dictionary object to deallocate.
94
+  @return   void
95
+
96
+  Deallocate a dictionary object and all memory associated to it.
97
+ */
98
+/*--------------------------------------------------------------------------*/
99
+void dictionary_del(dictionary * vd);
100
+
101
+/*-------------------------------------------------------------------------*/
102
+/**
103
+  @brief    Get a value from a dictionary.
104
+  @param    d       dictionary object to search.
105
+  @param    key     Key to look for in the dictionary.
106
+  @param    def     Default value to return if key not found.
107
+  @return   1 pointer to internally allocated character string.
108
+
109
+  This function locates a key in a dictionary and returns a pointer to its
110
+  value, or the passed 'def' pointer if no such key can be found in
111
+  dictionary. The returned character pointer points to data internal to the
112
+  dictionary object, you should not try to free it or modify it.
113
+ */
114
+/*--------------------------------------------------------------------------*/
115
+char * dictionary_get(dictionary * d, char * key, char * def);
116
+
117
+
118
+/*-------------------------------------------------------------------------*/
119
+/**
120
+  @brief    Set a value in a dictionary.
121
+  @param    d       dictionary object to modify.
122
+  @param    key     Key to modify or add.
123
+  @param    val     Value to add.
124
+  @return   int     0 if Ok, anything else otherwise
125
+
126
+  If the given key is found in the dictionary, the associated value is
127
+  replaced by the provided one. If the key cannot be found in the
128
+  dictionary, it is added to it.
129
+
130
+  It is Ok to provide a NULL value for val, but NULL values for the dictionary
131
+  or the key are considered as errors: the function will return immediately
132
+  in such a case.
133
+
134
+  Notice that if you dictionary_set a variable to NULL, a call to
135
+  dictionary_get will return a NULL value: the variable will be found, and
136
+  its value (NULL) is returned. In other words, setting the variable
137
+  content to NULL is equivalent to deleting the variable from the
138
+  dictionary. It is not possible (in this implementation) to have a key in
139
+  the dictionary without value.
140
+
141
+  This function returns non-zero in case of failure.
142
+ */
143
+/*--------------------------------------------------------------------------*/
144
+int dictionary_set(dictionary * vd, char * key, char * val);
145
+
146
+/*-------------------------------------------------------------------------*/
147
+/**
148
+  @brief    Delete a key in a dictionary
149
+  @param    d       dictionary object to modify.
150
+  @param    key     Key to remove.
151
+  @return   void
152
+
153
+  This function deletes a key in a dictionary. Nothing is done if the
154
+  key cannot be found.
155
+ */
156
+/*--------------------------------------------------------------------------*/
157
+void dictionary_unset(dictionary * d, char * key);
158
+
159
+
160
+/*-------------------------------------------------------------------------*/
161
+/**
162
+  @brief    Dump a dictionary to an opened file pointer.
163
+  @param    d   Dictionary to dump
164
+  @param    f   Opened file pointer.
165
+  @return   void
166
+
167
+  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
168
+  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
169
+  output file pointers.
170
+ */
171
+/*--------------------------------------------------------------------------*/
172
+void dictionary_dump(dictionary * d, FILE * out);
173
+
174
+#endif

+ 712
- 0
iniparser.c View File

@@ -0,0 +1,712 @@
1
+/*
2
+Copyright (c) 2000-2007 by Nicolas Devillard.
3
+MIT License
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a
6
+copy of this software and associated documentation files (the "Software"),
7
+to deal in the Software without restriction, including without limitation
8
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+and/or sell copies of the Software, and to permit persons to whom the
10
+Software is furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in
13
+all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+DEALINGS IN THE SOFTWARE.
22
+*/
23
+
24
+/*-------------------------------------------------------------------------*/
25
+/**
26
+   @file    iniparser.c
27
+   @author  N. Devillard
28
+   @date    Sep 2007
29
+   @version 3.0
30
+   @brief   Parser for ini files.
31
+*/
32
+/*--------------------------------------------------------------------------*/
33
+/*
34
+    Id: iniparser.c,v 2.18 2008-01-03 18:35:39 ndevilla Exp
35
+    Revision: 2.18
36
+    Date: 2008-01-03 18:35:39
37
+*/
38
+/*---------------------------- Includes ------------------------------------*/
39
+#include <ctype.h>
40
+#include "iniparser.h"
41
+
42
+/*---------------------------- Defines -------------------------------------*/
43
+#define ASCIILINESZ         (1024)
44
+#define INI_INVALID_KEY     ((char*)-1)
45
+#define MSG_PREFIX          "confparser: "
46
+
47
+/*---------------------------------------------------------------------------
48
+                        Private to this module
49
+ ---------------------------------------------------------------------------*/
50
+/**
51
+ * This enum stores the status for each parsed line (internal use only).
52
+ */
53
+typedef enum _line_status_ {
54
+    LINE_UNPROCESSED,
55
+    LINE_ERROR,
56
+    LINE_EMPTY,
57
+    LINE_COMMENT,
58
+    LINE_SECTION,
59
+    LINE_VALUE
60
+} line_status ;
61
+
62
+/*-------------------------------------------------------------------------*/
63
+/**
64
+  @brief	Convert a string to lowercase.
65
+  @param	s	String to convert.
66
+  @return	ptr to statically allocated string.
67
+
68
+  This function returns a pointer to a statically allocated string
69
+  containing a lowercased version of the input string. Do not free
70
+  or modify the returned string! Since the returned string is statically
71
+  allocated, it will be modified at each function call (not re-entrant).
72
+ */
73
+/*--------------------------------------------------------------------------*/
74
+static char * strlwc(const char * s)
75
+{
76
+    static char l[ASCIILINESZ+1];
77
+    int i ;
78
+
79
+    if (s==NULL) return NULL ;
80
+    memset(l, 0, ASCIILINESZ+1);
81
+    i=0 ;
82
+    while (s[i] && i<ASCIILINESZ) {
83
+        l[i] = (char)tolower((int)s[i]);
84
+        i++ ;
85
+    }
86
+    l[ASCIILINESZ]=(char)0;
87
+    return l ;
88
+}
89
+
90
+/*-------------------------------------------------------------------------*/
91
+/**
92
+  @brief	Remove blanks at the beginning and the end of a string.
93
+  @param	s	String to parse.
94
+  @return	ptr to statically allocated string.
95
+
96
+  This function returns a pointer to a statically allocated string,
97
+  which is identical to the input string, except that all blank
98
+  characters at the end and the beg. of the string have been removed.
99
+  Do not free or modify the returned string! Since the returned string
100
+  is statically allocated, it will be modified at each function call
101
+  (not re-entrant).
102
+ */
103
+/*--------------------------------------------------------------------------*/
104
+static char * strstrip(char * s)
105
+{
106
+    static char l[ASCIILINESZ+1];
107
+	char * last ;
108
+	
109
+    if (s==NULL) return NULL ;
110
+    
111
+	while (isspace((int)*s) && *s) s++;
112
+	memset(l, 0, ASCIILINESZ+1);
113
+	strcpy(l, s);
114
+	last = l + strlen(l);
115
+	while (last > l) {
116
+		if (!isspace((int)*(last-1)))
117
+			break ;
118
+		last -- ;
119
+	}
120
+	*last = (char)0;
121
+	return (char*)l ;
122
+}
123
+
124
+/*-------------------------------------------------------------------------*/
125
+/**
126
+  @brief    Get number of sections in a dictionary
127
+  @param    d   Dictionary to examine
128
+  @return   int Number of sections found in dictionary
129
+
130
+  This function returns the number of sections found in a dictionary.
131
+  The test to recognize sections is done on the string stored in the
132
+  dictionary: a section name is given as "section" whereas a key is
133
+  stored as "section:key", thus the test looks for entries that do not
134
+  contain a colon.
135
+
136
+  This clearly fails in the case a section name contains a colon, but
137
+  this should simply be avoided.
138
+
139
+  This function returns -1 in case of error.
140
+ */
141
+/*--------------------------------------------------------------------------*/
142
+int iniparser_getnsec(dictionary * d)
143
+{
144
+    int i ;
145
+    int nsec ;
146
+
147
+    if (d==NULL) return -1 ;
148
+    nsec=0 ;
149
+    for (i=0 ; i<d->size ; i++) {
150
+        if (d->key[i]==NULL)
151
+            continue ;
152
+        if (strchr(d->key[i], ':')==NULL) {
153
+            nsec ++ ;
154
+        }
155
+    }
156
+    return nsec ;
157
+}
158
+
159
+/*-------------------------------------------------------------------------*/
160
+/**
161
+  @brief    Get name for section n in a dictionary.
162
+  @param    d   Dictionary to examine
163
+  @param    n   Section number (from 0 to nsec-1).
164
+  @return   Pointer to char string
165
+
166
+  This function locates the n-th section in a dictionary and returns
167
+  its name as a pointer to a string statically allocated inside the
168
+  dictionary. Do not free or modify the returned string!
169
+
170
+  This function returns NULL in case of error.
171
+ */
172
+/*--------------------------------------------------------------------------*/
173
+char * iniparser_getsecname(dictionary * d, int n)
174
+{
175
+    int i ;
176
+    int foundsec ;
177
+
178
+    if (d==NULL || n<0) return NULL ;
179
+    foundsec=0 ;
180
+    for (i=0 ; i<d->size ; i++) {
181
+        if (d->key[i]==NULL)
182
+            continue ;
183
+        if (strchr(d->key[i], ':')==NULL) {
184
+            foundsec++ ;
185
+            if (foundsec>n)
186
+                break ;
187
+        }
188
+    }
189
+    if (foundsec<=n) {
190
+        return NULL ;
191
+    }
192
+    return d->key[i] ;
193
+}
194
+
195
+/*-------------------------------------------------------------------------*/
196
+/**
197
+  @brief    Dump a dictionary to an opened file pointer.
198
+  @param    d   Dictionary to dump.
199
+  @param    f   Opened file pointer to dump to.
200
+  @return   void
201
+
202
+  This function prints out the contents of a dictionary, one element by
203
+  line, onto the provided file pointer. It is OK to specify @c stderr
204
+  or @c stdout as output files. This function is meant for debugging
205
+  purposes mostly.
206
+ */
207
+/*--------------------------------------------------------------------------*/
208
+void iniparser_dump(dictionary * d, FILE * f)
209
+{
210
+    int     i ;
211
+
212
+    if (d==NULL || f==NULL) return ;
213
+    for (i=0 ; i<d->size ; i++) {
214
+        if (d->key[i]==NULL)
215
+            continue ;
216
+        if (d->val[i]!=NULL) {
217
+            fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
218
+        } else {
219
+            fprintf(f, "[%s]=UNDEF\n", d->key[i]);
220
+        }
221
+    }
222
+    return ;
223
+}
224
+
225
+/*-------------------------------------------------------------------------*/
226
+/**
227
+  @brief    Save a dictionary to a loadable ini file
228
+  @param    d   Dictionary to dump
229
+  @param    f   Opened file pointer to dump to
230
+  @return   void
231
+
232
+  This function dumps a given dictionary into a loadable ini file.
233
+  It is Ok to specify @c stderr or @c stdout as output files.
234
+ */
235
+/*--------------------------------------------------------------------------*/
236
+void iniparser_dump_ini(dictionary * d, FILE * f)
237
+{
238
+    int     i, j ;
239
+    char    keym[ASCIILINESZ+1];
240
+    int     nsec ;
241
+    char *  secname ;
242
+    int     seclen ;
243
+
244
+    if (d==NULL || f==NULL) return ;
245
+
246
+    nsec = iniparser_getnsec(d);
247
+    if (nsec<1) {
248
+        /* No section in file: dump all keys as they are */
249
+        for (i=0 ; i<d->size ; i++) {
250
+            if (d->key[i]==NULL)
251
+                continue ;
252
+            fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
253
+        }
254
+        return ;
255
+    }
256
+    for (i=0 ; i<nsec ; i++) {
257
+        secname = iniparser_getsecname(d, i) ;
258
+        seclen  = (int)strlen(secname);
259
+        fprintf(f, "\n[%s]\n", secname);
260
+        sprintf(keym, "%s:", secname);
261
+        for (j=0 ; j<d->size ; j++) {
262
+            if (d->key[j]==NULL)
263
+                continue ;
264
+            if (!strncmp(d->key[j], keym, seclen+1)) {
265
+                fprintf(f,
266
+                        "%-30s = %s\n",
267
+                        d->key[j]+seclen+1,
268
+                        d->val[j] ? d->val[j] : "");
269
+            }
270
+        }
271
+    }
272
+    fprintf(f, "\n");
273
+    return ;
274
+}
275
+
276
+/*-------------------------------------------------------------------------*/
277
+/**
278
+  @brief    Get the string associated to a key
279
+  @param    d       Dictionary to search
280
+  @param    key     Key string to look for
281
+  @param    def     Default value to return if key not found.
282
+  @return   pointer to statically allocated character string
283
+
284
+  This function queries a dictionary for a key. A key as read from an
285
+  ini file is given as "section:key". If the key cannot be found,
286
+  the pointer passed as 'def' is returned.
287
+  The returned char pointer is pointing to a string allocated in
288
+  the dictionary, do not free or modify it.
289
+ */
290
+/*--------------------------------------------------------------------------*/
291
+char * iniparser_getstring(dictionary * d, const char * key, char * def)
292
+{
293
+    char * lc_key ;
294
+    char * sval ;
295
+
296
+    if (d==NULL || key==NULL)
297
+        return def ;
298
+
299
+    lc_key = strlwc(key);
300
+    sval = dictionary_get(d, lc_key, def);
301
+    return sval ;
302
+}
303
+
304
+/*-------------------------------------------------------------------------*/
305
+/**
306
+  @brief    Get the string associated to a key, convert to an int
307
+  @param    d Dictionary to search
308
+  @param    key Key string to look for
309
+  @param    notfound Value to return in case of error
310
+  @return   integer
311
+
312
+  This function queries a dictionary for a key. A key as read from an
313
+  ini file is given as "section:key". If the key cannot be found,
314
+  the notfound value is returned.
315
+
316
+  Supported values for integers include the usual C notation
317
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
318
+  are supported. Examples:
319
+
320
+  "42"      ->  42
321
+  "042"     ->  34 (octal -> decimal)
322
+  "0x42"    ->  66 (hexa  -> decimal)
323
+
324
+  Warning: the conversion may overflow in various ways. Conversion is
325
+  totally outsourced to strtol(), see the associated man page for overflow
326
+  handling.
327
+
328
+  Credits: Thanks to A. Becker for suggesting strtol()
329
+ */
330
+/*--------------------------------------------------------------------------*/
331
+int iniparser_getint(dictionary * d, const char * key, int notfound)
332
+{
333
+    char    *   str ;
334
+
335
+    str = iniparser_getstring(d, key, INI_INVALID_KEY);
336
+    if (str==INI_INVALID_KEY) return notfound ;
337
+    return (int)strtol(str, NULL, 0);
338
+}
339
+
340
+/*-------------------------------------------------------------------------*/
341
+/**
342
+  @brief    Get the string associated to a key, convert to a double
343
+  @param    d Dictionary to search
344
+  @param    key Key string to look for
345
+  @param    notfound Value to return in case of error
346
+  @return   double
347
+
348
+  This function queries a dictionary for a key. A key as read from an
349
+  ini file is given as "section:key". If the key cannot be found,
350
+  the notfound value is returned.
351
+ */
352
+/*--------------------------------------------------------------------------*/
353
+double iniparser_getdouble(dictionary * d, char * key, double notfound)
354
+{
355
+    char    *   str ;
356
+
357
+    str = iniparser_getstring(d, key, INI_INVALID_KEY);
358
+    if (str==INI_INVALID_KEY) return notfound ;
359
+    return atof(str);
360
+}
361
+
362
+/*-------------------------------------------------------------------------*/
363
+/**
364
+  @brief    Get the string associated to a key, convert to a boolean
365
+  @param    d Dictionary to search
366
+  @param    key Key string to look for
367
+  @param    notfound Value to return in case of error
368
+  @return   integer
369
+
370
+  This function queries a dictionary for a key. A key as read from an
371
+  ini file is given as "section:key". If the key cannot be found,
372
+  the notfound value is returned.
373
+
374
+  A true boolean is found if one of the following is matched:
375
+
376
+  - A string starting with 'y'
377
+  - A string starting with 'Y'
378
+  - A string starting with 't'
379
+  - A string starting with 'T'
380
+  - A string starting with '1'
381
+
382
+  A false boolean is found if one of the following is matched:
383
+
384
+  - A string starting with 'n'
385
+  - A string starting with 'N'
386
+  - A string starting with 'f'
387
+  - A string starting with 'F'
388
+  - A string starting with '0'
389
+
390
+  The notfound value returned if no boolean is identified, does not
391
+  necessarily have to be 0 or 1.
392
+ */
393
+/*--------------------------------------------------------------------------*/
394
+int iniparser_getboolean(dictionary * d, const char * key, int notfound)
395
+{
396
+    char    *   c ;
397
+    int         ret ;
398
+
399
+    c = iniparser_getstring(d, key, INI_INVALID_KEY);
400
+    if (c==INI_INVALID_KEY) return notfound ;
401
+    if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
402
+        ret = 1 ;
403
+    } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
404
+        ret = 0 ;
405
+    } else {
406
+        ret = notfound ;
407
+    }
408
+    return ret;
409
+}
410
+
411
+/*-------------------------------------------------------------------------*/
412
+/**
413
+  @brief    Finds out if a given entry exists in a dictionary
414
+  @param    ini     Dictionary to search
415
+  @param    entry   Name of the entry to look for
416
+  @return   integer 1 if entry exists, 0 otherwise
417
+
418
+  Finds out if a given entry exists in the dictionary. Since sections
419
+  are stored as keys with NULL associated values, this is the only way
420
+  of querying for the presence of sections in a dictionary.
421
+ */
422
+/*--------------------------------------------------------------------------*/
423
+int iniparser_find_entry(
424
+    dictionary  *   ini,
425
+    char        *   entry
426
+)
427
+{
428
+    int found=0 ;
429
+    if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
430
+        found = 1 ;
431
+    }
432
+    return found ;
433
+}
434
+
435
+/*-------------------------------------------------------------------------*/
436
+/**
437
+  @brief    Set an entry in a dictionary.
438
+  @param    ini     Dictionary to modify.
439
+  @param    entry   Entry to modify (entry name)
440
+  @param    val     New value to associate to the entry.
441
+  @return   int 0 if Ok, -1 otherwise.
442
+
443
+  If the given entry can be found in the dictionary, it is modified to
444
+  contain the provided value. If it cannot be found, -1 is returned.
445
+  It is Ok to set val to NULL.
446
+ */
447
+/*--------------------------------------------------------------------------*/
448
+int iniparser_set(dictionary * ini, char * entry, char * val)
449
+{
450
+    return dictionary_set(ini, strlwc(entry), val) ;
451
+}
452
+
453
+/*-------------------------------------------------------------------------*/
454
+/**
455
+  @brief    Delete an entry in a dictionary
456
+  @param    ini     Dictionary to modify
457
+  @param    entry   Entry to delete (entry name)
458
+  @return   void
459
+
460
+  If the given entry can be found, it is deleted from the dictionary.
461
+ */
462
+/*--------------------------------------------------------------------------*/
463
+void iniparser_unset(dictionary * ini, char * entry)
464
+{
465
+    dictionary_unset(ini, strlwc(entry));
466
+}
467
+
468
+/*-------------------------------------------------------------------------*/
469
+/**
470
+  @brief	Load a single line from an INI file
471
+  @param    input_line  Input line, may be concatenated multi-line input
472
+  @param    section     Output space to store section
473
+  @param    key         Output space to store key
474
+  @param    value       Output space to store value
475
+  @return   line_status value
476
+ */
477
+/*--------------------------------------------------------------------------*/
478
+static line_status iniparser_line(
479
+    char * input_line,
480
+    char * section,
481
+    char * key,
482
+    char * value)
483
+{   
484
+    line_status sta ;
485
+    char        line[ASCIILINESZ+1];
486
+    int         len ;
487
+
488
+    strcpy(line, strstrip(input_line));
489
+    len = (int)strlen(line);
490
+
491
+    sta = LINE_UNPROCESSED ;
492
+    if (len<1) {
493
+        /* Empty line */
494
+        sta = LINE_EMPTY ;
495
+    } else if (line[0]=='#') {
496
+        /* Comment line */
497
+        sta = LINE_COMMENT ; 
498
+    } else if (line[0]=='[' && line[len-1]==']') {
499
+        /* Section name */
500
+        sscanf(line, "[%[^]]", section);
501
+        strcpy(section, strstrip(section));
502
+        strcpy(section, strlwc(section));
503
+        sta = LINE_SECTION ;
504
+    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
505
+           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2
506
+           ||  sscanf (line, "%[^=] = %[^;#]",     key, value) == 2) {
507
+        /* Usual key=value, with or without comments */
508
+        strcpy(key, strstrip(key));
509
+        strcpy(key, strlwc(key));
510
+        strcpy(value, strstrip(value));
511
+        /*
512
+         * sscanf cannot handle '' or "" as empty values
513
+         * this is done here
514
+         */
515
+        if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
516
+            value[0]=0 ;
517
+        }
518
+        sta = LINE_VALUE ;
519
+    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
520
+           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
521
+        /*
522
+         * Special cases:
523
+         * key=
524
+         * key=;
525
+         * key=#
526
+         */
527
+        strcpy(key, strstrip(key));
528
+        strcpy(key, strlwc(key));
529
+        value[0]=0 ;
530
+        sta = LINE_VALUE ;
531
+    } else {
532
+        /* Generate syntax error */
533
+        sta = LINE_ERROR ;
534
+    }
535
+    return sta ;
536
+}
537
+
538
+/*-------------------------------------------------------------------------*/
539
+/**
540
+  @brief    Parse an ini file and return an allocated dictionary object
541
+  @param    ininame Name of the ini file to read.
542
+  @return   Pointer to newly allocated dictionary
543
+
544
+  This is the parser for ini files. This function is called, providing
545
+  the name of the file to be read. It returns a dictionary object that
546
+  should not be accessed directly, but through accessor functions
547
+  instead.
548
+
549
+  The returned dictionary must be freed using iniparser_freedict().
550
+ */
551
+/*--------------------------------------------------------------------------*/
552
+dictionary * iniparser_load(const char * ininame)
553
+{
554
+    FILE * in ;
555
+
556
+    char line    [ASCIILINESZ+1] ;
557
+    char section [ASCIILINESZ+1] ;
558
+    char key     [ASCIILINESZ+1] ;
559
+    char tmp     [ASCIILINESZ+1] ;
560
+    char val     [ASCIILINESZ+1] ;
561
+
562
+    int  last=0 ;
563
+    int  len ;
564
+    int  lineno=0 ;
565
+    int  errs=0;
566
+
567
+    dictionary * dict ;
568
+
569
+    if ((in=fopen(ininame, "r"))==NULL) {
570
+        fprintf(stderr, MSG_PREFIX "cannot open %s\n", ininame);
571
+        return NULL ;
572
+    }
573
+
574
+    dict = dictionary_new(0) ;
575
+    if (!dict) {
576
+        fclose(in);
577
+        return NULL ;
578
+    }
579
+
580
+    memset(line,    0, ASCIILINESZ);
581
+    memset(section, 0, ASCIILINESZ);
582
+    memset(key,     0, ASCIILINESZ);
583
+    memset(val,     0, ASCIILINESZ);
584
+    last=0 ;
585
+
586
+    while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
587
+        lineno++ ;
588
+        len = (int)strlen(line)-1;
589
+        /* Safety check against buffer overflows */
590
+        if (line[len]!='\n') {
591
+            fprintf(stderr,
592
+                    MSG_PREFIX "input line too long in %s (%d)\n",
593
+                    ininame,
594
+                    lineno);
595
+            dictionary_del(dict);
596
+            fclose(in);
597
+            return NULL ;
598
+        }
599
+        /* Get rid of \n and spaces at end of line */
600
+        while ((len>=0) &&
601
+                ((line[len]=='\n') || (isspace(line[len])))) {
602
+            line[len]=0 ;
603
+            len-- ;
604
+        }
605
+        /* Detect multi-line */
606
+        if (line[len]=='\\') {
607
+            /* Multi-line value */
608
+            last=len ;
609
+            continue ;
610
+        } else {
611
+            last=0 ;
612
+        }
613
+        switch (iniparser_line(line, section, key, val)) {
614
+            case LINE_EMPTY:
615
+            case LINE_COMMENT:
616
+            break ;
617
+
618
+            case LINE_SECTION:
619
+            errs = dictionary_set(dict, section, NULL);
620
+            break ;
621
+
622
+            case LINE_VALUE:
623
+            sprintf(tmp, "%s:%s", section, key);
624
+            errs = dictionary_set(dict, tmp, val) ;
625
+            break ;
626
+
627
+            case LINE_ERROR:
628
+            fprintf(stderr, MSG_PREFIX "syntax error in %s (%d):\n",
629
+                    ininame,
630
+                    lineno);
631
+            fprintf(stderr, "-> %s\n", line);
632
+            errs++ ;
633
+            break;
634
+
635
+            default:
636
+            break ;
637
+        }
638
+        memset(line, 0, ASCIILINESZ);
639
+        last=0;
640
+        if (errs<0) {
641
+            fprintf(stderr, MSG_PREFIX "memory allocation failure\n");
642
+            break ;
643
+        }
644
+    }
645
+    if (errs) {
646
+        dictionary_del(dict);
647
+        dict = NULL ;
648
+    }
649
+    fclose(in);
650
+    return dict ;
651
+}
652
+
653
+/*-------------------------------------------------------------------------*/
654
+/**
655
+  @brief    Free all memory associated to an ini dictionary
656
+  @param    d Dictionary to free
657
+  @return   void
658
+
659
+  Free all memory associated to an ini dictionary.
660
+  It is mandatory to call this function before the dictionary object
661
+  gets out of the current context.
662
+ */
663
+/*--------------------------------------------------------------------------*/
664
+void iniparser_freedict(dictionary **d)
665
+{
666
+	if (*d) {
667
+		dictionary_del(*d);
668
+		*d = NULL;
669
+	}
670
+}
671
+
672
+/* vim: set ts=4 et sw=4 tw=75 */
673
+#include <stdarg.h>
674
+
675
+#define __handle_format \
676
+	char key[128]; \
677
+	{ \
678
+		va_list args; \
679
+		va_start(args, keyfmt); \
680
+		vsnprintf(key, sizeof(key)-1, keyfmt, args); \
681
+		va_end(args); \
682
+	}
683
+
684
+char *ini_get_string(dictionary *d, char *def, const char *keyfmt, ...) {
685
+	__handle_format;
686
+	return iniparser_getstring(d, key, def);
687
+}
688
+
689
+char *ini_get_string_copy(dictionary *d, char *def , const char *keyfmt, ...) {
690
+	__handle_format;
691
+	char *ret = iniparser_getstring(d, key, def);
692
+	if (ret)
693
+		ret = strdup(ret);
694
+	return ret;
695
+}
696
+
697
+int ini_get_int(dictionary *d, int def, const char *keyfmt, ...) {
698
+	__handle_format;
699
+	return iniparser_getint(d, key, def);
700
+}
701
+
702
+double ini_get_double(dictionary *d, double def, const char *keyfmt, ...) {
703
+	__handle_format;
704
+	return iniparser_getdouble(d, key, def);
705
+}
706
+
707
+int ini_get_bool(dictionary *d, int def   , const char *keyfmt, ...) {
708
+	__handle_format;
709
+	return iniparser_getboolean(d, key, def);
710
+}
711
+
712
+#undef __handle_format

+ 309
- 0
iniparser.h View File

@@ -0,0 +1,309 @@
1
+/*
2
+Copyright (c) 2000-2007 by Nicolas Devillard.
3
+MIT License
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a
6
+copy of this software and associated documentation files (the "Software"),
7
+to deal in the Software without restriction, including without limitation
8
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
+and/or sell copies of the Software, and to permit persons to whom the
10
+Software is furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in
13
+all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+DEALINGS IN THE SOFTWARE.
22
+*/
23
+
24
+/*-------------------------------------------------------------------------*/
25
+/**
26
+   @file    iniparser.h
27
+   @author  N. Devillard
28
+   @date    Sep 2007
29
+   @version 3.0
30
+   @brief   Parser for ini files.
31
+*/
32
+/*--------------------------------------------------------------------------*/
33
+
34
+/*
35
+	Id: iniparser.h,v 1.24 2007-11-23 21:38:19 ndevilla Exp
36
+	Revision: 1.24
37
+*/
38
+
39
+#ifndef _INIPARSER_H_
40
+#define _INIPARSER_H_
41
+
42
+/*---------------------------------------------------------------------------
43
+   								Includes
44
+ ---------------------------------------------------------------------------*/
45
+
46
+#include <stdio.h>
47
+#include <stdlib.h>
48
+#include <string.h>
49
+
50
+/*
51
+ * The following #include is necessary on many Unixes but not Linux.
52
+ * It is not needed for Windows platforms.
53
+ * Uncomment it if needed.
54
+ */
55
+#include <unistd.h>
56
+
57
+#include "inidict.h"
58
+
59
+/* Local extras */
60
+char *	ini_get_string		(dictionary *d, char *def , const char *keyfmt, ...);
61
+char *	ini_get_string_copy	(dictionary *d, char *def , const char *keyfmt, ...);
62
+int		ini_get_int			(dictionary *d, int def   , const char *keyfmt, ...);
63
+double	ini_get_double		(dictionary *d, double def, const char *keyfmt, ...);
64
+int		ini_get_bool		(dictionary *d, int def   , const char *keyfmt, ...);
65
+
66
+/*---------------------------------------------------------------------------
67
+   								Macros
68
+ ---------------------------------------------------------------------------*/
69
+/** For backwards compatibility only */
70
+#define iniparser_getstr(d, k)  iniparser_getstring(d, k, NULL)
71
+#define iniparser_setstr        iniparser_setstring
72
+
73
+/*-------------------------------------------------------------------------*/
74
+/**
75
+  @brief    Get number of sections in a dictionary
76
+  @param    d   Dictionary to examine
77
+  @return   int Number of sections found in dictionary
78
+
79
+  This function returns the number of sections found in a dictionary.
80
+  The test to recognize sections is done on the string stored in the
81
+  dictionary: a section name is given as "section" whereas a key is
82
+  stored as "section:key", thus the test looks for entries that do not
83
+  contain a colon.
84
+
85
+  This clearly fails in the case a section name contains a colon, but
86
+  this should simply be avoided.
87
+
88
+  This function returns -1 in case of error.
89
+ */
90
+/*--------------------------------------------------------------------------*/
91
+
92
+int iniparser_getnsec(dictionary * d);
93
+
94
+
95
+/*-------------------------------------------------------------------------*/
96
+/**
97
+  @brief    Get name for section n in a dictionary.
98
+  @param    d   Dictionary to examine
99
+  @param    n   Section number (from 0 to nsec-1).
100
+  @return   Pointer to char string
101
+
102
+  This function locates the n-th section in a dictionary and returns
103
+  its name as a pointer to a string statically allocated inside the
104
+  dictionary. Do not free or modify the returned string!
105
+
106
+  This function returns NULL in case of error.
107
+ */
108
+/*--------------------------------------------------------------------------*/
109
+
110
+char * iniparser_getsecname(dictionary * d, int n);
111
+
112
+
113
+/*-------------------------------------------------------------------------*/
114
+/**
115
+  @brief    Save a dictionary to a loadable ini file
116
+  @param    d   Dictionary to dump
117
+  @param    f   Opened file pointer to dump to
118
+  @return   void
119
+
120
+  This function dumps a given dictionary into a loadable ini file.
121
+  It is Ok to specify @c stderr or @c stdout as output files.
122
+ */
123
+/*--------------------------------------------------------------------------*/
124
+
125
+void iniparser_dump_ini(dictionary * d, FILE * f);
126
+
127
+/*-------------------------------------------------------------------------*/
128
+/**
129
+  @brief    Dump a dictionary to an opened file pointer.
130
+  @param    d   Dictionary to dump.
131
+  @param    f   Opened file pointer to dump to.
132
+  @return   void
133
+
134
+  This function prints out the contents of a dictionary, one element by
135
+  line, onto the provided file pointer. It is OK to specify @c stderr
136
+  or @c stdout as output files. This function is meant for debugging
137
+  purposes mostly.
138
+ */
139
+/*--------------------------------------------------------------------------*/
140
+void iniparser_dump(dictionary * d, FILE * f);
141
+
142
+/*-------------------------------------------------------------------------*/
143
+/**
144
+  @brief    Get the string associated to a key
145
+  @param    d       Dictionary to search
146
+  @param    key     Key string to look for
147
+  @param    def     Default value to return if key not found.
148
+  @return   pointer to statically allocated character string
149
+
150
+  This function queries a dictionary for a key. A key as read from an
151
+  ini file is given as "section:key". If the key cannot be found,
152
+  the pointer passed as 'def' is returned.
153
+  The returned char pointer is pointing to a string allocated in
154
+  the dictionary, do not free or modify it.
155
+ */
156
+/*--------------------------------------------------------------------------*/
157
+char * iniparser_getstring(dictionary * d, const char * key, char * def);
158
+
159
+/*-------------------------------------------------------------------------*/
160
+/**
161
+  @brief    Get the string associated to a key, convert to an int
162
+  @param    d Dictionary to search
163
+  @param    key Key string to look for
164
+  @param    notfound Value to return in case of error
165
+  @return   integer
166
+
167
+  This function queries a dictionary for a key. A key as read from an
168
+  ini file is given as "section:key". If the key cannot be found,
169
+  the notfound value is returned.
170
+
171
+  Supported values for integers include the usual C notation
172
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
173
+  are supported. Examples:
174
+
175
+  - "42"      ->  42
176
+  - "042"     ->  34 (octal -> decimal)
177
+  - "0x42"    ->  66 (hexa  -> decimal)
178
+
179
+  Warning: the conversion may overflow in various ways. Conversion is
180
+  totally outsourced to strtol(), see the associated man page for overflow
181
+  handling.
182
+
183
+  Credits: Thanks to A. Becker for suggesting strtol()
184
+ */
185
+/*--------------------------------------------------------------------------*/
186
+int iniparser_getint(dictionary * d, const char * key, int notfound);
187
+
188
+/*-------------------------------------------------------------------------*/
189
+/**
190
+  @brief    Get the string associated to a key, convert to a double
191
+  @param    d Dictionary to search
192
+  @param    key Key string to look for
193
+  @param    notfound Value to return in case of error
194
+  @return   double
195
+
196
+  This function queries a dictionary for a key. A key as read from an
197
+  ini file is given as "section:key". If the key cannot be found,
198
+  the notfound value is returned.
199
+ */
200
+/*--------------------------------------------------------------------------*/
201
+double iniparser_getdouble(dictionary * d, char * key, double notfound);
202
+
203
+/*-------------------------------------------------------------------------*/
204
+/**
205
+  @brief    Get the string associated to a key, convert to a boolean
206
+  @param    d Dictionary to search
207
+  @param    key Key string to look for
208
+  @param    notfound Value to return in case of error
209
+  @return   integer
210
+
211
+  This function queries a dictionary for a key. A key as read from an
212
+  ini file is given as "section:key". If the key cannot be found,
213
+  the notfound value is returned.
214
+
215
+  A true boolean is found if one of the following is matched:
216
+
217
+  - A string starting with 'y'
218
+  - A string starting with 'Y'
219
+  - A string starting with 't'
220
+  - A string starting with 'T'
221
+  - A string starting with '1'
222
+
223
+  A false boolean is found if one of the following is matched:
224
+
225
+  - A string starting with 'n'
226
+  - A string starting with 'N'
227
+  - A string starting with 'f'
228
+  - A string starting with 'F'
229
+  - A string starting with '0'
230
+
231
+  The notfound value returned if no boolean is identified, does not
232
+  necessarily have to be 0 or 1.
233
+ */
234
+/*--------------------------------------------------------------------------*/
235
+int iniparser_getboolean(dictionary * d, const char * key, int notfound);
236
+
237
+
238
+/*-------------------------------------------------------------------------*/
239
+/**
240
+  @brief    Set an entry in a dictionary.
241
+  @param    ini     Dictionary to modify.
242
+  @param    entry   Entry to modify (entry name)
243
+  @param    val     New value to associate to the entry.
244
+  @return   int 0 if Ok, -1 otherwise.
245
+
246
+  If the given entry can be found in the dictionary, it is modified to
247
+  contain the provided value. If it cannot be found, -1 is returned.
248
+  It is Ok to set val to NULL.
249
+ */
250
+/*--------------------------------------------------------------------------*/
251
+int iniparser_setstring(dictionary * ini, char * entry, char * val);
252
+
253
+
254
+/*-------------------------------------------------------------------------*/
255
+/**
256
+  @brief    Delete an entry in a dictionary
257
+  @param    ini     Dictionary to modify
258
+  @param    entry   Entry to delete (entry name)
259
+  @return   void
260
+
261
+  If the given entry can be found, it is deleted from the dictionary.
262
+ */
263
+/*--------------------------------------------------------------------------*/
264
+void iniparser_unset(dictionary * ini, char * entry);
265
+
266
+/*-------------------------------------------------------------------------*/
267
+/**
268
+  @brief    Finds out if a given entry exists in a dictionary
269
+  @param    ini     Dictionary to search
270
+  @param    entry   Name of the entry to look for
271
+  @return   integer 1 if entry exists, 0 otherwise
272
+
273
+  Finds out if a given entry exists in the dictionary. Since sections
274
+  are stored as keys with NULL associated values, this is the only way
275
+  of querying for the presence of sections in a dictionary.
276
+ */
277
+/*--------------------------------------------------------------------------*/
278
+int iniparser_find_entry(dictionary * ini, char * entry) ;
279
+
280
+/*-------------------------------------------------------------------------*/
281
+/**
282
+  @brief    Parse an ini file and return an allocated dictionary object
283
+  @param    ininame Name of the ini file to read.
284
+  @return   Pointer to newly allocated dictionary
285
+
286
+  This is the parser for ini files. This function is called, providing
287
+  the name of the file to be read. It returns a dictionary object that
288
+  should not be accessed directly, but through accessor functions
289
+  instead.
290
+
291
+  The returned dictionary must be freed using iniparser_freedict().
292
+ */
293
+/*--------------------------------------------------------------------------*/
294
+dictionary * iniparser_load(const char * ininame);
295
+
296
+/*-------------------------------------------------------------------------*/
297
+/**
298
+  @brief    Free all memory associated to an ini dictionary
299
+  @param    d Dictionary to free
300
+  @return   void
301
+
302
+  Free all memory associated to an ini dictionary.
303
+  It is mandatory to call this function before the dictionary object
304
+  gets out of the current context.
305
+ */
306
+/*--------------------------------------------------------------------------*/
307
+void iniparser_freedict(dictionary **d);
308
+
309
+#endif

+ 420
- 0
input.c View File

@@ -0,0 +1,420 @@
1
+#include <stdio.h>
2
+#include <math.h>
3
+#include <stdlib.h>
4
+#include <unistd.h>
5
+#include <signal.h>
6
+#include <string.h>
7
+
8
+#include "libfuncs/io.h"
9
+#include "libfuncs/log.h"
10
+
11
+#include "libtsfuncs/tsfuncs.h"
12
+
13
+#include "data.h"
14
+#include "config.h"
15
+#include "network.h"
16
+
17
+extern int keep_going;
18
+
19
+// #define dump_tables 1
20
+
21
+#define MAX_ZERO_READS 3
22
+
23
+/*         Start: 3 seconds on connect */
24
+/* In connection: Max UDP timeout == 3 seconds (read) + 2 seconds (connect) == 5 seconds */
25
+#define UDP_READ_RETRIES 3
26
+#define UDP_READ_TIMEOUT 1000
27
+
28
+/*         Start: 1/4 seconds on connect */
29
+/* In connection: Max TCP timeout == 5 seconds (read) + 2 seconds (connect)             == 7 seconds */
30
+/* In connection: Max TCP timeout == 5 seconds (read) + 8 seconds (connect, host unrch) == 13 seconds */
31
+#define TCP_READ_RETRIES 5
32
+#define TCP_READ_TIMEOUT 1000
33
+
34
+// Init pmt_pid and nit_pid
35
+// Return 0 on error, 1 on success
36
+int input_process_pat(INPUT *r) {
37
+	int i;
38
+	int num_programs = 0;
39
+	INPUT_STREAM *s = &r->stream;
40
+	struct ts_pat *pat = s->pat;
41
+
42
+	s->nit_pid = 0x10; // Default NIT pid
43
+	for (i=0;i<pat->programs_num;i++) {
44
+		struct ts_pat_program *prg = pat->programs[i];
45
+		if (prg->pid) {
46
+			if (prg->program == 0) { // NIT
47
+				s->nit_pid = prg->pid;
48
+			} else { // PAT
49
+				s->pmt_pid = prg->pid;
50
+				num_programs++;
51
+				break; // Get only the first program
52
+			}
53
+		}
54
+	}
55
+
56
+	// MPTS is not supported as input stream in the moment
57
+	if (num_programs > 1) {
58
+		LOGf("INPUT : %-10s | Can't handle MPTS (%d programs) as input stream\n", r->channel->id, num_programs);
59
+		return 0;
60
+	}
61
+
62
+	return 1;
63
+}
64
+
65
+void input_rewrite_pat(INPUT *r) {
66
+	int i;
67
+	INPUT_STREAM *s = &r->stream;
68
+	struct ts_pat *new_pat = ts_pat_copy(s->pat);
69
+	if (!new_pat)
70
+		return;
71
+
72
+	// Rewrite PAT pids
73
+	for (i=0;i<new_pat->programs_num;i++) {
74
+		struct ts_pat_program *prg = new_pat->programs[i];
75
+		if (prg->program != 0) { // Skip NIT
76
+			// Add pid to rewriter
77
+			pidref_add(s->pidref, prg->pid, s->pidref->base_pid);
78
+			// Rewrite PAT
79
+			prg->program = r->channel->service_id;
80
+			prg->pid     = s->pidref->base_pid;
81
+			s->pidref->base_pid++;
82
+		}
83
+	}
84
+
85
+	// Save rewritten packet
86
+	ts_pat_regenerate_packets(new_pat);
87
+	s->pat_rewritten = new_pat;
88
+}
89
+
90
+void input_rewrite_pmt(INPUT *r) {
91
+	INPUT_STREAM *s = &r->stream;
92
+	struct ts_pmt *new_pmt = ts_pmt_copy(s->pmt);
93
+	if (!new_pmt)
94
+		return;
95
+
96
+	// Rewrite PMT pids
97
+	new_pmt->ts_header.pid = pidref_get_new_pid(s->pidref, s->pmt_pid);
98
+	new_pmt->section_header->ts_id_number = r->channel->service_id;
99
+
100
+	uint16_t org_pcr_pid = new_pmt->PCR_pid;
101
+	s->pcr_pid = new_pmt->PCR_pid;
102
+	pidref_add(s->pidref, org_pcr_pid, s->pidref->base_pid);
103
+	new_pmt->PCR_pid = s->pidref->base_pid;
104
+	r->output_pcr_pid = new_pmt->PCR_pid;
105
+	s->pidref->base_pid++;
106
+
107
+	int i;
108
+	for (i=0;i<new_pmt->streams_num;i++) {
109
+		struct ts_pmt_stream *stream = new_pmt->streams[i];
110
+		if (stream->pid == org_pcr_pid) { // Already rewritten and added to pidref
111
+			stream->pid = new_pmt->PCR_pid;
112
+			continue;
113
+		}
114
+		pidref_add(s->pidref, stream->pid, s->pidref->base_pid);
115
+		stream->pid = s->pidref->base_pid;
116
+		s->pidref->base_pid++;
117
+	}
118
+
119
+	ts_pmt_regenerate_packets(new_pmt);
120
+	s->pmt_rewritten = new_pmt;
121
+}
122
+
123
+
124
+extern CONFIG *config;
125
+
126
+void input_buffer_add(INPUT *r, uint8_t *data, int datasize) {
127
+	if (r->dienow)
128
+		return;
129
+	if (r->ifd)
130
+		write(r->ifd, data, datasize);
131
+	if (r->disabled) {
132
+		unsigned long bufsize = r->buf->input - r->buf->output;
133
+		double buffull = ((double)bufsize / r->buf->size) * 100;
134
+		if (buffull <= 50) {
135
+			proxy_log(r, "Enable input");
136
+			r->disabled = 0;
137
+		} else {
138
+			return;
139
+		}
140
+	}
141
+	if (cbuf_fill(r->buf, data, datasize) != 0) {
142
+		proxy_log(r, "Disable input, buffer is full.");
143
+		r->disabled = 1;
144
+	}
145
+}
146
+
147
+int input_check_state(INPUT *r) {
148
+	if (r->dienow) {
149
+		// proxy_log(r, "Forced disconnect.");
150
+		return 2;
151
+	}
152
+	if (r->reconnect) {
153
+		proxy_log(r, "Forced reconnect.");
154
+		return 1;
155
+	}
156
+	return 0;
157
+}
158
+
159
+int process_pat(INPUT *r, uint16_t pid, uint8_t *ts_packet) {
160
+	INPUT_STREAM *s = &r->stream;
161
+
162
+	if (pid != 0)
163
+		return 0;
164
+
165
+	// Process PAT
166
+	s->pat = ts_pat_push_packet(s->pat, ts_packet);
167
+
168
+	s->last_pat = ts_pat_push_packet(s->last_pat, ts_packet);
169
+	if (s->last_pat->initialized) {
170
+		if (!ts_pat_is_same(s->pat, s->last_pat)) {
171
+			proxy_log(r, "PAT changed.");
172
+			return -1; // Reconnect
173
+		}
174
+		ts_pat_free(&s->last_pat);
175
+		s->last_pat = ts_pat_alloc();
176
+	}
177
+
178
+	if (s->pat->initialized) {
179
+		// PMT pid is still unknown
180
+		if (!s->pmt_pid) {
181
+			if (!input_process_pat(r)) {
182
+				proxy_log(r, "Can't parse PAT to find PMT pid.");
183
+				return -1;
184
+			}
185
+		}
186
+		// Rewritten PAT is not yet initialized
187
+		if (!s->pat_rewritten || !s->pat_rewritten->initialized) {
188
+			input_rewrite_pat(r);
189
+#if dump_tables
190
+			proxy_log(r, "PAT found!");
191
+			proxy_log(r, "*** Original PAT ***");
192
+			ts_pat_dump(s->pat);
193
+			proxy_log(r, "*** Rewritten PAT ***");
194
+			ts_pat_dump(s->pat_rewritten);
195
+			pidref_dump(s->pidref);
196
+#endif
197
+		}
198
+
199
+		// Only if output file is written
200
+		if (r->ifd && s->pat_rewritten && s->pat_rewritten->initialized) {
201
+			int j;
202
+			struct ts_pat *P = s->pat_rewritten;
203
+			for (j=0;j<P->section_header->num_packets;j++) {
204
+				ts_packet_set_cont(P->section_header->packet_data + (j * TS_PACKET_SIZE), j + s->pid_pat_cont);
205
+			}
206
+			P->ts_header.continuity = s->pid_pat_cont;
207
+			s->pid_pat_cont += P->section_header->num_packets;
208
+			write(r->ifd, P->section_header->packet_data, P->section_header->num_packets * TS_PACKET_SIZE);
209
+		}
210
+	}
211
+	// Stuff packet with NULL data
212
+	memset(ts_packet, 0xff, TS_PACKET_SIZE);
213
+	ts_packet[0] = 0x47;
214
+	ts_packet[1] = 0x1F;
215
+	ts_packet[2] = 0xFF;
216
+	ts_packet[3] = 0x10;
217
+
218
+	return 1;
219
+}
220
+
221
+int process_pmt(INPUT *r, uint16_t pid, uint8_t *ts_packet) {
222
+	INPUT_STREAM *s = &r->stream;
223
+
224
+	if (!pid || pid != s->pmt_pid)
225
+		return 0;
226
+
227
+	s->pmt = ts_pmt_push_packet(s->pmt, ts_packet);
228
+
229
+	s->last_pmt = ts_pmt_push_packet(s->last_pmt, ts_packet);
230
+	if (s->last_pmt->initialized) {
231
+		if (!ts_pmt_is_same(s->pmt, s->last_pmt)) {
232
+			proxy_log(r, "PMT changed.");
233
+			return -2; // Reconnect
234
+		}
235
+		ts_pmt_free(&s->last_pmt);
236
+		s->last_pmt = ts_pmt_alloc();
237
+	}
238
+
239
+	if (s->pmt->initialized) {
240
+		if (!s->pmt_rewritten || !s->pmt_rewritten->initialized) {
241
+			input_rewrite_pmt(r);
242
+#if dump_tables
243
+			proxy_log(r, "PMT found!");
244
+			proxy_log(r, "*** Original PMT ***");
245
+			ts_pmt_dump(s->pmt);
246
+			proxy_log(r, "*** Rewritten PMT ***");
247
+			ts_pmt_dump(s->pmt_rewritten);
248
+			// pidref_dump(s->pidref);
249
+#endif
250
+		}
251
+		if (s->pmt_rewritten && s->pmt_rewritten->initialized) {
252
+			int j;
253
+			struct ts_pmt *P = s->pmt_rewritten;
254
+			for (j=0;j<P->section_header->num_packets;j++) {
255
+				ts_packet_set_cont(P->section_header->packet_data + (j * TS_PACKET_SIZE), j + s->pid_pmt_cont);
256
+			}
257
+			P->ts_header.continuity = s->pid_pmt_cont;
258
+			s->pid_pmt_cont += P->section_header->num_packets;
259
+			input_buffer_add(r, P->section_header->packet_data, P->section_header->num_packets * TS_PACKET_SIZE);
260
+		}
261
+		return -1;
262
+	}
263
+	return 1;
264
+}
265
+
266
+int in_worktime(int start, int end) {
267
+	if (!start && !end)
268
+		return 1;
269
+	struct tm ltime;
270
+	struct tm *ltimep = &ltime;
271
+	time_t timep = time(NULL);
272
+	ltimep = localtime_r(&timep, ltimep);
273
+	int seconds = ltime.tm_sec + ltime.tm_min * 60 + ltime.tm_hour * 3600;
274
+	if (start > end) {
275
+		if (start >= seconds && end < seconds)
276
+			return 0;
277
+		else
278
+			return 1;
279
+	} else {
280
+		if (start <= seconds && end > seconds)
281
+			return 1;
282
+		else
283
+			return 0;
284
+	}
285
+	return 1;
286
+}
287
+
288
+void * input_stream(void *self) {
289
+	INPUT *r = self;
290
+	INPUT_STREAM *s = &r->stream;
291
+	char buf[FRAME_PACKET_SIZE];
292
+
293
+	signal(SIGPIPE, SIG_IGN);
294
+
295
+	proxy_log(r, "Start");
296
+	r->working = in_worktime(r->channel->worktime_start, r->channel->worktime_end);
297
+	if (!r->working)
298
+		proxy_log(r, "Worktime has not yet begin, sleeping.");
299
+
300
+	int http_code = 0;
301
+	while (keep_going) {
302
+		if (input_check_state(r) == 2) // r->dienow is on
303
+			goto QUIT;
304
+
305
+		while (!r->working) {
306
+			usleep(250000);
307
+			r->working = in_worktime(r->channel->worktime_start, r->channel->worktime_end);
308
+			if (r->working)
309
+				proxy_log(r, "Worktime started.");
310
+			if (!keep_going)
311
+				goto QUIT;
312
+		}
313
+
314
+		r->working = in_worktime(r->channel->worktime_start, r->channel->worktime_end);
315
+
316
+		int result = connect_source(self, 1, FRAME_PACKET_SIZE * 1000, &http_code);
317
+		if (result != 0)
318
+			goto RECONNECT;
319
+
320
+		channel_source sproto = get_sproto(r->channel->source);
321
+
322
+		if (mpeg_sync(r, sproto) != 0) {
323
+			proxy_log(r, "Can't sync input MPEG TS");
324
+			sleep(2);
325
+			goto RECONNECT;
326
+		}
327
+
328
+		ssize_t readen;
329
+		int max_zero_reads = MAX_ZERO_READS;
330
+
331
+		// Reset all stream parameters on reconnect.
332
+		input_stream_reset(r);
333
+
334
+		for (;;) {
335
+			r->working = in_worktime(r->channel->worktime_start, r->channel->worktime_end);
336
+			if (!r->working) {
337
+				proxy_log(r, "Worktime ended.");
338
+				goto STOP;
339
+			}
340
+
341
+			switch (input_check_state(r)) {
342
+				case 1: goto RECONNECT;		// r->reconnect is on
343
+				case 2: goto QUIT;			// r->dienow is on
344
+			}
345
+
346
+			if (sproto == tcp_sock) {
347
+				readen = fdread_ex(r->sock, buf, FRAME_PACKET_SIZE, TCP_READ_TIMEOUT, TCP_READ_RETRIES, 1);
348
+			} else {
349
+				readen = fdread_ex(r->sock, buf, FRAME_PACKET_SIZE, UDP_READ_TIMEOUT, UDP_READ_RETRIES, 0);
350
+			}
351
+
352
+			if (readen < 0)
353
+				goto RECONNECT;
354
+			if (readen == 0) { // ho, hum, wtf is going on here?
355
+				proxy_log(r, "Zero read, continuing...");
356
+				if (--max_zero_reads == 0) {
357
+					proxy_log(r, "Max zero reads reached, reconnecting.");
358
+					break;
359
+				}
360
+				continue;
361
+			}
362
+
363
+			int i;
364
+			for (i=0; i<readen; i+=188) {
365
+
366
+				if (r->dienow)
367
+					goto QUIT;
368
+				uint8_t *ts_packet = (uint8_t *)buf + i;
369
+				uint16_t pid = ts_packet_get_pid(ts_packet);
370
+
371
+				if (process_pat(r, pid, ts_packet) < 0)
372
+					goto RECONNECT;
373
+
374
+				int pmt_result = process_pmt(r, pid, ts_packet);
375
+				if (pmt_result == -2)
376
+					goto RECONNECT;
377
+				if (pmt_result < 0) // PMT rewritten
378
+					continue;
379
+
380
+				pid = ts_packet_get_pid(ts_packet);
381
+				// Kill incomming NIT, SDT, EIT, RST, TDT/TOT
382
+				if (pid == s->nit_pid || pid == 0x10 || pid == 0x11 || pid == 0x12 || pid == 0x13 || pid == 0x14 || pid == 0x1fff) {
383
+					// LOGf("INPUT: %-10s: Remove PID %03x\n", r->channel->id, pid);
384
+					continue;
385
+				}
386
+
387
+				// Do we have PAT and PMT? (if we have pmt we have PAT, so check only for PMT)
388
+				if (s->pmt_rewritten && pid == s->pcr_pid && ts_packet_has_pcr(ts_packet)) {
389
+					s->input_pcr = ts_packet_get_pcr(ts_packet);
390
+					// LOGf("INPUT : [%-12s] PCR: %llu\n", r->channel->id, s->input_pcr);
391
+				}
392
+
393
+				// Yes, we have enough data to start outputing
394
+				if (s->input_pcr) {
395
+					pidref_change_packet_pid(ts_packet, pid, s->pidref);
396
+					input_buffer_add(r, ts_packet, TS_PACKET_SIZE);
397
+					if (!r->input_ready)
398
+						r->input_ready = 1;
399
+				}
400
+			}
401
+
402
+			max_zero_reads = MAX_ZERO_READS;
403
+		}
404
+		proxy_log(r, "fdread timeout");
405
+RECONNECT:
406
+		proxy_log(r, "Reconnect");
407
+		shutdown_fd(&(r->sock));
408
+		chansrc_next(r->channel);
409
+		continue;
410
+STOP:
411
+		proxy_log(r, "Stop");
412
+		shutdown_fd(&(r->sock));
413
+		continue;
414
+QUIT:
415
+		break;
416
+	}
417
+	proxy_close(config->inputs, &r);
418
+
419
+	return 0;
420
+}

+ 6
- 0
input.h View File

@@ -0,0 +1,6 @@
1
+#ifndef INPUT_H
2
+#define INPUT_H
3
+
4
+void * input_stream(void *);
5
+
6
+#endif

+ 1
- 0
libfuncs

@@ -0,0 +1 @@
1
+Subproject commit f68888b2d07d55acaafc9882c8e06162e882e263

+ 1
- 0
libtsfuncs

@@ -0,0 +1 @@
1
+Subproject commit 5627d6d019dcc605862181231bb3841fb5dd4a6b

+ 167
- 0
mptsd.c View File

@@ -0,0 +1,167 @@
1
+/*
2
+ * MPTS Generator
3
+ * Reads MPEG TS over HTTP/UDP and generate MPTS
4
+ * Copyright (C) 2010 Unix Solutions Ltd.
5
+ */
6
+#include <stdlib.h>
7
+#include <unistd.h>
8
+#include <signal.h>
9
+#include <errno.h>
10
+
11
+#include "libfuncs/libfuncs.h"
12
+
13
+#include "libtsfuncs/tsfuncs.h"
14
+
15
+#include "iniparser.h"
16
+#include "data.h"
17
+#include "config.h"
18
+#include "network.h"
19
+#include "input.h"
20
+#include "output.h"
21
+#include "web_server.h"
22
+
23
+#define PROGRAM_NAME "ux-mptsd"
24
+
25
+const char *program_id = PROGRAM_NAME " " GIT_VER " build " BUILD_ID;
26
+
27
+char *server_sig = PROGRAM_NAME;
28
+char *server_ver = GIT_VER;
29
+char *copyright  = "Copyright (C) 2010-2011 Unix Solutions Ltd.";
30
+
31
+CONFIG *config;
32
+int keep_going = 1;
33
+int rcvsig = 0;
34
+
35
+void spawn_input_threads(CONFIG *conf) {
36
+	LNODE *lc, *lctmp;
37
+	LNODE *lr, *lrtmp;
38
+	int spawned = 0;
39
+	list_for_each(conf->channels, lc, lctmp) {
40
+		CHANNEL *c = lc->data;
41
+		int restreamer_active = 0;
42
+		list_lock(conf->inputs);
43
+		list_for_each(conf->inputs, lr, lrtmp) {
44
+			INPUT *r = lr->data;
45
+			if (xstrcmp(r->name, c->name)==0) {
46
+				restreamer_active = 1;
47
+				break;
48
+			}
49
+		}
50
+		list_unlock(conf->inputs);
51
+		if (!restreamer_active) {
52
+			INPUT *nr = input_new(c->name, c);
53
+			if (nr) {
54
+				list_add(conf->inputs, nr);
55
+//				LOGf("SPAWN : %s thread.\n", c->name);
56
+				if (pthread_create(&nr->thread, NULL, &input_stream, nr) == 0) {
57
+					spawned++;
58
+					pthread_detach(nr->thread);
59
+				} else {
60
+					LOGf("ERROR: Can't create proxy for %s\n", c->name);
61
+				}
62
+			} else {
63
+				LOGf("ERROR: Error creating proxy for %s\n", c->name);
64
+			}
65
+		}
66
+	}
67
+	LOGf("INPUT : %d thread%s spawned.\n", spawned, spawned > 1 ? "s" : "");
68
+}
69
+
70
+void spawn_output_threads(CONFIG *conf) {
71
+	if (pthread_create(&conf->output->psi_thread, NULL, &output_handle_psi, conf) == 0) {
72
+		pthread_detach(conf->output->psi_thread);
73
+	} else {
74
+		LOGf("ERROR: Can't spawn PSI output thread: %s\n", strerror(errno));
75
+		exit(1);
76
+	}
77
+
78
+	if (pthread_create(&conf->output->mix_thread, NULL, &output_handle_mix, conf) == 0) {
79
+		pthread_detach(conf->output->mix_thread);
80
+	} else {
81
+		LOGf("ERROR: Can't spawn MIX output thread: %s\n", strerror(errno));
82
+		exit(1);
83
+	}
84
+
85
+	if (pthread_create(&conf->output->write_thread, NULL, &output_handle_write, conf) == 0) {
86
+		pthread_detach(conf->output->write_thread);
87
+	} else {
88
+		LOGf("ERROR: Can't spawn WRITE output thread: %s\n", strerror(errno));
89
+		exit(1);
90
+	}
91
+}
92
+
93
+void kill_threads(CONFIG *conf) {
94
+	int loops = 0;
95
+	conf->output->dienow = 1;
96
+	while (conf->inputs->items || conf->output->dienow < 4) {
97
+		usleep(50000);
98
+		if (loops++ > 60) // 3 seconds
99
+			exit(0);
100
+	}
101
+}
102
+
103
+/*
104
+void do_reconnect(CONFIG *conf) {
105
+	LNODE *l, *tmp;
106
+	list_lock(conf->inputs);
107
+	list_for_each(conf->inputs, l, tmp) {
108
+		INPUT *r = l->data;
109
+		r->reconnect = 1;
110
+	}
111
+	list_unlock(conf->inputs);
112
+}
113
+
114
+void do_reconf(CONFIG *conf) {
115
+//	load_channels_config();
116
+	spawn_input_threads(conf);
117
+}
118
+*/
119
+
120
+void signal_quit(int sig) {
121
+	rcvsig = sig;
122
+	keep_going = 0;
123
+}
124
+
125
+void init_signals() {
126
+	signal(SIGCHLD, SIG_IGN);
127
+	signal(SIGPIPE, SIG_IGN);
128
+
129
+//	signal(SIGHUP , do_reconf);
130
+//	signal(SIGUSR1, do_reconnect);
131
+
132
+	signal(SIGINT , signal_quit);
133
+	signal(SIGTERM, signal_quit);
134
+}
135
+
136
+int main(int argc, char **argv) {
137
+	set_http_response_server_ident(server_sig, server_ver);
138
+	ts_set_log_func(LOG);
139
+
140
+	config = config_alloc();
141
+	config_load(config, argc, argv);
142
+
143
+	output_psi_init(config, config->output);
144
+
145
+	daemonize(config->pidfile);
146
+	web_server_start(config);
147
+	log_init(config->logident, config->syslog_active, config->pidfile == NULL, config->loghost, config->logport);
148
+	init_signals(config);
149
+
150
+	LOGf("INIT  : %s %s (%s)\n" , server_sig, server_ver, config->ident);
151
+
152
+	connect_output(config->output);
153
+	spawn_input_threads(config);
154
+	spawn_output_threads(config);
155
+
156
+	do { usleep(50000); } while(keep_going);
157
+
158
+	kill_threads(config);
159
+	web_server_stop(config);
160
+
161
+	LOGf("SHUTDOWN: Signal %d | %s %s (%s)\n", rcvsig, server_sig, server_ver, config->ident);
162
+	config_free(&config);
163
+
164
+	log_close();
165
+
166
+	exit(0);
167
+}

+ 12
- 0
mptsd.conf View File

@@ -0,0 +1,12 @@
1
+[Global]
2
+network_id=1
3
+
4
+[Timeouts]
5
+pat = 100
6
+pmt = 200
7
+sdt = 500
8
+nit = 2000
9
+eit = 1000
10
+tdt = 5000
11
+tot = 15000
12
+stats = 1000

+ 63
- 0
mptsd_channels.conf View File

@@ -0,0 +1,63 @@
1
+[Global]
2
+provider_name = Unix Solutions
3
+transport_stream_id = 1
4
+
5
+[Channel1]
6
+service_id	= 1
7
+id			= btv
8
+name		= bTV
9
+source1		= http://signal-server/stb/btv.mpg
10
+#source2		= http://signal-server2/stb/btv.mpg
11
+#source3		= http://signal-server3/stb/btv.mpg
12
+#source4		= udp://239.0.0.1:5000/
13
+#worktime	= 14:18-14:19
14
+
15
+[Channel2]
16
+service_id	= 2
17
+id			= kanal1
18
+name		= "Kanal 1"
19
+source		= http://signal-server/stb/kanal1.mpg
20
+
21
+[Channel3]
22
+service_id	= 3
23
+id			= novatv
24
+name		= "Nova"
25
+source1		= http://signal-server/stb/novatv.mpg
26
+
27
+[Channel4]
28
+service_id	= 4
29
+id			= tv2
30
+name		= "TV 2"
31
+source1		= http://signal-server/stb/tv2.mpg
32
+
33
+[Channel5]
34
+service_id	= 5
35
+id			= ngc
36
+name		= "NatGeo"
37
+source1		= http://signal-server/stb/ngc.mpg
38
+
39
+[Channel6]
40
+service_id	= 6
41
+id			= tv1
42
+name		= "TV 1"
43
+source1		= http://signal-server/stb/tv1.mpg
44
+
45
+[Channel7]
46
+service_id	= 7
47
+id			= planetahit
48
+name		= "Planeta HIT"
49
+source1		= http://signal-server/stb/planetahit.mpg
50
+
51
+[Channel8]
52
+service_id	= 8
53
+id			= fresh
54
+name		= "Radio Fresh"
55
+radio		= yes
56
+source		= http://signal-server/stb/fresh.mpg
57
+
58
+[Channel9]
59
+service_id	= 9
60
+id			= bgradio
61
+name		= "BG Radio"
62
+radio		= yes
63
+source1		= http://signal-server/stb/bgradio.mpg

+ 70
- 0
mptsd_epg.conf View File

@@ -0,0 +1,70 @@
1
+[btv-now]
2
+start=1271055600
3
+duration=3600
4
+event=Денят е прекрасен
5
+sdescr=женско токшоу с водещи Ива и Боги
6
+descr=123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890xxxxxxyy
7
+
8
+[btv-next]
9
+start=1271059200
10
+duration=3600
11
+event=Вятър от долината
12
+sdescr=сериал, еп. 69
13
+descr=
14
+
15
+[kanal1-now]
16
+start=1271056500
17
+duration=2700
18
+event=Дързост и красота
19
+sdescr=тв филм /2477 епизод/
20
+descr=
21
+
22
+[kanal1-next]
23
+start=1271059200
24
+duration=2700
25
+event=Жените с Марта Вачкова /токшоу/
26
+sdescr=
27
+descr=
28
+
29
+[novatv-now]
30
+start=1271052900
31
+duration=8100
32
+event=На кафе
33
+sdescr=предаване на НТВ
34
+descr=
35
+
36
+[novatv-next]
37
+start=1271061000
38
+duration=5400
39
+event=Къщата на парите
40
+sdescr=забавно предаване на НТВ
41
+descr=
42
+
43
+[tv2-now]
44
+start=1271055600
45
+duration=3600
46
+event=Антракт
47
+sdescr=
48
+descr=
49
+
50
+[tv2-next]
51
+start=1271059200
52
+duration=7200
53
+event=Парола. Риба меч
54
+sdescr=криминален с Джон Траволта, Холи Бери
55
+descr=
56
+
57
+[ngc-now]
58
+start=1271055600
59
+duration=3600
60
+event=Невероятната мечка
61
+sdescr=
62
+descr=
63
+
64
+[ngc-next]
65
+start=1271059200
66
+duration=3600
67
+event=Гигантската риба на Амазонка
68
+sdescr=
69
+descr=
70
+

+ 50
- 0
mptsd_nit.conf View File

@@ -0,0 +1,50 @@
1
+[Global]
2
+network_name = "Unixsol DVB-C network"
3
+
4
+[Transponder1]
5
+transport_stream_id = 1
6
+frequency           = 0658,0000
7
+modulation          = 64-QAM
8
+symbol_rate         = 006,8750
9
+
10
+[Transponder2]
11
+transport_stream_id = 2
12
+frequency           = 0666,0000
13
+modulation          = 64-QAM
14
+symbol_rate         = 006,8750
15
+
16
+[Transponder3]
17
+transport_stream_id = 3
18
+frequency           = 0674,0000
19
+modulation          = 64-QAM
20
+symbol_rate         = 006,8750
21
+
22
+[Transponder4]
23
+transport_stream_id = 4
24
+frequency           = 0682,0000
25
+modulation          = 64-QAM
26
+symbol_rate         = 006,8750
27
+
28
+[Transponder5]
29
+transport_stream_id = 5
30
+frequency           = 0690,0000
31
+modulation          = 64-QAM
32
+symbol_rate         = 006,8750
33
+
34
+[Transponder6]
35
+transport_stream_id = 6
36
+frequency           = 0698,0000
37
+modulation          = 64-QAM
38
+symbol_rate         = 006,8750
39
+
40
+[Transponder7]
41
+transport_stream_id = 7
42
+frequency           = 0706,0000
43
+modulation          = 64-QAM
44
+symbol_rate         = 006,8750
45
+
46
+[Transponder8]
47
+transport_stream_id = 8
48
+frequency           = 0714,0000
49
+modulation          = 64-QAM
50
+symbol_rate         = 006,8750

+ 11
- 0
mptsd_valgrind View File

@@ -0,0 +1,11 @@
1
+ulimit -c unlimited
2
+valgrind \
3
+	--log-file=mptsd-$(date +%F-%H:%M:%S).log \
4
+	--leak-check=full \
5
+	--show-reachable=yes \
6
+	--undef-value-errors=no \
7
+	--trace-children=yes \
8
+	--run-libc-freeres=yes \
9
+	--time-stamp=yes \
10
+		-- \
11
+	./mptsd -O 239.78.78.78

+ 294
- 0
network.c View File

@@ -0,0 +1,294 @@
1
+#include <stdio.h>
2
+#include <stdlib.h>
3
+#include <unistd.h>
4
+#include <string.h>
5
+#include <pthread.h>
6
+#include <errno.h>
7
+#include <arpa/inet.h>
8
+#include <netinet/in.h>
9
+#include <regex.h>
10
+
11
+#include "libfuncs/io.h"
12
+#include "libfuncs/log.h"
13
+#include "libfuncs/list.h"
14
+#include "libfuncs/asyncdns.h"
15
+
16
+#include "libtsfuncs/tsfuncs.h"
17
+
18
+#include "data.h"
19
+#include "config.h"
20
+
21
+extern char *server_sig;
22
+extern char *server_ver;
23
+extern CONFIG *config;
24
+
25
+int connect_udp(struct sockaddr_in send_to) {
26
+	int sendsock = socket(AF_INET, SOCK_DGRAM, 0);
27
+	if (sendsock < 0) {
28
+		LOGf("socket(SOCK_DGRAM): %s\n", strerror(errno));
29
+		return -1;
30
+	}
31
+	int on = 1;
32
+	setsockopt(sendsock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
33
+	// subscribe to multicast group
34
+	// LOGf("Using ttl %d\n", multicast_ttl);
35
+	if (IN_MULTICAST(ntohl(send_to.sin_addr.s_addr))) {
36
+		if (setsockopt(sendsock, IPPROTO_IP, IP_MULTICAST_TTL, &config->multicast_ttl, sizeof(config->multicast_ttl)) < 0) {
37
+			LOGf("setsockopt(IP_MUTICAST_TTL): %s\n", strerror(errno));
38
+			close(sendsock);
39
+			return -1;
40
+		}
41
+		if (setsockopt(sendsock, IPPROTO_IP, IP_MULTICAST_IF, &config->output_intf, sizeof(config->output_intf)) < 0) {
42
+			LOGf("setsockopt(IP_MUTICAST_IF, %s): %s\n", strerror(errno), inet_ntoa(config->output_intf));
43
+			close(sendsock);
44
+			return -1;
45
+		}
46
+	}
47
+	int writebuflen = 1316 * 100;
48
+	if (setsockopt(sendsock, SOL_SOCKET, SO_SNDBUF, (const char *)&writebuflen, sizeof(writebuflen)) < 0)
49
+		log_perror("play(): setsockopt(SO_SNDBUF)", errno);
50
+
51
+	// call connect to get errors
52
+	if (connect(sendsock, (struct sockaddr *)&send_to, sizeof send_to)) {
53
+		LOGf("udp_connect() error: %s\n", strerror(errno));
54
+		close(sendsock);
55
+		return -1;
56
+	}
57
+	return sendsock;
58
+}
59
+
60
+void connect_output(OUTPUT *o) {
61
+	struct sockaddr_in sock;
62
+	sock.sin_family = AF_INET;
63
+	sock.sin_port   = htons(o->out_port);
64
+	sock.sin_addr   = o->out_host;
65
+	o->out_sock = connect_udp(sock);
66
+	if (o->out_sock > -1) {
67
+		//LOGf("OUTPUT: Connected out_fd: %i | Output: udp://%s:%d\n", o->out_sock, inet_ntoa(o->out_host), o->out_port);
68
+	} else {
69
+		LOGf("ERROR: Can't connect output | Output: udp://%s:%d\n", inet_ntoa(o->out_host), o->out_port);
70
+		exit(1);
71
+	}
72
+}
73
+
74
+/*
75
+	On the last try, send no-signal to clients and exit
76
+	otherwise wait a little bit before trying again
77
+*/
78
+#define DO_RECONNECT do \
79
+{ \
80
+	chansrc_free(&src); \
81
+	if (retries == 0) { \
82
+		return -1; \
83
+	} else { \
84
+		if (errno != EHOSTUNREACH) /* When host is unreachable there is already a delay of ~4 secs per try so no sleep is needed */ \
85
+			usleep(PROXY_RETRY_TIMEOUT * 1000); \
86
+		if (r->dienow) \
87
+			return -1; \
88
+		return 1; \
89
+	} \
90
+} while(0)
91
+
92
+#define FATAL_ERROR do \
93
+{ \
94
+	chansrc_free(&src); \
95
+	return -1; \
96
+} while (0)
97
+
98
+/*
99
+	Returns:
100
+		-1 = exit thread
101
+		 1 = retry
102
+		 0 = connected ok
103
+*/
104
+int connect_source(INPUT *r, int retries, int readbuflen, int *http_code) {
105
+	CHANSRC *src = chansrc_init(r->channel->source);
106
+	if (!src) {
107
+		LOGf("ERR  : Can't parse channel source | Channel: %s Source: %s\n", r->channel->name, r->channel->source);
108
+		FATAL_ERROR;
109
+	}
110
+	r->connected = 0;
111
+	r->reconnect = 0;
112
+
113
+	int active = 1;
114
+	int dret = async_resolve_host(src->host, src->port, &(r->src_sockname), 5000, &active);
115
+	if (dret != 0) {
116
+		if (dret == 1)
117
+			proxy_log(r, "Can't resolve host");
118
+		if (dret == 2)
119
+			proxy_log(r, "Timeout resolving host");
120
+		DO_RECONNECT;
121
+	}
122
+
123
+	proxy_log(r, "Connecting");
124
+
125
+	char buf[1024];
126
+	*http_code = 0;
127
+	if (src->sproto == tcp_sock) {
128
+		r->sock = socket(PF_INET, SOCK_STREAM, 0);
129
+		if (r->sock < 0) {
130
+			log_perror("play(): Could not create SOCK_STREAM socket.", errno);
131
+			FATAL_ERROR;
132
+		}
133
+		//proxy_log(r, "Add");
134
+		if (do_connect(r->sock, (struct sockaddr *)&(r->src_sockname), sizeof(r->src_sockname), PROXY_CONNECT_TIMEOUT) < 0) {
135
+			LOGf("ERR  : Error connecting to %s srv_fd: %i err: %s\n", r->channel->source, r->sock, strerror(errno));
136
+			DO_RECONNECT;
137
+		}
138
+
139
+		snprintf(buf,sizeof(buf)-1, "GET /%s HTTP/1.0\nHost: %s:%u\nX-Smart-Client: yes\nUser-Agent: %s %s (%s)\n\n",
140
+		         src->path, src->host, src->port, server_sig, server_ver, config->ident);
141
+		buf[sizeof(buf)-1] = 0;
142
+		fdwrite(r->sock, buf, strlen(buf));
143
+
144
+		char xresponse[128];
145
+		memset(xresponse, 0, sizeof(xresponse));
146
+		memset(buf, 0, sizeof(buf));
147
+		regmatch_t res[4];
148
+		while (fdgetline(r->sock,buf,sizeof(buf)-1)) {
149
+			if (buf[0] == '\n' || buf[0] == '\r')
150
+				break;
151
+			if (strstr(buf,"HTTP/1.") != NULL) {
152
+				regex_t http_response;
153
+				regcomp(&http_response, "^HTTP/1.[0-1] (([0-9]{3}) .*)", REG_EXTENDED);
154
+				if (regexec(&http_response,buf,3,res,0) != REG_NOMATCH) {
155
+					char codestr[4];
156
+					if ((unsigned int)res[1].rm_eo-res[1].rm_so < sizeof(xresponse)) {
157
+						strncpy(xresponse, &buf[res[1].rm_so], res[1].rm_eo-res[1].rm_so);
158
+						xresponse[res[1].rm_eo-res[1].rm_so] = '\0';
159
+						chomp(xresponse);
160
+						strncpy(codestr, &buf[res[2].rm_so], res[2].rm_eo-res[2].rm_so);
161
+						codestr[3] = 0;
162
+						*http_code = atoi(codestr);
163
+					}
164
+				}
165
+				regfree(&http_response);
166
+			}
167
+			if (*http_code == 504) { // Extract extra error code
168
+				if (strstr(buf, "X-ErrorCode: ") != NULL) {
169
+					*http_code = atoi(buf+13);
170
+					break;
171
+				}
172
+			}
173
+		}
174
+		if (*http_code == 0) { // No valid HTTP response, retry
175
+			LOGf("DEBUG: Server returned not valid HTTP code | srv_fd: %i\n", r->sock);
176
+			DO_RECONNECT;
177
+		}
178
+		if (*http_code == 504) { // No signal, exit
179
+			LOGf("ERR  : Get no-signal for %s from %s on srv_fd: %i\n", r->channel->name, r->channel->source, r->sock);
180
+			FATAL_ERROR;
181
+		}
182
+		if (*http_code > 300) { // Unhandled or error codes, exit
183
+			LOGf("ERR  : Get code %i for %s from %s on srv_fd: %i exiting.\n", *http_code, r->channel->name, r->channel->source, r->sock);
184
+			FATAL_ERROR;
185
+		}
186
+		// connected ok, continue
187
+	} else {
188
+		if (!IN_MULTICAST(ntohl(r->src_sockname.sin_addr.s_addr))) {
189
+			LOGf("ERR  : %s is not multicast address\n", r->channel->source);
190
+			FATAL_ERROR;
191
+		}
192
+		struct ip_mreq mreq;
193
+		struct sockaddr_in receiving_from;
194
+
195
+		r->sock = socket(PF_INET, SOCK_DGRAM, 0);
196
+		if (r->sock < 0) {
197
+			log_perror("play(): Could not create SOCK_DGRAM socket.", errno);
198
+			FATAL_ERROR;
199
+		}
200
+		// LOGf("CONN : Listening on multicast socket %s srv_fd: %i retries left: %i\n", r->channel->source, r->sock, retries);
201
+		int on = 1;
202
+		setsockopt(r->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
203
+		// subscribe to multicast group
204
+		memcpy(&mreq.imr_multiaddr, &(r->src_sockname.sin_addr), sizeof(struct in_addr));
205
+		mreq.imr_interface.s_addr = htonl(INADDR_ANY);
206
+		if (setsockopt(r->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
207
+			LOGf("ERR  : Failed to add IP membership on %s srv_fd: %i\n", r->channel->source, r->sock);
208
+			FATAL_ERROR;
209
+		}
210
+		// bind to the socket so data can be read
211
+		memset(&receiving_from, 0, sizeof(receiving_from));
212
+		receiving_from.sin_family = AF_INET;
213
+		receiving_from.sin_addr   = r->src_sockname.sin_addr;
214
+		receiving_from.sin_port   = htons(src->port);
215
+		if (bind(r->sock, (struct sockaddr *) &receiving_from, sizeof(receiving_from)) < 0) {
216
+			LOGf("ERR  : Failed to bind to %s srv_fd: %i\n", r->channel->source, r->sock);
217
+			FATAL_ERROR;
218
+		}
219
+	}
220
+
221
+	if (setsockopt(r->sock, SOL_SOCKET, SO_RCVBUF, (const char *)&readbuflen, sizeof(readbuflen)) < 0)
222
+		log_perror("play(): setsockopt(SO_RCVBUF)", errno);
223
+
224
+	r->connected = 1;
225
+
226
+//	proxy_log(r, "Connected");
227
+	chansrc_free(&src);
228
+	return 0;
229
+}
230
+
231
+/*         Start: 3 seconds on connect */
232
+/* In connection: Max UDP timeout == 3 seconds (read) + 2 seconds (connect) == 5 seconds */
233
+#define UDP_READ_RETRIES 3
234
+#define UDP_READ_TIMEOUT 1000
235
+
236
+/*         Start: 1/4 seconds on connect */
237
+/* In connection: Max TCP timeout == 5 seconds (read) + 2 seconds (connect)             == 7 seconds */
238
+/* In connection: Max TCP timeout == 5 seconds (read) + 8 seconds (connect, host unrch) == 13 seconds */
239
+#define TCP_READ_RETRIES 5
240
+#define TCP_READ_TIMEOUT 1000
241
+
242
+/*
243
+	Returns:
244
+		0 = synced ok
245
+		1 = not synced, reconnect
246
+*/
247
+int mpeg_sync(INPUT *r, channel_source source_proto) {
248
+	time_t sync_start = time(NULL);
249
+	unsigned int sync_packets = 0;
250
+	unsigned int read_bytes = 0;
251
+	char syncframe[188];
252
+
253
+	int _timeout = TCP_READ_TIMEOUT;
254
+	int _retries = TCP_READ_RETRIES;
255
+	if (source_proto == udp_sock) {
256
+		_timeout = UDP_READ_TIMEOUT;
257
+		_retries = UDP_READ_RETRIES;
258
+	}
259
+	do {
260
+		if (r->dienow)
261
+			return 1;
262
+resync:
263
+		if (fdread_ex(r->sock, syncframe, 1, _timeout, _retries, 1) != 1) {
264
+			proxy_log(r, "mpeg_sync fdread() timeoutA");
265
+			return 1; // reconnect
266
+		}
267
+		// LOGf("DEBUG:     Read 0x%02x Offset %u Sync: %u\n", (uint8_t)syncframe[0], read_bytes, sync_packets);
268
+		read_bytes++;
269
+		if (syncframe[0] == 0x47) {
270
+			ssize_t rdsz = fdread_ex(r->sock, syncframe, 188-1, _timeout, _retries, 1);
271
+			if (rdsz != 188-1) {
272
+				proxy_log(r, "mpeg_sync fdread() timeoutB");
273
+				return 1; // reconnect
274
+			}
275
+			read_bytes += 188-1;
276
+			if (++sync_packets == 7) // sync 7 packets
277
+				break;
278
+			goto resync;
279
+		} else {
280
+			sync_packets = 0;
281
+		}
282
+		if (read_bytes > FRAME_PACKET_SIZE) { // Can't sync in 1316 bytes
283
+			proxy_log(r, "mpeg_sync can't sync after 1316 bytes");
284
+			return 1; // reconnect
285
+		}
286
+		if (sync_start+2 <= time(NULL)) { // Do not sync in two seconds
287
+			proxy_log(r, "mpeg_sync can't sync in 2 seconds");
288
+			return 1; // reconnect
289
+		}
290
+	} while (1);
291
+	if (read_bytes-FRAME_PACKET_SIZE != 0)
292
+		LOGf("INPUT : [%-12s] TS synced after %u bytes\n", r->channel->id, read_bytes-FRAME_PACKET_SIZE);
293
+	return 0;
294
+}

+ 10
- 0
network.h View File

@@ -0,0 +1,10 @@
1
+#ifndef NETWORK_H
2
+#define NETWORK_H
3
+
4
+#include "data.h"
5
+
6
+void	connect_output	(OUTPUT *o);
7
+int		connect_source	(INPUT *r, int retries, int readbuflen, int *http_code);
8
+int     mpeg_sync       (INPUT *r, channel_source source_proto);
9
+
10
+#endif

+ 13
- 0
output.h View File

@@ -0,0 +1,13 @@
1
+#ifndef OUTPUT_H
2
+#define OUTPUT_H
3
+
4
+#include "config.h"
5
+
6
+void output_psi_init			(CONFIG *conf, OUTPUT *output);
7
+void output_psi_free			(OUTPUT *output);
8
+
9
+void * output_handle_psi		(void *_config);
10
+void * output_handle_mix		(void *_config);
11
+void * output_handle_write		(void *_config);
12
+
13
+#endif

+ 215
- 0
output_mix.c View File

@@ -0,0 +1,215 @@
1
+#include <math.h>
2
+#include <stdlib.h>
3
+#include <unistd.h>
4
+#include <string.h>
5
+#include <signal.h>
6
+#include <errno.h>
7
+
8
+#include "libfuncs/libfuncs.h"
9
+
10
+#include "libtsfuncs/tsfuncs.h"
11
+
12
+#include "data.h"
13
+#include "config.h"
14
+#include "input.h"
15
+
16
+void output_show_programs(CONFIG *conf) {
17
+	LNODE *lr, *lrtmp;
18
+	list_for_each(conf->inputs, lr, lrtmp) {
19
+		INPUT *r = lr->data;
20
+		if (r->input_ready == 1) {
21
+			LOGf("OUTPUT: [%-12s] Service %d appeared.\n", r->channel->id, r->channel->service_id);
22
+			r->input_ready++;
23
+		}
24
+	}
25
+}
26
+
27
+void * output_handle_mix(void *_config) {
28
+	LNODE *lr, *lrtmp;
29
+	LNODE *inpt; // Track last used input
30
+	CONFIG *conf = _config;
31
+	OUTPUT *o = conf->output;
32
+	int buf_in_use = 0;
33
+	unsigned int o_datasize, o_packets, packets;
34
+	unsigned int o_maxpackets = o->obuf[0].size / TS_PACKET_SIZE;
35
+
36
+	signal(SIGPIPE, SIG_IGN);
37
+
38
+	inpt = conf->inputs->tail; // Next is the first one
39
+	while (!o->dienow) {
40
+		OBUF *curbuf = &o->obuf[buf_in_use];
41
+
42
+		usleep(o->obuf_ms); // Fill interval
43
+
44
+		output_show_programs(conf);
45
+
46
+		while (curbuf->status != obuf_empty) {
47
+			if (o->dienow)
48
+				goto OUT;
49
+			//LOGf("MIX: Waiting for obuf %d\n", buf_in_use);
50
+			usleep(1);
51
+		}
52
+
53
+		list_lock(conf->inputs);
54
+
55
+		o_datasize = o->psibuf->input - o->psibuf->output; // PSI data
56
+		list_for_each(conf->inputs, lr, lrtmp) { // INPUT data
57
+			INPUT *r = lr->data;
58
+			o_datasize += r->buf->input - r->buf->output;
59
+		}
60
+
61
+		o_packets  = o_datasize / TS_PACKET_SIZE;
62
+		packets = min(o_packets, o_maxpackets);
63
+
64
+		double null_per_data = 1;
65
+		double data_per_null = 0;
66
+		if (o_maxpackets - packets) {
67
+			data_per_null = (double)packets / (o_maxpackets-packets);
68
+			if (data_per_null < 1) {
69
+				null_per_data = (double)(o_maxpackets-packets) / packets;
70
+				data_per_null = 1;
71
+			}
72
+		}
73
+
74
+		curbuf->status = obuf_filling; // Mark buffer as being filled
75
+
76
+		if (conf->debug) {
77
+			LOGf("MIX[%2d]: Data:%6u | Bufsz:%6d | Packs:%4u | D/N:%5.2f/%5.2f\n",
78
+				buf_in_use,
79
+				o_datasize,
80
+				curbuf->size,
81
+				packets,
82
+				((double)packets / o_maxpackets) * 100,
83
+				(double)100 - ((double)packets / o_maxpackets) * 100
84
+			);
85
+			LOGf("datapacks:%5d maxpacks:%5d null:%5d (%5.2f) | null_per_data:%5.2f data_per_null:%5.2f\n",
86
+				packets,
87
+				o_maxpackets,
88
+				o_maxpackets-packets,
89
+				100-((double)packets / o_maxpackets)*100,
90
+				null_per_data,
91
+				data_per_null
92
+			);
93
+		}
94
+
95
+		unsigned int nulls=0, null_packets_count = o_maxpackets - packets;
96
+		// The is no data in the input buffer, send only NULLs
97
+		if (null_packets_count == o_maxpackets) {
98
+			// Increase sended packets
99
+			list_for_each(conf->inputs, lr, lrtmp) {
100
+				INPUT *r = lr->data;
101
+				r->outputed_packets += o_maxpackets;
102
+			}
103
+			goto NEXT_BUFFER;
104
+		}
105
+
106
+		unsigned int data_packets;
107
+		int data_size;
108
+		uint8_t *data;
109
+		for (data_packets=0;data_packets<packets;data_packets++) {
110
+			if (o->dienow)
111
+				break;
112
+
113
+			// Try the PSI data first
114
+			data = cbuf_get(o->psibuf, TS_PACKET_SIZE, &data_size);
115
+			if (data && data_size == TS_PACKET_SIZE)
116
+				goto SEND_PACKET;
117
+
118
+			// Loop over inputs
119
+			int inputs_left = conf->inputs->items;
120
+			while (inputs_left--) {
121
+				inpt = inpt->next;
122
+				INPUT *r = inpt->data;
123
+				if (!r || !r->buf)
124
+					continue;
125
+
126
+				// Move pcrs || Move & rewrite prcs
127
+				if (conf->pcr_mode == 1 || conf->pcr_mode == 3) {
128
+					// Is there any data in this input?
129
+					data = cbuf_peek(r->buf, TS_PACKET_SIZE, &data_size);
130
+					if (data_size == TS_PACKET_SIZE) {
131
+						uint16_t pid = ts_packet_get_pid(data);
132
+						// Do we have PCR packet?
133
+						if (pid == r->output_pcr_pid && ts_packet_has_pcr(data)) {
134
+							if (r->output_pcr_packets_needed > 0 && r->outputed_packets < r->output_pcr_packets_needed) {
135
+								data = NULL;
136
+								data_size = 0;
137
+								continue;
138
+							}
139
+/*
140
+							LOGf("%10s | pcr:%15llu last_pcr:%15llu diff:%10lld packets:%5d needed_packs:%d diff:%d\n",
141
+								r->channel->id,
142
+								r->output_pcr,
143
+								r->output_last_pcr,
144
+								r->output_pcr - r->output_last_pcr,
145
+								r->outputed_packets,
146
+								r->output_pcr_packets_needed,
147
+								r->outputed_packets - r->output_pcr_packets_needed
148
+							);
149
+*/
150
+							uint64_t last_last_pcr = r->output_last_pcr;
151
+							r->output_last_pcr = r->output_pcr;
152
+							r->output_pcr = ts_packet_get_pcr(data);
153
+							if (last_last_pcr)
154
+								r->output_pcr_packets_needed = round(conf->output_bitrate / 8 * (r->output_pcr - r->output_last_pcr) / 27000000 / 188);
155
+							r->outputed_packets = 0;
156
+						}
157
+						data = cbuf_get(r->buf, TS_PACKET_SIZE, &data_size);
158
+						if (data_size == TS_PACKET_SIZE) // We have our data, no need to look at other inputs
159
+							break;
160
+					}
161
+				// Do not move PCRs
162
+				} else {
163
+					data = cbuf_get(r->buf, TS_PACKET_SIZE, &data_size);
164
+					if (data_size == TS_PACKET_SIZE) // We have our data, no need to look at other inputs
165
+						break;
166
+				}
167
+			} // while (inputs_left--)
168
+
169
+			// We have data. Mix it with NULLs and stuff it in the output buffer
170
+			// If the have no data, the output buffer will automaticaly be left
171
+			// with NULL packets
172
+SEND_PACKET:
173
+			if (data && data_size == TS_PACKET_SIZE) {
174
+				// Mix data with NULLs
175
+				if (nulls < null_packets_count) {
176
+					if (round(nulls * data_per_null) < round(data_packets * null_per_data)) {
177
+						nulls += round(data_packets * null_per_data) - round(nulls * data_per_null);
178
+					}
179
+					if (nulls > null_packets_count)
180
+						nulls = null_packets_count;
181
+				}
182
+				if (data_packets+nulls >= o_maxpackets) { // Can't happen
183
+					LOGf("wtf: %d packets:%d\n", data_packets+nulls, o_maxpackets);
184
+					break;
185
+				}
186
+				uint8_t *bufptr = curbuf->buf + ((data_packets + nulls) * TS_PACKET_SIZE);
187
+				memcpy(bufptr, data, TS_PACKET_SIZE);
188
+			}
189
+
190
+			// Increase sended packets
191
+			list_for_each(conf->inputs, lr, lrtmp) {
192
+				INPUT *r = lr->data;
193
+				r->outputed_packets++;
194
+			}
195
+		}
196
+
197
+NEXT_BUFFER:
198
+		list_unlock(conf->inputs);
199
+		curbuf->status = obuf_full; // Mark buffer as full
200
+
201
+		buf_in_use = buf_in_use ? 0 : 1; // Switch buffer
202
+	}
203
+
204
+OUT:
205
+	LOG("OUTPUT: MIX thread stopped.\n");
206
+	o->dienow++;
207
+
208
+	LNODE *l, *tmp;
209
+	list_for_each(conf->inputs, l, tmp) {
210
+		INPUT *r = l->data;
211
+		r->dienow = 1;
212
+	}
213
+
214
+	return 0;
215
+}

+ 249
- 0
output_psi.c View File

@@ -0,0 +1,249 @@
1
+#include <stdlib.h>
2
+#include <unistd.h>
3
+#include <signal.h>
4
+
5
+#include "libfuncs/log.h"
6
+#include "libfuncs/list.h"
7
+
8
+#include "libtsfuncs/tsfuncs.h"
9
+
10
+#include "config.h"
11
+#include "data.h"
12
+
13
+static void output_psi_init_pat(CONFIG *conf, OUTPUT *o) {
14
+	LNODE *lc, *lctmp;
15
+	o->pat = ts_pat_alloc_init(conf->transport_stream_id);
16
+	list_lock(conf->channels);
17
+	list_for_each(conf->channels, lc, lctmp) {
18
+		CHANNEL *c = lc->data;
19
+		ts_pat_add_program(o->pat, c->service_id, c->pmt_pid);
20
+	}
21
+	list_unlock(conf->channels);
22
+	gettimeofday(&o->pat_ts, NULL);
23
+}
24
+
25
+static void output_psi_init_nit(CONFIG *conf, OUTPUT *o) {
26
+	struct ts_nit *nit = ts_nit_alloc_init(conf->network_id);
27
+
28
+	ts_nit_add_network_name_descriptor(nit, conf->network_name);
29
+
30
+	if (conf->nit->items < 64) {
31
+		int num;
32
+		LNODE *lc, *lctmp;
33
+		uint32_t *freqs = malloc(conf->nit->items * sizeof(uint32_t));
34
+		uint32_t *services = malloc(conf->channels->items * sizeof(uint32_t));
35
+		num = 0;
36
+		list_lock(conf->nit);
37
+		list_for_each(conf->nit, lc, lctmp) {
38
+			NIT *ndata = lc->data;
39
+			freqs[num++] = ndata->_freq;
40
+		}
41
+		ts_nit_add_frequency_list_descriptor_cable(nit, conf->transport_stream_id, conf->network_id, freqs, num);
42
+		list_for_each(conf->nit, lc, lctmp) {
43
+			NIT *ndata = lc->data;
44
+			ts_nit_add_cable_delivery_descriptor(nit, ndata->ts_id, conf->network_id, ndata->_freq, ndata->_modulation, ndata->_symbol_rate);
45
+		}
46
+		list_unlock(conf->nit);
47
+		num = 0;
48
+		list_lock(conf->channels);
49
+		list_for_each(conf->channels, lc, lctmp) {
50
+			CHANNEL *c = lc->data;
51
+			uint32_t srv = 0;
52
+			srv  = (c->service_id &~ 0x00ff) << 16;
53
+			srv |= (c->service_id &~ 0xff00) << 8;
54
+			srv |= c->radio ? 0x02 : 0x01;
55
+			services[num++] = srv;
56
+		}
57
+		list_unlock(conf->channels);
58
+		ts_nit_add_service_list_descriptor(nit, conf->transport_stream_id, conf->network_id, services, num);
59
+		free(freqs);
60
+		free(services);
61
+	} else {
62
+		LOG("CONF  : Too much items in the NIT, maximum is 64! NIT not generated.\n");
63
+	}
64
+	gettimeofday(&o->nit_ts, NULL);
65
+	o->nit = nit;
66
+}
67
+
68
+static void output_psi_init_sdt(CONFIG *conf, OUTPUT *o) {
69
+	LNODE *lc, *lctmp;
70
+	struct ts_sdt *sdt = ts_sdt_alloc_init(conf->network_id, conf->transport_stream_id);
71
+	list_lock(conf->channels);
72
+	list_for_each(conf->channels, lc, lctmp) {
73
+		CHANNEL *c = lc->data;
74
+		ts_sdt_add_service_descriptor(sdt, c->service_id, c->radio == 0, conf->provider_name, c->name);
75
+	}
76
+	list_unlock(conf->channels);
77
+	gettimeofday(&o->sdt_ts, NULL);
78
+	o->sdt = sdt;
79
+}
80
+
81
+static void output_psi_init_tdt_tot(CONFIG *conf, OUTPUT *o) {
82
+	conf = conf; // Silence warning
83
+	o->pid_tdt_cont = 15;
84
+	o->tdt = ts_tdt_alloc_init(time(NULL));
85
+	o->tot = ts_tot_alloc_init(time(NULL));
86
+	gettimeofday(&o->tdt_ts, NULL);
87
+	gettimeofday(&o->tot_ts, NULL);
88
+}
89
+
90
+
91
+static void output_add_pat(OUTPUT *o) {
92
+	if (!o->pat->programs_num) {
93
+		LOG("OUTPUT: Error no programs in PAT!\n");
94
+		return;
95
+	}
96
+	int i;
97
+	struct ts_pat *pat = o->pat;
98
+//	LOGf("OUTPUT: Outputing PAT with %d programs\n", o->pat->programs_num);
99
+	for (i=0;i<pat->section_header->num_packets;i++) {
100
+		ts_packet_set_cont(pat->section_header->packet_data + (i * TS_PACKET_SIZE), i + o->pid_pat_cont);
101
+	}
102
+	pat->ts_header.continuity = o->pid_pat_cont;
103
+	o->pid_pat_cont += pat->section_header->num_packets;
104
+	cbuf_fill(o->psibuf, pat->section_header->packet_data, pat->section_header->num_packets * TS_PACKET_SIZE);
105
+//	ts_pat_dump(o->pat);
106
+}
107
+
108
+void output_add_nit(OUTPUT *o) {
109
+	if (!o || !o->nit)
110
+		return;
111
+	int i;
112
+	struct ts_nit *nit = o->nit;
113
+//	LOGf("OUTPUT: Outputing NIT\n");
114
+	for (i=0;i<nit->section_header->num_packets;i++) {
115
+		ts_packet_set_cont(nit->section_header->packet_data + (i * TS_PACKET_SIZE), i + o->pid_nit_cont);
116
+	}
117
+	nit->ts_header.continuity = o->pid_nit_cont;
118
+	o->pid_nit_cont += nit->section_header->num_packets;
119
+	cbuf_fill(o->psibuf, nit->section_header->packet_data, nit->section_header->num_packets * TS_PACKET_SIZE);
120
+//	ts_nit_dump(nit);
121
+}
122
+
123
+void output_add_sdt(OUTPUT *o) {
124
+	if (!o || !o->sdt)
125
+		return;
126
+	int i;
127
+	struct ts_sdt *sdt = o->sdt;
128
+//	LOGf("OUTPUT: Outputing SDT\n");
129
+	for (i=0;i<sdt->section_header->num_packets;i++) {
130
+		ts_packet_set_cont(sdt->section_header->packet_data + (i * TS_PACKET_SIZE), i + o->pid_sdt_cont);
131
+	}
132
+	sdt->ts_header.continuity = o->pid_sdt_cont;
133
+	o->pid_sdt_cont += sdt->section_header->num_packets;
134
+	cbuf_fill(o->psibuf, sdt->section_header->packet_data, sdt->section_header->num_packets * TS_PACKET_SIZE);
135
+//	ts_sdt_dump(o->sdt);
136
+}
137
+
138
+static void output_add_pid0x14(OUTPUT *o, struct ts_tdt *tdt) {
139
+	if (!o || !o->tdt)
140
+		return;
141
+	int i;
142
+//	LOGf("OUTPUT: Outputing TDT\n");
143
+	for (i=0;i<tdt->section_header->num_packets;i++) {
144
+		ts_packet_set_cont(tdt->section_header->packet_data + (i * TS_PACKET_SIZE), i + o->pid_tdt_cont);
145
+	}
146
+	tdt->ts_header.continuity = o->pid_tdt_cont;
147
+	o->pid_tdt_cont += tdt->section_header->num_packets;
148
+	cbuf_fill(o->psibuf, tdt->section_header->packet_data, tdt->section_header->num_packets * TS_PACKET_SIZE);
149
+}
150
+
151
+static void output_add_tdt(OUTPUT *o) {
152
+//	LOGf("OUTPUT: Outputing TDT\n");
153
+	ts_tdt_set_time(o->tdt, time(NULL));
154
+	output_add_pid0x14(o, o->tdt);
155
+//	ts_tdt_dump(o->tdt);
156
+}
157
+
158
+static void output_add_tot(OUTPUT *o) {
159
+//	LOGf("OUTPUT: Outputing TOT\n");
160
+	ts_tot_set_localtime_offset_sofia(o->tot, time(NULL));
161
+	output_add_pid0x14(o, o->tot);
162
+//	ts_tdt_dump(o->tot);
163
+}
164
+
165
+static void __output_add_eit(OUTPUT *o, struct ts_eit *eit) {
166
+	if (!eit)
167
+		return;
168
+//	LOGf("OUTPUT: Outputing EIT\n");
169
+	int i, pcnt = o->pid_eit_cont;
170
+	if (eit->section_header && eit->section_header->packet_data) {
171
+		for (i=0;i<eit->section_header->num_packets;i++) {
172
+			ts_packet_set_cont(eit->section_header->packet_data + (i * TS_PACKET_SIZE), i + pcnt);
173
+		}
174
+		eit->ts_header.continuity = pcnt;
175
+		o->pid_eit_cont += eit->section_header->num_packets;
176
+		cbuf_fill(o->psibuf, eit->section_header->packet_data, eit->section_header->num_packets * TS_PACKET_SIZE);
177
+	}
178
+//	ts_eit_dump(eit);
179
+}
180
+
181
+static void output_add_eit(CONFIG *conf, OUTPUT *o) {
182
+	LNODE *lr, *lrtmp;
183
+	config_load_epg(conf);
184
+	list_for_each(conf->inputs, lr, lrtmp) {
185
+		INPUT *r = lr->data;
186
+		__output_add_eit(o, r->channel->eit_now);
187
+		__output_add_eit(o, r->channel->eit_next);
188
+	}
189
+}
190
+
191
+static void output_psi_add(CONFIG *conf, OUTPUT *o, struct timeval *now) {
192
+	if (timeval_diff_msec(&o->pat_ts, now) >= conf->timeouts.pat) {
193
+		o->pat_ts = *now;
194
+		output_add_pat(o);
195
+	}
196
+	if (timeval_diff_msec(&o->nit_ts, now) >= conf->timeouts.nit) {
197
+		o->nit_ts = *now;
198
+		output_add_nit(o);
199
+	}
200
+	if (timeval_diff_msec(&o->sdt_ts, now) >= conf->timeouts.sdt) {
201
+		o->sdt_ts = *now;
202
+		output_add_sdt(o);
203
+	}
204
+	if (timeval_diff_msec(&o->tdt_ts, now) >= conf->timeouts.tdt) {
205
+		o->tdt_ts = *now;
206
+		output_add_tdt(o);
207
+	}
208
+	if (timeval_diff_msec(&o->tot_ts, now) >= conf->timeouts.tot) {
209
+		o->tot_ts = *now;
210
+		output_add_tot(o);
211
+	}
212
+	if (timeval_diff_msec(&o->eit_ts, now) >= conf->timeouts.eit) {
213
+		o->eit_ts = *now;
214
+		output_add_eit(conf, o);
215
+	}
216
+}
217
+
218
+
219
+void output_psi_init(CONFIG *conf, OUTPUT *output) {
220
+	output_psi_init_pat(conf, output);
221
+	output_psi_init_nit(conf, output);
222
+	output_psi_init_sdt(conf, output);
223
+	output_psi_init_tdt_tot(conf, output);
224
+	gettimeofday(&output->eit_ts, NULL);
225
+}
226
+
227
+void output_psi_free(OUTPUT *o) {
228
+	ts_pat_free(&o->pat);
229
+	ts_nit_free(&o->nit);
230
+	ts_sdt_free(&o->sdt);
231
+	ts_tdt_free(&o->tdt);
232
+	ts_tdt_free(&o->tot);
233
+}
234
+
235
+void * output_handle_psi(void *_config) {
236
+	CONFIG *conf = _config;
237
+	OUTPUT *o = conf->output;
238
+	struct timeval now;
239
+
240
+	signal(SIGPIPE, SIG_IGN);
241
+	while (!o->dienow) {
242
+		gettimeofday(&now, NULL);
243
+		output_psi_add(conf, o, &now);
244
+		usleep(10000); // 10 ms
245
+	}
246
+	LOG("OUTPUT: PSI thread stopped.\n");
247
+	o->dienow++;
248
+	return 0;
249
+}

+ 211
- 0
output_write.c View File

@@ -0,0 +1,211 @@
1
+#include <unistd.h>
2
+#include <string.h>
3
+#include <signal.h>
4
+#include <sys/time.h>
5
+#include <errno.h>
6
+#include <math.h>
7
+
8
+#include "libfuncs/io.h"
9
+#include "libfuncs/log.h"
10
+#include "libfuncs/list.h"
11
+
12
+#include "libtsfuncs/tsfuncs.h"
13
+
14
+#include "sleep.h"
15
+#include "data.h"
16
+#include "config.h"
17
+#include "network.h"
18
+
19
+void increase_process_priority() {
20
+	return;
21
+#ifdef __linux__
22
+	struct sched_param param;
23
+	param.sched_priority = 99;
24
+	if (sched_setscheduler(0, SCHED_FIFO, &param)==-1) {
25
+		log_perror("sched_setscheduler() failed!", errno);
26
+	} else {
27
+		LOGf("PRIO : sched_setschedule() succeded.\n");
28
+	}
29
+#endif
30
+}
31
+
32
+void ts_frame_process(CONFIG *conf, OUTPUT *o, uint8_t *data) {
33
+	int i;
34
+	uint16_t pid;
35
+	uint8_t *ts_packet;
36
+	for (i=0; i<FRAME_PACKET_SIZE; i+=TS_PACKET_SIZE) {
37
+		ts_packet = data + i;
38
+		pid = ts_packet_get_pid(ts_packet);
39
+
40
+		if (pid == 0x1fff) // NULL packet
41
+			o->padding_period += TS_PACKET_SIZE;
42
+
43
+		if (ts_packet_has_pcr(ts_packet)) {
44
+			uint64_t pcr = ts_packet_get_pcr(ts_packet);	// Current PCR
45
+			uint64_t new_pcr = pcr;
46
+			uint64_t bytes = o->traffic + i;
47
+
48
+			if (o->last_pcr[pid]) {
49
+				uint64_t old_pcr     = o->last_pcr[pid];
50
+				uint64_t old_org_pcr = o->last_org_pcr[pid];
51
+				uint64_t old_bytes   = o->last_traffic[pid];
52
+				if (old_org_pcr < pcr) { // Detect PCR wraparound
53
+					new_pcr = old_pcr + (double)((bytes - old_bytes) * 8 * 27000000) / o->output_bitrate;
54
+					// Rewrite pcrs || Move pcrs & rewrite prcs
55
+					if (conf->pcr_mode == 2 || conf->pcr_mode == 3) {
56
+						ts_packet_set_pcr(ts_packet, new_pcr);
57
+					}
58
+					if (conf->debug) {
59
+						uint64_t ts_rate = (double)(((bytes - old_bytes) * 8) * 27000000) / (pcr - old_org_pcr);
60
+						uint64_t ts_rate_new = (double)(((bytes - old_bytes) * 8) * 27000000) / (new_pcr - old_pcr);
61
+						LOGf("PCR[%03x]: old:%14llu new:%14llu pcr_diff:%8lld ts_rate:%9llu ts_rate_new:%9llu diff:%9lld | passed:%llu\n",
62
+							pid,
63
+							pcr,
64
+							new_pcr,
65
+							pcr - new_pcr,
66
+							ts_rate,
67
+							ts_rate_new,
68
+							ts_rate - ts_rate_new,
69
+							bytes - old_bytes
70
+						);
71
+					}
72
+ 				}
73
+			} else {
74
+//				if (config->debug) {
75
+//					LOGf("PCR[%03x]: %10llu init\n", pid, pcr);
76
+//				}
77
+			}
78
+			o->last_pcr[pid] = new_pcr;
79
+			o->last_org_pcr[pid] = pcr;
80
+			o->last_traffic[pid] = bytes;
81
+		}
82
+	}
83
+}
84
+
85
+ssize_t ts_frame_write(OUTPUT *o, uint8_t *data) {
86
+	ssize_t written;
87
+	written = fdwrite(o->out_sock, (char *)data, FRAME_PACKET_SIZE);
88
+	if (written >= 0) {
89
+		o->traffic        += written;
90
+		o->traffic_period += written;
91
+	}
92
+
93
+	if (o->ofd)
94
+		write(o->ofd, data, FRAME_PACKET_SIZE);
95
+
96
+	return written;
97
+}
98
+
99
+void * output_handle_write(void *_config) {
100
+	CONFIG *conf = _config;
101
+	OUTPUT *o = conf->output;
102
+	int buf_in_use = 0;
103
+	unsigned int o_datasize = 0;
104
+	struct timeval stats_ts, now;
105
+	struct timeval start_write_ts, end_write_ts, used_ts;
106
+	unsigned long long stats_interval;
107
+
108
+	signal(SIGPIPE, SIG_IGN);
109
+
110
+	increase_process_priority();
111
+
112
+	gettimeofday(&stats_ts, NULL);
113
+	while (!o->dienow) {
114
+		gettimeofday(&now, NULL);
115
+		OBUF *curbuf = &o->obuf[buf_in_use];
116
+
117
+		while (curbuf->status != obuf_full) { // Wait untill the buffer is ready ot it is already emptying
118
+			if (o->dienow)
119
+				goto OUT;
120
+			//LOGf("MIX: Waiting for obuf %d\n", buf_in_use);
121
+			usleep(1);
122
+		}
123
+		curbuf->status = obuf_emptying; // Mark buffer as being filled
124
+
125
+		// Show stats
126
+		stats_interval = timeval_diff_msec(&stats_ts, &now);
127
+		if (stats_interval > conf->timeouts.stats) {
128
+			stats_ts = now;
129
+			double out_kbps = (double)(o->traffic_period * 8) / 1000;
130
+			double out_mbps = (double)out_kbps / 1000;
131
+			double opadding = ((double)o->padding_period / o->traffic_period) * 100;
132
+
133
+			if (!conf->quiet) {
134
+				LOGf("STAT  : Pad:%6.2f%% Traf:%5.2f Mbps | %8.2f | %7llu\n",
135
+					opadding,
136
+					out_mbps,
137
+					out_kbps,
138
+					o->traffic_period
139
+				);
140
+			}
141
+			o->traffic_period = 0;
142
+			o->padding_period = 0;
143
+			o_datasize = 0;
144
+		}
145
+
146
+		gettimeofday(&start_write_ts, NULL);
147
+		int packets_written = 0, real_sleep_time = conf->output_tmout - conf->usleep_overhead;
148
+		long time_taken, time_diff, real_time, overhead = 0, overhead_total = 0;
149
+		ssize_t written = 0;
150
+		while (curbuf->written < curbuf->size) {
151
+			if (o->dienow)
152
+				goto OUT;
153
+			long sleep_interval = conf->output_tmout;
154
+			uint8_t *ts_frame = curbuf->buf + curbuf->written;
155
+			ts_frame_process(conf, o, ts_frame);	// Fix PCR and count NULL packets
156
+			written += ts_frame_write(o, ts_frame);	// Write packet to network/file
157
+			curbuf->written += FRAME_PACKET_SIZE;
158
+			if (packets_written) {
159
+				time_taken = timeval_diff_usec(&start_write_ts, &used_ts);
160
+				real_time  = packets_written * (conf->output_tmout + conf->usleep_overhead);
161
+				time_diff = real_time - time_taken;
162
+				overhead = (time_taken / packets_written) - sleep_interval;
163
+				overhead_total += overhead;
164
+/*
165
+				LOGf("[%5d] time_taken:%5ld real_time:%5ld time_diff:%ld | overhead:%5ld overhead_total:%5ld\n",
166
+					packets_written,
167
+					time_taken,
168
+					real_time,
169
+					time_diff,
170
+					overhead,
171
+					overhead_total
172
+				);
173
+*/
174
+				if (time_diff > real_sleep_time) {
175
+					sleep_interval = time_diff - conf->usleep_overhead;
176
+					if (sleep_interval < 0)
177
+						sleep_interval = 1;
178
+					// LOGf("Add sleep. time_diff: %ld sleep_interval: %ld\n", time_diff, sleep_interval);
179
+				} else {
180
+					//LOGf("Skip sleep %ld\n", time_diff);
181
+					sleep_interval = 0;
182
+				}
183
+
184
+			}
185
+			if (sleep_interval > 0)
186
+				usleep(sleep_interval);
187
+			gettimeofday(&used_ts, NULL);
188
+			packets_written++;
189
+		}
190
+		gettimeofday(&end_write_ts, NULL);
191
+		unsigned long long write_time = timeval_diff_usec(&start_write_ts, &end_write_ts);
192
+		if (write_time < o->obuf_ms * 1000) {
193
+			//LOGf("Writen for -%llu us less\n", o->obuf_ms*1000 - write_time);
194
+			usleep(o->obuf_ms*1000 - write_time);
195
+		} else {
196
+			//LOGf("Writen for +%llu us more\n", write_time - o->obuf_ms*1000);
197
+		}
198
+
199
+		obuf_reset(curbuf); // Buffer us all used up
200
+		buf_in_use = buf_in_use ? 0 : 1; // Switch buffer
201
+		if (written < 0) {
202
+			LOG("OUTPUT: Error writing into output socket.\n");
203
+			shutdown_fd(&o->out_sock);
204
+			connect_output(o);
205
+		}
206
+	}
207
+OUT:
208
+	LOG("OUTPUT: WRITE thread stopped.\n");
209
+	o->dienow++;
210
+	return 0;
211
+}

+ 85
- 0
pidref.c View File

@@ -0,0 +1,85 @@
1
+#include <stdlib.h>
2
+
3
+#include "libfuncs/log.h"
4
+#include "libtsfuncs/tsfuncs.h"
5
+#include "pidref.h"
6
+
7
+PIDREF *pidref_init(int num, uint16_t base_pid) {
8
+	PIDREF *ref = calloc(1, sizeof(PIDREF));
9
+	ref->num = num;
10
+	ref->base_pid = base_pid;
11
+	ref->entries = calloc(ref->num, sizeof(PIDREF_ENTRY));
12
+	return ref;
13
+}
14
+
15
+void pidref_free(PIDREF **pref) {
16
+	PIDREF *ref = *pref;
17
+	if (!ref)
18
+		return;
19
+	FREE(ref->entries);
20
+	FREE(*pref);
21
+}
22
+
23
+int pidref_add(PIDREF *ref, uint16_t org_pid, uint16_t new_pid) {
24
+	int i;
25
+	if (!org_pid)
26
+		return 0;
27
+	for (i=0;i<ref->num;i++) {
28
+		PIDREF_ENTRY *entry = &ref->entries[i];
29
+		if (!entry->org_pid) {
30
+			entry->org_pid = org_pid;
31
+			entry->new_pid = new_pid;
32
+			return 1;
33
+		}
34
+	}
35
+	return 0;
36
+}
37
+
38
+int pidref_del(PIDREF *ref, uint16_t org_pid) {
39
+	int i;
40
+	if (!org_pid)
41
+		return 0;
42
+	for (i=0;i<ref->num;i++) {
43
+		PIDREF_ENTRY *entry = &ref->entries[i];
44
+		if (entry->org_pid == org_pid) {
45
+			entry->org_pid = 0;
46
+			entry->new_pid = 0;
47
+			return 1;
48
+		}
49
+	}
50
+	return 0;
51
+}
52
+
53
+uint16_t pidref_get_new_pid(PIDREF *ref, uint16_t org_pid) {
54
+	int i;
55
+	if (!org_pid)
56
+		return 0;
57
+	for (i=0;i<ref->num;i++) {
58
+		PIDREF_ENTRY *entry = &ref->entries[i];
59
+		if (entry->org_pid == org_pid) {
60
+			return entry->new_pid;
61
+		}
62
+	}
63
+	return 0;
64
+}
65
+
66
+int pidref_change_packet_pid(uint8_t *ts_packet, uint16_t packet_pid, PIDREF *ref) {
67
+	uint16_t new_pid = pidref_get_new_pid(ref, packet_pid);
68
+	if (new_pid) {
69
+		ts_packet_set_pid(ts_packet, new_pid);
70
+		return new_pid;
71
+	}
72
+	return 0;
73
+}
74
+
75
+void pidref_dump(PIDREF *ref) {
76
+	int i;
77
+	LOGf("pidref->base_pid = 0x%04x\n", ref->base_pid);
78
+	LOGf("pidref->num      = %d\n"    , ref->num);
79
+	LOG ("pidref->entries     org_pid  new_pid\n");
80
+	for (i=0;i<ref->num;i++) {
81
+		PIDREF_ENTRY *entry = &ref->entries[i];
82
+		if (entry->org_pid)
83
+			LOGf("pidref->entry[%02d] = 0x%04x   0x%04x\n", i, entry->org_pid, entry->new_pid);
84
+	}
85
+}

+ 29
- 0
pidref.h View File

@@ -0,0 +1,29 @@
1
+#ifndef PIDREF_H
2
+#define PIDREF_H
3
+
4
+#include <netdb.h>
5
+
6
+typedef struct {
7
+	uint16_t org_pid;
8
+	uint16_t new_pid;
9
+} PIDREF_ENTRY;
10
+
11
+typedef struct {
12
+	uint16_t base_pid;		// From this pid on there will be rewrites
13
+	int num;
14
+	PIDREF_ENTRY *entries;
15
+} PIDREF;
16
+
17
+PIDREF *	pidref_init	(int num, uint16_t base_pid);
18
+void		pidref_free	(PIDREF **ref);
19
+
20
+int			pidref_add	(PIDREF *ref, uint16_t org_pid, uint16_t new_pid);
21
+int			pidref_del	(PIDREF *ref, uint16_t org_pid);
22
+
23
+uint16_t	pidref_get_new_pid			(PIDREF *ref, uint16_t org_pid);
24
+
25
+int			pidref_change_packet_pid	(uint8_t *ts_packet, uint16_t packet_pid, PIDREF *ref);
26
+
27
+void		pidref_dump	(PIDREF *ref);
28
+
29
+#endif

+ 149
- 0
rc.mptsd View File

@@ -0,0 +1,149 @@
1
+#!/bin/sh
2
+# Server control script
3
+# Copyright (C) 2007-2010 Unix Solutions Ltd.
4
+
5
+PATH="/home/iptv:/bin:/sbin:/usr/bin:/usr/local/bin"
6
+export PATH
7
+cd $(dirname $0)
8
+
9
+CONFIG="$(basename $0).conf"
10
+
11
+if [ ! -r $CONFIG ]
12
+then
13
+	echo "[ERROR] $CONFIG is not found."
14
+	exit 1
15
+fi
16
+
17
+. ./$CONFIG
18
+
19
+istart() {
20
+	echo "[START] Starting $PRGNAME."
21
+	if [ ! -f "$SERVER" -o ! -x "$SERVER" ]
22
+	then
23
+		echo "[ERROR] $SERVER does not exist or it's not executable."
24
+		exit 1
25
+	fi
26
+	if [ -r "$PIDFILE" ]
27
+	then
28
+		fpid=$(cat "$PIDFILE" 2>/dev/null)
29
+		rpid=$(pidof $PRGNAME 2>/dev/null)
30
+		if [ -n "$rpid" -a "0$fpid" -eq "0$rpid" ]
31
+		then
32
+			echo "[ERROR] $PRGNAME is already running: (pid $rpid)"
33
+			exit 1
34
+		else
35
+			echo "[ERROR] $PIDFILE is stale, $PRGNAME is not running. Deleting it pid file."
36
+			rm $PIDFILE
37
+		fi
38
+	fi
39
+	echo "[CMD  ] $SERVER $PARAMS"
40
+	cd $(dirname $SERVER)
41
+	$SERVER $PARAMS
42
+	if [ $? -eq 0 ]
43
+	then
44
+		echo "[OK   ] $PRGNAME started."
45
+	else
46
+		echo "[ERROR] $PRGNAME not started."
47
+	fi
48
+}
49
+
50
+istop() {
51
+	echo "[STOP ] Stopping $PRGNAME."
52
+	killall $PRGNAME
53
+	if [ $? -eq 0 ]
54
+	then
55
+		echo -n "[WAIT ] Waiting"
56
+		echo -n "." && sleep .3
57
+		echo -n "." && sleep .3
58
+		echo -n "." && sleep .3
59
+		echo -n "." && sleep .2
60
+		echo "."
61
+		if [ -r "$PIDFILE" ]
62
+		then
63
+			RPID=$(pidof $PRGNAME 2>/dev/null)
64
+			if [ "0$RPID" -ne "0" ]
65
+			then
66
+				echo "[ERROR] $PRGNAME is still running: (pid $(cat $PIDFILE)). Kill -9ing it."
67
+				killall -9 $PRGNAME
68
+			fi
69
+		fi
70
+		echo "[OK   ] $PRGNAME is stopped."
71
+	fi
72
+}
73
+
74
+icheck() {
75
+	if [ -r "$PIDFILE" ]
76
+	then
77
+		fpid=$(cat "$PIDFILE" 2>/dev/null)
78
+		rpid=$(pidof $PRGNAME 2>/dev/null)
79
+		if [ -n "$rpid" -a "0$fpid" -eq "0$rpid" ]
80
+		then
81
+			echo "[CHECK] $PRGNAME is already running: (pid $rpid)"
82
+		else
83
+			istart
84
+		fi
85
+	else
86
+		echo "[CHECK] Stop and start"
87
+		istop
88
+		istart
89
+	fi
90
+}
91
+
92
+istatus() {
93
+	rpid=$(pidof $PRGNAME 2>/dev/null)
94
+	echo "[STATUS] $PRGNAME pidfile pid: $(cat $PIDFILE 2>/dev/null)"
95
+	echo "[STATUS] $PRGNAME pidof   pid: $rpid"
96
+	if [ -z "$rpid" ]
97
+	then
98
+		echo "[STATUS] $PRGNAME is not running."
99
+	else
100
+		if [ -n "$rpid" -a "0$(cat $PIDFILE 2>/dev/null)" -eq "0$rpid" ]
101
+		then
102
+			echo "[STATUS] $PRGNAME is running"
103
+		else
104
+			echo "[STATUS] $PRGNAME is running but no pid file exist: $PIDFILE"
105
+		fi
106
+		ps ax | grep "$PRGNAME -i" | grep -v grep
107
+	fi
108
+}
109
+
110
+ireconnect() {
111
+	echo "[RECONN] Sending SIGUSR1 to $PRGNAME causing reconnect"
112
+	kill -USR1 $(pidof $PRGNAME)
113
+}
114
+
115
+ireload() {
116
+	echo "[RECONN] Sending SIGHUP to $PRGNAME causing configuration reload"
117
+	kill -HUP $(pidof $PRGNAME)
118
+}
119
+
120
+case "$1" in
121
+'start')
122
+	if [ "$2" != "" ]
123
+	then
124
+		sleep "$2"
125
+	fi
126
+	istart
127
+	;;
128
+'stop')
129
+	istop
130
+	;;
131
+'check')
132
+	icheck
133
+	;;
134
+'status')
135
+	istatus
136
+	;;
137
+'restart')
138
+	istop
139
+	istart
140
+	;;
141
+'reload')
142
+	ireload
143
+	;;
144
+'reconnect')
145
+	ireconnect
146
+	;;
147
+*)
148
+	echo "Usage: `basename $0` start|stop|check|restart|reload|reconnect|status"
149
+esac

+ 25
- 0
rc.mptsd.conf View File

@@ -0,0 +1,25 @@
1
+#!/bin/sh
2
+
3
+ulimit -c unlimited -s 128 2>/dev/null
4
+
5
+PROVIDER="ux"
6
+IDENT="mptsd"
7
+LOGHOST="gf.unixsol.bg"
8
+OUTPUT_HOST="239.78.78.78"
9
+OUTPUT_PORT="5000"
10
+BITRATE="-B 37.3"
11
+CONFS="-g mptsd.conf -n mptsd_nit.conf -c mptsd_channels.conf -e mptsd_epg.conf"
12
+
13
+OUTPUT_HOST="238.0.0.10"
14
+OUTPUT_PORT="5010"
15
+CONFS=""
16
+
17
+BASEDIR="."
18
+PRGNAME="mptsd"
19
+
20
+SERVER="$BASEDIR/$PRGNAME"
21
+PIDFILE="$BASEDIR/$PRGNAME.pid"
22
+PARAMS="-i $PROVIDER/$IDENT $BITRATE -O $OUTPUT_HOST -P $OUTPUT_PORT $CONFS"
23
+#PARAMS="-i $PROVIDER/$IDENT $BITRATE -O $OUTPUT_HOST -P $OUTPUT_PORT $CONFS -l $LOGHOST"
24
+#PARAMS="-i $PROVIDER/$IDENT $BITRATE -O $OUTPUT_HOST -P $OUTPUT_PORT $CONFS -d $PIDFILE"
25
+#PARAMS="-i $PROVIDER/$IDENT $BITRATE -O $OUTPUT_HOST -P $OUTPUT_PORT $CONFS -l $LOGHOST -d $PIDFILE"

+ 43
- 0
sleep.c View File

@@ -0,0 +1,43 @@
1
+#include <unistd.h>
2
+#include <string.h>
3
+#include <signal.h>
4
+#include <sys/time.h>
5
+#include <errno.h>
6
+#include <math.h>
7
+
8
+#include "libfuncs/log.h"
9
+
10
+#include "config.h"
11
+
12
+void * calibrate_sleep(void *_config) {
13
+	struct timeval tv1, tv2;
14
+	unsigned long diff = 0, loops = 0;
15
+	CONFIG *conf = _config;
16
+
17
+	if (!conf->quiet) {
18
+		LOGf("\tCalibrating sleep timeout...\n");
19
+		LOGf("\tRequest timeout   : %ld us\n", conf->output_tmout);
20
+	}
21
+
22
+	do {
23
+		gettimeofday(&tv1, NULL);
24
+		usleep(1);
25
+		gettimeofday(&tv2, NULL);
26
+		diff += timeval_diff_usec(&tv1, &tv2) - 1;
27
+	} while (loops++ != 3000);
28
+
29
+	conf->usleep_overhead = diff / loops;
30
+	conf->output_tmout -= conf->usleep_overhead;
31
+
32
+	if (!conf->quiet) {
33
+		LOGf("\tusleep(1) overhead: %ld us\n", conf->usleep_overhead);
34
+		LOGf("\tOutput pkt tmout  : %ld us\n", conf->output_tmout);
35
+	}
36
+
37
+	if (conf->output_tmout < 0) {
38
+		LOGf("usleep overhead is to much!! Disabling output rate control.\n");
39
+		conf->output_tmout = 0;
40
+	}
41
+
42
+	pthread_exit(0);
43
+}

+ 6
- 0
sleep.h View File

@@ -0,0 +1,6 @@
1
+#ifndef SLEEP_H
2
+#define SLEEP_H
3
+
4
+void * calibrate_sleep(void *_config);
5
+
6
+#endif

+ 27
- 0
web_pages.c View File

@@ -0,0 +1,27 @@
1
+#include <stdlib.h>
2
+#include <string.h>
3
+#include <sys/types.h>
4
+#include <sys/stat.h>
5
+#include <fcntl.h>
6
+#include <unistd.h>
7
+
8
+#include "libfuncs/io.h"
9
+#include "libfuncs/log.h"
10
+#include "libfuncs/list.h"
11
+#include "libfuncs/http_response.h"
12
+
13
+#include "config.h"
14
+
15
+extern CONFIG *config;
16
+
17
+void cmd_index(int clientsock) {
18
+	send_200_ok(clientsock);
19
+	send_header_textplain(clientsock);
20
+	fdputs(clientsock, "\nHi from mptsd.\n");
21
+}
22
+
23
+void cmd_reconnect(int clientsock) {
24
+	send_200_ok(clientsock);
25
+	send_header_textplain(clientsock);
26
+	fdputsf(clientsock, "\nReconnecting %d inputs.\n", config->inputs->items);
27
+}

+ 7
- 0
web_pages.h View File

@@ -0,0 +1,7 @@
1
+#ifndef WEB_PAGES_H
2
+#define WEB_PAGES_H
3
+
4
+void cmd_index(int clientsock);
5
+void cmd_reconnect(int clientsock);
6
+
7
+#endif

+ 122
- 0
web_server.c View File

@@ -0,0 +1,122 @@
1
+#include <stdlib.h>
2
+#include <regex.h>
3
+#include <errno.h>
4
+#include <string.h>
5
+#include <signal.h>
6
+#include <arpa/inet.h>
7
+#include <netinet/in.h>
8
+
9
+#include "libfuncs/libfuncs.h"
10
+
11
+#include "web_pages.h"
12
+#include "web_server.h"
13
+
14
+typedef struct req_info {
15
+	int clientsock;
16
+	struct sockaddr_in client;
17
+} request_info;
18
+
19
+extern int keep_going;
20
+
21
+#define NEXT_CLIENT { FREE(path); FREE(buf); pthread_exit(0); }
22
+#define SHUTDOWN_CLIENT { FREE(path); FREE(buf); shutdown_fd(&clientsock); pthread_exit(0); }
23
+#define BUF_SIZE 1024
24
+
25
+void *process_web_request(void *);
26
+
27
+void *web_server_thread(void *data) {
28
+	CONFIG *conf = data;
29
+	while (keep_going) {
30
+		struct sockaddr_in client;
31
+		unsigned int clientlen = sizeof(client);
32
+		int clientsock;
33
+		clientsock = accept(conf->server_socket, (struct sockaddr *) &client, &clientlen);
34
+		if (clientsock < 0) {
35
+			if (conf->server_socket > -1)	// The server_socket is closed on exit, so do not report errors
36
+				LOGf("ERROR : Failed to accept client fd: %i err: %s\n", clientsock, strerror(errno));
37
+			if (errno==EMFILE || errno==ENFILE) /* No more FDs */
38
+				break;
39
+		} else {
40
+			request_info *req;
41
+			pthread_t req_thread;
42
+			req = malloc(sizeof(request_info));
43
+			if (!req) {
44
+				log_perror("Can't allocate request_info", errno);
45
+				continue;
46
+			}
47
+			req->clientsock = clientsock;
48
+			req->client = client;
49
+			if (pthread_create(&req_thread, NULL, (void *)&process_web_request, (void *)req)) {
50
+				log_perror("Error creating request processing thread.", errno);
51
+				exit(1);
52
+			}
53
+			pthread_detach(req_thread);
54
+		}
55
+	}
56
+
57
+	pthread_exit(0);
58
+}
59
+
60
+void web_server_start(CONFIG *conf) {
61
+	if (conf->server_socket > -1)
62
+		pthread_create(&conf->server_thread, NULL, &web_server_thread, conf);
63
+}
64
+
65
+void web_server_stop(CONFIG *conf) {
66
+	if (conf->server_socket > -1) {
67
+		shutdown_fd(&conf->server_socket);
68
+		pthread_join(conf->server_thread, NULL);
69
+	}
70
+}
71
+
72
+void *process_web_request(void *in_req) {
73
+	request_info *req = (request_info *)in_req;
74
+	int clientsock = req->clientsock;
75
+	regmatch_t res[3];
76
+	char *path=NULL, *buf=NULL;
77
+	FREE(req);
78
+
79
+	signal(SIGPIPE, SIG_IGN);
80
+
81
+	if (!keep_going)
82
+		pthread_exit(0);
83
+
84
+	buf = malloc(BUF_SIZE);
85
+	if (!buf) {
86
+		log_perror("Can't allocate buffer", errno);
87
+		SHUTDOWN_CLIENT;
88
+	}
89
+
90
+	if (fdgetline(clientsock,buf,BUF_SIZE)<=0) {
91
+		SHUTDOWN_CLIENT;
92
+	}
93
+
94
+	regex_t request_get;
95
+	regcomp(&request_get, "^GET /([^ ]*) HTTP/1.*$", REG_EXTENDED);
96
+	if (regexec(&request_get,buf,2,res,0)==REG_NOMATCH) {
97
+		send_501_not_implemented(clientsock);
98
+		SHUTDOWN_CLIENT;
99
+	}
100
+
101
+	buf[res[1].rm_eo]=0;
102
+	chomp(buf+res[1].rm_so);
103
+	if (buf[res[1].rm_eo-1]=='/') buf[res[1].rm_eo-1]=0;
104
+	path = strdup(buf+res[1].rm_so);
105
+	regfree(&request_get);
106
+
107
+	while (fdgetline(clientsock,buf,BUF_SIZE) > 0) {
108
+		if (buf[0] == '\n' || buf[0] == '\r') // End of headers
109
+			break;
110
+	}
111
+
112
+	if (strlen(path) == 0) {
113
+		cmd_index(clientsock);
114
+	} else if (strstr(path,"reconnect")==path) {
115
+		cmd_reconnect(clientsock);
116
+	} else {
117
+		send_404_not_found(clientsock);
118
+	}
119
+
120
+	SHUTDOWN_CLIENT;
121
+}
122
+

+ 9
- 0
web_server.h View File

@@ -0,0 +1,9 @@
1
+#ifndef WEB_SERVER_H
2
+# define WEB_SERVER_H
3
+
4
+#include "config.h"
5
+
6
+void web_server_start(CONFIG *conf);
7
+void web_server_stop(CONFIG *conf);
8
+
9
+#endif

Loading…
Cancel
Save