Browse Source

Add EMM filtering support.

Georgi Chorbadzhiyski 11 years ago
parent
commit
831eba2ca2
11 changed files with 372 additions and 4 deletions
  1. 1
    0
      ChangeLog
  2. 113
    0
      FILTERING
  3. 1
    0
      Makefile
  4. 3
    0
      README
  5. 0
    1
      TODO
  6. 23
    0
      data.h
  7. 142
    0
      filter.c
  8. 28
    0
      filter.h
  9. 16
    2
      tables.c
  10. 11
    0
      tsdecrypt.1
  11. 34
    1
      tsdecrypt.c

+ 1
- 0
ChangeLog View File

13
  * Add support for multiple CAMD addresses returned when resolving CAMD
13
  * Add support for multiple CAMD addresses returned when resolving CAMD
14
    server hostname. tsdecrypt would try each of the addresses and use
14
    server hostname. tsdecrypt would try each of the addresses and use
15
    the one that works.
15
    the one that works.
16
+ * Add --emm-filter (-a) option. This option implements EMM filtering.
16
 
17
 
17
 2012-04-19 : Version 8.1
18
 2012-04-19 : Version 8.1
18
  * Add support for Bulcrypt CAS.
19
  * Add support for Bulcrypt CAS.

+ 113
- 0
FILTERING View File

1
+tsdecrypt EMM filtering support
2
+===============================
3
+tsdecrypt have no specific knowledge about EMMs of each of the
4
+supported CA systems. This allows tsdecrypt to work even when
5
+the CA system is unknown. It just sends the EMM/ECM streams to the
6
+CAMD server for processing and filtering. The lack of specific
7
+knowledge about each of the supported CA systems is a feature.
8
+
9
+However there are cases where limiting the number of EMMs that
10
+reach CAMD is a must have feature. Since there is no code in
11
+tsdecrypt to detect whether the EMM type is GLOBAL, SHARED,
12
+UNIQUE, there is no detection of provider IDs, channel IDs or
13
+card numbers a simpler approach to filtering was implemented.
14
+
15
+The basic idea was implemented by Julian Gardner in his emm-buffer-mask
16
+patches. These patches were never merged in tsdecrypt but they
17
+inspired the current filtering implementation.
18
+
19
+tsdecrypt's EMM filters describe whether EMM should be processed
20
+or not based on an offset and data bytes that are compared with the
21
+incoming EMM.
22
+
23
+The option responsible for defining EMM filters is --emm-filter (-a)
24
+followed by the filter definition. Up to 16 filters can be defined.
25
+
26
+Filter definitions
27
+==================
28
+Filter definition contain command and command settings. The command
29
+and the settings are separated by / (forward slash) symbol, the
30
+settings are separated by ^ symbol.
31
+
32
+  Command  Settings
33
+  -------  --------
34
+  command[/setting1=abc,setting2=xyz...]
35
+
36
+Since filter definitions are passed as command line parameters, you must
37
+ensure that they are enclosed in quotes if they contain spaces.
38
+
39
+Filter commands
40
+===============
41
+Currently defined commands are:
42
+
43
+   accept_all    - Set the default to allow all EMMs to reach CAMD except
44
+                   EMMs that match "reject" command.
45
+
46
+                   When no "accept_all" or "reject_all" commands were used it
47
+                   is assumed that "accept_all" was the first command.
48
+
49
+   reject_all    - Set the default to skip all EMMs except those that
50
+                   are accepted by "accept" command.
51
+
52
+   * Both "accept_all" and "reject_all" can be used without command settings.
53
+
54
+   accept        - This command instructs tsdecrypt to allow EMM that matches
55
+                   the definition to be processed.
56
+
57
+   reject        - This command instructs tsdecrypt to skip EMM that matches
58
+                   the definition to be processed.
59
+
60
+   * Both commands must have at least two settings (offset and data).
61
+
62
+Filter settings
63
+===============
64
+Currently defined command settings are:
65
+
66
+ * name=X        - Sets the filter name (used when displaying filters).
67
+                   The name can not be longer than 32 symbols.
68
+
69
+ * offset=X      - Sets the offset at which data bytes would be checked
70
+                   against EMM. The default offset is 0.
71
+
72
+ * data=XX YY ZZ - Data bytes are series of hex numbers separated by
73
+                   space " " or dot ".". When data bytes are processed 0x
74
+                   prefix is removed from them. The maximum data bytes is 16.
75
+
76
+Filter processing
77
+=================
78
+Filters are processed one by one until "accept" or "reject" filter matches.
79
+If no "accept" or "reject" filters match, then the default match determined
80
+by "accept_all" or "reject_all" is returned.
81
+
82
+Example filters
83
+===============
84
+
85
+  Bulcrypt white list example:
86
+    Accept all EMMs except those starting with 8a 70 b4, 8b 70 b4, 8c or 8d.
87
+
88
+       tsdecrypt ... \
89
+         --emm-filter accept_all \
90
+         --emm-filter "reject/name=Bulcrypt_unknown_EMMs,offset=0,data=8a 70 b4" \
91
+         --emm-filter "reject/name=Bulcrypt_unknown_EMMs,offset=0,data=8b 70 b4" \
92
+         --emm-filter "reject/name=Some EMM,data=0x8c" \
93
+         --emm-filter reject/data=0x8d \
94
+
95
+  Bulcrypt black list example:
96
+    Reject all EMMs that don't start with 82, 84 or 85.
97
+
98
+      tsdecrypt ... \
99
+        --emm-filter reject_all \
100
+        --emm-filter accept/name=Bulcrypt_EMMs,offset=0,data=82 \
101
+        --emm-filter accept/name=Bulcrypt_EMMs,data=84 \
102
+        --emm-filter accept/name=Bulcrypt_EMMs,data=85 \
103
+
104
+  Example combination that builds white/black list.
105
+    Accept EMMs that start with 8a and 8b also accept EMMs which have
106
+    70 b4 at offset 1, and reject everything else.
107
+
108
+      tsdecrypt ... \
109
+        --emm-filter accept_all \
110
+        --emm-filter reject/name=Bulcrypt_unknown_EMMs,data=8a \
111
+        --emm-filter reject/name=Bulcrypt_unknown_EMMs,data=8b \
112
+        --emm-filter reject_all \
113
+        --emm-filter "accept/name=Bulcrypt_EMMs_with_correct_size,offset=1,data=70 b4" \

