Newer
Older
Fabrice Bellard
committed
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, RTSPHeader *h)
{
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
/* abort the session */
close_connection(rtp_c);
if (ff_rtsp_callback) {
ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
Fabrice Bellard
committed
NULL, 0,
rtp_c->stream->rtsp_option);
}
/* 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");
}
/********************************************************************/
/* RTP handling */
static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
FFStream *stream, const char *session_id,
enum RTSPProtocol 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;
pstrcpy(c->session_id, sizeof(c->session_id), session_id);
c->state = HTTPSTATE_READY;
c->is_packetized = 1;
Fabrice Bellard
committed
/* protocol is shown in statistics */
switch(c->rtp_protocol) {
case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
proto_str = "MCAST";
break;
case RTSP_PROTOCOL_RTP_UDP:
proto_str = "UDP";
break;
case RTSP_PROTOCOL_RTP_TCP:
proto_str = "TCP";
break;
default:
proto_str = "???";
break;
}
pstrcpy(c->protocol, sizeof(c->protocol), "RTP/");
pstrcat(c->protocol, sizeof(c->protocol), proto_str);
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;
URLContext *h;
char buf2[32];
Fabrice Bellard
committed
/* now we can open the relevant output stream */
ctx = av_alloc_format_context();
Fabrice Bellard
committed
if (!ctx)
return -1;
ctx->oformat = &rtp_mux;
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;
Fabrice Bellard
committed
c->stream->feed == c->stream) {
memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
} else {
Fabrice Bellard
committed
c->stream->feed->streams[c->stream->feed_streams[stream_index]],
sizeof(AVStream));
}
/* build destination RTP address */
ipaddr = inet_ntoa(dest_addr->sin_addr);
switch(c->rtp_protocol) {
case RTSP_PROTOCOL_RTP_UDP:
case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
/* RTP/UDP case */
/* 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;
case RTSP_PROTOCOL_RTP_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 - - [%s] \"PLAY %s/streamid=%d %s\"\n",
ipaddr, ntohs(dest_addr->sin_port),
ctime1(buf2),
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);
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));
fst->codec->coded_frame = &dummy_frame;
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:
av_abort();
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) {
if (*ps == stream) {
*ps = (*ps)->next;
} else {
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;
// av_hex_dump(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;
int i;
/* 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 == &rtp_mux) {
/* 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 (av_open_input_file(&infile, stream->feed_filename,
Fabrice Bellard
committed
stream->ifmt, 0, stream->ap_in) < 0) {
Fabrice Bellard
committed
http_log("%s not found", stream->feed_filename);
/* 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'",
Fabrice Bellard
committed
stream->feed_filename);
av_close_input_file(infile);
goto fail;
}
extract_mpeg4_header(infile);
Fabrice Bellard
committed
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) {
printf("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)) {
printf("Codecs do not match for stream %d\n", i);
matches = 0;
} else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
printf("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) ||
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
CHECK_CODEC(width) ||
CHECK_CODEC(height)) {
printf("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)) {
printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
matches = 0;
}
} else {
printf("Unknown codec type\n");
matches = 0;
}
}
if (!matches) {
break;
}
}
} else {
printf("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);
} else {
printf("Deleting feed file '%s' as it appears to be corrupt\n",
feed->feed_filename);
}
Philip Gladstone
committed
if (!matches) {
if (feed->readonly) {
printf("Unable to delete feed file '%s' as it is marked readonly\n",
feed->feed_filename);
exit(1);
}
unlink(feed->feed_filename);
Philip Gladstone
committed
}
}
if (!url_exist(feed->feed_filename)) {
AVFormatContext s1, *s = &s1;
Philip Gladstone
committed
if (feed->readonly) {
printf("Unable to create feed file '%s' as it is marked readonly\n",
feed->feed_filename);
exit(1);
}
/* only write the header of the ffm file */
if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
fprintf(stderr, "Could not open output feed file '%s'\n",
feed->feed_filename);
exit(1);
}
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;
}
av_write_header(s);
/* 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) {
fprintf(stderr, "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)
{
int bandwidth, i;
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;
}
}
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
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)
av->bit_rate_tolerance = av->bit_rate / 4;
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:
av_abort();
}
st = av_mallocz(sizeof(AVStream));
if (!st)
return;
st->codec = avcodec_alloc_context();
Michael Niedermayer
committed
memcpy(st->codec, av, sizeof(AVCodecContext));
static int opt_audio_codec(const char *arg)
Philip Gladstone
committed
{
AVCodec *p;
p = first_avcodec;
while (p) {
if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
break;
p = p->next;
}
if (p == NULL) {
return CODEC_ID_NONE;
}
return p->id;
}
static int opt_video_codec(const char *arg)
Philip Gladstone
committed
{
AVCodec *p;
p = first_avcodec;
while (p) {
if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
break;
p = p->next;
}
if (p == NULL) {
return CODEC_ID_NONE;
}
return p->id;
}
Fabrice Bellard
committed
/* simplistic plugin support */
#ifdef CONFIG_HAVE_DLOPEN
Fabrice Bellard
committed
void load_module(const char *filename)
{
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
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;
AVCodecContext audio_enc, video_enc;
int audio_id, video_id;
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);
Fabrice Bellard
committed
my_http_addr.sin_port = htons (atoi(arg));
} else if (!strcasecmp(cmd, "BindAddress")) {
get_arg(arg, sizeof(arg), &p);
Fabrice Bellard
committed
if (!inet_aton(arg, &my_http_addr.sin_addr)) {
fprintf(stderr, "%s:%d: Invalid 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);
my_rtsp_addr.sin_port = htons (atoi(arg));
} else if (!strcasecmp(cmd, "RTSPBindAddress")) {
get_arg(arg, sizeof(arg), &p);
if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
filename, line_num, arg);
errors++;
}
} else if (!strcasecmp(cmd, "MaxClients")) {
get_arg(arg, sizeof(arg), &p);
val = atoi(arg);
if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
filename, line_num, arg);
errors++;
} else {
nb_max_connections = val;
}
} else if (!strcasecmp(cmd, "MaxBandwidth")) {
get_arg(arg, sizeof(arg), &p);
val = atoi(arg);
if (val < 10 || val > 100000) {
fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
filename, line_num, arg);
errors++;
} else {
max_bandwidth = val;
} else if (!strcasecmp(cmd, "CustomLog")) {
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));
/* add in stream list */
*last_stream = feed;
last_stream = &feed->next;
/* add in feed list */
*last_feed = feed;
last_feed = &feed->next_feed;
get_arg(feed->filename, sizeof(feed->filename), &p);
q = strrchr(feed->filename, '>');
if (*q)
*q = '\0';
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 :-) */
}
} else if (!strcasecmp(cmd, "Launch")) {
if (feed) {
int i;
feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
Alex Beregszaszi
committed
for (i = 0; i < 62; i++) {
char argbuf[256];
get_arg(argbuf, sizeof(argbuf), &p);
if (!argbuf[0])
break;
Philip Gladstone
committed
feed->child_argv[i] = av_malloc(strlen(argbuf) + 1);
strcpy(feed->child_argv[i], argbuf);
}
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);
if (ffserver_debug)
{
int j;
fprintf(stdout, "Launch commandline: ");
for (j = 0; j <= i; j++)
fprintf(stdout, "%s ", feed->child_argv[j]);
fprintf(stdout, "\n");
}
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);
}
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
} else if (!strcasecmp(cmd, "File")) {
if (feed) {
get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
} else if (stream) {
get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
}
} else if (!strcasecmp(cmd, "FileMaxSize")) {
if (feed) {
const char *p1;
double fsize;
get_arg(arg, sizeof(arg), &p);
p1 = arg;
fsize = strtod(p1, (char **)&p1);
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++;
#if 0
Philip Gladstone
committed
} else {
/* Make sure that we start out clean */
if (unlink(feed->feed_filename) < 0
&& errno != ENOENT) {
fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
filename, line_num, feed->feed_filename, strerror(errno));
errors++;
}
#endif
}
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 {
stream = av_mallocz(sizeof(FFStream));
*last_stream = stream;
last_stream = &stream->next;
get_arg(stream->filename, sizeof(stream->filename), &p);
q = strrchr(stream->filename, '>');
if (*q)
*q = '\0';
stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
memset(&audio_enc, 0, sizeof(AVCodecContext));
memset(&video_enc, 0, sizeof(AVCodecContext));
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;
}
}
} else if (!strcasecmp(cmd, "Feed")) {
get_arg(arg, sizeof(arg), &p);
if (stream) {
FFStream *sfeed;
sfeed = first_feed;
while (sfeed != NULL) {
if (!strcmp(sfeed->filename, arg))
break;
sfeed = sfeed->next_feed;
}
if (!sfeed) {
fprintf(stderr, "%s:%d: feed '%s' not defined\n",
filename, line_num, arg);
} else {
stream->feed = sfeed;
}
}
} else if (!strcasecmp(cmd, "Format")) {
get_arg(arg, sizeof(arg), &p);
if (!strcmp(arg, "status")) {
stream->stream_type = STREAM_TYPE_STATUS;
stream->fmt = NULL;
} else {
stream->stream_type = STREAM_TYPE_LIVE;
/* jpeg cannot be used here, so use single frame jpeg */
if (!strcmp(arg, "jpeg"))
strcpy(arg, "mjpeg");
stream->fmt = guess_stream_format(arg, NULL, NULL);
fprintf(stderr, "%s:%d: Unknown Format: %s\n",
filename, line_num, arg);
errors++;
}
}
if (stream->fmt) {
audio_id = stream->fmt->audio_codec;
video_id = stream->fmt->video_codec;
}
Fabrice Bellard
committed
} else if (!strcasecmp(cmd, "InputFormat")) {
stream->ifmt = av_find_input_format(arg);
if (!stream->ifmt) {
fprintf(stderr, "%s:%d: Unknown input format: %s\n",
Fabrice Bellard
committed
filename, line_num, arg);
}
} else if (!strcasecmp(cmd, "FaviconURL")) {