1 /*
2 * tcmodinfo.c
3 *
4 * Copyright (C) Tilmann Bitterberg - August 2002
5 * updated and partially rewritten by
6 * Copyright (C) Francesco Romani - January 2006
7 *
8 * This file is part of transcode, a video stream processing tool
9 *
10 * transcode is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * transcode is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with GNU Make; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26 #include "config.h"
27 #include "tcstub.h"
28
29 #define EXE "tcmodinfo"
30
31 enum {
32 STATUS_OK = 0,
33 STATUS_BAD_PARAM,
34 STATUS_NO_MODULE,
35 STATUS_MODULE_ERROR,
36 STATUS_NO_SOCKET,
37 STATUS_SOCKET_ERROR,
38 STATUS_BAD_MODULES,
39 STATUS_MODULE_FAILED,
40 };
41
version(void)42 void version(void)
43 {
44 printf("%s (%s v%s) (C) 2001-2010 Tilmann Bitterberg, "
45 "Transcode Team\n", EXE, PACKAGE, VERSION);
46 }
47
usage(void)48 static void usage(void)
49 {
50 version();
51 tc_log_info(EXE, "Usage: %s [options]", EXE);
52 fprintf(stderr, " -i name Module name information (like \'smooth\')\n");
53 fprintf(stderr, " -p Print the compiled-in module path\n");
54 fprintf(stderr, " -d verbosity Verbosity mode [1 == TC_INFO]\n");
55 #ifdef ENABLE_EXPERIMENTAL
56 fprintf(stderr, " -m path Use PATH as module path\n");
57 fprintf(stderr, " -M element Request to module informations about <element>\n");
58 fprintf(stderr, " -C string Request to configure module using configuration <string>\n");
59 fprintf(stderr, " -t type Type of module (filter, encode, multiplex)\n");
60 #endif
61 fprintf(stderr, " -s socket Connect to transcode socket\n");
62 fprintf(stderr, "\n");
63 }
64
do_connect_socket(const char * socketfile)65 static int do_connect_socket(const char *socketfile)
66 {
67 int sock, retval;
68 struct sockaddr_un server;
69 char buf[OPTS_SIZE];
70 fd_set rfds;
71 struct timeval tv;
72 ssize_t n;
73
74 sock = socket(AF_UNIX, SOCK_STREAM, 0);
75 if (sock < 0) {
76 perror("opening stream socket");
77 return STATUS_NO_SOCKET;
78 }
79 server.sun_family = AF_UNIX;
80 strlcpy(server.sun_path, socketfile, sizeof(server.sun_path));
81
82 if (connect(sock, (struct sockaddr *) &server,
83 sizeof(struct sockaddr_un)) < 0) {
84 close(sock);
85 perror("connecting stream socket");
86 return STATUS_NO_SOCKET;
87 }
88
89 while (1) {
90 /* Watch stdin (fd 0) to see when it has input. */
91 FD_ZERO(&rfds);
92 FD_SET(0, &rfds); // stdin
93 FD_SET(sock, &rfds);
94 /* Wait up to five seconds. */
95 tv.tv_sec = 5;
96 tv.tv_usec = 0;
97
98 retval = select(sock+1, &rfds, NULL, NULL, NULL);
99 /* Don't rely on the value of tv now! */
100
101 memset(buf, 0, sizeof (buf)); // null-termination in advance, slowly
102
103 if (retval>0) {
104 if (FD_ISSET(0, &rfds)) {
105 fgets(buf, OPTS_SIZE, stdin);
106 }
107 if (FD_ISSET(sock, &rfds)) {
108 if ( (n = read(sock, buf, OPTS_SIZE)) < 0) {
109 perror("reading on stream socket");
110 break;
111 } else if (n == 0) { // EOF
112 fprintf (stderr, "Server closed connection\n");
113 break;
114 }
115 printf("%s", buf);
116 continue;
117 }
118 }
119
120 if (write(sock, buf, strlen(buf)) < 0)
121 perror("writing on stream socket");
122
123 memset(buf, 0, sizeof (buf));
124
125 if (read(sock, buf, OPTS_SIZE) < 0)
126 perror("reading on stream socket");
127
128 printf("%s", buf);
129
130 if (!isatty(0))
131 break;
132 }
133
134 close(sock);
135 return STATUS_OK;
136 }
137
main(int argc,char * argv[])138 int main(int argc, char *argv[])
139 {
140 int ch;
141 const char *filename = NULL;
142 const char *modpath = MOD_PATH;
143 #ifdef ENABLE_EXPERIMENTAL
144 const char *modtype = "filter";
145 const char *modarg = ""; /* nothing */
146 const char *modcfg = ""; /* nothing */
147 #endif
148 const char *socketfile = NULL;
149 char options[OPTS_SIZE] = { '\0', };
150 int print_mod = 0;
151 int connect_socket = 0;
152 int ret = 0;
153 int status = STATUS_NO_MODULE;
154
155 /* needed by filter modules */
156 TCVHandle tcv_handle = tcv_init();
157 #ifdef ENABLE_EXPERIMENTAL
158 TCFactory factory = NULL;
159 TCModule module = NULL;
160 #endif
161
162 vframe_list_t ptr;
163
164 memset(&ptr, 0, sizeof(ptr));
165
166 ac_init(AC_ALL);
167 tc_set_config_dir(NULL);
168
169 if (argc == 1) {
170 usage();
171 return STATUS_BAD_PARAM;
172 }
173
174 libtc_init(&argc, &argv);
175
176 while (1) {
177 #ifdef ENABLE_EXPERIMENTAL
178 ch = getopt(argc, argv, "C:d:i:?vhpm:M:s:t:");
179 #else /* !ENABLE_EXPERIMENTAL */
180 ch = getopt(argc, argv, "d:i:?vhps:");
181 #endif
182 if (ch == -1) {
183 break;
184 }
185
186 switch (ch) {
187 case 'd':
188 if (optarg[0] == '-') {
189 usage();
190 return STATUS_BAD_PARAM;
191 }
192 verbose = atoi(optarg);
193 break;
194 case 'i':
195 if (optarg[0] == '-') {
196 usage();
197 return STATUS_BAD_PARAM;
198 }
199 filename = optarg;
200 break;
201 #ifdef ENABLE_EXPERIMENTAL
202 case 'C':
203 modcfg = optarg;
204 break;
205 case 'm':
206 modpath = optarg;
207 break;
208 case 'M':
209 modarg = optarg;
210 break;
211 case 't':
212 if (!optarg) {
213 usage();
214 return STATUS_BAD_PARAM;
215 }
216 if (!strcmp(optarg, "filter")
217 || !strcmp(optarg, "encode")
218 || !strcmp(optarg, "multiplex")) {
219 modtype = optarg;
220 } else {
221 modtype = NULL;
222 }
223 break;
224 #endif
225 case 's':
226 if (optarg[0] == '-') {
227 usage();
228 return STATUS_BAD_PARAM;
229 }
230 connect_socket = 1;
231 socketfile = optarg;
232 break;
233 case 'p':
234 print_mod = 1;
235 break;
236 case 'v':
237 version();
238 return STATUS_OK;
239 case '?': /* fallthrough */
240 case 'h': /* fallthrough */
241 default:
242 usage();
243 return STATUS_OK;
244 }
245 }
246
247 if (print_mod) {
248 printf("%s\n", modpath);
249 return STATUS_OK;
250 }
251
252 if (connect_socket) {
253 do_connect_socket(socketfile);
254 return STATUS_OK;
255 }
256
257 if (!filename) {
258 usage();
259 return STATUS_BAD_PARAM;
260 }
261
262 #ifdef ENABLE_EXPERIMENTAL
263 if (!modtype || !strcmp(modtype, "import")) {
264 tc_log_error(EXE, "Unknown module type (not in filter, encode, multiplex)");
265 return STATUS_BAD_PARAM;
266 }
267
268 if (strlen(modcfg) > 0 && strlen(modarg) > 0) {
269 tc_log_error(EXE, "Cannot configure and inspect module on the same time");
270 return STATUS_BAD_PARAM;
271 }
272
273 /*
274 * we can't distinguish from OMS and NMS modules at glance, so try
275 * first using new module system
276 */
277 factory = tc_new_module_factory(modpath, TC_MAX(verbose - 4, 0));
278 module = tc_new_module(factory, modtype, filename, TC_NONE);
279
280 if (module != NULL) {
281 const char *answer = NULL;
282
283 if (verbose >= TC_DEBUG) {
284 tc_log_info(EXE, "using new module system");
285 }
286 if (strlen(modcfg) > 0) {
287 int ret = tc_module_configure(module, modcfg, tc_get_vob());
288 if (ret == TC_OK) {
289 status = STATUS_OK;
290 } else {
291 status = STATUS_MODULE_FAILED;
292 tc_log_error(EXE, "configure returned error");
293 }
294 tc_module_stop(module);
295 } else {
296 if (verbose >= TC_INFO) {
297 /* overview and options */
298 tc_module_inspect(module, "help", &answer);
299 puts(answer);
300 /* module capabilities */
301 tc_module_show_info(module, verbose);
302 }
303 if (strlen(modarg) > 0) {
304 tc_log_info(EXE, "informations about '%s' for "
305 "module:", modarg);
306 tc_module_inspect(module, modarg, &answer);
307 puts(answer);
308 }
309 status = STATUS_OK;
310 }
311 tc_del_module(factory, module);
312 } else if (!strcmp(modtype, "filter"))
313 #endif /* ENABLE_EXPERIMENTAL */
314 {
315 char namebuf[NAME_LEN];
316 #ifdef ENABLE_EXPERIMENTAL
317 /* compatibility support only for filters */
318 if (verbose >= TC_DEBUG) {
319 tc_log_info(EXE, "using old module system");
320 }
321 #endif
322 /* ok, fallback to old module system */
323 strlcpy(namebuf, filename, NAME_LEN);
324 filter[0].name = namebuf;
325
326 ret = load_plugin(modpath, 0, verbose);
327 if (ret != 0) {
328 tc_log_error(__FILE__, "unable to load filter `%s' (path=%s)",
329 filter[0].name, modpath);
330 status = STATUS_NO_MODULE;
331 } else {
332 strlcpy(options, "help", OPTS_SIZE);
333 ptr.tag = TC_FILTER_INIT;
334 if ((ret = filter[0].entry(&ptr, options)) != 0) {
335 status = STATUS_MODULE_ERROR;
336 } else {
337 memset(options, 0, OPTS_SIZE);
338 ptr.tag = TC_FILTER_GET_CONFIG;
339 ret = filter[0].entry(&ptr, options);
340
341 if (ret == 0) {
342 if (verbose >= TC_INFO) {
343 fputs("START\n", stdout);
344 fputs(options, stdout);
345 fputs("END\n", stdout);
346 }
347 status = STATUS_OK;
348 }
349 }
350 }
351 }
352
353 #ifdef ENABLE_EXPERIMENTAL
354 ret = tc_del_module_factory(factory);
355 #endif
356 tcv_free(tcv_handle);
357 return status;
358 }
359
360 /* just for correct linking purposes */
361 #ifdef ENABLE_EXPERIMENTAL
362 #include "libtc/tcframes.h"
363 void dummy_tcframes(void);
dummy_tcframes(void)364 void dummy_tcframes(void) {
365 tc_del_video_frame(NULL);
366 tc_del_audio_frame(NULL);
367 }
368
369
370 #endif
371
372 /*************************************************************************/
373
374 /*
375 * Local variables:
376 * c-file-style: "stroustrup"
377 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
378 * indent-tabs-mode: nil
379 * End:
380 *
381 * vim: expandtab shiftwidth=4:
382 */
383