+ 1
- 0
Makefile View File

41
  csa.c \
41
  csa.c \
42
  udp.c \
42
  udp.c \
43
  util.c \
43
  util.c \
44
+ filter.c \
44
  camd.c \
45
  camd.c \
45
  camd-cs378x.c \
46
  camd-cs378x.c \
46
  camd-newcamd.c \
47
  camd-newcamd.c \

+ 3
- 0
README View File

164
  -f --emm-report-time <sec> | Report each <sec> seconds how much EMMs have been
164
  -f --emm-report-time <sec> | Report each <sec> seconds how much EMMs have been
165
                             .   received/processed. Set <sec> to 0 to disable
165
                             .   received/processed. Set <sec> to 0 to disable
166
                             .   the reports. Default: 60 sec
166
                             .   the reports. Default: 60 sec
167
+ -a --emm-filter <filter>   | Add EMM filter defined by <filter>.
168
+                            . This option can be used multiple times (max:16).
169
+                            . See FILTERING file for more info.
167
 
170
 
168
 ECM options:
171
 ECM options:
169
  -X --ecm-pid <pid>         | Force ECM pid. Default: none
172
  -X --ecm-pid <pid>         | Force ECM pid. Default: none

+ 0
- 1
TODO View File

1
 - Sort nanos of viaccess and cryptoworks emms.
1
 - Sort nanos of viaccess and cryptoworks emms.
2
 - Add ipv6 support (multicast send/recv).
2
 - Add ipv6 support (multicast send/recv).
3
-- Investigate EMM filters implemented in https://github.com/joolzg/tsdecrypt

+ 23
- 0
data.h View File

185
 	uint8_t		data[188];
185
 	uint8_t		data[188];
186
 };
186
 };
187
 
187
 
