Newer
Older
Fabrice Bellard
committed
if (rtp_c->state != HTTPSTATE_SEND_DATA &&
rtp_c->state != HTTPSTATE_WAIT_FEED &&
rtp_c->state != HTTPSTATE_READY) {
rtsp_reply_error(c, RTSP_STATUS_STATE);
return;
}
Fabrice Bellard
committed
#if 0
/* XXX: seek in stream */
if (h->range_start != AV_NOPTS_VALUE) {
printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
}
#endif
Fabrice Bellard
committed
rtp_c->state = HTTPSTATE_SEND_DATA;
Fabrice Bellard
committed
/* now everything is OK, so we can send the connection parameters */
rtsp_reply_header(c, RTSP_STATUS_OK);
/* session ID */
url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
url_fprintf(c->pb, "\r\n");
}
static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
Fabrice Bellard
committed
{
HTTPContext *rtp_c;
rtp_c = find_rtp_session_with_url(url, h->session_id);
if (!rtp_c) {
rtsp_reply_error(c, RTSP_STATUS_SESSION);
return;
}
Fabrice Bellard
committed
if (rtp_c->state != HTTPSTATE_SEND_DATA &&
rtp_c->state != HTTPSTATE_WAIT_FEED) {
rtsp_reply_error(c, RTSP_STATUS_STATE);
return;
}
Fabrice Bellard
committed
rtp_c->state = HTTPSTATE_READY;
Giancarlo Formicuccia
committed
rtp_c->first_pts = AV_NOPTS_VALUE;
Fabrice Bellard
committed
/* now everything is OK, so we can send the connection parameters */
rtsp_reply_header(c, RTSP_STATUS_OK);
/* session ID */
url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
url_fprintf(c->pb, "\r\n");
}
static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
Fabrice Bellard
committed
{
HTTPContext *rtp_c;
Fabrice Bellard
committed
rtp_c = find_rtp_session_with_url(url, h->session_id);
if (!rtp_c) {
rtsp_reply_error(c, RTSP_STATUS_SESSION);
return;
}
av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
Fabrice Bellard
committed
/* abort the session */
close_connection(rtp_c);
/* now everything is OK, so we can send the connection parameters */
rtsp_reply_header(c, RTSP_STATUS_OK);
/* session ID */
url_fprintf(c->pb, "Session: %s\r\n", session_id);
Fabrice Bellard
committed
url_fprintf(c->pb, "\r\n");
}
/********************************************************************/
/* RTP handling */
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
FFStream *stream, const char *session_id,
Ronald S. Bultje
committed
enum RTSPLowerTransport rtp_protocol)
Fabrice Bellard
committed
{
HTTPContext *c = NULL;
Fabrice Bellard
committed
/* XXX: should output a warning page when coming
close to the connection limit */
if (nb_connections >= nb_max_connections)
goto fail;
Fabrice Bellard
committed
/* add a new connection */
c = av_mallocz(sizeof(HTTPContext));
if (!c)
goto fail;
Fabrice Bellard
committed
c->fd = -1;
c->poll_entry = NULL;
c->from_addr = *from_addr;
Fabrice Bellard
committed
c->buffer_size = IOBUFFER_INIT_SIZE;
c->buffer = av_malloc(c->buffer_size);
if (!c->buffer)
goto fail;
nb_connections++;
c->stream = stream;
av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
Fabrice Bellard
committed
c->state = HTTPSTATE_READY;
c->is_packetized = 1;
Fabrice Bellard
committed
/* protocol is shown in statistics */
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_UDP:
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_TCP:
proto_str = "TCP";
break;
default:
proto_str = "???";
break;
}
av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
Fabrice Bellard
committed
current_bandwidth += stream->bandwidth;
Fabrice Bellard
committed
c->next = first_http_ctx;
first_http_ctx = c;
return c;
Fabrice Bellard
committed
fail:
if (c) {
av_free(c->buffer);
av_free(c);
}
return NULL;
}
/* add a new RTP stream in an RTP connection (used in RTSP SETUP
command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
Fabrice Bellard
committed
used. */
static int rtp_new_av_stream(HTTPContext *c,
int stream_index, struct sockaddr_in *dest_addr,
HTTPContext *rtsp_c)
Fabrice Bellard
committed
{
AVFormatContext *ctx;
AVStream *st;
char *ipaddr;
Fabrice Bellard
committed
/* now we can open the relevant output stream */
ctx = avformat_alloc_context();
Fabrice Bellard
committed
if (!ctx)
return -1;
ctx->oformat = guess_format("rtp", NULL, NULL);
Fabrice Bellard
committed
st = av_mallocz(sizeof(AVStream));
if (!st)
goto fail;
st->codec= avcodec_alloc_context();
Fabrice Bellard
committed
ctx->nb_streams = 1;
ctx->streams[0] = st;
c->stream->feed == c->stream)
Fabrice Bellard
committed
memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
Fabrice Bellard
committed
c->stream->feed->streams[c->stream->feed_streams[stream_index]],
sizeof(AVStream));
st->priv_data = NULL;
/* build destination RTP address */
ipaddr = inet_ntoa(dest_addr->sin_addr);
switch(c->rtp_protocol) {
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_UDP:
case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
/* XXX: also pass as parameter to function ? */
if (c->stream->is_multicast) {
int ttl;
ttl = c->stream->multicast_ttl;
if (!ttl)
ttl = 16;
snprintf(ctx->filename, sizeof(ctx->filename),
"rtp://%s:%d?multicast=1&ttl=%d",
ipaddr, ntohs(dest_addr->sin_port), ttl);
} else {
snprintf(ctx->filename, sizeof(ctx->filename),
"rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
}
Fabrice Bellard
committed
if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
goto fail;
c->rtp_handles[stream_index] = h;
max_packet_size = url_get_max_packet_size(h);
break;
Ronald S. Bultje
committed
case RTSP_LOWER_TRANSPORT_TCP:
/* RTP/TCP case */
c->rtsp_c = rtsp_c;
max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
break;
default:
Fabrice Bellard
committed
goto fail;
}
http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
ipaddr, ntohs(dest_addr->sin_port),
c->stream->filename, stream_index, c->protocol);
Fabrice Bellard
committed
/* normally, no packets should be output here, but the packet size may be checked */
if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
Fabrice Bellard
committed
/* XXX: close stream */
goto fail;
}
Fabrice Bellard
committed
if (av_write_header(ctx) < 0) {
fail:
if (h)
url_close(h);
av_free(ctx);
return -1;
}
url_close_dyn_buf(ctx->pb, &dummy_buf);
Fabrice Bellard
committed
av_free(dummy_buf);
Fabrice Bellard
committed
c->rtp_ctx[stream_index] = ctx;
return 0;
}
/********************************************************************/
/* ffserver initialization */
static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
Fabrice Bellard
committed
{
AVStream *fst;
fst = av_mallocz(sizeof(AVStream));
if (!fst)
return NULL;
fst->codec= avcodec_alloc_context();
Fabrice Bellard
committed
fst->priv_data = av_mallocz(sizeof(FeedData));
Michael Niedermayer
committed
memcpy(fst->codec, codec, sizeof(AVCodecContext));
Philip Gladstone
committed
fst->index = stream->nb_streams;
av_set_pts_info(fst, 33, 1, 90000);
Fabrice Bellard
committed
stream->streams[stream->nb_streams++] = fst;
return fst;
}
static int add_av_stream(FFStream *feed, AVStream *st)
{
AVStream *fst;
AVCodecContext *av, *av1;
int i;
Michael Niedermayer
committed
av = st->codec;
Michael Niedermayer
committed
av1 = st->codec;
Philip Gladstone
committed
if (av1->codec_id == av->codec_id &&
av1->codec_type == av->codec_type &&
av1->bit_rate == av->bit_rate) {
switch(av->codec_type) {
case CODEC_TYPE_AUDIO:
if (av1->channels == av->channels &&
av1->sample_rate == av->sample_rate)
goto found;
break;
case CODEC_TYPE_VIDEO:
if (av1->width == av->width &&
av1->height == av->height &&
av1->time_base.den == av->time_base.den &&
av1->time_base.num == av->time_base.num &&
Philip Gladstone
committed
default:
Fabrice Bellard
committed
fst = add_av_stream1(feed, av);
if (!fst)
return -1;
return feed->nb_streams - 1;
found:
return i;
}
static void remove_stream(FFStream *stream)
Fabrice Bellard
committed
{
FFStream **ps;
ps = &first_stream;
while (*ps != NULL) {
Fabrice Bellard
committed
*ps = (*ps)->next;
Fabrice Bellard
committed
ps = &(*ps)->next;
}
}
/* specific mpeg4 handling : we extract the raw parameters */
static void extract_mpeg4_header(AVFormatContext *infile)
{
int mpeg4_count, i, size;
AVPacket pkt;
AVStream *st;
mpeg4_count = 0;
for(i=0;i<infile->nb_streams;i++) {
st = infile->streams[i];
Michael Niedermayer
committed
if (st->codec->codec_id == CODEC_ID_MPEG4 &&
st->codec->extradata_size == 0) {
mpeg4_count++;
}
}
if (!mpeg4_count)
return;
Philip Gladstone
committed
printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
while (mpeg4_count > 0) {
if (av_read_packet(infile, &pkt) < 0)
break;
st = infile->streams[pkt.stream_index];
Michael Niedermayer
committed
if (st->codec->codec_id == CODEC_ID_MPEG4 &&
st->codec->extradata_size == 0) {
av_freep(&st->codec->extradata);
/* fill extradata with the header */
/* XXX: we make hard suppositions here ! */
p = pkt.data;
while (p < pkt.data + pkt.size - 4) {
/* stop when vop header is found */
if (p[0] == 0x00 && p[1] == 0x00 &&
p[2] == 0x01 && p[3] == 0xb6) {
size = p - pkt.data;
Panagiotis Issaris
committed
// av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
Michael Niedermayer
committed
st->codec->extradata = av_malloc(size);
st->codec->extradata_size = size;
memcpy(st->codec->extradata, pkt.data, size);
break;
}
p++;
}
mpeg4_count--;
}
av_free_packet(&pkt);
}
}
Fabrice Bellard
committed
/* compute the needed AVStream for each file */
static void build_file_streams(void)
Fabrice Bellard
committed
{
FFStream *stream, *stream_next;
AVFormatContext *infile;
Fabrice Bellard
committed
/* gather all streams */
for(stream = first_stream; stream != NULL; stream = stream_next) {
stream_next = stream->next;
if (stream->stream_type == STREAM_TYPE_LIVE &&
!stream->feed) {
/* the stream comes from a file */
/* try to open the file */
/* open stream */
Fabrice Bellard
committed
stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
Fabrice Bellard
committed
/* specific case : if transport stream output to RTP,
we use a raw transport stream reader */
stream->ap_in->mpeg2ts_raw = 1;
stream->ap_in->mpeg2ts_compute_pcr = 1;
}
if ((ret = av_open_input_file(&infile, stream->feed_filename,
stream->ifmt, 0, stream->ap_in)) < 0) {
http_log("could not open %s: %d\n", stream->feed_filename, ret);
Fabrice Bellard
committed
/* remove stream (no need to spend more time on it) */
fail:
remove_stream(stream);
} else {
/* find all the AVStreams inside and reference them in
'stream' */
if (av_find_stream_info(infile) < 0) {
http_log("Could not find codec parameters from '%s'\n",
Fabrice Bellard
committed
stream->feed_filename);
av_close_input_file(infile);
goto fail;
}
extract_mpeg4_header(infile);
for(i=0;i<infile->nb_streams;i++)
Michael Niedermayer
committed
add_av_stream1(stream, infile->streams[i]->codec);
Fabrice Bellard
committed
av_close_input_file(infile);
}
}
}
}
static void build_feed_streams(void)
{
FFStream *stream, *feed;
int i;
/* gather all streams */
for(stream = first_stream; stream != NULL; stream = stream->next) {
feed = stream->feed;
if (feed) {
if (!stream->is_feed) {
Fabrice Bellard
committed
/* we handle a stream coming from a feed */
for(i=0;i<stream->nb_streams;i++)
stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
}
}
}
/* gather all streams */
for(stream = first_stream; stream != NULL; stream = stream->next) {
feed = stream->feed;
if (feed) {
if (stream->is_feed) {
for(i=0;i<stream->nb_streams;i++)
stream->feed_streams[i] = i;
}
}
}
/* create feed files if needed */
for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
int fd;
if (url_exist(feed->feed_filename)) {
/* See if it matches */
AVFormatContext *s;
int matches = 0;
if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
/* Now see if it matches */
if (s->nb_streams == feed->nb_streams) {
matches = 1;
for(i=0;i<s->nb_streams;i++) {
AVStream *sf, *ss;
sf = feed->streams[i];
ss = s->streams[i];
if (sf->index != ss->index ||
sf->id != ss->id) {
http_log("Index & Id do not match for stream %d (%s)\n",
Fabrice Bellard
committed
i, feed->feed_filename);
matches = 0;
} else {
AVCodecContext *ccf, *ccs;
Michael Niedermayer
committed
ccf = sf->codec;
ccs = ss->codec;
#define CHECK_CODEC(x) (ccf->x != ccs->x)
if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
http_log("Codecs do not match for stream %d\n", i);
matches = 0;
} else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
http_log("Codec bitrates do not match for stream %d\n", i);
matches = 0;
} else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
if (CHECK_CODEC(time_base.den) ||
CHECK_CODEC(time_base.num) ||
CHECK_CODEC(width) ||
CHECK_CODEC(height)) {
http_log("Codec width, height and framerate do not match for stream %d\n", i);
matches = 0;
}
} else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
if (CHECK_CODEC(sample_rate) ||
CHECK_CODEC(channels) ||
CHECK_CODEC(frame_size)) {
http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
matches = 0;
}
} else {
http_log("Unknown codec type\n");
matches = 0;
}
}
break;
}
http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
feed->feed_filename, s->nb_streams, feed->nb_streams);
av_close_input_file(s);
http_log("Deleting feed file '%s' as it appears to be corrupt\n",
feed->feed_filename);
Philip Gladstone
committed
if (!matches) {
if (feed->readonly) {
http_log("Unable to delete feed file '%s' as it is marked readonly\n",
Philip Gladstone
committed
feed->feed_filename);
exit(1);
}
unlink(feed->feed_filename);
Philip Gladstone
committed
}
}
AVFormatContext s1 = {0}, *s = &s1;
Philip Gladstone
committed
if (feed->readonly) {
http_log("Unable to create feed file '%s' as it is marked readonly\n",
Philip Gladstone
committed
feed->feed_filename);
exit(1);
}
/* only write the header of the ffm file */
if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
http_log("Could not open output feed file '%s'\n",
feed->feed_filename);
s->oformat = feed->fmt;
s->nb_streams = feed->nb_streams;
for(i=0;i<s->nb_streams;i++) {
AVStream *st;
st = feed->streams[i];
s->streams[i] = st;
}
Alex Beregszaszi
committed
if (av_write_header(s) < 0) {
http_log("Container doesn't supports the required parameters\n");
Alex Beregszaszi
committed
exit(1);
}
/* XXX: need better api */
av_freep(&s->priv_data);
url_fclose(s->pb);
}
/* get feed size and write index */
fd = open(feed->feed_filename, O_RDONLY);
if (fd < 0) {
http_log("Could not open output feed file '%s'\n",
feed->feed_filename);
exit(1);
}
feed->feed_write_index = ffm_read_write_index(fd);
feed->feed_size = lseek(fd, 0, SEEK_END);
/* ensure that we do not wrap before the end of file */
if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
feed->feed_max_size = feed->feed_size;
close(fd);
}
}
/* compute the bandwidth used by each stream */
static void compute_bandwidth(void)
{
FFStream *stream;
for(stream = first_stream; stream != NULL; stream = stream->next) {
bandwidth = 0;
for(i=0;i<stream->nb_streams;i++) {
AVStream *st = stream->streams[i];
Michael Niedermayer
committed
switch(st->codec->codec_type) {
case CODEC_TYPE_AUDIO:
case CODEC_TYPE_VIDEO:
Michael Niedermayer
committed
bandwidth += st->codec->bit_rate;
break;
default:
break;
}
}
stream->bandwidth = (bandwidth + 999) / 1000;
}
}
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
static void get_arg(char *buf, int buf_size, const char **pp)
{
const char *p;
char *q;
int quote;
p = *pp;
while (isspace(*p)) p++;
q = buf;
quote = 0;
if (*p == '\"' || *p == '\'')
quote = *p++;
for(;;) {
if (quote) {
if (*p == quote)
break;
} else {
if (isspace(*p))
break;
}
if (*p == '\0')
break;
if ((q - buf) < buf_size - 1)
*q++ = *p;
p++;
}
*q = '\0';
if (quote && *p == quote)
p++;
*pp = p;
}
/* add a codec and set the default parameters */
static void add_codec(FFStream *stream, AVCodecContext *av)
{
AVStream *st;
/* compute default parameters */
switch(av->codec_type) {
case CODEC_TYPE_AUDIO:
if (av->bit_rate == 0)
av->bit_rate = 64000;
if (av->sample_rate == 0)
av->sample_rate = 22050;
if (av->channels == 0)
av->channels = 1;
break;
case CODEC_TYPE_VIDEO:
if (av->bit_rate == 0)
av->bit_rate = 64000;
if (av->time_base.num == 0){
av->time_base.den = 5;
av->time_base.num = 1;
Michael Niedermayer
committed
}
if (av->width == 0 || av->height == 0) {
av->width = 160;
av->height = 128;
}
/* Bitrate tolerance is less for streaming */
if (av->bit_rate_tolerance == 0)
Baptiste Coudurier
committed
av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
(int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
if (av->qmin == 0)
av->qmin = 3;
if (av->qmax == 0)
av->qmax = 31;
if (av->max_qdiff == 0)
av->max_qdiff = 3;
av->qcompress = 0.5;
av->qblur = 0.5;
Philip Gladstone
committed
av->nsse_weight = 8;
av->frame_skip_cmp = FF_CMP_DCTMAX;
av->me_method = ME_EPZS;
av->rc_buffer_aggressivity = 1.0;
if (!av->rc_eq)
av->rc_eq = "tex^qComp";
if (!av->i_quant_factor)
Michael Niedermayer
committed
av->i_quant_factor = -0.8;
if (!av->b_quant_factor)
av->b_quant_factor = 1.25;
if (!av->b_quant_offset)
av->b_quant_offset = 1.25;
if (!av->rc_max_rate)
av->rc_max_rate = av->bit_rate * 2;
Philip Gladstone
committed
if (av->rc_max_rate && !av->rc_buffer_size) {
av->rc_buffer_size = av->rc_max_rate;
}
Philip Gladstone
committed
default:
}
st = av_mallocz(sizeof(AVStream));
if (!st)
return;
st->codec = avcodec_alloc_context();
Michael Niedermayer
committed
memcpy(st->codec, av, sizeof(AVCodecContext));
static enum CodecID opt_audio_codec(const char *arg)
Philip Gladstone
committed
{
AVCodec *p= avcodec_find_encoder_by_name(arg);
Philip Gladstone
committed
if (p == NULL || p->type != CODEC_TYPE_AUDIO)
Philip Gladstone
committed
return CODEC_ID_NONE;
return p->id;
}
static enum CodecID opt_video_codec(const char *arg)
Philip Gladstone
committed
{
AVCodec *p= avcodec_find_encoder_by_name(arg);
Philip Gladstone
committed
if (p == NULL || p->type != CODEC_TYPE_VIDEO)
Philip Gladstone
committed
return CODEC_ID_NONE;
return p->id;
}
Fabrice Bellard
committed
/* simplistic plugin support */
Stefan Huehner
committed
static void load_module(const char *filename)
Fabrice Bellard
committed
{
void *dll;
void (*init_func)(void);
dll = dlopen(filename, RTLD_NOW);
if (!dll) {
fprintf(stderr, "Could not load module '%s' - %s\n",
filename, dlerror());
return;
}
Fabrice Bellard
committed
init_func = dlsym(dll, "ffserver_module_init");
if (!init_func) {
Fabrice Bellard
committed
"%s: init function 'ffserver_module_init()' not found\n",
filename);
dlclose(dll);
}
init_func();
}
Fabrice Bellard
committed
Michael Niedermayer
committed
static int ffserver_opt_default(const char *opt, const char *arg,
AVCodecContext *avctx, int type)
{
int ret = 0;
const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
if(o)
ret = av_set_string3(avctx, opt, arg, 1, NULL);
return ret;
static int parse_ffconfig(const char *filename)
{
FILE *f;
char line[1024];
char cmd[64];
char arg[1024];
const char *p;
int val, errors, line_num;
FFStream **last_stream, *stream, *redirect;
FFStream **last_feed, *feed, *s;
f = fopen(filename, "r");
if (!f) {
perror(filename);
return -1;
}
errors = 0;
line_num = 0;
first_stream = NULL;
last_stream = &first_stream;
first_feed = NULL;
last_feed = &first_feed;
stream = NULL;
feed = NULL;
audio_id = CODEC_ID_NONE;
video_id = CODEC_ID_NONE;
for(;;) {
if (fgets(line, sizeof(line), f) == NULL)
break;
line_num++;
p = line;
p++;
if (*p == '\0' || *p == '#')
continue;
get_arg(cmd, sizeof(cmd), &p);
if (!strcasecmp(cmd, "Port")) {
get_arg(arg, sizeof(arg), &p);
val = atoi(arg);
if (val < 1 || val > 65536) {
fprintf(stderr, "%s:%d: Invalid port: %s\n",
filename, line_num, arg);
errors++;
}
my_http_addr.sin_port = htons(val);
} else if (!strcasecmp(cmd, "BindAddress")) {
get_arg(arg, sizeof(arg), &p);
Alex Beregszaszi
committed
if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
Fabrice Bellard
committed
filename, line_num, arg);
errors++;
}
} else if (!strcasecmp(cmd, "NoDaemon")) {
ffserver_daemon = 0;
} else if (!strcasecmp(cmd, "RTSPPort")) {
get_arg(arg, sizeof(arg), &p);
val = atoi(arg);
if (val < 1 || val > 65536) {
fprintf(stderr, "%s:%d: Invalid port: %s\n",
filename, line_num, arg);
errors++;
}
my_rtsp_addr.sin_port = htons(atoi(arg));
Fabrice Bellard
committed
} else if (!strcasecmp(cmd, "RTSPBindAddress")) {
get_arg(arg, sizeof(arg), &p);
Alex Beregszaszi
committed
if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
} else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
get_arg(arg, sizeof(arg), &p);
val = atoi(arg);
if (val < 1 || val > 65536) {
fprintf(stderr, "%s:%d: Invalid MaxHTTPConnections: %s\n",
filename, line_num, arg);
errors++;
}
nb_max_http_connections = val;
} else if (!strcasecmp(cmd, "MaxClients")) {
get_arg(arg, sizeof(arg), &p);
val = atoi(arg);
if (val < 1 || val > nb_max_http_connections) {
fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
filename, line_num, arg);
errors++;
} else {
nb_max_connections = val;
}
} else if (!strcasecmp(cmd, "MaxBandwidth")) {
int64_t llval;
get_arg(arg, sizeof(arg), &p);
llval = atoll(arg);
if (llval < 10 || llval > 10000000) {
fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
filename, line_num, arg);
errors++;
max_bandwidth = llval;
if (!ffserver_debug)
get_arg(logfilename, sizeof(logfilename), &p);
} else if (!strcasecmp(cmd, "<Feed")) {
/*********************************************/
/* Feed related options */
char *q;
if (stream || feed) {
fprintf(stderr, "%s:%d: Already in a tag\n",
filename, line_num);
} else {
feed = av_mallocz(sizeof(FFStream));
get_arg(feed->filename, sizeof(feed->filename), &p);
q = strrchr(feed->filename, '>');
if (*q)
*q = '\0';
for (s = first_feed; s; s = s->next) {
if (!strcmp(feed->filename, s->filename)) {
fprintf(stderr, "%s:%d: Feed '%s' already registered\n",
filename, line_num, s->filename);
errors++;
}
}
feed->fmt = guess_format("ffm", NULL, NULL);
/* defaut feed file */
snprintf(feed->feed_filename, sizeof(feed->feed_filename),
"/tmp/%s.ffm", feed->filename);
feed->feed_max_size = 5 * 1024 * 1024;
feed->is_feed = 1;
feed->feed = feed; /* self feeding :-) */
/* add in stream list */
*last_stream = feed;
last_stream = &feed->next;
/* add in feed list */
*last_feed = feed;
last_feed = &feed->next_feed;
} else if (!strcasecmp(cmd, "Launch")) {
if (feed) {
int i;
feed->child_argv = av_mallocz(64 * sizeof(char *));
Alex Beregszaszi
committed
for (i = 0; i < 62; i++) {
}
feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
snprintf(feed->child_argv[i], 30+strlen(feed->filename),
"http://%s:%d/%s",
(my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
inet_ntoa(my_http_addr.sin_addr),
ntohs(my_http_addr.sin_port), feed->filename);
Philip Gladstone
committed
} else if (!strcasecmp(cmd, "ReadOnlyFile")) {
if (feed) {
get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
feed->readonly = 1;
} else if (stream) {
get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
}
} else if (!strcasecmp(cmd, "File")) {
if (feed) {
get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
} else if (!strcasecmp(cmd, "FileMaxSize")) {
if (feed) {
double fsize;
get_arg(arg, sizeof(arg), &p);
p1 = arg;
switch(toupper(*p1)) {
case 'K':
fsize *= 1024;
break;
case 'M':
fsize *= 1024 * 1024;
break;
case 'G':
fsize *= 1024 * 1024 * 1024;
break;
}
feed->feed_max_size = (int64_t)fsize;
}
} else if (!strcasecmp(cmd, "</Feed>")) {
if (!feed) {
fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
filename, line_num);
errors++;
}
feed = NULL;
} else if (!strcasecmp(cmd, "<Stream")) {
/*********************************************/
/* Stream related options */
char *q;
if (stream || feed) {
fprintf(stderr, "%s:%d: Already in a tag\n",
filename, line_num);
} else {
FFStream *s;
const AVClass *class;
stream = av_mallocz(sizeof(FFStream));
get_arg(stream->filename, sizeof(stream->filename), &p);
q = strrchr(stream->filename, '>');
if (*q)
*q = '\0';
for (s = first_stream; s; s = s->next) {
if (!strcmp(stream->filename, s->filename)) {
fprintf(stderr, "%s:%d: Stream '%s' already registered\n",
filename, line_num, s->filename);
errors++;
}
}
stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
/* fetch avclass so AVOption works
* FIXME try to use avcodec_get_context_defaults2
* without changing defaults too much */
avcodec_get_context_defaults(&video_enc);
class = video_enc.av_class;
memset(&audio_enc, 0, sizeof(AVCodecContext));
memset(&video_enc, 0, sizeof(AVCodecContext));
audio_enc.av_class = class;
video_enc.av_class = class;
audio_id = CODEC_ID_NONE;
video_id = CODEC_ID_NONE;
if (stream->fmt) {
audio_id = stream->fmt->audio_codec;
video_id = stream->fmt->video_codec;
}
*last_stream = stream;
last_stream = &stream->next;