1 /*
2  *  This file is part of vban_emitter.
3  *  Copyright (c) 2015 by Benoît Quiniou <quiniouben@yahoo.fr>
4  *
5  *  vban_emitter is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  vban_emitter is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with vban_emitter.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <signal.h>
23 #include <getopt.h>
24 #include "common/version.h"
25 #include "common/socket.h"
26 #include "common/audio.h"
27 #include "common/logger.h"
28 #include "common/packet.h"
29 #include "common/backend/audio_backend.h"
30 
31 struct config_t
32 {
33     struct socket_config_t      socket;
34     struct audio_config_t       audio;
35     struct stream_config_t      stream;
36     struct audio_map_config_t   map;
37     char                        stream_name[VBAN_STREAM_NAME_SIZE];
38 };
39 
40 struct main_t
41 {
42     socket_handle_t             socket;
43     audio_handle_t              audio;
44     char                        buffer[VBAN_PROTOCOL_MAX_SIZE];
45 };
46 
47 static int MainRun = 1;
signalHandler(int signum)48 void signalHandler(int signum)
49 {
50     MainRun = 0;
51 }
52 
usage()53 void usage()
54 {
55     printf("\nUsage: vban_emitter [OPTIONS]...\n\n");
56     printf("-i, --ipaddress=IP      : MANDATORY. ipaddress to send stream to\n");
57     printf("-p, --port=PORT         : MANDATORY. port to use\n");
58     printf("-s, --streamname=NAME   : MANDATORY. streamname to use\n");
59     printf("-b, --backend=TYPE      : audio backend to use. %s\n", audio_backend_get_help());
60     printf("-d, --device=NAME       : Audio device name. This is file name for file backend, server name for jack backend, device for alsa, stream_name for pulseaudio.\n");
61     printf("-r, --rate=VALUE        : Audio device sample rate. default 44100\n");
62     printf("-n, --nbchannels=VALUE  : Audio device number of channels. default 2\n");
63     printf("-f, --format=VALUE      : Audio device sample format (see below). default is 16I (16bits integer)\n");
64     printf("-c, --channels=LIST     : channels from the audio device to use. LIST is of form x,y,z,... default is to forward the stream as it is\n");
65 
66     printf("-l, --loglevel=LEVEL    : Log level, from 0 (FATAL) to 4 (DEBUG). default is 1 (ERROR)\n");
67     printf("-h, --help              : display this message\n\n");
68     printf("%s\n\n", stream_bit_fmt_help());
69 }
70 
get_options(struct config_t * config,int argc,char * const * argv)71 int get_options(struct config_t* config, int argc, char* const* argv)
72 {
73     int c = 0;
74     int ret = 0;
75 
76     static const struct option options[] =
77     {
78         {"ipaddress",   required_argument,  0, 'i'},
79         {"port",        required_argument,  0, 'p'},
80         {"streamname",  required_argument,  0, 's'},
81         {"backend",     required_argument,  0, 'b'},
82         {"device",      required_argument,  0, 'd'},
83         {"rate",        required_argument,  0, 'r'},
84         {"nbchannels",  required_argument,  0, 'n'},
85         {"format",      required_argument,  0, 'f'},
86         {"channels",    required_argument,  0, 'c'},
87         {"loglevel",    required_argument,  0, 'l'},
88         {"help",        no_argument,        0, 'h'},
89         {0,             0,                  0,  0 }
90     };
91 
92     // default values
93     config->stream.nb_channels  = 2;
94     config->stream.sample_rate  = 44100;
95     config->stream.bit_fmt      = VBAN_BITFMT_16_INT;
96     config->audio.buffer_size   = 1024; /*XXX Why ?*/
97 
98     config->socket.direction    = SOCKET_OUT;
99 
100     /* yes, I assume config is not 0 */
101     while (1)
102     {
103         c = getopt_long(argc, argv, "i:p:s:b:d:r:n:f:c:l:h", options, 0);
104         if (c == -1)
105             break;
106 
107         switch (c)
108         {
109             case 'i':
110                 strncpy(config->socket.ip_address, optarg, SOCKET_IP_ADDRESS_SIZE-1);
111                 break;
112 
113             case 'p':
114                 config->socket.port = atoi(optarg);
115                 break;
116 
117             case 's':
118                 strncpy(config->stream_name, optarg, VBAN_STREAM_NAME_SIZE-1);
119                 break;
120 
121             case 'b':
122                 strncpy(config->audio.backend_name, optarg, AUDIO_BACKEND_NAME_SIZE-1);
123                 break;
124 
125             case 'd':
126                 strncpy(config->audio.device_name, optarg, AUDIO_DEVICE_NAME_SIZE-1);
127                 break;
128 
129             case 'r':
130                 config->stream.sample_rate = atoi(optarg);
131                 break;
132 
133             case 'n':
134                 config->stream.nb_channels = atoi(optarg);
135                 break;
136 
137             case 'f':
138                 config->stream.bit_fmt = stream_parse_bit_fmt(optarg);
139                 break;
140 
141             case 'c':
142                 ret = audio_parse_map_config(&config->map, optarg);
143                 break;
144 
145             case 'l':
146                 logger_set_output_level(atoi(optarg));
147                 break;
148 
149             case 'h':
150             default:
151                 usage();
152                 return 1;
153         }
154 
155         if (ret)
156         {
157             return ret;
158         }
159     }
160 
161     /** check if we got all arguments */
162     if ((config->socket.ip_address[0] == 0)
163         || (config->socket.port == 0)
164         || (config->stream_name[0] == 0))
165     {
166         logger_log(LOG_FATAL, "Missing ip address, port or stream name");
167         usage();
168         return 1;
169     }
170 
171     if (!strncmp(config->audio.backend_name, "jack", AUDIO_BACKEND_NAME_SIZE))
172     {
173         logger_log(LOG_FATAL, "Sorry jack backend is not ready for emitter yet");
174         return 1;
175     }
176 
177     return 0;
178 }
179 
180 
main(int argc,char * const * argv)181 int main(int argc, char* const* argv)
182 {
183     int ret = 0;
184     int size = 0;
185     struct config_t config;
186     struct stream_config_t stream_config;
187     struct main_t   main_s;
188     int max_size = 0;
189 
190     printf("%s version %s\n\n", argv[0], VBAN_VERSION);
191 
192     memset(&config, 0, sizeof(struct config_t));
193     memset(&main_s, 0, sizeof(struct main_t));
194 
195     ret = get_options(&config, argc, argv);
196     if (ret != 0)
197     {
198         return ret;
199     }
200 
201     ret = socket_init(&main_s.socket, &config.socket);
202     if (ret != 0)
203     {
204         return ret;
205     }
206 
207     ret = audio_init(&main_s.audio, &config.audio);
208     if (ret != 0)
209     {
210         return ret;
211     }
212 
213     ret = audio_set_map_config(main_s.audio, &config.map);
214     if (ret != 0)
215     {
216         return ret;
217     }
218 
219     ret = audio_set_stream_config(main_s.audio, &config.stream);
220     if (ret != 0)
221     {
222         return ret;
223     }
224 
225     audio_get_stream_config(main_s.audio, &stream_config);
226     packet_init_header(main_s.buffer, &stream_config, config.stream_name);
227     max_size = packet_get_max_payload_size(main_s.buffer);
228 
229     while (MainRun)
230     {
231         size = audio_read(main_s.audio, PACKET_PAYLOAD_PTR(main_s.buffer), max_size);
232         if (size < 0)
233         {
234             MainRun = 0;
235             break;
236         }
237 
238         packet_set_new_content(main_s.buffer, size);
239         ret = packet_check(config.stream_name, main_s.buffer, size + sizeof(struct VBanHeader));
240         if (ret != 0)
241         {
242             logger_log(LOG_ERROR, "%s: packet prepared is invalid", __func__);
243             break;
244         }
245 
246         ret = socket_write(main_s.socket, main_s.buffer, size + sizeof(struct VBanHeader));
247         if (ret < 0)
248         {
249             MainRun = 0;
250             break;
251         }
252     }
253 
254     audio_release(&main_s.audio);
255     socket_release(&main_s.socket);
256 
257     return ret;
258 }
259 
260