188
+#define MAX_FILTERS     16
189
+#define MAX_FILTER_NAME 32
190
+#define MAX_FILTER_LEN  16
191
+
192
+enum filter_action {
193
+	FILTER_NO_MATCH   = 0,
194
+	FILTER_ACCEPT_ALL = 1,
195
+	FILTER_REJECT_ALL = 2,
196
+	FILTER_ACCEPT     = 3,
197
+	FILTER_REJECT     = 4,
198
+};
199
+
200
+struct filter {
201
+	enum filter_action action;			// If set to 1, the filter is negative else the filter is positive
202
+	uint8_t		offset;					// Offset into EMM
203
+	uint8_t		data_len;				// Filter length
204
+	uint8_t		data[MAX_FILTER_LEN];	// Filter bytes
205
+	char		name[MAX_FILTER_NAME];	// Filter name (default: NO_NAME)
206
+};
207
+
188
 #define MAX_PIDS 8192
208
 #define MAX_PIDS 8192
189
 
209
 
190
 struct ts {
210
 struct ts {
294
 
314
 
295
 	unsigned int		input_buffer_time;
315
 	unsigned int		input_buffer_time;
296
 	LIST				*input_buffer;
316
 	LIST				*input_buffer;
317
+
318
+	int					emm_filters_num;
319
+	struct filter		emm_filters[MAX_FILTERS];
297
 };
320
 };
298
 
321
 
299
 void data_init(struct ts *ts);
322
 void data_init(struct ts *ts);

+ 142
- 0
filter.c View File

