2183
2181
buf_size = FFM_PACKET_SIZE;
2184
2182
/* compute position (absolute time) */
2185
2183
if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2186
if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
2184
if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0) {
2185
http_log("Invalid date specification '%s' for stream\n", buf);
2188
2188
} else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
2189
2189
int prebuffer = strtol(buf, 0, 10);
2190
2190
stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
2196
2196
/* compute position (relative time) */
2197
2197
if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
2198
if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
2198
if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0) {
2199
http_log("Invalid date specification '%s' for stream\n", buf);
2201
2203
stream_pos = 0;
2203
if (input_filename[0] == '\0')
2205
if (!input_filename[0]) {
2206
http_log("No filename was specified for stream\n");
2207
return AVERROR(EINVAL);
2206
2210
/* open stream */
2207
2211
if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
2208
http_log("could not open %s: %d\n", input_filename, ret);
2212
http_log("Could not open input '%s': %s\n", input_filename, av_err2str(ret));
2212
2216
/* set buffer size */
2215
2219
s->flags |= AVFMT_FLAG_GENPTS;
2217
if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
2218
http_log("Could not find stream info '%s'\n", input_filename);
2221
if (strcmp(s->iformat->name, "ffm") &&
2222
(ret = avformat_find_stream_info(c->fmt_in, NULL)) < 0) {
2223
http_log("Could not find stream info for input '%s'\n", input_filename);
2219
2224
avformat_close_input(&s);
2223
2228
/* choose stream as clock source (we favorize video stream if
2271
2276
switch(c->state) {
2272
2277
case HTTPSTATE_SEND_DATA_HEADER:
2273
2278
memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2274
av_dict_set(&c->fmt_ctx.metadata, "author" , c->stream->author , 0);
2275
av_dict_set(&c->fmt_ctx.metadata, "comment" , c->stream->comment , 0);
2276
av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
2277
av_dict_set(&c->fmt_ctx.metadata, "title" , c->stream->title , 0);
2279
av_dict_copy(&(c->fmt_ctx.metadata), c->stream->metadata, 0);
2279
2280
c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
2281
2282
for(i=0;i<c->stream->nb_streams;i++) {
2314
2315
c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2316
if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
2317
http_log("Error writing output header\n");
2317
if ((ret = avformat_write_header(&c->fmt_ctx, NULL)) < 0) {
2318
http_log("Error writing output header for stream '%s': %s\n",
2319
c->stream->filename, av_err2str(ret));
2320
2322
av_dict_free(&c->fmt_ctx.metadata);
2446
2448
if (pkt.pts != AV_NOPTS_VALUE)
2447
2449
pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2448
2450
pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2449
if (av_write_frame(ctx, &pkt) < 0) {
2450
http_log("Error writing frame to output\n");
2451
if ((ret = av_write_frame(ctx, &pkt)) < 0) {
2452
http_log("Error writing frame to output for stream '%s': %s\n",
2453
c->stream->filename, av_err2str(ret));
2451
2454
c->state = HTTPSTATE_SEND_DATA_TRAILER;
2614
2617
static int http_start_receive_data(HTTPContext *c)
2618
if (c->stream->feed_opened)
2622
if (c->stream->feed_opened) {
2623
http_log("Stream feed '%s' was not opened\n", c->stream->feed_filename);
2624
return AVERROR(EINVAL);
2621
2627
/* Don't permit writing to this one */
2622
if (c->stream->readonly)
2628
if (c->stream->readonly) {
2629
http_log("Cannot write to read-only file '%s'\n", c->stream->feed_filename);
2630
return AVERROR(EINVAL);
2625
2633
/* open feed */
2626
2634
fd = open(c->stream->feed_filename, O_RDWR);
2628
http_log("Error opening feeder file: %s\n", strerror(errno));
2636
ret = AVERROR(errno);
2637
http_log("Could not open feed file '%s': %s\n",
2638
c->stream->feed_filename, strerror(errno));
2631
2641
c->feed_fd = fd;
2635
2645
ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
2636
2646
http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
2637
2647
if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
2638
http_log("Error truncating feed file: %s\n", strerror(errno));
2648
ret = AVERROR(errno);
2649
http_log("Error truncating feed file '%s': %s\n",
2650
c->stream->feed_filename, strerror(errno));
2642
if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
2643
http_log("Error reading write index from feed file: %s\n", strerror(errno));
2654
ret = ffm_read_write_index(fd);
2656
http_log("Error reading write index from feed file '%s': %s\n",
2657
c->stream->feed_filename, strerror(errno));
2660
c->stream->feed_write_index = ret;
2979
2996
avc->oformat = rtp_format;
2980
2997
av_dict_set(&avc->metadata, "title",
2981
stream->title[0] ? stream->title : "No Title", 0);
2998
entry ? entry->value : "No Title", 0);
2982
2999
avc->nb_streams = stream->nb_streams;
2983
3000
if (stream->is_multicast) {
2984
3001
snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
3669
3686
av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
3672
http_log("Opening file '%s'\n", stream->feed_filename);
3689
if (!stream->feed_filename[0]) {
3690
http_log("Unspecified feed file for stream '%s'\n", stream->filename);
3694
http_log("Opening feed file '%s' for stream '%s'\n", stream->feed_filename, stream->filename);
3673
3695
if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
3674
http_log("Could not open '%s': %d\n", stream->feed_filename, ret);
3696
http_log("Could not open '%s': %s\n", stream->feed_filename, av_err2str(ret));
3675
3697
/* remove stream (no need to spend more time on it) */
3677
3699
remove_stream(stream);
3942
3964
memcpy(st->codec, av, sizeof(AVCodecContext));
3945
static enum AVCodecID opt_audio_codec(const char *arg)
3947
AVCodec *p= avcodec_find_encoder_by_name(arg);
3949
if (p == NULL || p->type != AVMEDIA_TYPE_AUDIO)
3950
return AV_CODEC_ID_NONE;
3955
static enum AVCodecID opt_video_codec(const char *arg)
3957
AVCodec *p= avcodec_find_encoder_by_name(arg);
3959
if (p == NULL || p->type != AVMEDIA_TYPE_VIDEO)
3960
return AV_CODEC_ID_NONE;
3967
static enum AVCodecID opt_codec(const char *name, enum AVMediaType type)
3969
AVCodec *codec = avcodec_find_encoder_by_name(name);
3971
if (!codec || codec->type != type)
3972
return AV_CODEC_ID_NONE;
3965
3976
static int ffserver_opt_default(const char *opt, const char *arg,
4000
4011
if(!strcmp(tmp, "acodec")){
4001
*audio_id = opt_audio_codec(tmp2);
4012
*audio_id = opt_codec(tmp2, AVMEDIA_TYPE_AUDIO);
4002
4013
}else if(!strcmp(tmp, "vcodec")){
4003
*video_id = opt_video_codec(tmp2);
4014
*video_id = opt_codec(tmp2, AVMEDIA_TYPE_VIDEO);
4004
4015
}else if(!strcmp(tmp, "scodec")){
4005
4016
/* opt_subtitle_codec(tmp2); */
4006
4017
}else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
4037
static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
4048
static void report_config_error(const char *filename, int line_num, int log_level, int *errors, const char *fmt, ...)
4040
4051
va_start(vl, fmt);
4041
fprintf(stderr, "%s:%d: ", filename, line_num);
4042
vfprintf(stderr, fmt, vl);
4052
av_log(NULL, log_level, "%s:%d: ", filename, line_num);
4053
av_vlog(NULL, log_level, fmt, vl);
4051
4062
char line[1024];
4064
char arg[1024], arg2[1024];
4055
int val, errors, line_num;
4066
int val, errors, warnings, line_num;
4056
4067
FFStream **last_stream, *stream, *redirect;
4057
4068
FFStream **last_feed, *feed, *s;
4058
4069
AVCodecContext audio_enc, video_enc;
4059
4070
enum AVCodecID audio_id, video_id;
4061
4073
f = fopen(filename, "r");
4075
ret = AVERROR(errno);
4076
av_log(NULL, AV_LOG_ERROR, "Could not open the configuration file '%s'\n", filename);
4080
errors = warnings = 0;
4069
4082
first_stream = NULL;
4070
4083
last_stream = &first_stream;
4075
4088
redirect = NULL;
4076
4089
audio_id = AV_CODEC_ID_NONE;
4077
4090
video_id = AV_CODEC_ID_NONE;
4091
#define ERROR(...) report_config_error(filename, line_num, AV_LOG_ERROR, &errors, __VA_ARGS__)
4092
#define WARNING(...) report_config_error(filename, line_num, AV_LOG_WARNING, &warnings, __VA_ARGS__)
4079
#define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
4081
4095
if (fgets(line, sizeof(line), f) == NULL)
4102
4116
ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
4104
4118
} else if (!av_strcasecmp(cmd, "NoDaemon")) {
4105
// do nothing here, its the default now
4119
WARNING("NoDaemon option has no effect, you should remove it\n");
4106
4120
} else if (!av_strcasecmp(cmd, "RTSPPort")) {
4107
4121
get_arg(arg, sizeof(arg), &p);
4108
4122
val = atoi(arg);
4163
4181
feed->fmt = av_guess_format("ffm", NULL, NULL);
4164
/* defaut feed file */
4182
/* default feed file */
4165
4183
snprintf(feed->feed_filename, sizeof(feed->feed_filename),
4166
4184
"/tmp/%s.ffm", feed->filename);
4167
4185
feed->feed_max_size = 5 * 1024 * 1024;
4182
4200
feed->child_argv = av_mallocz(64 * sizeof(char *));
4201
if (!feed->child_argv) {
4202
ret = AVERROR(ENOMEM);
4184
4205
for (i = 0; i < 62; i++) {
4185
4206
get_arg(arg, sizeof(arg), &p);
4189
4210
feed->child_argv[i] = av_strdup(arg);
4211
if (!feed->child_argv[i]) {
4212
ret = AVERROR(ENOMEM);
4192
feed->child_argv[i] = av_asprintf("http://%s:%d/%s",
4193
(my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4194
inet_ntoa(my_http_addr.sin_addr),
4195
ntohs(my_http_addr.sin_port), feed->filename);
4217
feed->child_argv[i] =
4218
av_asprintf("http://%s:%d/%s",
4219
(my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
4220
inet_ntoa(my_http_addr.sin_addr), ntohs(my_http_addr.sin_port),
4222
if (!feed->child_argv[i]) {
4223
ret = AVERROR(ENOMEM);
4197
} else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
4227
} else if (!av_strcasecmp(cmd, "File") || !av_strcasecmp(cmd, "ReadOnlyFile")) {
4199
4229
get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4230
feed->readonly = !av_strcasecmp(cmd, "ReadOnlyFile");
4201
4231
} else if (stream) {
4202
4232
get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4204
} else if (!av_strcasecmp(cmd, "File")) {
4206
get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
4208
get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4209
4234
} else if (!av_strcasecmp(cmd, "Truncate")) {
4211
4236
get_arg(arg, sizeof(arg), &p);
4212
feed->truncate = strtod(arg, NULL);
4237
/* assume Truncate is true in case no argument is specified */
4241
WARNING("Truncate N syntax in configuration file is deprecated, "
4242
"use Truncate alone with no arguments\n");
4243
feed->truncate = strtod(arg, NULL);
4214
4246
} else if (!av_strcasecmp(cmd, "FileMaxSize")) {
4326
4362
ERROR("FaviconURL only permitted for status streams\n");
4328
} else if (!av_strcasecmp(cmd, "Author")) {
4330
get_arg(stream->author, sizeof(stream->author), &p);
4331
} else if (!av_strcasecmp(cmd, "Comment")) {
4333
get_arg(stream->comment, sizeof(stream->comment), &p);
4334
} else if (!av_strcasecmp(cmd, "Copyright")) {
4336
get_arg(stream->copyright, sizeof(stream->copyright), &p);
4337
} else if (!av_strcasecmp(cmd, "Title")) {
4339
get_arg(stream->title, sizeof(stream->title), &p);
4364
} else if (!av_strcasecmp(cmd, "Author") ||
4365
!av_strcasecmp(cmd, "Comment") ||
4366
!av_strcasecmp(cmd, "Copyright") ||
4367
!av_strcasecmp(cmd, "Title")) {
4368
get_arg(arg, sizeof(arg), &p);
4374
for (i = 0; i < strlen(cmd); i++)
4375
key[i] = av_tolower(cmd[i]);
4377
WARNING("'%s' option in configuration file is deprecated, "
4378
"use 'Metadata %s VALUE' instead\n", cmd, key);
4379
if ((ret = av_dict_set(&stream->metadata, key, arg, 0)) < 0) {
4380
ERROR("Could not set metadata '%s' to value '%s': %s\n",
4381
key, arg, av_err2str(ret));
4384
} else if (!av_strcasecmp(cmd, "Metadata")) {
4385
get_arg(arg, sizeof(arg), &p);
4386
get_arg(arg2, sizeof(arg2), &p);
4389
if ((ret = av_dict_set(&stream->metadata, arg, arg2, 0)) < 0) {
4390
ERROR("Could not set metadata '%s' to value '%s': %s\n",
4391
arg, arg2, av_err2str(ret));
4340
4394
} else if (!av_strcasecmp(cmd, "Preroll")) {
4341
4395
get_arg(arg, sizeof(arg), &p);
4346
4400
stream->send_on_key = 1;
4347
4401
} else if (!av_strcasecmp(cmd, "AudioCodec")) {
4348
4402
get_arg(arg, sizeof(arg), &p);
4349
audio_id = opt_audio_codec(arg);
4403
audio_id = opt_codec(arg, AVMEDIA_TYPE_AUDIO);
4350
4404
if (audio_id == AV_CODEC_ID_NONE) {
4351
4405
ERROR("Unknown AudioCodec: %s\n", arg);
4353
4407
} else if (!av_strcasecmp(cmd, "VideoCodec")) {
4354
4408
get_arg(arg, sizeof(arg), &p);
4355
video_id = opt_video_codec(arg);
4409
video_id = opt_codec(arg, AVMEDIA_TYPE_VIDEO);
4356
4410
if (video_id == AV_CODEC_ID_NONE) {
4357
4411
ERROR("Unknown VideoCodec: %s\n", arg);
4372
4426
get_arg(arg, sizeof(arg), &p);
4374
4428
audio_enc.sample_rate = atoi(arg);
4375
} else if (!av_strcasecmp(cmd, "AudioQuality")) {
4376
get_arg(arg, sizeof(arg), &p);
4378
// audio_enc.quality = atof(arg) * 1000;
4380
4429
} else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
4382
4431
int minrate, maxrate;
4418
4467
} else if (!av_strcasecmp(cmd, "VideoSize")) {
4419
4468
get_arg(arg, sizeof(arg), &p);
4421
av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4422
if ((video_enc.width % 16) != 0 ||
4423
(video_enc.height % 16) != 0) {
4424
ERROR("Image size must be a multiple of 16\n");
4470
ret = av_parse_video_size(&video_enc.width, &video_enc.height, arg);
4472
ERROR("Invalid video size '%s'\n", arg);
4474
if ((video_enc.width % 16) != 0 ||
4475
(video_enc.height % 16) != 0) {
4476
ERROR("Image size must be a multiple of 16\n");
4427
4480
} else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
4473
4525
type = AV_OPT_FLAG_AUDIO_PARAM;
4475
4527
if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4476
ERROR("AVOption error: %s %s\n", arg, arg2);
4528
ERROR("Error setting %s option to %s %s\n", cmd, arg, arg2);
4478
4530
} else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
4479
4531
!av_strcasecmp(cmd, "AVPresetAudio")) {
4711
4771
sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4712
4772
sigaction(SIGCHLD, &sigact, 0);
4714
if (parse_ffconfig(config_filename) < 0) {
4715
fprintf(stderr, "Incorrect config file - exiting.\n");
4774
if ((ret = parse_ffconfig(config_filename)) < 0) {
4775
fprintf(stderr, "Error reading configuration file '%s': %s\n",
4776
config_filename, av_err2str(ret));
4718
4779
av_freep(&config_filename);