You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1261 lines
48KB

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