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

+ 113
- 0
FILTERING View File

@@ -0,0 +1,113 @@
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,6 +41,7 @@ tsdecrypt_SRC = data.c \
41 41
  csa.c \
42 42
  udp.c \
43 43
  util.c \
44
+ filter.c \
44 45
  camd.c \
45 46
  camd-cs378x.c \
46 47
  camd-newcamd.c \

+ 3
- 0
README View File

@@ -164,6 +164,9 @@ EMM options:
164 164
  -f --emm-report-time <sec> | Report each <sec> seconds how much EMMs have been
165 165
                             .   received/processed. Set <sec> to 0 to disable
166 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 171
 ECM options:
169 172
  -X --ecm-pid <pid>         | Force ECM pid. Default: none

+ 0
- 1
TODO View File

@@ -1,3 +1,2 @@
1 1
 - Sort nanos of viaccess and cryptoworks emms.
2 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,6 +185,26 @@ struct packet_buf {
185 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 208
 #define MAX_PIDS 8192
189 209
 
190 210
 struct ts {
@@ -294,6 +314,9 @@ struct ts {
294 314
 
295 315
 	unsigned int		input_buffer_time;
296 316
 	LIST				*input_buffer;
317
+
318
+	int					emm_filters_num;
319
+	struct filter		emm_filters[MAX_FILTERS];
297 320
 };
298 321
 
299 322
 void data_init(struct ts *ts);

+ 142
- 0
filter.c View File

@@ -0,0 +1,142 @@
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

@@ -0,0 +1,28 @@
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,9 +12,12 @@
12 12
  * GNU General Public License (COPYING file) for more details.
13 13
  *
14 14
  */
15
+#include <string.h>
16
+
15 17
 #include "data.h"
16 18
 #include "tables.h"
17 19
 #include "camd.h"
20
+#include "filter.h"
18 21
 
19 22
 #include "libtsfuncs/tsfuncs.h"
20 23
 #include "libfuncs/libfuncs.h"
@@ -270,17 +273,28 @@ static void __process_emm(struct ts *ts, uint16_t pid, uint8_t *ts_packet) {
270 273
 
271 274
 	struct ts_header *th = &ts->emm->ts_header;
272 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 281
 	if (ts->debug_level >= 2) {
274 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 284
 			ts->service_id,
277 285
 			ts->emm_caid,
278 286
 			th->pid,
279 287
 			sec->table_id,
280 288
 			sec->section_data_len,
289
+			emm_ok == 1 ? "Data:" : "SKIP:",
281 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 298
 	ts_privsec_copy(ts->emm, ts->last_emm);
285 299
 	ts_privsec_clear(ts->emm);
286 300
 }

+ 11
- 0
tsdecrypt.1 View File

@@ -223,6 +223,17 @@ stream has very high rate and is interfering with ECM processing. Using
223 223
 Set interval for EMM reports. The default is \fB60\fR seconds. Set to \fB0\fR
224 224
 to disable EMM reports.
225 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 237
 .SH ECM OPTIONS
227 238
 .PP
228 239
 .TP

+ 34
- 1
tsdecrypt.c View File

@@ -16,6 +16,7 @@
16 16
 #include <unistd.h>
17 17
 #include <getopt.h>
18 18
 #include <string.h>
19
+#include <ctype.h>
19 20
 #include <sys/types.h>
20 21
 #include <sys/stat.h>
21 22
 #include <signal.h>
@@ -35,6 +36,7 @@
35 36
 #include "process.h"
36 37
 #include "udp.h"
37 38
 #include "notify.h"
39
+#include "filter.h"
38 40
 
39 41
 #define FIRST_REPORT_SEC 3
40 42
 
@@ -78,7 +80,7 @@ static void LOG_func(const char *msg) {
78 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 85
 // Unused short options: aqv01235789
84 86
 static const struct option long_options[] = {
@@ -126,6 +128,7 @@ static const struct option long_options[] = {
126 128
 	{ "emm-pid",			required_argument, NULL, 'Z' },
127 129
 	{ "emm-only",			no_argument,       NULL, 'E' },
128 130
 	{ "emm-report-time",	required_argument, NULL, 'f' },
131
+	{ "emm-filter",			required_argument, NULL, 'a' },
129 132
 
130 133
 	{ "ecm-pid",			required_argument, NULL, 'X' },
131 134
 	{ "ecm-report-time",	required_argument, NULL, 'H' },
@@ -219,6 +222,9 @@ static void show_help(struct ts *ts) {
219 222
 	printf(" -f --emm-report-time <sec> | Report each <sec> seconds how much EMMs have been\n");
220 223
 	printf("                            .   received/processed. Set <sec> to 0 to disable\n");
221 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 228
 	printf("\n");
223 229
 	printf("ECM options:\n");
224 230
 	printf(" -X --ecm-pid <pid>         | Force ECM pid. Default: none\n");
@@ -513,6 +519,19 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
513 519
 					ts->emm_report_interval = 86400;
514 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 535
 			case 'X': // --ecm-pid
517 536
 				ts->forced_ecm_pid = strtoul(optarg, NULL, 0) & 0x1fff;
518 537
 				break;
@@ -598,6 +617,7 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
598 617
 		ts->cw_warn_sec = 0;
599 618
 		ts->camd.no_reconnect = 1;
600 619
 		ts->camd.check_emm_errors = 1;
620
+		ts->emm_filters_num = 0;
601 621
 	}
602 622
 
603 623
 	// Constant codeword is special. Disable conflicting options
@@ -755,6 +775,11 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
755 775
 		ts_LOGf("TS discont : %s\n", ts->ts_discont ? "report" : "ignore");
756 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 783
 	if (!packet_from_file) {
759 784
 		if (ts->emm_send && ts->emm_report_interval)
760 785
 			ts_LOGf("EMM report : %d sec\n", ts->emm_report_interval);
@@ -773,6 +798,14 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
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 809
 	if (!packet_from_file) {
777 810
 		if (!ts->emm_only && ts->ecm_report_interval)
778 811
 			ts_LOGf("ECM report : %d sec\n", ts->emm_report_interval);

Loading…
Cancel
Save