Newer
Older
Fabrice Bellard
committed
path++;
if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
for(s=0; s<rtp_c->stream->nb_streams; ++s) {
snprintf(buf, sizeof(buf), "%s/streamid=%d",
rtp_c->stream->filename, s);
if(!strncmp(path, buf, sizeof(buf))) {
// XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
return rtp_c;
}
}
return NULL;
Fabrice Bellard
committed
}
static void rtsp_cmd_play(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 &&
rtp_c->state != HTTPSTATE_READY) {
rtsp_reply_error(c, RTSP_STATUS_STATE);
return;
}
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 = av_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;
}
http_log("Opening file '%s'\n", stream->feed_filename);
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_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
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;
}
}
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 AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
const char *mime_type)
{
AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
if (fmt) {
AVOutputFormat *stream_fmt;
char stream_format_name[64];
snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
if (stream_fmt)
fmt = stream_fmt;
}
return fmt;
}
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 = av_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, "Truncate")) {
if (feed) {
get_arg(arg, sizeof(arg), &p);
feed->truncate = strtod(arg, NULL);
}
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;
if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
fprintf(stderr, "%s:%d: Feed max file size is too small, "
"must be at least %d\n", filename, line_num, FFM_PACKET_SIZE*4);
errors++;
}
}
} 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 = ffserver_guess_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;