Browse Source

VideoHub control initial commit.

Georgi Chorbadzhiyski 6 years ago
commit
09814bb426
18 changed files with 1096 additions and 0 deletions
  1. 4
    0
      .gitignore
  2. 3
    0
      .gitmodules
  3. 4
    0
      ChangeLog
  4. 20
    0
      LICENSE-MIT.txt
  5. 69
    0
      Makefile
  6. 166
    0
      README
  7. 1
    0
      RELEASE
  8. 5
    0
      TODO
  9. 301
    0
      cmd.c
  10. 18
    0
      cmd.h
  11. 66
    0
      data.h
  12. 1
    0
      libfuncs
  13. 103
    0
      net.c
  14. 18
    0
      net.h
  15. 83
    0
      util.c
  16. 30
    0
      util.h
  17. 31
    0
      version.sh
  18. 173
    0
      videohubctrl.c

+ 4
- 0
.gitignore View File

@@ -0,0 +1,4 @@
1
+videohubctrl
2
+version.h
3
+*.o
4
+*.d

+ 3
- 0
.gitmodules View File

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

+ 4
- 0
ChangeLog View File

@@ -0,0 +1,4 @@
1
+|-----------------------------------------------------------------------|
2
+2014-11-26 : Version 0.1
3
+ * Initial vesion with support for showing Videohub configuration
4
+|-----------------------------------------------------------------------|

+ 20
- 0
LICENSE-MIT.txt View File

@@ -0,0 +1,20 @@
1
+Copyright (C) 2014 Unix Solutions Ltd.
2
+
3
+Permission is hereby granted, free of charge, to any person obtaining
4
+a copy of this software and associated documentation files (the
5
+"Software"), to deal in the Software without restriction, including
6
+without limitation the rights to use, copy, modify, merge, publish,
7
+distribute, sublicense, and/or sell copies of the Software, and to
8
+permit persons to whom the Software is furnished to do so, subject
9
+to the following conditions:
10
+
11
+The above copyright notice and this permission notice shall be
12
+included in all copies or substantial portions of the Software.
13
+
14
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 69
- 0
Makefile View File

@@ -0,0 +1,69 @@
1
+CC = cc
2
+STRIP = strip
3
+CROSS := $(TARGET)
4
+MKDEP = $(CROSS)$(CC) -MP -MM -o $*.d $<
5
+RM = rm -f
6
+MV = mv -f
7
+
8
+ifndef V
9
+Q = @
10
+endif
11
+
12
+CFLAGS ?= -O2 -ggdb -pipe -ffunction-sections -fdata-sections \
13
+ -W -Wall -Wextra \
14
+ -Wshadow -Wformat-security -Wstrict-prototypes -Wno-unused-parameter \
15
+ -Wredundant-decls -Wold-style-definition
16
+
17
+LDFLAGS ?= -Wl,--gc-sections
18
+
19
+DEFS += -D_FILE_OFFSET_BITS=64
20
+
21
+FUNCS_DIR = libfuncs
22
+FUNCS_LIB = $(FUNCS_DIR)/libfuncs.a
23
+
24
+videohubctrl_SRC = \
25
+	cmd.c \
26
+	net.c \
27
+	util.c \
28
+	videohubctrl.c
29
+videohubctrl_LIBS = -lpthread
30
+videohubctrl_OBJS = $(FUNCS_LIB) $(videohubctrl_SRC:.c=.o)
31
+
32
+CLEAN_OBJS = videohubctrl $(videohubctrl_SRC:.c=.o) $(videohubctrl_SRC:.c=.d)
33
+DISTCLEAN_OBJS = version.h
34
+
35
+.PHONY: distclean clean version
36
+
37
+PROGS=videohubctrl
38
+
39
+version:
40
+	$(shell ./version.sh >/dev/null)
41
+	@$(MAKE) --no-print-directory videohubctrl
42
+
43
+videohubctrl: $(videohubctrl_OBJS)
44
+	$(Q)echo "  LINK	videohubctrl"
45
+	$(Q)$(CROSS)$(CC) $(CFLAGS) $(DEFS) $(videohubctrl_OBJS) $(videohubctrl_LIBS) $(LDFLAGS) -o videohubctrl
46
+
47
+$(FUNCS_LIB): $(FUNCS_DIR)/libfuncs.h
48
+	$(Q)echo "  MAKE	$(FUNCS_LIB)"
49
+	$(Q)$(MAKE) -s -C $(FUNCS_DIR)
50
+
51
+%.o: %.c Makefile RELEASE
52
+	@$(MKDEP)
53
+	$(Q)echo "  CC	videohubctrl	$<"
54
+	$(Q)$(CROSS)$(CC) $(CFLAGS) $(DEFS) -c $<
55
+
56
+-include $(videohubctrl_SRC:.c=.d)
57
+
58
+strip:
59
+	$(Q)echo "  STRIP	$(PROGS)"
60
+	$(Q)$(CROSS)$(STRIP) $(PROGS)
61
+
62
+clean:
63
+	$(Q)echo "  RM	$(CLEAN_OBJS)"
64
+	$(Q)$(RM) $(CLEAN_OBJS)
65
+
66
+distclean: clean
67
+	$(Q)echo "  RM	$(DISTCLEAN_OBJS)"
68
+	$(Q)$(RM) $(DISTCLEAN_OBJS)
69
+	$(Q)$(MAKE) -s -C $(FUNCS_DIR) clean

