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