1
+/*
2
+ * Filtering functions
3
+ * Copyright (C) 2012 Unix Solutions Ltd.
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License version 2
7
+ * as published by the Free Software Foundation.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ * GNU General Public License (COPYING file) for more details.
13
+ *
14
+ */
15
+#include <ctype.h>
16
+#include <string.h>
17
+
18
+#include "data.h"
19
+#include "filter.h"
20
+
21
+int filter_parse(char *filter_def, struct filter *filter) {
22
+	int i, j, k, ret = 0;
23
+	char *str1, *saveptr1;
24
+	char *f = strdup(filter_def);
25
+	memset(filter, 0, sizeof(struct filter));
26
+	snprintf(filter->name, sizeof(filter->name), "NONAME");
27
+	for (j = 1, str1 = f; ; j++, str1 = NULL) {
28
+		char *token = strtok_r(str1, "/", &saveptr1);
29
+		if (token == NULL)
30
+			break;
31
+		if (j == 1) { // First token, the command
32
+			if (strstr(token, "accept_all") == token || strstr(token, "acceptall") == token) {
33
+				filter->action = FILTER_ACCEPT_ALL;
34
+				ret = 1;
35
+				goto OUT; // Other tokens are not needed
36
+			} else if (strstr(token, "reject_all") == token || strstr(token, "rejectall") == token) {
37
+				filter->action = FILTER_REJECT_ALL;
38
+				ret = 1;
39
+				goto OUT; // Other tokens are not needed
40
+			} else if (strstr(token, "accept") == token) {
41
+				filter->action = FILTER_ACCEPT;
42
+				continue; // Continue looking for other tokes
43
+			} else if (strstr(token, "reject") == token) {
44
+				filter->action = FILTER_REJECT;
45
+				continue; // Continue looking for other tokes
46
+			} else {
47
+				fprintf(stderr, "ERROR: Unknown filter command: %s\n", token);
48
+				ret = 0;
49
+				goto OUT; // Other tokens are not needed
50
+			}
51
+		}
52
+		if (j == 2) { // Second token, the settings
53
+			char *str2, *saveptr2;
54
+			for (k = 1, str2 = token; ; k++, str2 = NULL) {
55
+				char *token2 = strtok_r(str2, ",", &saveptr2);
56
+				if (token2 == NULL)
57
+					break;
58
+				char *eq = strrchr(token2, '=');
59
+				if (eq) {
60
+					if (strstr(token2, "ofs") == token2 || strstr(token2, "offset") == token2) {
61
+						filter->offset = strtoul(eq + 1, NULL, 0);
62
+					} else if (strstr(token2, "name") == token2) {
63
+						snprintf(filter->name, sizeof(filter->name), "%s", eq + 1);
64
+					} else if (strstr(token2, "data") == token2) {
65
+						char *data = eq + 1;
66
+						int len = strlen(data), consumed = 0;
67
+						for (i = 0; i < len; i++) { // Parse data (01 02 03 04 ...)
68
+							char ch = toupper(data[i]);
69
+							// Skip 0x prefixes
70
+							if (i + 1 < len && ch == '0' && toupper(data[i + 1]) == 'X')
71
+								continue;
72
+							if (!isxdigit(ch))
73
+								continue;
74
+							ch -= ch > 64 ? 55 : 48; // hex2dec
75
+							if (consumed % 2 == 0) {
76
+								filter->data[filter->data_len  ] += ch << 4;
77
+							} else {
78
+								filter->data[filter->data_len++] += ch;
79
+								if (filter->data_len + 1 >= MAX_FILTER_LEN) {
80
+									fprintf(stderr, "WARN : Too much filter data (max %d bytes), ignoring last bytes: %s\n",
81
+										MAX_FILTER_LEN, data + i + 2);
82
+									break;
83
+								}
84
+							}
85
+							consumed++;
86
+						}
87
+						ret = filter->data_len;
88
+					} else {
89
+						fprintf(stderr, "WARN : Unknown filter setting: %s\n", token2);
90
+					}
91
+				}
92
+			}
93
+		}
94
+	}
95
+OUT:
96
+	FREE(f);
97
+	return ret;
98
+}
99
+
100
+void filter_dump(struct filter *filter, char *buffer, unsigned int buf_len) {
101
+	unsigned int pos = 0;
102
+	memset(buffer, 0, buf_len);
103
+	pos += snprintf(buffer + pos, buf_len - pos, "Action: %s",
104
+		filter->action == FILTER_ACCEPT_ALL ? "ACCEPT_ALL (default)" :
105
+		filter->action == FILTER_REJECT_ALL ? "REJECT_ALL (default)" :
106
+		filter->action == FILTER_ACCEPT     ? "ACCEPT" :
107
+		filter->action == FILTER_REJECT     ? "REJECT" : "???");
108
+	if (filter->action == FILTER_ACCEPT || filter->action == FILTER_REJECT)
109
+		pos += snprintf(buffer + pos, buf_len - pos, " Name: %-20s", filter->name);
110
+	if (filter->action == FILTER_ACCEPT || filter->action == FILTER_REJECT) {
111
+		char tmp[MAX_FILTER_LEN * 6];
112
+		ts_hex_dump_buf(tmp, sizeof(tmp), filter->data, filter->data_len, 0);
113
+		pos += snprintf(buffer + pos, buf_len - pos, " Offset: %2d Data: %s", filter->offset, tmp);
114
+	}
115
+}
116
+
117
+static enum filter_action filter_match(uint8_t *data, unsigned int data_len, struct filter *filter) {
118
+	if (filter->action == FILTER_ACCEPT_ALL || filter->action == FILTER_REJECT_ALL)
119
+		return filter->action;
120
+	if (filter->action == FILTER_ACCEPT || filter->action == FILTER_REJECT) {
121
+		if (filter->data_len + filter->offset > data_len)
122
+			return FILTER_NO_MATCH;
123
+		if (memcmp(data + filter->offset, filter->data, filter->data_len) == 0)
124
+			return filter->action;
125
+	}
126
+	return FILTER_NO_MATCH;
127
+}
128
+
129
+int filter_match_emm(struct ts *ts, uint8_t *data, unsigned int data_len) {
130
+	int i, ret = 1;
131
+	for (i = 0; i < ts->emm_filters_num; i++) {
132
+		enum filter_action result = filter_match(data, data_len, &ts->emm_filters[i]);
133
+		switch (result) {
134
+			case FILTER_NO_MATCH  : continue;
135
+			case FILTER_ACCEPT_ALL: ret = 1; continue;
136
+			case FILTER_ACCEPT    : ret = 1; break;
137
+			case FILTER_REJECT_ALL: ret = 0; continue;
138
+			case FILTER_REJECT    : ret = 0; break;
139
+		}
140
+	}
141
+	return ret;
142
+}

