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