+ 166
- 0
README View File

@@ -0,0 +1,166 @@
1
+videohubctrl - Blackmagic Videohub control application
2
+======================================================
3
+
4
+Blackmagic Design Videohub is SDI router device which can be controlled
5
+over the network using very simple text based protocol which 'videohubctrl'
6
+implements.
7
+
8
+'videohubctrl' is tested with Blackmagic Design Micro Videohub 16x16 router
9
+and probably will work with other Videohub models.
10
+
11
+'videohubctrl' currently displays:
12
+
13
+  - Input port labels
14
+  - Output port labels
15
+  - Output routing
16
+  - Output locking
17
+
18
+The following features found in bigger Videohub models are currently not
19
+supported (I don't have the hardware):
20
+
21
+  - Configuration of video processing units
22
+  - Configuration of Video monitoring outputs
23
+  - Configuration of Serial ports
24
+
25
+Configuration of Videohub's network settings can be made using Blackmagic's
26
+Windows program when the device is connected via USB.
27
+
28
+
29
+License
30
+=======
31
+
32
+videohubctrl is released under MIT license.
33
+See LICENSE-MIT.txt for full license terms.
34
+
35
+
36
+Development
37
+===========
38
+
39
+The development is done using git. videohubctrl repository is hosted at
40
+
41
+   http://github.com/gfto/videohubctrl
42
+
43
+To clone the repository issue the following commands:
44
+
45
+   git clone git://github.com/gfto/videohubctrl.git
46
+   cd videohubctrl
47
+   git submodule init
48
+   git submodule update
49
+   make
50
+
51
+The code is developed and tested under modern Linux. It's also compiled from
52
+time to time under OS X but it's not tested there.
53
+
54
+
55
+Updating the code
56
+=================
57
+
58
+To update cloned videohubctrl, go to the directory where the repository
59
+is cloned and run the following commands:
60
+
61
+   git fetch origin
62
+   git merge origin/master
63
+   git submodule update
64
+   make clean all
65
+
66
+videohubctrl's master branch should always be useful so it is safe to
67
+use it instead of official release. The master branch will always
68
+be better than any released version.
69
+
70
+
71
+Command line parameters
72
+=======================
73
+
74
+videohubctrl is controlled using command line parameters. Here is a list
75
+of supported command line parameters:
76
+
77
+ Usage: videohubctrl --host <host> [..commands..]
78
+
79
+Main options:
80
+ -s --host <hostname>       | Set device hostname.
81
+ -p --port <port_number>    | Set device port (default: 9990).
82
+
83
+Misc options:
84
+ -v --verbose               | Enable verbose logging.
85
+ -q --quiet                 | Suppress warnings.
86
+ -h --help                  | Show help screen.
87
+ -V --version               | Show program version.
88
+
89
+Commands:
90
+ -i --info                  | Show device info (default command).
91
+
92
+
93
+Example usage
94
+=============
95
+
96
+TODO: Add examples once set commands are done.
97
+
98
+
99
+Example output
100
+==============
101
+
102
+Here is how videohubctrl output looks like:
103
+
104
+|-----------------------------------------------------------------------------|
105
+gf@gf:~/git/videohubctrl$ ./videohubctrl --host sdi-matrix
106
+videohubctrl Version: 0.1 Git: 0.1
107
+
108
+Protocol version: 2.4
109
+Model name: Blackmagic Micro Videohub
110
+Unique ID: 7c2e0d021714
111
+Video inputs: 16
112
+Video processing units: 0
113
+Video outputs: 16
114
+Video monitoring outputs: 0
115
+Serial ports: 0
116
+
117
+----------------------------------------------------------------------------
118
+|  # | x | Input name                     | Output name                    |
119
+----------------------------------------------------------------------------
120
+|  1 | L | Windows 1                      | Enc1 3                         |
121
+|  2 | L | Windows 2                      | Enc1 2                         |
122
+|  3 | L | Windows 3                      | Enc1 1                         |
123
+|  4 | L | Windows 4 HD                   | Enc1 1                         |
124
+|  5 |   | Input 5                        | Output 5                       |
125
+|  6 |   | Input 6                        | Output 6                       |
126
+|  7 |   | Input 7                        | Output 7                       |
127
+|  8 |   | Input 8                        | Output 8                       |
128
+|  9 | L | Input 9                        | Enc1 4                         |
129
+| 10 |   | Input 10                       | Enc1 4                         |
130
+| 11 |   | Input 11                       | Output 11                      |
131
+| 12 | L | DPlay1                         | Output 13                      |
132
+| 13 | L | DPlay2                         | Denc                           |
133
+| 14 |   | Input 14                       | Output 14                      |
134
+| 15 |   | Input 15                       | Output 15                      |
135
+| 16 |   | Loopback                       | Loopback                       |
136
+----------------------------------------------------------------------------
137
+
138
+gf@gf:~/git/videohubctrl$
139
+|-----------------------------------------------------------------------------|
140
+
141
+
142
+Reporting bugs
143
+==============
144
+
145
+If you think you have found bug in videohubctrl, please report it to the
146
+e-mail listed in Contact section (see below) of this README file.
147
+
148
+When reporting bugs, please send the whole output that videohubctrl generated
149
+(preferably by using --verbose option) and also the full command line which
150
+you used and describe what you think the videohubctrl does wrong.
151
+
152
+
153
+Releases
154
+========
155
+
156
+Official releases can be downloaded from videohubctrl home page which is
157
+
158
+   http://georgi.unixsol.org/programs/videohubctrl/
159
+
160
+
161
+Contact
162
+=======
163
+
164
+For requests, patches, bug reports, complaints and so on send e-mail to
165
+
166
+   Georgi Chorbadzhiyski <georgi@unixsol.org>

+ 1
- 0
RELEASE View File

@@ -0,0 +1 @@
1
+0.1

+ 5
- 0
TODO View File

@@ -0,0 +1,5 @@
1
+- Add --monitor option to continually display configuration and changes to it.
2
+- Add --save-config and --restore-config.
3
+- Add commands to set labels, routing and locking.
4
+- Create man page.
5
+- Investigate the USB protocol for configuring network settings.

+ 301
- 0
cmd.c View File

@@ -0,0 +1,301 @@
1
+/*
2
+ * === Command parser ===
3
+ *
4
+ * Blackmagic Design Videohub control application
5
+ * Copyright (C) 2014 Unix Solutions Ltd.
6
+ * Written by Georgi Chorbadzhiyski
7
+ *
8
+ * Released under MIT license.
9
+ * See LICENSE-MIT.txt for license terms.
10
+ *
11
+ */
12
+
13
+#include <stdio.h>
14
+#include <stdlib.h>
15
+#include <string.h>
16
+
17
+#include "data.h"
18
+#include "util.h"
19
+
20
+/*
21
+Example response from videohub (on connect)
22
+===========================================
23
+PROTOCOL PREAMBLE:
24
+Version: 2.4
25
+
26
+VIDEOHUB DEVICE:
27
+Device present: true
28
+Model name: Blackmagic Micro Videohub
29
+Unique ID: 7c2e0d021714
30
+Video inputs: 16
31
+Video processing units: 0
32
+Video outputs: 16
33
+Video monitoring outputs: 0
34
+Serial ports: 0
35
+
36
+INPUT LABELS:
37
+0 Windows 1
38
+1 Windows 2
39
+2 Windows 3
40
+3 Windows 4 HD
41
+4 Input 5
42
+5 Input 6
43
+6 Input 7
44
+7 Input 8
45
+8 Input 9
46
+9 Input 10
47
+10 Input 11
48
+11 DPlay1
49
+12 DPlay2
50
+13 Input 14
51
+14 Input 15
52
+15 Loopback
53
+
54
+OUTPUT LABELS:
55
+0 Enc1 1
56
+1 Enc1 2
57
+2 Enc1 3
58
+3 Enc1 4
59
+4 Output 5
60
+5 Output 6
61
+6 Output 7
62
+7 Output 8
63
+8 Enc2 1
64
+9 Output 10
65
+10 Output 11
66
+11 Denc
67
+12 Output 13
68
+13 Output 14
69
+14 Output 15
70
+15 Loopback
71
+
72
+VIDEO OUTPUT LOCKS:
73
+0 L
74
+1 L
75
+2 L
76
+3 L
77
+4 U
78
+5 U
79
+6 U
80
+7 U
81
+8 L
82
+9 U
83
+10 U
84
+11 L
85
+12 L
86
+13 U
87
+14 U
88
+15 U
89
+
90
+VIDEO OUTPUT ROUTING:
91
+0 2
92
+1 1
93
+2 0
94
+3 0
95
+4 4
96
+5 5
97
+6 6
98
+7 7
99
+8 3
100
+9 3
101
+10 10
102
+11 12
103
+12 11
104
+13 13
105
+14 14
106
+15 15
107
+
108
+*/
109
+
110
+enum vcmd {
111
+	CMD_PROTOCOL_PREAMBLE,
112
+	CMD_VIDEOHUB_DEVICE,
113
+	CMD_INPUT_LABELS,
114
+	CMD_OUTPUT_LABELS,
115
+	CMD_VIDEO_OUTPUT_LOCKS,
116
+	CMD_VIDEO_OUTPUT_ROUTING,
117
+	CMD_PING,
118
+};
119
+
120
+static struct videohub_commands {
121
+	enum vcmd	cmd;
122
+	const char	*txt;
123
+} videohub_commands[] = {
124
+	{ CMD_PROTOCOL_PREAMBLE,       "PROTOCOL PREAMBLE" },
125
+	{ CMD_VIDEOHUB_DEVICE,         "VIDEOHUB DEVICE" },
126
+	{ CMD_INPUT_LABELS,            "INPUT LABELS" },
127
+	{ CMD_OUTPUT_LABELS,           "OUTPUT LABELS" },
128
+	{ CMD_VIDEO_OUTPUT_LOCKS,      "VIDEO OUTPUT LOCKS" },
129
+	{ CMD_VIDEO_OUTPUT_ROUTING,    "VIDEO OUTPUT ROUTING" },
130
+	{ CMD_PING,                    "PING" },
131
+};
132
+
133
+static char *parse_text(char *line, char *cmd) {
134
+	char *parsed_text = strstr(line, cmd);
135
+	if (parsed_text == line) {
136
+		return parsed_text + strlen(cmd);
137
+	}
138
+	return NULL;
139
+}
140
+
141
+bool parse_command(struct videohub_data *data, char *cmd) {
142
+	unsigned int i;
143
+	struct videohub_commands *v = NULL;
144
+	for (i = 0; i < ARRAY_SIZE(videohub_commands); i++) {
145
+		if (!videohub_commands[i].txt)
146
+			continue;
147
+		if (strstr(cmd, videohub_commands[i].txt) == cmd) {
148
+			v = &videohub_commands[i];
149
+			break;
150
+		}
151
+	}
152
+
153
+	if (!v) {
154
+		if (!quiet) {
155
+			fprintf(stderr, "WARNING: Videohub sent unknown command!\n");
156
+			fprintf(stderr, "         Please report this command to author's email: georgi@unixsol.org\n");
157
+			fprintf(stderr, "         You may use -q or --quiet to suppress the message.\n");
158
+			fprintf(stderr, "---------8<-----------8<----------- cut here ---------8<------------8<---------\n");
159
+			fprintf(stderr, "%s\n", cmd);
160
+			fprintf(stderr, "---------8<-----------8<----------- cut here ---------8<------------8<---------\n");
161
+		}
162
+		return false;
163
+	}
164
+
165
+	v("verbose: Got '%s' command.\n", v->txt);
166
+
167
+	bool cmd_response = false;
168
+	char *p, *cmd_data = xstrdup( cmd + strlen(v->txt) + 2 ); // +2 to compensate for :\n at the end of the command
169
+	// Split line by line
170
+	char *line, *saveptr = NULL;
171
+	for(i = 0, line = strtok_r(cmd_data, "\n", &saveptr); line; line = strtok_r(NULL, "\n", &saveptr), i++) {
172
+		if (i == 0) { // Handle command response
173
+			if (streq(line, "NAK"))
174
+				return false;
175
+			if (streq(line, "ACK")) {
176
+				cmd_response = true;
177
+				continue;
178
+			}
179
+		}
180
+
181
+		if (i == 1 && cmd_response) { // The command must match
182
+			if (strstr(line, v->txt) != line)
183
+				return false;
184
+			continue;
185
+		}
186
+
187
+		// Parse command data response looking like that: "[slot_pos] [slot_data]"
188
+		bool valid_slot = false;
189
+		unsigned int slot_pos = 0;
190
+		char *slot_data = NULL;
191
+		switch (v->cmd) {
192
+		case CMD_INPUT_LABELS:
193
+		case CMD_OUTPUT_LABELS:
194
+		case CMD_VIDEO_OUTPUT_LOCKS:
195
+		case CMD_VIDEO_OUTPUT_ROUTING:
196
+			slot_data = strchr(line, ' ');
197
+			if (slot_data) {
198
+				slot_data[0] = '\0'; // Separate slot_pos from slot_data
199
+				slot_data++;
200
+				slot_pos = strtoul(line, NULL, 10);
201
+				if (slot_pos < ARRAY_SIZE(data->inputs))
202
+					valid_slot = true;
203
+			}
204
+			break;
205
+		default:
206
+			break;
207
+		}
208
+
209
+		// Parse commands
210
+		switch (v->cmd) {
211
+		case CMD_PROTOCOL_PREAMBLE:
212
+			if ((p = parse_text(line, "Version: ")))
213
+				snprintf(data->device.protocol_ver, sizeof(data->device.protocol_ver), "%s", p);
214
+			break;
215
+
216
+		case CMD_VIDEOHUB_DEVICE:
217
+			if ((p = parse_text(line, "Device present: ")))
218
+				data->device.dev_present = streq(p, "true");
219
+
220
+			if ((p = parse_text(line, "Model name: ")))
221
+				snprintf(data->device.model_name, sizeof(data->device.model_name), "%s", p);
222
+
223
+			if ((p = parse_text(line, "Unique ID: ")))
224
+				snprintf(data->device.unique_id, sizeof(data->device.unique_id) , "%s", p);
225
+
226
+			if ((p = parse_text(line, "Video inputs: ")))
227
+				data->device.num_video_inputs = strtoul(p, NULL, 10);
228
+
229
+			if ((p = parse_text(line, "Video processing units: ")))
230
+				data->device.num_video_processing_units = strtoul(p, NULL, 10);
231
+
232
+			if ((p = parse_text(line, "Video outputs: ")))
233
+				data->device.num_video_outputs = strtoul(p, NULL, 10);
234
+
235
+			if ((p = parse_text(line, "Video monitoring output: ")))
236
+				data->device.num_video_monitoring_outputs = strtoul(p, NULL, 10);
237
+
238
+			if ((p = parse_text(line, "Serial ports: ")))
239
+				data->device.num_serial_ports = strtoul(p, NULL, 10);
240
+			break;
241
+
242
+		case CMD_INPUT_LABELS:
243
+			if (valid_slot)
244
+				snprintf(data->inputs[slot_pos].name, sizeof(data->inputs[slot_pos].name), "%s", slot_data);
245
+			break;
246
+
247
+		case CMD_OUTPUT_LABELS:
248
+			if (valid_slot)
249
+				snprintf(data->outputs[slot_pos].name, sizeof(data->inputs[slot_pos].name), "%s", slot_data);
250
+			break;
251
+
252
+		case CMD_VIDEO_OUTPUT_LOCKS:
253
+			if (valid_slot)
254
+				data->inputs[slot_pos].locked = slot_data[0] == 'L' ? true : false;
255
+			break;
256
+
257
+		case CMD_VIDEO_OUTPUT_ROUTING:
258
+			if (valid_slot) {
259
+				unsigned int dest_pos = strtoul(slot_data, NULL, 10);
260
+				if (dest_pos < ARRAY_SIZE(data->outputs))
261
+					data->inputs[slot_pos].routed_to = dest_pos;
262
+			}
263
+			break;
264
+
265
+		case CMD_PING:
266
+			// Do nothing, we just get ACK without any data
267
+			break;
268
+		}
269
+	}
270
+	free(cmd_data);
271
+
272
+	/* Check if everything is within limits */
273
+	switch (v->cmd) {
274
+	case CMD_PROTOCOL_PREAMBLE:
275
+		if (!streq(data->device.protocol_ver, "2.4")) {
276
+			if (!quiet) {
277
+				fprintf(stderr, "WARNING: Device protocol is %s but this program is tested with 2.4 only.\n",
278
+					data->device.protocol_ver);
279
+				fprintf(stderr, "         Please report successes/failures to author's email: georgi@unixsol.org\n");
280
+				fprintf(stderr, "         You may use -q or --quiet to suppress the message.\n");
281
+			}
282
+		}
283
+		break;
284
+	case CMD_VIDEOHUB_DEVICE:
285
+		if (!data->device.dev_present) {
286
+			die("Device reports that it's not present.");
287
+		}
288
+		if (data->device.num_video_inputs > ARRAY_SIZE(data->inputs)) {
289
+			die("The device supports %d inputs. Recompile the program with more MAX_INPUTS (currently %d)",
290
+				data->device.num_video_inputs, MAX_INPUTS);
291
+		}
292
+		if (data->device.num_video_outputs > ARRAY_SIZE(data->outputs)) {
293
+			die("The device supports %d outputs. Recompile the program with more MAX_OUTPUTS (currently %d)\n",
294
+				data->device.num_video_outputs, MAX_OUTPUTS);
295
+		}
296
+	default:
297
+		break;
298
+	}
299
+
300
+	return true;
301
+}

+ 18
- 0
cmd.h View File

@@ -0,0 +1,18 @@
1
+/*
2
+ * === Command parser ===
3
+ *
4
+ * Blackmagic Design Videohub control application
5
+ * Copyright (C) 2014 Unix Solutions Ltd.
6
+ * Written by Georgi Chorbadzhiyski
7
+ *
8
+ * Released under MIT license.
9
+ * See LICENSE-MIT.txt for license terms.
10
+ *
11
+ */
12
+
13
+#ifndef CMD_H
14
+#define CMD_H
15
+
16
+bool parse_command(struct videohub_data *d, char *cmd);
17
+
18
+#endif

+ 66
- 0
data.h View File

@@ -0,0 +1,66 @@
1
+/*
2
+ * === Main data structures and helpers ===
3
+ *
4
+ * Blackmagic Design Videohub control application
5
+ * Copyright (C) 2014 Unix Solutions Ltd.
6
+ * Written by Georgi Chorbadzhiyski
7
+ *
8
+ * Released under MIT license.
9
+ * See LICENSE-MIT.txt for license terms.
10
+ *
11
+ */
12
+
13
+#ifndef DATA_H
14
+#define DATA_H
15
+
16
+#include <stdbool.h>
17
+
18
+#define MAX_INPUTS 64
19
+#define MAX_OUTPUTS 64
20
+#define MAX_NAME_LEN 64
21
+
22
+struct device_desc {
23
+	bool			dev_present;
24
+	char			protocol_ver[16];
25
+	char			model_name[MAX_NAME_LEN];
26
+	char			unique_id[MAX_NAME_LEN];
27
+	unsigned int	num_video_inputs;
28
+	unsigned int	num_video_processing_units;
29
+	unsigned int	num_video_outputs;
30
+	unsigned int	num_video_monitoring_outputs;
31
+	unsigned int	num_serial_ports;
32
+};
33
+
34
+struct input_desc {
35
+	char			name[MAX_NAME_LEN];
36
+	bool			locked;
37
+	unsigned int	routed_to;
38
+};
39
+
40
+struct output_desc {
41
+	char			name[MAX_NAME_LEN];
42
+};
43
+
44
+struct videohub_data {
45
+	char					*dev_host;
46
+	char					*dev_port;
47
+	int						dev_fd;
48
+	struct device_desc		device;
49
+	struct input_desc		inputs[MAX_INPUTS];
50
+	struct output_desc		outputs[MAX_OUTPUTS];
51
+};
52
+
53
+extern int verbose;
54
+extern int quiet;
55
+
56
+#define v(fmt, arguments...) \
57
+	do { \
58
+		if (verbose) \
59
+			printf("verbose: " fmt, ## arguments); \
60
+	} while(0)
61
+
62
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
63
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
64
+#define UNUSED(x) UNUSED_ ## x __attribute__((unused))
65
+
66
+#endif

+ 1
- 0
libfuncs

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

+ 103
- 0
net.c View File

@@ -0,0 +1,103 @@
1
+/*
2
+ * === Network functions ===
3
+ *
4
+ * Blackmagic Design Videohub control application
5
+ * Copyright (C) 2014 Unix Solutions Ltd.
6
+ * Written by Georgi Chorbadzhiyski
7
+ *
8
+ * Released under MIT license.
9
+ * See LICENSE-MIT.txt for license terms.
10
+ *
11
+ */
12
+
13
+#include <stdlib.h>
14
+#include <ctype.h>
15
+#include <unistd.h>
16
+#include <string.h>
17
+#include <sys/errno.h>
18
+#include <sys/socket.h>
19
+#include <netinet/in.h>
20
+#include <netinet/tcp.h>
21
+#include <arpa/inet.h>
22
+
23
+#include "data.h"
24
+#include "net.h"
25
+
26
+#include "libfuncs/libfuncs.h"
27
+
28
+int ai_family = AF_UNSPEC;
29
+
30
+static char *my_inet_ntop(int family, struct sockaddr *addr, char *dest, int dest_len) {
31
+	struct sockaddr_in  *addr_v4 = (struct sockaddr_in  *)addr;
32
+	struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6 *)addr;
33
+	switch (family) {
34
+		case AF_INET:
35
+			return (char *)inet_ntop(AF_INET, &addr_v4->sin_addr, dest, dest_len);
36
+			break;
37
+		case AF_INET6:
38
+			return (char *)inet_ntop(AF_INET6, &addr_v6->sin6_addr, dest, dest_len);
39
+			break;
40
+		default:
41
+			memset(dest, 0, dest_len);
42
+			strcpy(dest, "unknown");
43
+			return dest;
44
+	}
45
+}
46
+
47
+int connect_client(int socktype, const char *hostname, const char *service) {
48
+	struct addrinfo hints, *res;
49
+	int n;
50
+
51
+	memset(&hints, 0, sizeof(struct addrinfo));
52
+
53
+	hints.ai_family = ai_family;
54
+	hints.ai_socktype = socktype;
55
+
56
+	v("Connecting to server %s port %s\n", hostname, service);
57
+
58
+	n = getaddrinfo(hostname, service, &hints, &res);
59
+
60
+	if (n < 0) {
61
+		fprintf(stderr, "ERROR: getaddrinfo(%s): %s\n", hostname, gai_strerror(n));
62
+		return -1;
63
+	}
64
+
65
+	int sockfd = -1;
66
+	struct addrinfo *ressave = res;
67
+	char str_addr[INET6_ADDRSTRLEN] = { 0 };
68
+	while (res) {
69
+		sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
70
+		if (sockfd > -1) {
71
+			my_inet_ntop(res->ai_family, res->ai_addr, str_addr, sizeof(str_addr));
72
+			if (do_connect(sockfd, res->ai_addr, res->ai_addrlen, 1000) < 0) {
73
+				fprintf(stderr, "ERROR: Cant connect to server %s port %s (addr=%s) | %s\n",
74
+					hostname, service, str_addr, strerror(errno));
75
+				close(sockfd);
76
+				sockfd = -1;
77
+			} else {
78
+				break; // connected
79
+			}
80
+		} else {
81
+			fprintf(stderr, "ERROR: Could not create socket: %s\n", strerror(errno));
82
+			sleep(1); // 1 second between socket creation (after error)
83
+			return -1;
84
+		}
85
+		res = res->ai_next;
86
+	}
87
+	freeaddrinfo(ressave);
88
+
89
+	if (sockfd < 0)
90
+		return -1;
91
+
92
+	if (socktype == SOCK_STREAM) {
93
+		int flag = 1;
94
+		setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
95
+	}
96
+
97
+	v("Connected to server %s port %s (addr=%s fd=%d).\n",
98
+		hostname, service, str_addr, sockfd);
99
+
100
+	set_sock_nonblock(sockfd);
101
+
102
+	return sockfd;
103
+}

+ 18
- 0
net.h View File

@@ -0,0 +1,18 @@
1
+/*
2
+ * === Network functions ===
3
+ *
4
+ * Blackmagic Design Videohub control application
5
+ * Copyright (C) 2014 Unix Solutions Ltd.
6
+ * Written by Georgi Chorbadzhiyski
7
+ *
8
+ * Released under MIT license.
9
+ * See LICENSE-MIT.txt for license terms.
10
+ *
11
+ */
12
+
13
+#ifndef _NET_H
14
+#define _NET_H
15
+
16
+int connect_client(int socktype, const char *hostname, const char *service);
17
+
18
+#endif

+ 83
- 0
util.c View File

@@ -0,0 +1,83 @@
1
+/*
2
+ * === Utility functions ===
3
+ *
4
+ * Blackmagic Design Videohub control application
5
+ * Copyright (C) 2014 Unix Solutions Ltd.
6
+ * Written by Georgi Chorbadzhiyski
7
+ *
8
+ * Released under MIT license.
9
+ * See LICENSE-MIT.txt for license terms.
10
+ *
11
+ */
12
+
13
+#include <stdio.h>
14
+#include <stdarg.h>
15
+#include <stdlib.h>
16
+#include <string.h>
17
+#include <inttypes.h>
18
+#include <malloc.h>
19
+#include <unistd.h>
20
+
21
+#include "util.h"
22
+
23
+void die(const char *fmt, ...) {
24
+	va_list args;
25
+	va_start(args, fmt);
26
+	fprintf(stderr, "ERROR: ");
27
+	vfprintf(stderr, fmt, args);
28
+	if (fmt[strlen(fmt) - 1] != '\n')
29
+		fprintf(stderr, "\n");
30
+	va_end(args);
31
+	exit(EXIT_FAILURE);
32
+}
33
+
34
+void *xmalloc(size_t size) {
35
+	void *ret = memalign(16, size);
36
+	if (!ret)
37
+		die("Can't alloc %ld bytes\n", (unsigned long)size);
38
+	return ret;
39
+}
40
+
41
+void *xzalloc(size_t size) {
42
+	void *ret = xmalloc(size);
43
+	memset(ret, 0, size);
44
+	return ret;
45
+}
46
+
47
+void *xcalloc(size_t nmemb, size_t size) {
48
+	return xzalloc(nmemb * size);
49
+}
50
+
51
+void *xrealloc(void *ptr, size_t size) {
52
+	void *ret = realloc(ptr, size);
53
+	if (!ret)
54
+		die("Can't realloc %ld bytes\n", (unsigned long)size);
55
+	return ret;
56
+}
57
+
58
+char *xstrdup(const char *s) {
59
+	char *ret;
60
+	if (!s)
61
+		return NULL;
62
+	ret = strdup(s);
63
+	if (!ret)
64
+		die("Can't strdup %lu bytes\n", (unsigned long)strlen(s) + 1);
65
+	return ret;
66
+}
67
+
68
+char *xstrndup(const char *s, size_t n) {
69
+	char *ret;
70
+	if (!s)
71
+		return NULL;
72
+	ret = strndup(s, n);
73
+	if (!ret)
74
+		die("Can't strndup %lu bytes\n", (unsigned long)n + 1);
75
+	return ret;
76
+}
77
+
78
+bool streq(const char *s1, const char *s2) {
79
+	if(!s1 && s2) { return 0; }
80
+	if(s1 && !s2) { return 0; }
81
+	if(!s1 && !s2) { return 1; }
82
+	return strcmp(s1, s2) == 0;
83
+}

+ 30
- 0
util.h View File

@@ -0,0 +1,30 @@
1
+/*
2
+ * === Utility functions ===
3
+ *
4
+ * Blackmagic Design Videohub control application
5
+ * Copyright (C) 2014 Unix Solutions Ltd.
6
+ * Written by Georgi Chorbadzhiyski
7
+ *
8
+ * Released under MIT license.
9
+ * See LICENSE-MIT.txt for license terms.
10
+ *
11
+ */
12
+
13
+#ifndef _UTIL_H
14
+#define _UTIL_H
15
+
16
+#include <stdbool.h>
17
+#include <inttypes.h>
18
+#include <sys/time.h>
19
+
20
+void die(const char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
21
+void *xmalloc(size_t size);
22
+void *xzalloc(size_t size);
23
+void *xcalloc(size_t nmemb, size_t size);
24
+void *xrealloc(void *ptr, size_t size);
25
+char *xstrdup(const char *s);
26
+char *xstrndup(const char *s, size_t n);
27
+
28
+bool streq(const char *s1, const char *s2);
29
+
30
+#endif

+ 31
- 0
version.sh View File

@@ -0,0 +1,31 @@
1
+#!/bin/sh
2
+# Create/Update version.h
3
+# Written by Georgi Chorbadzhiyski
4
+
5
+VERSION=$(cat RELEASE 2>/dev/null || echo 'unknown')
6
+GIT_VER=$(git describe --tags --dirty --always 2>/dev/null || echo 'release')
7
+
8
+TMPFILE=`mktemp /tmp/version.h.XXXXXX` || exit 1
9
+
10
+trap "{ rm -f $TMPFILE ; exit 1 ; }" INT TERM
11
+
12
+echo "\
13
+#ifndef _VERSION_H_
14
+#define _VERSION_H_
15
+
16
+#define VERSION \"${VERSION}\"
17
+#define GIT_VER \"${GIT_VER}\"
18
+#define PROGRAM_NAME \"videohubctrl\"
19
+
20
+#endif\
21
+" > $TMPFILE
22
+
23
+if ! cmp version.h $TMPFILE >/dev/null 2>/dev/null
24
+then
25
+	cat $TMPFILE > version.h
26
+	echo "Updated version.h with VERSION=${VERSION} GIT_VER=${GIT_VER}"
27
+else
28
+	echo "version.h is up to date with VERSION=${VERSION} GIT_VER=${GIT_VER}"
29
+fi
30
+
31
+rm -f $TMPFILE

+ 173
- 0
videohubctrl.c View File

@@ -0,0 +1,173 @@
1
+/*
2
+ * Blackmagic Design Videohub control application
3
+ * Copyright (C) 2014 Unix Solutions Ltd.
4
+ * Written by Georgi Chorbadzhiyski
5
+ *
6
+ * Released under MIT license.
7
+ * See LICENSE-MIT.txt for license terms.
8
+ *
9
+ */
10
+
11
+#include <stdio.h>
12
+#include <stdlib.h>
13
+#include <string.h>
14
+#include <getopt.h>
15
+
16
+#include "data.h"
17
+#include "cmd.h"
18
+#include "net.h"
19
+#include "util.h"
20
+#include "version.h"
21
+
22
+#include "libfuncs/libfuncs.h"
23
+
24
+int verbose;
25
+int quiet;
26
+
27
+static struct videohub_data maindata;
28
+static int show_info = 1;
29
+
30
+static const char *program_id = PROGRAM_NAME " Version: " VERSION " Git: " GIT_VER;
31
+
32
+static const char short_options[] = "s:p:qvhVi";
33
+
34
+static const struct option long_options[] = {
35
+	{ "host",				required_argument, NULL, 's' },
36
+	{ "port",				required_argument, NULL, 'p' },
37
+	{ "quiet",				no_argument,       NULL, 'q' },
38
+	{ "verbose",			no_argument,       NULL, 'v' },
39
+	{ "help",				no_argument,       NULL, 'h' },
40
+	{ "version",			no_argument,       NULL, 'V' },
41
+	{ "info",				no_argument,       NULL, 'i' },
42
+	{ 0, 0, 0, 0 }
43
+};
44
+
45
+static void show_help(struct videohub_data *data) {
46
+	printf("\n");
47
+	printf(" Usage: " PROGRAM_NAME " --host <host> [..commands..]\n");
48
+	printf("\n");
49
+	printf("Main options:\n");
50
+	printf(" -s --host <hostname>       | Set device hostname.\n");
51
+	printf(" -p --port <port_number>    | Set device port (default: 9990).\n");
52
+	printf("\n");
53
+	printf("Misc options:\n");
54
+	printf(" -v --verbose               | Enable verbose logging.\n");
55
+	printf(" -q --quiet                 | Suppress warnings.\n");
56
+	printf(" -h --help                  | Show help screen.\n");
57
+	printf(" -V --version               | Show program version.\n");
58
+	printf("\n");
59
+	printf("Commands:\n");
60
+	printf(" -i --info                  | Show device info (default command).\n");
61
+	printf("\n");
62
+}
63
+
64
+static void parse_options(struct videohub_data *data, int argc, char **argv) {
65
+	int j, err = 0;
66
+	// Set defaults
67
+	data->dev_port = "9990";
68
+	while ((j = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
69
+		switch (j) {
70
+			case 's': // --host
71
+				data->dev_host = optarg;
72
+				break;
73
+			case 'p': // --port
74
+				data->dev_port = optarg;
75
+				break;
76
+			case 'v': // --verbose
77
+				verbose = !verbose;
78
+				if (verbose)
79
+					quiet = 0; // Disable quiet
80
+				break;
81
+			case 'q': // --quiet
82
+				quiet = !quiet;
83
+				break;
84
+			case 'i': // --info
85
+				show_info = 1;
86
+				break;
87
+			case 'h': // --help
88
+				show_help(data);
89
+				exit(EXIT_SUCCESS);
90
+			case 'V': // --version
91
+				// program_id is already printed on startup, just exit.
92
+				exit(EXIT_SUCCESS);
93
+		}
94
+	}
95
+
96
+	if (!data->dev_host || !strtoul(data->dev_port, NULL, 10))
97
+		err = 1;
98
+
99
+	if (err) {
100
+		show_help(data);
101
+		if (!data->dev_host)
102
+			fprintf(stderr, "ERROR: host is not set. Use --host option.\n");
103
+		exit(EXIT_FAILURE);
104
+	}
105
+
106
+	v("Device address: %s:%s\n", data->dev_host, data->dev_port);
107
+}
108
+
109
+static void print_device_desc(struct device_desc *d) {
110
+	printf("\n");
111
+	printf("Protocol version: %s\n", d->protocol_ver);
112
+	printf("Model name: %s\n", d->model_name);
113
+	printf("Unique ID: %s\n", d->unique_id);
114
+	printf("Video inputs: %u\n", d->num_video_inputs);
115
+	printf("Video processing units: %u\n", d->num_video_processing_units);
116
+	printf("Video outputs: %u\n", d->num_video_outputs);
117
+	printf("Video monitoring outputs: %u\n", d->num_video_monitoring_outputs);
118
+	printf("Serial ports: %u\n", d->num_serial_ports);
119
+}
120
+
121
+static void printf_line(int len) {
122
+	int i;
123
+	for (i = 0; i < len; i++)
124
+		printf("-");
125
+	printf("\n");
126
+}
127
+
128
+static void print_device_settings(struct videohub_data *d) {
129
+	unsigned int i;
130
+	printf("\n");
131
+	printf_line(76);
132
+	printf("|  # | x | %-30s | %-30s |\n", "Input name", "Output name");
133
+	printf_line(76);
134
+	for(i = 0; i < d->device.num_video_inputs; i++) {
135
+		printf("| %2d | %c | %-30s | %-30s |\n",
136
+			i + 1,
137
+			d->inputs[i].locked ? 'L' : ' ',
138
+			d->inputs[i].name,
139
+			d->outputs[d->inputs[i].routed_to].name
140
+		);
141
+	}
142
+	printf_line(76);
143
+}
144
+
145
+int main(int argc, char **argv) {
146
+	struct videohub_data *data = &maindata;
147
+
148
+	printf("%s\n", program_id);
149
+
150
+	parse_options(data, argc, argv);
151
+	set_log_io_errors(0);
152
+
153
+	data->dev_fd = connect_client(SOCK_STREAM, data->dev_host, data->dev_port);
154
+	if (data->dev_fd < 0)
155
+		exit(EXIT_FAILURE);
156
+
157
+	int ret;
158
+	char buf[8192 + 1];
159
+	memset(buf, 0, sizeof(buf));
160
+	while ((ret = fdread_ex(data->dev_fd, buf, sizeof(buf) - 1, 5, 0, 0)) >= 0) {
161
+		parse_command(data, buf);
162
+		memset(buf, 0, sizeof(buf));
163
+	}
164
+	shutdown_fd(&data->dev_fd);
165
+
166
+	if (show_info) {
167
+		print_device_desc(&data->device);
168
+		print_device_settings(data);
169
+		fflush(stdout);
170
+	}
171
+
172
+	return 0;
173
+}

Loading…
Cancel
Save