+ 28
- 0
filter.h View File

1
+/*
2
+ * Filtering functions
3
+ * Copyright (C) 2012 Unix Solutions Ltd.
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License version 2
7
+ * as published by the Free Software Foundation.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ * GNU General Public License (COPYING file) for more details.
13
+ *
14
+ */
15
+#ifndef FILTERS_H
16
+#define FILTERS_H
17
+
18
+#include <inttypes.h>
19
+
20
+#include "data.h"
21
+
22
+int filter_parse(char *filter_def, struct filter *filter);
23
+
24
+int filter_match_emm(struct ts *ts, uint8_t *data, unsigned int data_len);
25
+
26
+void filter_dump(struct filter *filter, char *buffer, unsigned int buf_len);
27
+
28
+#endif

+ 16
- 2
tables.c View File

12
  * GNU General Public License (COPYING file) for more details.
12
  * GNU General Public License (COPYING file) for more details.
13
  *
13
  *
14
  */
14
  */
15
+#include <string.h>
16
+
15
 #include "data.h"
17
 #include "data.h"
16
 #include "tables.h"
18
 #include "tables.h"
17
 #include "camd.h"
19
 #include "camd.h"
20
+#include "filter.h"
18
 
21
 
19
 #include "libtsfuncs/tsfuncs.h"
22
 #include "libtsfuncs/tsfuncs.h"
20
 #include "libfuncs/libfuncs.h"
23
 #include "libfuncs/libfuncs.h"
270
 
273
 
271
 	struct ts_header *th = &ts->emm->ts_header;
274
 	struct ts_header *th = &ts->emm->ts_header;
272
 	struct ts_section_header *sec = ts->emm->section_header;
275
 	struct ts_section_header *sec = ts->emm->section_header;
276
+
277
+	int emm_ok = 1;
278
+	if (ts->emm_filters_num)
279
+		emm_ok = filter_match_emm(ts, sec->section_data, sec->section_data_len);
280
+
273
 	if (ts->debug_level >= 2) {
281
 	if (ts->debug_level >= 2) {
274
 		ts_hex_dump_buf(dump, dump_buf_sz, sec->section_data, min(dump_sz, sec->section_data_len), 0);
282
 		ts_hex_dump_buf(dump, dump_buf_sz, sec->section_data, min(dump_sz, sec->section_data_len), 0);
275
-		ts_LOGf("EMM | SID 0x%04x CAID: 0x%04x PID 0x%04x Table: 0x%02x Length: %4d Data: %s..\n",
283
+		ts_LOGf("EMM | SID 0x%04x CAID: 0x%04x PID 0x%04x Table: 0x%02x Length: %4d %s %s..\n",
276
 			ts->service_id,
284
 			ts->service_id,
277
 			ts->emm_caid,
285
 			ts->emm_caid,
278
 			th->pid,
286
 			th->pid,
279
 			sec->table_id,
287
 			sec->table_id,
280
 			sec->section_data_len,
288
 			sec->section_data_len,
289
+			emm_ok == 1 ? "Data:" : "SKIP:",
281
 			dump);
290
 			dump);
282
 	}
291
 	}
283
-	camd_process_packet(ts, camd_msg_alloc(EMM_MSG, ts->emm_caid, ts->service_id, sec->section_data, sec->section_data_len));
292
+
293
+	if (emm_ok)
294
+		camd_process_packet(ts, camd_msg_alloc(EMM_MSG, ts->emm_caid, ts->service_id, sec->section_data, sec->section_data_len));
295
+	else
296
+		ts->emm_skipped_count++;
297
+
284
 	ts_privsec_copy(ts->emm, ts->last_emm);
298
 	ts_privsec_copy(ts->emm, ts->last_emm);
285
 	ts_privsec_clear(ts->emm);
299
 	ts_privsec_clear(ts->emm);
286
 }
300
 }

+ 11
- 0
tsdecrypt.1 View File

223
 Set interval for EMM reports. The default is \fB60\fR seconds. Set to \fB0\fR
223
 Set interval for EMM reports. The default is \fB60\fR seconds. Set to \fB0\fR
