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.

1217 lines
46KB

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