ffserver_config: simplify some if true conditions
[ffmpeg.git] / ffserver_config.c
1 /*
2  * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 #include <float.h>
22 #include "libavutil/opt.h"
23 #include "libavutil/parseutils.h"
24 #include "libavutil/avstring.h"
25 #include "libavutil/pixdesc.h"
26 #include "libavutil/avassert.h"
27
28 // FIXME those are internal headers, ffserver _really_ shouldn't use them
29 #include "libavformat/ffm.h"
30
31 #include "cmdutils.h"
32 #include "ffserver_config.h"
33
34 /* FIXME: make ffserver work with IPv6 */
35 /* resolve host with also IP address parsing */
36 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
37 {
38
39     if (!ff_inet_aton(hostname, sin_addr)) {
40 #if HAVE_GETADDRINFO
41         struct addrinfo *ai, *cur;
42         struct addrinfo hints = { 0 };
43         hints.ai_family = AF_INET;
44         if (getaddrinfo(hostname, NULL, &hints, &ai))
45             return -1;
46         /* getaddrinfo returns a linked list of addrinfo structs.
47          * Even if we set ai_family = AF_INET above, make sure
48          * that the returned one actually is of the correct type. */
49         for (cur = ai; cur; cur = cur->ai_next) {
50             if (cur->ai_family == AF_INET) {
51                 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
52                 freeaddrinfo(ai);
53                 return 0;
54             }
55         }
56         freeaddrinfo(ai);
57         return -1;
58 #else
59         struct hostent *hp;
60         hp = gethostbyname(hostname);
61         if (!hp)
62             return -1;
63         memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
64 #endif
65     }
66     return 0;
67 }
68
69 void ffserver_get_arg(char *buf, int buf_size, const char **pp)
70 {
71     const char *p;
72     char *q;
73     int quote;
74
75     p = *pp;
76     while (av_isspace(*p)) p++;
77     q = buf;
78     quote = 0;
79     if (*p == '\"' || *p == '\'')
80         quote = *p++;
81     for(;;) {
82         if (quote) {
83             if (*p == quote)
84                 break;
85         } else {
86             if (av_isspace(*p))
87                 break;
88         }
89         if (*p == '\0')
90             break;
91         if ((q - buf) < buf_size - 1)
92             *q++ = *p;
93         p++;
94     }
95     *q = '\0';
96     if (quote && *p == quote)
97         p++;
98     *pp = p;
99 }
100
101 void ffserver_parse_acl_row(FFServerStream *stream, FFServerStream* feed,
102                             FFServerIPAddressACL *ext_acl,
103                             const char *p, const char *filename, int line_num)
104 {
105     char arg[1024];
106     FFServerIPAddressACL acl;
107     int errors = 0;
108
109     ffserver_get_arg(arg, sizeof(arg), &p);
110     if (av_strcasecmp(arg, "allow") == 0)
111         acl.action = IP_ALLOW;
112     else if (av_strcasecmp(arg, "deny") == 0)
113         acl.action = IP_DENY;
114     else {
115         fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
116                 filename, line_num, arg);
117         errors++;
118     }
119
120     ffserver_get_arg(arg, sizeof(arg), &p);
121
122     if (resolve_host(&acl.first, arg)) {
123         fprintf(stderr, "%s:%d: ACL refers to invalid host or IP address '%s'\n",
124                 filename, line_num, arg);
125         errors++;
126     } else
127         acl.last = acl.first;
128
129     ffserver_get_arg(arg, sizeof(arg), &p);
130
131     if (arg[0]) {
132         if (resolve_host(&acl.last, arg)) {
133             fprintf(stderr,
134                     "%s:%d: ACL refers to invalid host or IP address '%s'\n",
135                     filename, line_num, arg);
136             errors++;
137         }
138     }
139
140     if (!errors) {
141         FFServerIPAddressACL *nacl = av_mallocz(sizeof(*nacl));
142         FFServerIPAddressACL **naclp = 0;
143
144         acl.next = 0;
145         *nacl = acl;
146
147         if (stream)
148             naclp = &stream->acl;
149         else if (feed)
150             naclp = &feed->acl;
151         else if (ext_acl)
152             naclp = &ext_acl;
153         else {
154             fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
155                     filename, line_num);
156             errors++;
157         }
158
159         if (naclp) {
160             while (*naclp)
161                 naclp = &(*naclp)->next;
162
163             *naclp = nacl;
164         } else
165             av_free(nacl);
166     }
167 }
168
169 /* add a codec and set the default parameters */
170 static void add_codec(FFServerStream *stream, AVCodecContext *av)
171 {
172     AVStream *st;
173
174     if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams))
175         return;
176
177     /* compute default parameters */
178     switch(av->codec_type) {
179     case AVMEDIA_TYPE_AUDIO:
180         if (av->bit_rate == 0)
181             av->bit_rate = 64000;
182         if (av->sample_rate == 0)
183             av->sample_rate = 22050;
184         if (av->channels == 0)
185             av->channels = 1;
186         break;
187     case AVMEDIA_TYPE_VIDEO:
188         if (av->bit_rate == 0)
189             av->bit_rate = 64000;
190         if (av->time_base.num == 0){
191             av->time_base.den = 5;
192             av->time_base.num = 1;
193         }
194         if (av->width == 0 || av->height == 0) {
195             av->width = 160;
196             av->height = 128;
197         }
198         /* Bitrate tolerance is less for streaming */
199         if (av->bit_rate_tolerance == 0)
200             av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
201                       (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
202         if (av->qmin == 0)
203             av->qmin = 3;
204         if (av->qmax == 0)
205             av->qmax = 31;
206         if (av->max_qdiff == 0)
207             av->max_qdiff = 3;
208         av->qcompress = 0.5;
209         av->qblur = 0.5;
210
211         if (!av->nsse_weight)
212             av->nsse_weight = 8;
213
214         av->frame_skip_cmp = FF_CMP_DCTMAX;
215         if (!av->me_method)
216             av->me_method = ME_EPZS;
217         av->rc_buffer_aggressivity = 1.0;
218
219         if (!av->rc_eq)
220             av->rc_eq = av_strdup("tex^qComp");
221         if (!av->i_quant_factor)
222             av->i_quant_factor = -0.8;
223         if (!av->b_quant_factor)
224             av->b_quant_factor = 1.25;
225         if (!av->b_quant_offset)
226             av->b_quant_offset = 1.25;
227         if (!av->rc_max_rate)
228             av->rc_max_rate = av->bit_rate * 2;
229
230         if (av->rc_max_rate && !av->rc_buffer_size) {
231             av->rc_buffer_size = av->rc_max_rate;
232         }
233
234
235         break;
236     default:
237         abort();
238     }
239
240     st = av_mallocz(sizeof(AVStream));
241     if (!st)
242         return;
243     st->codec = av;
244     stream->streams[stream->nb_streams++] = st;
245 }
246
247 static enum AVCodecID opt_codec(const char *name, enum AVMediaType type)
248 {
249     AVCodec *codec = avcodec_find_encoder_by_name(name);
250
251     if (!codec || codec->type != type)
252         return AV_CODEC_ID_NONE;
253     return codec->id;
254 }
255
256 static int ffserver_opt_default(const char *opt, const char *arg,
257                        AVCodecContext *avctx, int type)
258 {
259     int ret = 0;
260     const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
261     if(o)
262         ret = av_opt_set(avctx, opt, arg, 0);
263     return ret;
264 }
265
266 static int ffserver_opt_preset(const char *arg,
267                        AVCodecContext *avctx, int type,
268                        enum AVCodecID *audio_id, enum AVCodecID *video_id)
269 {
270     FILE *f=NULL;
271     char filename[1000], tmp[1000], tmp2[1000], line[1000];
272     int ret = 0;
273     AVCodec *codec = NULL;
274
275     if (avctx)
276         codec = avcodec_find_encoder(avctx->codec_id);
277
278     if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
279                               codec ? codec->name : NULL))) {
280         fprintf(stderr, "File for preset '%s' not found\n", arg);
281         return AVERROR(EINVAL);
282     }
283
284     while(!feof(f)){
285         int e= fscanf(f, "%999[^\n]\n", line) - 1;
286         if(line[0] == '#' && !e)
287             continue;
288         e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
289         if(e){
290             fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
291             ret = AVERROR(EINVAL);
292             break;
293         }
294         if (audio_id && !strcmp(tmp, "acodec")) {
295             *audio_id = opt_codec(tmp2, AVMEDIA_TYPE_AUDIO);
296         } else if (video_id && !strcmp(tmp, "vcodec")){
297             *video_id = opt_codec(tmp2, AVMEDIA_TYPE_VIDEO);
298         } else if(!strcmp(tmp, "scodec")) {
299             /* opt_subtitle_codec(tmp2); */
300         } else if (avctx && (ret = ffserver_opt_default(tmp, tmp2, avctx, type)) < 0) {
301             fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as "
302                     "'%s' = '%s'\n", filename, line, tmp, tmp2);
303             break;
304         }
305     }
306
307     fclose(f);
308
309     return ret;
310 }
311
312 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename, const char *mime_type)
313 {
314     AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
315
316     if (fmt) {
317         AVOutputFormat *stream_fmt;
318         char stream_format_name[64];
319
320         snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream",
321                 fmt->name);
322         stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
323
324         if (stream_fmt)
325             fmt = stream_fmt;
326     }
327
328     return fmt;
329 }
330
331 static void vreport_config_error(const char *filename, int line_num, int log_level, int *errors, const char *fmt, va_list vl)
332 {
333     av_log(NULL, log_level, "%s:%d: ", filename, line_num);
334     av_vlog(NULL, log_level, fmt, vl);
335     (*errors)++;
336 }
337
338 static void report_config_error(const char *filename, int line_num, int log_level, int *errors, const char *fmt, ...)
339 {
340     va_list vl;
341     va_start(vl, fmt);
342     vreport_config_error(filename, line_num, log_level, errors, fmt, vl);
343     va_end(vl);
344 }
345
346 static int ffserver_set_int_param(int *dest, const char *value, int factor, int min, int max,
347                                   FFServerConfig *config, int line_num, const char *error_msg, ...)
348 {
349     int tmp;
350     char *tailp;
351     if (!value || !value[0])
352         goto error;
353     errno = 0;
354     tmp = strtol(value, &tailp, 0);
355     if (tmp < min || tmp > max)
356         goto error;
357     if (factor) {
358         if (FFABS(tmp) > INT_MAX / FFABS(factor))
359             goto error;
360         tmp *= factor;
361     }
362     if (tailp[0] || errno)
363         goto error;
364     if (dest)
365         *dest = tmp;
366     return 0;
367   error:
368     if (config) {
369         va_list vl;
370         va_start(vl, error_msg);
371         vreport_config_error(config->filename, line_num, AV_LOG_ERROR,
372                 &config->errors, error_msg, vl);
373         va_end(vl);
374     }
375     return AVERROR(EINVAL);
376 }
377
378 static int ffserver_set_float_param(float *dest, const char *value, float factor, float min, float max,
379                                     FFServerConfig *config, int line_num, const char *error_msg, ...)
380 {
381     double tmp;
382     char *tailp;
383     if (!value || !value[0])
384         goto error;
385     errno = 0;
386     tmp = strtod(value, &tailp);
387     if (tmp < min || tmp > max)
388         goto error;
389     if (factor)
390         tmp *= factor;
391     if (tailp[0] || errno)
392         goto error;
393     if (dest)
394         *dest = tmp;
395     return 0;
396   error:
397     if (config) {
398         va_list vl;
399         va_start(vl, error_msg);
400         vreport_config_error(config->filename, line_num, AV_LOG_ERROR,
401                 &config->errors, error_msg, vl);
402         va_end(vl);
403     }
404     return AVERROR(EINVAL);
405 }
406
407 static int ffserver_save_avoption(const char *opt, const char *arg, AVDictionary **dict,
408                                   int type, FFServerConfig *config, int line_num)
409 {
410     int ret = 0;
411     AVDictionaryEntry *e;
412     const AVOption *o = av_opt_find(config->dummy_ctx, opt, NULL, type | AV_OPT_FLAG_ENCODING_PARAM, AV_OPT_SEARCH_CHILDREN);
413     if (!o) {
414         report_config_error(config->filename, line_num, AV_LOG_ERROR,
415                 &config->errors, "Option not found: %s\n", opt);
416     } else if ((ret = av_opt_set(config->dummy_ctx, opt, arg, AV_OPT_SEARCH_CHILDREN)) < 0) {
417         report_config_error(config->filename, line_num, AV_LOG_ERROR,
418                 &config->errors, "Invalid value for option %s (%s): %s\n", opt,
419                 arg, av_err2str(ret));
420     } else if ((e = av_dict_get(*dict, opt, NULL, 0))) {
421         if ((o->type == AV_OPT_TYPE_FLAGS) && arg && (arg[0] == '+' || arg[0] == '-'))
422             return av_dict_set(dict, opt, arg, AV_DICT_APPEND);
423         report_config_error(config->filename, line_num, AV_LOG_ERROR,
424                 &config->errors,
425                 "Redeclaring value of the option %s, previous value: %s\n",
426                 opt, e->value);
427     } else if (av_dict_set(dict, opt, arg, 0) < 0) {
428         return AVERROR(ENOMEM);
429     }
430     return 0;
431 }
432
433 #define ERROR(...)   report_config_error(config->filename, line_num, AV_LOG_ERROR,   &config->errors,   __VA_ARGS__)
434 #define WARNING(...) report_config_error(config->filename, line_num, AV_LOG_WARNING, &config->warnings, __VA_ARGS__)
435
436 static int ffserver_parse_config_global(FFServerConfig *config, const char *cmd,
437                                         const char **p, int line_num)
438 {
439     int val;
440     char arg[1024];
441     if (!av_strcasecmp(cmd, "Port") || !av_strcasecmp(cmd, "HTTPPort")) {
442         if (!av_strcasecmp(cmd, "Port"))
443             WARNING("Port option is deprecated, use HTTPPort instead\n");
444         ffserver_get_arg(arg, sizeof(arg), p);
445         ffserver_set_int_param(&val, arg, 0, 1, 65535, config, line_num,
446                 "Invalid port: %s\n", arg);
447         if (val < 1024)
448             WARNING("Trying to use IETF assigned system port: %d\n", val);
449         config->http_addr.sin_port = htons(val);
450     } else if (!av_strcasecmp(cmd, "HTTPBindAddress") || !av_strcasecmp(cmd, "BindAddress")) {
451         if (!av_strcasecmp(cmd, "BindAddress"))
452             WARNING("BindAddress option is deprecated, use HTTPBindAddress instead\n");
453         ffserver_get_arg(arg, sizeof(arg), p);
454         if (resolve_host(&config->http_addr.sin_addr, arg))
455             ERROR("Invalid host/IP address: %s\n", arg);
456     } else if (!av_strcasecmp(cmd, "NoDaemon")) {
457         WARNING("NoDaemon option has no effect, you should remove it\n");
458     } else if (!av_strcasecmp(cmd, "RTSPPort")) {
459         ffserver_get_arg(arg, sizeof(arg), p);
460         ffserver_set_int_param(&val, arg, 0, 1, 65535, config, line_num,
461                 "Invalid port: %s\n", arg);
462         config->rtsp_addr.sin_port = htons(val);
463     } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
464         ffserver_get_arg(arg, sizeof(arg), p);
465         if (resolve_host(&config->rtsp_addr.sin_addr, arg))
466             ERROR("Invalid host/IP address: %s\n", arg);
467     } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
468         ffserver_get_arg(arg, sizeof(arg), p);
469         ffserver_set_int_param(&val, arg, 0, 1, 65535, config, line_num,
470                 "Invalid MaxHTTPConnections: %s\n", arg);
471         config->nb_max_http_connections = val;
472         if (config->nb_max_connections > config->nb_max_http_connections)
473             ERROR("Inconsistent configuration: MaxClients(%d) > MaxHTTPConnections(%d)\n",
474                   config->nb_max_connections, config->nb_max_http_connections);
475     } else if (!av_strcasecmp(cmd, "MaxClients")) {
476         ffserver_get_arg(arg, sizeof(arg), p);
477         ffserver_set_int_param(&val, arg, 0, 1, 65535, config, line_num,
478                 "Invalid MaxClients: %s\n", arg);
479         config->nb_max_connections = val;
480         if (config->nb_max_connections > config->nb_max_http_connections)
481             ERROR("Inconsistent configuration: MaxClients(%d) > MaxHTTPConnections(%d)\n",
482                   config->nb_max_connections, config->nb_max_http_connections);
483     } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
484         int64_t llval;
485         char *tailp;
486         ffserver_get_arg(arg, sizeof(arg), p);
487         errno = 0;
488         llval = strtoll(arg, &tailp, 10);
489         if (llval < 10 || llval > 10000000 || tailp[0] || errno)
490             ERROR("Invalid MaxBandwidth: %s\n", arg);
491         else
492             config->max_bandwidth = llval;
493     } else if (!av_strcasecmp(cmd, "CustomLog")) {
494         if (!config->debug)
495             ffserver_get_arg(config->logfilename, sizeof(config->logfilename), p);
496     } else if (!av_strcasecmp(cmd, "LoadModule")) {
497         ERROR("Loadable modules are no longer supported\n");
498     } else
499         ERROR("Incorrect keyword: '%s'\n", cmd);
500     return 0;
501 }
502
503 static int ffserver_parse_config_feed(FFServerConfig *config, const char *cmd, const char **p,
504                                       int line_num, FFServerStream **pfeed)
505 {
506     FFServerStream *feed;
507     char arg[1024];
508     av_assert0(pfeed);
509     feed = *pfeed;
510     if (!av_strcasecmp(cmd, "<Feed")) {
511         char *q;
512         FFServerStream *s;
513         feed = av_mallocz(sizeof(FFServerStream));
514         if (!feed)
515             return AVERROR(ENOMEM);
516         ffserver_get_arg(feed->filename, sizeof(feed->filename), p);
517         q = strrchr(feed->filename, '>');
518         if (*q)
519             *q = '\0';
520
521         for (s = config->first_feed; s; s = s->next) {
522             if (!strcmp(feed->filename, s->filename))
523                 ERROR("Feed '%s' already registered\n", s->filename);
524         }
525
526         feed->fmt = av_guess_format("ffm", NULL, NULL);
527         /* default feed file */
528         snprintf(feed->feed_filename, sizeof(feed->feed_filename),
529                  "/tmp/%s.ffm", feed->filename);
530         feed->feed_max_size = 5 * 1024 * 1024;
531         feed->is_feed = 1;
532         feed->feed = feed; /* self feeding :-) */
533         *pfeed = feed;
534         return 0;
535     }
536     av_assert0(feed);
537     if (!av_strcasecmp(cmd, "Launch")) {
538         int i;
539
540         feed->child_argv = av_mallocz(64 * sizeof(char *));
541         if (!feed->child_argv)
542             return AVERROR(ENOMEM);
543         for (i = 0; i < 62; i++) {
544             ffserver_get_arg(arg, sizeof(arg), p);
545             if (!arg[0])
546                 break;
547
548             feed->child_argv[i] = av_strdup(arg);
549             if (!feed->child_argv[i])
550                 return AVERROR(ENOMEM);
551         }
552
553         feed->child_argv[i] =
554             av_asprintf("http://%s:%d/%s",
555                         (config->http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
556                         inet_ntoa(config->http_addr.sin_addr), ntohs(config->http_addr.sin_port),
557                         feed->filename);
558         if (!feed->child_argv[i])
559             return AVERROR(ENOMEM);
560     } else if (!av_strcasecmp(cmd, "ACL")) {
561         ffserver_parse_acl_row(NULL, feed, NULL, *p, config->filename,
562                 line_num);
563     } else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) {
564         ffserver_get_arg(feed->feed_filename, sizeof(feed->feed_filename), p);
565         feed->readonly = !av_strcasecmp(cmd, "ReadOnlyFile");
566     } else if (!av_strcasecmp(cmd, "Truncate")) {
567         ffserver_get_arg(arg, sizeof(arg), p);
568         /* assume Truncate is true in case no argument is specified */
569         if (!arg[0]) {
570             feed->truncate = 1;
571         } else {
572             WARNING("Truncate N syntax in configuration file is deprecated, "
573                     "use Truncate alone with no arguments\n");
574             feed->truncate = strtod(arg, NULL);
575         }
576     } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
577         char *p1;
578         double fsize;
579
580         ffserver_get_arg(arg, sizeof(arg), p);
581         p1 = arg;
582         fsize = strtod(p1, &p1);
583         switch(av_toupper(*p1)) {
584         case 'K':
585             fsize *= 1024;
586             break;
587         case 'M':
588             fsize *= 1024 * 1024;
589             break;
590         case 'G':
591             fsize *= 1024 * 1024 * 1024;
592             break;
593         default:
594             ERROR("Invalid file size: %s\n", arg);
595             break;
596         }
597         feed->feed_max_size = (int64_t)fsize;
598         if (feed->feed_max_size < FFM_PACKET_SIZE*4)
599             ERROR("Feed max file size is too small, must be at least %d\n",
600                     FFM_PACKET_SIZE*4);
601     } else if (!av_strcasecmp(cmd, "</Feed>")) {
602         *pfeed = NULL;
603     } else {
604         ERROR("Invalid entry '%s' inside <Feed></Feed>\n", cmd);
605     }
606     return 0;
607 }
608
609 static void ffserver_apply_stream_config(AVCodecContext *enc, const AVDictionary *conf, AVDictionary **opts)
610 {
611     AVDictionaryEntry *e;
612
613     /* Return values from ffserver_set_*_param are ignored.
614        Values are initially parsed and checked before inserting to
615        AVDictionary. */
616
617     //video params
618     if ((e = av_dict_get(conf, "VideoBitRateRangeMin", NULL, 0)))
619         ffserver_set_int_param(&enc->rc_min_rate, e->value, 1000, INT_MIN,
620                 INT_MAX, NULL, 0, NULL);
621     if ((e = av_dict_get(conf, "VideoBitRateRangeMax", NULL, 0)))
622         ffserver_set_int_param(&enc->rc_max_rate, e->value, 1000, INT_MIN,
623                 INT_MAX, NULL, 0, NULL);
624     if ((e = av_dict_get(conf, "Debug", NULL, 0)))
625         ffserver_set_int_param(&enc->debug, e->value, 0, INT_MIN, INT_MAX,
626                 NULL, 0, NULL);
627     if ((e = av_dict_get(conf, "Strict", NULL, 0)))
628         ffserver_set_int_param(&enc->strict_std_compliance, e->value, 0,
629                 INT_MIN, INT_MAX, NULL, 0, NULL);
630     if ((e = av_dict_get(conf, "VideoBufferSize", NULL, 0)))
631         ffserver_set_int_param(&enc->rc_buffer_size, e->value, 8*1024,
632                 INT_MIN, INT_MAX, NULL, 0, NULL);
633     if ((e = av_dict_get(conf, "VideoBitRateTolerance", NULL, 0)))
634         ffserver_set_int_param(&enc->bit_rate_tolerance, e->value, 1000,
635                 INT_MIN, INT_MAX, NULL, 0, NULL);
636     if ((e = av_dict_get(conf, "VideoBitRate", NULL, 0)))
637         ffserver_set_int_param(&enc->bit_rate, e->value, 1000, INT_MIN,
638                 INT_MAX, NULL, 0, NULL);
639     if ((e = av_dict_get(conf, "VideoSizeWidth", NULL, 0)))
640         ffserver_set_int_param(&enc->width, e->value, 0, INT_MIN, INT_MAX,
641                 NULL, 0, NULL);
642     if ((e = av_dict_get(conf, "VideoSizeHeight", NULL, 0)))
643         ffserver_set_int_param(&enc->height, e->value, 0, INT_MIN, INT_MAX,
644                 NULL, 0, NULL);
645     if ((e = av_dict_get(conf, "PixelFormat", NULL, 0))) {
646         int val;
647         ffserver_set_int_param(&val, e->value, 0, INT_MIN, INT_MAX, NULL, 0,
648                 NULL);
649         enc->pix_fmt = val;
650     }
651     if ((e = av_dict_get(conf, "VideoGopSize", NULL, 0)))
652         ffserver_set_int_param(&enc->gop_size, e->value, 0, INT_MIN, INT_MAX,
653                 NULL, 0, NULL);
654     if ((e = av_dict_get(conf, "VideoFrameRateNum", NULL, 0)))
655         ffserver_set_int_param(&enc->time_base.num, e->value, 0, INT_MIN,
656                 INT_MAX, NULL, 0, NULL);
657     if ((e = av_dict_get(conf, "VideoFrameRateDen", NULL, 0)))
658         ffserver_set_int_param(&enc->time_base.den, e->value, 0, INT_MIN,
659                 INT_MAX, NULL, 0, NULL);
660     if ((e = av_dict_get(conf, "VideoQDiff", NULL, 0)))
661         ffserver_set_int_param(&enc->max_qdiff, e->value, 0, INT_MIN, INT_MAX,
662                 NULL, 0, NULL);
663     if ((e = av_dict_get(conf, "VideoQMax", NULL, 0)))
664         ffserver_set_int_param(&enc->qmax, e->value, 0, INT_MIN, INT_MAX, NULL,
665                 0, NULL);
666     if ((e = av_dict_get(conf, "VideoQMin", NULL, 0)))
667         ffserver_set_int_param(&enc->qmin, e->value, 0, INT_MIN, INT_MAX, NULL,
668                 0, NULL);
669     if ((e = av_dict_get(conf, "LumiMask", NULL, 0)))
670         ffserver_set_float_param(&enc->lumi_masking, e->value, 0, -FLT_MAX,
671                 FLT_MAX, NULL, 0, NULL);
672     if ((e = av_dict_get(conf, "DarkMask", NULL, 0)))
673         ffserver_set_float_param(&enc->dark_masking, e->value, 0, -FLT_MAX,
674                 FLT_MAX, NULL, 0, NULL);
675     if (av_dict_get(conf, "BitExact", NULL, 0))
676         enc->flags |= CODEC_FLAG_BITEXACT;
677     if (av_dict_get(conf, "DctFastint", NULL, 0))
678         enc->dct_algo  = FF_DCT_FASTINT;
679     if (av_dict_get(conf, "IdctSimple", NULL, 0))
680         enc->idct_algo = FF_IDCT_SIMPLE;
681     if (av_dict_get(conf, "VideoHighQuality", NULL, 0))
682         enc->mb_decision = FF_MB_DECISION_BITS;
683     if ((e = av_dict_get(conf, "VideoTag", NULL, 0)))
684         enc->codec_tag = MKTAG(e->value[0], e->value[1], e->value[2], e->value[3]);
685     if (av_dict_get(conf, "Qscale", NULL, 0)) {
686         enc->flags |= CODEC_FLAG_QSCALE;
687         ffserver_set_int_param(&enc->global_quality, e->value, FF_QP2LAMBDA,
688                 INT_MIN, INT_MAX, NULL, 0, NULL);
689     }
690     if (av_dict_get(conf, "Video4MotionVector", NULL, 0)) {
691         enc->mb_decision = FF_MB_DECISION_BITS; //FIXME remove
692         enc->flags |= CODEC_FLAG_4MV;
693     }
694     //audio params
695     if ((e = av_dict_get(conf, "AudioChannels", NULL, 0)))
696         ffserver_set_int_param(&enc->channels, e->value, 0, INT_MIN, INT_MAX,
697                 NULL, 0, NULL);
698     if ((e = av_dict_get(conf, "AudioSampleRate", NULL, 0)))
699         ffserver_set_int_param(&enc->sample_rate, e->value, 0, INT_MIN,
700                 INT_MAX, NULL, 0, NULL);
701     if ((e = av_dict_get(conf, "AudioBitRate", NULL, 0)))
702         ffserver_set_int_param(&enc->bit_rate, e->value, 0, INT_MIN, INT_MAX,
703                 NULL, 0, NULL);
704
705     av_opt_set_dict2(enc, opts, AV_OPT_SEARCH_CHILDREN);
706 }
707
708 static int ffserver_parse_config_stream(FFServerConfig *config, const char *cmd, const char **p,
709                                         int line_num, FFServerStream **pstream)
710 {
711     char arg[1024], arg2[1024];
712     FFServerStream *stream;
713     int val;
714
715     av_assert0(pstream);
716     stream = *pstream;
717
718     if (!av_strcasecmp(cmd, "<Stream")) {
719         char *q;
720         FFServerStream *s;
721         stream = av_mallocz(sizeof(FFServerStream));
722         if (!stream)
723             return AVERROR(ENOMEM);
724         config->dummy_ctx = avcodec_alloc_context3(NULL);
725         if (!config->dummy_ctx) {
726             av_free(stream);
727             return AVERROR(ENOMEM);
728         }
729         ffserver_get_arg(stream->filename, sizeof(stream->filename), p);
730         q = strrchr(stream->filename, '>');
731         if (q)
732             *q = '\0';
733
734         for (s = config->first_stream; s; s = s->next) {
735             if (!strcmp(stream->filename, s->filename))
736                 ERROR("Stream '%s' already registered\n", s->filename);
737         }
738
739         stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
740         if (stream->fmt) {
741             config->audio_id = stream->fmt->audio_codec;
742             config->video_id = stream->fmt->video_codec;
743         } else {
744             config->audio_id = AV_CODEC_ID_NONE;
745             config->video_id = AV_CODEC_ID_NONE;
746         }
747         *pstream = stream;
748         return 0;
749     }
750     av_assert0(stream);
751     if (!av_strcasecmp(cmd, "Feed")) {
752         FFServerStream *sfeed;
753         ffserver_get_arg(arg, sizeof(arg), p);
754         sfeed = config->first_feed;
755         while (sfeed) {
756             if (!strcmp(sfeed->filename, arg))
757                 break;
758             sfeed = sfeed->next_feed;
759         }
760         if (!sfeed)
761             ERROR("Feed with name '%s' for stream '%s' is not defined\n", arg,
762                     stream->filename);
763         else
764             stream->feed = sfeed;
765     } else if (!av_strcasecmp(cmd, "Format")) {
766         ffserver_get_arg(arg, sizeof(arg), p);
767         if (!strcmp(arg, "status")) {
768             stream->stream_type = STREAM_TYPE_STATUS;
769             stream->fmt = NULL;
770         } else {
771             stream->stream_type = STREAM_TYPE_LIVE;
772             /* JPEG cannot be used here, so use single frame MJPEG */
773             if (!strcmp(arg, "jpeg"))
774                 strcpy(arg, "mjpeg");
775             stream->fmt = ffserver_guess_format(arg, NULL, NULL);
776             if (!stream->fmt)
777                 ERROR("Unknown Format: %s\n", arg);
778         }
779         if (stream->fmt) {
780             config->audio_id = stream->fmt->audio_codec;
781             config->video_id = stream->fmt->video_codec;
782         }
783     } else if (!av_strcasecmp(cmd, "InputFormat")) {
784         ffserver_get_arg(arg, sizeof(arg), p);
785         stream->ifmt = av_find_input_format(arg);
786         if (!stream->ifmt)
787             ERROR("Unknown input format: %s\n", arg);
788     } else if (!av_strcasecmp(cmd, "FaviconURL")) {
789         if (stream->stream_type == STREAM_TYPE_STATUS)
790             ffserver_get_arg(stream->feed_filename,
791                     sizeof(stream->feed_filename), p);
792         else
793             ERROR("FaviconURL only permitted for status streams\n");
794     } else if (!av_strcasecmp(cmd, "Author")    ||
795                !av_strcasecmp(cmd, "Comment")   ||
796                !av_strcasecmp(cmd, "Copyright") ||
797                !av_strcasecmp(cmd, "Title")) {
798         char key[32];
799         int i;
800         ffserver_get_arg(arg, sizeof(arg), p);
801         for (i = 0; i < strlen(cmd); i++)
802             key[i] = av_tolower(cmd[i]);
803         key[i] = 0;
804         WARNING("'%s' option in configuration file is deprecated, "
805                 "use 'Metadata %s VALUE' instead\n", cmd, key);
806         if (av_dict_set(&stream->metadata, key, arg, 0) < 0)
807             goto nomem;
808     } else if (!av_strcasecmp(cmd, "Metadata")) {
809         ffserver_get_arg(arg, sizeof(arg), p);
810         ffserver_get_arg(arg2, sizeof(arg2), p);
811         if (av_dict_set(&stream->metadata, arg, arg2, 0) < 0)
812             goto nomem;
813     } else if (!av_strcasecmp(cmd, "Preroll")) {
814         ffserver_get_arg(arg, sizeof(arg), p);
815         stream->prebuffer = atof(arg) * 1000;
816     } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
817         stream->send_on_key = 1;
818     } else if (!av_strcasecmp(cmd, "AudioCodec")) {
819         ffserver_get_arg(arg, sizeof(arg), p);
820         config->audio_id = opt_codec(arg, AVMEDIA_TYPE_AUDIO);
821         if (config->audio_id == AV_CODEC_ID_NONE)
822             ERROR("Unknown AudioCodec: %s\n", arg);
823     } else if (!av_strcasecmp(cmd, "VideoCodec")) {
824         ffserver_get_arg(arg, sizeof(arg), p);
825         config->video_id = opt_codec(arg, AVMEDIA_TYPE_VIDEO);
826         if (config->video_id == AV_CODEC_ID_NONE)
827             ERROR("Unknown VideoCodec: %s\n", arg);
828     } else if (!av_strcasecmp(cmd, "MaxTime")) {
829         ffserver_get_arg(arg, sizeof(arg), p);
830         stream->max_time = atof(arg) * 1000;
831     } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
832         float f;
833         ffserver_get_arg(arg, sizeof(arg), p);
834         ffserver_set_float_param(&f, arg, 1000, 0, FLT_MAX, config, line_num,
835                 "Invalid %s: %s\n", cmd, arg);
836         if (av_dict_set_int(&config->audio_conf, cmd, lrintf(f), 0) < 0)
837             goto nomem;
838     } else if (!av_strcasecmp(cmd, "AudioChannels")) {
839         ffserver_get_arg(arg, sizeof(arg), p);
840         ffserver_set_int_param(NULL, arg, 0, 1, 8, config, line_num,
841                 "Invalid %s: %s, valid range is 1-8.", cmd, arg);
842         if (av_dict_set(&config->audio_conf, cmd, arg, 0) < 0)
843             goto nomem;
844     } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
845         ffserver_get_arg(arg, sizeof(arg), p);
846         ffserver_set_int_param(NULL, arg, 0, 0, INT_MAX, config, line_num,
847                 "Invalid %s: %s", cmd, arg);
848         if (av_dict_set(&config->audio_conf, cmd, arg, 0) < 0)
849             goto nomem;
850     } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
851         int minrate, maxrate;
852         ffserver_get_arg(arg, sizeof(arg), p);
853         if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
854             if (av_dict_set_int(&config->video_conf, "VideoBitRateRangeMin", minrate, 0) < 0 ||
855                 av_dict_set_int(&config->video_conf, "VideoBitRateRangeMax", maxrate, 0) < 0)
856                 goto nomem;
857         } else
858             ERROR("Incorrect format for VideoBitRateRange -- should be "
859                     "<min>-<max>: %s\n", arg);
860     } else if (!av_strcasecmp(cmd, "Debug")) {
861         ffserver_get_arg(arg, sizeof(arg), p);
862         ffserver_set_int_param(NULL, arg, 0, INT_MIN, INT_MAX, config, line_num,
863                 "Invalid %s: %s", cmd, arg);
864         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
865             goto nomem;
866     } else if (!av_strcasecmp(cmd, "Strict")) {
867         ffserver_get_arg(arg, sizeof(arg), p);
868         ffserver_set_int_param(NULL, arg, 0, INT_MIN, INT_MAX, config, line_num,
869                 "Invalid %s: %s", cmd, arg);
870         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
871             goto nomem;
872     } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
873         ffserver_get_arg(arg, sizeof(arg), p);
874         ffserver_set_int_param(NULL, arg, 8*1024, 0, INT_MAX, config, line_num,
875                 "Invalid %s: %s", cmd, arg);
876         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
877             goto nomem;
878     } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
879         ffserver_get_arg(arg, sizeof(arg), p);
880         ffserver_set_int_param(NULL, arg, 1000, INT_MIN, INT_MAX, config,
881                 line_num, "Invalid %s: %s", cmd, arg);
882         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
883             goto nomem;
884     } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
885         ffserver_get_arg(arg, sizeof(arg), p);
886         ffserver_set_int_param(NULL, arg, 1000, 0, INT_MAX, config, line_num,
887                 "Invalid %s: %s", cmd, arg);
888         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
889            goto nomem;
890     } else if (!av_strcasecmp(cmd, "VideoSize")) {
891         int ret, w, h;
892         ffserver_get_arg(arg, sizeof(arg), p);
893         ret = av_parse_video_size(&w, &h, arg);
894         if (ret < 0)
895             ERROR("Invalid video size '%s'\n", arg);
896         else if ((w % 16) || (h % 16))
897             ERROR("Image size must be a multiple of 16\n");
898         if (av_dict_set_int(&config->video_conf, "VideoSizeWidth", w, 0) < 0 ||
899             av_dict_set_int(&config->video_conf, "VideoSizeHeight", h, 0) < 0)
900             goto nomem;
901     } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
902         AVRational frame_rate;
903         ffserver_get_arg(arg, sizeof(arg), p);
904         if (av_parse_video_rate(&frame_rate, arg) < 0) {
905             ERROR("Incorrect frame rate: %s\n", arg);
906         } else {
907             if (av_dict_set_int(&config->video_conf, "VideoFrameRateNum", frame_rate.num, 0) < 0 ||
908                 av_dict_set_int(&config->video_conf, "VideoFrameRateDen", frame_rate.den, 0) < 0)
909                 goto nomem;
910         }
911     } else if (!av_strcasecmp(cmd, "PixelFormat")) {
912         enum AVPixelFormat pix_fmt;
913         ffserver_get_arg(arg, sizeof(arg), p);
914         pix_fmt = av_get_pix_fmt(arg);
915         if (pix_fmt == AV_PIX_FMT_NONE)
916             ERROR("Unknown pixel format: %s\n", arg);
917         if (av_dict_set_int(&config->video_conf, cmd, pix_fmt, 0) < 0)
918             goto nomem;
919     } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
920         ffserver_get_arg(arg, sizeof(arg), p);
921         ffserver_set_int_param(NULL, arg, 0, INT_MIN, INT_MAX, config, line_num,
922                 "Invalid %s: %s", cmd, arg);
923         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
924             goto nomem;
925     } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
926         if (av_dict_set(&config->video_conf, cmd, "1", 0) < 0)
927             goto nomem;
928     } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
929         if (av_dict_set(&config->video_conf, cmd, "", 0) < 0)
930             goto nomem;
931     } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
932         if (av_dict_set(&config->video_conf, cmd, "", 0) < 0)
933             goto nomem;
934     } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
935                !av_strcasecmp(cmd, "AVOptionAudio")) {
936         int ret;
937         ffserver_get_arg(arg, sizeof(arg), p);
938         ffserver_get_arg(arg2, sizeof(arg2), p);
939         if (!av_strcasecmp(cmd, "AVOptionVideo"))
940             ret = ffserver_save_avoption(arg, arg2, &config->video_opts, AV_OPT_FLAG_VIDEO_PARAM ,config, line_num);
941         else
942             ret = ffserver_save_avoption(arg, arg2, &config->audio_opts, AV_OPT_FLAG_AUDIO_PARAM ,config, line_num);
943         if (ret < 0)
944             goto nomem;
945     } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
946                !av_strcasecmp(cmd, "AVPresetAudio")) {
947         char **preset = NULL;
948         ffserver_get_arg(arg, sizeof(arg), p);
949         if (!av_strcasecmp(cmd, "AVPresetVideo")) {
950             preset = &config->video_preset;
951             ffserver_opt_preset(arg, NULL, 0, NULL, &config->video_id);
952         } else {
953             preset = &config->audio_preset;
954             ffserver_opt_preset(arg, NULL, 0, &config->audio_id, NULL);
955         }
956         *preset = av_strdup(arg);
957         if (!preset)
958             return AVERROR(ENOMEM);
959     } else if (!av_strcasecmp(cmd, "VideoTag")) {
960         ffserver_get_arg(arg, sizeof(arg), p);
961         if (strlen(arg) == 4) {
962             if (av_dict_set(&config->video_conf, "VideoTag", "arg", 0) < 0)
963                 goto nomem;
964         }
965     } else if (!av_strcasecmp(cmd, "BitExact")) {
966         if (av_dict_set(&config->video_conf, cmd, "", 0) < 0)
967             goto nomem;
968     } else if (!av_strcasecmp(cmd, "DctFastint")) {
969         if (av_dict_set(&config->video_conf, cmd, "", 0) < 0)
970             goto nomem;
971     } else if (!av_strcasecmp(cmd, "IdctSimple")) {
972         if (av_dict_set(&config->video_conf, cmd, "", 0) < 0)
973             goto nomem;
974     } else if (!av_strcasecmp(cmd, "Qscale")) {
975         ffserver_get_arg(arg, sizeof(arg), p);
976         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
977             goto nomem;
978     } else if (!av_strcasecmp(cmd, "VideoQDiff")) {
979         ffserver_get_arg(arg, sizeof(arg), p);
980         ffserver_set_int_param(NULL, arg, 0, 1, 31, config, line_num,
981                 "%s out of range\n", cmd);
982         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
983             goto nomem;
984     } else if (!av_strcasecmp(cmd, "VideoQMax")) {
985         ffserver_get_arg(arg, sizeof(arg), p);
986         ffserver_set_int_param(NULL, arg, 0, 1, 31, config, line_num,
987                 "%s out of range\n", cmd);
988         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
989             goto nomem;
990     } else if (!av_strcasecmp(cmd, "VideoQMin")) {
991         ffserver_get_arg(arg, sizeof(arg), p);
992         ffserver_set_int_param(NULL, arg, 0, 1, 31, config, line_num,
993                 "%s out of range\n", cmd);
994         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
995             goto nomem;
996     } else if (!av_strcasecmp(cmd, "LumiMask")) {
997         ffserver_get_arg(arg, sizeof(arg), p);
998         ffserver_set_float_param(NULL, arg, 0, -FLT_MAX, FLT_MAX, config,
999                 line_num, "Invalid %s: %s", cmd, arg);
1000         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
1001             goto nomem;
1002     } else if (!av_strcasecmp(cmd, "DarkMask")) {
1003         ffserver_get_arg(arg, sizeof(arg), p);
1004         ffserver_set_float_param(NULL, arg, 0, -FLT_MAX, FLT_MAX, config,
1005                 line_num, "Invalid %s: %s", cmd, arg);
1006         if (av_dict_set(&config->video_conf, cmd, arg, 0) < 0)
1007             goto nomem;
1008     } else if (!av_strcasecmp(cmd, "NoVideo")) {
1009         config->video_id = AV_CODEC_ID_NONE;
1010     } else if (!av_strcasecmp(cmd, "NoAudio")) {
1011         config->audio_id = AV_CODEC_ID_NONE;
1012     } else if (!av_strcasecmp(cmd, "ACL")) {
1013         ffserver_parse_acl_row(stream, NULL, NULL, *p, config->filename,
1014                 line_num);
1015     } else if (!av_strcasecmp(cmd, "DynamicACL")) {
1016         ffserver_get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), p);
1017     } else if (!av_strcasecmp(cmd, "RTSPOption")) {
1018         ffserver_get_arg(arg, sizeof(arg), p);
1019         av_freep(&stream->rtsp_option);
1020         stream->rtsp_option = av_strdup(arg);
1021     } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
1022         ffserver_get_arg(arg, sizeof(arg), p);
1023         if (resolve_host(&stream->multicast_ip, arg))
1024             ERROR("Invalid host/IP address: %s\n", arg);
1025         stream->is_multicast = 1;
1026         stream->loop = 1; /* default is looping */
1027     } else if (!av_strcasecmp(cmd, "MulticastPort")) {
1028         ffserver_get_arg(arg, sizeof(arg), p);
1029         ffserver_set_int_param(&val, arg, 0, 1, 65535, config, line_num,
1030                 "Invalid MulticastPort: %s\n", arg);
1031         stream->multicast_port = val;
1032     } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
1033         ffserver_get_arg(arg, sizeof(arg), p);
1034         ffserver_set_int_param(&val, arg, 0, INT_MIN, INT_MAX, config,
1035                 line_num, "Invalid MulticastTTL: %s\n", arg);
1036         stream->multicast_ttl = val;
1037     } else if (!av_strcasecmp(cmd, "NoLoop")) {
1038         stream->loop = 0;
1039     } else if (!av_strcasecmp(cmd, "</Stream>")) {
1040         if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm")) {
1041             if (config->audio_id != AV_CODEC_ID_NONE) {
1042                 AVCodecContext *audio_enc = avcodec_alloc_context3(avcodec_find_encoder(config->audio_id));
1043                 if (config->audio_preset &&
1044                     ffserver_opt_preset(arg, audio_enc, AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_ENCODING_PARAM,
1045                                         NULL, NULL) < 0)
1046                     ERROR("Could not apply preset '%s'\n", arg);
1047                 ffserver_apply_stream_config(audio_enc, config->audio_conf,
1048                         &config->audio_opts);
1049                 add_codec(stream, audio_enc);
1050             }
1051             if (config->video_id != AV_CODEC_ID_NONE) {
1052                 AVCodecContext *video_enc = avcodec_alloc_context3(avcodec_find_encoder(config->video_id));
1053                 if (config->video_preset &&
1054                     ffserver_opt_preset(arg, video_enc, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_ENCODING_PARAM,
1055                                         NULL, NULL) < 0)
1056                     ERROR("Could not apply preset '%s'\n", arg);
1057                 ffserver_apply_stream_config(video_enc, config->video_conf,
1058                         &config->video_opts);
1059                 add_codec(stream, video_enc);
1060             }
1061         }
1062         av_dict_free(&config->video_opts);
1063         av_dict_free(&config->video_conf);
1064         av_dict_free(&config->audio_opts);
1065         av_dict_free(&config->audio_conf);
1066         av_freep(&config->video_preset);
1067         av_freep(&config->audio_preset);
1068         avcodec_free_context(&config->dummy_ctx);
1069         *pstream = NULL;
1070     } else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) {
1071         ffserver_get_arg(stream->feed_filename, sizeof(stream->feed_filename),
1072                 p);
1073     } else {
1074         ERROR("Invalid entry '%s' inside <Stream></Stream>\n", cmd);
1075     }
1076     return 0;
1077   nomem:
1078     av_log(NULL, AV_LOG_ERROR, "Out of memory. Aborting.\n");
1079     av_dict_free(&config->video_opts);
1080     av_dict_free(&config->video_conf);
1081     av_dict_free(&config->audio_opts);
1082     av_dict_free(&config->audio_conf);
1083     av_freep(&config->video_preset);
1084     av_freep(&config->audio_preset);
1085     avcodec_free_context(&config->dummy_ctx);
1086     return AVERROR(ENOMEM);
1087 }
1088
1089 static int ffserver_parse_config_redirect(FFServerConfig *config, const char *cmd, const char **p,
1090                                           int line_num, FFServerStream **predirect)
1091 {
1092     FFServerStream *redirect;
1093     av_assert0(predirect);
1094     redirect = *predirect;
1095
1096     if (!av_strcasecmp(cmd, "<Redirect")) {
1097         char *q;
1098         redirect = av_mallocz(sizeof(FFServerStream));
1099         if (!redirect)
1100             return AVERROR(ENOMEM);
1101
1102         ffserver_get_arg(redirect->filename, sizeof(redirect->filename), p);
1103         q = strrchr(redirect->filename, '>');
1104         if (*q)
1105             *q = '\0';
1106         redirect->stream_type = STREAM_TYPE_REDIRECT;
1107         *predirect = redirect;
1108         return 0;
1109     }
1110     av_assert0(redirect);
1111     if (!av_strcasecmp(cmd, "URL")) {
1112         ffserver_get_arg(redirect->feed_filename,
1113                 sizeof(redirect->feed_filename), p);
1114     } else if (!av_strcasecmp(cmd, "</Redirect>")) {
1115         if (!redirect->feed_filename[0])
1116             ERROR("No URL found for <Redirect>\n");
1117         *predirect = NULL;
1118     } else {
1119         ERROR("Invalid entry '%s' inside <Redirect></Redirect>\n", cmd);
1120     }
1121     return 0;
1122 }
1123
1124 int ffserver_parse_ffconfig(const char *filename, FFServerConfig *config)
1125 {
1126     FILE *f;
1127     char line[1024];
1128     char cmd[64];
1129     const char *p;
1130     int line_num = 0;
1131     FFServerStream **last_stream, *stream = NULL, *redirect = NULL;
1132     FFServerStream **last_feed, *feed = NULL;
1133     int ret = 0;
1134
1135     av_assert0(config);
1136
1137     f = fopen(filename, "r");
1138     if (!f) {
1139         ret = AVERROR(errno);
1140         av_log(NULL, AV_LOG_ERROR,
1141                 "Could not open the configuration file '%s'\n", filename);
1142         return ret;
1143     }
1144
1145     config->first_stream = NULL;
1146     last_stream = &config->first_stream;
1147     config->first_feed = NULL;
1148     last_feed = &config->first_feed;
1149     config->errors = config->warnings = 0;
1150
1151     for(;;) {
1152         if (fgets(line, sizeof(line), f) == NULL)
1153             break;
1154         line_num++;
1155         p = line;
1156         while (av_isspace(*p))
1157             p++;
1158         if (*p == '\0' || *p == '#')
1159             continue;
1160
1161         ffserver_get_arg(cmd, sizeof(cmd), &p);
1162
1163         if (feed || !av_strcasecmp(cmd, "<Feed")) {
1164             int opening = !av_strcasecmp(cmd, "<Feed");
1165             if (opening && (stream || feed || redirect)) {
1166                 ERROR("Already in a tag\n");
1167             } else {
1168                 if ((ret = ffserver_parse_config_feed(config, cmd, &p, line_num, &feed)) < 0)
1169                     break;
1170                 if (opening) {
1171                     /* add in stream list */
1172                     *last_stream = feed;
1173                     last_stream = &feed->next;
1174                     /* add in feed list */
1175                     *last_feed = feed;
1176                     last_feed = &feed->next_feed;
1177                 }
1178             }
1179         } else if (stream || !av_strcasecmp(cmd, "<Stream")) {
1180             int opening = !av_strcasecmp(cmd, "<Stream");
1181             if (opening && (stream || feed || redirect)) {
1182                 ERROR("Already in a tag\n");
1183             } else {
1184                 if ((ret = ffserver_parse_config_stream(config, cmd, &p, line_num, &stream)) < 0)
1185                     break;
1186                 if (opening) {
1187                     /* add in stream list */
1188                     *last_stream = stream;
1189                     last_stream = &stream->next;
1190                 }
1191             }
1192         } else if (redirect || !av_strcasecmp(cmd, "<Redirect")) {
1193             int opening = !av_strcasecmp(cmd, "<Redirect");
1194             if (opening && (stream || feed || redirect))
1195                 ERROR("Already in a tag\n");
1196             else {
1197                 if ((ret = ffserver_parse_config_redirect(config, cmd, &p, line_num, &redirect)) < 0)
1198                     break;
1199                 if (opening) {
1200                     /* add in stream list */
1201                     *last_stream = redirect;
1202                     last_stream = &redirect->next;
1203                 }
1204             }
1205         } else {
1206             ffserver_parse_config_global(config, cmd, &p, line_num);
1207         }
1208     }
1209
1210     fclose(f);
1211     if (ret < 0)
1212         return ret;
1213     if (config->errors)
1214         return AVERROR(EINVAL);
1215     else
1216         return 0;
1217 }
1218
1219 #undef ERROR
1220 #undef WARNING