224
 to disable EMM reports.
224
 to disable EMM reports.
225
 .TP
225
 .TP
226
+\fB\-a\fR, \fB\-\-emm\-filter\fR <filter_definition>
227
+Add EMM filter described by <filter_definition>. EMM filters are useful if
228
+you want to limit the number of EMMs that should reach your CAMD server.
229
+The basic \fB<filter_defintion>\fR is \fBCommand/Settings\fR where
230
+the commands are: \fBaccept_all\fR, \fBreject_all\fR, \fBaccept\fR
231
+and \fBreject\fR.
232
+
233
+For more information about filtering and for example filters, please
234
+read \fBFILTERING\fR file that comes with tsdecrypt. This option can be
235
+used multiple times to define up to \fB16\fR different filters.
236
+.TP
226
 .SH ECM OPTIONS
237
 .SH ECM OPTIONS
227
 .PP
238
 .PP
228
 .TP
239
 .TP

+ 34
- 1
tsdecrypt.c View File

16
 #include <unistd.h>
16
 #include <unistd.h>
17
 #include <getopt.h>
17
 #include <getopt.h>
18
 #include <string.h>
18
 #include <string.h>
19
+#include <ctype.h>
19
 #include <sys/types.h>
20
 #include <sys/types.h>
20
 #include <sys/stat.h>
21
 #include <sys/stat.h>
21
 #include <signal.h>
22
 #include <signal.h>
35
 #include "process.h"
36
 #include "process.h"
36
 #include "udp.h"
37
 #include "udp.h"
37
 #include "notify.h"
38
 #include "notify.h"
39
+#include "filter.h"
38
 
40
 
39
 #define FIRST_REPORT_SEC 3
41
 #define FIRST_REPORT_SEC 3
40
 
42
 
78
 		LOG(msg);
80
 		LOG(msg);
79
 }
81
 }
80
 
82
 
81
-static const char short_options[] = "i:d:N:Sl:L:F:I:RzM:T:W:O:o:t:rk:g:upwxyc:C:Y:Q:A:s:U:P:B:46eZ:Ef:X:H:G:KJ:D:jbhVn:m:";
83
+static const char short_options[] = "i:d:N:Sl:L:F:I:RzM:T:W:O:o:t:rk:g:upwxyc:C:Y:Q:A:s:U:P:B:46eZ:Ef:a:X:H:G:KJ:D:jbhVn:m:";
82
 
84
 
83
 // Unused short options: aqv01235789
85
 // Unused short options: aqv01235789
84
 static const struct option long_options[] = {
86
 static const struct option long_options[] = {
126
 	{ "emm-pid",			required_argument, NULL, 'Z' },
128
 	{ "emm-pid",			required_argument, NULL, 'Z' },
127
 	{ "emm-only",			no_argument,       NULL, 'E' },
129
 	{ "emm-only",			no_argument,       NULL, 'E' },
128
 	{ "emm-report-time",	required_argument, NULL, 'f' },
130
 	{ "emm-report-time",	required_argument, NULL, 'f' },
131
+	{ "emm-filter",			required_argument, NULL, 'a' },
129
 
132
 
130
 	{ "ecm-pid",			required_argument, NULL, 'X' },
133
 	{ "ecm-pid",			required_argument, NULL, 'X' },
131
 	{ "ecm-report-time",	required_argument, NULL, 'H' },
134
 	{ "ecm-report-time",	required_argument, NULL, 'H' },
219
 	printf(" -f --emm-report-time <sec> | Report each <sec> seconds how much EMMs have been\n");
222
 	printf(" -f --emm-report-time <sec> | Report each <sec> seconds how much EMMs have been\n");
220
 	printf("                            .   received/processed. Set <sec> to 0 to disable\n");
223
 	printf("                            .   received/processed. Set <sec> to 0 to disable\n");
221
 	printf("                            .   the reports. Default: %d sec\n", ts->emm_report_interval);
224
 	printf("                            .   the reports. Default: %d sec\n", ts->emm_report_interval);
225
+	printf(" -a --emm-filter <filter>   | Add EMM filter defined by <filter>.\n");
226
+	printf("                            . This option can be used multiple times (max:%u).\n", MAX_FILTERS);
227
+	printf("                            . See FILTERING file for more info.\n");
222
 	printf("\n");
228
 	printf("\n");
223
 	printf("ECM options:\n");
229
 	printf("ECM options:\n");
224
 	printf(" -X --ecm-pid <pid>         | Force ECM pid. Default: none\n");
230
 	printf(" -X --ecm-pid <pid>         | Force ECM pid. Default: none\n");
513
 					ts->emm_report_interval = 86400;
519
 					ts->emm_report_interval = 86400;
514
 				break;
520
 				break;
515
 
521
 
522
+			case 'a': // --emm-filter
523
+				if (ts->emm_filters_num + 1 > MAX_FILTERS) {
524
+					fprintf(stderr, "ERROR: Maximum allowed filters are %d.\n", MAX_FILTERS);
525
+					exit(EXIT_FAILURE);
526
+				}
527
+				if (filter_parse(optarg, &ts->emm_filters[ts->emm_filters_num])) {
528
+					ts->emm_filters_num++;
529
+				} else {
530
+					fprintf(stderr, "ERROR: Can't parse EMM filter: %s\n", optarg);
531
+					exit(EXIT_FAILURE);
532
+				}
533
+				break;
534
+
516
 			case 'X': // --ecm-pid
535
 			case 'X': // --ecm-pid
517
 				ts->forced_ecm_pid = strtoul(optarg, NULL, 0) & 0x1fff;
536
 				ts->forced_ecm_pid = strtoul(optarg, NULL, 0) & 0x1fff;
518
 				break;
537
 				break;
598
 		ts->cw_warn_sec = 0;
617
 		ts->cw_warn_sec = 0;
599
 		ts->camd.no_reconnect = 1;
618
 		ts->camd.no_reconnect = 1;
600
 		ts->camd.check_emm_errors = 1;
619
 		ts->camd.check_emm_errors = 1;
620
+		ts->emm_filters_num = 0;
601
 	}
621
 	}
602
 
622
 
603
 	// Constant codeword is special. Disable conflicting options
623
 	// Constant codeword is special. Disable conflicting options
755
 		ts_LOGf("TS discont : %s\n", ts->ts_discont ? "report" : "ignore");
775
 		ts_LOGf("TS discont : %s\n", ts->ts_discont ? "report" : "ignore");
756
 		ts->threaded = !(ts->input.type == FILE_IO && ts->input.fd != 0);
776
 		ts->threaded = !(ts->input.type == FILE_IO && ts->input.fd != 0);
757
 	}
777
 	}
778
+
779
+	if (!packet_from_file && !ts->emm_only) {
780
+		ts_LOGf("Decoding   : %s\n", ts->threaded ? "threaded" : "single thread");
781
+	}
782
+
758
 	if (!packet_from_file) {
783
 	if (!packet_from_file) {
759
 		if (ts->emm_send && ts->emm_report_interval)
784
 		if (ts->emm_send && ts->emm_report_interval)
760
 			ts_LOGf("EMM report : %d sec\n", ts->emm_report_interval);
785
 			ts_LOGf("EMM report : %d sec\n", ts->emm_report_interval);
773
 		}
798
 		}
774
 	}
799
 	}
775
 
800
 
801
+	if (ts->emm_send) {
802
+		for (i = 0; i < ts->emm_filters_num; i++) {
803
+			char tmp[512];
804
+			filter_dump(&ts->emm_filters[i], tmp, sizeof(tmp));
805
+			ts_LOGf("EMM filter : [%2d] %s\n", i + 1,  tmp);
806
+		}
807
+	}
808
+
776
 	if (!packet_from_file) {
809
 	if (!packet_from_file) {
777
 		if (!ts->emm_only && ts->ecm_report_interval)
810
 		if (!ts->emm_only && ts->ecm_report_interval)
778
 			ts_LOGf("ECM report : %d sec\n", ts->emm_report_interval);
811
 			ts_LOGf("ECM report : %d sec\n", ts->emm_report_interval);

Loading…
Cancel
Save